f1fed6cdd5
cargo-cyclonedx 0.5.9 walks the whole workspace in a single pass and writes a <package>.cdx.json next to each member's Cargo.toml; it has no -p/--package selector. The previous three 'cargo cyclonedx ... -p <crate>' invocations aborted with 'error: unexpected argument -p found', failing the cargo-publish job after the crates were already published and, in turn, skipping the github-release attach-assets job (it needs all four publish jobs). v0.3.0 published to every registry but got no GitHub Release page or SBOM assets as a result. Replace the three invalid calls with a single workspace pass and copy the three crates.io crate SBOMs into the upload dir.
544 lines
23 KiB
YAML
544 lines
23 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags: ["v*"]
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
# --------------------------------------------------------------------------
|
|
# crates.io: three crates in dependency order
|
|
# --------------------------------------------------------------------------
|
|
cargo-publish:
|
|
name: Publish to crates.io
|
|
runs-on: ubuntu-latest
|
|
# The publish jobs run with long-lived registry tokens. Binding them to a
|
|
# protected GitHub environment lets the org require a reviewer to approve
|
|
# each release and restrict which tags/branches may deploy, so the secrets
|
|
# are not reachable from an arbitrary workflow run. The `release`
|
|
# environment and its protection rules are configured under repo
|
|
# Settings -> Environments.
|
|
environment: release
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
|
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
|
|
|
# Idempotent publishing: if the version is already on crates.io we
|
|
# treat that as success so re-runs of the workflow don't fail.
|
|
- name: Publish wickra-core (idempotent)
|
|
run: |
|
|
out=$(cargo publish -p wickra-core --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \
|
|
|| (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \
|
|
|| (echo "$out"; exit 1))
|
|
env:
|
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
|
|
- name: Wait for crates.io index to refresh
|
|
run: sleep 45
|
|
|
|
- name: Publish wickra-data (idempotent)
|
|
run: |
|
|
out=$(cargo publish -p wickra-data --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \
|
|
|| (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \
|
|
|| (echo "$out"; exit 1))
|
|
env:
|
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
|
|
- name: Wait for crates.io index to refresh
|
|
run: sleep 45
|
|
|
|
- name: Publish wickra (idempotent)
|
|
run: |
|
|
out=$(cargo publish -p wickra --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \
|
|
|| (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \
|
|
|| (echo "$out"; exit 1))
|
|
env:
|
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
|
|
# Produce .crate files for the GitHub Release attachments. `cargo package`
|
|
# writes them to target/package/<name>-<version>.crate.
|
|
#
|
|
# No --allow-dirty: `actions/checkout` gives a clean tree and nothing
|
|
# above mutates it. No --no-verify: every crate was just published to
|
|
# crates.io in the steps above, so the verification build resolves its
|
|
# workspace dependencies from the registry and confirms each .crate
|
|
# actually builds before it is attached to the release.
|
|
- name: Build .crate files for release attachment
|
|
run: |
|
|
cargo package -p wickra-core
|
|
cargo package -p wickra-data
|
|
cargo package -p wickra
|
|
|
|
- name: Upload .crate files
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: crate-files
|
|
path: target/package/*.crate
|
|
|
|
# CycloneDX SBOM per published crate. Attached to the GitHub Release
|
|
# alongside the .crate / .whl / .tgz artefacts so downstream
|
|
# consumers can audit the published dependency tree without
|
|
# re-resolving Cargo.lock.
|
|
- name: Install cargo-cyclonedx
|
|
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
|
with:
|
|
tool: cargo-cyclonedx
|
|
|
|
- name: Generate CycloneDX SBOMs
|
|
run: |
|
|
# cargo-cyclonedx walks the whole workspace in a single pass and
|
|
# writes a <package>.cdx.json next to each member's Cargo.toml; it
|
|
# has no -p/--package selector. Collect the three crates.io crates
|
|
# (the .crate files published by this job) into the upload dir.
|
|
cargo cyclonedx --format json --top-level
|
|
mkdir -p sboms
|
|
cp crates/wickra-core/wickra-core.cdx.json sboms/
|
|
cp crates/wickra-data/wickra-data.cdx.json sboms/
|
|
cp crates/wickra/wickra.cdx.json sboms/
|
|
ls -lh sboms/
|
|
|
|
- name: Upload SBOMs
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: sboms
|
|
path: sboms/*.cdx.json
|
|
|
|
# --------------------------------------------------------------------------
|
|
# PyPI: cross-platform wheels + sdist
|
|
# --------------------------------------------------------------------------
|
|
python-wheels:
|
|
name: Build wheels (${{ matrix.target }}/${{ matrix.manylinux }} on ${{ matrix.os }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# glibc Linux (manylinux)
|
|
- { os: ubuntu-latest, target: x86_64, manylinux: auto }
|
|
- { os: ubuntu-latest, target: aarch64, manylinux: auto }
|
|
# musl Linux (Alpine and other musl distros)
|
|
- { os: ubuntu-latest, target: x86_64, manylinux: musllinux_1_2 }
|
|
- { os: ubuntu-latest, target: aarch64, manylinux: musllinux_1_2 }
|
|
# macOS
|
|
- { os: macos-latest, target: x86_64, manylinux: auto }
|
|
- { os: macos-latest, target: aarch64, manylinux: auto }
|
|
# Windows
|
|
- { os: windows-latest, target: x64, manylinux: auto }
|
|
- { os: windows-11-arm, target: aarch64, manylinux: auto }
|
|
runs-on: ${{ matrix.os }}
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: "3.11"
|
|
- name: Sync root README into bindings/python so it ships with the wheel
|
|
shell: bash
|
|
run: cp README.md bindings/python/README.md
|
|
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
with:
|
|
working-directory: bindings/python
|
|
target: ${{ matrix.target }}
|
|
args: --release --strip --out dist
|
|
manylinux: ${{ matrix.manylinux }}
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
# Include manylinux in the name so the glibc and musl x86_64/aarch64
|
|
# builds do not collide on the same artifact name.
|
|
name: wheels-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux }}
|
|
path: bindings/python/dist/*
|
|
|
|
python-sdist:
|
|
name: Build Python sdist
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- name: Sync root README into bindings/python so it ships in the sdist
|
|
run: cp README.md bindings/python/README.md
|
|
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
with:
|
|
working-directory: bindings/python
|
|
command: sdist
|
|
args: --out dist
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: wheels-sdist
|
|
path: bindings/python/dist/*
|
|
|
|
python-publish:
|
|
name: Publish to PyPI
|
|
needs: [python-wheels, python-sdist]
|
|
runs-on: ubuntu-latest
|
|
environment: release
|
|
steps:
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
path: dist
|
|
pattern: wheels-*
|
|
merge-multiple: true
|
|
|
|
- name: Upload to PyPI
|
|
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
with:
|
|
command: upload
|
|
args: --skip-existing dist/*
|
|
env:
|
|
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# npm: per-platform native binaries + main meta-package
|
|
# --------------------------------------------------------------------------
|
|
node-build:
|
|
name: Node build (${{ matrix.target }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { host: ubuntu-latest, target: x86_64-unknown-linux-gnu }
|
|
- { host: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu }
|
|
- { host: macos-latest, target: x86_64-apple-darwin }
|
|
- { host: macos-latest, target: aarch64-apple-darwin }
|
|
- { host: windows-latest, target: x86_64-pc-windows-msvc }
|
|
- { host: windows-11-arm, target: aarch64-pc-windows-msvc }
|
|
runs-on: ${{ matrix.host }}
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: "20"
|
|
|
|
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
|
|
|
- name: Install Node deps
|
|
working-directory: bindings/node
|
|
run: npm install
|
|
|
|
- name: Build native module
|
|
working-directory: bindings/node
|
|
run: npx napi build --platform --release --target ${{ matrix.target }}
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: bindings-${{ matrix.target }}
|
|
path: bindings/node/wickra.*.node
|
|
if-no-files-found: error
|
|
|
|
node-publish:
|
|
name: Publish to npm
|
|
needs: node-build
|
|
runs-on: ubuntu-latest
|
|
environment: release
|
|
# `id-token: write` lets npm publish embed a Sigstore provenance
|
|
# attestation generated from the GitHub Actions OIDC token. The npm
|
|
# registry then shows a "Verified provenance" badge and lets
|
|
# consumers verify the package was built from this exact workflow
|
|
# run.
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: "20"
|
|
registry-url: "https://registry.npmjs.org"
|
|
|
|
- name: Install Node deps
|
|
working-directory: bindings/node
|
|
run: npm install
|
|
|
|
- name: Download all platform binaries
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
path: bindings/node/artifacts
|
|
pattern: bindings-*
|
|
|
|
# The npm/<platform>/package.json templates are committed to the
|
|
# repo (see bindings/node/npm/), so `napi artifacts` only needs to
|
|
# drop the freshly built .node binary into the matching directory.
|
|
- name: Move binaries into platform package layout
|
|
working-directory: bindings/node
|
|
run: npx napi artifacts --dir artifacts
|
|
|
|
# Publish each platform package individually. Skip versions that are
|
|
# already on npm. A first-attempt 403 from npm's spam filter is
|
|
# tolerated for a single 30-second retry — that historically clears
|
|
# rate-limit-driven false positives. Anything that still fails after
|
|
# the retry is a *real* failure (the platform binary will be missing
|
|
# from `optionalDependencies` and Windows-style installs will break,
|
|
# exactly the regression that produced audit finding R20) — fail the
|
|
# job loudly so the release does not silently land in a half-published
|
|
# state. Previously this loop swallowed the second-attempt failure with
|
|
# a `::warning::` and `return 0`; that mask is removed.
|
|
- name: Publish platform packages (idempotent)
|
|
working-directory: bindings/node
|
|
env:
|
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
run: |
|
|
set +e
|
|
version=$(node -p "require('./package.json').version")
|
|
fail=0
|
|
publish_dir() {
|
|
local dir=$1
|
|
local pkg=$(basename "$dir")
|
|
local pkgname="wickra-$pkg"
|
|
local existing
|
|
existing=$(npm view "$pkgname@$version" version 2>/dev/null)
|
|
if [ "$existing" = "$version" ]; then
|
|
echo "::notice::skip $pkgname@$version (already on npm)"
|
|
return 0
|
|
fi
|
|
echo "::group::publish $pkgname@$version"
|
|
# --ignore-scripts: a per-platform package must never run lifecycle
|
|
# scripts during publish (npm runs prepublishOnly/prepare/etc. from
|
|
# the package being published — a malicious or stray script would
|
|
# execute with the npm token in the environment).
|
|
(cd "$dir" && npm publish --access public --ignore-scripts --provenance)
|
|
local rc=$?
|
|
echo "::endgroup::"
|
|
if [ "$rc" -ne 0 ]; then
|
|
echo "::warning::first attempt of $pkgname failed (rc=$rc); retrying after 30s"
|
|
sleep 30
|
|
(cd "$dir" && npm publish --access public --ignore-scripts --provenance)
|
|
rc=$?
|
|
fi
|
|
if [ "$rc" -ne 0 ]; then
|
|
echo "::error::$pkgname could not be published — the release would land with a missing platform binary; failing the job."
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
for dir in npm/*/; do
|
|
publish_dir "$dir" || fail=1
|
|
done
|
|
exit $fail
|
|
|
|
- name: Sync root README into bindings/node so it ships with the npm tarball
|
|
# npm reads README.md from the package directory at publish time. Copy
|
|
# the canonical root README in just before the publish so every
|
|
# registry shows the same project page.
|
|
shell: bash
|
|
run: cp ../../README.md README.md
|
|
working-directory: bindings/node
|
|
|
|
- name: Publish main package to npm (idempotent)
|
|
working-directory: bindings/node
|
|
env:
|
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
# GitHub Actions defaults to `bash -e`. `npm view` on a not-yet-
|
|
# published package returns 1, which would kill the step before
|
|
# we got to publish. Force a graceful empty result instead.
|
|
set +e
|
|
version=$(node -p "require('./package.json').version")
|
|
existing=$(npm view "wickra@$version" version 2>/dev/null)
|
|
if [ "$existing" = "$version" ]; then
|
|
echo "::notice::skip wickra@$version (already on npm)"
|
|
exit 0
|
|
fi
|
|
# --ignore-scripts so any leftover prepublish hooks (which would
|
|
# otherwise try to republish the already-published platform
|
|
# subpackages) can't sabotage the main publish.
|
|
npm publish --access public --ignore-scripts --provenance
|
|
rc=$?
|
|
if [ "$rc" -ne 0 ]; then
|
|
echo "::warning::first attempt failed (rc=$rc); retrying after 30s"
|
|
sleep 30
|
|
npm publish --access public --ignore-scripts --provenance
|
|
rc=$?
|
|
fi
|
|
exit $rc
|
|
|
|
- name: Pack node tarballs for release attachment
|
|
working-directory: bindings/node
|
|
run: |
|
|
# Main package
|
|
npm pack --ignore-scripts
|
|
# Each per-platform package (the binaries were already moved in by
|
|
# napi artifacts). --ignore-scripts for the same reason as publish:
|
|
# packing must not execute lifecycle scripts from the packed dir.
|
|
for d in npm/*/; do
|
|
(cd "$d" && npm pack --ignore-scripts)
|
|
done
|
|
|
|
- name: Upload Node tarballs
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: node-tarballs
|
|
path: |
|
|
bindings/node/*.tgz
|
|
bindings/node/npm/*/*.tgz
|
|
|
|
# --------------------------------------------------------------------------
|
|
# WASM: wasm-pack build + npm publish (as `wickra-wasm`)
|
|
# --------------------------------------------------------------------------
|
|
# Note: this job's npm publish call uses `--provenance` (see below),
|
|
# which requires the `id-token: write` permission set at the job level.
|
|
wasm-publish:
|
|
name: Publish wickra-wasm to npm
|
|
runs-on: ubuntu-latest
|
|
environment: release
|
|
# `id-token: write` lets npm publish embed a Sigstore provenance
|
|
# attestation generated from the GitHub Actions OIDC token (same
|
|
# mechanism as the node-publish job above).
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: "20"
|
|
registry-url: "https://registry.npmjs.org"
|
|
|
|
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
|
|
with:
|
|
targets: wasm32-unknown-unknown
|
|
|
|
- name: Install wasm-pack (latest, via prebuilt binary)
|
|
# See the matching note in ci.yml: jetli's default installs an old
|
|
# 0.10.x wasm-pack whose build subcommand rejects --features.
|
|
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
|
with:
|
|
tool: wasm-pack
|
|
|
|
- name: Sync root README into bindings/wasm so wasm-pack ships it in pkg/
|
|
# wasm-pack copies the crate's README.md into the generated pkg/
|
|
# directory it then publishes. Refresh it from the canonical root
|
|
# README right before the build.
|
|
run: cp README.md bindings/wasm/README.md
|
|
|
|
- name: Build WASM package (bundler target)
|
|
run: wasm-pack build bindings/wasm --target bundler --release --features panic-hook
|
|
|
|
- name: Enrich generated package.json with author + repo metadata
|
|
working-directory: bindings/wasm/pkg
|
|
run: |
|
|
node -e "
|
|
const fs = require('fs');
|
|
const pkg = JSON.parse(fs.readFileSync('package.json'));
|
|
pkg.author = 'kingchenc <wickra.lib@gmail.com>';
|
|
pkg.repository = { type: 'git', url: 'https://github.com/wickra-lib/wickra' };
|
|
pkg.homepage = 'https://github.com/wickra-lib/wickra';
|
|
pkg.bugs = { url: 'https://github.com/wickra-lib/wickra/issues' };
|
|
pkg.license = 'PolyForm-Noncommercial-1.0.0';
|
|
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
|
|
"
|
|
|
|
- name: Pack wickra-wasm for release attachment
|
|
working-directory: bindings/wasm/pkg
|
|
run: npm pack
|
|
|
|
- name: Upload WASM tarball
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: wasm-tarball
|
|
path: bindings/wasm/pkg/wickra-wasm-*.tgz
|
|
|
|
- name: Publish wickra-wasm to npm (idempotent)
|
|
working-directory: bindings/wasm/pkg
|
|
run: |
|
|
out=$(npm publish --access public --provenance 2>&1) && echo "$out" \
|
|
|| (echo "$out" | grep -q "You cannot publish over" && echo "skip: version already on npm" \
|
|
|| (echo "$out"; exit 1))
|
|
env:
|
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# GitHub Release: attach every built artefact to the tag's release page.
|
|
# --------------------------------------------------------------------------
|
|
github-release:
|
|
name: Attach assets to the GitHub Release
|
|
needs: [cargo-publish, python-publish, node-publish, wasm-publish]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Resolve target tag
|
|
id: tag
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
|
tag="${{ github.ref_name }}"
|
|
else
|
|
# workflow_dispatch / non-tag push: attach to the latest v* tag.
|
|
tag=$(git tag --list 'v*' --sort=-v:refname | head -n1)
|
|
fi
|
|
if [ -z "$tag" ]; then
|
|
echo "::error::no v* tag found to attach assets to"
|
|
exit 1
|
|
fi
|
|
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
echo "::notice::attaching assets to release $tag"
|
|
|
|
- name: Download all build artifacts
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Stage release assets
|
|
run: |
|
|
set -e
|
|
mkdir -p release-assets
|
|
# Python wheels + sdist (5 wheel artifacts + 1 sdist artifact).
|
|
find artifacts -type f -name "*.whl" -exec cp {} release-assets/ \;
|
|
find artifacts -type f -name "*.tar.gz" -exec cp {} release-assets/ \;
|
|
# Native Node binaries (one per platform).
|
|
find artifacts -type f -name "*.node" -exec cp {} release-assets/ \;
|
|
# Node npm-pack tarballs (main + per-platform).
|
|
find artifacts -type f -name "wickra-*.tgz" -exec cp {} release-assets/ \;
|
|
# Cargo .crate files (one per workspace member).
|
|
find artifacts -type f -name "*.crate" -exec cp {} release-assets/ \;
|
|
# CycloneDX SBOMs (one per published crate).
|
|
find artifacts -type f -name "*.cdx.json" -exec cp {} release-assets/ \;
|
|
ls -lh release-assets/
|
|
echo "asset-count=$(ls release-assets/ | wc -l)"
|
|
|
|
- name: Create / update GitHub Release with assets
|
|
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
|
|
with:
|
|
tag_name: ${{ steps.tag.outputs.tag }}
|
|
name: Wickra ${{ steps.tag.outputs.tag }}
|
|
files: release-assets/*
|
|
generate_release_notes: true
|
|
fail_on_unmatched_files: false
|
|
body: |
|
|
Wickra ${{ github.ref_name }} — streaming-first technical indicators across 4 language registries.
|
|
|
|
### Install
|
|
|
|
```bash
|
|
cargo add wickra
|
|
pip install wickra
|
|
npm install wickra
|
|
npm install wickra-wasm
|
|
```
|
|
|
|
### Attached assets
|
|
|
|
Pre-built artefacts for every supported platform — the same files that
|
|
were uploaded to crates.io, PyPI, and npm by this workflow run.
|
|
|
|
- `*.whl` / `wickra-*.tar.gz` — Python wheels + sdist (5 platforms, ABI3 ≥ 3.9)
|
|
- `wickra.*.node` — native Node bindings (linux-x64-gnu, darwin-x64,
|
|
darwin-arm64, win32-x64-msvc)
|
|
- `wickra-*.tgz` — npm-pack tarballs (main package + per-platform subpackages + WASM)
|
|
- `*.crate` — cargo source crates (wickra-core, wickra-data, wickra)
|
|
|
|
### Auto-generated changelog
|
|
|
|
See below; GitHub computes it from the commits since the previous tag. |