diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index f1661546..6603eef0 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -61,6 +61,40 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Read version + id: version + shell: bash + run: | + VERSION=$(jq -r '.version' desktop/src-tauri/tauri.conf.json) + echo "value=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Validate tag matches version + if: github.event_name == 'push' + shell: bash + run: | + EXPECTED_TAG="v${{ steps.version.outputs.value }}" + if [ "${GITHUB_REF_NAME}" != "$EXPECTED_TAG" ]; then + echo "::error::Tag ${GITHUB_REF_NAME} does not match app version ${EXPECTED_TAG}" + exit 1 + fi + + - name: Load release notes + id: release_notes + shell: bash + run: | + NOTES_FILE="release-notes/v${{ steps.version.outputs.value }}.md" + if [ ! -f "$NOTES_FILE" ]; then + echo "::error::Missing release notes file: $NOTES_FILE" + exit 1 + fi + + { + echo 'body<<__RELEASE_NOTES__' + cat "$NOTES_FILE" + echo + echo '__RELEASE_NOTES__' + } >> "$GITHUB_OUTPUT" + # ── System dependencies (Linux) ────────────────────────── - name: Install Linux dependencies if: contains(matrix.platform, 'ubuntu') @@ -134,29 +168,7 @@ jobs: tauriScript: bunx tauri tagName: v__VERSION__ releaseName: 'Claude Code Haha v__VERSION__' - releaseBody: | - ## Claude Code Haha v__VERSION__ - - ### Downloads - - | Platform | File | - |----------|------| - | macOS (Apple Silicon) | `.dmg` | - | macOS (Intel) | `.dmg` | - | Windows | `.exe` (NSIS installer) | - | Linux | `.deb` | - - ### First-time installation - - **macOS**: This build is unsigned. If you see "app is damaged" or "unidentified developer", run: - ```bash - xattr -cr /Applications/Claude\ Code\ Haha.app - ``` - - **Windows**: If SmartScreen blocks the app, click "More info" → "Run anyway". - - --- - See [Installation Guide](https://github.com/NanmiCoder/cc-haha/blob/main/docs/desktop/04-installation.md) for details. + releaseBody: ${{ steps.release_notes.outputs.body }} releaseDraft: ${{ github.event_name == 'workflow_dispatch' && inputs.draft || false }} prerelease: false args: ${{ matrix.tauri_args }} --config src-tauri/tauri.release-ci.json diff --git a/scripts/release.ts b/scripts/release.ts index efdfeb3b..dd13233d 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -10,7 +10,7 @@ * bun run scripts/release.ts patch --dry # preview without changes */ -import { readFileSync, writeFileSync } from 'node:fs' +import { existsSync, readFileSync, writeFileSync } from 'node:fs' import path from 'node:path' const root = path.resolve(import.meta.dir, '..') @@ -43,6 +43,10 @@ function getCurrentVersion(): string { return tauriConf.version } +function getReleaseNotesPath(version: string): string { + return path.join(root, 'release-notes', `v${version}.md`) +} + function bumpVersion(current: string, bump: string): string { if (/^\d+\.\d+\.\d+$/.test(bump)) { return bump @@ -88,9 +92,11 @@ if (!bumpArg) { const current = getCurrentVersion() const next = bumpVersion(current, bumpArg) +const releaseNotesPath = getReleaseNotesPath(next) console.log(`\n Version: ${current} → ${next}`) console.log(` Tag: v${next}`) +console.log(` Notes: ${path.relative(root, releaseNotesPath)}`) console.log(` Dry run: ${dryRun}\n`) if (dryRun) { @@ -98,9 +104,16 @@ if (dryRun) { for (const file of VERSION_FILES) { console.log(` - ${path.relative(root, file.path)}`) } + console.log(` - ${path.relative(root, releaseNotesPath)} ${existsSync(releaseNotesPath) ? '(present)' : '(missing)'}`) process.exit(0) } +if (!existsSync(releaseNotesPath)) { + console.error(`Missing release notes file: ${path.relative(root, releaseNotesPath)}`) + console.error(`Create it before releasing so GitHub Release can use it automatically.`) + process.exit(1) +} + // Update version in all files for (const file of VERSION_FILES) { const content = readFileSync(file.path, 'utf-8') @@ -122,6 +135,7 @@ await run([ 'desktop/src-tauri/tauri.conf.json', 'desktop/src-tauri/Cargo.toml', 'desktop/src-tauri/Cargo.lock', + path.relative(root, releaseNotesPath), ]) await run(['git', 'commit', '-m', `release: v${next}`]) await run(['git', 'tag', '-a', `v${next}`, '-m', `Release v${next}`])