name: Release
permissions:
contents: write
on:
workflow_run:
workflows: ["CI"]
types:
- completed
branches: [main]
jobs:
check_release:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
outputs:
releaseExists: ${{ steps.check_release.outputs.releaseExists }}
version: ${{ steps.check_release.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check if release exists
id: check_release
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const fs = require("fs");
const content = fs.readFileSync("Cargo.toml", "utf8");
const match = content.match(/^version = "(.*?)"/m);
const version = match ? match[1] : "";
const tag = `v${version}`;
console.log("Extracted version:", tag);
core.setOutput("version", version);
let releaseExists = false;
try {
await github.rest.repos.getReleaseByTag({ owner, repo, tag });
releaseExists = true;
} catch (error) {
if (error.status !== 404) throw error;
}
console.log("Release exists:", releaseExists);
core.setOutput("releaseExists", releaseExists);
extract_changelog:
needs: check_release
if: needs.check_release.outputs.releaseExists == 'false'
runs-on: ubuntu-latest
outputs:
changelog: ${{ steps.changelog.outputs.changelog }}
steps:
- uses: actions/checkout@v4
- name: Extract changelog for version
id: changelog
run: |
VERSION="${{ needs.check_release.outputs.version }}"
# Extract content between [X.Y.Z] header and next ## header
CHANGELOG=$(awk "/^## \\[${VERSION}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md)
# If no versioned section found, try [Unreleased]
if [ -z "$CHANGELOG" ]; then
CHANGELOG=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
fi
echo "changelog<<CHANGELOG_EOF" >> "$GITHUB_OUTPUT"
echo "$CHANGELOG" >> "$GITHUB_OUTPUT"
echo "CHANGELOG_EOF" >> "$GITHUB_OUTPUT"
release:
needs: [check_release, extract_changelog]
if: needs.check_release.outputs.releaseExists == 'false'
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- arm-unknown-linux-gnueabihf
- powerpc64le-unknown-linux-gnu
- s390x-unknown-linux-gnu
- aarch64-apple-darwin
- x86_64-apple-darwin
- x86_64-pc-windows-gnu
runs-on: ${{ (matrix.target == 'aarch64-apple-darwin' || matrix.target == 'x86_64-apple-darwin') && 'macos-latest' || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: release-${{ matrix.target }}
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross
if: runner.os == 'Linux'
- name: Install mingw-w64
run: sudo apt install -y mingw-w64
if: matrix.target == 'x86_64-pc-windows-gnu'
- name: Build (cross)
run: cross build --release --target ${{ matrix.target }}
if: runner.os == 'Linux'
- name: Build (native)
run: cargo build --release --target ${{ matrix.target }}
if: runner.os != 'Linux'
- name: Package
shell: bash
run: |
short_target=$(echo "${{ matrix.target }}" | sed 's/-unknown//')
staging="killport-${short_target}"
mkdir -p "$staging/completions" "$staging/man"
# Binary
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
cp "target/${{ matrix.target }}/release/killport.exe" "$staging/"
else
cp "target/${{ matrix.target }}/release/killport" "$staging/"
fi
# Shell completions and man page from build.rs output
out_dir=$(find "target/${{ matrix.target }}/release/build/killport-"*/out -maxdepth 0 -type d 2>/dev/null | head -1)
if [ -n "$out_dir" ]; then
cp "$out_dir"/completions/* "$staging/completions/" 2>/dev/null || true
cp "$out_dir"/man/* "$staging/man/" 2>/dev/null || true
fi
tar czvf "${staging}.tar.gz" "$staging"
- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.check_release.outputs.version }}
name: Release v${{ needs.check_release.outputs.version }}
body: ${{ needs.extract_changelog.outputs.changelog }}
files: 'killport-*.tar.gz'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish:
needs: [check_release, release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo publish --token ${{ secrets.CRATES_IO }}