ci: repair reviewed successor branches #44
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: Verify current model preset successor | ||
| on: | ||
| pull_request_target: | ||
| types: [opened, synchronize, reopened] | ||
| permissions: | ||
| contents: write | ||
| jobs: | ||
| verify: | ||
| if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'successor/model-preset-default-action-current' | ||
| runs-on: ubuntu-22.04 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.pull_request.head.ref }} | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Check one-shot trigger | ||
| id: gate | ||
| shell: bash | ||
| run: | | ||
| if [ -f .github/trigger/verify-model-preset-current ]; then | ||
| echo "run=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "run=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Rebuild minimal diff on current upstream dev | ||
| if: steps.gate.outputs.run == 'true' | ||
| shell: bash | ||
| run: | | ||
| git config user.name "twoimo" | ||
| git config user.email "32544727+twoimo@users.noreply.github.com" | ||
| git remote add upstream https://github.com/Yeachan-Heo/gajae-code.git || true | ||
| git fetch --no-tags upstream dev | ||
| git reset --hard upstream/dev | ||
| python3 - <<'PY' | ||
| from pathlib import Path | ||
| selector_path = Path('packages/coding-agent/src/modes/components/model-selector.ts') | ||
| selector = selector_path.read_text() | ||
| replacements = { | ||
| 'const PRESET_SCOPE_LABELS = ["Apply for this session", "Set as default"];': | ||
| 'const PRESET_SCOPE_LABELS = ["Set as default", "Apply for this session"];', | ||
| 'const CUSTOM_PRESET_SCOPE_LABELS = ["Apply for this session", "Set as default", "Rename", "Delete"];': | ||
| 'const CUSTOM_PRESET_SCOPE_LABELS = ["Set as default", "Apply for this session", "Rename", "Delete"];', | ||
| 'this.#listContainer.addChild(new Text(theme.fg("muted", " Press Enter to apply this preset"), 0, 0));': | ||
| 'this.#listContainer.addChild(new Text(theme.fg("muted", " Press Enter to choose an action"), 0, 0));', | ||
| 'setDefault: this.#presetScopeIndex === 1,': 'setDefault: this.#presetScopeIndex === 0,', | ||
| } | ||
| for old, new in replacements.items(): | ||
| if old not in selector: | ||
| raise SystemExit(f'model-selector anchor missing: {old}') | ||
| selector = selector.replace(old, new, 1) | ||
| selector_path.write_text(selector) | ||
| test_path = Path('packages/coding-agent/test/model-selector-profiles-redteam.test.ts') | ||
| tests = test_path.read_text() | ||
| old = '''\ttest("profile actions wire Apply for this session to persistDefault false and Set as default to true", async () => { | ||
| \t\tconst selections: ModelSelectorSelection[] = []; | ||
| \t\tconst applySelector = createSelector(selection => { | ||
| \t\t\tselections.push(selection); | ||
| \t\t}); | ||
| \t\tawait renderSelector(applySelector); | ||
| \t\tapplySelector.handleInput("\\x1b[C"); | ||
| \t\tapplySelector.handleInput("\\x1b[B"); | ||
| \t\tapplySelector.handleInput("\\n"); | ||
| \t\tapplySelector.handleInput("\\n"); | ||
| \t\tapplySelector.handleInput("\\n"); | ||
| \t\tconst defaultSelector = createSelector(selection => { | ||
| \t\t\tselections.push(selection); | ||
| \t\t}); | ||
| \t\tawait renderSelector(defaultSelector); | ||
| \t\tdefaultSelector.handleInput("\\x1b[C"); | ||
| \t\tdefaultSelector.handleInput("\\x1b[B"); | ||
| \t\tdefaultSelector.handleInput("\\n"); | ||
| \t\tdefaultSelector.handleInput("\\n"); | ||
| \t\tdefaultSelector.handleInput("\\x1b[B"); | ||
| \t\tdefaultSelector.handleInput("\\n"); | ||
| \t\texpect(selections).toEqual([ | ||
| \t\t\t{ kind: "profile", profileName: "profile-a", setDefault: false }, | ||
| \t\t\t{ kind: "profile", profileName: "profile-a", setDefault: true }, | ||
| \t\t]); | ||
| \t}); | ||
| ''' | ||
| new = '''\ttest("profile actions default to persistence and retain session-only application", async () => { | ||
| \t\tconst selections: ModelSelectorSelection[] = []; | ||
| \t\tconst select = (selection: ModelSelectorSelection) => selections.push(selection); | ||
| \t\tconst persistentSelector = createSelector(select); | ||
| \t\tawait renderSelector(persistentSelector); | ||
| \t\tpersistentSelector.handleInput("\\x1b[C"); | ||
| \t\tpersistentSelector.handleInput("\\x1b[B"); | ||
| \t\tpersistentSelector.handleInput("\\n"); | ||
| \t\tpersistentSelector.handleInput("\\n"); | ||
| \t\tconst menu = normalizeRenderedText(persistentSelector.render(240).join("\\n")); | ||
| \t\texpect(menu).toContain("Set as default"); | ||
| \t\texpect(menu).toContain("Apply for this session"); | ||
| \t\tpersistentSelector.handleInput("\\n"); | ||
| \t\tconst sessionSelector = createSelector(select); | ||
| \t\tawait renderSelector(sessionSelector); | ||
| \t\tsessionSelector.handleInput("\\x1b[C"); | ||
| \t\tsessionSelector.handleInput("\\x1b[B"); | ||
| \t\tsessionSelector.handleInput("\\n"); | ||
| \t\tsessionSelector.handleInput("\\n"); | ||
| \t\tsessionSelector.handleInput("\\x1b[B"); | ||
| \t\tsessionSelector.handleInput("\\n"); | ||
| \t\texpect(selections).toEqual([ | ||
| \t\t\t{ kind: "profile", profileName: "profile-a", setDefault: true }, | ||
| \t\t\t{ kind: "profile", profileName: "profile-a", setDefault: false }, | ||
| \t\t]); | ||
| \t}); | ||
| \ttest("custom profile action indices retain rename and delete", async () => { | ||
| \t\tconst renamed: ModelSelectorSelection[] = []; | ||
| \t\tconst renameSelector = createSelector(selection => renamed.push(selection)); | ||
| \t\tawait renderSelector(renameSelector); | ||
| \t\trenameSelector.refreshPresetProfiles("profile-a"); | ||
| \t\trenameSelector.handleInput("\\n"); | ||
| \t\trenameSelector.handleInput("\\x1b[B"); | ||
| \t\trenameSelector.handleInput("\\x1b[B"); | ||
| \t\trenameSelector.handleInput("\\n"); | ||
| \t\tconst deleted: ModelSelectorSelection[] = []; | ||
| \t\tconst deleteSelector = createSelector(selection => deleted.push(selection)); | ||
| \t\tawait renderSelector(deleteSelector); | ||
| \t\tdeleteSelector.refreshPresetProfiles("profile-a"); | ||
| \t\tdeleteSelector.handleInput("\\n"); | ||
| \t\tdeleteSelector.handleInput("\\x1b[B"); | ||
| \t\tdeleteSelector.handleInput("\\x1b[B"); | ||
| \t\tdeleteSelector.handleInput("\\x1b[B"); | ||
| \t\tdeleteSelector.handleInput("\\n"); | ||
| \t\texpect(renamed).toEqual([{ kind: "renameProfile", profileName: "profile-a" }]); | ||
| \t\texpect(deleted).toEqual([{ kind: "deleteProfile", profileName: "profile-a" }]); | ||
| \t}); | ||
| ''' | ||
| if old not in tests: | ||
| raise SystemExit('model selector red-team anchor missing') | ||
| test_path.write_text(tests.replace(old, new, 1)) | ||
| changelog_path = Path('packages/coding-agent/CHANGELOG.md') | ||
| changelog = changelog_path.read_text() | ||
| 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' | ||
| if entry not in changelog: | ||
| marker = '### Changed\n\n' | ||
| if marker not in changelog: | ||
| changelog = changelog.replace('## [Unreleased]\n', '## [Unreleased]\n### Changed\n\n', 1) | ||
| changelog = changelog.replace('### Changed\n\n', '### Changed\n\n' + entry, 1) | ||
| changelog_path.write_text(changelog) | ||
| PY | ||
| - uses: oven-sh/setup-bun@v2 | ||
| if: steps.gate.outputs.run == 'true' | ||
| with: | ||
| bun-version: '1.3' | ||
| - name: Install, format, and verify | ||
| if: steps.gate.outputs.run == 'true' | ||
| run: | | ||
| bun install --frozen-lockfile | ||
| bunx biome check --write \ | ||
| packages/coding-agent/src/modes/components/model-selector.ts \ | ||
| packages/coding-agent/test/model-selector-profiles-redteam.test.ts | ||
| bun test --timeout 30000 \ | ||
| packages/coding-agent/test/model-selector-profiles.test.ts \ | ||
| packages/coding-agent/test/model-selector-profiles-redteam.test.ts \ | ||
| packages/coding-agent/test/model-preset-landing-redteam-qa.test.ts \ | ||
| packages/coding-agent/test/cli-args-mpreset.test.ts | ||
| bun --cwd=packages/coding-agent run check | ||
| - name: Publish verified clean successor | ||
| if: steps.gate.outputs.run == 'true' | ||
| shell: bash | ||
| run: | | ||
| git add \ | ||
| packages/coding-agent/CHANGELOG.md \ | ||
| packages/coding-agent/src/modes/components/model-selector.ts \ | ||
| packages/coding-agent/test/model-selector-profiles-redteam.test.ts | ||
| git commit -m "feat(coding-agent): default preset selection to persistence" | ||
| git push --force origin HEAD:${{ github.event.pull_request.head.ref }} | ||