Skip to content

Add reusable workflow for performing rustc-pull on CI #9

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

Merged
merged 1 commit into from
Jul 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .github/workflows/rustc-pull.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: 'Josh Subtree Sync'

on:
workflow_call:
inputs:
branch-name:
description: 'Name of the branch to create for the sync'
required: false
default: rustc-pull
type: string
pr-base-branch:
description: 'Base branch for the pull request'
required: false
type: string
default: 'master'
zulip-stream-id:
description: 'Zulip stream ID for notifications'
required: false
type: string
zulip-topic:
description: 'Zulip topic for notifications'
required: false
type: string
default: 'Subtree sync automation'
zulip-bot-email:
description: 'Zulip bot email address'
required: false
type: string
secrets:
token:
description: 'GITHUB_TOKEN from the caller workflow'
required: true
zulip-api-token:
description: 'Zulip API token for authentication'
required: false

jobs:
perform-pull:
runs-on: ubuntu-latest
outputs:
pr_url: ${{ steps.update-pr.outputs.pr_url }}
pull_result: ${{ steps.josh-sync.outputs.pull_result }}
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '0'

- name: Install stable Rust toolchain
run: rustup update stable

- uses: Swatinem/rust-cache@v2
with:
cache-directories: "/home/runner/.cache/rustc-josh"

- name: Setup bot git name and email
run: |
git config --global user.name 'The rustc-josh-sync Cronjob Bot'
git config --global user.email '[email protected]'

- name: Install rustc-josh-sync
run: cargo install --locked --git https://github.com/rust-lang/josh-sync

- name: Perform josh sync
id: josh-sync
shell: bash {0}
run: |
rustc-josh-sync pull
exitcode=$?

if [ $exitcode -eq 0 ]; then
echo "pull_result=pull-finished" >> $GITHUB_OUTPUT
elif [ $exitcode -eq 2 ]; then
echo "pull_result=skipped" >> $GITHUB_OUTPUT
exitcode=0
else
echo "pull_result=failed" >> $GITHUB_OUTPUT
fi

exit ${exitcode}

- name: Push changes to a branch
if: ${{ steps.josh-sync.outputs.pull_result == 'pull-finished' }}
run: |
BRANCH="${{ inputs.branch-name }}"
git switch -c $BRANCH
git push -u origin $BRANCH --force

- name: Create pull request
id: update-pr
if: ${{ steps.josh-sync.outputs.pull_result == 'pull-finished' }}
env:
GITHUB_TOKEN: ${{ secrets.token }}
run: |
# Check if an open pull request already exists
RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title`
if [[ "$RESULT" -eq 0 ]]; then
echo "Creating new pull request"
PR_URL=`gh pr create -B ${{ inputs.pr-base-branch }} --title 'Rustc pull update' --body 'Latest update from rustc.'`
echo "Created pull request ${PR_URL}"
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
else
PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
echo "Updating pull request ${PR_URL}"
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
fi

send-zulip-notification:
needs: [ perform-pull ]
if: ${{ !cancelled() && inputs.zulip-stream-id != '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Compute message
id: create-message
env:
GITHUB_TOKEN: ${{ secrets.token }}
run: |
if [ "${{ needs.perform-pull.result }}" == "failure" ]; then
WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "message=Josh subtree sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT
else
CREATED_AT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].createdAt' --json createdAt,title`
PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
week_ago=$(date +%F -d '7 days ago')

# If there is an open PR that is at least a week old, post a message about it
if [[ -n $CREATED_AT && $CREATED_AT < $week_ago ]]; then
echo "message=A PR with a josh subtree sync has been opened for more than a week. Check out the [PR](${PR_URL})." >> $GITHUB_OUTPUT
fi
fi

- name: Send Zulip message
if: ${{ steps.create-message.outputs.message != '' }}
uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5
with:
api-key: ${{ secrets.zulip-api-token }}
email: ${{ secrets.zulip-bot-email }}
organization-url: "https://rust-lang.zulipchat.com"
to: ${{ inputs.zulip-stream-id }}
type: "stream"
topic: ${{ inputs.zulip-topic }}
content: ${{ steps.create-message.outputs.message }}
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ A push operation takes changes performed in the subtree repository and merges th
- The branch with the push contents will be created in `https://github.com/<your-github-username>/rust` fork, in the `<branch>` branch.
3) Send a PR to [rust-lang/rust]

## Automating pulls on CI

This repository contains a reusable workflow for performing the `pull` operation from CI. The workflow does the following:

1) Installs `rustc-josh-sync` and `josh`
2) Performs a `pull` operation
3) Either creates a new PR (if it did not exist) with the resulting pull branch or force-pushes to an existing PR on the subtree repository
4) (optional) If a failure (usually a merge conflict) has happened, or a PR has been opened for more than a week without a merge, it posts a message to a Zulip stream

Here is an example of how you can use the workflow in a subtree repository:

```yaml
name: rustc-pull

on:
workflow_dispatch:
schedule:
# Run at 04:00 UTC every Monday and Thursday
- cron: '0 4 * * 1,4'

jobs:
pull:
uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@master
with:
# If you want the Zulip post functionality
#zulip-stream-id: 1234 # optional
#zulip-bot-email: [email protected] # optional
pr-base-branch: master # optional
branch-name: rustc-pull # optional
secrets:
#zulip-api-token: <Zulip API TOKEN> # optional
token: ${{ secrets.GITHUB_TOKEN }}
```

## Git peculiarities

NOTE: If you use Git/SSH protocol to push to your fork of [rust-lang/rust],
Expand All @@ -59,5 +93,4 @@ To minimize the likelihood of this happening, you may wish to keep a separate *m
GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' rustc-josh-sync ...
```


[rust-lang/rust]: (https://github.com/rust-lang/rust)
Loading