Skip to content

Release Universal Binaries #3

Release Universal Binaries

Release Universal Binaries #3

name: Release Universal Binaries
on:
push:
tags:
- 'puniversald/v*'
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g., v1.0.0 — will be tagged as puniversald/v1.0.0)'
required: true
type: string
branch:
description: 'Branch to build from (default: main)'
required: false
type: string
default: 'main'
commit_id:
description: 'Specific commit SHA to build (overrides branch if set)'
required: false
type: string
compare_from:
description: 'Compare from tag (optional, auto-detects if empty)'
required: false
type: string
prerelease:
description: 'Mark as pre-release'
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
env:
BINARY_NAME: puniversald
PROJECT_NAME: push-universal
TAG_PREFIX: puniversald/
jobs:
# ===========================================
# Linux Build (ubuntu)
# ===========================================
build-linux:
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.version.outputs.version }}
version_no_v: ${{ steps.version.outputs.version_no_v }}
full_tag: ${{ steps.version.outputs.full_tag }}
steps:
- name: Resolve checkout ref
id: resolve-ref
run: |
COMMIT_ID="${{ github.event.inputs.commit_id }}"
BRANCH="${{ github.event.inputs.branch }}"
COMMIT_ID="${COMMIT_ID#vcs.revision=}"
if [ -n "$COMMIT_ID" ]; then
echo "ref=$COMMIT_ID" >> $GITHUB_OUTPUT
elif [ -n "$BRANCH" ]; then
echo "ref=$BRANCH" >> $GITHUB_OUTPUT
else
echo "ref=" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ steps.resolve-ref.outputs.ref || '' }}
fetch-depth: 0
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_VERSION="${{ github.event.inputs.version }}"
# Strip puniversald/ prefix if user supplied it
INPUT_VERSION="${INPUT_VERSION#${TAG_PREFIX}}"
VERSION="$INPUT_VERSION"
FULL_TAG="${TAG_PREFIX}${INPUT_VERSION}"
else
FULL_TAG="${GITHUB_REF#refs/tags/}"
VERSION="${FULL_TAG#${TAG_PREFIX}}"
fi
VERSION_NO_V="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
echo "full_tag=$FULL_TAG" >> $GITHUB_OUTPUT
echo "Building version: $VERSION (full tag: $FULL_TAG)"
- name: Setup dkls23-rs
uses: ./.github/actions/setup-dkls23
with:
ci_token: ${{ secrets.CI_DKLS_GARBLING }}
- name: Copy dkls23-rs and garbling into build context
run: |
cp -r ../dkls23-rs ./dkls23-rs
cp -r ../garbling ./garbling
sed -i 's|go-wrapper => ../dkls23-rs/wrapper/go-wrappers|go-wrapper => ./dkls23-rs/wrapper/go-wrappers|' go.mod
- name: Create and push version tag
if: github.event_name == 'workflow_dispatch'
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
FULL_TAG="${{ steps.version.outputs.full_tag }}"
COMMIT_REF="${{ github.event.inputs.commit_id }}"
COMMIT_REF="${COMMIT_REF#vcs.revision=}"
if [ -z "$COMMIT_REF" ]; then
COMMIT_REF="HEAD"
fi
echo "Tagging commit: $COMMIT_REF as $FULL_TAG"
if git tag --list | grep -q "^${FULL_TAG}$"; then
echo "Tag $FULL_TAG already exists, deleting it first"
git tag -d "$FULL_TAG" || true
git push --delete origin "$FULL_TAG" || true
fi
git tag -a "$FULL_TAG" "$COMMIT_REF" -m "Release $FULL_TAG"
git push origin "$FULL_TAG"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23.8'
cache: true
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Patch dkls23-rs for static linking
run: |
if grep -q 'crate-type = \["cdylib", "rlib"\]' dkls23-rs/wrapper/go-dkls/Cargo.toml; then
sed -i 's/crate-type = \["cdylib", "rlib"\]/crate-type = ["staticlib", "cdylib", "rlib"]/' dkls23-rs/wrapper/go-dkls/Cargo.toml
echo "Added staticlib to crate-type"
fi
find dkls23-rs -name "Cargo.toml" -type f -exec grep -l "hd-migration" {} \; | while read -r file; do
sed -i 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main" }|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
sed -i 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main"}|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
done
- name: Build dkls23-rs dependency
run: |
cd dkls23-rs/wrapper/go-dkls
cargo build --release
cd ../../..
echo "Verifying libgodkls was built..."
ls -la dkls23-rs/target/release/libgodkls* || \
(echo "ERROR: libgodkls not found. Searching..." && find dkls23-rs/target -name "*godkls*" -type f && exit 1)
- name: Download libwasmvm_muslc
run: |
WASM_VER=$(grep "github.com/CosmWasm/wasmvm/v2" go.mod | head -1 | awk '{print $2}')
echo "Downloading wasmvm static library version: $WASM_VER"
sudo wget -O /usr/lib/libwasmvm_muslc.x86_64.a \
"https://github.com/CosmWasm/wasmvm/releases/download/${WASM_VER}/libwasmvm_muslc.x86_64.a"
sudo ln -sf /usr/lib/libwasmvm_muslc.x86_64.a /usr/lib/libwasmvm_muslc.a
ls -la /usr/lib/libwasmvm_muslc*
- name: Patch chain ID for production
run: |
sed -i 's/"localchain_9000-1"/"push_42101-1"/' app/app.go
grep -n "ChainID" app/app.go
- name: Build Linux binary
run: |
VERSION=${{ steps.version.outputs.version }}
VERSION_NO_V="${VERSION#v}"
COMMIT=$(git rev-parse HEAD)
mkdir -p dist
export GOTOOLCHAIN=local
export CGO_ENABLED=1
export CGO_LDFLAGS="-L$(pwd)/dkls23-rs/target/release -lm"
echo "Building puniversald for linux/amd64 (glibc, static)..."
go build \
-tags "muslc" \
-ldflags="-s -w -X github.com/cosmos/cosmos-sdk/version.Name=puniversald -X github.com/cosmos/cosmos-sdk/version.AppName=puniversald -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} -X github.com/cosmos/cosmos-sdk/version.BuildTags=muslc -linkmode=external -extldflags '-Wl,-z,muldefs -static'" \
-trimpath \
-o dist/puniversald \
./cmd/puniversald
file dist/puniversald
file dist/puniversald | grep "statically linked"
cd dist
mkdir -p bin
mv puniversald bin/
tar -czvf push-universal_${VERSION_NO_V}_linux_amd64.tar.gz bin/
shasum -a 256 push-universal_${VERSION_NO_V}_linux_amd64.tar.gz > push-universal_${VERSION_NO_V}_linux_amd64.tar.gz.sha256
rm -rf bin
- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: linux-binaries
path: |
dist/*.tar.gz
dist/*.sha256
retention-days: 1
# ===========================================
# Linux ARM64 Build
# ===========================================
build-linux-arm64:
runs-on: ubuntu-22.04-arm
needs: build-linux
steps:
- name: Resolve checkout ref
id: resolve-ref
run: |
COMMIT_ID="${{ github.event.inputs.commit_id }}"
BRANCH="${{ github.event.inputs.branch }}"
COMMIT_ID="${COMMIT_ID#vcs.revision=}"
if [ -n "$COMMIT_ID" ]; then
echo "ref=$COMMIT_ID" >> $GITHUB_OUTPUT
elif [ -n "$BRANCH" ]; then
echo "ref=$BRANCH" >> $GITHUB_OUTPUT
else
echo "ref=" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ steps.resolve-ref.outputs.ref || '' }}
fetch-depth: 0
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_VERSION="${{ github.event.inputs.version }}"
INPUT_VERSION="${INPUT_VERSION#${TAG_PREFIX}}"
VERSION="$INPUT_VERSION"
else
FULL_TAG="${GITHUB_REF#refs/tags/}"
VERSION="${FULL_TAG#${TAG_PREFIX}}"
fi
VERSION_NO_V="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
- name: Setup dkls23-rs
uses: ./.github/actions/setup-dkls23
with:
ci_token: ${{ secrets.CI_DKLS_GARBLING }}
- name: Copy dkls23-rs and garbling into build context
run: |
cp -r ../dkls23-rs ./dkls23-rs
cp -r ../garbling ./garbling
sed -i 's|go-wrapper => ../dkls23-rs/wrapper/go-wrappers|go-wrapper => ./dkls23-rs/wrapper/go-wrappers|' go.mod
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23.8'
cache: true
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Patch dkls23-rs for static linking
run: |
if grep -q 'crate-type = \["cdylib", "rlib"\]' dkls23-rs/wrapper/go-dkls/Cargo.toml; then
sed -i 's/crate-type = \["cdylib", "rlib"\]/crate-type = ["staticlib", "cdylib", "rlib"]/' dkls23-rs/wrapper/go-dkls/Cargo.toml
fi
find dkls23-rs -name "Cargo.toml" -type f -exec grep -l "hd-migration" {} \; | while read -r file; do
sed -i 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main" }|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
sed -i 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main"}|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
done
- name: Build dkls23-rs dependency
run: |
cd dkls23-rs/wrapper/go-dkls
cargo build --release
cd ../../..
ls -la dkls23-rs/target/release/libgodkls* || \
(echo "ERROR: libgodkls not found." && find dkls23-rs/target -name "*godkls*" -type f && exit 1)
- name: Download libwasmvm_muslc
run: |
WASM_VER=$(grep "github.com/CosmWasm/wasmvm/v2" go.mod | head -1 | awk '{print $2}')
echo "Downloading wasmvm static library version: $WASM_VER"
sudo wget -O /usr/lib/libwasmvm_muslc.aarch64.a \
"https://github.com/CosmWasm/wasmvm/releases/download/${WASM_VER}/libwasmvm_muslc.aarch64.a"
sudo ln -sf /usr/lib/libwasmvm_muslc.aarch64.a /usr/lib/libwasmvm_muslc.a
ls -la /usr/lib/libwasmvm_muslc*
- name: Patch chain ID for production
run: |
sed -i 's/"localchain_9000-1"/"push_42101-1"/' app/app.go
grep -n "ChainID" app/app.go
- name: Build Linux ARM64 binary
run: |
VERSION=${{ steps.version.outputs.version }}
VERSION_NO_V="${VERSION#v}"
COMMIT=$(git rev-parse HEAD)
mkdir -p dist
export GOTOOLCHAIN=local
export CGO_ENABLED=1
export CGO_LDFLAGS="-L$(pwd)/dkls23-rs/target/release -lm"
echo "Building puniversald for linux/arm64 (glibc, static)..."
go build \
-tags "muslc" \
-ldflags="-s -w -X github.com/cosmos/cosmos-sdk/version.Name=puniversald -X github.com/cosmos/cosmos-sdk/version.AppName=puniversald -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} -X github.com/cosmos/cosmos-sdk/version.BuildTags=muslc -linkmode=external -extldflags '-Wl,-z,muldefs -static'" \
-trimpath \
-o dist/puniversald \
./cmd/puniversald
file dist/puniversald
file dist/puniversald | grep "statically linked"
cd dist
mkdir -p bin
mv puniversald bin/
tar -czvf push-universal_${VERSION_NO_V}_linux_arm64.tar.gz bin/
shasum -a 256 push-universal_${VERSION_NO_V}_linux_arm64.tar.gz > push-universal_${VERSION_NO_V}_linux_arm64.tar.gz.sha256
rm -rf bin
- name: Upload Linux ARM64 artifacts
uses: actions/upload-artifact@v4
with:
name: linux-arm64-binaries
path: |
dist/*.tar.gz
dist/*.sha256
retention-days: 1
# ===========================================
# macOS Build (Apple Silicon)
# ===========================================
build-macos:
runs-on: macos-latest
needs: build-linux
steps:
- name: Resolve checkout ref
id: resolve-ref
run: |
COMMIT_ID="${{ github.event.inputs.commit_id }}"
BRANCH="${{ github.event.inputs.branch }}"
COMMIT_ID="${COMMIT_ID#vcs.revision=}"
if [ -n "$COMMIT_ID" ]; then
echo "ref=$COMMIT_ID" >> $GITHUB_OUTPUT
elif [ -n "$BRANCH" ]; then
echo "ref=$BRANCH" >> $GITHUB_OUTPUT
else
echo "ref=" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ steps.resolve-ref.outputs.ref || '' }}
fetch-depth: 0
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_VERSION="${{ github.event.inputs.version }}"
INPUT_VERSION="${INPUT_VERSION#${TAG_PREFIX}}"
VERSION="$INPUT_VERSION"
else
FULL_TAG="${GITHUB_REF#refs/tags/}"
VERSION="${FULL_TAG#${TAG_PREFIX}}"
fi
VERSION_NO_V="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
echo "Building version: $VERSION (stripped: $VERSION_NO_V)"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23.8'
cache: true
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Setup dkls23-rs
uses: ./.github/actions/setup-dkls23
with:
ci_token: ${{ secrets.CI_DKLS_GARBLING }}
- name: Copy dkls23-rs and garbling into build context
run: |
rsync -a --exclude='ci/template' ../dkls23-rs/ ./dkls23-rs/
rsync -a ../garbling/ ./garbling/
sed -i '' 's|go-wrapper => ../dkls23-rs/wrapper/go-wrappers|go-wrapper => ./dkls23-rs/wrapper/go-wrappers|' go.mod
- name: Patch dkls23-rs hd-migration to local garbling path
run: |
# puniversald pulls in go-dkls (and its hd-migration dep) which is normally fetched from git;
# redirect to the locally copied garbling tree so cargo doesn't need GitHub auth.
find dkls23-rs -name "Cargo.toml" -type f -exec grep -l "hd-migration" {} \; | while read -r file; do
sed -i '' 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main" }|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
sed -i '' 's|hd-migration = { git = "https://github.com/pushchain/garbling.git", branch = "main"}|hd-migration = { path = "../garbling/crates/hd-migration" }|g' "$file"
done
- name: Build dkls23-rs dependency
run: |
# Build go-dkls from the workspace root using `-p` so the wrapper's
# .cargo/config.toml (which injects Linux-only `-soname` linker flags)
# stays out of scope on macOS.
cd dkls23-rs
cargo build -p go-dkls --release
cd ..
echo "Verifying libgodkls was built..."
ls -la dkls23-rs/target/release/libgodkls* || \
(echo "ERROR: libgodkls not found." && find dkls23-rs/target -name "*godkls*" -type f && exit 1)
- name: Import Code Signing Certificate
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 -base64 32)
echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > "$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 list-keychain -d user -s "$KEYCHAIN_PATH"
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s \
-k "$KEYCHAIN_PASSWORD" \
"$KEYCHAIN_PATH"
echo "Available signing identities:"
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
- name: Download libwasmvm
run: |
WASM_VER=$(grep "github.com/CosmWasm/wasmvm/v2" go.mod | head -1 | awk '{print $2}')
echo "Wasmvm version: $WASM_VER"
curl -L -o libwasmvm.dylib \
"https://github.com/CosmWasm/wasmvm/releases/download/${WASM_VER}/libwasmvm.dylib"
ls -la libwasmvm.dylib
- name: Patch chain ID for production
run: |
sed -i '' 's/"localchain_9000-1"/"push_42101-1"/' app/app.go
grep -n "ChainID" app/app.go
- name: Build Mac Binary (ARM64 only - native build)
run: |
mkdir -p dist
VERSION=${{ steps.version.outputs.version }}
export CGO_ENABLED=1
export CGO_LDFLAGS="-L$(pwd)/dkls23-rs/target/release"
COMMIT=$(git rev-parse HEAD)
echo "Building puniversald for Apple Silicon (arm64)..."
GOOS=darwin GOARCH=arm64 go build \
-tags "netgo,ledger" \
-ldflags="-s -w -X github.com/cosmos/cosmos-sdk/version.Name=puniversald -X github.com/cosmos/cosmos-sdk/version.AppName=puniversald -X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} -X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger" \
-o dist/${BINARY_NAME}-darwin-arm64 \
./cmd/puniversald
cp libwasmvm.dylib dist/
install_name_tool -change @rpath/libwasmvm.dylib @loader_path/libwasmvm.dylib dist/${BINARY_NAME}-darwin-arm64
ls -la dist/
echo "Checking dependencies..."
otool -L dist/${BINARY_NAME}-darwin-arm64 || true
- name: Sign Binaries
env:
APPLE_IDENTITY: ${{ secrets.APPLE_IDENTITY }}
run: |
codesign --force --options runtime --timestamp --sign "$APPLE_IDENTITY" dist/${BINARY_NAME}-darwin-arm64
codesign --force --options runtime --timestamp --sign "$APPLE_IDENTITY" dist/libwasmvm.dylib
echo "Verifying signatures..."
codesign -dv --verbose=2 dist/${BINARY_NAME}-darwin-arm64
codesign -dv --verbose=2 dist/libwasmvm.dylib
- name: Notarize Binary
timeout-minutes: 15
continue-on-error: true
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
VERSION=${{ steps.version.outputs.version }}
xcrun notarytool store-credentials "notary-profile" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD"
cd dist
VERSION_NO_V="${VERSION#v}"
mkdir -p bin
cp ${BINARY_NAME}-darwin-arm64 bin/${BINARY_NAME}
cp libwasmvm.dylib bin/
tar -czvf ${PROJECT_NAME}_${VERSION_NO_V}_darwin_arm64.tar.gz bin/
rm -rf bin
zip -j ${BINARY_NAME}-darwin-arm64-notarize.zip ${BINARY_NAME}-darwin-arm64 libwasmvm.dylib
cd ..
xcrun notarytool submit dist/${BINARY_NAME}-darwin-arm64-notarize.zip \
--keychain-profile "notary-profile" --wait
rm -f dist/*-notarize.zip
- name: Create Checksums
run: |
VERSION=${{ steps.version.outputs.version }}
VERSION_NO_V="${VERSION#v}"
cd dist
shasum -a 256 ${PROJECT_NAME}_${VERSION_NO_V}_darwin_arm64.tar.gz > ${PROJECT_NAME}_${VERSION_NO_V}_darwin_arm64.tar.gz.sha256
ls -la
- name: Upload macOS artifacts
uses: actions/upload-artifact@v4
with:
name: macos-binaries
path: |
dist/*.tar.gz
dist/*.sha256
retention-days: 1
# ===========================================
# Create Unified Release
# ===========================================
create-release:
runs-on: ubuntu-latest
needs: [build-linux, build-linux-arm64, build-macos]
steps:
- name: Resolve checkout ref
id: resolve-ref
run: |
COMMIT_ID="${{ github.event.inputs.commit_id }}"
BRANCH="${{ github.event.inputs.branch }}"
COMMIT_ID="${COMMIT_ID#vcs.revision=}"
if [ -n "$COMMIT_ID" ]; then
echo "ref=$COMMIT_ID" >> $GITHUB_OUTPUT
elif [ -n "$BRANCH" ]; then
echo "ref=$BRANCH" >> $GITHUB_OUTPUT
else
echo "ref=" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ steps.resolve-ref.outputs.ref || '' }}
fetch-depth: 0
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_VERSION="${{ github.event.inputs.version }}"
INPUT_VERSION="${INPUT_VERSION#${TAG_PREFIX}}"
VERSION="$INPUT_VERSION"
FULL_TAG="${TAG_PREFIX}${INPUT_VERSION}"
else
FULL_TAG="${GITHUB_REF#refs/tags/}"
VERSION="${FULL_TAG#${TAG_PREFIX}}"
fi
VERSION_NO_V="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
echo "full_tag=$FULL_TAG" >> $GITHUB_OUTPUT
- name: Download Linux artifacts
uses: actions/download-artifact@v4
with:
name: linux-binaries
path: dist/linux
- name: Download Linux ARM64 artifacts
uses: actions/download-artifact@v4
with:
name: linux-arm64-binaries
path: dist/linux-arm64
- name: Download macOS artifacts
uses: actions/download-artifact@v4
with:
name: macos-binaries
path: dist/macos
- name: Prepare release assets
run: |
mkdir -p release
cp dist/linux/*.tar.gz release/ 2>/dev/null || true
cp dist/linux/*.sha256 release/ 2>/dev/null || true
cp dist/linux-arm64/*.tar.gz release/ 2>/dev/null || true
cp dist/linux-arm64/*.sha256 release/ 2>/dev/null || true
cp dist/macos/*.tar.gz release/ 2>/dev/null || true
cp dist/macos/*.sha256 release/ 2>/dev/null || true
echo "Release assets:"
ls -la release/
- name: Generate changelog
id: changelog
run: |
FULL_TAG="${{ steps.version.outputs.full_tag }}"
COMPARE_FROM="${{ github.event.inputs.compare_from }}"
if [ -z "$COMPARE_FROM" ]; then
COMPARE_FROM=$(git tag --sort=-version:refname | grep -E "^${TAG_PREFIX}v[0-9]+\.[0-9]+\.[0-9]+" | grep -v "^${FULL_TAG}$" | head -1)
fi
if [ -z "$COMPARE_FROM" ]; then
COMPARE_FROM=$(git rev-list --max-parents=0 HEAD)
fi
echo "Generating changelog from $COMPARE_FROM to $FULL_TAG"
{
echo "## What's Changed"
echo ""
git log ${COMPARE_FROM}..HEAD --pretty=format:"- %s" | head -30
} > changelog.md
cat changelog.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.full_tag }}
name: Universal Client ${{ steps.version.outputs.version }}
body: |
## 📦 puniversald Binaries
| Platform | Architecture | File |
|----------|--------------|------|
| Linux | AMD64 (x86_64) | `${{ env.PROJECT_NAME }}_*_linux_amd64.tar.gz` |
| Linux | ARM64 (aarch64) | `${{ env.PROJECT_NAME }}_*_linux_arm64.tar.gz` |
| macOS | Apple Silicon (arm64) | `${{ env.PROJECT_NAME }}_*_darwin_arm64.tar.gz` |
## ✅ Verification
macOS binaries are code-signed and notarized by Apple.
Verify checksums using the `.sha256` files.
draft: false
prerelease: ${{ github.event.inputs.prerelease || false }}
files: release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Verify Release
run: |
FULL_TAG="${{ steps.version.outputs.full_tag }}"
echo "Verifying release assets..."
gh release view "$FULL_TAG" --json assets --jq '.assets[].name' || true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}