β¬οΈ Update winboat: v0.1.0 β v0.9.0 #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Bot | |
| on: | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| actions: write | |
| attestations: write | |
| id-token: write | |
| packages: write | |
| jobs: | |
| handle-command: | |
| # Only run on PR comments (not issue comments) and from authorized users | |
| if: | | |
| github.event.issue.pull_request && | |
| startsWith(github.event.comment.body, '/') && | |
| ( | |
| github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'COLLABORATOR' | |
| ) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Get PR info | |
| id: pr-info | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}) | |
| echo "head_sha=$(echo "$PR_DATA" | jq -r '.head.sha')" >> $GITHUB_OUTPUT | |
| echo "head_ref=$(echo "$PR_DATA" | jq -r '.head.ref')" >> $GITHUB_OUTPUT | |
| echo "base_ref=$(echo "$PR_DATA" | jq -r '.base.ref')" >> $GITHUB_OUTPUT | |
| - name: Parse command | |
| id: parse | |
| run: | | |
| COMMENT="${{ github.event.comment.body }}" | |
| COMMAND=$(echo "$COMMENT" | head -1 | awk '{print $1}' | tr -d '/') | |
| ARGS=$(echo "$COMMENT" | head -1 | cut -d' ' -f2- -s) | |
| echo "command=$COMMAND" >> $GITHUB_OUTPUT | |
| echo "args=$ARGS" >> $GITHUB_OUTPUT | |
| echo "Parsed command: $COMMAND, args: $ARGS" | |
| - name: React to comment | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ | |
| -f content='eyes' || true | |
| - name: Handle /help | |
| if: steps.parse.outputs.command == 'help' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| HELP_MSG=$(cat << 'EOF' | |
| ## π€ PR Bot Commands | |
| | Command | Description | | |
| |---------|-------------| | |
| | `/help` | Show this help message | | |
| | `/build` | Build all changed recipes in this PR | | |
| | `/build x86_64` | Build for specific host (x86_64, aarch64, riscv64) | | |
| | `/build <recipe>` | Build a specific recipe | | |
| | `/lint` | Run linter on changed recipes | | |
| | `/check` | Check upstream versions | | |
| | `/approve` | Approve and merge (maintainers only) | | |
| <sub>Commands are only available to repository collaborators.</sub> | |
| EOF | |
| ) | |
| gh pr comment ${{ github.event.issue.number }} \ | |
| --repo ${{ github.repository }} \ | |
| --body "$HELP_MSG" | |
| - name: Handle /build | |
| if: steps.parse.outputs.command == 'build' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| ARGS="${{ steps.parse.outputs.args }}" | |
| PR_NUM="${{ github.event.issue.number }}" | |
| # Determine host | |
| HOST="x86_64-Linux" | |
| case "$ARGS" in | |
| x86_64*|X86_64*) HOST="x86_64-Linux" ;; | |
| aarch64*|AARCH64*|arm64*) HOST="aarch64-Linux" ;; | |
| riscv64*|RISCV64*) HOST="riscv64-Linux" ;; | |
| all|ALL) HOST="ALL" ;; | |
| esac | |
| # Post acknowledgment | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "π **Build triggered!** | |
| | | | | |
| |---|---| | |
| | **PR** | #${PR_NUM} | | |
| | **Host** | \`${HOST}\` | | |
| | **Triggered by** | @${{ github.event.comment.user.login }} | | |
| Build will start shortly. I'll post results when complete. | |
| [View workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" | |
| # Trigger the build workflow | |
| gh workflow run pr-build-test.yaml \ | |
| --repo ${{ github.repository }} \ | |
| -f pr_number="$PR_NUM" \ | |
| -f host="$HOST" | |
| - name: Handle /lint | |
| if: steps.parse.outputs.command == 'lint' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUM="${{ github.event.issue.number }}" | |
| HEAD_SHA="${{ steps.pr-info.outputs.head_sha }}" | |
| BASE_REF="${{ steps.pr-info.outputs.base_ref }}" | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "π **Running linter...**" | |
| # Checkout PR | |
| gh pr checkout "$PR_NUM" --repo ${{ github.repository }} | |
| # Download linter | |
| curl -fsSL "https://github.com/pkgforge/sbuilder/releases/download/latest/sbuild-linter-x86_64-linux" \ | |
| -o /tmp/sbuild-linter && chmod +x /tmp/sbuild-linter | |
| # Get changed files | |
| CHANGED=$(git diff --name-only "origin/${BASE_REF}" HEAD -- 'binaries/**/*.yaml' 'packages/**/*.yaml' 2>/dev/null || true) | |
| if [ -z "$CHANGED" ]; then | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "β **Lint complete** - No recipe files changed" | |
| exit 0 | |
| fi | |
| # Run linter on each file | |
| RESULTS="| Recipe | Status | | |
| |--------|--------|" | |
| HAS_ERRORS=false | |
| for file in $CHANGED; do | |
| if [ -f "$file" ]; then | |
| if /tmp/sbuild-linter lint "$file" > /tmp/lint-output.txt 2>&1; then | |
| RESULTS="${RESULTS} | |
| | \`${file}\` | β Valid |" | |
| else | |
| RESULTS="${RESULTS} | |
| | \`${file}\` | β Invalid |" | |
| HAS_ERRORS=true | |
| fi | |
| fi | |
| done | |
| if [ "$HAS_ERRORS" = true ]; then | |
| EMOJI="β" | |
| STATUS="Lint Failed" | |
| else | |
| EMOJI="β " | |
| STATUS="Lint Passed" | |
| fi | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "## ${EMOJI} ${STATUS} | |
| ${RESULTS} | |
| <sub>Triggered by @${{ github.event.comment.user.login }}</sub>" | |
| - name: Handle /check | |
| if: steps.parse.outputs.command == 'check' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUM="${{ github.event.issue.number }}" | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "π **Checking upstream versions...**" | |
| # Checkout PR | |
| gh pr checkout "$PR_NUM" --repo ${{ github.repository }} | |
| # Download sbuild-meta | |
| curl -fsSL "https://github.com/pkgforge/sbuilder/releases/download/latest/sbuild-meta-x86_64-linux" \ | |
| -o /tmp/sbuild-meta && chmod +x /tmp/sbuild-meta || { | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "β οΈ Failed to download sbuild-meta" | |
| exit 1 | |
| } | |
| # Run version check | |
| /tmp/sbuild-meta check-updates \ | |
| --recipes ./binaries ./packages \ | |
| --output /tmp/updates.json \ | |
| --parallel 5 \ | |
| --timeout 30 || true | |
| if [ ! -f /tmp/updates.json ] || [ "$(jq 'length' /tmp/updates.json)" = "0" ]; then | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "β **Version check complete** - All packages are up to date!" | |
| exit 0 | |
| fi | |
| # Format results | |
| RESULTS="| Package | Current | Upstream | | |
| |---------|---------|----------|" | |
| while read -r line; do | |
| pkg=$(echo "$line" | jq -r '.pkg') | |
| current=$(echo "$line" | jq -r '.current_version') | |
| upstream=$(echo "$line" | jq -r '.upstream_version') | |
| RESULTS="${RESULTS} | |
| | \`${pkg}\` | ${current} | **${upstream}** |" | |
| done < <(jq -c '.[]' /tmp/updates.json) | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "## π¦ Version Check Results | |
| ${RESULTS} | |
| <sub>Triggered by @${{ github.event.comment.user.login }}</sub>" | |
| - name: Handle /approve | |
| if: steps.parse.outputs.command == 'approve' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUM="${{ github.event.issue.number }}" | |
| USER="${{ github.event.comment.user.login }}" | |
| ASSOC="${{ github.event.comment.author_association }}" | |
| # Only owners/admins can approve | |
| if [[ "$ASSOC" != "OWNER" && "$ASSOC" != "MEMBER" ]]; then | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "β οΈ @${USER} Only repository owners/members can use \`/approve\`" | |
| exit 0 | |
| fi | |
| # Approve the PR | |
| gh pr review "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --approve \ | |
| --body "β Approved via \`/approve\` command by @${USER}" | |
| gh pr comment "$PR_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body "β **PR Approved** by @${USER} | |
| Ready to merge when checks pass." | |
| - name: Handle unknown command | |
| if: | | |
| steps.parse.outputs.command != 'help' && | |
| steps.parse.outputs.command != 'build' && | |
| steps.parse.outputs.command != 'lint' && | |
| steps.parse.outputs.command != 'check' && | |
| steps.parse.outputs.command != 'approve' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| COMMAND="${{ steps.parse.outputs.command }}" | |
| gh pr comment ${{ github.event.issue.number }} \ | |
| --repo ${{ github.repository }} \ | |
| --body "β Unknown command: \`/${COMMAND}\` | |
| Use \`/help\` to see available commands." |