Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: CI

# Quality gate: runs on every push to main and on pull requests so regressions
# are caught before they reach a release tag.
on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
go:
name: Go (build · vet · test · lint)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disable credential persistence on checkout actions.

The actions/checkout@v5 actions default to persist-credentials: true, which leaves the GitHub token in ~/.netrc for the duration of the job. This is unnecessary for read-only CI and increases the surface for token exfiltration. Set persist-credentials: false explicitly.

🔒 Proposed fix: disable credential persistence
       - uses: actions/checkout@v5
         with:
           fetch-depth: 0
+          persist-credentials: false

Apply the same change to line 62.

Also applies to: 62-62

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 22-25: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 22, The `actions/checkout@v5` actions on
lines 22 and 62 use the default setting of `persist-credentials: true`, which
unnecessarily leaves the GitHub token in the file system for the job duration.
Add the `persist-credentials: false` parameter explicitly to both checkout
action usages to disable credential persistence and reduce the attack surface
for token exfiltration.

Source: Linters/SAST tools


⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin action versions to commit hashes for supply-chain security.

All action references use major version tags (@v5, @v6, @v7), which point to a moving target. Consider pinning to a commit hash (e.g., actions/checkout@a1b2c3d4e5f6...) to protect against unexpected upstream changes and prevent exploitation if an action's tag is mutated. This is especially important for security-sensitive workflows.

🔒 Proposed fix: pin action versions

Example pins (check the latest release commit for each action):

-      - uses: actions/checkout@v5
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871  # v5
-      - uses: actions/setup-go@v6
+      - uses: actions/setup-go@0a12ed9d6470c34bdae9eab0d4af20222284e98da  # v6
-        uses: golangci/golangci-lint-action@v7
+        uses: golangci/golangci-lint-action@971e8129edad04ea426c2b9e4eec8aaefb378f59  # v7
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@60edb5dd545925195646e3ea4c3f2f7fb38105ee  # v5

Find the actual commit SHAs via the GitHub action's release page or gh api repos/{owner}/{repo}/releases/{tag}.

Also applies to: 27-27, 47-47, 62-62, 64-64

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 22-25: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 22, Replace all action version references
that use major version tags (such as `@v5`, `@v6`, `@v7`) with pinned commit hashes
throughout the workflow file. Locate each action reference (such as
actions/checkout and any other actions used) on lines 22, 27, 47, 62, and 64,
and update them from the format `action-name@vX` to `action-name@{commit-hash}`
where the commit hash is obtained from the action's latest release page or
GitHub API. This ensures the workflow uses specific, immutable versions rather
than moving target tags.

Source: Linters/SAST tools

with:
# Full history so golangci-lint's only-new-issues can diff against base.
fetch-depth: 0

- uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'

# build-web (vite) is needed because internal/web embeds dist/* via
# //go:embed — without it the whole module fails to compile.
- uses: actions/setup-node@v5
with:
node-version: '22'
- name: Install pnpm
run: npm install -g pnpm

# registry_generated.go + the theme TS/CSS are generated (gitignored), and
# internal/web/dist must exist for the go:embed. `make generate build-web`
# produces all three. model generate fetches models.dev; theme is offline.
- name: Generate code + build frontend
run: make generate build-web

- name: Build
run: go build ./...

- name: Vet
run: go vet ./...

- name: Test
run: go test ./...

- name: golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: latest
# Gate regressions without forcing a flag-day cleanup of the existing
# lint debt (mostly upstream adk.AgentMiddleware deprecations). New
# issues introduced by a PR still fail.
only-new-issues: true

web:
name: Web (type-check · build)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

# Go is needed only to run the theme generator, which writes
# web/src/composables/themes.generated.ts + styles/tokens.generated.css
# (gitignored) — without them vue-tsc can't resolve ./themes.generated.
- uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'

- uses: actions/setup-node@v5
with:
node-version: '22'

- name: Install pnpm
run: npm install -g pnpm

- name: Generate theme assets
run: go generate ./internal/theme/...

- name: Install deps
working-directory: web
run: pnpm install --frozen-lockfile

- name: Type-check
working-directory: web
run: pnpm type-check

- name: Lint
working-directory: web
run: npx oxlint .

- name: Build
working-directory: web
run: npx vite build
Comment on lines +74 to +81

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for lint script in web/package.json
cat web/package.json | jq '.scripts.lint'

Repository: cnjack/jcode

Length of output: 71


🏁 Script executed:

cat web/package.json | jq '.scripts'

Repository: cnjack/jcode

Length of output: 353


Update the CI lint command to run the full linting workflow.

The CI runs npx oxlint . but the project defines a complete lint workflow in web/package.json that runs both oxlint and eslint via pnpm lint. The CI is currently skipping the eslint check. Update the lint step to: cd web && pnpm lint (or npm run lint from the web directory) to match the project's linting setup and ensure all linting rules are enforced in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 74 - 81, The Lint step in the CI
workflow currently only runs oxlint via `npx oxlint .` but skips the eslint
checks that are part of the complete linting workflow defined in the project.
Update the Lint step to change the run command from `npx oxlint .` to `cd web &&
pnpm lint` to execute the full linting workflow that includes both oxlint and
eslint checks as configured in the project's package.json.

Source: Coding guidelines

240 changes: 235 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Build binary
env:
Expand Down Expand Up @@ -148,8 +148,229 @@ jobs:
name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
path: dist/jcode-*

# Desktop (Tauri) app bundles. Each platform builds on a native runner because
# Tauri produces OS-native installers (.dmg / .msi / .deb / AppImage). The Go
# binary is compiled as the Tauri "sidecar" (binaries/jcode-<target-triple>) and
# embedded into the bundle; at runtime it serves the same web UI on loopback.
desktop:
needs: prepare
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# macOS Apple Silicon (M-series)
- os: macos-latest
triple: aarch64-apple-darwin
ext: ''
cgo: 1
bundles: dmg
label: macos-arm64
# macOS Intel
- os: macos-15-intel
triple: x86_64-apple-darwin
ext: ''
cgo: 1
bundles: dmg
label: macos-x64
# Windows x64
- os: windows-latest
triple: x86_64-pc-windows-msvc
ext: '.exe'
cgo: 0
bundles: 'msi,nsis'
label: windows-x64
# Linux x64
- os: ubuntu-22.04
triple: x86_64-unknown-linux-gnu
ext: ''
cgo: 0
bundles: 'deb,appimage'
label: linux-x64

steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: '22'

- name: Install pnpm
run: npm install -g pnpm

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.triple }}

- name: Cache Rust
uses: swatinem/rust-cache@v2
with:
workspaces: desktop/src-tauri
Comment on lines +155 to +218

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path
import re

path = Path(".github/workflows/release.yml")
for lineno, line in enumerate(path.read_text().splitlines(), 1):
    match = re.search(r'^\s*uses:\s*([^#\s]+)', line)
    if not match:
        continue
    ref = match.group(1)
    if "@" not in ref or not re.search(r'@[0-9a-fA-F]{40}$', ref):
        print(f"{path}:{lineno}: {ref}")
PY

Repository: cnjack/jcode

Length of output: 1181


🏁 Script executed:

cat -n .github/workflows/release.yml | head -220

Repository: cnjack/jcode

Length of output: 7143


🏁 Script executed:

cat -n .github/workflows/release.yml | sed -n '235,370p'

Repository: cnjack/jcode

Length of output: 7002


🏁 Script executed:

cat -n .github/workflows/release.yml | sed -n '394,405p'

Repository: cnjack/jcode

Length of output: 470


Harden the desktop release job before producing artifacts.

The desktop job (lines 155-218) and related build steps (lines 235-369, 394-405) run with the default checkout token persisted and use tag-pinned action references instead of immutable SHAs. The job inherits the global contents: write permission unnecessarily. Add least-privilege permissions, disable checkout credential persistence, and pin all action references to full commit SHAs.

All 13 actions in the affected ranges are tag-pinned (@v5, @v6, @stable, @v2) rather than SHA-pinned, creating supply chain risk.

🔒 Suggested hardening for the desktop job token boundary
   desktop:
     needs: prepare
+    permissions:
+      contents: read
     runs-on: ${{ matrix.os }}
@@
       - name: Checkout code
         uses: actions/checkout@v5
         with:
           fetch-depth: 0
+          persist-credentials: false

Pin all workflow action references to full SHAs across lines 155-218, 235-369, and 394-405. Example:

  • actions/checkout@v5actions/checkout@ebd315eff573669d375a86f4f7fb08efb87bdc18
  • actions/setup-go@v6actions/setup-go@0a12ed9d6470115ce6e23a8cdc390f3116122b7a
  • dtolnay/rust-toolchain@stable → use pinned commit SHA
  • swatinem/rust-cache@v2 → use pinned commit SHA
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 192-195: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 193-193: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 198-198: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 203-203: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 211-211: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 216-216: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 198-198: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)


[error] 203-203: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)


[error] 216-216: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 155 - 218, The desktop release
job uses tag-pinned action references such as actions/checkout@v5,
actions/setup-go@v6, dtolnay/rust-toolchain@stable, and swatinem/rust-cache@v2,
which creates supply chain security risk. Replace all tag-pinned action
references throughout the desktop job configuration with their corresponding
full commit SHAs to ensure immutable versions. For each action, look up its
latest stable release commit SHA and update the reference from the tag format
(e.g., `@v5`) to the full commit SHA format to harden the workflow against supply
chain attacks.

Source: Linters/SAST tools


- name: Install Linux dependencies
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libssl-dev \
libxdo-dev \
libfuse2 \
patchelf \
file

- name: Download prepared artifacts
uses: actions/download-artifact@v5
with:
name: prepared
path: internal/

- name: Get version
id: get_version
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Sync bundle version
shell: bash
run: |
VER="${{ steps.get_version.outputs.version }}"
VER="${VER#v}"
node -e "const fs=require('fs');const f='desktop/src-tauri/tauri.conf.json';const j=JSON.parse(fs.readFileSync(f));j.version='${VER}';fs.writeFileSync(f, JSON.stringify(j,null,2)+'\n')"
echo "Set desktop bundle version to ${VER}"

- name: Build sidecar
shell: bash
env:
CGO_ENABLED: ${{ matrix.cgo }}
run: |
VERSION="${{ steps.get_version.outputs.version }}"
COMMIT="$(git rev-parse --short HEAD)"
mkdir -p desktop/src-tauri/binaries
go build -trimpath \
-ldflags "-s -w -X github.com/cnjack/jcode/internal/command.Version=${VERSION} -X github.com/cnjack/jcode/internal/command.GitCommit=${COMMIT}" \
-o "desktop/src-tauri/binaries/jcode-${{ matrix.triple }}${{ matrix.ext }}" \
Comment on lines +241 to +270

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the workflow file
git ls-files .github/workflows/release.yml

Repository: cnjack/jcode

Length of output: 86


🏁 Script executed:

# Get the file and check the specific lines
cat -n .github/workflows/release.yml | sed -n '240,275p'

Repository: cnjack/jcode

Length of output: 1881


🏁 Script executed:

# Let's also check the full context of these steps to understand the flow
cat -n .github/workflows/release.yml | sed -n '200,300p'

Repository: cnjack/jcode

Length of output: 4800


Pass version input through environment variables and validate the format before embedding in scripts.

Lines 246, 255, and 265 inject version strings directly from GitHub Actions context expressions into bash and Node scripts, creating injection vulnerabilities. A crafted version like v1.0.0"; rm -rf /; echo " would escape the bash context and execute arbitrary commands. Adopt safer practices:

  1. Use environment variables to isolate untrusted input from script evaluation
  2. Validate the version format against a semantic version regex before use
  3. Pass data to Node/Go through environment variables rather than inline script strings

See suggested implementation in the details section below:

🛡️ Safer version handling
       - name: Get version
         id: get_version
         shell: bash
+        env:
+          WORKFLOW_VERSION: ${{ github.event.inputs.version }}
         run: |
-          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
-            VERSION="${{ github.event.inputs.version }}"
+          if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
+            VERSION="$WORKFLOW_VERSION"
           else
             VERSION="${GITHUB_REF#refs/tags/}"
           fi
-          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+          if [[ ! "$VERSION" =~ ^v?[0-9]+[.][0-9]+[.][0-9]+(-[0-9A-Za-z.-]+)?([+][0-9A-Za-z.-]+)?$ ]]; then
+            echo "::error::invalid release version: $VERSION"
+            exit 1
+          fi
+          printf 'version=%s\n' "$VERSION" >> "$GITHUB_OUTPUT"
@@
       - name: Sync bundle version
         shell: bash
+        env:
+          RELEASE_VERSION: ${{ steps.get_version.outputs.version }}
         run: |
-          VER="${{ steps.get_version.outputs.version }}"
-          VER="${VER#v}"
-          node -e "const fs=require('fs');const f='desktop/src-tauri/tauri.conf.json';const j=JSON.parse(fs.readFileSync(f));j.version='${VER}';fs.writeFileSync(f, JSON.stringify(j,null,2)+'\n')"
+          VER="${RELEASE_VERSION#v}"
+          export BUNDLE_VERSION="$VER"
+          node <<'NODE'
+          const fs = require('fs')
+          const f = 'desktop/src-tauri/tauri.conf.json'
+          const j = JSON.parse(fs.readFileSync(f, 'utf8'))
+          j.version = process.env.BUNDLE_VERSION
+          fs.writeFileSync(f, JSON.stringify(j, null, 2) + '\n')
+          NODE
           echo "Set desktop bundle version to ${VER}"
@@
         env:
           CGO_ENABLED: ${{ matrix.cgo }}
+          RELEASE_VERSION: ${{ steps.get_version.outputs.version }}
         run: |
-          VERSION="${{ steps.get_version.outputs.version }}"
+          VERSION="$RELEASE_VERSION"
           COMMIT="$(git rev-parse --short HEAD)"
🧰 Tools
🪛 zizmor (1.25.2)

[error] 246-246: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 255-255: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 265-265: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 241 - 270, The version strings
from GitHub Actions context are being injected directly into bash and Node.js
scripts, creating command injection vulnerabilities. In the get_version step,
add validation to ensure the version matches a semantic version regex pattern
before outputting it. In the Sync bundle version and Build sidecar steps, pass
the version as an environment variable instead of embedding it directly in
inline scripts, then modify the Node.js script that updates tauri.conf.json to
read the version from the environment variable rather than using template
literal injection, and ensure the go build command similarly references the
version through environment variables instead of direct interpolation.

Source: Linters/SAST tools

./cmd/jcode/

# macOS signing + notarization. Everything here is optional: with no secrets
# the build still succeeds and produces an UNSIGNED bundle (Gatekeeper warns
# on first launch). When the secrets are present, the Tauri CLI imports the
# cert into a temp keychain, signs with Developer ID, then notarizes.
#
# We resolve the credentials into $GITHUB_ENV here (rather than as a static
# env block on the build step) for two reasons: the notarization .p8 is a
# FILE that must be written to disk from a base64 secret, and the two notary
# auth methods are mutually exclusive — exporting only the configured one
# avoids an empty APPLE_ID accidentally selecting the Apple-ID path.
- name: Set up macOS signing & notarization
if: runner.os == 'macOS'
shell: bash
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
# Notarization — App Store Connect API key (.p8), preferred for CI.
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
# Notarization — Apple ID (alternative; use one method, not both).
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
# Code-signing certificate (Tauri imports it from these env vars).
if [ -n "$APPLE_CERTIFICATE" ]; then
{
echo "APPLE_CERTIFICATE=$APPLE_CERTIFICATE"
echo "APPLE_CERTIFICATE_PASSWORD=$APPLE_CERTIFICATE_PASSWORD"
} >> "$GITHUB_ENV"
[ -n "$APPLE_SIGNING_IDENTITY" ] && echo "APPLE_SIGNING_IDENTITY=$APPLE_SIGNING_IDENTITY" >> "$GITHUB_ENV"
echo "code-signing: enabled"
else
echo "code-signing: DISABLED (APPLE_CERTIFICATE unset) — bundle will be unsigned"
fi

# Notarization auth — pick exactly one method.
if [ -n "$APPLE_API_KEY_P8_BASE64" ]; then
# APPLE_API_KEY is the Key ID STRING; the .p8 key material is a file
# referenced by APPLE_API_KEY_PATH. Decode the base64 secret to disk.
KEY_PATH="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
echo -n "$APPLE_API_KEY_P8_BASE64" | base64 --decode > "$KEY_PATH"
{
echo "APPLE_API_ISSUER=$APPLE_API_ISSUER"
echo "APPLE_API_KEY=$APPLE_API_KEY_ID"
echo "APPLE_API_KEY_PATH=$KEY_PATH"
} >> "$GITHUB_ENV"
echo "notarization: App Store Connect API key"
elif [ -n "$APPLE_ID" ]; then
{
echo "APPLE_ID=$APPLE_ID"
echo "APPLE_PASSWORD=$APPLE_PASSWORD"
echo "APPLE_TEAM_ID=$APPLE_TEAM_ID"
} >> "$GITHUB_ENV"
echo "notarization: Apple ID"
else
echo "notarization: DISABLED (no API key or Apple ID secrets)"
fi

- name: Build desktop bundle
shell: bash
working-directory: desktop
env:
# Lets linuxdeploy build the AppImage on runners without FUSE mounted.
# The macOS signing/notarization vars arrive via $GITHUB_ENV (above).
APPIMAGE_EXTRACT_AND_RUN: 1
run: |
pnpm install --frozen-lockfile
pnpm tauri build --bundles ${{ matrix.bundles }}

- name: Collect bundles
shell: bash
run: |
mkdir -p dist-desktop
B="desktop/src-tauri/target/release/bundle"
shopt -s nullglob 2>/dev/null || true
for f in "$B"/dmg/*.dmg "$B"/msi/*.msi "$B"/nsis/*-setup.exe "$B"/deb/*.deb "$B"/appimage/*.AppImage; do
[ -f "$f" ] && cp "$f" dist-desktop/
done
cd dist-desktop
for f in *; do
[ -f "$f" ] || continue
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$f" > "$f.sha256"
else
shasum -a 256 "$f" > "$f.sha256"
fi
done
ls -la

- name: Upload desktop bundles
uses: actions/upload-artifact@v5
with:
name: desktop-${{ matrix.label }}
path: dist-desktop/*
if-no-files-found: error

release:
needs: build
needs: [build, desktop]
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -168,18 +389,26 @@ jobs:
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Download all artifacts
- name: Download CLI binaries
uses: actions/download-artifact@v5
with:
path: dist
pattern: binaries-*
merge-multiple: true

- name: Download desktop bundles
uses: actions/download-artifact@v5
with:
path: dist-desktop
pattern: desktop-*
merge-multiple: true

- name: List artifacts
run: |
ls -la dist/
echo "── CLI binaries ──"; ls -la dist/
echo "── Desktop bundles ──"; ls -la dist-desktop/

- name: Create Release
uses: softprops/action-gh-release@v2
Expand All @@ -190,6 +419,7 @@ jobs:
prerelease: ${{ contains(steps.get_version.outputs.version, 'alpha') || contains(steps.get_version.outputs.version, 'beta') || contains(steps.get_version.outputs.version, 'rc') }}
files: |
dist/jcode-*
dist-desktop/*
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
Loading
Loading