chore: add temporary welcome gutter repair workflow #1
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: Rebuild welcome gutter successor on current dev | ||
| on: | ||
| issue_comment: | ||
| types: [created] | ||
| permissions: | ||
| contents: write | ||
| issues: write | ||
| jobs: | ||
| rebuild: | ||
| if: github.event.issue.number == 1 && github.event.comment.body == '/rebuild-welcome-gutter' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: fix/welcome-composer-gutter-current | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - uses: oven-sh/setup-bun@v2 | ||
| - name: Apply gutter alignment including one-row fallback | ||
| shell: bash | ||
| run: | | ||
| python3 - <<'PY' | ||
| from pathlib import Path | ||
| welcome_path = Path('packages/coding-agent/src/modes/components/welcome.ts') | ||
| welcome = welcome_path.read_text() | ||
| replacements = [ | ||
| ('\tchangelogMarkdown?: string;\n\tcollapseChangelog?: boolean;', | ||
| '\tchangelogMarkdown?: string;\n\trightGutterWidth?: number;\n\tcollapseChangelog?: boolean;'), | ||
| ('\t\tconst boxWidth = Math.max(0, termWidth);', | ||
| '\t\tconst rightGutterWidth = this.#rightGutterWidth(termWidth);\n\t\tconst boxWidth = Math.max(0, termWidth - rightGutterWidth);'), | ||
| ('\t\tif (outputRows === 1) {\n\t\t\treturn lines;\n\t\t}', | ||
| '\t\tif (outputRows === 1) {\n\t\t\treturn this.#withRightGutter(lines, rightGutterWidth);\n\t\t}'), | ||
| ('\t\treturn lines;\n\t}\n\n\t/** Center text within a given width */', | ||
| '\t\treturn this.#withRightGutter(lines, rightGutterWidth);\n\t}\n\n\t/** Center text within a given width */'), | ||
| ('\t#targetRows(termWidth: number): number | undefined {', | ||
| '''\t#rightGutterWidth(termWidth: number): number { | ||
| \t\tconst configured = this.options.rightGutterWidth ?? 0; | ||
| \t\tif (!Number.isFinite(configured) || configured <= 0) return 0; | ||
| \t\tconst gutterWidth = Math.floor(configured); | ||
| \t\treturn Math.min(gutterWidth, Math.max(0, termWidth - 4)); | ||
| \t} | ||
| \t#withRightGutter(lines: string[], rightGutterWidth: number): string[] { | ||
| \t\tif (rightGutterWidth <= 0) return lines; | ||
| \t\tconst gutter = padding(rightGutterWidth); | ||
| \t\treturn lines.map(line => line + gutter); | ||
| \t} | ||
| \t#targetRows(termWidth: number): number | undefined {'''), | ||
| ] | ||
| for old, new in replacements: | ||
| if old in welcome: | ||
| welcome = welcome.replace(old, new, 1) | ||
| elif new not in welcome: | ||
| raise SystemExit(f'welcome anchor mismatch: {old[:80]}') | ||
| welcome_path.write_text(welcome) | ||
| mode_path = Path('packages/coding-agent/src/modes/interactive-mode.ts') | ||
| mode = mode_path.read_text() | ||
| replacements = [ | ||
| ('const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;\n', | ||
| 'const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;\nconst COMPOSER_RIGHT_GUTTER_WIDTH = 1;\n'), | ||
| ('\teditor.setRightGutterWidth(1);', | ||
| '\teditor.setRightGutterWidth(COMPOSER_RIGHT_GUTTER_WIDTH);'), | ||
| ('\t\t\t\t\tchangelogMarkdown: this.#changelogMarkdown,\n\t\t\t\t\tcollapseChangelog:', | ||
| '\t\t\t\t\tchangelogMarkdown: this.#changelogMarkdown,\n\t\t\t\t\trightGutterWidth: COMPOSER_RIGHT_GUTTER_WIDTH,\n\t\t\t\t\tcollapseChangelog:'), | ||
| ] | ||
| for old, new in replacements: | ||
| if old in mode: | ||
| mode = mode.replace(old, new, 1) | ||
| elif new not in mode: | ||
| raise SystemExit(f'interactive-mode anchor mismatch: {old[:80]}') | ||
| mode_path.write_text(mode) | ||
| test_path = Path('packages/coding-agent/test/welcome-viewport.test.ts') | ||
| tests = test_path.read_text() | ||
| marker = '\tit("renders the build label from metadata instead of defaulting to dev", () => {' | ||
| regression = '''\tit("reserves the composer gutter for full and one-row welcome layouts", () => { | ||
| \t\tconst full = new WelcomeComponent("1.2.3", "test-model", "test-provider", [], [], "ascii", { | ||
| \t\t\trightGutterWidth: 1, | ||
| \t\t}); | ||
| \t\tconst fullLines = full.render(100).map(stripRenderControls); | ||
| \t\texpect(fullLines.length).toBeGreaterThan(0); | ||
| \t\tfor (const line of fullLines) { | ||
| \t\t\texpect(visibleWidth(line)).toBe(100); | ||
| \t\t\texpect(line.endsWith(" ")).toBe(true); | ||
| \t\t\texpect(visibleWidth(line.trimEnd())).toBe(99); | ||
| \t\t} | ||
| \t\tconst oneRow = new WelcomeComponent("1.2.3", "test-model", "test-provider", [], [], "ascii", { | ||
| \t\t\trightGutterWidth: 1, | ||
| \t\t\tgetViewportRows: () => 1, | ||
| \t\t\tgetReservedBottomRows: () => 0, | ||
| \t\t}); | ||
| \t\tconst [line] = oneRow.render(100).map(stripRenderControls); | ||
| \t\texpect(line).toBeDefined(); | ||
| \t\texpect(visibleWidth(line!)).toBe(100); | ||
| \t\texpect(line!.endsWith(" ")).toBe(true); | ||
| \t\texpect(visibleWidth(line!.trimEnd())).toBe(99); | ||
| \t}); | ||
| ''' | ||
| if 'reserves the composer gutter for full and one-row welcome layouts' not in tests: | ||
| if tests.count(marker) != 1: | ||
| raise SystemExit('welcome test anchor mismatch') | ||
| tests = tests.replace(marker, regression + marker, 1) | ||
| test_path.write_text(tests) | ||
| changelog_path = Path('packages/coding-agent/CHANGELOG.md') | ||
| changelog = changelog_path.read_text() | ||
| entry = '- Aligned the startup GJC Forge splash border with the composer trailing gutter, including the one-row constrained fallback.\n' | ||
| if entry not in changelog: | ||
| anchor = '### Fixed\n\n' | ||
| if anchor not in changelog: | ||
| raise SystemExit('changelog Fixed anchor mismatch') | ||
| changelog = changelog.replace(anchor, anchor + entry, 1) | ||
| changelog_path.write_text(changelog) | ||
| PY | ||
| - name: Install dependencies | ||
| run: bun install --frozen-lockfile | ||
| - name: Format changed files | ||
| run: | | ||
| bunx biome check --write \ | ||
| packages/coding-agent/src/modes/components/welcome.ts \ | ||
| packages/coding-agent/src/modes/interactive-mode.ts \ | ||
| packages/coding-agent/test/welcome-viewport.test.ts | ||
| - name: Run focused viewport and composer integration tests | ||
| run: | | ||
| bun test --timeout 30000 packages/coding-agent/test/welcome-viewport.test.ts | ||
| bun test --timeout 30000 packages/coding-agent/test/interactive-mode-editor-component.test.ts --test-name-pattern "welcome|viewport-bound|composer" | ||
| - name: Run package check | ||
| run: bun --cwd=packages/coding-agent run check | ||
| - name: Commit verified successor | ||
| shell: bash | ||
| run: | | ||
| git config user.name "twoimo" | ||
| git config user.email "32544727+twoimo@users.noreply.github.com" | ||
| git add packages/coding-agent/CHANGELOG.md packages/coding-agent/src/modes/components/welcome.ts packages/coding-agent/src/modes/interactive-mode.ts packages/coding-agent/test/welcome-viewport.test.ts | ||
| git commit -m "fix(coding-agent): align welcome and composer gutters" | ||
| git push origin HEAD:fix/welcome-composer-gutter-current | ||
| - name: Report successful verification | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: 1, | ||
| body: `WELCOME_GUTTER_OK\n\n${process.env.GITHUB_SHA}`, | ||
| }); | ||