-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Ensure updates are pushing to other orgs and repos #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c2d9d7c
cde310d
330d352
caa4bce
46f5a4b
2f7efb2
2722283
217ed3a
4b38457
5b82d34
a360f58
4cb9cfb
6e24632
fce6ef6
779f03e
4fda558
541d59b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,132 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Auto-merge PRs after CI passes | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Automatically merges approved PRs to main once all checks pass | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Auto Merge | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pull_request_review: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| types: [submitted] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflow_run: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflows: ["CI"] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| types: [completed] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auto-merge: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Auto Merge PR | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Only run on approved PRs targeting main | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: | | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+22
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: write | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pull-requests: write | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checks: read | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/checkout@v4 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Get PR info | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: pr | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Get PR number from the event | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "${{ github.event_name }}" == "pull_request_review" ]; then | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PR_NUMBER="${{ github.event.pull_request.number }}" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [ "${{ github.event_name }}" == "workflow_run" ]; then | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Extract PR number from workflow run | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PR_NUMBER=$(gh pr list --json number,headRefName --jq '.[] | select(.headRefName=="${{ github.event.workflow_run.head_branch }}") | .number' | head -1) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Code injection Critical
Potential code injection in
${ github.event.workflow_run.head_branch } Error loading related location Loading workflow_run Error loading related location Loading
Copilot AutofixAI about 10 hours ago In general, to fix this type of problem in GitHub Actions, you should avoid using For this specific workflow, the risky part is line 41: PR_NUMBER=$(gh pr list --json number,headRefName --jq '.[] | select(.headRefName=="${{ github.event.workflow_run.head_branch }}") | .number' | head -1)The best fix that preserves existing functionality is:
Concretely:
All changes are within
Suggested changeset
1
.github/workflows/auto-merge.yml
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
Comment on lines
+40
to
+41
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Extract PR number from workflow run | |
| PR_NUMBER=$(gh pr list --json number,headRefName --jq '.[] | select(.headRefName=="${{ github.event.workflow_run.head_branch }}") | .number' | head -1) | |
| # Extract PR number directly from workflow_run payload | |
| if [ -n "${{ github.event.workflow_run.pull_requests[0].number }}" ]; then | |
| PR_NUMBER="${{ github.event.workflow_run.pull_requests[0].number }}" | |
| else | |
| echo "Workflow run is not associated with a pull request." | |
| exit 1 | |
| fi |
Check failure
Code scanning / CodeQL
Code injection Critical
${ steps.pr.outputs.state }
pull_request_review
Potential code injection in
${ steps.pr.outputs.state }
workflow_run
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 10 hours ago
To fix this, we should stop using GitHub expression syntax (${{ ... }}) directly inside the shell script body for values that originate from workflow inputs/outputs. Instead, we should bind those values to environment variables at the step level (env:) and then reference them using standard shell variable expansion (e.g., $STATE) inside the script. This aligns with GitHub’s guidance and breaks the taint flow into the shell command parser.
Concretely, for the “Check merge conditions” step (lines 67–87), we will:
- Add an
env:section that mapsSTATE,MERGEABLE, andREVIEW_DECISIONto${{ steps.pr.outputs.state }},${{ steps.pr.outputs.mergeable }}, and${{ steps.pr.outputs.review_decision }}respectively. - Simplify the
run:script so it relies solely on$STATE,$MERGEABLE, and$REVIEW_DECISION, removing the inline${{ ... }}assignments in the script body.
No other steps need modification to address this particular alert, and this change preserves existing behavior: the variables in the script will contain exactly the same values as before, but are populated in a safer way by the runner rather than the shell parsing interpolated expressions.
-
Copy modified lines R69-R72
| @@ -66,11 +66,11 @@ | ||
|
|
||
| - name: Check merge conditions | ||
| id: check | ||
| env: | ||
| STATE: ${{ steps.pr.outputs.state }} | ||
| MERGEABLE: ${{ steps.pr.outputs.mergeable }} | ||
| REVIEW_DECISION: ${{ steps.pr.outputs.review_decision }} | ||
| run: | | ||
| STATE="${{ steps.pr.outputs.state }}" | ||
| MERGEABLE="${{ steps.pr.outputs.mergeable }}" | ||
| REVIEW_DECISION="${{ steps.pr.outputs.review_decision }}" | ||
|
|
||
| echo "PR State: $STATE" | ||
| echo "Mergeable: $MERGEABLE" | ||
| echo "Review Decision: $REVIEW_DECISION" |
Check failure
Code scanning / CodeQL
Code injection Critical
${ steps.pr.outputs.mergeable }
pull_request_review
Potential code injection in
${ steps.pr.outputs.mergeable }
workflow_run
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 10 hours ago
In general, to fix this class of problems in GitHub Actions, any untrusted or potentially untrusted data that needs to be used in a shell script should be assigned to an environment variable at the workflow level (using ${{ ... }} only in the env: mapping), and then referenced inside the script using the shell’s own variable syntax ($VAR), not ${{ env.VAR }} or similar expression syntax.
For this workflow, the problematic use is in the “Check merge conditions” step, where the script begins with:
run: |
STATE="${{ steps.pr.outputs.state }}"
MERGEABLE="${{ steps.pr.outputs.mergeable }}"
REVIEW_DECISION="${{ steps.pr.outputs.review_decision }}"The safest fix, without altering behavior, is:
- Add an
env:section to that step, mapping:STATE: ${{ steps.pr.outputs.state }}MERGEABLE: ${{ steps.pr.outputs.mergeable }}REVIEW_DECISION: ${{ steps.pr.outputs.review_decision }}
- Remove the three assignment lines inside the
run:block and instead rely on those environment variables directly ($STATE,$MERGEABLE,$REVIEW_DECISION) everywhere in the script, which is already the case for subsequent references.
This keeps functionality identical (the variables have the same names and contents) while complying with the recommended pattern: untrusted values are injected only into environment variables via expressions, and the shell script reads them with native syntax. No additional imports or external dependencies are required, and no other steps need to change.
-
Copy modified lines R69-R72
| @@ -66,11 +66,11 @@ | ||
|
|
||
| - name: Check merge conditions | ||
| id: check | ||
| env: | ||
| STATE: ${{ steps.pr.outputs.state }} | ||
| MERGEABLE: ${{ steps.pr.outputs.mergeable }} | ||
| REVIEW_DECISION: ${{ steps.pr.outputs.review_decision }} | ||
| run: | | ||
| STATE="${{ steps.pr.outputs.state }}" | ||
| MERGEABLE="${{ steps.pr.outputs.mergeable }}" | ||
| REVIEW_DECISION="${{ steps.pr.outputs.review_decision }}" | ||
|
|
||
| echo "PR State: $STATE" | ||
| echo "Mergeable: $MERGEABLE" | ||
| echo "Review Decision: $REVIEW_DECISION" |
Check failure
Code scanning / CodeQL
Code injection Critical
${ steps.pr.outputs.review_decision }
pull_request_review
Potential code injection in
${ steps.pr.outputs.review_decision }
workflow_run
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 10 hours ago
General approach: Avoid using ${{ steps.pr.outputs.review_decision }} directly inside the run: script. Instead, pass the value into the step via env: and use standard shell variable expansion ($REVIEW_DECISION) within the script. This aligns with the recommended pattern of copying untrusted expressions into environment variables and only using native shell syntax in the command body.
Concrete fix for this workflow:
-
In the
Check merge conditionsstep (lines 67–96), add anenv:section that maps a new environment variable (e.g.,STATE,MERGEABLE,REVIEW_DECISION) from the step outputs:
REVIEW_DECISION: ${{ steps.pr.outputs.review_decision }}. -
In the
run:script of that same step, stop reassigning these variables using${{ ... }}. Instead, rely on the environment variables already populated byenv:. So remove or simplify the lines:STATE="${{ steps.pr.outputs.state }}" MERGEABLE="${{ steps.pr.outputs.mergeable }}" REVIEW_DECISION="${{ steps.pr.outputs.review_decision }}"
and use
$STATE,$MERGEABLE, and$REVIEW_DECISIONdirectly. -
No other steps need to change, because the CodeQL alert is specific to
${{ steps.pr.outputs.review_decision }}at line 72.
This preserves behavior (the logic still checks the same values) while ensuring no GitHub Actions expressions are present inside the actual shell script body.
-
Copy modified lines R69-R72
| @@ -66,11 +66,11 @@ | ||
|
|
||
| - name: Check merge conditions | ||
| id: check | ||
| env: | ||
| STATE: ${{ steps.pr.outputs.state }} | ||
| MERGEABLE: ${{ steps.pr.outputs.mergeable }} | ||
| REVIEW_DECISION: ${{ steps.pr.outputs.review_decision }} | ||
| run: | | ||
| STATE="${{ steps.pr.outputs.state }}" | ||
| MERGEABLE="${{ steps.pr.outputs.mergeable }}" | ||
| REVIEW_DECISION="${{ steps.pr.outputs.review_decision }}" | ||
|
|
||
| echo "PR State: $STATE" | ||
| echo "Mergeable: $MERGEABLE" | ||
| echo "Review Decision: $REVIEW_DECISION" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enforce base branch before auto-merge
The merge gate only checks state, mergeable, and reviewDecision and never verifies the PR’s base branch. Because CI also runs on develop, an approved PR targeting develop will satisfy these checks and be auto-merged, despite the workflow’s stated intent to merge only to main. Add a base-branch condition (e.g., github.event.pull_request.base.ref == 'main' or equivalent for workflow_run) to prevent unintended merges to non-main branches.
Useful? React with 👍 / 👎.
Check failure
Code scanning / CodeQL
Code injection Critical
${ steps.pr.outputs.state }
pull_request_review
Potential code injection in
${ steps.pr.outputs.state }
workflow_run
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 10 hours ago
General fix approach: avoid using ${{ ... }} directly inside shell scripts for values derived from external data. Instead, assign those expression values to environment variables in the step configuration (env:) and then reference them in the script using the shell’s native $VAR syntax.
Best concrete fix here: in the Summary step, move all ${{ steps.* }} and ${{ job.status }} references out of the run: body into env: variables, then reference them with $VAR inside the script. This removes the vulnerable pattern and also addresses any related variants (not only state but pr_number, can_merge, job.status) in one place.
Specifically, in .github/workflows/auto-merge.yml:
- For the
Summarystep (lines 124–132), add anenv:section with variables likeSUMMARY_PR_NUMBER,SUMMARY_STATE,SUMMARY_CAN_MERGE, andSUMMARY_JOB_STATUS, each assigned from the corresponding${{ ... }}expression. - Update the
run:block to echo using$SUMMARY_PR_NUMBER,$SUMMARY_STATE,$SUMMARY_CAN_MERGE, and$SUMMARY_JOB_STATUSinstead of interpolated expressions.
No new methods or imports are required; this is purely a YAML/workflow configuration change.
-
Copy modified lines R126-R130 -
Copy modified lines R134-R137
| @@ -123,10 +123,15 @@ | ||
|
|
||
| - name: Summary | ||
| if: always() | ||
| env: | ||
| SUMMARY_PR_NUMBER: ${{ steps.pr.outputs.pr_number }} | ||
| SUMMARY_STATE: ${{ steps.pr.outputs.state }} | ||
| SUMMARY_CAN_MERGE: ${{ steps.check.outputs.can_merge }} | ||
| SUMMARY_JOB_STATUS: ${{ job.status }} | ||
| run: | | ||
| echo "🎯 Auto-merge Summary" | ||
| echo "" | ||
| echo "PR: #${{ steps.pr.outputs.pr_number }}" | ||
| echo "State: ${{ steps.pr.outputs.state }}" | ||
| echo "Can merge: ${{ steps.check.outputs.can_merge }}" | ||
| echo "Status: ${{ job.status }}" | ||
| echo "PR: #$SUMMARY_PR_NUMBER" | ||
| echo "State: $SUMMARY_STATE" | ||
| echo "Can merge: $SUMMARY_CAN_MERGE" | ||
| echo "Status: $SUMMARY_JOB_STATUS" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,197 @@ | ||||||
| # Sync shared workflows and configs to other org repos | ||||||
| # This workflow pushes templates and shared files to target organizations | ||||||
|
|
||||||
| name: Sync to Orgs | ||||||
|
|
||||||
| on: | ||||||
| push: | ||||||
| branches: [main] | ||||||
| paths: | ||||||
| - 'templates/**' | ||||||
| - '.github/workflows/**' | ||||||
blackboxprogramming marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| - '!.github/workflows/sync-to-orgs.yml' | ||||||
| - 'routes/registry.yaml' | ||||||
| workflow_dispatch: | ||||||
| inputs: | ||||||
| target_orgs: | ||||||
| description: 'Target orgs (comma-separated, or "all")' | ||||||
| required: false | ||||||
| default: 'all' | ||||||
| type: string | ||||||
| dry_run: | ||||||
| description: 'Dry run (test without pushing)' | ||||||
| required: false | ||||||
| type: boolean | ||||||
| default: false | ||||||
|
|
||||||
| jobs: | ||||||
| sync: | ||||||
| name: Sync to Organizations | ||||||
| runs-on: ubuntu-latest | ||||||
|
|
||||||
| permissions: | ||||||
| contents: read | ||||||
|
|
||||||
| steps: | ||||||
| - uses: actions/checkout@v4 | ||||||
| with: | ||||||
| fetch-depth: 1 | ||||||
|
|
||||||
| - name: Set up Python | ||||||
| uses: actions/setup-python@v5 | ||||||
| with: | ||||||
| python-version: '3.11' | ||||||
|
|
||||||
| - name: Install dependencies | ||||||
| run: pip install pyyaml requests | ||||||
|
|
||||||
| - name: Load registry | ||||||
| id: registry | ||||||
| run: | | ||||||
| python -c " | ||||||
| import yaml | ||||||
| import json | ||||||
|
|
||||||
| with open('routes/registry.yaml') as f: | ||||||
| registry = yaml.safe_load(f) | ||||||
|
|
||||||
| # Extract active orgs | ||||||
| orgs = [] | ||||||
| for code, org in registry.get('orgs', {}).items(): | ||||||
| if org.get('status') == 'active': | ||||||
| orgs.append({ | ||||||
| 'code': code, | ||||||
| 'name': org['name'], | ||||||
| 'github': org['github'], | ||||||
| 'repos': org.get('repos', []) | ||||||
| }) | ||||||
|
|
||||||
| print(f'Found {len(orgs)} active orgs') | ||||||
| for org in orgs: | ||||||
| print(f' - {org[\"code\"]}: {org[\"name\"]}') | ||||||
|
|
||||||
| # Output for next steps | ||||||
| with open('$GITHUB_OUTPUT', 'a') as f: | ||||||
| f.write(f'orgs={json.dumps(orgs)}\\n') | ||||||
|
Comment on lines
+73
to
+75
|
||||||
| " | ||||||
|
|
||||||
| - name: Dispatch to target orgs | ||||||
| env: | ||||||
| GITHUB_TOKEN: ${{ secrets.DISPATCH_TOKEN || secrets.GITHUB_TOKEN }} | ||||||
|
||||||
| GITHUB_TOKEN: ${{ secrets.DISPATCH_TOKEN || secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.DISPATCH_TOKEN }} |
Copilot
AI
Feb 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dispatch script collects failures but never exits non-zero when dispatches fail, so the workflow can report success even when nothing was synced. Consider failing the job when failures is non-empty (and potentially exempting dry_run) so broken dispatches surface immediately.
Uh oh!
There was an error while loading. Please reload this page.