Skip to content

Commit 35fc62e

Browse files
committed
ci: add current model preset successor gate
1 parent b778ae6 commit 35fc62e

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
name: Verify current model preset successor
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize, reopened]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
verify:
12+
if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'successor/model-preset-default-action-current'
13+
runs-on: ubuntu-22.04
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
ref: ${{ github.event.pull_request.head.ref }}
18+
fetch-depth: 0
19+
token: ${{ secrets.GITHUB_TOKEN }}
20+
21+
- name: Check one-shot trigger
22+
id: gate
23+
shell: bash
24+
run: |
25+
if [ -f .github/trigger/verify-model-preset-current ]; then
26+
echo "run=true" >> "$GITHUB_OUTPUT"
27+
else
28+
echo "run=false" >> "$GITHUB_OUTPUT"
29+
fi
30+
31+
- name: Rebuild minimal diff on current upstream dev
32+
if: steps.gate.outputs.run == 'true'
33+
shell: bash
34+
run: |
35+
git config user.name "twoimo"
36+
git config user.email "32544727+twoimo@users.noreply.github.com"
37+
git remote add upstream https://github.com/Yeachan-Heo/gajae-code.git || true
38+
git fetch --no-tags upstream dev
39+
git reset --hard upstream/dev
40+
python3 - <<'PY'
41+
from pathlib import Path
42+
43+
selector_path = Path('packages/coding-agent/src/modes/components/model-selector.ts')
44+
selector = selector_path.read_text()
45+
replacements = {
46+
'const PRESET_SCOPE_LABELS = ["Apply for this session", "Set as default"];':
47+
'const PRESET_SCOPE_LABELS = ["Set as default", "Apply for this session"];',
48+
'const CUSTOM_PRESET_SCOPE_LABELS = ["Apply for this session", "Set as default", "Rename", "Delete"];':
49+
'const CUSTOM_PRESET_SCOPE_LABELS = ["Set as default", "Apply for this session", "Rename", "Delete"];',
50+
'this.#listContainer.addChild(new Text(theme.fg("muted", " Press Enter to apply this preset"), 0, 0));':
51+
'this.#listContainer.addChild(new Text(theme.fg("muted", " Press Enter to choose an action"), 0, 0));',
52+
'setDefault: this.#presetScopeIndex === 1,': 'setDefault: this.#presetScopeIndex === 0,',
53+
}
54+
for old, new in replacements.items():
55+
if old not in selector:
56+
raise SystemExit(f'model-selector anchor missing: {old}')
57+
selector = selector.replace(old, new, 1)
58+
selector_path.write_text(selector)
59+
60+
test_path = Path('packages/coding-agent/test/model-selector-profiles-redteam.test.ts')
61+
tests = test_path.read_text()
62+
old = '''\ttest("profile actions wire Apply for this session to persistDefault false and Set as default to true", async () => {
63+
\t\tconst selections: ModelSelectorSelection[] = [];
64+
\t\tconst applySelector = createSelector(selection => {
65+
\t\t\tselections.push(selection);
66+
\t\t});
67+
\t\tawait renderSelector(applySelector);
68+
\t\tapplySelector.handleInput("\\x1b[C");
69+
\t\tapplySelector.handleInput("\\x1b[B");
70+
\t\tapplySelector.handleInput("\\n");
71+
\t\tapplySelector.handleInput("\\n");
72+
\t\tapplySelector.handleInput("\\n");
73+
74+
\t\tconst defaultSelector = createSelector(selection => {
75+
\t\t\tselections.push(selection);
76+
\t\t});
77+
\t\tawait renderSelector(defaultSelector);
78+
\t\tdefaultSelector.handleInput("\\x1b[C");
79+
\t\tdefaultSelector.handleInput("\\x1b[B");
80+
\t\tdefaultSelector.handleInput("\\n");
81+
\t\tdefaultSelector.handleInput("\\n");
82+
\t\tdefaultSelector.handleInput("\\x1b[B");
83+
\t\tdefaultSelector.handleInput("\\n");
84+
85+
\t\texpect(selections).toEqual([
86+
\t\t\t{ kind: "profile", profileName: "profile-a", setDefault: false },
87+
\t\t\t{ kind: "profile", profileName: "profile-a", setDefault: true },
88+
\t\t]);
89+
\t});
90+
'''
91+
new = '''\ttest("profile actions default to persistence and retain session-only application", async () => {
92+
\t\tconst selections: ModelSelectorSelection[] = [];
93+
\t\tconst select = (selection: ModelSelectorSelection) => selections.push(selection);
94+
\t\tconst persistentSelector = createSelector(select);
95+
\t\tawait renderSelector(persistentSelector);
96+
\t\tpersistentSelector.handleInput("\\x1b[C");
97+
\t\tpersistentSelector.handleInput("\\x1b[B");
98+
\t\tpersistentSelector.handleInput("\\n");
99+
\t\tpersistentSelector.handleInput("\\n");
100+
\t\tconst menu = normalizeRenderedText(persistentSelector.render(240).join("\\n"));
101+
\t\texpect(menu).toContain("Set as default");
102+
\t\texpect(menu).toContain("Apply for this session");
103+
\t\tpersistentSelector.handleInput("\\n");
104+
105+
\t\tconst sessionSelector = createSelector(select);
106+
\t\tawait renderSelector(sessionSelector);
107+
\t\tsessionSelector.handleInput("\\x1b[C");
108+
\t\tsessionSelector.handleInput("\\x1b[B");
109+
\t\tsessionSelector.handleInput("\\n");
110+
\t\tsessionSelector.handleInput("\\n");
111+
\t\tsessionSelector.handleInput("\\x1b[B");
112+
\t\tsessionSelector.handleInput("\\n");
113+
114+
\t\texpect(selections).toEqual([
115+
\t\t\t{ kind: "profile", profileName: "profile-a", setDefault: true },
116+
\t\t\t{ kind: "profile", profileName: "profile-a", setDefault: false },
117+
\t\t]);
118+
\t});
119+
120+
\ttest("custom profile action indices retain rename and delete", async () => {
121+
\t\tconst renamed: ModelSelectorSelection[] = [];
122+
\t\tconst renameSelector = createSelector(selection => renamed.push(selection));
123+
\t\tawait renderSelector(renameSelector);
124+
\t\trenameSelector.refreshPresetProfiles("profile-a");
125+
\t\trenameSelector.handleInput("\\n");
126+
\t\trenameSelector.handleInput("\\x1b[B");
127+
\t\trenameSelector.handleInput("\\x1b[B");
128+
\t\trenameSelector.handleInput("\\n");
129+
130+
\t\tconst deleted: ModelSelectorSelection[] = [];
131+
\t\tconst deleteSelector = createSelector(selection => deleted.push(selection));
132+
\t\tawait renderSelector(deleteSelector);
133+
\t\tdeleteSelector.refreshPresetProfiles("profile-a");
134+
\t\tdeleteSelector.handleInput("\\n");
135+
\t\tdeleteSelector.handleInput("\\x1b[B");
136+
\t\tdeleteSelector.handleInput("\\x1b[B");
137+
\t\tdeleteSelector.handleInput("\\x1b[B");
138+
\t\tdeleteSelector.handleInput("\\n");
139+
140+
\t\texpect(renamed).toEqual([{ kind: "renameProfile", profileName: "profile-a" }]);
141+
\t\texpect(deleted).toEqual([{ kind: "deleteProfile", profileName: "profile-a" }]);
142+
\t});
143+
'''
144+
if old not in tests:
145+
raise SystemExit('model selector red-team anchor missing')
146+
test_path.write_text(tests.replace(old, new, 1))
147+
148+
changelog_path = Path('packages/coding-agent/CHANGELOG.md')
149+
changelog = changelog_path.read_text()
150+
entry = '- `/model` preset selection now offers `Set as default` as the first action while retaining `Apply for this session`, custom preset rename, and delete actions.\n'
151+
if entry not in changelog:
152+
marker = '### Changed\n\n'
153+
if marker not in changelog:
154+
changelog = changelog.replace('## [Unreleased]\n', '## [Unreleased]\n### Changed\n\n', 1)
155+
changelog = changelog.replace('### Changed\n\n', '### Changed\n\n' + entry, 1)
156+
changelog_path.write_text(changelog)
157+
PY
158+
159+
- uses: oven-sh/setup-bun@v2
160+
if: steps.gate.outputs.run == 'true'
161+
with:
162+
bun-version: '1.3'
163+
164+
- name: Install, format, and verify
165+
if: steps.gate.outputs.run == 'true'
166+
run: |
167+
bun install --frozen-lockfile
168+
bunx biome check --write \
169+
packages/coding-agent/src/modes/components/model-selector.ts \
170+
packages/coding-agent/test/model-selector-profiles-redteam.test.ts
171+
bun test --timeout 30000 \
172+
packages/coding-agent/test/model-selector-profiles.test.ts \
173+
packages/coding-agent/test/model-selector-profiles-redteam.test.ts \
174+
packages/coding-agent/test/model-preset-landing-redteam-qa.test.ts \
175+
packages/coding-agent/test/cli-args-mpreset.test.ts
176+
bun --cwd=packages/coding-agent run check
177+
178+
- name: Publish verified clean successor
179+
if: steps.gate.outputs.run == 'true'
180+
shell: bash
181+
run: |
182+
git add \
183+
packages/coding-agent/CHANGELOG.md \
184+
packages/coding-agent/src/modes/components/model-selector.ts \
185+
packages/coding-agent/test/model-selector-profiles-redteam.test.ts
186+
git commit -m "feat(coding-agent): default preset selection to persistence"
187+
git push --force origin HEAD:${{ github.event.pull_request.head.ref }}

0 commit comments

Comments
 (0)