main 98594d0

push.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package workflow
4
5import "strings"
6
7// Limits GitHub applies to push events.
8const (
9	// MaxFilesEvaluated is the number of changed files a paths or paths-ignore
10	// filter looks at; files after the 3000th are ignored. Recorded on
11	// github.com (filter-paths-3000-boundary): a push of exactly 3000 files
12	// whose only match is the last one runs the workflow, the same push with
13	// 3001 files does not. It is not 300 (filter-paths-300-files and
14	// filter-paths-1200-files: a match beyond the 300th file still runs).
15	MaxFilesEvaluated = 3000
16	// MaxTagsPerPush is the number of tags that can be pushed at once before
17	// GitHub creates no event for them.
18	MaxTagsPerPush = 3
19)
20
21// PushInfo describes a push to one ref.
22type PushInfo struct {
23	// Ref is the full name, "refs/heads/main" or "refs/tags/v1".
24	Ref string
25	// Created and Deleted say whether the push created or deleted the ref.
26	Created bool
27	Deleted bool
28	// ChangedFiles are the paths changed by the push, in the order of the
29	// diff. Only the first MaxFilesEvaluated are looked at.
30	ChangedFiles []string
31	// FilesTruncated is set when the list of changed files is not available:
32	// the push has more than 1000 commits, or the diff could not be
33	// computed. A paths filter is then considered satisfied.
34	FilesTruncated bool
35	// TagCount is the number of tags pushed in the same push.
36	TagCount int
37}
38
39// MatchPush reports whether a push triggers the workflow, applying the
40// "push" event's filters like GitHub:
41//
42//   - the deletion of a ref triggers nothing;
43//   - a push of more than three tags creates no event for its tags;
44//   - a workflow with only branches or branches-ignore does not run for a tag,
45//     and one with only tags or tags-ignore does not run for a branch; with
46//     neither it runs for both;
47//   - when both a branches and a tags filter exist, a ref is checked against
48//     the filter for its kind;
49//   - paths and paths-ignore apply to branch pushes only, against the first
50//     3000 changed files; when the list is unavailable (see
51//     [PushInfo.FilesTruncated]) they pass;
52//   - patterns are evaluated in order and the last one that matches wins.
53//
54// Commit-message directives are separate: see [SkipCI].
55//
56// Recorded on github.com: push-tags-limit, filter-tags, filter-branches-ignore,
57// filter-tag-branches-only, filter-paths-tag, push-delete-branch and
58// push-delete-tag, see TestMatchPushRecordedTags. Still assumed: the
59// behaviour on refs that are neither a branch nor a tag, and on a push that
60// changes no file.
61func (w *Workflow) MatchPush(p PushInfo) bool {
62	if w == nil || w.On == nil {
63		return false
64	}
65	ev := w.On.Event("push")
66	if ev == nil {
67		return false
68	}
69	if p.Deleted {
70		return false
71	}
72	switch {
73	case strings.HasPrefix(p.Ref, "refs/heads/"):
74		name := strings.TrimPrefix(p.Ref, "refs/heads/")
75		if !ev.refFilterAllows(true, name) {
76			return false
77		}
78		return ev.pathsAllow(p)
79	case strings.HasPrefix(p.Ref, "refs/tags/"):
80		if p.TagCount > MaxTagsPerPush {
81			return false
82		}
83		return ev.refFilterAllows(false, strings.TrimPrefix(p.Ref, "refs/tags/"))
84	}
85	return false
86}
87
88// refFilterAllows applies the branches or tags filters to the name of a
89// branch (branch is true) or a tag.
90func (e *Event) refFilterAllows(branch bool, name string) bool {
91	cf := e.filters()
92	inc, exc := cf.branches, cf.branchesIgnore
93	own, ownIgnore, other, otherIgnore := e.Branches, e.BranchesIgnore, e.Tags, e.TagsIgnore
94	if !branch {
95		inc, exc = cf.tags, cf.tagsIgnore
96		own, ownIgnore, other, otherIgnore = e.Tags, e.TagsIgnore, e.Branches, e.BranchesIgnore
97	}
98	if own == nil && ownIgnore == nil && (other != nil || otherIgnore != nil) {
99		return false
100	}
101	if own != nil && !matchList(inc, name) {
102		return false
103	}
104	if ownIgnore != nil && matchList(exc, name) {
105		return false
106	}
107	return true
108}
109
110// pathsAllow applies paths and paths-ignore to a branch push.
111func (e *Event) pathsAllow(p PushInfo) bool {
112	if e.Paths == nil && e.PathsIgnore == nil {
113		return true
114	}
115	if p.FilesTruncated {
116		return true
117	}
118	cf := e.filters()
119	files := p.ChangedFiles
120	if len(files) > MaxFilesEvaluated {
121		files = files[:MaxFilesEvaluated]
122	}
123	if e.Paths != nil {
124		any := false
125		for _, f := range files {
126			if matchList(cf.paths, f) {
127				any = true
128				break
129			}
130		}
131		if !any {
132			return false
133		}
134	}
135	if e.PathsIgnore != nil && len(files) > 0 {
136		any := false
137		for _, f := range files {
138			if !matchList(cf.pathsIgnore, f) {
139				any = true
140				break
141			}
142		}
143		if !any {
144			return false
145		}
146	}
147	return true
148}

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 1

Tags 3

↑↓ moveenter openesc close