fix(e2e): fix biome empty pattern and useAwait in agents beforeEach #217
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Packages | |
| on: | |
| push: | |
| tags: | |
| - "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].[0-9][0-9]" | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Build release packages without publishing artifacts, images, or release assets" | |
| required: false | |
| default: false | |
| type: boolean | |
| pre_release: | |
| description: "Publish as pre-release (builds and uploads artifacts but skips Homebrew tap, deploy templates, and releases.json updates)" | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: {} | |
| env: | |
| # Must match rust-toolchain.toml (single source of truth for the nightly pin). | |
| NIGHTLY_TOOLCHAIN: nightly-2025-12-27 | |
| RELEASE_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && 'true' || 'false' }} | |
| RELEASE_PRE_RELEASE: ${{ github.event_name == 'workflow_dispatch' && inputs.pre_release && 'true' || 'false' }} | |
| jobs: | |
| zizmor: | |
| name: Workflow Security | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping workflow security checks" | |
| - uses: zizmorcore/zizmor-action@135698455da5c3b3e55f73f4419e481ab68cdd95 # v0.4.1 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| advanced-security: false | |
| online-audits: false | |
| validate-tag: | |
| if: startsWith(github.ref, 'refs/tags/') | |
| runs-on: ubuntu-latest | |
| name: Validate Tag Format | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check tag matches YYYYMMDD.NN | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| if [[ ! "$TAG" =~ ^[0-9]{8}\.[0-9]{2}$ ]]; then | |
| echo "::error::Tag '$TAG' must match YYYYMMDD.NN format" | |
| exit 1 | |
| fi | |
| biome: | |
| needs: zizmor | |
| runs-on: ubuntu-latest | |
| name: Biome | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping biome checks" | |
| - uses: biomejs/setup-biome@29711cbb52afee00eb13aeb30636592f9edc0088 # v2.7.0 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| version: "2.4.6" | |
| - if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: biome ci crates/web/ui/src/ crates/web/ui/e2e/ | |
| - if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: ./scripts/i18n-check.sh | |
| fmt: | |
| needs: zizmor | |
| runs-on: ubuntu-latest | |
| name: Format | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping rustfmt check" | |
| - uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| toolchain: ${{ env.NIGHTLY_TOOLCHAIN }} | |
| components: rustfmt | |
| - if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: cargo fmt --all -- --check | |
| clippy: | |
| needs: [fmt, biome] | |
| runs-on: [self-hosted, Linux, X64] | |
| container: | |
| image: nvidia/cuda:12.4.1-devel-ubuntu22.04 | |
| env: | |
| LD_LIBRARY_PATH: /usr/local/cuda/compat:/usr/local/nvidia/lib:/usr/local/nvidia/lib64 | |
| name: Clippy | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Install git for proper clone (CUDA image has none) | |
| run: apt-get update -qq && apt-get install -y -qq --no-install-recommends git ca-certificates >/dev/null | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Mark workspace safe for gix (container uid mismatch) | |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping clippy job" | |
| - name: Clean up corrupted cargo config | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: rm -f ~/.cargo/config.toml | |
| - name: Install build dependencies | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: bash ./scripts/install-linux-build-deps.sh | |
| - uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| toolchain: ${{ env.NIGHTLY_TOOLCHAIN }} | |
| components: clippy, rustfmt | |
| - name: Initialize git repo in llama-cpp source to satisfy cmake | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| cargo fetch --locked | |
| LLAMA_SRC=$(find ~/.cargo/registry/src -name "llama-cpp-sys-2-*" -type d 2>/dev/null | head -1) | |
| if [ -n "$LLAMA_SRC" ] && [ ! -d "$LLAMA_SRC/.git" ]; then | |
| cd "$LLAMA_SRC" | |
| git init | |
| git config user.email "ci@example.com" | |
| git config user.name "CI" | |
| git add -A | |
| git commit -m "init" --allow-empty | |
| git tag v0.0.0 | |
| fi | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| ./scripts/download-tailwindcss-cli.sh tailwindcss-linux-x64 | |
| TAILWINDCSS=./tailwindcss-linux-x64 ./scripts/build-web-assets.sh | |
| - if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: cargo clippy -Z unstable-options --workspace --all-features --all-targets --timings -- -D warnings | |
| test: | |
| needs: [fmt, biome] | |
| runs-on: [self-hosted, Linux, X64] | |
| container: | |
| image: nvidia/cuda:12.4.1-devel-ubuntu22.04 | |
| env: | |
| LD_LIBRARY_PATH: /usr/local/cuda/compat:/usr/local/nvidia/lib:/usr/local/nvidia/lib64 | |
| name: Test | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Install git for proper clone (CUDA image has none) | |
| run: apt-get update -qq && apt-get install -y -qq --no-install-recommends git ca-certificates >/dev/null | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Mark workspace safe for gix (container uid mismatch) | |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping unit test job" | |
| - name: Clean up corrupted cargo config | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: rm -f ~/.cargo/config.toml | |
| - name: Install build dependencies | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: bash ./scripts/install-linux-build-deps.sh | |
| - uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| toolchain: stable | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install QMD CLI | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| npm install -g @tobilu/qmd | |
| qmd --version | |
| - uses: taiki-e/install-action@f176c07a0a40cbfdd08ee9aa8bf1655701d11e69 # v2.67.25 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| tool: cargo-nextest | |
| - name: Initialize git repo in llama-cpp source to satisfy cmake | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| cargo fetch --locked | |
| LLAMA_SRC=$(find ~/.cargo/registry/src -name "llama-cpp-sys-2-*" -type d 2>/dev/null | head -1) | |
| if [ -n "$LLAMA_SRC" ] && [ ! -d "$LLAMA_SRC/.git" ]; then | |
| cd "$LLAMA_SRC" | |
| git init | |
| git config user.email "ci@example.com" | |
| git config user.name "CI" | |
| git add -A | |
| git commit -m "init" --allow-empty | |
| git tag v0.0.0 | |
| fi | |
| - name: Install npm dependencies | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| ./scripts/download-tailwindcss-cli.sh tailwindcss-linux-x64 | |
| TAILWINDCSS=./tailwindcss-linux-x64 ./scripts/build-web-assets.sh | |
| - if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: cargo nextest run --all-features --profile ci | |
| e2e: | |
| name: E2E Tests | |
| needs: [fmt, biome] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Dry-run short-circuit | |
| if: ${{ env.RELEASE_DRY_RUN == 'true' }} | |
| run: echo "Dry run enabled, skipping E2E job" | |
| - uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| toolchain: stable | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| working-directory: crates/web/ui | |
| run: npm ci | |
| - name: Build web assets | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: | | |
| ./scripts/download-tailwindcss-cli.sh tailwindcss-linux-x64 | |
| TAILWINDCSS=./tailwindcss-linux-x64 ./scripts/build-web-assets.sh | |
| - name: Build moltis binary | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| run: cargo build --bin moltis | |
| - name: Install Playwright browsers | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| working-directory: crates/web/ui | |
| run: npx playwright install --with-deps chromium | |
| - name: Run E2E tests | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| working-directory: crates/web/ui | |
| env: | |
| CI: "true" | |
| run: npx playwright test | |
| - name: Upload test results | |
| if: ${{ !cancelled() && env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: e2e-test-results-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: | | |
| crates/web/ui/playwright-report/ | |
| crates/web/ui/test-results/ | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| build-deb: | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| arch: amd64 | |
| os: ubuntu-22.04 | |
| - target: aarch64-unknown-linux-gnu | |
| arch: arm64 | |
| os: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.os }} | |
| name: Build .deb (${{ matrix.arch }}) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve release version | |
| id: release_version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }}, wasm32-wasip2 | |
| - name: Install cargo-deb | |
| run: cargo install cargo-deb | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| ARCH=$(uname -m) | |
| case "$ARCH" in x86_64) TW="tailwindcss-linux-x64";; aarch64) TW="tailwindcss-linux-arm64";; esac | |
| ./scripts/download-tailwindcss-cli.sh "$TW" | |
| TAILWINDCSS=./$TW ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Build release binary | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.release_version.outputs.version }} | |
| run: ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Stage WASM assets for cargo-deb | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| run: bash ./scripts/stage-wasm-package-assets.sh "target/$BUILD_TARGET/release" | |
| - name: Build .deb package | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.release_version.outputs.version }} | |
| run: cargo deb -p moltis --no-build --target "$BUILD_TARGET" --deb-version "$MOLTIS_VERSION" | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: '*.deb' | |
| working-directory: target/${{ matrix.target }}/debian | |
| - name: Upload .deb artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ matrix.arch }}.deb | |
| path: | | |
| target/${{ matrix.target }}/debian/*.deb | |
| target/${{ matrix.target }}/debian/*.sha256 | |
| target/${{ matrix.target }}/debian/*.sha512 | |
| target/${{ matrix.target }}/debian/*.sig | |
| target/${{ matrix.target }}/debian/*.crt | |
| build-rpm: | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| arch: x86_64 | |
| os: ubuntu-22.04 | |
| - target: aarch64-unknown-linux-gnu | |
| arch: aarch64 | |
| os: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.os }} | |
| name: Build .rpm (${{ matrix.arch }}) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve release version | |
| id: release_version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }}, wasm32-wasip2 | |
| - name: Install cargo-generate-rpm | |
| run: cargo install cargo-generate-rpm | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| ARCH=$(uname -m) | |
| case "$ARCH" in x86_64) TW="tailwindcss-linux-x64";; aarch64) TW="tailwindcss-linux-arm64";; esac | |
| ./scripts/download-tailwindcss-cli.sh "$TW" | |
| TAILWINDCSS=./$TW ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Build release binary | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.release_version.outputs.version }} | |
| run: ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Build .rpm package | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.release_version.outputs.version }} | |
| run: cargo generate-rpm -p crates/cli --target "$BUILD_TARGET" --set-metadata="version=\"$MOLTIS_VERSION\"" | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: '*.rpm' | |
| working-directory: target/${{ matrix.target }}/generate-rpm | |
| - name: Upload .rpm artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ matrix.arch }}.rpm | |
| path: | | |
| target/${{ matrix.target }}/generate-rpm/*.rpm | |
| target/${{ matrix.target }}/generate-rpm/*.sha256 | |
| target/${{ matrix.target }}/generate-rpm/*.sha512 | |
| target/${{ matrix.target }}/generate-rpm/*.sig | |
| target/${{ matrix.target }}/generate-rpm/*.crt | |
| build-arch: | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| arch: x86_64 | |
| os: ubuntu-22.04 | |
| - target: aarch64-unknown-linux-gnu | |
| arch: aarch64 | |
| os: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.os }} | |
| name: Build .pkg.tar.zst (${{ matrix.arch }}) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }}, wasm32-wasip2 | |
| - name: Install packaging tools | |
| run: sudo apt-get update && sudo apt-get install -y fakeroot zstd | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| ARCH=$(uname -m) | |
| case "$ARCH" in x86_64) TW="tailwindcss-linux-x64";; aarch64) TW="tailwindcss-linux-arm64";; esac | |
| ./scripts/download-tailwindcss-cli.sh "$TW" | |
| TAILWINDCSS=./$TW ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Determine package version | |
| id: version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release binary | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.version.outputs.version }} | |
| run: ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Build .pkg.tar.zst | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MATRIX_ARCH: ${{ matrix.arch }} | |
| run: | | |
| PKG_DIR="pkg-root" | |
| mkdir -p "$PKG_DIR/usr/bin" "$PKG_DIR/usr/share/moltis/web" "$PKG_DIR/usr/share/moltis/wasm" | |
| cp "target/$BUILD_TARGET/release/moltis" "$PKG_DIR/usr/bin/moltis" | |
| chmod 755 "$PKG_DIR/usr/bin/moltis" | |
| cp -R crates/web/src/assets/. "$PKG_DIR/usr/share/moltis/web/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_calc.wasm" "$PKG_DIR/usr/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_fetch.wasm" "$PKG_DIR/usr/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_search.wasm" "$PKG_DIR/usr/share/moltis/wasm/" | |
| cat > "$PKG_DIR/.PKGINFO" <<PKGINFO | |
| pkgname = moltis | |
| pkgver = ${VERSION}-1 | |
| pkgdesc = Personal AI gateway inspired by OpenClaw | |
| url = https://www.moltis.org/ | |
| arch = $MATRIX_ARCH | |
| license = MIT | |
| PKGINFO | |
| cd "$PKG_DIR" | |
| fakeroot -- tar --zstd -cf "../moltis-${VERSION}-1-${MATRIX_ARCH}.pkg.tar.zst" .PKGINFO usr/ | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-${{ steps.version.outputs.version }}-1-${{ matrix.arch }}.pkg.tar.zst | |
| - name: Upload .pkg.tar.zst artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ matrix.arch }}.pkg.tar.zst | |
| path: | | |
| *.pkg.tar.zst | |
| *.pkg.tar.zst.sha256 | |
| *.pkg.tar.zst.sha512 | |
| *.pkg.tar.zst.sig | |
| *.pkg.tar.zst.crt | |
| build-appimage: | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| arch: x86_64 | |
| os: ubuntu-22.04 | |
| appimagetool_arch: x86_64 | |
| - target: aarch64-unknown-linux-gnu | |
| arch: aarch64 | |
| os: ubuntu-24.04-arm | |
| appimagetool_arch: aarch64 | |
| runs-on: ${{ matrix.os }} | |
| name: Build AppImage (${{ matrix.arch }}) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }}, wasm32-wasip2 | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| ARCH=$(uname -m) | |
| case "$ARCH" in x86_64) TW="tailwindcss-linux-x64";; aarch64) TW="tailwindcss-linux-arm64";; esac | |
| ./scripts/download-tailwindcss-cli.sh "$TW" | |
| TAILWINDCSS=./$TW ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Determine package version | |
| id: version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release binary | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.version.outputs.version }} | |
| run: ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Build AppImage | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MATRIX_ARCH: ${{ matrix.arch }} | |
| APPIMAGETOOL_ARCH: ${{ matrix.appimagetool_arch }} | |
| run: | | |
| # Download appimagetool matching the runner architecture | |
| wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${APPIMAGETOOL_ARCH}.AppImage" -O appimagetool | |
| chmod +x appimagetool | |
| # Build AppDir structure | |
| APP_DIR="moltis.AppDir" | |
| mkdir -p "$APP_DIR/usr/bin" "$APP_DIR/usr/share/moltis/web" "$APP_DIR/usr/share/moltis/wasm" | |
| cp "target/$BUILD_TARGET/release/moltis" "$APP_DIR/usr/bin/moltis" | |
| chmod 755 "$APP_DIR/usr/bin/moltis" | |
| cp -R crates/web/src/assets/. "$APP_DIR/usr/share/moltis/web/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_calc.wasm" "$APP_DIR/usr/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_fetch.wasm" "$APP_DIR/usr/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_search.wasm" "$APP_DIR/usr/share/moltis/wasm/" | |
| # Create .desktop file | |
| cat > "$APP_DIR/moltis.desktop" <<DESKTOP | |
| [Desktop Entry] | |
| Type=Application | |
| Name=Moltis | |
| Exec=moltis | |
| Icon=moltis | |
| Categories=Network; | |
| Terminal=true | |
| DESKTOP | |
| # Create minimal icon | |
| cat > "$APP_DIR/moltis.svg" <<SVG | |
| <svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"><rect width="256" height="256" fill="#333"/><text x="128" y="140" font-size="120" text-anchor="middle" fill="white">M</text></svg> | |
| SVG | |
| ln -s moltis.svg "$APP_DIR/.DirIcon" | |
| # Create AppRun | |
| cat > "$APP_DIR/AppRun" <<'APPRUN' | |
| #!/bin/sh | |
| SELF=$(readlink -f "$0") | |
| HERE=${SELF%/*} | |
| export MOLTIS_SHARE_DIR="$HERE/usr/share/moltis" | |
| exec "$HERE/usr/bin/moltis" "$@" | |
| APPRUN | |
| chmod +x "$APP_DIR/AppRun" | |
| # Package - use --appimage-extract-and-run to avoid FUSE requirement in CI | |
| ARCH="$MATRIX_ARCH" ./appimagetool --appimage-extract-and-run "$APP_DIR" "moltis-${VERSION}-${MATRIX_ARCH}.AppImage" | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-${{ steps.version.outputs.version }}-${{ matrix.arch }}.AppImage | |
| - name: Upload AppImage artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ matrix.arch }}.AppImage | |
| path: | | |
| *.AppImage | |
| *.AppImage.sha256 | |
| *.AppImage.sha512 | |
| *.AppImage.sig | |
| *.AppImage.crt | |
| build-snap: | |
| needs: [clippy, test, e2e] | |
| runs-on: ubuntu-latest | |
| name: Build Snap | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve release version | |
| id: release_version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Sync snap version to release version | |
| env: | |
| VERSION: ${{ steps.release_version.outputs.version }} | |
| run: | | |
| sed -Ei "s/^version:[[:space:]]*'.*'/version: '${VERSION}'/" snap/snapcraft.yaml | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1 | |
| id: build-snap | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: ${{ steps.build-snap.outputs.snap }} | |
| - name: Upload Snap artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis.snap | |
| path: | | |
| ${{ steps.build-snap.outputs.snap }} | |
| ${{ steps.build-snap.outputs.snap }}.sha256 | |
| ${{ steps.build-snap.outputs.snap }}.sha512 | |
| ${{ steps.build-snap.outputs.snap }}.sig | |
| ${{ steps.build-snap.outputs.snap }}.crt | |
| build-homebrew-binaries: | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-apple-darwin | |
| os: macos-latest | |
| - target: aarch64-apple-darwin | |
| os: macos-latest | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-22.04 | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.os }} | |
| name: Build binary (${{ matrix.target }}) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: ${{ env.NIGHTLY_TOOLCHAIN }} | |
| targets: ${{ matrix.target }}, wasm32-wasip2 | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| OS=$(uname -s) | |
| ARCH=$(uname -m) | |
| case "${OS}-${ARCH}" in | |
| Linux-x86_64) TW="tailwindcss-linux-x64";; | |
| Linux-aarch64) TW="tailwindcss-linux-arm64";; | |
| Darwin-arm64) TW="tailwindcss-macos-arm64";; | |
| Darwin-x86_64) TW="tailwindcss-macos-x64";; | |
| esac | |
| ./scripts/download-tailwindcss-cli.sh "$TW" | |
| TAILWINDCSS=./$TW ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Determine package version | |
| id: version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release binary | |
| env: | |
| BUILD_TARGET: ${{ matrix.target }} | |
| MOLTIS_VERSION: ${{ steps.version.outputs.version }} | |
| run: ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Package binary | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| BUILD_TARGET: ${{ matrix.target }} | |
| run: | | |
| PKG_DIR="moltis-package" | |
| mkdir -p "$PKG_DIR/share/moltis/web" "$PKG_DIR/share/moltis/wasm" | |
| cp "target/$BUILD_TARGET/release/moltis" "$PKG_DIR/moltis" | |
| cp -R crates/web/src/assets/. "$PKG_DIR/share/moltis/web/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_calc.wasm" "$PKG_DIR/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_fetch.wasm" "$PKG_DIR/share/moltis/wasm/" | |
| cp "target/wasm32-wasip2/release/moltis_wasm_web_search.wasm" "$PKG_DIR/share/moltis/wasm/" | |
| tar czf "moltis-${VERSION}-${BUILD_TARGET}.tar.gz" -C "$PKG_DIR" moltis share | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-${{ steps.version.outputs.version }}-${{ matrix.target }}.tar.gz | |
| - name: Upload binary artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ matrix.target }} | |
| path: | | |
| *.tar.gz | |
| *.tar.gz.sha256 | |
| *.tar.gz.sha512 | |
| *.tar.gz.sig | |
| *.tar.gz.crt | |
| build-macos-app: | |
| needs: [clippy, test, e2e] | |
| runs-on: macos-latest | |
| name: Build macOS app | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| - name: Install Swift build dependencies | |
| run: brew install xcodegen cbindgen | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| run: | | |
| ./scripts/download-tailwindcss-cli.sh tailwindcss-macos-arm64 | |
| TAILWINDCSS=./tailwindcss-macos-arm64 ./scripts/build-web-assets.sh | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - name: Import Apple certificate | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| env: | |
| APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| run: | | |
| CERTIFICATE_PATH="$RUNNER_TEMP/certificate.p12" | |
| KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" | |
| KEYCHAIN_PASSWORD="$(openssl rand -hex 16)" | |
| echo -n "$APPLE_CERTIFICATE_BASE64" | base64 --decode -o "$CERTIFICATE_PATH" | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security import "$CERTIFICATE_PATH" \ | |
| -P "$APPLE_CERTIFICATE_PASSWORD" \ | |
| -A -t cert -f pkcs12 \ | |
| -k "$KEYCHAIN_PATH" | |
| security set-key-partition-list \ | |
| -S apple-tool:,apple:,codesign: \ | |
| -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security list-keychain -d user -s "$KEYCHAIN_PATH" | |
| rm -f "$CERTIFICATE_PATH" | |
| - name: Determine package version | |
| id: version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build Swift bridge artifacts and generate Xcode project | |
| env: | |
| MOLTIS_VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| ./scripts/build-swift-bridge.sh | |
| ./scripts/generate-swift-project.sh | |
| - name: Build macOS app | |
| env: | |
| DERIVED_DATA_DIR: apps/macos/.derivedData-ci | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| if [[ "$RELEASE_DRY_RUN" == "true" ]]; then | |
| EXTRA_FLAGS=(CODE_SIGNING_ALLOWED=NO) | |
| else | |
| EXTRA_FLAGS=(CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY="Developer ID Application" DEVELOPMENT_TEAM="${APPLE_TEAM_ID}" OTHER_CODE_SIGN_FLAGS="--timestamp --options runtime") | |
| fi | |
| xcodebuild \ | |
| -project apps/macos/Moltis.xcodeproj \ | |
| -scheme Moltis \ | |
| -configuration Release \ | |
| -destination "platform=macOS" \ | |
| -derivedDataPath "$DERIVED_DATA_DIR" \ | |
| "${EXTRA_FLAGS[@]}" \ | |
| build | |
| # Re-sign with hardened runtime + timestamp + clean entitlements | |
| # to strip com.apple.security.get-task-allow (debug entitlement) | |
| if [[ "$RELEASE_DRY_RUN" != "true" ]]; then | |
| APP_PATH="${DERIVED_DATA_DIR}/Build/Products/Release/Moltis.app" | |
| /usr/bin/codesign --force --deep --timestamp --options runtime \ | |
| --entitlements apps/macos/Moltis.entitlements \ | |
| --sign "Developer ID Application" \ | |
| "$APP_PATH" | |
| fi | |
| - name: Package macOS app | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| APP_PATH: apps/macos/.derivedData-ci/Build/Products/Release/Moltis.app | |
| run: | | |
| if [ ! -d "$APP_PATH" ]; then | |
| echo "expected app bundle not found at $APP_PATH" >&2 | |
| exit 1 | |
| fi | |
| ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "moltis-${VERSION}-macos.app.zip" | |
| - name: Notarize macOS app | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| xcrun notarytool store-credentials "notarytool-profile" \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --password "$APPLE_ID_PASSWORD" | |
| SUBMIT_OUT="$(xcrun notarytool submit "moltis-${VERSION}-macos.app.zip" \ | |
| --keychain-profile "notarytool-profile" \ | |
| --wait 2>&1)" || true | |
| echo "$SUBMIT_OUT" | |
| SUBMISSION_ID="$(echo "$SUBMIT_OUT" | grep ' id:' | head -1 | awk '{print $2}')" | |
| if echo "$SUBMIT_OUT" | grep -q 'status: Invalid'; then | |
| echo "::error::Notarization failed. Fetching log..." | |
| xcrun notarytool log "$SUBMISSION_ID" \ | |
| --keychain-profile "notarytool-profile" || true | |
| exit 1 | |
| fi | |
| # Staple the notarization ticket to the app bundle | |
| APP_PATH="apps/macos/.derivedData-ci/Build/Products/Release/Moltis.app" | |
| xcrun stapler staple "$APP_PATH" | |
| # Re-package with the stapled ticket | |
| rm "moltis-${VERSION}-macos.app.zip" | |
| ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "moltis-${VERSION}-macos.app.zip" | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-${{ steps.version.outputs.version }}-macos.app.zip | |
| - name: Upload macOS app artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-macos-app | |
| path: | | |
| *.app.zip | |
| *.app.zip.sha256 | |
| *.app.zip.sha512 | |
| *.app.zip.sig | |
| *.app.zip.crt | |
| - name: Clean up keychain | |
| if: always() | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" | |
| if [ -f "$KEYCHAIN_PATH" ]; then | |
| security delete-keychain "$KEYCHAIN_PATH" | |
| fi | |
| build-windows-exe: | |
| needs: [clippy, test, e2e] | |
| runs-on: windows-latest | |
| name: Build .exe (x86_64) | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Sigstore keyless signing | |
| env: | |
| BUILD_TARGET: x86_64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master | |
| with: | |
| toolchain: stable | |
| targets: ${{ env.BUILD_TARGET }}, wasm32-wasip2 | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - name: Configure Perl for vendored OpenSSL | |
| shell: pwsh | |
| run: | | |
| $strawberryPerl = "C:\Strawberry\perl\bin\perl.exe" | |
| if (-not (Test-Path $strawberryPerl)) { | |
| Write-Error "Strawberry Perl not found at $strawberryPerl" | |
| } | |
| Add-Content -Path $env:GITHUB_ENV -Value "OPENSSL_SRC_PERL=$strawberryPerl" | |
| Add-Content -Path $env:GITHUB_ENV -Value "PERL=$strawberryPerl" | |
| & $strawberryPerl -v | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| package-manager-cache: false | |
| - name: Install npm dependencies | |
| working-directory: crates/web/ui | |
| run: npm ci --ignore-scripts | |
| - name: Build web assets | |
| shell: bash | |
| run: | | |
| ./scripts/download-tailwindcss-cli.sh tailwindcss-windows-x64.exe | |
| TAILWINDCSS=./tailwindcss-windows-x64.exe ./scripts/build-web-assets.sh | |
| - name: Build WASM components | |
| shell: bash | |
| run: | | |
| cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release | |
| cargo run -p moltis-wasm-precompile --release | |
| - name: Determine package version | |
| id: version | |
| shell: bash | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release binary | |
| shell: bash | |
| env: | |
| MOLTIS_VERSION: ${{ steps.version.outputs.version }} | |
| run: MOLTIS_BUILD_FEATURES=full,embedded-assets,embedded-wasm ./scripts/cargo-build-moltis.sh --release --target "$BUILD_TARGET" | |
| - name: Package .exe | |
| shell: bash | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| cp "target/$BUILD_TARGET/release/moltis.exe" "moltis-${VERSION}-${BUILD_TARGET}.exe" | |
| - name: Sign with Sigstore and generate checksums | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-${{ steps.version.outputs.version }}-${{ env.BUILD_TARGET }}.exe | |
| - name: Upload .exe artifact | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: moltis-${{ env.BUILD_TARGET }}.exe | |
| path: | | |
| *.exe | |
| *.exe.sha256 | |
| *.exe.sha512 | |
| *.exe.sig | |
| *.exe.crt | |
| build-docker: | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.dry_run) }} | |
| needs: [clippy, test, e2e] | |
| strategy: | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| os: ubuntu-latest | |
| - platform: linux/arm64 | |
| os: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.os }} | |
| name: Build Docker (${{ matrix.platform }}) | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write # Required for Sigstore keyless signing | |
| attestations: write # Required for provenance attestations | |
| outputs: | |
| # Each matrix leg overwrites, but merge job reads digests from artifacts | |
| image: ghcr.io/${{ github.repository }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve release version | |
| id: release_version | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| VERSION="0.0.0-dev" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| - name: Build and push by digest | |
| id: build | |
| uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6 | |
| env: | |
| DOCKER_BUILD_RECORD_UPLOAD: "false" | |
| with: | |
| context: . | |
| platforms: ${{ matrix.platform }} | |
| build-args: MOLTIS_VERSION=${{ steps.release_version.outputs.version }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| outputs: type=image,"name=ghcr.io/${{ github.repository }}",push-by-digest=true,name-canonical=true,push=true | |
| cache-from: type=gha,scope=${{ matrix.platform }} | |
| cache-to: type=gha,scope=${{ matrix.platform }},mode=max | |
| sbom: true | |
| provenance: mode=max | |
| - name: Smoke test Docker image startup | |
| env: | |
| DIGEST: ${{ steps.build.outputs.digest }} | |
| run: | | |
| set -euo pipefail | |
| IMAGE="ghcr.io/${{ github.repository }}@${DIGEST}" | |
| docker run --rm "${IMAGE}" --help > /dev/null | |
| DOCKER_CLI_VERSION="$(docker run --rm --entrypoint docker "${IMAGE}" --version | awk '{print $3}' | tr -d ',')" | |
| DOCKER_CLI_MAJOR="${DOCKER_CLI_VERSION%%.*}" | |
| if [[ -z "${DOCKER_CLI_MAJOR}" || "${DOCKER_CLI_MAJOR}" -lt 25 ]]; then | |
| echo "Docker CLI in image is too old: ${DOCKER_CLI_VERSION} (require >= 25.x; API >= 1.44)" >&2 | |
| exit 1 | |
| fi | |
| - name: Export digest | |
| env: | |
| DIGEST: ${{ steps.build.outputs.digest }} | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${DIGEST#sha256:}" | |
| touch "/tmp/digests/${digest}" | |
| - name: Upload digest | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: docker-digests-${{ strategy.job-index }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| merge-docker: | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.dry_run) }} | |
| needs: build-docker | |
| runs-on: ubuntu-latest | |
| name: Merge Docker manifest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write # Required for Sigstore keyless signing | |
| attestations: write # Required for artifact attestations | |
| steps: | |
| - name: Download digests | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| path: /tmp/digests | |
| pattern: docker-digests-* | |
| merge-multiple: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3 | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if this is the highest release tag | |
| id: check_latest | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| CURRENT="${GITHUB_REF_NAME}" | |
| LATEST=$(gh api "repos/${{ github.repository }}/tags" --paginate --jq '.[].name' \ | |
| | grep -E '^[0-9]{8}\.[0-9]{2}$' \ | |
| | sort | tail -1) | |
| if [ "$CURRENT" = "$LATEST" ]; then | |
| echo "is_latest=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_latest=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Skipping floating tags: ${CURRENT} is not the highest version (${LATEST} is)" | |
| fi | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=match,pattern=\d{8}\.\d{2} | |
| type=sha | |
| type=raw,value=latest,enable=${{ steps.check_latest.outputs.is_latest }} | |
| - name: Create multi-arch manifest and push | |
| env: | |
| TAGS: ${{ steps.meta.outputs.tags }} | |
| working-directory: /tmp/digests | |
| run: | | |
| mapfile -t tags < <(jq -r '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON") | |
| tag_args=() | |
| for tag in "${tags[@]}"; do | |
| tag_args+=("-t" "$tag") | |
| done | |
| image_refs=() | |
| for digest in *; do | |
| image_refs+=("ghcr.io/${{ github.repository }}@sha256:${digest}") | |
| done | |
| docker buildx imagetools create "${tag_args[@]}" "${image_refs[@]}" | |
| - name: Inspect manifest | |
| env: | |
| TAGS: ${{ steps.meta.outputs.tags }} | |
| run: | | |
| tag=$(echo "$TAGS" | head -1) | |
| docker buildx imagetools inspect "$tag" | |
| - name: Get manifest digest | |
| id: manifest | |
| env: | |
| TAGS: ${{ steps.meta.outputs.tags }} | |
| run: | | |
| tag=$(echo "$TAGS" | head -1) | |
| digest=$(docker buildx imagetools inspect "$tag" --format '{{json .Manifest}}' | jq -r '.digest') | |
| echo "digest=${digest}" >> "$GITHUB_OUTPUT" | |
| - name: Sign container image with cosign (keyless) | |
| if: github.event_name != 'pull_request' | |
| env: | |
| DIGEST: ${{ steps.manifest.outputs.digest }} | |
| TAGS: ${{ steps.meta.outputs.tags }} | |
| run: | | |
| mapfile -t tags < <(printf '%s\n' "$TAGS") | |
| images=() | |
| for tag in "${tags[@]}"; do | |
| [ -n "$tag" ] || continue | |
| images+=("${tag}@${DIGEST}") | |
| done | |
| cosign sign --yes "${images[@]}" | |
| - name: Verify container signature | |
| if: github.event_name != 'pull_request' | |
| env: | |
| DIGEST: ${{ steps.manifest.outputs.digest }} | |
| run: | | |
| cosign verify \ | |
| --certificate-identity-regexp="https://github.com/${{ github.repository }}/*" \ | |
| --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ | |
| "ghcr.io/${{ github.repository }}@${DIGEST}" | |
| - name: Attest build provenance for container image | |
| if: github.event_name != 'pull_request' | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4 | |
| with: | |
| subject-name: ghcr.io/${{ github.repository }} | |
| subject-digest: ${{ steps.manifest.outputs.digest }} | |
| push-to-registry: true | |
| upload-release: | |
| needs: | |
| - build-deb | |
| - build-rpm | |
| - build-arch | |
| - build-appimage | |
| - build-snap | |
| - build-homebrew-binaries | |
| - build-macos-app | |
| - build-windows-exe | |
| if: startsWith(github.ref, 'refs/tags/') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) | |
| runs-on: ubuntu-latest | |
| name: Upload release assets | |
| permissions: | |
| contents: write | |
| id-token: write # Required for artifact attestations | |
| attestations: write # Required for artifact attestations | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: CHANGELOG.md | |
| sparse-checkout-cone-mode: false | |
| - name: Download all build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| path: artifacts | |
| pattern: moltis-* | |
| - name: Download snap artifact | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| name: moltis.snap | |
| path: artifacts/moltis.snap | |
| - name: Collect all release files | |
| run: | | |
| mkdir -p release-files | |
| find artifacts -type f \( \ | |
| -name '*.deb' -o -name '*.rpm' -o -name '*.pkg.tar.zst' \ | |
| -o -name '*.AppImage' -o -name '*.snap' -o -name '*.tar.gz' -o -name '*.zip' -o -name '*.exe' \ | |
| -o -name '*.sha256' -o -name '*.sha512' -o -name '*.sig' -o -name '*.crt' \ | |
| \) -exec cp {} release-files/ \; | |
| cp CHANGELOG.md release-files/ | |
| echo "Files to upload:" | |
| ls -lh release-files/ | |
| - name: Upload to release | |
| uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 | |
| with: | |
| files: release-files/* | |
| draft: false | |
| prerelease: ${{ env.RELEASE_PRE_RELEASE == 'true' }} | |
| - name: Attest build provenance for release artifacts | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4 | |
| with: | |
| subject-path: | | |
| release-files/*.tar.gz | |
| release-files/*.deb | |
| release-files/*.rpm | |
| release-files/*.pkg.tar.zst | |
| release-files/*.AppImage | |
| release-files/*.snap | |
| release-files/*.zip | |
| release-files/*.exe | |
| # Generate SBOM for the entire release | |
| generate-sbom: | |
| needs: | |
| - upload-release | |
| - merge-docker | |
| if: startsWith(github.ref, 'refs/tags/') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) | |
| runs-on: ubuntu-latest | |
| name: Generate Release SBOM | |
| permissions: | |
| contents: write | |
| id-token: write # Required for Sigstore keyless signing | |
| attestations: write # Required for artifact attestations | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - name: Install cosign | |
| if: ${{ env.RELEASE_DRY_RUN != 'true' }} | |
| uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 | |
| - name: Install cargo-sbom | |
| run: cargo install cargo-sbom | |
| - name: Generate SBOM (CycloneDX and SPDX) | |
| run: | | |
| cargo sbom --output-format cyclone_dx_json_1_4 > moltis-sbom.cdx.json | |
| cargo sbom --output-format spdx_json_2_3 > moltis-sbom.spdx.json | |
| - name: Sign SBOMs with Sigstore and generate checksums | |
| uses: ./.github/actions/sign-artifacts | |
| with: | |
| files: moltis-sbom.cdx.json moltis-sbom.spdx.json | |
| skip-sha512: 'true' | |
| - name: Upload SBOM to release | |
| uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 | |
| with: | |
| files: | | |
| moltis-sbom.cdx.json | |
| moltis-sbom.cdx.json.sha256 | |
| moltis-sbom.cdx.json.sig | |
| moltis-sbom.cdx.json.crt | |
| moltis-sbom.spdx.json | |
| moltis-sbom.spdx.json.sha256 | |
| moltis-sbom.spdx.json.sig | |
| moltis-sbom.spdx.json.crt | |
| - name: Attest build provenance for SBOMs | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4 | |
| with: | |
| subject-path: | | |
| moltis-sbom.cdx.json | |
| moltis-sbom.spdx.json | |
| update-homebrew-tap: | |
| needs: | |
| - upload-release | |
| - merge-docker | |
| if: startsWith(github.ref, 'refs/tags/') && !(github.event_name == 'workflow_dispatch' && (inputs.dry_run || inputs.pre_release)) | |
| runs-on: ubuntu-latest | |
| name: Update Homebrew tap | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check Homebrew tap token | |
| id: token_check | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${HOMEBREW_TAP_TOKEN}" ]; then | |
| echo "has_token=false" >> "$GITHUB_OUTPUT" | |
| echo "HOMEBREW_TAP_TOKEN is not set; skipping Homebrew tap update" | |
| else | |
| echo "has_token=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download release assets and compute SHAs | |
| if: steps.token_check.outputs.has_token == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${TAG}" | |
| echo "VERSION=$VERSION" >> "$GITHUB_ENV" | |
| for target in aarch64-apple-darwin x86_64-apple-darwin aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu; do | |
| ASSET="moltis-${VERSION}-${target}.tar.gz" | |
| URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${ASSET}" | |
| curl --silent --show-error --fail --location "$URL" -o "$ASSET" | |
| SHA=$(sha256sum "$ASSET" | cut -d' ' -f1) | |
| VAR="SHA_$(echo "$target" | tr '-' '_')" | |
| echo "${VAR}=${SHA}" >> "$GITHUB_ENV" | |
| echo "$target: $SHA" | |
| done | |
| - name: Checkout homebrew-tap | |
| if: steps.token_check.outputs.has_token == 'true' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| repository: moltis-org/homebrew-tap | |
| token: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| persist-credentials: true | |
| - name: Update formula | |
| if: steps.token_check.outputs.has_token == 'true' | |
| run: | | |
| cat > Formula/moltis.rb <<RUBY | |
| class Moltis < Formula | |
| desc "Personal AI gateway - one binary, multiple LLM providers" | |
| homepage "https://www.moltis.org/" | |
| license "MIT" | |
| version "${VERSION}" | |
| on_macos do | |
| if Hardware::CPU.arm? | |
| url "https://github.com/moltis-org/moltis/releases/download/#{version}/moltis-#{version}-aarch64-apple-darwin.tar.gz" | |
| sha256 "${SHA_aarch64_apple_darwin}" | |
| else | |
| url "https://github.com/moltis-org/moltis/releases/download/#{version}/moltis-#{version}-x86_64-apple-darwin.tar.gz" | |
| sha256 "${SHA_x86_64_apple_darwin}" | |
| end | |
| end | |
| on_linux do | |
| if Hardware::CPU.arm? | |
| url "https://github.com/moltis-org/moltis/releases/download/#{version}/moltis-#{version}-aarch64-unknown-linux-gnu.tar.gz" | |
| sha256 "${SHA_aarch64_unknown_linux_gnu}" | |
| else | |
| url "https://github.com/moltis-org/moltis/releases/download/#{version}/moltis-#{version}-x86_64-unknown-linux-gnu.tar.gz" | |
| sha256 "${SHA_x86_64_unknown_linux_gnu}" | |
| end | |
| end | |
| def install | |
| libexec.install "moltis" | |
| share.install "share/moltis" | |
| (bin/"moltis").write_env_script libexec/"moltis", MOLTIS_SHARE_DIR: share/"moltis" | |
| end | |
| test do | |
| assert_match "moltis", shell_output("#{bin}/moltis --version") | |
| end | |
| end | |
| RUBY | |
| - name: Commit and push | |
| if: steps.token_check.outputs.has_token == 'true' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/moltis.rb | |
| if git diff --cached --quiet; then | |
| echo "No Homebrew formula changes to commit" | |
| exit 0 | |
| fi | |
| git commit -m "moltis ${VERSION}" | |
| git push | |
| update-deploy-tags: | |
| needs: | |
| - upload-release | |
| - merge-docker | |
| if: startsWith(github.ref, 'refs/tags/') && !(github.event_name == 'workflow_dispatch' && (inputs.dry_run || inputs.pre_release)) | |
| runs-on: ubuntu-latest | |
| name: Update deploy template tags | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| ref: main | |
| - name: Update image tags in deploy templates | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${TAG}" | |
| echo "Updating deploy templates to version ${VERSION}" | |
| # DigitalOcean App Platform (pattern matches both semver 0.10.18 and date 20260311.01) | |
| sed -i "s/tag: \"[0-9][0-9.]*\"/tag: \"${VERSION}\"/" .do/deploy.template.yaml | |
| # Render | |
| sed -i "s|ghcr.io/moltis-org/moltis:[0-9][0-9.]*|ghcr.io/moltis-org/moltis:${VERSION}|" render.yaml | |
| # Fly.io | |
| sed -i "s|ghcr.io/moltis-org/moltis:[0-9][0-9.]*|ghcr.io/moltis-org/moltis:${VERSION}|" fly.toml | |
| # Website releases.json | |
| jq --arg v "$VERSION" \ | |
| '.stable.version = $v | .stable.release_url = "https://github.com/moltis-org/moltis/releases/tag/" + $v' \ | |
| website/releases.json > website/releases.json.tmp \ | |
| && mv website/releases.json.tmp website/releases.json | |
| # Check if any files changed | |
| if git diff --quiet .do/deploy.template.yaml render.yaml fly.toml website/releases.json; then | |
| echo "No deploy template changes needed" | |
| exit 0 | |
| fi | |
| # Commit via GitHub API so the commit is signed/verified | |
| REPO="${{ github.repository }}" | |
| MAIN_SHA=$(gh api "repos/${REPO}/git/ref/heads/main" --jq '.object.sha') | |
| BASE_TREE=$(gh api "repos/${REPO}/git/commits/${MAIN_SHA}" --jq '.tree.sha') | |
| # Create blobs for each updated file | |
| DO_BLOB=$(gh api "repos/${REPO}/git/blobs" \ | |
| -f content="$(base64 -w0 .do/deploy.template.yaml)" \ | |
| -f encoding=base64 --jq '.sha') | |
| RENDER_BLOB=$(gh api "repos/${REPO}/git/blobs" \ | |
| -f content="$(base64 -w0 render.yaml)" \ | |
| -f encoding=base64 --jq '.sha') | |
| FLY_BLOB=$(gh api "repos/${REPO}/git/blobs" \ | |
| -f content="$(base64 -w0 fly.toml)" \ | |
| -f encoding=base64 --jq '.sha') | |
| RELEASES_BLOB=$(gh api "repos/${REPO}/git/blobs" \ | |
| -f content="$(base64 -w0 website/releases.json)" \ | |
| -f encoding=base64 --jq '.sha') | |
| # Create a new tree with the updated files (JSON input for proper array structure) | |
| TREE_SHA=$(jq -n \ | |
| --arg base "$BASE_TREE" \ | |
| --arg do_sha "$DO_BLOB" \ | |
| --arg render_sha "$RENDER_BLOB" \ | |
| --arg fly_sha "$FLY_BLOB" \ | |
| --arg releases_sha "$RELEASES_BLOB" \ | |
| '{ | |
| base_tree: $base, | |
| tree: [ | |
| {path: ".do/deploy.template.yaml", mode: "100644", type: "blob", sha: $do_sha}, | |
| {path: "render.yaml", mode: "100644", type: "blob", sha: $render_sha}, | |
| {path: "fly.toml", mode: "100644", type: "blob", sha: $fly_sha}, | |
| {path: "website/releases.json", mode: "100644", type: "blob", sha: $releases_sha} | |
| ] | |
| }' | gh api "repos/${REPO}/git/trees" --input - --jq '.sha') | |
| # Create a signed commit | |
| COMMIT_SHA=$(jq -n \ | |
| --arg msg "chore: update deploy templates and releases to ${VERSION}" \ | |
| --arg tree "$TREE_SHA" \ | |
| --arg parent "$MAIN_SHA" \ | |
| '{message: $msg, tree: $tree, parents: [$parent]}' \ | |
| | gh api "repos/${REPO}/git/commits" --input - --jq '.sha') | |
| # Update main to point to the new commit | |
| gh api "repos/${REPO}/git/refs/heads/main" \ | |
| -X PATCH -f sha="${COMMIT_SHA}" | |
| echo "Updated deploy templates to ${VERSION} (commit ${COMMIT_SHA})" |