mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-06 19:35:13 +02:00
288 lines
8.6 KiB
YAML
288 lines
8.6 KiB
YAML
name: Release build & publish
|
|
|
|
on:
|
|
release:
|
|
types: [created]
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: "Existing release tag to (re)build and publish (e.g. v0.5.0)"
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
BIN_NAME: nyx
|
|
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
|
|
|
|
jobs:
|
|
frontend:
|
|
name: build-frontend
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check out sources
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ env.RELEASE_TAG }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: frontend/package-lock.json
|
|
|
|
- name: Install frontend dependencies
|
|
working-directory: frontend
|
|
run: npm ci
|
|
|
|
- name: Build frontend
|
|
working-directory: frontend
|
|
run: npm run build
|
|
|
|
- name: Upload frontend dist
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: frontend-dist
|
|
path: src/server/assets/dist/
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
build:
|
|
needs: frontend
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- target: x86_64-unknown-linux-gnu
|
|
os: ubuntu-latest
|
|
- target: aarch64-unknown-linux-gnu
|
|
os: ubuntu-latest
|
|
- target: x86_64-pc-windows-msvc
|
|
os: windows-latest
|
|
- target: x86_64-apple-darwin
|
|
os: macos-14
|
|
- target: aarch64-apple-darwin
|
|
os: macos-14
|
|
runs-on: ${{ matrix.os }}
|
|
|
|
steps:
|
|
- name: Check out sources
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ env.RELEASE_TAG }}
|
|
|
|
- name: Download prebuilt frontend dist
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: frontend-dist
|
|
path: src/server/assets/dist/
|
|
|
|
- name: Install Rust toolchain
|
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
target: ${{ matrix.target }}
|
|
cache: true
|
|
|
|
- name: Install cross-compilation tools (ARM Linux)
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y gcc-aarch64-linux-gnu
|
|
echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
|
|
echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
|
|
|
|
- name: Install target
|
|
run: rustup target add ${{ matrix.target }}
|
|
|
|
- name: Build
|
|
run: cargo build --release --bin ${{ env.BIN_NAME }} --target ${{ matrix.target }}
|
|
|
|
- name: Package (Linux & macOS)
|
|
if: runner.os != 'Windows'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
BIN=${{ env.BIN_NAME }}
|
|
TARGET=${{ matrix.target }}
|
|
EXT=$([[ "$TARGET" == *windows* ]] && echo ".exe" || echo "")
|
|
BIN_PATH=target/$TARGET/release/$BIN$EXT
|
|
mkdir -p dist
|
|
ARCHIVE=$BIN-$TARGET.zip
|
|
files=("$BIN_PATH" THIRDPARTY-LICENSES.html)
|
|
shopt -s nullglob
|
|
license_files=(LICENSE* COPYING*)
|
|
shopt -u nullglob
|
|
files+=("${license_files[@]}")
|
|
zip -9 "dist/$ARCHIVE" "${files[@]}"
|
|
echo "ASSET=$ARCHIVE" >> "$GITHUB_ENV"
|
|
|
|
- name: Package (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$Bin = '${{ env.BIN_NAME }}'
|
|
$Target = '${{ matrix.target }}'
|
|
$Ext = '.exe'
|
|
$BinPath = "target/$Target/release/$Bin$Ext"
|
|
New-Item -ItemType Directory -Path dist -Force | Out-Null
|
|
$Archive = "$Bin-$Target.zip"
|
|
$LicenseFiles = @(Get-ChildItem -Path 'LICENSE*', 'COPYING*' -File -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName })
|
|
$Files = @($BinPath, 'THIRDPARTY-LICENSES.html') + $LicenseFiles
|
|
|
|
Compress-Archive `
|
|
-Path $Files `
|
|
-DestinationPath "dist/$Archive" `
|
|
-CompressionLevel Optimal
|
|
|
|
Add-Content -Path $env:GITHUB_ENV -Value "ASSET=$Archive"
|
|
|
|
- name: Upload build artifact
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: release-${{ matrix.target }}
|
|
path: dist/${{ env.ASSET }}
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
reproducibility:
|
|
name: reproducibility-check
|
|
needs: frontend
|
|
runs-on: ubuntu-latest
|
|
continue-on-error: true
|
|
steps:
|
|
- name: Check out sources
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ env.RELEASE_TAG }}
|
|
|
|
- name: Download prebuilt frontend dist
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: frontend-dist
|
|
path: src/server/assets/dist/
|
|
|
|
- name: Install Rust toolchain
|
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
target: x86_64-unknown-linux-gnu
|
|
cache: true
|
|
|
|
- name: Build twice and diff hashes
|
|
shell: bash
|
|
env:
|
|
RUSTFLAGS: "--remap-path-prefix=${{ github.workspace }}=/build"
|
|
run: |
|
|
set -euo pipefail
|
|
TARGET=x86_64-unknown-linux-gnu
|
|
BIN=${{ env.BIN_NAME }}
|
|
BIN_PATH="target/$TARGET/release/$BIN"
|
|
|
|
SOURCE_DATE_EPOCH=$(git log -1 --format=%ct HEAD)
|
|
export SOURCE_DATE_EPOCH
|
|
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
|
|
|
|
cargo build --release --bin "$BIN" --target "$TARGET"
|
|
HASH1=$(sha256sum "$BIN_PATH" | awk '{print $1}')
|
|
echo "first build: $HASH1"
|
|
|
|
cargo clean --release --target "$TARGET"
|
|
cargo build --release --bin "$BIN" --target "$TARGET"
|
|
HASH2=$(sha256sum "$BIN_PATH" | awk '{print $1}')
|
|
echo "second build: $HASH2"
|
|
|
|
if [ "$HASH1" != "$HASH2" ]; then
|
|
echo "::error::Reproducibility check failed: builds are not bit-identical"
|
|
echo " first: $HASH1"
|
|
echo " second: $HASH2"
|
|
exit 1
|
|
fi
|
|
echo "::notice::Reproducible build verified (sha256=$HASH1)"
|
|
|
|
publish:
|
|
name: publish-release
|
|
runs-on: ubuntu-latest
|
|
needs: [build]
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
attestations: write
|
|
steps:
|
|
- name: Check out sources
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ env.RELEASE_TAG }}
|
|
|
|
- name: Generate CycloneDX SBOM
|
|
uses: anchore/sbom-action@v0
|
|
with:
|
|
path: .
|
|
format: cyclonedx-json
|
|
output-file: nyx-${{ env.RELEASE_TAG }}.cdx.json
|
|
upload-artifact: false
|
|
upload-release-assets: false
|
|
|
|
- name: Download all build artifacts
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
path: release-artifacts
|
|
pattern: release-*
|
|
merge-multiple: true
|
|
|
|
- name: Generate SHA256SUMS
|
|
run: |
|
|
set -euo pipefail
|
|
cd release-artifacts
|
|
ls -lh
|
|
sha256sum *.zip > SHA256SUMS
|
|
cat SHA256SUMS
|
|
|
|
# Sigstore keyless signing. Verify with:
|
|
# cosign verify-blob --bundle <file>.bundle \
|
|
# --certificate-identity-regexp 'https://github.com/elicpeter/nyx/.*' \
|
|
# --certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
|
# <file>
|
|
- name: Install cosign
|
|
uses: sigstore/cosign-installer@v4.1.2
|
|
|
|
- name: Cosign keyless sign release artifacts
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
SBOM="nyx-${{ env.RELEASE_TAG }}.cdx.json"
|
|
(
|
|
cd release-artifacts
|
|
for f in *.zip SHA256SUMS; do
|
|
cosign sign-blob --yes \
|
|
--bundle "$f.bundle" \
|
|
"$f"
|
|
done
|
|
)
|
|
cosign sign-blob --yes \
|
|
--bundle "$SBOM.bundle" \
|
|
"$SBOM"
|
|
|
|
# SLSA v1 provenance. Verify with `gh attestation verify <file> --repo <repo>`.
|
|
- name: Generate SLSA build provenance
|
|
uses: actions/attest-build-provenance@v4
|
|
with:
|
|
subject-path: |
|
|
release-artifacts/*.zip
|
|
release-artifacts/SHA256SUMS
|
|
nyx-${{ env.RELEASE_TAG }}.cdx.json
|
|
|
|
- name: Upload to the release
|
|
uses: softprops/action-gh-release@v3
|
|
with:
|
|
tag_name: ${{ env.RELEASE_TAG }}
|
|
files: |
|
|
release-artifacts/*.zip
|
|
release-artifacts/*.zip.bundle
|
|
release-artifacts/SHA256SUMS
|
|
release-artifacts/SHA256SUMS.bundle
|
|
nyx-${{ env.RELEASE_TAG }}.cdx.json
|
|
nyx-${{ env.RELEASE_TAG }}.cdx.json.bundle
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|