Skip to content

fix(ci): pass TAURI_SIGNING_PRIVATE_KEY to tauri build step (#241) #327

fix(ci): pass TAURI_SIGNING_PRIVATE_KEY to tauri build step (#241)

fix(ci): pass TAURI_SIGNING_PRIVATE_KEY to tauri build step (#241) #327

Workflow file for this run

name: CI
on:
# Explicit types. Excluding 'closed' stops GH from re-running CI after
# a PR merges or closes (when the merge closed the prior run, the
# 'closed' event used to trigger a redundant 're-verify already-green
# code' run).
pull_request:
types: [opened, synchronize, reopened]
branches: [main]
push:
branches: [main]
# Cancel any in-flight or queued runs in this group when a new run
# starts. Keyed by workflow + PR number (for PRs) or workflow + ref
# (for direct branch pushes) so concurrent PRs and unrelated runs
# don't trip over each other. Industry standard pattern.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# Detect which paths changed in this PR. Other jobs conditionally run
# based on these outputs, so doc-only PRs skip the heavy Rust/Node jobs
# (saves ~5min CI time per docs PR).
detect-changes:
name: Detect changed paths
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
rust: ${{ steps.filter.outputs.rust }}
npm: ${{ steps.filter.outputs.npm }}
cargo: ${{ steps.filter.outputs.cargo }}
docs: ${{ steps.filter.outputs.docs }}
workflows: ${{ steps.filter.outputs.workflows }}
config: ${{ steps.filter.outputs.config }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
id: filter
with:
base: ${{ github.base_ref }}
# Keys become job outputs. Values are glob patterns matched against
# changed files. A key evaluates to "true" if any matching file
# changed, "false" otherwise.
filters: |
frontend:
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'vitest.config.ts'
- 'vitest.browser.config.ts'
- 'eslint.config.js'
rust:
- 'src-tauri/**'
- 'src-tauri/Cargo.toml'
- 'src-tauri/Cargo.lock'
npm:
- 'package.json'
- 'package-lock.json'
cargo:
- 'src-tauri/Cargo.toml'
- 'src-tauri/Cargo.lock'
docs:
- 'docs/**'
- 'README.md'
- '.opencode/AGENTS.md'
workflows:
- '.github/workflows/**'
config:
- 'lefthook.yml'
- 'eslint.config.js'
- 'dprint.json'
- 'vitest.config.ts'
- 'vitest.browser.config.ts'
- 'opencode.json'
npm-audit:
name: npm Audit
needs: detect-changes
if: needs.detect-changes.outputs.npm == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Audit signatures (verify package integrity)
run: npm audit signatures
- name: Audit vulnerabilities
run: npm audit --audit-level=critical
# NOTE: cargo-audit runs via the cargo-audit-check wrapper script (not
# raw cargo audit). The wrapper:
# - Fails ONLY on real vulnerabilities (RUSTSEC type "vulnerability")
# - Logs unmaintained/yanked/unsound warnings to job output without blocking
# - Uses zero --ignore flags (no silencing)
#
# 17 transitive advisories remain (gtk-rs GTK3 bindings + glib unsound +
# unic-* sub-deps) that cannot be fixed without upstream action.
# Tracked in #204.
cargo-audit:
name: cargo Audit
needs: detect-changes
if: needs.detect-changes.outputs.cargo == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1.0.0
- name: Install jq
run: sudo apt-get install -y jq
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Run audit check (vulnerabilities fail, warnings log)
run: bash "$GITHUB_WORKSPACE/scripts/cargo-audit-check.sh"
env:
CARGO_WORKSPACE_DIR: src-tauri
check-versions:
name: Version Consistency
needs: detect-changes
if: needs.detect-changes.outputs.cargo == 'true' || needs.detect-changes.outputs.npm == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Check versions match across package.json, Cargo.toml, tauri.conf.json
run: |
jsver=$(node -p "require('./package.json').version")
rsver=$(grep '^version' src-tauri/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
taver=$(node -p "require('./src-tauri/tauri.conf.json').version")
if [ "$jsver" != "$rsver" ] || [ "$jsver" != "$taver" ]; then
echo "Version mismatch!"
echo " package.json: $jsver"
echo " Cargo.toml: $rsver"
echo " tauri.conf.json: $taver"
exit 1
fi
echo "All versions match: $jsver"
lint-ts:
name: Lint (TypeScript)
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: TypeScript lint
run: npm run lint
- name: TypeScript type check
run: npm run type-check
lint-rust:
name: Lint (Rust)
needs: detect-changes
if: needs.detect-changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1.0.0
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: src-tauri
- name: Install system dependencies
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
version: 1.0
- name: Rust format check
run: cargo fmt --all -- --check
working-directory: src-tauri
- name: Rust clippy
run: cargo clippy --all-targets --all-features -- -D warnings
working-directory: src-tauri
test-rust:
name: Test Rust
needs: detect-changes
if: needs.detect-changes.outputs.rust == 'true'
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: test_root_password
MYSQL_DATABASE: test_db
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password
ports:
- 13306:3306
options: >-
--health-cmd "mysqladmin ping -h localhost"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1.0.0
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: src-tauri
- name: Seed test database
run: mysql -h 127.0.0.1 -P 13306 -u root -ptest_root_password < tests/fixtures/sql/seed.sql
- name: Install cargo-tarpaulin
run: cargo install cargo-tarpaulin --locked
- name: Run Rust tests with coverage
run: cargo tarpaulin -p mas-core -p mas-export -p mas-admin --out Lcov --output-dir coverage/
working-directory: src-tauri
test-frontend:
name: Test Frontend
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Upload coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: frontend-coverage
path: coverage/
# Lightweight check: dprint format on changed files only (any markdown/
# TS/JSON touched). Catches formatting issues that lefthook might miss
# (e.g., PR opened via GitHub UI without local pre-commit hook).
# Restricted to PR diff so an unrelated pre-existing format issue in
# another file doesn't block this PR.
dprint:
name: Format Check (dprint)
needs: detect-changes
if: |
needs.detect-changes.outputs.docs == 'true' ||
needs.detect-changes.outputs.frontend == 'true' ||
needs.detect-changes.outputs.config == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: List changed files
id: changes
run: |
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.(md|mdx|markdown|ts|tsx|js|jsx|json|yml|yaml|toml)$' || true)
if [ -z "$CHANGED" ]; then
echo "No format-checkable files changed. Skipping."
echo "has_files=false" >> "$GITHUB_OUTPUT"
else
echo "Format-checking:"
echo "$CHANGED" | sed 's/^/ /'
echo "has_files=true" >> "$GITHUB_OUTPUT"
echo "files<<EOF" >> "$GITHUB_OUTPUT"
echo "$CHANGED" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
fi
- name: Check formatting on changed files
if: steps.changes.outputs.has_files == 'true'
run: |
echo "${{ steps.changes.outputs.files }}" | tr '\n' '\0' | xargs -0 npx dprint check
test-e2e:
name: E2E Tests
if: false
runs-on: ubuntu-latest
needs: [detect-changes, test-rust, test-frontend]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
- name: Install system dependencies
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
version: 1.0
- name: Start test databases
run: docker compose -f docker-compose.test.yml up -d
- name: Wait for databases
run: |
echo "Waiting for MySQL 8..."
timeout 60 bash -c 'until docker exec mas-mysql-8 mysqladmin ping -h localhost -u root -ptest_root_password 2>/dev/null; do sleep 2; done' || echo "WARNING: MySQL 8 did not become ready"
echo "Waiting for MySQL 5.7..."
timeout 60 bash -c 'until docker exec mas-mysql-57 mysqladmin ping -h localhost -u root -ptest_root_password 2>/dev/null; do sleep 2; done' || echo "WARNING: MySQL 5.7 did not become ready"
echo "Waiting for MariaDB..."
timeout 60 bash -c 'until docker exec mas-mariadb-11 mariadb-admin ping -h localhost -u root -ptest_root_password 2>/dev/null; do sleep 2; done' || echo "WARNING: MariaDB did not become ready"
echo "Database wait complete"
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Build application
run: npm run build
- name: Run E2E tests
run: npm run test:e2e
env:
TEST_MYSQL8_URL: "mysql://test_user:test_password@127.0.0.1:13306/test_db"
TEST_MYSQL57_URL: "mysql://test_user:test_password@127.0.0.1:13307/test_db"
TEST_MARIADB_URL: "mysql://test_user:test_password@127.0.0.1:13308/test_db"
- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-results
path: |
playwright-report/
test-results/
- name: Stop test databases
if: always()
run: docker compose -f docker-compose.test.yml down -v