Repository navigation
179 lines (175 loc) · 9.15 KB
/
Copy pathopencode.yml
File metadata and controls
179 lines (175 loc) · 9.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
name: opencode
run-name: "(comment ${{ github.event.comment.id }}) Issue #${{ github.event.issue.number }} — ${{ github.event.issue.title }}"
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
# One run per trigger comment: a redelivered webhook queues behind the
# original instead of racing it. The dedupe step below then no-ops the later
# run, so the queue costs a no-op, never a second agent. Different comments
# still run in parallel.
concurrency:
group: opencode-${{ github.event_name }}-${{ github.event.comment.id || github.event.issue.number || github.run_id }}
cancel-in-progress: false
jobs:
opencode:
# The triage and approve exclusions are prefix/contains based and mirror each
# other. Being slightly broad only under-runs an unknown "approve*"/"triage*"
# verb, which is the safe direction for an agent trigger. An anchored
# trigger is exempt from the contains half: a body that starts with
# "/oc " but not "/oc approve"/"/oc triage" has already picked its own
# verb, so quoting another verb later in the prompt (e.g. a review
# discussing "/oc approve") must not skip the run (#862). Mirrored and
# pinned by .github/scripts/opencode-trigger.js.
if: |
(contains(github.event.comment.body, ' /oc ') ||
startsWith(github.event.comment.body, '/oc ')) &&
((startsWith(github.event.comment.body, '/oc ') &&
!startsWith(github.event.comment.body, '/oc approve') &&
!startsWith(github.event.comment.body, '/oc triage')) ||
(!(startsWith(github.event.comment.body, '/oc triage') ||
contains(github.event.comment.body, ' /oc triage')) &&
!(startsWith(github.event.comment.body, '/oc approve') ||
contains(github.event.comment.body, ' /oc approve')))) &&
!contains(github.event.comment.body, 'opencode.ai/s/') &&
(github.event.comment.author_association == 'COLLABORATOR' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'OWNER')
runs-on: ubuntu-latest
# 45 minutes was not enough for deep VM/kernel debugging tickets: issue
# #621 (scheduler stop/sleep-queue work) produced a full baseline,
# reproduction and instrumentation trail and was killed at the timeout with
# the root cause still open. 90 gives a hard ticket room for build plus
# QEMU experiments; routine tickets finish in minutes either way.
timeout-minutes: 90
env:
# This forces actions to use Node 24 and clears the warning
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
# Default (coding) model. Read by .github/hooks/prepare-commit-msg to stamp
# each agent commit with an "Assisted-by: <model>" trailer, so the commit
# attribution cannot drift from the model that actually ran. The selection
# step below overrides this per role (review vs coding); both the value and
# the effort variant come from .github/scripts/model-config.js, which is the
# single place to change either.
OPENCODE_MODEL: opencode/deepseek-v4.1-flash-free
permissions:
id-token: write
contents: write
pull-requests: write
issues: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
# Do not persist the automatic GITHUB_TOKEN. A push made with it is
# attributed to github-actions[bot], and GitHub then parks the PR's
# Java CI run in action_required (waiting for a maintainer to approve)
# instead of running it. The PAT is installed below instead.
persist-credentials: false
- name: Dedupe guard for this comment
# One /oc comment -> at most one agent action. No-ops a redelivered
# duplicate (an earlier run already carries this comment id in its
# run name) and an agent report that merely quotes /oc. Every later
# step is gated on steps.dedupe.outputs.should_run, so a no-op run
# runs no agent and applies no agent/* label.
id: dedupe
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
script: |
const { opencodeDedupe } = require('./.github/scripts/opencode-dedupe.js');
await opencodeDedupe({ github, context, core });
- name: Select model and effort for this run
if: steps.dedupe.outputs.should_run == 'true'
# Role -> model + effort variant, from .github/scripts/model-config.js.
# A review (/oc review) can run a different model from the coding runs
# (/oc, /oc fix, ...) without a second workflow: the verb is matched
# anchored at the start of the comment body, exactly like the job's own
# trigger gate, so a fix prompt quoting "/oc review" still codes.
# Selection is pinned to this run's event payload only
# (context.payload.comment.body): never a fresh API read of the
# latest comment, which could hand a redelivered duplicate a
# different body than the comment it was dispatched for.
uses: actions/github-script@v7
with:
script: |
const { resolveModel } = require('./.github/scripts/model-config.js');
const sel = resolveModel((context.payload.comment || {}).body);
core.info('Model selection: role=' + sel.role + ' model=' + sel.model + ' variant=' + sel.variant);
core.exportVariable('SELECTED_MODEL', sel.model);
core.exportVariable('SELECTED_VARIANT', sel.variant);
// Keep the hook's variable equal to the model that actually runs, so
// an Assisted-by trailer can never name a model that did not make it.
core.exportVariable('OPENCODE_MODEL', sel.model);
- name: Configure git identity
if: steps.dedupe.outputs.should_run == 'true'
env:
GH_PAT: ${{ secrets.GH_PAT }}
run: |
# Attribute agent commits to the maintainer, never a bot. GitHub renders
# the author avatar and the Contributors list from the git identity and
# from Co-authored-by: trailers; the model therefore rides only in an
# Assisted-by: trailer (see the prepare-commit-msg hook), which GitHub
# does not parse. The GitHub noreply keeps the personal email private.
git config user.name "LSantha"
git config user.email "LSantha@users.noreply.github.com"
# Git does not clone hooks, and core.hooksPath defaults to .git/hooks,
# so point it at the tracked hooks dir to install the attribution hook.
git config core.hooksPath "$GITHUB_WORKSPACE/.github/hooks"
# Authenticate pushes with the PAT so a pushed commit is attributed to
# the maintainer. Same-repo collaborator pushes are never approval
# gated; a GITHUB_TOKEN push makes the PR's Java CI run
# action_required and stalls the REVIEW/FEEDBACK loop until approved.
auth=$(printf 'x-access-token:%s' "$GH_PAT" | base64 -w0)
git config --local http.https://github.com/.extraheader "AUTHORIZATION: basic $auth"
- name: Clone wiki
if: steps.dedupe.outputs.should_run == 'true'
run: gh repo clone LSantha/jnode_ai.wiki .wiki
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
- name: Set up JDK 8
if: steps.dedupe.outputs.should_run == 'true'
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'zulu'
- name: Install QEMU
if: steps.dedupe.outputs.should_run == 'true'
run: sudo apt-get update -qq && sudo apt-get install -y -qq qemu-system-x86 socat
- name: Prepare jnode.properties
if: steps.dedupe.outputs.should_run == 'true'
run: cp .github/qemu/jnode.properties .
- name: Cache ISO build
if: steps.dedupe.outputs.should_run == 'true'
id: cache-iso
uses: actions/cache@v4
with:
path: all/build/cdroms/jnode-x86-lite.iso
key: jnode-iso-${{ hashFiles('**/*.java', '**/build.xml', 'build.sh') }}
- name: Build ISO
if: steps.dedupe.outputs.should_run == 'true' && steps.cache-iso.outputs.cache-hit != 'true'
run: sh build.sh cd-x86-lite
- name: Run opencode
if: steps.dedupe.outputs.should_run == 'true'
id: run_agent
uses: anomalyco/opencode/github@latest
env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
with:
model: ${{ env.SELECTED_MODEL }}
variant: ${{ env.SELECTED_VARIANT }}
use_github_token: true
- name: Apply agent/* label and close if work-product is the comment
if: (success() || failure() || cancelled()) && steps.dedupe.outputs.should_run == 'true'
env:
PREV_CONCLUSION: ${{ steps.run_agent.outcome }}
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
script: |
const postStep = require('./.github/scripts/opencode-post-step.js');
await postStep({ github, context, core });