Skip to content

feat(core): an on switch wears the theme's accent (#138) (#139) #66

feat(core): an on switch wears the theme's accent (#138) (#139)

feat(core): an on switch wears the theme's accent (#138) (#139) #66

Workflow file for this run

# EVERY MERGE TO MAIN IS A RELEASE, when it carries a new version (#23).
#
# The app already checks GitHub Releases for a newer `v<version>` and shows an
# update chip in its title bar (src/main/update.ts). Until now nothing PUBLISHED
# a release: the chip had never had anything to find. Owner, 2026-09-19: "users
# of the app can get an update available banner in their app title bar."
#
# Same gates as ci.yml, then the installer, then `v<version>` as the latest
# release. A push whose version is ALREADY released publishes nothing and says
# so, without failing: a docs-only or CI-only merge is not an error. Bump
# package.json in the PR when a release is meant (patch for fixes, minor for
# features).
#
# The app's tags are `v*`. The shared terminal core's are `core-v*`, plain git
# tags with no GitHub Release behind them (core-release.yml), so "latest
# release" is always an app release.
#
# SIGNED ONCE SIGNPATH IS SET UP (docs/code-signing.md). Until the secret
# SIGNPATH_API_TOKEN exists the two signing steps are skipped and the release is
# unsigned, exactly as before; with it, the installer is sent to SignPath
# (SignPath Foundation's free certificate for open source), approved there, and
# the SIGNED file is what gets published. Nothing else changes.
name: release
on:
push:
branches: [main]
concurrency:
group: release-main
cancel-in-progress: false
jobs:
release:
runs-on: windows-latest
timeout-minutes: 40
permissions:
contents: write
# Job level, so a step's `if` can see whether signing is set up at all.
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Is this version already released?
id: ver
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$v = (Get-Content package.json -Raw | ConvertFrom-Json).version
"version=$v" >> $env:GITHUB_OUTPUT
gh release view "v$v" *> $null
if ($LASTEXITCODE -eq 0) {
"fresh=false" >> $env:GITHUB_OUTPUT
Write-Host "::notice::v$v is already released. Nothing to publish (bump package.json in a PR to release)."
} else {
"fresh=true" >> $env:GITHUB_OUTPUT
}
$global:LASTEXITCODE = 0
- uses: actions/setup-node@v4
if: steps.ver.outputs.fresh == 'true'
with:
node-version: 22
cache: npm
- run: npm ci
if: steps.ver.outputs.fresh == 'true'
- run: npm run typecheck
if: steps.ver.outputs.fresh == 'true'
- run: npm run lint
if: steps.ver.outputs.fresh == 'true'
- run: npm test
if: steps.ver.outputs.fresh == 'true'
# The speech engine for dictation, pinned by SHA-256. `--if-present`: the
# script arrives with the dictation PR, and this file may land before it.
- run: npm run fetch:bin --if-present
if: steps.ver.outputs.fresh == 'true'
- run: npx electron-vite build
if: steps.ver.outputs.fresh == 'true'
- run: npx electron-builder --win --publish never
if: steps.ver.outputs.fresh == 'true'
# Code signing (docs/code-signing.md). Skipped while the secret is absent.
- name: Upload the unsigned installer for signing
id: unsigned
if: steps.ver.outputs.fresh == 'true' && env.SIGNPATH_API_TOKEN != ''
uses: actions/upload-artifact@v4
with:
name: unsigned-installer
path: dist/PrismTerminal-Setup-x64-${{ steps.ver.outputs.version }}.exe
if-no-files-found: error
- name: Sign the installer through SignPath
if: steps.ver.outputs.fresh == 'true' && env.SIGNPATH_API_TOKEN != ''
uses: signpath/github-action-submit-signing-request@v3
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
github-artifact-id: ${{ steps.unsigned.outputs.artifact-id }}
wait-for-completion: true
# Written over the unsigned file, so Publish below sends the signed one.
output-artifact-directory: dist
- name: Publish
if: steps.ver.outputs.fresh == 'true'
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$v = "${{ steps.ver.outputs.version }}"
$exe = "dist/PrismTerminal-Setup-x64-$v.exe"
if (-not (Test-Path $exe)) { throw "installer not found: $exe" }
# --target pins the tag to the commit this run BUILT (code review
# 2026-09-24, #11). Without it GitHub tags main's head at publish
# time, which is a later merge whenever one landed during the build:
# the installer from one commit, the tag, source and notes of another.
#
# BUT NOT OVER A NEWER WORKFLOW (2026-09-28, MEASURED on v0.18.5): a
# merge that changed .github/workflows landed 49 s before this step,
# and GitHub refuses the workflow token a tag on a commit whose
# workflows differ from main's ("HTTP 403: Resource not accessible by
# integration"; that needs a permission the token cannot hold). The
# next run on main builds a commit that contains this one, starts
# only after this run (the concurrency group), finds the version
# unreleased and publishes it; so this run says so and stands down.
git fetch -q --depth=1 origin main
git diff --quiet "${{ github.sha }}" FETCH_HEAD -- .github/workflows
if ($LASTEXITCODE -ne 0) {
"::notice::main has moved past ${{ github.sha }} with changed workflows; the next release run publishes v$v."
exit 0
}
gh release create "v$v" $exe --target "${{ github.sha }}" --title "Prism Terminal $v" --generate-notes --latest
if ($LASTEXITCODE -ne 0) { throw "Release publication failed" }