Repository navigation
Remove Fuzz 3 from stop init patch #21
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: opencode | |
| on: | |
| #schedule: | |
| # - cron: '0 0 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| prompt: | |
| description: 'Custom prompt for the agent (leave empty for daily sync)' | |
| required: false | |
| type: string | |
| issue_comment: | |
| types: [created] | |
| pull_request_review_comment: | |
| types: [created] | |
| pull_request: | |
| types: [opened, synchronize] | |
| # Serialize sync runs, and serialize per-PR comment runs, so concurrent | |
| # dispatches/comments don't race on branch and PR creation. | |
| concurrency: | |
| group: opencode-${{ github.event.issue.number || github.event.pull_request.number || 'sync' }} | |
| cancel-in-progress: false | |
| jobs: | |
| check-upstream: | |
| name: Check Upstream for Updates | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_updates: ${{ steps.check.outputs.has_updates }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Check for updates | |
| id: check | |
| run: | | |
| git remote add upstream https://github.com/WildKernels/OnePlus_KernelSU_SUSFS.git | |
| git fetch upstream main | |
| LOCAL_COMMIT=$(git rev-parse HEAD) | |
| UPSTREAM_COMMIT=$(git rev-parse upstream/main) | |
| echo "Local commit: $LOCAL_COMMIT" | |
| echo "Upstream commit: $UPSTREAM_COMMIT" | |
| if ! git merge-base --is-ancestor $UPSTREAM_COMMIT $LOCAL_COMMIT; then | |
| echo "Upstream has new commits." | |
| echo "has_updates=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "No new updates from upstream." | |
| echo "has_updates=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| opencode: | |
| name: Run Opencode Agent | |
| needs: [check-upstream] | |
| if: >- | |
| always() && ( | |
| (github.event_name == 'schedule' && needs.check-upstream.result == 'success' && needs.check-upstream.outputs.has_updates == 'true') || | |
| (github.event_name == 'workflow_dispatch') || | |
| (github.event_name == 'issue_comment' && github.event.comment.user.type != 'Bot' && (contains(github.event.comment.body, ' /oc') || startsWith(github.event.comment.body, '/oc') || contains(github.event.comment.body, ' /opencode') || startsWith(github.event.comment.body, '/opencode'))) || | |
| (github.event_name == 'pull_request_review_comment' && github.event.comment.user.type != 'Bot' && (contains(github.event.comment.body, ' /oc') || startsWith(github.event.comment.body, '/oc') || contains(github.event.comment.body, ' /opencode') || startsWith(github.event.comment.body, '/opencode'))) | |
| ) | |
| runs-on: ubuntu-latest | |
| # NOTE: opencode's `opencode github run` emits ~21 lines of internal | |
| # INFO (#NNNN) telemetry/pino logs that CANNOT be suppressed from this | |
| # workflow β verified in CI that LOG_LEVEL, OPENCODE_LOG_LEVEL and | |
| # OPENCODE_PRINT_LOGS all have no effect (the lines come from the | |
| # always-on OpenTelemetry logger, not the env-gated stderr printer). | |
| # The agent's own tool/Shell transcript prints to stdout regardless. | |
| # Removing the noise would require forking/wrapping the action. | |
| permissions: | |
| id-token: write | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| actions: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| persist-credentials: true | |
| fetch-depth: 0 | |
| - name: Prepare Codebase & Config | |
| shell: bash | |
| env: | |
| OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }} | |
| OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| DISPATCH_PROMPT: ${{ github.event.inputs.prompt }} | |
| run: | | |
| echo "::group::Configuring Git" | |
| git config user.name "opencode" | |
| git config user.email "opencode@anomaly.co" | |
| # Ensure git uses the token for pushing | |
| git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git | |
| echo "::endgroup::" | |
| if [[ "$EVENT_NAME" == "schedule" || ( "$EVENT_NAME" == "workflow_dispatch" && ( "$DISPATCH_PROMPT" == "" || "$DISPATCH_PROMPT" == "null" ) ) ]]; then | |
| echo "::group::Automatic Upstream Merge" | |
| git remote add upstream https://github.com/WildKernels/OnePlus_KernelSU_SUSFS.git | |
| git -c http.extraHeader="" fetch --no-tags upstream main | |
| git merge upstream/main --no-edit || echo "Merge conflicts detected (expected)" | |
| echo "::endgroup::" | |
| echo "::group::Auto-resolve merge conflicts (split by ownership)" | |
| # GOAL: take ALL upstream changes AND let THIS workflow commit a real 2-parent | |
| # merge commit (so the sync PR contains the upstream commits + a merge commit | |
| # and merge-base advances). Every conflict is resolved deterministically below | |
| # -- nothing is left for the agent -- because the agent's tool flattens its | |
| # commit to a single parent and would drop the upstream ancestry. | |
| # | |
| # Non-conflicting upstream edits -- including ones under .github/** -- are | |
| # ALREADY merged in by `git merge` above, so upstream CI improvements (SusFS | |
| # support, cache fixes, ...) in areas the fork didn't touch flow in for free. | |
| # | |
| # Only directly CONFLICTING hunks are resolved here, split by ownership: | |
| # * Fork-owned CI/docs (.github/**, README, ...) keep OURS -- the fork is a | |
| # heavy rewrite (ReSukiSU branding, ccache job, model-link table, DWC3, KSUN | |
| # removal, t.me links); a blind --theirs would drop those. | |
| # * Everything else (device configs, manifests, kernel sources) takes | |
| # UPSTREAM; sanitize.py below re-applies the deterministic fork transforms | |
| # (uname -> OP-RESUKISU, AnyKernel3 URL, ...). | |
| git ls-files -u 2>/dev/null | awk '{print $4}' | sort -u | while IFS= read -r f; do | |
| [ -z "$f" ] && continue | |
| case "$f" in | |
| .github/*|README.md|compatibility.md|LICENSE) | |
| # Fork-owned CI/docs: for directly COLLIDING hunks keep OURS so fork CI | |
| # stays working. Non-conflicting upstream edits to these files were | |
| # ALREADY merged cleanly by `git merge` above, so upstream CI improvements | |
| # in areas the fork didn't touch still flow in -- only hunks that overlap | |
| # a fork customization fall back to fork. | |
| # | |
| # Resolving HERE (rather than leaving the conflict for the opencode agent) | |
| # is deliberate: it lets THIS workflow commit a real 2-parent merge commit | |
| # below. The agent runs in a tool that flattens its commit to a single | |
| # parent, which drops the upstream ancestry -- main's merge-base would | |
| # never advance and every sync would re-merge the same commits. Keeping | |
| # the merge in the workflow makes the sync PR contain the upstream commits | |
| # + one merge commit, and advances merge-base. | |
| git checkout --ours -- "$f" 2>/dev/null || true | |
| git add -- "$f" 2>/dev/null || true | |
| ;; | |
| *) | |
| # Device configs, manifests, kernel sources: take UPSTREAM; sanitize.py | |
| # below re-applies deterministic fork transforms (uname, URLs). | |
| git checkout --theirs -- "$f" 2>/dev/null || git rm -f --quiet "$f" 2>/dev/null || true | |
| git add -- "$f" 2>/dev/null || true | |
| ;; | |
| esac | |
| done | |
| echo "Remaining unmerged after auto-resolve: $(git ls-files -u 2>/dev/null | awk '{print $4}' | sort -u | wc -l | tr -d ' ')" | |
| echo "::endgroup::" | |
| fi | |
| echo "::group::Sanitize & Ignore Rules" | |
| # Count distinct conflicted files (git ls-files -u lists 3 stage rows per file). | |
| CONFLICT_COUNT=$(git ls-files -u 2>/dev/null | awk '{print $4}' | sort -u | wc -l | tr -d ' ') | |
| python3 .github/opencode/sanitize.py | |
| if [ "$CONFLICT_COUNT" -eq 0 ]; then | |
| git add -A | |
| git commit -m "chore: merge upstream/main + sanitize" || echo "No changes to commit" | |
| else | |
| # Write conflict scratch files OUTSIDE the work tree so the agent's | |
| # `git add -A` never commits them into the repo. Read back via $RUNNER_TEMP. | |
| git ls-files -u 2>/dev/null | awk '{print $4}' | sort -u > "$RUNNER_TEMP/conflict_files.txt" | |
| { | |
| echo "The following files have UNRESOLVED MERGE CONFLICTS. Conflict markers are at these exact lines:" | |
| echo "" | |
| while IFS= read -r f; do | |
| [ -z "$f" ] && continue | |
| echo "=== $f ===" | |
| grep -n '<<<<<<< HEAD\|=======\|>>>>>>> upstream/main' "$f" 2>/dev/null || echo " (markers not found in working copy, check git diff)" | |
| echo "" | |
| done < "$RUNNER_TEMP/conflict_files.txt" | |
| } > "$RUNNER_TEMP/conflict_summary.txt" | |
| echo "Merge has $CONFLICT_COUNT conflict file(s), skipping commit. Conflict summary saved." | |
| fi | |
| echo "::endgroup::" | |
| echo "::group::Preparing opencode.json" | |
| # Ensure defaults to prevent invalid JSON if secrets are missing | |
| echo "OPENAI_BASE_URL=${OPENAI_BASE_URL:-https://api.openai.com/v1}" >> "$GITHUB_ENV" | |
| echo "OPENAI_MODEL=${OPENAI_MODEL:-gpt-4o}" >> "$GITHUB_ENV" | |
| # We need to source them for the current step too | |
| export OPENAI_BASE_URL="${OPENAI_BASE_URL:-https://api.openai.com/v1}" | |
| export OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o}" | |
| # Create opencode.json from template, substituting environment variables | |
| envsubst < .github/opencode/opencode.json > opencode.json | |
| echo "::endgroup::" | |
| - name: Auto-PR on Clean Merge | |
| id: merge-check | |
| if: always() && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && (github.event.inputs.prompt == '' || github.event.inputs.prompt == 'null'))) | |
| env: | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} | |
| TG_CHAT_ID: ${{ secrets.TG_TEST_CHAT_ID || secrets.TG_CHAT_ID }} | |
| run: | | |
| CONFLICTS=$(git ls-files -u 2>/dev/null | awk '{print $4}' | sort -u | wc -l | tr -d ' ') | |
| if [ "$CONFLICTS" -gt 0 ]; then | |
| echo "has_conflicts=true" >> "$GITHUB_OUTPUT" | |
| echo "Merge has $CONFLICTS conflict file(s), agent will resolve." | |
| exit 0 | |
| fi | |
| echo "has_conflicts=false" >> "$GITHUB_OUTPUT" | |
| AHEAD=$(git rev-list --count origin/main..HEAD) | |
| echo "Commits ahead of origin/main: $AHEAD" | |
| if [ "$AHEAD" -eq 0 ]; then | |
| echo "skip_agent=true" >> "$GITHUB_OUTPUT" | |
| echo "No new commits, nothing to do." | |
| exit 0 | |
| fi | |
| # Reuse an already-open upstream-sync PR instead of spawning a duplicate. | |
| # Only matches bot-managed `opencode/upstream-sync-*` branches (never the | |
| # agent's `dispatch-*` conflict branches, so no agent work is clobbered). | |
| EXISTING=$(gh pr list --repo "${{ github.repository }}" --state open --base main \ | |
| --json number,headRefName \ | |
| --jq '[.[] | select(.headRefName | startswith("opencode/upstream-sync-"))][0] // {}') | |
| EXISTING_BRANCH=$(echo "$EXISTING" | jq -r '.headRefName // empty') | |
| EXISTING_NUM=$(echo "$EXISTING" | jq -r '.number // empty') | |
| if [ -n "$EXISTING_BRANCH" ]; then | |
| BRANCH="$EXISTING_BRANCH" | |
| # Bot-only branch: force-update it to the freshly merged state. | |
| git push origin "HEAD:refs/heads/$BRANCH" --force | |
| PR_URL=$(gh pr view "$EXISTING_NUM" --repo "${{ github.repository }}" --json url --jq '.url') | |
| gh pr comment "$EXISTING_NUM" --repo "${{ github.repository }}" \ | |
| --body "β»οΈ Refreshed with latest upstream ($AHEAD commits ahead of main)." | |
| echo "Reused existing sync PR #$EXISTING_NUM ($BRANCH)." | |
| else | |
| BRANCH="opencode/upstream-sync-$(date +%Y%m%d%H%M%S)" | |
| git checkout -b "$BRANCH" | |
| git push origin "$BRANCH" | |
| sleep 10 | |
| PR_URL=$(gh pr create \ | |
| --title "π Sync upstream $(date +%Y-%m-%d)" \ | |
| --body "Automatic upstream sync. No conflicts detected." \ | |
| --base main \ | |
| --head "$BRANCH" \ | |
| --repo "${{ github.repository }}") | |
| echo "PR created: $PR_URL" | |
| fi | |
| echo "skip_agent=true" >> "$GITHUB_OUTPUT" | |
| if [ -n "$TG_BOT_TOKEN" ] && [ -n "$TG_CHAT_ID" ]; then | |
| MSG="β <b>Upstream Sync PR Created</b>"$'\n' | |
| MSG+="ββββββββββββββββββ"$'\n' | |
| MSG+="<b>PR:</b> <a href=\"$PR_URL\">$PR_URL</a>"$'\n' | |
| MSG+="<b>Ahead by:</b> $AHEAD commits" | |
| IFS=' ,' read -r -a CHAT_IDS <<< "$TG_CHAT_ID" | |
| for ID in "${CHAT_IDS[@]}"; do | |
| [ -z "$ID" ] && continue | |
| PAYLOAD=$(jq -n --arg chat_id "$ID" --arg text "$MSG" '{chat_id: $chat_id, parse_mode: "HTML", disable_web_page_preview: true, text: $text}') | |
| curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" -H "Content-Type: application/json" -d "$PAYLOAD" > /dev/null | |
| done | |
| fi | |
| - name: Prepare Prompt | |
| id: prep | |
| env: | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| DISPATCH_PROMPT: ${{ github.event.inputs.prompt }} | |
| run: | | |
| TASK_DESC="Synchronizing upstream and resolving conflicts." | |
| if [[ "$EVENT_NAME" == "schedule" || ( "$EVENT_NAME" == "workflow_dispatch" && ( "$DISPATCH_PROMPT" == "" || "$DISPATCH_PROMPT" == "null" ) ) ]]; then | |
| { | |
| echo "A 'git merge upstream/main' has already been attempted. Automatic scripts have also fixed AnyKernel3 URLs, 'uname' fields, and KSUN removal." | |
| echo "Your primary task is to:" | |
| echo "1. Resolve any UNRESOLVED CONFLICTS." | |
| echo "2. Final audit against guidelines." | |
| echo "" | |
| if [ -f "$RUNNER_TEMP/conflict_summary.txt" ]; then | |
| echo "========== CONFLICT DETAILS ==========" | |
| cat "$RUNNER_TEMP/conflict_summary.txt" | |
| echo "=====================================" | |
| echo "" | |
| fi | |
| cat .github/opencode/guidelines.txt | |
| } > final_prompt.txt | |
| elif [[ "$EVENT_NAME" == "issue_comment" || "$EVENT_NAME" == "pull_request_review_comment" ]]; then | |
| TASK_DESC="Processing user request from comment." | |
| PR_OR_ISSUE_NUMBER="${{ github.event.issue.number || github.event.pull_request.number }}" | |
| echo "Fetching comment history for #$PR_OR_ISSUE_NUMBER..." | |
| gh api repos/${{ github.repository }}/issues/$PR_OR_ISSUE_NUMBER/comments | jq -r '.[].body' > user_comment.txt | |
| TRIMMED_COMMENT=$(echo "$COMMENT_BODY" | xargs) | |
| EXTRA_INSTRUCTION="" | |
| if [[ "$TRIMMED_COMMENT" == "/oc" || "$TRIMMED_COMMENT" == "/opencode" ]]; then | |
| EXTRA_INSTRUCTION="The user triggered the agent with an empty '/oc' command. This is a shorthand request. Please implicitly execute the following: \"please review the build logs, fetch the corresponding kernel source code and other .patch files if needed, and fix the compile errors\"" | |
| fi | |
| { | |
| echo "Interactive mode. The user requested the following in a PR/Issue comment thread:"; | |
| echo "-----------------"; | |
| cat user_comment.txt; | |
| echo "-----------------"; | |
| if [[ -n "$EXTRA_INSTRUCTION" ]]; then | |
| echo ""; | |
| echo "$EXTRA_INSTRUCTION"; | |
| fi | |
| echo ""; | |
| echo "Please fulfill the user's request while strictly adhering to these guidelines:"; | |
| echo ""; | |
| cat .github/opencode/guidelines.txt; | |
| } > final_prompt.txt | |
| rm -f user_comment.txt | |
| elif [[ "$EVENT_NAME" == "workflow_dispatch" && "$DISPATCH_PROMPT" != "" ]]; then | |
| TASK_DESC="Processing manual workflow dispatch prompt." | |
| { printf '%s\n' "$DISPATCH_PROMPT"; echo ""; echo "---"; cat .github/opencode/guidelines.txt; } > final_prompt.txt | |
| else | |
| echo "" > final_prompt.txt | |
| fi | |
| # Random delimiter so a line equal to "EOF" inside the prompt can't break the output. | |
| DELIM="EOF_$(openssl rand -hex 8)" | |
| { echo "prompt<<$DELIM"; cat final_prompt.txt; echo "$DELIM"; } >> "$GITHUB_OUTPUT" | |
| echo "task_desc=$TASK_DESC" >> "$GITHUB_OUTPUT" | |
| rm -f final_prompt.txt | |
| - name: Notify TG (Agent Start) | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} | |
| TG_CHAT_ID: ${{ secrets.TG_TEST_CHAT_ID || secrets.TG_CHAT_ID }} | |
| run: | | |
| if [ -z "$TG_BOT_TOKEN" ] || [ -z "$TG_CHAT_ID" ]; then exit 0; fi | |
| TASK="${{ steps.prep.outputs.task_desc }}" | |
| [ -z "$TASK" ] && TASK="Unknown task" | |
| MSG="π <b>Opencode Agent is Starting</b>"$'\n' | |
| MSG+="ββββββββββββββββββ"$'\n' | |
| MSG+="<b>Task:</b> $TASK" | |
| IFS=' ,' read -r -a CHAT_IDS <<< "$TG_CHAT_ID" | |
| for ID in "${CHAT_IDS[@]}"; do | |
| [ -z "$ID" ] && continue | |
| PAYLOAD=$(jq -n --arg chat_id "$ID" --arg text "$MSG" '{chat_id: $chat_id, parse_mode: "HTML", disable_web_page_preview: true, text: $text}') | |
| curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" -H "Content-Type: application/json" -d "$PAYLOAD" > /dev/null | |
| done | |
| - name: Run opencode | |
| id: agent | |
| if: steps.merge-check.outputs.skip_agent != 'true' | |
| uses: anomalyco/opencode/github@latest | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| OPENAI_BASE_URL: ${{ env.OPENAI_BASE_URL }} | |
| OPENAI_MODEL: ${{ env.OPENAI_MODEL }} | |
| GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| model: custom-openai/${{ env.OPENAI_MODEL }} | |
| prompt: ${{ steps.prep.outputs.prompt }} | |
| use_github_token: true | |
| - name: Cleanup Custom Provider Config | |
| if: always() | |
| run: rm -f opencode.json | |
| - name: Notify TG (Agent Finish) | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} | |
| TG_CHAT_ID: ${{ secrets.TG_TEST_CHAT_ID || secrets.TG_CHAT_ID }} | |
| run: | | |
| if [ -z "$TG_BOT_TOKEN" ] || [ -z "$TG_CHAT_ID" ]; then exit 0; fi | |
| STATUS="${{ steps.agent.outcome }}"; EMOJI="β "; [ "$STATUS" != "success" ] && EMOJI="β" | |
| MSG="${EMOJI} <b>Opencode Agent Finished</b>"$'\n' | |
| MSG+="ββββββββββββββββββ"$'\n' | |
| MSG+="<b>Result:</b> <code>${STATUS}</code>"$'\n' | |
| MSG+="<b>Note:</b> A new Pull Request should be available soon." | |
| IFS=' ,' read -r -a CHAT_IDS <<< "$TG_CHAT_ID" | |
| for ID in "${CHAT_IDS[@]}"; do | |
| [ -z "$ID" ] && continue | |
| PAYLOAD=$(jq -n --arg chat_id "$ID" --arg text "$MSG" '{chat_id: $chat_id, parse_mode: "HTML", disable_web_page_preview: true, text: $text}') | |
| curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" -H "Content-Type: application/json" -d "$PAYLOAD" > /dev/null | |
| done | |
| # -------- PULL REQUEST WORKFLOW CHAIN -------- | |
| auto-mirror: | |
| name: Auto-Mirror Toolchains | |
| if: github.event_name == 'pull_request' && startsWith(github.event.pull_request.head.ref, 'opencode/') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: write | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v6 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Check for Changes & Trigger | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| if git diff --name-only origin/${{ github.event.pull_request.base.ref }} HEAD | grep -E 'manifests/|configs/'; then | |
| echo "Toolchain changes detected." | |
| PR_BRANCH="${{ github.event.pull_request.head.ref }}" | |
| gh workflow run mirror-toolchains.yml --repo ${{ github.repository }} --ref "$PR_BRANCH" | |
| # Poll for the run we just dispatched on THIS branch (avoid grabbing another branch's run). | |
| RUN_ID="" | |
| for _ in $(seq 1 12); do | |
| sleep 5 | |
| RUN_ID=$(gh run list --workflow=mirror-toolchains.yml --branch "$PR_BRANCH" --event workflow_dispatch --limit 1 --json databaseId -q '.[0].databaseId' --repo ${{ github.repository }}) | |
| [ -n "$RUN_ID" ] && break | |
| done | |
| if [ -n "$RUN_ID" ]; then | |
| gh run watch $RUN_ID --repo ${{ github.repository }} --exit-status || echo "Mirroring failed, proceeding anyway..." | |
| fi | |
| else | |
| echo "No toolchain changes." | |
| fi | |
| trigger-test-build: | |
| name: Trigger Test Build | |
| needs: [auto-mirror] | |
| # always(): still run the build even if auto-mirror failed/was skipped. | |
| if: always() && github.event_name == 'pull_request' && startsWith(github.event.pull_request.head.ref, 'opencode/') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: write | |
| contents: read | |
| steps: | |
| - name: Trigger build-kernel-release.yml | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_BRANCH="${{ github.event.pull_request.head.ref }}" | |
| gh workflow run build-kernel-release.yml --repo ${{ github.repository }} --ref "$PR_BRANCH" -f make_release=false | |
| # Poll for the run we just dispatched on THIS branch (avoid grabbing another branch's run). | |
| RUN_ID="" | |
| for _ in $(seq 1 12); do | |
| sleep 5 | |
| RUN_ID=$(gh run list --workflow=build-kernel-release.yml --branch "$PR_BRANCH" --event workflow_dispatch --limit 1 --json databaseId -q '.[0].databaseId' --repo ${{ github.repository }}) | |
| [ -n "$RUN_ID" ] && break | |
| done | |
| if [ -n "$RUN_ID" ]; then | |
| gh run watch $RUN_ID --repo ${{ github.repository }} --exit-status | |
| else | |
| exit 1 | |
| fi | |
| report-test-result: | |
| name: Report and Auto-Fix | |
| needs: [trigger-test-build] | |
| if: always() && github.event_name == 'pull_request' && startsWith(github.event.pull_request.head.ref, 'opencode/') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| actions: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Prepare & Send TG Message | |
| env: | |
| TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} | |
| TG_CHAT_ID: ${{ secrets.TG_TEST_CHAT_ID || secrets.TG_CHAT_ID }} | |
| run: | | |
| STATUS="${{ needs.trigger-test-build.result }}"; EMOJI="β "; [ "$STATUS" != "success" ] && EMOJI="β" | |
| MSG_TEXT="${EMOJI} <b>Opencode PR Test Build Finished</b>"$'\n' | |
| MSG_TEXT+="ββββββββββββββββββ"$'\n' | |
| MSG_TEXT+="<b>Result:</b> <code>${STATUS}</code>"$'\n' | |
| MSG_TEXT+="<b>PR:</b> <a href=\"${{ github.event.pull_request.html_url }}\">#${{ github.event.pull_request.number }}</a>"$'\n' | |
| if [ "$STATUS" != "success" ]; then | |
| MSG_TEXT+="ββββββββββββββββββ"$'\n' | |
| MSG_TEXT+="π <i>The build failed. To ask the agent to fix it, comment:</i>"$'\n' | |
| MSG_TEXT+="<code>/oc please review the build logs, fetch the corresponding kernel source code and other .patch files if needed, and fix the compile errors</code>" | |
| fi | |
| PAYLOAD=$(jq -n --arg chat_id "$TG_CHAT_ID" --arg text "$MSG_TEXT" '{chat_id: $chat_id, parse_mode: "HTML", disable_web_page_preview: true, text: $text}') | |
| curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" -H "Content-Type: application/json" -d "$PAYLOAD" > /dev/null | |
| - name: Auto-Comment on PR | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| STATUS="${{ needs.trigger-test-build.result }}" | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| if [ "$STATUS" == "success" ]; then | |
| COMMENT="β **Test build passed successfully!** π" | |
| else | |
| COMMENT=$(cat << 'EOF' | |
| β **Test build failed.** | |
| The AI agent can attempt to fix the errors. Please check the action logs, and reply to this PR with: | |
| `/oc please review the build logs, fetch the corresponding kernel source code and other .patch files if needed, and fix the compile errors` | |
| EOF | |
| ) | |
| fi | |
| gh pr comment $PR_NUMBER --body "$COMMENT" --repo "${{ github.repository }}" | |
| - name: Fail the workflow | |
| if: needs.trigger-test-build.result != 'success' | |
| run: exit 1 |