Release #13
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: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Release tag (e.g. v0.4.0)' | |
| required: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| BINARY_NAME: null-e | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.target }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Linux | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| artifact_name: null-e | |
| asset_name: null-e-linux-x86_64 | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-musl | |
| artifact_name: null-e | |
| asset_name: null-e-linux-x86_64-musl | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| artifact_name: null-e | |
| asset_name: null-e-linux-aarch64 | |
| # macOS | |
| - os: macos-latest | |
| target: x86_64-apple-darwin | |
| artifact_name: null-e | |
| asset_name: null-e-darwin-x86_64 | |
| - os: macos-latest | |
| target: aarch64-apple-darwin | |
| artifact_name: null-e | |
| asset_name: null-e-darwin-aarch64 | |
| # Windows | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| artifact_name: null-e.exe | |
| asset_name: null-e-windows-x86_64 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install musl-tools (Linux musl) | |
| if: matrix.target == 'x86_64-unknown-linux-musl' | |
| run: sudo apt-get update && sudo apt-get install -y musl-tools | |
| - name: Install cross-compilation tools (Linux aarch64) | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Build | |
| run: cargo build --release --target ${{ matrix.target }} --workspace --exclude null-e-gui | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc | |
| - name: Create tarball (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| tar czvf ../../../${{ matrix.asset_name }}.tar.gz ${{ matrix.artifact_name }} | |
| cd ../../.. | |
| shasum -a 256 ${{ matrix.asset_name }}.tar.gz > ${{ matrix.asset_name }}.tar.gz.sha256 | |
| - name: Create zip (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| Compress-Archive -Path ${{ matrix.artifact_name }} -DestinationPath ../../../${{ matrix.asset_name }}.zip | |
| cd ../../.. | |
| (Get-FileHash ${{ matrix.asset_name }}.zip -Algorithm SHA256).Hash.ToLower() + " " + "${{ matrix.asset_name }}.zip" | Out-File -Encoding ASCII ${{ matrix.asset_name }}.zip.sha256 | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.asset_name }} | |
| path: | | |
| *.tar.gz | |
| *.tar.gz.sha256 | |
| *.zip | |
| *.zip.sha256 | |
| if-no-files-found: ignore | |
| build-tauri: | |
| name: Build Tauri ${{ matrix.target }} | |
| needs: release | |
| runs-on: ${{ matrix.platform }} | |
| # Map the optional signing secret to env so steps can gate on it (`secrets` is not allowed in | |
| # `if:` conditions, but `env` is). | |
| env: | |
| MACOS_SELF_SIGNED_P12: ${{ secrets.MACOS_SELF_SIGNED_P12 }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos-latest | |
| target: aarch64-apple-darwin | |
| - platform: macos-latest | |
| target: x86_64-apple-darwin | |
| - platform: ubuntu-22.04 | |
| target: x86_64-unknown-linux-gnu | |
| - platform: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Rust cache | |
| uses: swatinem/rust-cache@v2 | |
| with: | |
| workspaces: './tauri -> target' | |
| - name: Install Linux dependencies | |
| if: matrix.platform == 'ubuntu-22.04' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| - name: Install and build frontend | |
| run: cd ui && bun install && bun run build | |
| # Optional: sign macOS builds with a STABLE self-signed certificate so the Full Disk Access | |
| # grant persists across updates (TCC binds to the Designated Requirement of a stable cert | |
| # instead of the per-build cdhash). This is NOT Apple notarization — Gatekeeper still requires | |
| # the user to "Open Anyway" once. To enable: create a self-signed code-signing cert once, | |
| # export it as a .p12, and set these repo secrets: | |
| # MACOS_SELF_SIGNED_P12 (base64 of the .p12) | |
| # MACOS_SELF_SIGNED_P12_PASSWORD (the .p12 password) | |
| # MACOS_SIGNING_IDENTITY (the cert's common name) | |
| # If the secret is absent the build stays ad-hoc/unsigned (FDA will reset on each update). | |
| - name: Import self-signed signing certificate (macOS, optional) | |
| if: startsWith(matrix.platform, 'macos') && env.MACOS_SELF_SIGNED_P12 != '' | |
| env: | |
| MACOS_SELF_SIGNED_P12_PASSWORD: ${{ secrets.MACOS_SELF_SIGNED_P12_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| KEYCHAIN="$RUNNER_TEMP/build.keychain" | |
| KEYCHAIN_PWD="$(openssl rand -base64 24)" | |
| security create-keychain -p "$KEYCHAIN_PWD" "$KEYCHAIN" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN" | |
| security unlock-keychain -p "$KEYCHAIN_PWD" "$KEYCHAIN" | |
| echo "$MACOS_SELF_SIGNED_P12" | base64 --decode > "$RUNNER_TEMP/cert.p12" | |
| security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" \ | |
| -P "$MACOS_SELF_SIGNED_P12_PASSWORD" -T /usr/bin/codesign | |
| security list-keychains -d user -s "$KEYCHAIN" login.keychain | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PWD" "$KEYCHAIN" | |
| rm -f "$RUNNER_TEMP/cert.p12" | |
| - name: Build Tauri app | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| # When the MACOS_SIGNING_IDENTITY secret is set, tauri-action signs the macOS bundle with | |
| # that (stable self-signed) identity. When it's absent, fall back to "-" so codesign does | |
| # AD-HOC signing instead of failing — passing an empty identity makes codesign error with | |
| # "specified item could not be found in the keychain" and aborts the .dmg bundle. | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY != '' && secrets.MACOS_SIGNING_IDENTITY || '-' }} | |
| with: | |
| projectPath: ./tauri | |
| args: --target ${{ matrix.target }} --config '{"build":{"beforeBuildCommand":""}}' | |
| tagName: ${{ env.RELEASE_TAG }} | |
| releaseName: 'null-e ${{ env.RELEASE_TAG }}' | |
| releaseBody: '' | |
| releaseDraft: false | |
| prerelease: ${{ contains(env.RELEASE_TAG, 'alpha') || contains(env.RELEASE_TAG, 'beta') || contains(env.RELEASE_TAG, 'rc') }} | |
| release: | |
| name: Create Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Flatten artifacts | |
| run: | | |
| mkdir -p release | |
| find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.sha256" \) -exec mv {} release/ \; | |
| ls -la release/ | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| files: release/* | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: ${{ contains(env.RELEASE_TAG, 'alpha') || contains(env.RELEASE_TAG, 'beta') || contains(env.RELEASE_TAG, 'rc') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| publish-crate: | |
| name: Publish to crates.io | |
| needs: release | |
| if: ${{ github.event_name == 'push' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Verify version matches tag | |
| run: | | |
| TAG="${RELEASE_TAG#v}" | |
| TOML_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') | |
| if [ "$TAG" != "$TOML_VERSION" ]; then | |
| echo "Tag v$TAG does not match Cargo.toml version $TOML_VERSION" | |
| exit 1 | |
| fi | |
| echo "Publishing v$TAG" | |
| # Publish core first (the CLI depends on it); cargo waits for the index to settle. | |
| # No continue-on-error: a failed publish should be visible, not silently swallowed. | |
| - name: Publish to crates.io | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: | | |
| cargo publish -p null-e-core | |
| cargo publish -p null-e | |
| update-homebrew: | |
| name: Update Homebrew Formula | |
| needs: release | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'push' && !contains(github.event.inputs.tag || github.ref_name, 'alpha') && !contains(github.event.inputs.tag || github.ref_name, 'beta') && !contains(github.event.inputs.tag || github.ref_name, 'rc') }} | |
| steps: | |
| - name: Update Homebrew formula | |
| uses: mislav/bump-homebrew-formula-action@v3 | |
| with: | |
| formula-name: null-e | |
| homebrew-tap: us/homebrew-tap | |
| download-url: https://github.com/us/null-e/archive/refs/tags/${{ env.RELEASE_TAG }}.tar.gz | |
| env: | |
| COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| continue-on-error: true |