name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
publish:
description: Publish to crates.io after creating the GitHub release
required: false
default: false
type: boolean
permissions:
contents: write
env:
PUBLIC_MIRROR_REPO: LATTIX-IO/sdk-rust-public
jobs:
verify-version:
runs-on: ubuntu-latest
outputs:
crate_version: ${{ steps.version.outputs.crate_version }}
tag_name: ${{ steps.version.outputs.tag_name }}
should_publish: ${{ steps.version.outputs.should_publish }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Verify tag and crate version
id: version
shell: bash
env:
INPUT_PUBLISH: ${{ inputs.publish }}
run: |
set -euo pipefail
crate_version=$(cargo metadata --no-deps --format-version 1 | python -c "import json,sys; print(json.load(sys.stdin)['packages'][0]['version'])")
tag_name="${GITHUB_REF_NAME:-v${crate_version}}"
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
expected_tag="v${crate_version}"
if [[ "${tag_name}" != "${expected_tag}" ]]; then
echo "::error::Tag ${tag_name} does not match Cargo.toml version ${crate_version}" >&2
exit 1
fi
fi
should_publish=false
if [[ "${GITHUB_EVENT_NAME}" == "push" || "${INPUT_PUBLISH:-false}" == "true" ]]; then
should_publish=true
fi
echo "crate_version=${crate_version}" >> "$GITHUB_OUTPUT"
echo "tag_name=${tag_name}" >> "$GITHUB_OUTPUT"
echo "should_publish=${should_publish}" >> "$GITHUB_OUTPUT"
native-assets:
needs: verify-version
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
archive_name: sdk-rust-native-linux-x86_64.tar.gz
library_name: libsdk_rust.so
rust_target: ''
library_dir: target/release
- os: macos-14
archive_name: sdk-rust-native-macos-x86_64.tar.gz
library_name: libsdk_rust.dylib
rust_target: x86_64-apple-darwin
library_dir: target/x86_64-apple-darwin/release
- os: macos-14
archive_name: sdk-rust-native-macos-aarch64.tar.gz
library_name: libsdk_rust.dylib
rust_target: ''
library_dir: target/release
- os: windows-latest
archive_name: sdk-rust-native-windows-x86_64.zip
library_name: sdk_rust.dll
rust_target: ''
library_dir: target/release
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Install extra Rust target
if: ${{ matrix.rust_target != '' }}
shell: bash
run: rustup target add "${{ matrix.rust_target }}"
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@v2
- name: Build release library
if: ${{ matrix.rust_target == '' }}
shell: bash
run: cargo build --release
- name: Build release library for explicit target
if: ${{ matrix.rust_target != '' }}
shell: bash
run: cargo build --release --target "${{ matrix.rust_target }}"
- name: Package native archive (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
archive_root="dist/native"
mkdir -p "${archive_root}/include"
cp "${{ matrix.library_dir }}/${{ matrix.library_name }}" "${archive_root}/"
cp "include/lattix_sdk.h" "${archive_root}/include/"
tar -C dist -czf "dist/${{ matrix.archive_name }}" native
- name: Package native archive (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$archiveRoot = Join-Path $PWD 'dist/native'
New-Item -ItemType Directory -Force -Path (Join-Path $archiveRoot 'include') | Out-Null
Copy-Item (Join-Path $PWD '${{ matrix.library_dir }}/${{ matrix.library_name }}') $archiveRoot -Force
Copy-Item (Join-Path $PWD 'include/lattix_sdk.h') (Join-Path $archiveRoot 'include/lattix_sdk.h') -Force
Compress-Archive -Path (Join-Path $PWD 'dist/native/*') -DestinationPath (Join-Path $PWD 'dist/${{ matrix.archive_name }}') -Force
- name: Upload native archive
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.archive_name }}
path: dist/${{ matrix.archive_name }}
github-release:
needs:
- verify-version
- native-assets
runs-on: ubuntu-latest
steps:
- name: Download native artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.verify-version.outputs.tag_name }}
generate_release_notes: true
files: dist/*
fail_on_unmatched_files: true
prerelease: ${{ contains(needs.verify-version.outputs.tag_name, '-') }}
public-mirror-release:
needs:
- verify-version
- github-release
runs-on: ubuntu-latest
permissions:
contents: read
env:
MIRROR_TOKEN: ${{ secrets.GH_PAT }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download native artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Verify public mirror token is configured
shell: bash
run: |
set -euo pipefail
if [[ -z "${MIRROR_TOKEN:-}" ]]; then
echo "::error::GH_PAT secret is not configured for sdk-rust releases." >&2
exit 1
fi
- name: Configure git identity
shell: bash
run: |
set -euo pipefail
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Sync repository contents to public mirror
shell: bash
run: |
set -euo pipefail
mirror_dir="$(mktemp -d)"
trap 'rm -rf "${mirror_dir}"' EXIT
git clone "https://x-access-token:${MIRROR_TOKEN}@github.com/${PUBLIC_MIRROR_REPO}.git" "${mirror_dir}"
rsync -a --delete \
--exclude '.git/' \
--exclude 'target/' \
--exclude 'dist/' \
./ "${mirror_dir}/"
pushd "${mirror_dir}" >/dev/null
if [[ -n "$(git status --porcelain)" ]]; then
git add -A
git commit -m "Mirror ${GITHUB_REPOSITORY}@${GITHUB_SHA}"
git push origin HEAD:main
else
git push origin HEAD:main
fi
git tag -f "${{ needs.verify-version.outputs.tag_name }}"
git push origin "refs/tags/${{ needs.verify-version.outputs.tag_name }}" --force
popd >/dev/null
- name: Publish mirrored GitHub release
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
run: |
set -euo pipefail
tag_name="${{ needs.verify-version.outputs.tag_name }}"
prerelease_flag=()
if [[ "${tag_name}" == *-* ]]; then
prerelease_flag+=(--prerelease)
fi
if gh release view "${tag_name}" --repo "${PUBLIC_MIRROR_REPO}" >/dev/null 2>&1; then
gh release upload "${tag_name}" dist/* --repo "${PUBLIC_MIRROR_REPO}" --clobber
else
gh release create "${tag_name}" dist/* --repo "${PUBLIC_MIRROR_REPO}" --generate-notes "${prerelease_flag[@]}"
fi
publish-readiness:
if: ${{ needs.verify-version.outputs.should_publish == 'true' }}
needs:
- verify-version
- github-release
- public-mirror-release
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Verify crates.io publish readiness
run: cargo publish --locked --dry-run
publish-crate:
if: ${{ needs.verify-version.outputs.should_publish == 'true' }}
needs:
- verify-version
- publish-readiness
runs-on: ubuntu-latest
permissions:
contents: read
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Verify crates.io token is configured
shell: bash
run: |
set -euo pipefail
if [[ -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then
echo "::error::CARGO_REGISTRY_TOKEN secret is not configured for sdk-rust releases." >&2
exit 1
fi
- name: Publish crate to crates.io
run: cargo publish --locked