Skip to content

ci(release): GitHub Releases for v0.1 (unsigned)#21

Merged
hqhq1025 merged 6 commits intomainfrom
wt/release-pipeline
Apr 18, 2026
Merged

ci(release): GitHub Releases for v0.1 (unsigned)#21
hqhq1025 merged 6 commits intomainfrom
wt/release-pipeline

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Summary

  • Tag-triggered (v*.*.*) release workflow with mac / win / linux build matrix
  • Gate job (lint + typecheck + test) must pass before packaging starts
  • Auto changelog from conventional commits via mikepenz/release-changelog-builder-action@v5
  • workflow_dispatch with draft boolean for manual dry-runs
  • README ## Install section with platform-specific unsigned-binary workarounds

Changes

File What changed
.github/workflows/release.yml Full rewrite: tag trigger, gate job, changelog job, 3-platform build matrix, publish job
apps/desktop/package.json Added release script (electron-vite build && electron-builder --publish always)
apps/desktop/electron-builder.yml Added releaseType: release comment; publish provider was already wired
README.md Added ## Install section with Gatekeeper / SmartScreen / AppImage instructions

Stage 1 constraints

  • No code signing in v0.1 — macOS Gatekeeper blocks double-click (right-click → Open required); Windows SmartScreen shows warning
  • No auto-update without explicit opt-in (constraint from CLAUDE.md and electron-updater is present but not activated by this PR)
  • Install size budget enforcement deferred to Stage 2 CI check

Checklist

  • Compatibility — reuses exact same pnpm/node setup as ci.yml
  • Upgradeability — no pinned action SHAs except via tags (easy to bump)
  • No bloat — zero new runtime dependencies
  • Elegance — gate → changelog → build matrix → publish linear flow

Test plan

  • act -j gate local dry-run (if act is installed)
  • Push a v0.1.0-rc.1 test tag → verify draft release appears in GitHub Releases
  • Confirm three platform artifacts attached (.dmg, .exe, .AppImage)
  • README renders correctly on GitHub
  • Verify no auto-update behavior is triggered on install

Next (Stage 2)

  • Apple Developer ID notarization (codesign + notarytool)
  • Windows Authenticode certificate
  • Install size CI gate (fail if > 80 MB)

- 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>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Blocker] workflow_dispatch can publish a release from a branch ref instead of a tag — manual runs use github.ref_name, which is typically main for dispatch runs, so this can generate/update a main tag 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 — release script uses electron-builder --publish always and workflow sets GitHub publish metadata, while a separate publish job also uploads dist/**. This can lead to concurrent writes to the same release and non-deterministic failures/duplicates. Evidence apps/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

Comment thread .github/workflows/release.yml Outdated
version: pnpm version-packages
commit: 'chore(release): version packages'
title: 'chore(release): version packages'
tag_name: ${{ github.ref_name }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/desktop/package.json Outdated
"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",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 like v0.1.0-rc.1 fail 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

Comment thread .github/workflows/release.yml Outdated
- name: Validate tag input on dispatch
if: github.event_name == 'workflow_dispatch'
run: |
if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
fi

Signed-off-by: hqhq1025 <1506751656@qq.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread .github/workflows/release.yml Outdated

- name: Build changelog
id: build
uses: mikepenz/release-changelog-builder-action@v5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] workflow_dispatch can publish artifacts from a different commit than the release tag — tag_name/name are derived from inputs.tag, but all build jobs checkout the dispatch ref (no explicit ref). 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 }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Blocker] Manual dispatch release validation runs without a git checkout — Validate tag input on dispatch executes git ls-remote --tags origin ..., but the publish job never checks out the repository, so origin is 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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: 0

Place 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>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@hqhq1025 hqhq1025 merged commit d6c8ea6 into main Apr 18, 2026
7 checks passed
@hqhq1025 hqhq1025 deleted the wt/release-pipeline branch April 18, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant