|
| 1 | +# robostack-bot: routine maintenance (see tools/maintenance.py). |
| 2 | +# |
| 3 | +# - weekly: refresh every distribution's rosdistro snapshot (one PR per distribution) |
| 4 | +# and the shared conda-forge pinning (one PR) |
| 5 | +# - on demand (owners, members and collaborators): |
| 6 | +# Actions > robostack-bot > Run workflow, an issue from the "robostack-bot command" |
| 7 | +# template, or a comment: @robostack-bot <command> [<distro>] |
| 8 | +# commands: update-rosdistro-snapshot <distro> | find-stale-packages <distro> | |
| 9 | +# update-conda-forge-pinning |
| 10 | +# |
| 11 | +# PRs are opened with the robostack-bot GitHub App (vars.ROBOSTACK_BOT_APP_ID + |
| 12 | +# secrets.ROBOSTACK_BOT_PRIVATE_KEY, falling back to secrets.GHA_PAT) so CI runs on them. |
| 13 | +name: robostack-bot |
| 14 | + |
| 15 | +on: |
| 16 | + schedule: |
| 17 | + - cron: "17 3 * * 1" |
| 18 | + workflow_dispatch: |
| 19 | + inputs: |
| 20 | + command: |
| 21 | + type: choice |
| 22 | + options: [update-rosdistro-snapshot, find-stale-packages, update-conda-forge-pinning] |
| 23 | + distro: |
| 24 | + description: Distribution (for update-rosdistro-snapshot / find-stale-packages) |
| 25 | + required: false |
| 26 | + issue_comment: |
| 27 | + types: [created] |
| 28 | + issues: |
| 29 | + types: [opened] |
| 30 | + |
| 31 | +permissions: |
| 32 | + contents: read |
| 33 | + |
| 34 | +jobs: |
| 35 | + plan: |
| 36 | + if: >- |
| 37 | + github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || |
| 38 | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@robostack-bot')) || |
| 39 | + (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'robostack-bot')) |
| 40 | + runs-on: ubuntu-latest |
| 41 | + outputs: |
| 42 | + jobs: ${{ steps.plan.outputs.jobs }} |
| 43 | + steps: |
| 44 | + - uses: actions/checkout@v6 |
| 45 | + with: |
| 46 | + persist-credentials: false |
| 47 | + - uses: prefix-dev/setup-pixi@v0.10.2 |
| 48 | + with: |
| 49 | + frozen: true |
| 50 | + pixi-version: v0.75.0 |
| 51 | + - id: plan |
| 52 | + env: |
| 53 | + EVENT: ${{ github.event_name }} |
| 54 | + COMMAND: ${{ inputs.command }} |
| 55 | + DISTRO: ${{ inputs.distro }} |
| 56 | + BODY: ${{ github.event.comment.body || github.event.issue.body }} |
| 57 | + ASSOCIATION: ${{ github.event.comment.author_association || github.event.issue.author_association }} |
| 58 | + run: | |
| 59 | + case "$EVENT" in |
| 60 | + schedule) |
| 61 | + jobs=$(pixi run -q python tools/robostack.py changed-distros --base HEAD --all \ |
| 62 | + | python3 -c 'import json,sys; print(json.dumps([{"command":"update-rosdistro-snapshot","distro":d} for d in json.load(sys.stdin)] + [{"command":"update-conda-forge-pinning","distro":""}]))') ;; |
| 63 | + workflow_dispatch) |
| 64 | + jobs=$(python3 -c 'import json,os; print(json.dumps([{"command":os.environ["COMMAND"],"distro":os.environ["DISTRO"]}]))') ;; |
| 65 | + *) |
| 66 | + parsed=$(pixi run -q rs parse-command --body "$BODY" --association "$ASSOCIATION") |
| 67 | + jobs=$(echo "$parsed" | python3 -c 'import json,sys; c=json.load(sys.stdin); print(json.dumps([c] if c["command"] else []))') ;; |
| 68 | + esac |
| 69 | + echo "jobs=$jobs" | tee -a "$GITHUB_OUTPUT" |
| 70 | +
|
| 71 | + run: |
| 72 | + needs: plan |
| 73 | + if: needs.plan.outputs.jobs != '[]' |
| 74 | + runs-on: ubuntu-latest |
| 75 | + strategy: |
| 76 | + fail-fast: false |
| 77 | + matrix: |
| 78 | + include: ${{ fromJSON(needs.plan.outputs.jobs) }} |
| 79 | + permissions: |
| 80 | + contents: write |
| 81 | + pull-requests: write |
| 82 | + issues: write |
| 83 | + steps: |
| 84 | + - name: Bot token |
| 85 | + id: app |
| 86 | + if: vars.ROBOSTACK_BOT_APP_ID != '' |
| 87 | + uses: actions/create-github-app-token@v2 |
| 88 | + with: |
| 89 | + app-id: ${{ vars.ROBOSTACK_BOT_APP_ID }} |
| 90 | + private-key: ${{ secrets.ROBOSTACK_BOT_PRIVATE_KEY }} |
| 91 | + - uses: actions/checkout@v6 |
| 92 | + with: |
| 93 | + token: ${{ steps.app.outputs.token || secrets.GHA_PAT || github.token }} |
| 94 | + - uses: prefix-dev/setup-pixi@v0.10.2 |
| 95 | + with: |
| 96 | + frozen: true |
| 97 | + pixi-version: v0.75.0 |
| 98 | + - name: robostack-bot ${{ matrix.command }} ${{ matrix.distro }} |
| 99 | + id: bot |
| 100 | + env: |
| 101 | + COMMAND: ${{ matrix.command }} |
| 102 | + DISTRO: ${{ matrix.distro }} |
| 103 | + GITHUB_TOKEN: ${{ github.token }} |
| 104 | + run: | |
| 105 | + set +e |
| 106 | + case "$COMMAND" in |
| 107 | + update-rosdistro-snapshot) pixi run rs "$DISTRO" update-snapshot --summary "$RUNNER_TEMP/summary.md" ;; |
| 108 | + find-stale-packages) pixi run rs "$DISTRO" find-stale --summary "$RUNNER_TEMP/summary.md" ;; |
| 109 | + update-conda-forge-pinning) pixi run rs update-pinning --summary "$RUNNER_TEMP/summary.md" ;; |
| 110 | + esac |
| 111 | + status=$? |
| 112 | + cat "$RUNNER_TEMP/summary.md" >> "$GITHUB_STEP_SUMMARY" 2>/dev/null |
| 113 | + exit $status |
| 114 | + - name: Open or update the pull request |
| 115 | + if: steps.bot.outputs.changed == 'true' |
| 116 | + uses: peter-evans/create-pull-request@v7 |
| 117 | + with: |
| 118 | + token: ${{ steps.app.outputs.token || secrets.GHA_PAT || github.token }} |
| 119 | + branch: bot/${{ matrix.command }}${{ matrix.distro && format('-{0}', matrix.distro) || '' }} |
| 120 | + delete-branch: true |
| 121 | + commit-message: ${{ steps.bot.outputs.title }} |
| 122 | + title: ${{ steps.bot.outputs.title }} |
| 123 | + body-path: ${{ runner.temp }}/summary.md |
| 124 | + labels: robostack-bot |
| 125 | + - name: Reply on the issue / pull request |
| 126 | + if: always() && (github.event_name == 'issue_comment' || github.event_name == 'issues') |
| 127 | + env: |
| 128 | + GH_TOKEN: ${{ github.token }} |
| 129 | + NUMBER: ${{ github.event.issue.number }} |
| 130 | + TITLE: ${{ steps.bot.outputs.title || 'failed' }} |
| 131 | + run: | |
| 132 | + { |
| 133 | + echo "**robostack-bot ${{ matrix.command }} ${{ matrix.distro }}**: $TITLE" |
| 134 | + echo |
| 135 | + [ -f "$RUNNER_TEMP/summary.md" ] && cat "$RUNNER_TEMP/summary.md" |
| 136 | + echo |
| 137 | + echo "[Workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)" |
| 138 | + } > "$RUNNER_TEMP/comment.md" |
| 139 | + gh issue comment "$NUMBER" --repo "$GITHUB_REPOSITORY" --body-file "$RUNNER_TEMP/comment.md" |
| 140 | + if [ "$GITHUB_EVENT_NAME" = issues ] && [ "${{ job.status }}" = success ]; then |
| 141 | + gh issue close "$NUMBER" --repo "$GITHUB_REPOSITORY" |
| 142 | + fi |
0 commit comments