Skip to content

Commit 0d1561f

Browse files
authored
Merge pull request #406 from xylar/fix-copilot-instruction-formatting
Fix updating config machines (again)
2 parents fca8ff4 + f64b679 commit 0d1561f

File tree

8 files changed

+154
-18
lines changed

8 files changed

+154
-18
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Follow the repository's automated style configuration in
1010
environment. Prefer `pixi run -e py314 <command>` for `python`, `pytest`,
1111
`pre-commit`, `ruff`, and `mypy`, and check `.pixi/` plus `pixi.toml`
1212
before concluding a tool is missing.
13+
- Do not run `workflow_tests/test_deploy_workflow.py` as part of normal
14+
validation. It is a long-running workflow test and should only be run if
15+
the user explicitly asks for it.
1316
- pre-commit on changed files is required before finishing; if sandboxed
1417
execution fails, request escalation and do not close the task until it has
1518
run or the user declines.

.github/workflows/cime_machine_config_update.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
3838
- name: Generate machine update report
3939
env:
40+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4041
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
4142
run: |
4243
pixi run -e ${PIXI_ENV} python utils/update_cime_machine_config.py \

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ These instructions apply to the whole repository unless a deeper
4343
- Run tests and linting through Pixi unless the task explicitly requires a
4444
different environment.
4545
- Prefer `pixi run -e py314 pytest` for tests.
46+
- Do not run `workflow_tests/test_deploy_workflow.py` during routine
47+
validation unless the user explicitly requests that workflow test.
4648
- pre-commit on changed files is required before finishing; if sandboxed
4749
execution fails, request escalation and do not close the task until it has
4850
run or the user declines.

docs/developers_guide/config_machines_updates.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ The required work section tells Copilot to:
142142

143143
- run `pixi run -e py314 python utils/update_cime_machine_config.py
144144
--work-dir .`,
145+
- if GitHub API access is unavailable, rerun the command with
146+
`--upstream-revision <sha>` when the upstream E3SM commit is already known,
147+
or provide `GITHUB_TOKEN`/`GH_TOKEN` in the environment,
145148
- replace `mache/cime_machine_config/config_machines.xml` with the generated
146149
`upstream_config_machines.xml`,
147150
- remove `upstream_config_machines.xml` before committing,
@@ -231,6 +234,10 @@ pixi run -e py314 python utils/update_cime_machine_config.py \
231234
--markdown-output /tmp/cime_machine_config_report.md
232235
```
233236

237+
If GitHub API access is rate-limited or forbidden in your environment,
238+
rerun with `GITHUB_TOKEN` or `GH_TOKEN` set, or provide
239+
`--upstream-revision <sha>` when the upstream commit hash is already known.
240+
234241
Run the focused tests:
235242

236243
```bash

mache/cime_machine_config/report.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,24 @@ def render_update_issue(report, run_url=None):
133133
'Required work:',
134134
'',
135135
'- Run `pixi run -e py314 python '
136-
'utils/update_cime_machine_config.py --work-dir .` from the',
137-
' repository root.',
138-
'- Replace mache/cime_machine_config/config_machines.xml with',
139-
' upstream_config_machines.xml generated by that command, then',
140-
' remove upstream_config_machines.xml before committing.',
136+
'utils/update_cime_machine_config.py --work-dir . '
137+
'--upstream-revision <sha>` ',
138+
' from the repository root.',
139+
'- Replace `mache/cime_machine_config/config_machines.xml` with',
140+
' `upstream_config_machines.xml` generated by that command, then',
141+
' remove `upstream_config_machines.xml` before committing.',
141142
'- If module or environment changes imply different',
142143
' system-package',
143144
' versions, update the corresponding mache Spack templates and',
144145
' version strings for the affected toolchains.',
145146
'- Look for related Spack template files under',
146-
' mache/spack/templates/<machine>*.yaml,',
147-
' mache/spack/templates/<machine>*.sh, and',
148-
' mache/spack/templates/<machine>*.csh.',
147+
' `mache/spack/templates/<machine>*.yaml`,',
148+
' `mache/spack/templates/<machine>*.sh`, and',
149+
' `mache/spack/templates/<machine>*.csh`.',
149150
'- If the only follow-up is module or version drift, keep the PR',
150151
' focused on those updates.',
151152
'- In the PR summary, state which upstream E3SM commit hash was',
152-
' used for the config_machines.xml replacement.',
153+
' used for the `config_machines.xml` replacement.',
153154
'- If any prefix or path values changed and the correct',
154155
' replacement',
155156
' is not obvious, add a TODO comment in the PR for the reviewer',

tests/test_update_cime_machine_config.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ def raise_for_status(self):
4444
def json(self):
4545
return [{'sha': 'abc123'}]
4646

47-
def fake_get(url, *, params, timeout):
47+
def fake_get(url, *, params, headers, timeout):
4848
captured['url'] = url
4949
captured['params'] = params
50+
captured['headers'] = headers
5051
captured['timeout'] = timeout
5152
return FakeResponse()
5253

@@ -66,4 +67,82 @@ def fake_get(url, *, params, timeout):
6667
'path': 'cime_config/machines/config_machines.xml',
6768
'per_page': 1,
6869
}
70+
assert captured['headers'] == {
71+
'Accept': 'application/vnd.github+json',
72+
'User-Agent': 'mache/update_cime_machine_config',
73+
}
6974
assert captured['timeout'] == 60
75+
76+
77+
def test_get_latest_commit_sha_uses_github_token_when_available(monkeypatch):
78+
update_module = _load_update_module()
79+
captured = {}
80+
81+
class FakeResponse:
82+
def raise_for_status(self):
83+
return None
84+
85+
def json(self):
86+
return [{'sha': 'abc123'}]
87+
88+
def fake_get(url, *, params, headers, timeout):
89+
captured['headers'] = headers
90+
return FakeResponse()
91+
92+
monkeypatch.setenv('GITHUB_TOKEN', 'secret-token')
93+
monkeypatch.setattr(update_module.requests, 'get', fake_get)
94+
95+
sha = update_module._get_latest_commit_sha(
96+
owner='E3SM-Project',
97+
repo='E3SM',
98+
ref='master',
99+
path='cime_config/machines/config_machines.xml',
100+
)
101+
102+
assert sha == 'abc123'
103+
assert captured['headers'] == {
104+
'Accept': 'application/vnd.github+json',
105+
'User-Agent': 'mache/update_cime_machine_config',
106+
'Authorization': 'Bearer secret-token',
107+
}
108+
109+
110+
def test_build_report_uses_manual_upstream_revision(monkeypatch):
111+
update_module = _load_update_module()
112+
113+
monkeypatch.setattr(
114+
update_module,
115+
'_build_report_in_dir',
116+
lambda **kwargs: kwargs['upstream_revision'],
117+
)
118+
119+
report = update_module.build_report(
120+
upstream_url='https://example.com/config_machines.xml',
121+
upstream_revision='deadbeef1234',
122+
work_dir='.',
123+
)
124+
125+
assert report == 'deadbeef1234'
126+
127+
128+
def test_resolve_upstream_revision_ignores_github_api_errors(monkeypatch):
129+
update_module = _load_update_module()
130+
131+
class FakeResponse:
132+
status_code = 403
133+
134+
def fake_get_latest_commit_sha(**kwargs):
135+
raise update_module.requests.HTTPError(response=FakeResponse())
136+
137+
monkeypatch.setattr(
138+
update_module,
139+
'_get_latest_commit_sha',
140+
fake_get_latest_commit_sha,
141+
)
142+
143+
revision = update_module._resolve_upstream_revision(
144+
'https://raw.githubusercontent.com/E3SM-Project/E3SM/'
145+
'refs/heads/master/cime_config/machines/config_machines.xml'
146+
)
147+
148+
assert revision is None

utils/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ When drift is detected, the follow-up PR should replace
1515
from `E3SM-Project/E3SM`. This utility helps confirm the drift and review the
1616
affected supported machines; it does not rewrite the repository copy in place.
1717

18+
If the script cannot resolve the upstream commit from the GitHub API,
19+
provide `GITHUB_TOKEN` or `GH_TOKEN` in the environment, or rerun it with
20+
`--upstream-revision <sha>` when the upstream commit is already known.
21+
1822
The PR should also make the changes associated with the differences that this
1923
utility displays in the appropriate `mache/spack/templates` files.
2024

utils/update_cime_machine_config.py

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import argparse
44
import os
5+
import sys
56
import tempfile
67
from urllib.parse import urlparse
78

@@ -61,11 +62,17 @@ def main():
6162
'--work-dir',
6263
help='Optional directory for temporary XML comparison files.',
6364
)
65+
parser.add_argument(
66+
'--upstream-revision',
67+
help='Optional upstream revision to record in the report. If omitted, '
68+
'the script tries to resolve it from the GitHub API.',
69+
)
6470

6571
args = parser.parse_args()
6672

6773
report = build_report(
6874
upstream_url=args.upstream_url,
75+
upstream_revision=args.upstream_revision,
6976
work_dir=args.work_dir,
7077
)
7178
print_console_report(report)
@@ -80,10 +87,11 @@ def main():
8087
handle.write('\n')
8188

8289

83-
def build_report(*, upstream_url, work_dir=None):
90+
def build_report(*, upstream_url, upstream_revision=None, work_dir=None):
8491
"""Download upstream config and return a structured drift report."""
8592

86-
upstream_revision = _resolve_upstream_revision(upstream_url)
93+
if upstream_revision is None:
94+
upstream_revision = _resolve_upstream_revision(upstream_url)
8795

8896
if work_dir is not None:
8997
return _build_report_in_dir(
@@ -142,12 +150,16 @@ def _resolve_upstream_revision(upstream_url):
142150
return None
143151

144152
owner, repo, ref, path = github_source
145-
return _get_latest_commit_sha(
146-
owner=owner,
147-
repo=repo,
148-
ref=ref,
149-
path=path,
150-
)
153+
try:
154+
return _get_latest_commit_sha(
155+
owner=owner,
156+
repo=repo,
157+
ref=ref,
158+
path=path,
159+
)
160+
except requests.RequestException as exc:
161+
_warn_revision_resolution_failure(exc)
162+
return None
151163

152164

153165
def _parse_github_raw_url(upstream_url):
@@ -179,6 +191,7 @@ def _get_latest_commit_sha(*, owner, repo, ref, path):
179191
'path': path,
180192
'per_page': 1,
181193
},
194+
headers=_build_github_api_headers(),
182195
timeout=60,
183196
)
184197
response.raise_for_status()
@@ -194,5 +207,31 @@ def _get_latest_commit_sha(*, owner, repo, ref, path):
194207
return sha
195208

196209

210+
def _build_github_api_headers():
211+
headers = {
212+
'Accept': 'application/vnd.github+json',
213+
'User-Agent': 'mache/update_cime_machine_config',
214+
}
215+
216+
token = os.getenv('GITHUB_TOKEN') or os.getenv('GH_TOKEN')
217+
if token:
218+
headers['Authorization'] = f'Bearer {token}'
219+
220+
return headers
221+
222+
223+
def _warn_revision_resolution_failure(error):
224+
response = getattr(error, 'response', None)
225+
status_code = getattr(response, 'status_code', None)
226+
status = '' if status_code is None else f' (HTTP {status_code})'
227+
print(
228+
'Warning: could not resolve upstream revision from the GitHub API'
229+
f'{status}; continuing without revision metadata. Set '
230+
'GITHUB_TOKEN or GH_TOKEN, or rerun with --upstream-revision <sha> '
231+
'to record a specific upstream commit.',
232+
file=sys.stderr,
233+
)
234+
235+
197236
if __name__ == '__main__':
198237
main()

0 commit comments

Comments
 (0)