ci(release): GitHub Releases for v0.1 (unsigned)#21
Conversation
- Replace changesets-only release.yml with a full tag-triggered build matrix (macos / windows / ubuntu) that packages and uploads installers to GitHub Releases via softprops/action-gh-release@v2 - Add gate job (lint + typecheck + test) that must pass before packaging - Add changelog job using mikepenz/release-changelog-builder-action@v5 (feat / fix / docs grouped from conventional commits) - workflow_dispatch includes a `draft` boolean for manual dry-runs - Add `release` script to apps/desktop/package.json (electron-builder --publish always) - Add releaseType comment to electron-builder.yml (no config change, provider was already wired) - Add ## Install section to README with per-platform unsigned-binary workaround instructions (Gatekeeper / SmartScreen / AppImage) Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
-
[Blocker]
workflow_dispatchcan publish a release from a branch ref instead of a tag — manual runs usegithub.ref_name, which is typicallymainfor dispatch runs, so this can generate/update amaintag release rather than a semver tag and publish installers under the wrong version context. Evidence.github/workflows/release.yml:181
Suggested fix:on: workflow_dispatch: inputs: tag: description: 'Tag to release (e.g. v0.1.0)' required: true type: string jobs: publish: if: startsWith(inputs.tag, 'v') steps: - uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.tag }} name: ${{ inputs.tag }}
-
[Major] Build jobs are publishing in parallel before the dedicated publish stage, creating duplicate/racy release uploads —
releasescript useselectron-builder --publish alwaysand workflow sets GitHub publish metadata, while a separatepublishjob also uploadsdist/**. This can lead to concurrent writes to the same release and non-deterministic failures/duplicates. Evidenceapps/desktop/package.json:11,.github/workflows/release.yml:154,apps/desktop/electron-builder.yml:33
Suggested fix:{ "scripts": { "release": "electron-vite build && electron-builder --publish never" } }- name: Package desktop run: pnpm --filter @open-codesign/desktop release # keep publishing only in the final `publish` job
Summary
Review mode: initial
Two issues found in the release pipeline changes: (1) manual dispatch can target a non-version ref for release tagging, and (2) artifact publishing is performed in both matrix build jobs and final publish stage, which introduces race/duplication risk.
Testing
Not run (automation)
open-codesign Bot
| version: pnpm version-packages | ||
| commit: 'chore(release): version packages' | ||
| title: 'chore(release): version packages' | ||
| tag_name: ${{ github.ref_name }} |
There was a problem hiding this comment.
tag_name: ${{ github.ref_name }} is unsafe for workflow_dispatch: on manual runs this is usually the branch name (e.g., main), not a semver tag. Please require a tag input and use that value for tag_name/name to avoid publishing from the wrong ref.
| "dev": "electron-vite dev", | ||
| "build": "electron-vite build && electron-builder", | ||
| "build:dir": "electron-vite build && electron-builder --dir", | ||
| "release": "electron-vite build && electron-builder --publish always", |
There was a problem hiding this comment.
This enables publish during each matrix build (--publish always), but the workflow also has a final publish job that uploads all artifacts. That creates duplicate/racy release writes. Prefer --publish never here and keep a single publish point in the final publish job.
- workflow_dispatch now requires an explicit `tag` input (must match v*.*.*), preventing accidental releases tagged "main" or any other branch name - publish job validates the tag format before downloading artifacts and derives tag_name / name / draft from event type (push vs dispatch) - release script changed to --publish never so electron-builder only builds; softprops/action-gh-release is the sole publish path - remove publish: block from electron-builder.yml to match --publish never Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- [Major] Manual release validation rejects prerelease tags even though workflow supports prerelease handling — the dispatch tag regex only accepts
vX.Y.Z, so valid semver prerelease tags likev0.1.0-rc.1fail before release creation; this conflicts with the prerelease branch in the same workflow and blocks RC dry-runs. Evidence.github/workflows/release.yml:180,.github/workflows/release.yml:198
Suggested fix:- name: Validate tag input on dispatch if: github.event_name == 'workflow_dispatch' run: | if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then echo "::error::tag must be semver-like (e.g. v1.2.3 or v1.2.3-rc.1)" exit 1 fi
Summary
Review mode: follow-up after new commits
One issue remains: workflow_dispatch validation is stricter than the workflow's own prerelease support and prevents prerelease tag runs.
Testing
Not run (automation)
open-codesign Bot
| - name: Validate tag input on dispatch | ||
| if: github.event_name == 'workflow_dispatch' | ||
| run: | | ||
| if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then |
There was a problem hiding this comment.
The dispatch tag validation currently rejects prerelease tags (v0.1.0-rc.1) even though this workflow explicitly computes prerelease based on - in the tag below. Consider allowing semver prerelease suffixes so RC dry-runs are possible.
if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::tag must be semver-like (e.g. v1.2.3 or v1.2.3-rc.1)"
exit 1
fiSigned-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- [Major] Unpinned third-party GitHub Actions in release workflow increase CI supply-chain risk — mutable major tags can be retargeted, so a future upstream tag move could execute unintended code in your release pipeline. Evidence
.github/workflows/release.yml:80,.github/workflows/release.yml:192.
Suggested fix:- name: Build changelog uses: mikepenz/release-changelog-builder-action@c9dc8369bccbc41e0ac887f8fd674f5925d315f7 # v5 - name: Create GitHub Release uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
Summary
Review mode: follow-up after new commits
One major issue remains: third-party actions in the new release workflow are tag-pinned instead of commit-pinned.
Testing
Not run (automation)
open-codesign Bot
|
|
||
| - name: Build changelog | ||
| id: build | ||
| uses: mikepenz/release-changelog-builder-action@v5 |
There was a problem hiding this comment.
[Major] This new third-party action is pinned to a mutable major tag (@v5) rather than an immutable commit SHA. In a release workflow, this is a supply-chain risk because tag retargets can change executed code.
Suggested fix:
- name: Build changelog
uses: mikepenz/release-changelog-builder-action@c9dc8369bccbc41e0ac887f8fd674f5925d315f7 # v5…risk Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- [Major]
workflow_dispatchcan publish artifacts from a different commit than the release tag —tag_name/nameare derived frominputs.tag, but all build jobs checkout the dispatch ref (no explicitref). This breaks source-to-binary traceability and can ship mismatched artifacts. Evidence.github/workflows/release.yml:38,.github/workflows/release.yml:74,.github/workflows/release.yml:132,.github/workflows/release.yml:194.
Suggested fix:env: RELEASE_REF: ${{ github.event_name == 'push' && github.ref || format('refs/tags/{0}', inputs.tag) }} # apply to each checkout in gate/changelog/build - uses: actions/checkout@v4 with: ref: ${{ env.RELEASE_REF }} fetch-depth: 0
Summary
Review mode: follow-up after new commits
One major issue remains in the release workflow: manual dispatch can produce a release whose tag does not match the commit used for build/changelog jobs.
Testing
Not run (automation)
open-codesign Bot
| version: pnpm version-packages | ||
| commit: 'chore(release): version packages' | ||
| title: 'chore(release): version packages' | ||
| tag_name: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} |
There was a problem hiding this comment.
[Major] workflow_dispatch uses inputs.tag for release metadata, but checkout steps do not pin ref to that tag. This can publish artifacts built from a different commit than the declared release tag.\n\nSuggested fix:\nyaml\nenv:\n RELEASE_REF: ${{ github.event_name == push && github.ref || format(refs/tags/{0}, inputs.tag) }}\n\n- uses: actions/checkout@v4\n with:\n ref: ${{ env.RELEASE_REF }}\n fetch-depth: 0\n
…bility Add RELEASE_REF env to all four jobs (gate, changelog, build, publish) that resolves to github.ref on push-tag triggers and to refs/tags/<inputs.tag> on workflow_dispatch. Pass it as the `ref` input to every actions/checkout@v4 call so all jobs build from the exact tag commit rather than the default HEAD of the dispatching branch. Also strengthen the validate-tag step in the publish job: after the semver format check, verify that the tag exists on origin via git ls-remote before any artifacts are downloaded or released, providing early failure and a clear error message. Fixes the Major finding reported by Codex bot: source-to-binary traceability was broken on workflow_dispatch releases. Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- [Blocker] Manual dispatch release validation runs without a git checkout —
Validate tag input on dispatchexecutesgit ls-remote --tags origin ..., but thepublishjob never checks out the repository, sooriginis undefined and workflow-dispatch releases fail before publishing. Evidence.github/workflows/release.yml:188,.github/workflows/release.yml:196.
Suggested fix:publish: steps: - uses: actions/checkout@v4 with: ref: ${{ env.RELEASE_REF }} fetch-depth: 0 - name: Validate tag input on dispatch if: github.event_name == 'workflow_dispatch' run: | if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then echo "::error::tag must be semver-like (e.g. v1.2.3 or v1.2.3-rc.1)" exit 1 fi if ! git ls-remote --tags origin "refs/tags/${{ inputs.tag }}" | grep -q "${{ inputs.tag }}"; then echo "::error::tag '${{ inputs.tag }}' does not exist on origin" exit 1 fi
Summary
Review mode: follow-up after new commits
One blocker remains: the new workflow-dispatch validation path in publish fails because it uses git remote origin without any checkout in that job.
Testing
Not run (automation). Residual risk: no Vitest/Playwright coverage applies to this CI workflow path; rely on workflow-level validation (e.g., actionlint + dry-run strategy) for regression prevention.
open-codesign Bot
| exit 1 | ||
| fi | ||
| # Verify the tag exists on the remote before proceeding | ||
| if ! git ls-remote --tags origin "refs/tags/${{ inputs.tag }}" | grep -q "${{ inputs.tag }}"; then |
There was a problem hiding this comment.
[Blocker] publish does not checkout the repository, so git ls-remote --tags origin ... in this step has no configured origin and fails on workflow_dispatch.
Suggested fix:
- uses: actions/checkout@v4
with:
ref: ${{ env.RELEASE_REF }}
fetch-depth: 0Place this before tag validation (or replace git usage with gh api against refs/tags).
The validate-tag step called `git ls-remote --tags origin` but the
publish job had no checkout, so the `origin` remote did not exist and
the step always failed on workflow_dispatch.
Fix: add `actions/checkout@v4` (ref=RELEASE_REF, fetch-depth=0) as the
first step in the publish job. On push, RELEASE_REF is the pushed tag
ref; on dispatch, it is `refs/tags/<inputs.tag>`, so a missing tag
causes checkout to fail early with a clear git error before validate-tag
even runs. The explicit ls-remote check is kept for a friendlier error
message ("tag does not exist on origin" vs. git's generic reference
not-found).
Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- No new high-confidence issues found on added/modified lines in this revision.
Summary
Review mode: follow-up after new commits
No blocking/major/minor issues found in the latest diff. Residual risk: this is workflow-only change, so functional coverage still depends on running the workflow on both push tag and workflow_dispatch paths.
Testing
Not run (automation)
open-codesign Bot
Summary
v*.*.*) release workflow with mac / win / linux build matrixmikepenz/release-changelog-builder-action@v5workflow_dispatchwithdraftboolean for manual dry-runs## Installsection with platform-specific unsigned-binary workaroundsChanges
.github/workflows/release.ymlapps/desktop/package.jsonreleasescript (electron-vite build && electron-builder --publish always)apps/desktop/electron-builder.ymlreleaseType: releasecomment; publish provider was already wiredREADME.md## Installsection with Gatekeeper / SmartScreen / AppImage instructionsStage 1 constraints
electron-updateris present but not activated by this PR)Checklist
ci.ymlTest plan
act -j gatelocal dry-run (ifactis installed)v0.1.0-rc.1test tag → verify draft release appears in GitHub Releases.dmg,.exe,.AppImage)Next (Stage 2)
codesign+notarytool)