From bc36c411b8a9d2ee274ef9ef5e447a5afebc8667 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 00:54:06 +0800 Subject: [PATCH 1/6] test: fix admin credential save locator --- app/dashboard/dashboard.tsx | 21 +++++++++++++++++++++ app/globals.scss | 24 ++++++++++++++++++++++++ e2e/admin-console.spec.ts | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/dashboard/dashboard.tsx b/app/dashboard/dashboard.tsx index c63380d..fbe1590 100644 --- a/app/dashboard/dashboard.tsx +++ b/app/dashboard/dashboard.tsx @@ -160,6 +160,27 @@ const Dashboard = () => { ))} + + + orangeboyChen/codebuddy2api + ); }; diff --git a/app/globals.scss b/app/globals.scss index ed2ab7f..f2478cc 100644 --- a/app/globals.scss +++ b/app/globals.scss @@ -195,6 +195,12 @@ textarea { margin-left: 24px; max-width: none; min-width: 0; + overflow-x: auto; + scrollbar-width: none; +} + +.admin-header-navigation::-webkit-scrollbar { + display: none; } .admin-header-navigation-compact { @@ -205,6 +211,8 @@ textarea { background: transparent !important; border-color: transparent !important; box-shadow: none !important; + flex: 0 0 auto; + white-space: nowrap; } .admin-header-tab[data-active] { @@ -219,7 +227,9 @@ textarea { } .admin-header-controls { + flex: 0 0 auto; margin-left: auto; + min-width: 0; } .admin-header-select > *:last-child { @@ -312,6 +322,20 @@ textarea { background: var(--lobe-color-fill); } +.dashboard-github-link { + align-items: center; + align-self: center; + color: var(--lobe-color-text-secondary); + display: inline-flex; + gap: 8px; + justify-content: center; + transition: color 160ms ease; +} + +.dashboard-github-link:hover { + color: var(--lobe-color-text); +} + .account-status-card-busy { opacity: 0.72; } diff --git a/e2e/admin-console.spec.ts b/e2e/admin-console.spec.ts index 56796fd..f8d9065 100644 --- a/e2e/admin-console.spec.ts +++ b/e2e/admin-console.spec.ts @@ -173,7 +173,7 @@ test.describe('Admin console essentials', () => { .last(); await expect(credentialCard).toBeVisible(); await credentialCard.getByRole('button', { name: 'Edit' }).click(); - const saveButton = credentialCard.getByRole('button', { + const saveButton = page.getByRole('button', { name: 'Save', exact: true, }); From 3404b6806c61723cb14ed4017b6d2a40a56fcc51 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 01:09:06 +0800 Subject: [PATCH 2/6] feat: align dashboard release versioning --- .github/workflows/release.yml | 42 +++++++++++++++++++-------------- app/dashboard/dashboard.tsx | 44 +++++++++++++++++++---------------- app/globals.scss | 15 ++++++++++-- package.json | 2 +- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf786c1..a8e04bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} release_tag: ${{ steps.version.outputs.release_tag }} + commit_sha: ${{ steps.version.outputs.commit_sha }} owner: ${{ steps.owner.outputs.owner }} image: ghcr.io/${{ steps.owner.outputs.owner }}/codebuddy2api @@ -33,29 +34,31 @@ jobs: with: fetch-depth: 0 - - name: Determine next version + - name: Update package version id: version shell: bash run: | set -euo pipefail - latest_tag="$(git tag --list 'v*' --sort=-v:refname | head -n 1 || true)" - if [[ -z "$latest_tag" ]]; then - version="1.0.0" - else - current_version="${latest_tag#v}" - IFS='.' read -r major minor patch <<< "$current_version" - - if [[ -z "${major:-}" || -z "${minor:-}" || -z "${patch:-}" ]]; then - echo "Latest tag '$latest_tag' is not in vX.Y.Z format." >&2 - exit 1 - fi - - version="${major}.${minor}.$((patch + 1))" + current_version="$(node -p "require('./package.json').version")" + IFS='.' read -r major minor patch <<< "$current_version" + if [[ -z "${major:-}" || -z "${minor:-}" || -z "${patch:-}" ]]; then + echo "package.json version '$current_version' is not in X.Y.Z format." >&2 + exit 1 fi + version="${major}.${minor}.$((patch + 1))" + node -e "const fs=require('fs'); const path='package.json'; const pkg=JSON.parse(fs.readFileSync(path,'utf8')); pkg.version=process.argv[1]; fs.writeFileSync(path, JSON.stringify(pkg,null,2)+'\\n');" "$version" + + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add package.json + git commit -m "chore: bump version to v${version}" + git push origin "HEAD:${GITHUB_REF_NAME}" + commit_sha="$(git rev-parse HEAD)" echo "version=$version" >> "$GITHUB_OUTPUT" echo "release_tag=v${version}" >> "$GITHUB_OUTPUT" + echo "commit_sha=$commit_sha" >> "$GITHUB_OUTPUT" - name: Determine lowercase owner id: owner @@ -74,6 +77,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v7 + with: + ref: ${{ needs.prepare.outputs.commit_sha }} - name: Set up Bun uses: oven-sh/setup-bun@v2 @@ -128,6 +133,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v7 + with: + ref: ${{ needs.prepare.outputs.commit_sha }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -278,6 +285,7 @@ jobs: uses: actions/checkout@v7 with: fetch-depth: 0 + ref: ${{ needs.prepare.outputs.commit_sha }} - name: Create GitHub release env: @@ -291,10 +299,10 @@ jobs: set -euo pipefail latest_tag="$(git tag --list 'v*' --sort=-v:refname | grep -Fxv "${RELEASE_TAG}" | head -n 1 || true)" - commit_range="${GITHUB_SHA}" + commit_range="${{ needs.prepare.outputs.commit_sha }}" if [[ -n "${latest_tag}" ]]; then - commit_range="${latest_tag}..${GITHUB_SHA}" + commit_range="${latest_tag}..${{ needs.prepare.outputs.commit_sha }}" fi commit_notes="$(git log "${commit_range}" --pretty=format:'- %s (%h)' || true)" @@ -312,7 +320,7 @@ jobs: release_notes="${release_notes}"$'\n\n'"## Commits"$'\n'"${commit_notes}" gh release create "$RELEASE_TAG" \ - --target "$GITHUB_SHA" \ + --target "${{ needs.prepare.outputs.commit_sha }}" \ --title "$RELEASE_TITLE" \ --latest \ --notes "$release_notes" diff --git a/app/dashboard/dashboard.tsx b/app/dashboard/dashboard.tsx index fbe1590..0a2b949 100644 --- a/app/dashboard/dashboard.tsx +++ b/app/dashboard/dashboard.tsx @@ -14,6 +14,7 @@ import { createContext, useContext } from 'react'; import type { AdminConsoleInitialData } from '@/app/page-data'; import type { AppMessages } from '@/lib/i18n/messages'; +import packageJson from '@/package.json'; export interface DashboardSummary { cacheHitTokens: number; @@ -160,27 +161,30 @@ const Dashboard = () => { ))} - - - orangeboyChen/codebuddy2api - + + orangeboyChen/codebuddy2api + + v{packageJson.version} + ); }; diff --git a/app/globals.scss b/app/globals.scss index f2478cc..c40363a 100644 --- a/app/globals.scss +++ b/app/globals.scss @@ -325,15 +325,26 @@ textarea { .dashboard-github-link { align-items: center; align-self: center; - color: var(--lobe-color-text-secondary); display: inline-flex; gap: 8px; justify-content: center; +} + +.dashboard-github-repository { + align-items: center; + color: var(--lobe-color-text-secondary); + display: inline-flex; + gap: 8px; transition: color 160ms ease; } -.dashboard-github-link:hover { +.dashboard-github-repository:hover { + color: var(--lobe-color-text); +} + +.dashboard-version { color: var(--lobe-color-text); + font-variant-numeric: tabular-nums; } .account-status-card-busy { diff --git a/package.json b/package.json index 4c86621..ef5ae22 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codebuddy2api", - "version": "2.0.0", + "version": "1.0.31", "private": true, "scripts": { "dev": "next dev", From 64fc988eb55534174ef0dc57afbe237b0baf2f69 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 01:21:09 +0800 Subject: [PATCH 3/6] fix: address release and e2e review feedback --- .github/workflows/release.yml | 57 +++++++++++++++++++++++------------ e2e/account-status.spec.ts | 2 +- e2e/admin-console.spec.ts | 15 ++++++--- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8e04bc..2bef69d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,6 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} release_tag: ${{ steps.version.outputs.release_tag }} - commit_sha: ${{ steps.version.outputs.commit_sha }} owner: ${{ steps.owner.outputs.owner }} image: ghcr.io/${{ steps.owner.outputs.owner }}/codebuddy2api @@ -34,7 +33,7 @@ jobs: with: fetch-depth: 0 - - name: Update package version + - name: Determine next version id: version shell: bash run: | @@ -47,18 +46,8 @@ jobs: exit 1 fi version="${major}.${minor}.$((patch + 1))" - node -e "const fs=require('fs'); const path='package.json'; const pkg=JSON.parse(fs.readFileSync(path,'utf8')); pkg.version=process.argv[1]; fs.writeFileSync(path, JSON.stringify(pkg,null,2)+'\\n');" "$version" - - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add package.json - git commit -m "chore: bump version to v${version}" - git push origin "HEAD:${GITHUB_REF_NAME}" - commit_sha="$(git rev-parse HEAD)" - echo "version=$version" >> "$GITHUB_OUTPUT" echo "release_tag=v${version}" >> "$GITHUB_OUTPUT" - echo "commit_sha=$commit_sha" >> "$GITHUB_OUTPUT" - name: Determine lowercase owner id: owner @@ -70,6 +59,7 @@ jobs: echo "owner=$lower_owner" >> "$GITHUB_OUTPUT" verify: + needs: prepare name: Verify Bun / TypeScript / Coverage runs-on: ubuntu-latest timeout-minutes: 20 @@ -77,8 +67,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v7 - with: - ref: ${{ needs.prepare.outputs.commit_sha }} - name: Set up Bun uses: oven-sh/setup-bun@v2 @@ -112,9 +100,38 @@ jobs: - name: Build Next.js app run: bun run build + commit_version: + needs: + - prepare + - verify + runs-on: ubuntu-latest + outputs: + commit_sha: ${{ steps.commit.outputs.commit_sha }} + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Commit package version + id: commit + shell: bash + env: + VERSION: ${{ needs.prepare.outputs.version }} + run: | + set -euo pipefail + node -e "const fs=require('fs'); const path='package.json'; const pkg=JSON.parse(fs.readFileSync(path,'utf8')); pkg.version=process.argv[1]; fs.writeFileSync(path, JSON.stringify(pkg,null,2)+'\\n');" "$VERSION" + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add package.json + git commit -m "chore: bump version to v${VERSION}" + git push origin "HEAD:${GITHUB_REF_NAME}" + echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + publish-image: needs: - prepare + - commit_version - verify name: Publish ${{ matrix.arch }} image runs-on: ${{ matrix.runs_on }} @@ -134,7 +151,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v7 with: - ref: ${{ needs.prepare.outputs.commit_sha }} + ref: ${{ needs.commit_version.outputs.commit_sha }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -211,6 +228,7 @@ jobs: merge-image-manifests: needs: - prepare + - commit_version - publish-image name: Merge multi-arch manifests runs-on: ubuntu-latest @@ -276,6 +294,7 @@ jobs: release: needs: - prepare + - commit_version - merge-image-manifests runs-on: ubuntu-latest timeout-minutes: 15 @@ -285,7 +304,7 @@ jobs: uses: actions/checkout@v7 with: fetch-depth: 0 - ref: ${{ needs.prepare.outputs.commit_sha }} + ref: ${{ needs.commit_version.outputs.commit_sha }} - name: Create GitHub release env: @@ -299,10 +318,10 @@ jobs: set -euo pipefail latest_tag="$(git tag --list 'v*' --sort=-v:refname | grep -Fxv "${RELEASE_TAG}" | head -n 1 || true)" - commit_range="${{ needs.prepare.outputs.commit_sha }}" + commit_range="${{ needs.commit_version.outputs.commit_sha }}" if [[ -n "${latest_tag}" ]]; then - commit_range="${latest_tag}..${{ needs.prepare.outputs.commit_sha }}" + commit_range="${latest_tag}..${{ needs.commit_version.outputs.commit_sha }}" fi commit_notes="$(git log "${commit_range}" --pretty=format:'- %s (%h)' || true)" @@ -320,7 +339,7 @@ jobs: release_notes="${release_notes}"$'\n\n'"## Commits"$'\n'"${commit_notes}" gh release create "$RELEASE_TAG" \ - --target "${{ needs.prepare.outputs.commit_sha }}" \ + --target "${{ needs.commit_version.outputs.commit_sha }}" \ --title "$RELEASE_TITLE" \ --latest \ --notes "$release_notes" diff --git a/e2e/account-status.spec.ts b/e2e/account-status.spec.ts index 14fce8f..60bfeda 100644 --- a/e2e/account-status.spec.ts +++ b/e2e/account-status.spec.ts @@ -73,7 +73,7 @@ test.describe('Account Status tab', () => { await context.grantPermissions(['clipboard-read', 'clipboard-write'], { origin: 'http://127.0.0.1:8001', }); - const filename = 'account-status-copy.json'; + const filename = `account-status-copy-${process.pid}.json`; const createResponse = await page.request.post('/admin-api/credentials', { data: { bearer_token: 'e2e-token', diff --git a/e2e/admin-console.spec.ts b/e2e/admin-console.spec.ts index f8d9065..d85996a 100644 --- a/e2e/admin-console.spec.ts +++ b/e2e/admin-console.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '@playwright/test'; -const filename = 'admin-console-e2e.json'; +const filename = `admin-console-e2e-${process.pid}.json`; const json = (body: unknown) => ({ data: body, headers: { 'Content-Type': 'application/json' }, @@ -173,10 +173,15 @@ test.describe('Admin console essentials', () => { .last(); await expect(credentialCard).toBeVisible(); await credentialCard.getByRole('button', { name: 'Edit' }).click(); - const saveButton = page.getByRole('button', { - name: 'Save', - exact: true, - }); + const saveButton = page + .locator('.credential-card') + .filter({ + hasText: filename, + }) + .getByRole('button', { + name: 'Save', + exact: true, + }); await expect(saveButton).toBeVisible(); await saveButton.click(); await expect(page.getByText('Credential saved:')).toBeVisible(); From 07d4a62ef26d148f7714a1d33d15a2ed30f3e410 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 01:28:25 +0800 Subject: [PATCH 4/6] chore: streamline pre-push checks --- .husky/pre-push | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 6eb4199..39477c2 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -5,8 +5,3 @@ set -eu bun run lint bun run format:check bun run typecheck -bun run test:ci - -base_sha="$(git merge-base HEAD origin/main)" -bun run test:patch-branches --base "$base_sha" -bun run build From c374f08cf8ab777affc6d13d712641cc81627987 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 01:41:03 +0800 Subject: [PATCH 5/6] test: stabilize credential save locator --- e2e/admin-console.spec.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/e2e/admin-console.spec.ts b/e2e/admin-console.spec.ts index d85996a..2e70fe7 100644 --- a/e2e/admin-console.spec.ts +++ b/e2e/admin-console.spec.ts @@ -173,15 +173,10 @@ test.describe('Admin console essentials', () => { .last(); await expect(credentialCard).toBeVisible(); await credentialCard.getByRole('button', { name: 'Edit' }).click(); - const saveButton = page - .locator('.credential-card') - .filter({ - hasText: filename, - }) - .getByRole('button', { - name: 'Save', - exact: true, - }); + const saveButton = credentialCard.getByRole('button', { + name: 'Save', + exact: true, + }); await expect(saveButton).toBeVisible(); await saveButton.click(); await expect(page.getByText('Credential saved:')).toBeVisible(); From 257801d3707f3e0f6be9c27edda6c306c8f1e2c4 Mon Sep 17 00:00:00 2001 From: orangeboyChen Date: Fri, 4 Sep 2026 01:51:16 +0800 Subject: [PATCH 6/6] test: use stable credential card selectors --- app/credentials/credential-card.tsx | 1 + e2e/admin-console.spec.ts | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/credentials/credential-card.tsx b/app/credentials/credential-card.tsx index d39ee5d..d87a79b 100644 --- a/app/credentials/credential-card.tsx +++ b/app/credentials/credential-card.tsx @@ -70,6 +70,7 @@ export const CredentialCard = ({ return ( { }); expect(createResponse.ok()).toBe(true); await page.goto('/credentials'); - const credentialCard = page - .locator('.credential-card') - .filter({ - hasText: filename, - }) - .last(); + const credentialCard = page.locator( + `[data-credential-filename="${filename}"]`, + ); await expect(credentialCard).toBeVisible(); await credentialCard.getByRole('button', { name: 'Edit' }).click(); const saveButton = credentialCard.getByRole('button', {