feat: tauri release workflow + updater manifest CLI#2
Conversation
Adds the pre-release half of the desktop pipeline so protoLabs apps
can build, sign, notarize, and publish a Tauri 2 app to R2 without
each repo copy-pasting (and drifting from) the same workflow.
Two pieces:
1. .github/workflows/tauri-release.yml — reusable workflow callable
from a consumer repo via:
uses: protoLabsAI/release-tools/.github/workflows/tauri-release.yml@v1
secrets: inherit
with:
project-path: apps/desktop
app-identifier: studio.protolabs.example
r2-bucket: example-desktop-releases
r2-public-base-url: https://dl.example.studio
Mac builds universal (aarch64 + x86_64) with full sign + notarize
via tauri-action; Windows signs via SSL.com eSigner; Linux ships
AppImage unsigned. Publish job uploads versioned artifacts and a
no-cache latest.json to R2, then drafts a GH Release.
2. bin/build-updater-manifest.mjs — second CLI exported from the
package. Walks an artifact directory, finds platform binaries +
matching .sig files, writes the JSON manifest the in-app Tauri
updater pulls. Exits non-zero if any binary is missing its sig
so the publish job fails loudly when signing didn't run.
Conventions match the existing rewrite-release-notes CLI: single-file
ESM, no deps, self-documenting --help that reads its own header
comment, smoke-tested in CI with a synthetic dist tree.
README gains a "Tauri release workflow" section covering the inputs,
secret list, and direct CLI usage. CI smoke test now exercises both
binaries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WalkthroughAdds a reusable Tauri release GitHub Actions workflow, a new Node.js CLI to build updater manifests from distribution artifacts, CI updates to validate the new CLI, and package.json adjustments to expose the CLI and include workflow files in published package. ChangesTauri release + manifest tooling
sequenceDiagram
participant GH as GitHub Actions
participant Builder as Matrix Runners (macOS/Win/Linux)
participant ArtifactStore as Cloudflare R2
participant Release as GitHub Release
GH->>Builder: trigger build (matrix)
Builder-->>GH: upload platform artifacts (DMG, NSIS, AppImage, signatures)
GH->>GH: download artifacts into dist/
GH->>GH: run build-updater-manifest -> latest.json
GH->>ArtifactStore: upload versioned binaries and latest.json
GH->>Release: create draft GitHub Release with dist artifacts
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Review rate limit: 0/5 reviews remaining, refill in 50 minutes and 42 seconds. Comment |
tauri-action's notarization step expects APPLE_API_KEY_PATH to be a filesystem path to the .p8, not the file contents. GitHub secrets are strings, so consumers store the .p8 as APPLE_API_KEY_BASE64; the workflow now decodes it to $RUNNER_TEMP and exports the path before the build step runs. Caught by CodeRabbit on the mythxengine doc PR that referenced the same incorrect "APPLE_API_KEY_PATH = base64 of .p8" contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/tauri-release.yml:
- Around line 227-234: The workflow step that runs npx -p
'@protolabsai/release-tools@latest' build-updater-manifest should not use
`@latest`; change it to a pinned semver (e.g., '@protolabsai/release-tools@0.2.0')
or expose a new input like release-tools-version with a default and use that
variable in the npx package spec, updating the step name "Generate updater
manifest (latest.json)" and the npx invocation (build-updater-manifest) to
reference the pinned or input-driven package spec so CI installs a reproducible,
auditable release-tools version.
In `@bin/build-updater-manifest.mjs`:
- Around line 128-139: The current .app.tar.gz branch silently defaults unknown
macOS bundles to ['darwin-aarch64']; change this to fail closed by throwing an
Error (or otherwise returning a failure) instead of guessing. In
bin/build-updater-manifest.mjs replace the fallback "return ['darwin-aarch64']"
in the name.endsWith('.app.tar.gz') block with code that throws a clear Error
(including name and filePath) so the caller can abort/prompt handling for
unknown macOS bundle mappings.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9a4d48f4-07cc-4e02-8145-f137ca3446d3
⛔ Files ignored due to path filters (1)
README.mdis excluded by!*.md
📒 Files selected for processing (4)
.github/workflows/ci.yml.github/workflows/tauri-release.ymlbin/build-updater-manifest.mjspackage.json
| - name: Generate updater manifest (latest.json) | ||
| run: | | ||
| npx --yes -p '@protolabsai/release-tools@latest' build-updater-manifest \ | ||
| --version "${{ steps.version.outputs.version }}" \ | ||
| --dist dist \ | ||
| --base-url "${{ inputs.r2-public-base-url }}/${{ steps.version.outputs.version }}" \ | ||
| --notes "${{ inputs.release-notes-text }}" \ | ||
| --out latest.json |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What are current best practices for pinning npm package versions in CI/CD release workflows, specifically risks of using npx -p @latest?
💡 Result:
Best practices to pin npm versions in CI/CD release workflows 1) Pin the whole dependency tree via committed lockfiles - Commit package-lock.json (or yarn.lock / pnpm-lock.yaml) to version control. - In CI/CD, install using the deterministic command for your ecosystem (npm ci for npm). - npm ci “installs from a package’s lockfile” and “will never write to … package-lock” and “ensures reproducible builds” by bypassing package.json resolution. It also fails if package.json and the lockfile don’t match. 2) Treat “installing new versions” as a deliberate change - Use automated dependency update PRs (e.g., Renovate/Dependabot) rather than allowing builds to float to whatever is currently “latest”. - Only merge after reviewing lockfile diffs. 3) Harden execution during installs (supply-chain risk reduction) - Avoid running lifecycle scripts unless you must. Many supply-chain attacks rely on install-time scripts (preinstall/postinstall). - Use npm ci flags such as --ignore-scripts when appropriate; or in npm config set ignore-scripts=true (with care because some packages require build steps). 4) Add additional gates beyond “known CVEs” - npm audit is useful but not sufficient; it mainly catches known vulnerabilities rather than novel malicious packages. - Consider proactive scanning / analysis tools and provenance verification as layers before or during CI runs. Risks of using npx -p @latest 1) Non-deterministic builds (time-based changes) - @latest resolves to whatever the registry considers latest at execution time. That means two CI runs at different times can execute different package versions/contents even if your repo code is unchanged. - Deterministic CI is the core goal of lockfiles + npm ci; installing a moving target subverts that goal. 2) Increased exposure window for compromised “new releases” - Supply-chain attacks often rely on a short window between malicious publication and detection/removal. Fetching “latest” in CI means you may pull something that was just published. - Security guidance for supply-chain attacks recommends delaying updates / enforcing minimum release age (in pnpm) and, more generally, avoiding blind upgrades to newly published versions. 3) You may execute attacker code directly as part of your pipeline step - npx/npm exec are designed to “run an arbitrary command from an npm package” and may fetch a remote package if it’s not already installed. - That makes the exact code being executed harder to reason about (and harder to tie back to a reviewed lockfile snapshot), which undermines forensic and compliance goals. 4) Lockfile/integrity guarantees don’t cover the runtime execution of npx@latest - Lockfiles (and npm ci) provide cryptographic integrity verification and strict replay of exactly resolved versions. - A one-off npx -p pkg@latest ... fetch is not the same operational model as installing your project from a committed lockfile; the pipeline no longer has a stable, reviewable dependency snapshot for that executed tool. Concrete “safer” alternatives - If you need a CLI tool in CI: install it as a devDependency and run it from node_modules/.bin - Add the tool to package.json with an exact version (or a controlled range), commit lockfile changes, then use npm ci + npm run <script>. - If you must use npx/npm exec: pin an explicit version (not @latest) and preferably still keep it in lockfiles - Example pattern: resolve a specific version in code review (e.g., npx pkg@1.2.3 ...), not a floating tag. - Better: use your project’s installed tool (npm ci + script) so the version is locked and reviewable. - Install dependencies deterministically - npm ci in CI/CD (not npm install) to avoid lockfile drift and unexpected updates during builds. Short checklist - CI/CD uses npm ci. - package-lock.json is committed and matches package.json. - No floating versions like @latest in release-critical steps; avoid npx -p @latest. - Disable/limit install scripts (ignore-scripts) when feasible. - Add layered supply-chain security checks; don’t rely only on npm audit. If you want, paste your current CI job step(s) (redact secrets) and I can suggest a drop-in replacement for the specific npx -p @latest usages.
Citations:
- 1: https://docs.npmjs.com/cli/v8/commands/npm-ci/
- 2: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable.html
- 3: https://thelinuxcode.com/difference-between-npm-i-and-npm-ci-in-nodejs-a-practical-guide-for-2026-teams/
- 4: https://docs.npmjs.com/cli/v9/commands/npx/
- 5: https://docs.npmjs.com/cli/v8/commands/npm-exec
- 6: https://www.pkgpulse.com/blog/package-security-best-practices
- 7: https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html
- 8: https://snyk.io/articles/npm-security-best-practices-shai-hulud-attack/
- 9: https://pnpm.io/next/supply-chain-security
- 10: https://www.pkgpulse.com/blog/npm-supply-chain-security-guide-2026
- 11: https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/
Pin @protolabsai/release-tools to a specific version instead of @latest.
Using @latest makes releases non-reproducible and creates a supply-chain risk vector. If a malicious version is published between your release runs, your CI pipeline will execute it without review. Additionally, this bypasses the integrity guarantees of lockfiles—npm ci cannot protect against fetching whatever is currently latest.
Pin to a concrete version (e.g., 0.2.0), or expose a release-tools-version input with a fixed default.
Suggested change
- npx --yes -p '@protolabsai/release-tools@latest' build-updater-manifest \
+ npx --yes -p '@protolabsai/release-tools@0.2.0' build-updater-manifest \
--version "${{ steps.version.outputs.version }}" \
--dist dist \
--base-url "${{ inputs.r2-public-base-url }}/${{ steps.version.outputs.version }}" \
--notes "${{ inputs.release-notes-text }}" \
--out latest.json📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Generate updater manifest (latest.json) | |
| run: | | |
| npx --yes -p '@protolabsai/release-tools@latest' build-updater-manifest \ | |
| --version "${{ steps.version.outputs.version }}" \ | |
| --dist dist \ | |
| --base-url "${{ inputs.r2-public-base-url }}/${{ steps.version.outputs.version }}" \ | |
| --notes "${{ inputs.release-notes-text }}" \ | |
| --out latest.json | |
| - name: Generate updater manifest (latest.json) | |
| run: | | |
| npx --yes -p '@protolabsai/release-tools@0.2.0' build-updater-manifest \ | |
| --version "${{ steps.version.outputs.version }}" \ | |
| --dist dist \ | |
| --base-url "${{ inputs.r2-public-base-url }}/${{ steps.version.outputs.version }}" \ | |
| --notes "${{ inputs.release-notes-text }}" \ | |
| --out latest.json |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/tauri-release.yml around lines 227 - 234, The workflow
step that runs npx -p '@protolabsai/release-tools@latest' build-updater-manifest
should not use `@latest`; change it to a pinned semver (e.g.,
'@protolabsai/release-tools@0.2.0') or expose a new input like
release-tools-version with a default and use that variable in the npx package
spec, updating the step name "Generate updater manifest (latest.json)" and the
npx invocation (build-updater-manifest) to reference the pinned or input-driven
package spec so CI installs a reproducible, auditable release-tools version.
| if (name.endsWith('.app.tar.gz')) { | ||
| if (filePath.includes('universal-apple-darwin') || /universal/i.test(name)) { | ||
| return ['darwin-aarch64', 'darwin-x86_64']; | ||
| } | ||
| if (filePath.includes('aarch64-apple-darwin') || /aarch64|arm64/i.test(name)) { | ||
| return ['darwin-aarch64']; | ||
| } | ||
| if (filePath.includes('x86_64-apple-darwin') || /x86_64|x64/i.test(name)) { | ||
| return ['darwin-x86_64']; | ||
| } | ||
| return ['darwin-aarch64']; // safe default for native runner builds | ||
| } |
There was a problem hiding this comment.
Avoid silently mapping unknown macOS bundles to darwin-aarch64.
On Line 138, unknown .app.tar.gz artifacts default to arm64. That can publish a wrong platform mapping (e.g., x86_64-only bundle exposed as arm64) and break updater resolution. Fail closed instead of guessing.
Suggested fix
if (name.endsWith('.app.tar.gz')) {
if (filePath.includes('universal-apple-darwin') || /universal/i.test(name)) {
return ['darwin-aarch64', 'darwin-x86_64'];
}
if (filePath.includes('aarch64-apple-darwin') || /aarch64|arm64/i.test(name)) {
return ['darwin-aarch64'];
}
if (filePath.includes('x86_64-apple-darwin') || /x86_64|x64/i.test(name)) {
return ['darwin-x86_64'];
}
- return ['darwin-aarch64']; // safe default for native runner builds
+ return []; // unknown arch: do not mislabel
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (name.endsWith('.app.tar.gz')) { | |
| if (filePath.includes('universal-apple-darwin') || /universal/i.test(name)) { | |
| return ['darwin-aarch64', 'darwin-x86_64']; | |
| } | |
| if (filePath.includes('aarch64-apple-darwin') || /aarch64|arm64/i.test(name)) { | |
| return ['darwin-aarch64']; | |
| } | |
| if (filePath.includes('x86_64-apple-darwin') || /x86_64|x64/i.test(name)) { | |
| return ['darwin-x86_64']; | |
| } | |
| return ['darwin-aarch64']; // safe default for native runner builds | |
| } | |
| if (name.endsWith('.app.tar.gz')) { | |
| if (filePath.includes('universal-apple-darwin') || /universal/i.test(name)) { | |
| return ['darwin-aarch64', 'darwin-x86_64']; | |
| } | |
| if (filePath.includes('aarch64-apple-darwin') || /aarch64|arm64/i.test(name)) { | |
| return ['darwin-aarch64']; | |
| } | |
| if (filePath.includes('x86_64-apple-darwin') || /x86_64|x64/i.test(name)) { | |
| return ['darwin-x86_64']; | |
| } | |
| return []; // unknown arch: do not mislabel | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/build-updater-manifest.mjs` around lines 128 - 139, The current
.app.tar.gz branch silently defaults unknown macOS bundles to
['darwin-aarch64']; change this to fail closed by throwing an Error (or
otherwise returning a failure) instead of guessing. In
bin/build-updater-manifest.mjs replace the fallback "return ['darwin-aarch64']"
in the name.endsWith('.app.tar.gz') block with code that throws a clear Error
(including name and filePath) so the caller can abort/prompt handling for
unknown macOS bundle mappings.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/tauri-release.yml:
- Around line 123-133: The install and pre-build steps ("Install dependencies
(pnpm)", "Install dependencies (npm)", "Pre-build command") run in the repo root
instead of the app folder; update those steps to run in the app package root by
adding working-directory: ${{ inputs.project-path }} to each step (or add a new
input like package-root/install-root and use that variable instead) so
dependency installs and the pre-build command execute inside the target app
(reference inputs.project-path and the three step names to locate the lines to
change).
- Around line 65-66: The workflow-level permissions block currently grants
contents: write to all jobs; move this permission into the publish job only by
removing or changing the top-level permissions: contents: write and instead add
a job-scoped permissions entry under the publish job (the job that runs
softprops/action-gh-release) so only publish has GITHUB_TOKEN with contents:
write while build matrix runners keep default (read) permissions.
- Around line 92-105: Replace mutable action tags with immutable full commit
SHAs: update uses entries for actions/checkout@v6, pnpm/action-setup@v6,
actions/setup-node@v6, dtolnay/rust-toolchain@stable,
tauri-apps/tauri-action@v0, cloudflare/wrangler-action@v3, and
softprops/action-gh-release@v3 to use the corresponding full-length commit SHAs
(e.g., actions/checkout@<full-sha>) so the workflow always references an
immutable commit; locate the usages by the exact strings "actions/checkout",
"pnpm/action-setup", "actions/setup-node", "dtolnay/rust-toolchain",
"tauri-apps/tauri-action", "cloudflare/wrangler-action", and
"softprops/action-gh-release" in the file and replace their tag suffixes with
the verified commit SHA for the intended release.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ddc20f57-5ffb-4226-a36b-1690701ce9b4
⛔ Files ignored due to path filters (1)
README.mdis excluded by!*.md
📒 Files selected for processing (1)
.github/workflows/tauri-release.yml
| permissions: | ||
| contents: write |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/tauri-release.ymlRepository: protoLabsAI/release-tools
Length of output: 13196
Move contents: write down to the publish job.
This workflow-level permissions block gives all build matrix runners (macOS, Windows, Linux) a write-capable GITHUB_TOKEN, but only the publish job needs it—specifically for the softprops/action-gh-release step that creates the GitHub Release. The build job performs no operations requiring contents write access. Since GitHub Actions supports job-scoped permissions, move this to the publish job only to follow the principle of least privilege and reduce the blast radius. (docs.github.com)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/tauri-release.yml around lines 65 - 66, The workflow-level
permissions block currently grants contents: write to all jobs; move this
permission into the publish job only by removing or changing the top-level
permissions: contents: write and instead add a job-scoped permissions entry
under the publish job (the job that runs softprops/action-gh-release) so only
publish has GITHUB_TOKEN with contents: write while build matrix runners keep
default (read) permissions.
| - uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup pnpm | ||
| if: inputs.pnpm-version != '' | ||
| uses: pnpm/action-setup@v6 | ||
| with: | ||
| version: ${{ inputs.pnpm-version }} | ||
|
|
||
| - uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: ${{ inputs.node-version }} | ||
| cache: ${{ inputs.pnpm-version != '' && 'pnpm' || 'npm' }} | ||
|
|
||
| - uses: dtolnay/rust-toolchain@stable |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '85,110p'Repository: protoLabsAI/release-tools
Length of output: 1027
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '150,165p'Repository: protoLabsAI/release-tools
Length of output: 996
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '176,190p'Repository: protoLabsAI/release-tools
Length of output: 928
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '190,205p'Repository: protoLabsAI/release-tools
Length of output: 787
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '249,260p'Repository: protoLabsAI/release-tools
Length of output: 639
🏁 Script executed:
cat -n .github/workflows/tauri-release.yml | sed -n '277,290p'Repository: protoLabsAI/release-tools
Length of output: 598
🏁 Script executed:
wc -l .github/workflows/tauri-release.ymlRepository: protoLabsAI/release-tools
Length of output: 109
🌐 Web query:
GitHub Actions security hardening pin full commit SHA
💡 Result:
To “pin GitHub Actions to full commit SHAs” you replace tag/branch references in uses: with the action’s full-length commit SHA (typically 40 hex characters). Example: - Before: uses: actions/checkout@v6 - After: uses: actions/checkout@5a4ac90 # v6 Why this is the recommended hardening: - GitHub’s security guidance states that pinning an action to a full-length commit SHA is the only way to use an action “as an immutable release,” helping mitigate the risk of a malicious actor modifying the action associated with a tag/branch. Operational note: - GitHub added policy enforcement (“SHA pinning enforcement”) so workflows that use actions not pinned to a full commit SHA can fail when the policy is enabled.
Citations:
- 1: https://docs.github.com/en/actions/reference/security/secure-use
- 2: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 3: https://github.com/marketplace/actions/ensure-sha-pinned-actions
🏁 Script executed:
# Check if there are any commit SHA pinned actions in the workflow to compare
rg -n '@[a-f0-9]{40}' .github/workflows/tauri-release.ymlRepository: protoLabsAI/release-tools
Length of output: 51
Pin all third-party actions to full commit SHAs.
This release workflow references third-party actions using mutable tags (@v6, @v0, @v3, @stable) while handling signing, notarization, R2, and release credentials. GitHub's security hardening guidance recommends pinning actions to full-length commit SHAs—the only immutable way to reference an action and mitigate the risk of malicious modifications to tagged releases.
Update the following actions: actions/checkout@v6, pnpm/action-setup@v6, actions/setup-node@v6, dtolnay/rust-toolchain@stable, tauri-apps/tauri-action@v0 (lines 156, 182, 196), cloudflare/wrangler-action@v3, and softprops/action-gh-release@v3.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/tauri-release.yml around lines 92 - 105, Replace mutable
action tags with immutable full commit SHAs: update uses entries for
actions/checkout@v6, pnpm/action-setup@v6, actions/setup-node@v6,
dtolnay/rust-toolchain@stable, tauri-apps/tauri-action@v0,
cloudflare/wrangler-action@v3, and softprops/action-gh-release@v3 to use the
corresponding full-length commit SHAs (e.g., actions/checkout@<full-sha>) so the
workflow always references an immutable commit; locate the usages by the exact
strings "actions/checkout", "pnpm/action-setup", "actions/setup-node",
"dtolnay/rust-toolchain", "tauri-apps/tauri-action",
"cloudflare/wrangler-action", and "softprops/action-gh-release" in the file and
replace their tag suffixes with the verified commit SHA for the intended
release.
| - name: Install dependencies (pnpm) | ||
| if: inputs.pnpm-version != '' | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Install dependencies (npm) | ||
| if: inputs.pnpm-version == '' | ||
| run: npm ci | ||
|
|
||
| - name: Pre-build command | ||
| if: inputs.pre-build-command != '' | ||
| run: ${{ inputs.pre-build-command }} |
There was a problem hiding this comment.
Scope install and pre-build steps to the app path.
Lines 123-133 still run dependency install and the optional pre-build from the repository root, while the actual Tauri build is the only place that respects inputs.project-path. A caller with project-path: apps/desktop and a standalone apps/desktop/package.json/lockfile will fail before tauri-action starts. Add a separate package-root/install-root input, or default these steps to the app path.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/tauri-release.yml around lines 123 - 133, The install and
pre-build steps ("Install dependencies (pnpm)", "Install dependencies (npm)",
"Pre-build command") run in the repo root instead of the app folder; update
those steps to run in the app package root by adding working-directory: ${{
inputs.project-path }} to each step (or add a new input like
package-root/install-root and use that variable instead) so dependency installs
and the pre-build command execute inside the target app (reference
inputs.project-path and the three step names to locate the lines to change).
Summary
Adds the pre-release half of the desktop pipeline so protoLabs apps can build, sign, notarize, and publish a Tauri 2 app to R2 without each repo copy-pasting (and drifting from) the same workflow. Same lifecycle as
rewrite-release-notes, just upstream of it.Two pieces:
.github/workflows/tauri-release.yml— reusable workflow. Consumer repos shrink theirbuild-desktop.ymlto ~10 lines:```yaml
jobs:
desktop:
uses: protoLabsAI/release-tools/.github/workflows/tauri-release.yml@v1
secrets: inherit
with:
project-path: apps/desktop
app-identifier: studio.protolabs.example
r2-bucket: example-desktop-releases
r2-public-base-url: https://dl.example.studio
```
Mac builds universal (aarch64 + x86_64) with full sign + notarize via `tauri-action`; Windows signs via SSL.com eSigner; Linux ships AppImage unsigned. The `publish` job uploads versioned artifacts + a no-cache `latest.json` to R2, then drafts a GH Release.
bin/build-updater-manifest.mjs— second CLI. Walks an artifact directory, finds platform binaries + matching.sigfiles, writes the JSON manifest the in-app Tauri updater pulls. Exits non-zero if any binary is missing its signature so the publish job fails loudly when signing didn't run.Why now
mythxengine is the first protoLabs desktop app gearing up for paid release; the architectural-extraction conversation flagged that we'd want this shared across all future protoLabs desktop apps from day one. Building it here, before the second consumer exists, keeps the API honest (one consumer + one well-specified abstraction beats three near-duplicates that get "unified" later).
The runtime side (license validation lib + Stripe webhook handler) deliberately does not land here — different audience (runtime engineers), different consumer shape (npm package imported into app code, not invoked from CI). Earmarked for a future `@protolabsai/desktop-app-kit`.
Conventions matched
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores