v0.14.0 815fb98

ci_concurrency.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain
4
5import (
6	"strconv"
7	"unicode"
8	"unicode/utf16"
9)
10
11// CIConcurrencyState is where a run or a job is in its concurrency group.
12type CIConcurrencyState string
13
14// Concurrency states.
15const (
16	// CIConcurrencyIdle: the group is evaluated but the entry has not asked for
17	// its turn yet.
18	CIConcurrencyIdle CIConcurrencyState = ""
19	// CIConcurrencyPending: the group record names this entry as the waiting
20	// one, behind the running entry.
21	CIConcurrencyPending CIConcurrencyState = "pending"
22	// CIConcurrencyHolding: this entry is the running one of its group.
23	CIConcurrencyHolding CIConcurrencyState = "holding"
24	// CIConcurrencyReplaced: only in a mark. A newer entry replaced this one:
25	// it is owed a cancellation and never acquires the group.
26	CIConcurrencyReplaced CIConcurrencyState = "replaced"
27)
28
29// CIMaxConcurrencyGroup is the longest concurrency group name GitHub accepts
30// (actions/runner WorkflowTemplateConverter.cs:679-687, "must be less than 400
31// characters").
32const CIMaxConcurrencyGroup = 400
33
34// CIConcurrency is the evaluated "concurrency:" of a run (workflow level) or of
35// a job.
36type CIConcurrency struct {
37	// Group is the evaluated group as written; "" for none. Groups are compared
38	// without regard to case (ConcurrencyKey).
39	Group string
40	// Cancel is the evaluated cancel-in-progress.
41	Cancel bool
42	// State is written to the record of the entry only after the group record
43	// named it (acquire committed): an entry that waits in a group that does
44	// not name it any more was replaced, and is cancelled.
45	State CIConcurrencyState
46	// Evaluated is set once the expressions were evaluated, whatever the result
47	// (a run starts unevaluated, and a re-run evaluates again).
48	Evaluated bool
49	// FromRun is set on a job that shows the group of its run: the entry is the
50	// run's, the job holds nothing of its own.
51	FromRun bool
52	// Preempted is set when a newer entry of the group cancelled this one.
53	Preempted bool
54}
55
56// ConcurrencyKey is the identity of a group within a repository. GitHub compares
57// groups with .NET's OrdinalIgnoreCase, which compares strings upper-cased
58// character by character with the simple case mapping (OrdinalCasing in
59// dotnet/runtime, System.Private.CoreLib/src/System/Globalization/
60// OrdinalCasing.cs; the source was not available here, so this follows its
61// documented rule). A character that is not ASCII and whose upper case is ASCII
62// keeps itself: ı (U+0131) and ſ (U+017F) are neither I nor S. So σ, ς and Σ are
63// one letter, ı and İ are distinct from I, and i equals I. The key is upper
64// case and is never lowered back.
65func ConcurrencyKey(group string) string {
66	out := make([]rune, 0, len(group))
67	for _, r := range group {
68		if u := unicode.ToUpper(r); r <= unicode.MaxASCII || u > unicode.MaxASCII {
69			r = u
70		}
71		out = append(out, r)
72	}
73	return string(out)
74}
75
76// ConcurrencyGroupLength is the length of a group as C#'s String.Length counts
77// it: in UTF-16 code units.
78func ConcurrencyGroupLength(group string) int { return len(utf16.Encode([]rune(group))) }
79
80// ConcurrencyCancelMessage is GitHub's annotation for an entry cancelled by a
81// newer one (docs.github.com, "Control the concurrency of workflows and jobs").
82func ConcurrencyCancelMessage(group string) string {
83	return "Canceling since a higher priority waiting request for '" + group + "' exists"
84}
85
86// CIConcurrencyEntry is a run (JobID empty) or a job waiting for or holding a
87// concurrency group.
88type CIConcurrencyEntry struct {
89	RunID   int64
90	Attempt int32
91	JobID   string
92	// Cancel is the cancel-in-progress of the entry: while it waits, the
93	// running entry of the group is cancelled.
94	Cancel bool
95	// Canceling is set on a running entry that a waiting entry with
96	// cancel-in-progress asked to stop; it stays named until it has ended, and
97	// the reconciliation asks it again.
98	Canceling bool
99}
100
101// Key identifies the entry in the marks of its group.
102func (e CIConcurrencyEntry) Key() string {
103	return strconv.FormatInt(e.RunID, 10) + "/" + strconv.FormatInt(int64(e.Attempt), 10) + "/" + e.JobID
104}
105
106// Same reports whether two entries are the same run or job.
107func (e CIConcurrencyEntry) Same(o CIConcurrencyEntry) bool {
108	return e.RunID == o.RunID && e.Attempt == o.Attempt && e.JobID == o.JobID
109}
110
111// CIConcurrencyRecord is the state of a group: at most one running entry and
112// one pending entry (queue: single).
113type CIConcurrencyRecord struct {
114	Running *CIConcurrencyEntry
115	Pending *CIConcurrencyEntry
116}
117
118// IsEmpty reports whether nothing runs or waits in the group.
119func (r CIConcurrencyRecord) IsEmpty() bool { return r.Running == nil && r.Pending == nil }
120
121// Names reports whether the record names the entry as the running or the
122// pending one.
123func (r CIConcurrencyRecord) Names(e CIConcurrencyEntry) bool {
124	return (r.Running != nil && r.Running.Same(e)) || (r.Pending != nil && r.Pending.Same(e))
125}
126
127// CIConcurrencyMark says where an entry stands in its group. The marks of a group
128// are written in the same batch as its record: an entry is marked pending or
129// holding if and only if the record names it as the pending or the running
130// entry, and an entry that a newer one replaced is marked replaced in the batch
131// that unnames it. So a replaced entry is found from the marks alone, whatever
132// failed after that batch.
133type CIConcurrencyMark struct {
134	Entry CIConcurrencyEntry
135	// Group is the group as the newest request wrote it (the cancel annotation
136	// names it).
137	Group string
138	State CIConcurrencyState
139}
140
141// CIConcurrencyGroupState is a group as one transaction sees it: its record and
142// the marks of the entries that belong to it, by CIConcurrencyEntry.Key.
143type CIConcurrencyGroupState struct {
144	Record CIConcurrencyRecord
145	Marks  map[string]CIConcurrencyMark
146}
147
148// IsEmpty reports whether the group has no entry and no mark left.
149func (s CIConcurrencyGroupState) IsEmpty() bool { return s.Record.IsEmpty() && len(s.Marks) == 0 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 44

↑↓ moveenter openesc close