Skip to content

Release

Release #28

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
dry-run:
description: 'Dry run (build only, skip publish)'
type: boolean
default: false
permissions: {}
jobs:
version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.read.outputs.VERSION }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Read version from Cargo.toml
id: read
run: |
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
echo "Release version: $VERSION"
- name: Check tag does not exist
run: |
VERSION="${{ steps.read.outputs.VERSION }}"
if git rev-parse "v${VERSION}" >/dev/null 2>&1; then
echo "::error::Tag v${VERSION} already exists. Bump the version in a PR first."
exit 1
fi
- name: Check CHANGELOG.md has entry for version
run: |
VERSION="${{ steps.read.outputs.VERSION }}"
if [ ! -f CHANGELOG.md ]; then
echo "::error::CHANGELOG.md does not exist at the repository root."
exit 1
fi
# Accept either `## [X.Y.Z]` or `## X.Y.Z` headings, with an
# optional trailing space (followed by `— DATE`) or end-of-line.
if ! grep -qE "^## \[?${VERSION}\]?( |$)" CHANGELOG.md; then
echo "::error::CHANGELOG.md is missing an entry for version ${VERSION}."
echo "::error::Add a heading like \`## [${VERSION}] — $(date +%Y-%m-%d)\` describing the release before re-running."
exit 1
fi
build:
needs: version
strategy:
matrix:
include:
- target: aarch64-apple-darwin
runner: macos-14
archive: tar.gz
build-tool: cargo
- target: x86_64-apple-darwin
runner: macos-14
archive: tar.gz
build-tool: cargo
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: x86_64-unknown-linux-musl
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: aarch64-unknown-linux-gnu
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: aarch64-unknown-linux-musl
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: x86_64-pc-windows-msvc
runner: windows-latest
archive: zip
build-tool: cargo
- target: i686-pc-windows-msvc
runner: windows-latest
archive: zip
build-tool: cargo
- target: aarch64-pc-windows-msvc
runner: windows-latest
archive: zip
build-tool: cargo
- target: aarch64-linux-android
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: arm-unknown-linux-gnueabihf
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: arm-unknown-linux-musleabihf
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: i686-unknown-linux-gnu
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
- target: i686-unknown-linux-musl
runner: ubuntu-latest
archive: tar.gz
build-tool: cross
runs-on: ${{ matrix.runner }}
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `rustup show`
# reads rust-toolchain.toml in the repo root, then installs the
# pinned channel + listed components if missing. The dtolnay action
# cannot auto-detect the channel when pinned by SHA (it normally
# parses it from the ref name), so we go through rustup directly.
run: |
rustup show
rustup target add ${{ matrix.target }}
- name: Install cross
if: matrix.build-tool == 'cross'
run: cargo install --locked --version =0.2.5 cross
- name: Build (cargo)
if: matrix.build-tool == 'cargo'
run: cargo build --release --target ${{ matrix.target }}
- name: Build (cross)
if: matrix.build-tool == 'cross'
run: cross build --release --target ${{ matrix.target }}
- name: Package (unix)
if: matrix.archive == 'tar.gz'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../socket-patch-${{ matrix.target }}.tar.gz socket-patch
cd ../../..
- name: Package (windows)
if: matrix.archive == 'zip'
shell: pwsh
run: |
Compress-Archive -Path "target/${{ matrix.target }}/release/socket-patch.exe" -DestinationPath "socket-patch-${{ matrix.target }}.zip"
- name: Upload artifact (tar.gz)
if: matrix.archive == 'tar.gz'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: socket-patch-${{ matrix.target }}
path: socket-patch-${{ matrix.target }}.tar.gz
- name: Upload artifact (zip)
if: matrix.archive == 'zip'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: socket-patch-${{ matrix.target }}
path: socket-patch-${{ matrix.target }}.zip
tag:
needs: [version, build]
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Create and push tag
run: |
TAG="v${{ needs.version.outputs.version }}"
git tag "$TAG"
git push origin "$TAG"
github-release:
needs: [version, build, tag]
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts
merge-multiple: true
- name: Generate SHA256SUMS
run: |
cd artifacts
# Hash every release artifact (tar.gz + zip) so install.sh can verify
# the binary before extraction. Sorted output keeps the file stable.
sha256sum *.tar.gz *.zip 2>/dev/null | sort > SHA256SUMS
cat SHA256SUMS
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="v${{ needs.version.outputs.version }}"
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--generate-notes \
artifacts/*
cargo-publish:
needs: [version, build, tag]
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `rustup show`
# reads rust-toolchain.toml in the repo root, then installs the
# pinned channel + listed components if missing.
run: rustup show
- name: Authenticate with crates.io
id: crates-io-auth
uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1.0.3
- name: Publish socket-patch-core
run: cargo publish -p socket-patch-core
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
- name: Wait for crates.io index update
run: sleep 30
- name: Copy README for CLI crate
run: cp README.md crates/socket-patch-cli/README.md
- name: Publish socket-patch-cli
run: cargo publish -p socket-patch-cli
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
npm-publish:
needs: [version, build, tag]
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Configure git for HTTPS
run: git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts
merge-multiple: true
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22.22.1'
registry-url: 'https://registry.npmjs.org'
- name: Update npm for trusted publishing
run: npm install -g npm@11.15.0
- name: Stage binaries into platform packages
run: |
# Unix platforms: extract binary into each platform package directory
stage_unix() {
local artifact="$1" pkg_dir="$2"
tar xzf "artifacts/${artifact}.tar.gz" -C "${pkg_dir}/"
}
# Windows platforms: extract .exe into each platform package directory
stage_win() {
local artifact="$1" pkg_dir="$2"
unzip -o "artifacts/${artifact}.zip" -d "${pkg_dir}/"
}
stage_unix socket-patch-aarch64-apple-darwin npm/socket-patch-darwin-arm64
stage_unix socket-patch-x86_64-apple-darwin npm/socket-patch-darwin-x64
stage_unix socket-patch-x86_64-unknown-linux-gnu npm/socket-patch-linux-x64-gnu
stage_unix socket-patch-x86_64-unknown-linux-musl npm/socket-patch-linux-x64-musl
stage_unix socket-patch-aarch64-unknown-linux-gnu npm/socket-patch-linux-arm64-gnu
stage_unix socket-patch-aarch64-unknown-linux-musl npm/socket-patch-linux-arm64-musl
stage_unix socket-patch-arm-unknown-linux-gnueabihf npm/socket-patch-linux-arm-gnu
stage_unix socket-patch-arm-unknown-linux-musleabihf npm/socket-patch-linux-arm-musl
stage_unix socket-patch-i686-unknown-linux-gnu npm/socket-patch-linux-ia32-gnu
stage_unix socket-patch-i686-unknown-linux-musl npm/socket-patch-linux-ia32-musl
stage_unix socket-patch-aarch64-linux-android npm/socket-patch-android-arm64
stage_win socket-patch-x86_64-pc-windows-msvc npm/socket-patch-win32-x64
stage_win socket-patch-i686-pc-windows-msvc npm/socket-patch-win32-ia32
stage_win socket-patch-aarch64-pc-windows-msvc npm/socket-patch-win32-arm64
- name: Publish platform packages
run: |
for pkg_dir in npm/socket-patch-*/; do
echo "Publishing ${pkg_dir}..."
npm publish "./${pkg_dir}" --provenance --access public || {
if npm view "@socketsecurity/$(basename "$pkg_dir")@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then
echo "Already published, skipping."
else
exit 1
fi
}
done
- name: Wait for npm registry propagation
run: sleep 30
- name: Copy README for npm package
run: cp README.md npm/socket-patch/README.md
- name: Publish main package
run: |
npm publish ./npm/socket-patch --provenance --access public || {
if npm view "@socketsecurity/socket-patch@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then
echo "Already published, skipping."
else
exit 1
fi
}
pypi-publish:
needs: [version, build, tag]
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts
merge-multiple: true
- name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12.13'
- name: Copy README for PyPI package
run: cp README.md pypi/socket-patch/README.md
- name: Build platform wheels
run: |
VERSION="${{ needs.version.outputs.version }}"
python scripts/build-pypi-wheels.py --version "$VERSION" --artifacts artifacts --dist dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
packages-dir: dist/