Skip to content

ci: add generate_workflows.py --sync-registry to adopt Dependabot action bumps - #4259

Open
AlgoVoi (Christopher Hopley) (chopmob-cloud) wants to merge 1 commit into
microsoft:mainfrom
chopmob-cloud:ci/dependabot-registry-sync
Open

AlgoVoi (Christopher Hopley) (chopmob-cloud) wants to merge 1 commit into
microsoft:mainfrom
chopmob-cloud:ci/dependabot-registry-sync

Conversation

@chopmob-cloud

Copy link
Copy Markdown
Contributor

Related Issue

Related to #3638. Implements option 3 (teach the generator to run the other direction: read the bumped pin out of the generated file and update the registry).

Problem & Solution

Problem: Dependabot can edit a pinned action in the generated .github/workflows/policy-engine-ci.yml but cannot edit its source of truth .github/ci/actions.toml, so an actions major leaves the generated file drifting from the registry and generate_workflows.py --check fails. Four such PRs (#3549, #3598, #3599, #3633) each needed a maintainer to hand-sync the registry and regenerate.

Solution: add a --sync-registry mode to scripts/ci/generate_workflows.py. It reads the bumped pin back out of the managed files (the generated workflow outputs plus the hand-authored composite actions under .github/actions, which by the registry's own contract reference every registered action), updates actions.toml to match, then regenerates. This is the deterministic equivalent of the manual sync, so the recurring step becomes python3 scripts/ci/generate_workflows.py --sync-registry on a Dependabot actions PR.

It fails closed: if two managed files disagree on the pin for the same action it raises rather than guessing, and it is a no-op when the registry already matches. Only managed files drive the registry, so an unrelated hand-authored workflow cannot. The registry rewrite is line-based and leaves the file's header comments, blank lines, and other entries byte for byte intact.

Path safety: because this mode both reads the managed files and now writes actions.toml, it validates that every path it touches stays inside the tree. Generated output paths must resolve beneath the repo root, composite action files must resolve beneath .github/actions, adopted version comments must look like a tag before they are quoted into TOML, and a symlinked registry file, composite directory, or composite action file is refused. The real tree has none of these, so normal --write and --check behaviour is unchanged; the checks harden the file I/O the generator was already doing.

This intentionally does not add a workflow that auto-commits onto Dependabot branches. That would need a pull_request_target-class trigger with write access, which is a larger security surface and a separate decision. The deterministic script lands first; I am happy to follow up with the auto-commit workflow (option 2) on top of it if you want it.

Impact on Your Work

Removes the manual registry-sync commit on every GitHub-Actions major. No change to --write or --check behaviour on a clean tree.

Alternatives Considered

  • Option 1 (exclude the generated workflow from Dependabot / ignore the pins): Dependabot cannot exclude a single file, and ignoring the pins stops it proposing action updates at all, which loses the coverage the registry exists to track.
  • Option 2 (auto-commit workflow): deferred as above; it builds on this script and carries a separate security review.

Type of Change

  • Maintenance (dependency updates, CI/CD, refactoring)

Package(s) Affected

  • policy-engine

Testing

Unit Testing

Hermetic tests added to tests/ci/test_generate_workflows.py covering: adopting a bumped pin, no-op when in sync, fail-closed on conflicting pins, registry-layout preservation, unsafe-comment rejection, output-path traversal, and symlinked registry / composite directory / composite files (escaping and in-tree). pytest tests/ci -q is green (119 tests).

Manual Testing

On a clean checkout where --check passes, simulated a Dependabot bump by changing the actions/checkout SHA and # v comment across every managed file, confirmed --check fails on drift, ran --sync-registry, confirmed actions.toml adopted the new pin and --check passed again, then restored the tree. ruff check passes on both files.

Checklist

  • I have linked a related issue above, or completed "Problem & Solution", "Impact on Your Work", and "Alternatives Considered"
  • My code follows the project style guidelines (ruff check) - ruff check passes; ruff format is not applied to scripts/ci (the pristine file predates it), so the new code matches the surrounding style
  • I have added tests that prove my fix/feature works
  • All new and existing tests pass (pytest)
  • I have updated documentation as needed - the module docstring documents the new mode
  • I have signed the Microsoft CLA

Attribution & Prior Art

  • This contribution does not contain code copied or derived from other projects without attribution
  • Any external projects that inspired this design are credited in code comments or documentation
  • If this PR implements functionality similar to an existing open-source project, I have listed it below

Prior art / related projects: none; this extends the repo's own generate_workflows.py.

AI Assistance

  • I can explain every meaningful change in this PR: what it does, why, and what tradeoffs were considered
  • I have run tests and verification appropriate for this change
  • No part of this PR was autonomously submitted by an AI agent without my review

If AI tools materially shaped this change: drafted with AI assistance and independently validated by running the tests and the end-to-end sync on a Linux VM; every change was reviewed before submission.

IP, Patents, and Licensing

  • This contribution does not implement patent-pending or patent-encumbered techniques
  • This contribution does not require an NDA or licensing agreement to understand or use
  • Any AI tools used have terms compatible with the MIT License

…ion bumps

Dependabot can bump a pinned action in the generated policy-engine-ci.yml but
cannot edit .github/ci/actions.toml, so the generated file drifts from the
registry and --check fails until a maintainer hand-syncs it. Add a
--sync-registry mode that reads the bumped pin out of the managed files (the
generated workflow outputs plus the composite actions), updates actions.toml,
and regenerates. It fails closed on conflicting pins, is a no-op when already
in sync, preserves the registry file layout, and validates that every path it
reads or writes stays within the tree (refusing traversal and symlink escapes).
Adds hermetic tests for all of these.

Implements option 3 of microsoft#3638.

Signed-off-by: AlgoVoi <chopmob@gmail.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions github-actions Bot added tests size/L Large PR (< 500 lines) labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

PR Review Summary

Check Status Details
🔍 Code Review ⚠️ Missing No current-run comment
🛡️ Security Scan ⚠️ Missing No current-run comment
🔄 Breaking Changes ⚠️ Missing No current-run comment
📝 Docs Sync ⚠️ Missing No current-run comment
🧪 Test Coverage ⚠️ Missing No current-run comment

Verdict: ⚠️ AI review incomplete; ready for human review

AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims.

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

🔴 Contributor Check: HIGH

Check Result
Profile HIGH
Credential LOW
Overall HIGH

Automated check by AGT Contributor Check.

@github-actions github-actions Bot added the needs-review:HIGH Contributor reputation check flagged HIGH risk label Oct 6, 2026
if not match:
continue
ref = match.group("ref")
key = key_by_name.get(ref.split("@", 1)[0])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scripts/ci/generate_workflows.py:317 The sync adopts any 40-hex SHA for a registered action with no check that it resolves to the version in the trailing comment: replacing actions/checkout's SHA with forty f's and # v9.9.9 in the managed files, --sync-registry wrote it into actions.toml and --check then passed. The registry exists to keep pins honest, and this turns adopting a pin into one command, so please verify before writing: git ls-remote https://github.com/<owner>/<repo> refs/tags/<comment> (and refs/tags/<comment>^{} for annotated tags) must equal the SHA, with a clear error otherwise. An explicit --no-verify for offline use is fine if the output says the pin was not verified.

except (OSError, UnicodeError) as exc:
raise GenerationError(f"cannot read {rel}: {exc}") from exc
for line in text.splitlines():
match = _PIN_LINE_RE.search(line)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scripts/ci/generate_workflows.py:313 _PIN_LINE_RE.search is unanchored, so a commented-out YAML line such as # - uses: actions/setup-python@<sha> # v9.0.0 counts as a pin source (it fails closed today only because the live pin in the same file conflicts). Please anchor it to a step line, for example ^\s*-?\s*uses:.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:HIGH Contributor reputation check flagged HIGH risk size/L Large PR (< 500 lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants