Skip to content

sync-team dry-run

sync-team dry-run #17

Workflow file for this run

# This workflow executes a dry-run of the sync-team tool after a push to any pull request.
# This allows us to see which changes would be applied to live services after the PR
# would be merged.
#
# The workflow uses the `workflow_run` trigger, which should always run in the default branch of
# this repository. This is required so that the workflow has permissions to post PR comments.
# We should not check out any code from the PR, as it could present a security hazard.
# Instead, we simply download a GitHub artifact with a directory of JSON files and use that as
# input for sync-team.
# This artifact is created and uploaded on PR pushes using the CI workflow in `main.yml`.
# Details about `workflow_run`:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run:
name: sync-team dry-run
on:
workflow_run:
workflows: [ CI ]
types:
- completed
jobs:
dry-run:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
concurrency:
# Only run this once at a time on any given PR
group: dry-run-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
repository: rust-lang/sync-team
persist-credentials: false
- name: Install Rust Stable
run: |
rustc -vV
rustup update stable
rustup default stable
rustc -vV
# Pinning a specific version to avoid surprises
- uses: Swatinem/rust-cache@27b8ea9368cf428f0bfe41b0876b1a7e809d9844
- name: Download built JSON API
uses: actions/download-artifact@v4
with:
name: team-api-output
path: team-api
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# GitHub tokens generated from GitHub Apps can access resources from one organization,
# so we need to generate a token for each organization.
- name: Generate GitHub token (rust-lang)
uses: actions/create-github-app-token@v1
id: rust-lang-token
with:
# GitHub App ID secret name
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
# GitHub App private key secret name
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
# Set the owner, so the token can be used in all repositories
owner: rust-lang
- name: Generate GitHub token (rust-lang-ci)
uses: actions/create-github-app-token@v1
id: rust-lang-ci-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-lang-ci
- name: Generate GitHub token (rust-lang-deprecated)
uses: actions/create-github-app-token@v1
id: rust-lang-deprecated-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-lang-deprecated
- name: Generate GitHub token (rust-lang-nursery)
uses: actions/create-github-app-token@v1
id: rust-lang-nursery-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-lang-nursery
- name: Generate GitHub token (bors-rs)
uses: actions/create-github-app-token@v1
id: bors-rs-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: bors-rs
- name: Generate GitHub token (rust-analyzer)
uses: actions/create-github-app-token@v1
id: rust-analyzer-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-analyzer
- name: Generate GitHub token (rust-embedded)
uses: actions/create-github-app-token@v1
id: rust-embedded-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-embedded
- name: Generate GitHub token (rust-dev-tools)
uses: actions/create-github-app-token@v1
id: rust-dev-tools-token
with:
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
owner: rust-dev-tools
- name: Run sync-team dry-run check
env:
GITHUB_TOKEN_RUST_LANG: ${{ steps.rust-lang-token.outputs.token }}
GITHUB_TOKEN_RUST_LANG_CI: ${{ steps.rust-lang-ci-token.outputs.token }}
GITHUB_TOKEN_RUST_LANG_DEPRECATED: ${{ steps.rust-lang-deprecated-token.outputs.token }}
GITHUB_TOKEN_RUST_LANG_NURSERY: ${{ steps.rust-lang-nursery-token.outputs.token }}
GITHUB_TOKEN_BORS_RS: ${{ steps.bors-rs-token.outputs.token }}
GITHUB_TOKEN_RUST_ANALYZER: ${{ steps.rust-analyzer-token.outputs.token }}
GITHUB_TOKEN_RUST_EMBEDDED: ${{ steps.rust-embedded-token.outputs.token }}
GITHUB_TOKEN_RUST_DEV_TOOLS: ${{ steps.rust-dev-tools-token.outputs.token }}
run: |
# Perform build and execution separately to avoid any potential output from
# cargo leaking into the output file.
cargo build --release
./target/release/sync-team print-plan --services github --team-json team-api > output.txt
- name: Prepare comment
run: |
cat > comment.txt << EOL
<details>
<summary>Dry-run check results</summary>
<pre><code>
EOL
cat output.txt >> comment.txt
printf "</pre></code>\n</details>\n" >> comment.txt
cat comment.txt
- name: Extract PR number
run: |
# We read the PR number that is stored in the uploaded archive
# and check that it is an integer (as the workflow could upload whatever it wants).
UNSANITIZED_PR=`cat team-api/pr.txt`
if [[ ${UNSANITIZED_PR} =~ ^[0-9]+$ ]]; then
echo "PR_NUMBER=${UNSANITIZED_PR}" >> $GITHUB_ENV
else
echo "Invalid PR number passed: ${UNSANITIZED_PR}"
exit 1
fi
- name: Send comment
env:
GH_TOKEN: ${{ github.token }}
run: |
PR=${PR_NUMBER}
echo "Pull request ${PR}"
# --edit-last doesn't work if there is no previous comment, so we have to figure out
# if we should create a comment or not
if gh issue view ${PR} --repo rust-lang/team --json comments \
--jq '.comments.[].author.login' | grep --quiet --fixed-strings "github-actions"; then
echo "Editing comment"
gh pr comment ${PR} --repo rust-lang/team --body-file comment.txt --edit-last
else
echo "Creating new comment"
gh pr comment ${PR} --repo rust-lang/team --body-file comment.txt
fi