Repository navigation
204 lines (200 loc) · 10.5 KB
/
Copy pathchanges.yml
File metadata and controls
204 lines (200 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
name: changes
# Reusable path-filter: emits per-area booleans so callers can gate expensive jobs.
# Used by main.yml (path-driven CI) and pr-label-suites.yml (label-driven suites, which
# consult these to avoid double-running a suite main.yml is already covering).
#
# Outputs default to 'true' for non-pull_request events (push, workflow_dispatch) so those
# run everything regardless of which files changed.
on:
workflow_call:
outputs:
runtime:
value: ${{ jobs.detect.outputs.runtime }}
ios:
value: ${{ jobs.detect.outputs.ios }}
ios-tests:
value: ${{ jobs.detect.outputs.ios-tests }}
android:
value: ${{ jobs.detect.outputs.android }}
full:
value: ${{ jobs.detect.outputs.full }}
permissions:
contents: read
pull-requests: read
jobs:
detect:
runs-on: ubuntu-latest
outputs:
runtime: ${{ steps.comments.outputs.comment_only == 'true' && 'false' || steps.filter.outputs.runtime || 'true' }}
ios: ${{ steps.comments.outputs.comment_only == 'true' && 'false' || steps.filter.outputs.ios || 'true' }}
ios-tests: ${{ steps.comments.outputs.comment_only == 'true' && 'false' || steps.filter.outputs.ios-tests || 'true' }}
android: ${{ steps.comments.outputs.comment_only == 'true' && 'false' || steps.filter.outputs.android || 'true' }}
full: ${{ steps.comments.outputs.comment_only == 'true' && 'false' || steps.filter.outputs.full || 'true' }}
steps:
- uses: actions/checkout@v7
if: github.event_name == 'pull_request'
# Comment-only PRs skip everything (Gate stays green via skipped jobs). Conservative:
# only whole-line comments/blanks in known file types count; anything else (inline
# comment edits, unknown extensions, huge/binary patches) runs CI normally. Known
# hole: comment-looking lines inside multi-line string literals.
- name: Detect comment-only changes
id: comments
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --slurp > /tmp/pr-files.json
python3 - <<'EOF' >> "$GITHUB_OUTPUT"
import json, os
markers = {'.cs': '//', '.yml': '#', '.yaml': '#'}
files = [f for page in json.load(open('/tmp/pr-files.json')) for f in page]
def comment_only():
if len(files) >= 3000: # API truncates the file list there
return False
for f in files:
marker = markers.get(os.path.splitext(f['filename'])[1].lower())
patch = f.get('patch') # absent for binary/oversized diffs
if marker is None or patch is None:
return False
for line in patch.split('\n'):
if line[:1] in ('+', '-'):
body = line[1:].strip()
# U+2028/29: line terminators to the C# compiler but not to diff;
# code could hide behind a comment marker on the same diff line.
if chr(0x2028) in body or chr(0x2029) in body:
return False
if body and not body.startswith(marker):
return False
return bool(files)
print('comment_only=' + str(comment_only()).lower())
EOF
# Users get the samples from the published content template packs (StrideSamplesVersion.props), not from
# the repository, so a sample change only reaches them through a samples release (release-samples.yml).
# Editing samples is normal work, so this only warns, as a reminder that one is pending. The engine-versioned
# template sources and prose don't count.
- name: Warn on sample changes (samples release needed)
if: github.event_name == 'pull_request'
run: |
python3 - <<'EOF'
import json, re
files = [f for page in json.load(open('/tmp/pr-files.json')) for f in page]
def is_content(name):
return (name.startswith('samples/')
and not name.startswith(('samples/NewGame/', 'samples/Library/'))
and not name.lower().endswith('.md'))
changed = [f for f in files if is_content(f['filename']) or is_content(f.get('previous_filename') or '')]
if changed:
names = [f['filename'] for f in changed]
shown = ', '.join(names[:5]) + (f' and {len(names) - 5} more' if len(names) > 5 else '')
# The PR diff shows an annotation only on a file of the new version, on a line inside the diff: the
# first changed sample file that still exists and has a text diff, at the start of its first changed
# block. Deleted and binary-only changes leave the warning on the run summary only.
where = ''
for f in changed:
hunk = re.search(r'^@@ -\d+(?:,\d+)? \+(\d+)', f.get('patch') or '', re.M)
if f['status'] != 'removed' and hunk and int(hunk.group(1)) > 0:
where = f"file={f['filename']},line={hunk.group(1)},"
break
print('::warning ' + where + 'title=Samples changed::This PR changes the samples (' + shown + '). '
'Users only get them with the next samples release: after merging, run release-samples.yml '
'(before the next engine release if the engine needs them; see docs/build/versioning.md).')
EOF
# The crash reporter carries its own package version, so an engine release republishes it only when that
# version changes. A change shipped without a bump is pushed as a duplicate, skipped, and never reaches users.
# Only files that end up in the package count: its tests and the probe don't.
- name: Warn on crash reporter changes (version bump needed)
if: github.event_name == 'pull_request'
run: |
python3 - <<'EOF'
import json
VERSION_FILE = 'sources/crashreport/StrideCrashReporterVersion.props'
files = [f for page in json.load(open('/tmp/pr-files.json')) for f in page]
def is_shipped(name):
return (name.startswith('sources/crashreport/')
and name != VERSION_FILE # the version itself isn't packaged; a bump is detected below
and not name.startswith(('sources/crashreport/Stride.CrashReport.Tests/',
'sources/crashreport/Stride.CrashReport.TestProbe/'))
and not name.lower().endswith('.md'))
changed = [f['filename'] for f in files if is_shipped(f['filename'])]
bumped = any(f['filename'] == VERSION_FILE
and any(line.startswith('+') and '<StrideCrashReporterVersion' in line
for line in (f.get('patch') or '').split('\n'))
for f in files)
if changed and not bumped:
shown = ', '.join(changed[:5]) + (f' and {len(changed) - 5} more' if len(changed) > 5 else '')
print('::warning title=Crash reporter changed::This PR changes the crash reporter (' + shown + ') '
'without bumping its version. It is versioned separately from the engine: raise '
'StrideCrashReporterVersion in ' + VERSION_FILE + ', or the package is pushed as a '
'duplicate, skipped, and users keep the old reporter.')
EOF
- uses: dorny/paths-filter@v4
if: github.event_name == 'pull_request'
id: filter
with:
filters: |
runtime:
- 'sources/engine/**'
- 'sources/core/**'
- 'sources/shaders/**'
- 'sources/native/**'
- 'sources/shared/**'
- 'sources/sdk/**'
- 'sources/assets/**'
- 'sources/plugins/**'
- 'deps/**'
- 'tests/**'
- 'sources/Stride.Runtime.slnf'
# The workflows of the jobs it gates, their actions and the auditor the ApiNeutral job runs: an
# edit runs them
- '.github/workflows/test-windows-game-api-neutral.yml'
- '.github/workflows/test-windows-game.yml'
- '.github/workflows/test-windows-simple.yml'
- '.github/workflows/test-linux-game.yml'
- '.github/workflows/test-linux-simple.yml'
- '.github/workflows/test-macos-game.yml'
- '.github/actions/setup-vulkan-windows/**'
- '.github/actions/stride-checkout/**'
- '.github/actions/stride-workload/**'
- 'build/tools/ParallelBuildAuditor/**'
ios:
- 'sources/engine/**'
- 'sources/plugins/**'
- 'sources/core/**'
- 'sources/native/**'
- 'sources/shared/**'
- 'sources/sdk/**'
- 'deps/**'
- 'sources/Stride.iOS.slnf'
# Strictly iOS-specific paths: shared engine / MoltenVK / AVFoundation code is
# already covered per-PR by macOS-Tests-Game on the cheaper macOS-vs-iOS surface.
ios-tests:
- 'sources/engine/Stride.Games/**'
- 'sources/**/*.iOS.*'
- 'sources/**/Platforms/iOS/**'
- 'sources/native/**'
- 'sources/sdk/**'
- 'tests/infra/ios/**'
- 'tests/**/iOS.Vulkan/**'
- 'sources/Stride.iOS.slnf'
- '.github/workflows/test-ios-game.yml'
- '.github/actions/stride-checkout/**'
- '.github/actions/stride-workload/**'
android:
- 'sources/engine/Stride.Games/**'
- 'sources/**/*.Android.*'
- 'sources/**/Platforms/Android/**'
- 'sources/native/**'
- 'sources/sdk/**'
- 'tests/infra/android/**'
- 'tests/**/Android.Vulkan/**'
- 'sources/Stride.Tests.Game.Android.slnf'
- '.github/workflows/test-android-game.yml'
- '.github/actions/stride-checkout/**'
- '.github/actions/stride-workload/**'
full:
- 'sources/**'
- 'deps/**'
# Projects of the full build that live outside sources/
- 'tests/infra/Stride.*/**'
- 'tests/enduser/Stride.Templates.Tests/**'
- 'Stride.slnx'