feat(release): add per-tool VERSION files and auto-tag support #2
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "*/v*" | |
| permissions: | |
| contents: write | |
| jobs: | |
| meta: | |
| name: Parse release tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tool: ${{ steps.parse.outputs.tool }} | |
| version: ${{ steps.parse.outputs.version }} | |
| lang: ${{ steps.parse.outputs.lang }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Parse tag | |
| id: parse | |
| run: | | |
| TAG="${{ github.ref_name }}" | |
| TOOL="${TAG%%/v*}" | |
| VERSION="${TAG##*/}" | |
| echo "tool=$TOOL" >> "$GITHUB_OUTPUT" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| # Detect language by checking where the tool lives | |
| if [ -d "tools/$TOOL" ]; then | |
| echo "lang=go" >> "$GITHUB_OUTPUT" | |
| elif [ -d "libs/$TOOL" ]; then | |
| echo "lang=rust" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "::error::Unknown tool '$TOOL' — no tools/$TOOL or libs/$TOOL found" | |
| exit 1 | |
| fi | |
| echo "### Release Info" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Tool:** $TOOL" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Version:** $VERSION" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Validate VERSION file | |
| run: | | |
| TOOL="${{ steps.parse.outputs.tool }}" | |
| VERSION="${{ steps.parse.outputs.version }}" | |
| LANG="${{ steps.parse.outputs.lang }}" | |
| if [ "$LANG" = "go" ]; then | |
| VERSION_FILE="tools/${TOOL}/VERSION" | |
| else | |
| VERSION_FILE="libs/${TOOL}/VERSION" | |
| fi | |
| if [ ! -f "$VERSION_FILE" ]; then | |
| echo "::error::VERSION file not found at $VERSION_FILE" | |
| exit 1 | |
| fi | |
| FILE_VERSION="v$(tr -d '[:space:]' < "$VERSION_FILE")" | |
| if [ "$FILE_VERSION" != "$VERSION" ]; then | |
| echo "::error::Tag version ($VERSION) does not match $VERSION_FILE ($FILE_VERSION). Update the VERSION file first." | |
| exit 1 | |
| fi | |
| echo "✓ Tag $VERSION matches $VERSION_FILE" | |
| build-go: | |
| name: Go — ${{ needs.meta.outputs.tool }} (${{ matrix.goos }}/${{ matrix.goarch }}) | |
| needs: meta | |
| if: needs.meta.outputs.lang == 'go' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| goos: [linux, darwin, windows] | |
| goarch: [amd64, arm64] | |
| exclude: | |
| - goos: windows | |
| goarch: arm64 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.24" | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libpcap-dev | |
| - name: Build | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| CGO_ENABLED: "0" | |
| run: | | |
| TOOL="${{ needs.meta.outputs.tool }}" | |
| VERSION="${{ needs.meta.outputs.version }}" | |
| EXT="" | |
| if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi | |
| mkdir -p dist | |
| go build \ | |
| -ldflags="-s -w -X main.version=${VERSION}" \ | |
| -o "dist/${TOOL}-${GOOS}-${GOARCH}${EXT}" \ | |
| "./tools/${TOOL}" || echo "::warning::Skipped ${TOOL} for ${GOOS}/${GOARCH} (CGO required)" | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ needs.meta.outputs.tool }}-${{ matrix.goos }}-${{ matrix.goarch }} | |
| path: dist/ | |
| build-rust: | |
| name: Rust — ${{ needs.meta.outputs.tool }} (${{ matrix.target }}) | |
| needs: meta | |
| if: needs.meta.outputs.lang == 'rust' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| suffix: linux-amd64 | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| suffix: linux-arm64 | |
| - target: x86_64-apple-darwin | |
| os: macos-latest | |
| suffix: darwin-amd64 | |
| - target: aarch64-apple-darwin | |
| os: macos-latest | |
| suffix: darwin-arm64 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install cross-compilation tools | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV" | |
| - name: Build | |
| run: | | |
| TOOL="${{ needs.meta.outputs.tool }}" | |
| cargo build --release --target ${{ matrix.target }} -p "$TOOL" | |
| - name: Collect binary | |
| run: | | |
| TOOL="${{ needs.meta.outputs.tool }}" | |
| mkdir -p dist | |
| SRC="target/${{ matrix.target }}/release/${TOOL}" | |
| if [ -f "$SRC" ]; then | |
| cp "$SRC" "dist/${TOOL}-${{ matrix.suffix }}" | |
| elif [ -f "${SRC}.exe" ]; then | |
| cp "${SRC}.exe" "dist/${TOOL}-${{ matrix.suffix }}.exe" | |
| fi | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ needs.meta.outputs.tool }}-${{ matrix.suffix }} | |
| path: dist/ | |
| release: | |
| name: "Release ${{ needs.meta.outputs.tool }} ${{ needs.meta.outputs.version }}" | |
| needs: [meta, build-go, build-rust] | |
| if: | | |
| always() && | |
| needs.meta.result == 'success' && | |
| (needs.build-go.result == 'success' || needs.build-go.result == 'skipped') && | |
| (needs.build-rust.result == 'success' || needs.build-rust.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: "${{ needs.meta.outputs.tool }}-*" | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd artifacts | |
| sha256sum * > checksums-sha256.txt 2>/dev/null || echo "No binaries" | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| TOOL="${{ needs.meta.outputs.tool }}" | |
| # Find previous tag for this specific tool | |
| PREV_TAG=$(git tag -l "${TOOL}/v*" --sort=-v:refname | head -2 | tail -1) | |
| if [ -z "$PREV_TAG" ] || [ "$PREV_TAG" = "${{ github.ref_name }}" ]; then | |
| echo "changelog<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "Initial release of **${TOOL}**" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "changelog<<EOF" >> "$GITHUB_OUTPUT" | |
| git log --pretty=format:"- %s (%h)" "${PREV_TAG}..HEAD" -- "tools/${TOOL}" "libs/${TOOL}" "libs/netutil/" >> "$GITHUB_OUTPUT" | |
| echo "" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Detect install command | |
| id: install | |
| run: | | |
| TOOL="${{ needs.meta.outputs.tool }}" | |
| VERSION="${{ needs.meta.outputs.version }}" | |
| LANG="${{ needs.meta.outputs.lang }}" | |
| if [ "$LANG" = "go" ]; then | |
| echo "cmd=go install github.com/flaviomilan/sectools/tools/${TOOL}@${VERSION}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "cmd=cargo install --git https://github.com/flaviomilan/sectools --tag ${{ github.ref_name }} -p ${TOOL}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: "${{ needs.meta.outputs.tool }} ${{ needs.meta.outputs.version }}" | |
| tag_name: ${{ github.ref_name }} | |
| body: | | |
| ## ${{ needs.meta.outputs.tool }} ${{ needs.meta.outputs.version }} | |
| ${{ steps.changelog.outputs.changelog }} | |
| ### Install | |
| **From source:** | |
| ```bash | |
| ${{ steps.install.outputs.cmd }} | |
| ``` | |
| **Pre-built binary:** | |
| ```bash | |
| # Linux amd64 | |
| curl -Lo ${{ needs.meta.outputs.tool }} \ | |
| https://github.com/flaviomilan/sectools/releases/download/${{ github.ref_name }}/${{ needs.meta.outputs.tool }}-linux-amd64 | |
| chmod +x ${{ needs.meta.outputs.tool }} | |
| sudo mv ${{ needs.meta.outputs.tool }} /usr/local/bin/ | |
| ``` | |
| ### Checksums (SHA-256) | |
| See `checksums-sha256.txt` in the release assets. | |
| files: artifacts/* |