diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3f038b7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,638 @@ +name: Release + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + +permissions: + contents: write + +jobs: + validate: + name: Validate + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Wait for Tests workflow to complete + uses: WyriHaximus/github-action-wait-for-status@c638eadb55e7c6d951b0eb5fff5733569d82b03e # pinned commit for 1.8.0 + with: + ignoreActions: Release + checkInterval: 30 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout code + uses: actions/checkout@v4 # not pinning to commit hash since this is a GitHub action, which we trust + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # pinned commit for 1.15.2 + with: + toolchain: stable + components: rustfmt, clippy + + - name: Check code formatting + run: cargo fmt --check + + - name: Run clippy + run: cargo clippy -- -D warnings + + build: + name: Build ${{ matrix.platform }}-${{ matrix.arch }} + needs: validate + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-22.04 + platform: linux + arch: x86_64 + ext: tar.gz + + - target: aarch64-unknown-linux-gnu + os: ubuntu-22.04-arm + platform: linux + arch: aarch64 + ext: tar.gz + + - target: aarch64-apple-darwin + os: macos-14 + platform: macos + arch: aarch64 + ext: tar.gz + + - target: x86_64-pc-windows-msvc + os: windows-2022 + platform: windows + arch: x86_64 + ext: zip + + - target: aarch64-pc-windows-msvc + os: windows-11-arm + platform: windows + arch: aarch64 + ext: zip + steps: + - name: Checkout code + uses: actions/checkout@v4 # not pinning to commit hash since this is a GitHub action, which we trust + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # pinned commit for 1.15.2 + with: + toolchain: stable + + - name: Build release binaries + run: cargo build --release + + - name: Verify library files (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + echo "Verifying library files..." + if [ "${{ matrix.platform }}" = "linux" ]; then + test -f target/release/libcrc_fast.so || { echo "Missing libcrc_fast.so"; exit 1; } + test -f target/release/libcrc_fast.a || { echo "Missing libcrc_fast.a"; exit 1; } + echo "✓ Found libcrc_fast.so and libcrc_fast.a" + elif [ "${{ matrix.platform }}" = "macos" ]; then + test -f target/release/libcrc_fast.dylib || { echo "Missing libcrc_fast.dylib"; exit 1; } + test -f target/release/libcrc_fast.a || { echo "Missing libcrc_fast.a"; exit 1; } + echo "✓ Found libcrc_fast.dylib and libcrc_fast.a" + fi + + - name: Verify library files (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Host "Verifying library files..." + if (-not (Test-Path "target/release/crc_fast.dll")) { + Write-Error "Missing crc_fast.dll" + exit 1 + } + if (-not (Test-Path "target/release/crc_fast.dll.lib")) { + Write-Error "Missing crc_fast.dll.lib" + exit 1 + } + if (-not (Test-Path "target/release/crc_fast.lib")) { + Write-Error "Missing crc_fast.lib" + exit 1 + } + Write-Host "✓ Found crc_fast.dll, crc_fast.dll.lib, and crc_fast.lib" + + - name: Verify CLI binaries (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + echo "Verifying CLI binaries..." + test -f target/release/checksum || { echo "Missing checksum"; exit 1; } + test -f target/release/arch-check || { echo "Missing arch-check"; exit 1; } + test -f target/release/get-custom-params || { echo "Missing get-custom-params"; exit 1; } + echo "✓ Found checksum, arch-check, and get-custom-params" + + - name: Verify CLI binaries (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Host "Verifying CLI binaries..." + if (-not (Test-Path "target/release/checksum.exe")) { + Write-Error "Missing checksum.exe" + exit 1 + } + if (-not (Test-Path "target/release/arch-check.exe")) { + Write-Error "Missing arch-check.exe" + exit 1 + } + if (-not (Test-Path "target/release/get-custom-params.exe")) { + Write-Error "Missing get-custom-params.exe" + exit 1 + } + Write-Host "✓ Found checksum.exe, arch-check.exe, and get-custom-params.exe" + + - name: Stage Linux package + if: matrix.platform == 'linux' + shell: bash + run: | + PKG_NAME="crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}" + echo "Creating package directory structure for $PKG_NAME..." + + mkdir -p "$PKG_NAME/lib" + mkdir -p "$PKG_NAME/include" + mkdir -p "$PKG_NAME/bin" + + echo "Copying library files..." + cp target/release/libcrc_fast.so "$PKG_NAME/lib/" + cp target/release/libcrc_fast.a "$PKG_NAME/lib/" + + echo "Copying header file..." + cp libcrc_fast.h "$PKG_NAME/include/" + + echo "Copying CLI binaries..." + cp target/release/checksum "$PKG_NAME/bin/" + cp target/release/arch-check "$PKG_NAME/bin/" + cp target/release/get-custom-params "$PKG_NAME/bin/" + + echo "Setting executable permissions on binaries..." + chmod +x "$PKG_NAME/bin/checksum" + chmod +x "$PKG_NAME/bin/arch-check" + chmod +x "$PKG_NAME/bin/get-custom-params" + + echo "Creating VERSION file..." + echo "${{ github.ref_name }}" > "$PKG_NAME/VERSION" + + echo "Creating README.txt..." + cat > "$PKG_NAME/README.txt" < "$CHECKSUM_FILE" + + echo "Verifying checksum file was created..." + test -f "$CHECKSUM_FILE" || { echo "Failed to create $CHECKSUM_FILE"; exit 1; } + + echo "Checksum file contents:" + cat "$CHECKSUM_FILE" + + echo "✓ SHA256 checksum generated successfully" + + - name: Stage macOS package + if: matrix.platform == 'macos' + shell: bash + run: | + PKG_NAME="crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}" + echo "Creating package directory structure for $PKG_NAME..." + + mkdir -p "$PKG_NAME/lib" + mkdir -p "$PKG_NAME/include" + mkdir -p "$PKG_NAME/bin" + + echo "Copying library files..." + cp target/release/libcrc_fast.dylib "$PKG_NAME/lib/" + cp target/release/libcrc_fast.a "$PKG_NAME/lib/" + + echo "Copying header file..." + cp libcrc_fast.h "$PKG_NAME/include/" + + echo "Copying CLI binaries..." + cp target/release/checksum "$PKG_NAME/bin/" + cp target/release/arch-check "$PKG_NAME/bin/" + cp target/release/get-custom-params "$PKG_NAME/bin/" + + echo "Setting executable permissions on binaries..." + chmod +x "$PKG_NAME/bin/checksum" + chmod +x "$PKG_NAME/bin/arch-check" + chmod +x "$PKG_NAME/bin/get-custom-params" + + echo "Creating VERSION file..." + echo "${{ github.ref_name }}" > "$PKG_NAME/VERSION" + + echo "Creating README.txt..." + cat > "$PKG_NAME/README.txt" < "$CHECKSUM_FILE" + + echo "Verifying checksum file was created..." + test -f "$CHECKSUM_FILE" || { echo "Failed to create $CHECKSUM_FILE"; exit 1; } + + echo "Checksum file contents:" + cat "$CHECKSUM_FILE" + + echo "✓ SHA256 checksum generated successfully" + + - name: Stage Windows package + if: matrix.platform == 'windows' + shell: pwsh + run: | + $PKG_NAME = "crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}" + Write-Host "Creating package directory structure for $PKG_NAME..." + + New-Item -ItemType Directory -Path "$PKG_NAME/bin" -Force | Out-Null + New-Item -ItemType Directory -Path "$PKG_NAME/lib" -Force | Out-Null + New-Item -ItemType Directory -Path "$PKG_NAME/include" -Force | Out-Null + + Write-Host "Copying library files..." + Copy-Item "target/release/crc_fast.dll" "$PKG_NAME/bin/" + Copy-Item "target/release/crc_fast.dll.lib" "$PKG_NAME/lib/" + Copy-Item "target/release/crc_fast.lib" "$PKG_NAME/lib/" + + Write-Host "Copying header file..." + Copy-Item "libcrc_fast.h" "$PKG_NAME/include/" + + Write-Host "Copying CLI binaries..." + Copy-Item "target/release/checksum.exe" "$PKG_NAME/bin/" + Copy-Item "target/release/arch-check.exe" "$PKG_NAME/bin/" + Copy-Item "target/release/get-custom-params.exe" "$PKG_NAME/bin/" + + Write-Host "Creating VERSION file..." + "${{ github.ref_name }}" | Out-File -FilePath "$PKG_NAME/VERSION" -Encoding utf8 -NoNewline + + Write-Host "Creating README.txt..." + @" + crc-fast Binary Distribution + Version: ${{ github.ref_name }} + Platform: Windows ${{ matrix.arch }} + + CONTENTS + ======== + This package contains: + - Dynamic library (crc_fast.dll) with import library (crc_fast.dll.lib) + - Static library (crc_fast.lib) + - C/C++ header file (libcrc_fast.h) + - Command-line utilities (checksum.exe, arch-check.exe, get-custom-params.exe) + + INSTALLATION + ============ + 1. Extract this archive to your desired location + 2. Add the bin\ directory to your PATH environment variable: + - Open System Properties > Environment Variables + - Edit the PATH variable and add: C:\path\to\crc-fast\bin + + For development: + - Add lib\ directory to your linker library path + - Add include\ directory to your compiler include path + + LINKING + ======= + Dynamic linking (MSVC): + cl your_program.c /I"C:\path\to\crc-fast\include" /link crc_fast.dll.lib /LIBPATH:"C:\path\to\crc-fast\lib" + Note: crc_fast.dll must be in PATH or same directory as your executable + + Static linking (MSVC): + cl your_program.c /I"C:\path\to\crc-fast\include" /link crc_fast.lib /LIBPATH:"C:\path\to\crc-fast\lib" + + Dynamic linking (MinGW): + gcc your_program.c -I"C:\path\to\crc-fast\include" -L"C:\path\to\crc-fast\lib" -lcrc_fast + + USAGE + ===== + Command-line tools: + checksum.exe --help # Calculate CRC checksums + arch-check.exe # Display CPU architecture features + get-custom-params.exe --help # Generate CRC parameters + + DOCUMENTATION + ============= + Full documentation: https://github.com/awesomized/crc-fast-rust + + LICENSE + ======= + This software is dual-licensed under MIT OR Apache-2.0. + See LICENSE-MIT and LICENSE-Apache files for details. + "@ | Out-File -FilePath "$PKG_NAME/README.txt" -Encoding utf8 + + Write-Host "Copying license files..." + Copy-Item "LICENSE-MIT" "$PKG_NAME/" + Copy-Item "LICENSE-Apache" "$PKG_NAME/" + + Write-Host "✓ Windows package staged successfully" + Get-ChildItem -Recurse "$PKG_NAME" + + - name: Create zip package (Windows) + if: matrix.platform == 'windows' + shell: pwsh + run: | + $PKG_NAME = "crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}" + $ARCHIVE_NAME = "${PKG_NAME}.zip" + + Write-Host "Creating compressed archive: $ARCHIVE_NAME" + Compress-Archive -Path "$PKG_NAME" -DestinationPath "$ARCHIVE_NAME" -CompressionLevel Optimal + + Write-Host "Verifying archive was created..." + if (-not (Test-Path "$ARCHIVE_NAME")) { + Write-Error "Failed to create $ARCHIVE_NAME" + exit 1 + } + + Write-Host "Archive details:" + Get-Item "$ARCHIVE_NAME" | Format-List Name, Length, LastWriteTime + + Write-Host "✓ zip package created successfully" + + - name: Generate SHA256 checksum (Windows) + if: matrix.platform == 'windows' + shell: pwsh + run: | + $ARCHIVE_NAME = "crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}.zip" + $CHECKSUM_FILE = "${ARCHIVE_NAME}.sha256" + + Write-Host "Generating SHA256 checksum for $ARCHIVE_NAME..." + $hash = (Get-FileHash -Path "$ARCHIVE_NAME" -Algorithm SHA256).Hash.ToLower() + "$hash $ARCHIVE_NAME" | Out-File -FilePath "$CHECKSUM_FILE" -Encoding utf8 -NoNewline + + Write-Host "Verifying checksum file was created..." + if (-not (Test-Path "$CHECKSUM_FILE")) { + Write-Error "Failed to create $CHECKSUM_FILE" + exit 1 + } + + Write-Host "Checksum file contents:" + Get-Content "$CHECKSUM_FILE" + + Write-Host "✓ SHA256 checksum generated successfully" + + - name: Upload package artifact + uses: actions/upload-artifact@v4 # not pinning to commit hash since this is a GitHub action, which we trust + with: + name: crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}.${{ matrix.ext }} + path: crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}.${{ matrix.ext }} + if-no-files-found: error + retention-days: 90 + + - name: Upload checksum artifact + uses: actions/upload-artifact@v4 # not pinning to commit hash since this is a GitHub action, which we trust + with: + name: crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}.${{ matrix.ext }}.sha256 + path: crc-fast-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.arch }}.${{ matrix.ext }}.sha256 + if-no-files-found: error + retention-days: 90 + + publish: + name: Publish + needs: build + runs-on: ubuntu-latest + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 # not pinning to commit hash since this is a GitHub action, which we trust + with: + path: artifacts + merge-multiple: false + + - name: Check for existing release + id: check_release + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Checking if release exists for tag: ${{ github.ref_name }}" + + RELEASE_DATA=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}") + + if echo "$RELEASE_DATA" | jq -e '.id' > /dev/null 2>&1; then + RELEASE_ID=$(echo "$RELEASE_DATA" | jq -r '.id') + echo "release_exists=true" >> $GITHUB_OUTPUT + echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT + echo "✓ Release already exists with ID: $RELEASE_ID" + else + echo "release_exists=false" >> $GITHUB_OUTPUT + echo "release_id=" >> $GITHUB_OUTPUT + echo "✓ No existing release found for this tag" + fi + + - name: Organize files for upload + shell: bash + run: | + echo "Organizing downloaded artifacts..." + + mkdir -p release-assets + + echo "Moving packages and checksums to release-assets directory..." + find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.sha256" \) -exec mv {} release-assets/ \; + + echo "Release assets ready for upload:" + ls -lh release-assets/ + + echo "Verifying all expected files are present..." + EXPECTED_FILES=( + "crc-fast-${{ github.ref_name }}-linux-x86_64.tar.gz" + "crc-fast-${{ github.ref_name }}-linux-x86_64.tar.gz.sha256" + "crc-fast-${{ github.ref_name }}-linux-aarch64.tar.gz" + "crc-fast-${{ github.ref_name }}-linux-aarch64.tar.gz.sha256" + "crc-fast-${{ github.ref_name }}-macos-aarch64.tar.gz" + "crc-fast-${{ github.ref_name }}-macos-aarch64.tar.gz.sha256" + "crc-fast-${{ github.ref_name }}-windows-x86_64.zip" + "crc-fast-${{ github.ref_name }}-windows-x86_64.zip.sha256" + "crc-fast-${{ github.ref_name }}-windows-aarch64.zip" + "crc-fast-${{ github.ref_name }}-windows-aarch64.zip.sha256" + ) + + MISSING_FILES=() + for file in "${EXPECTED_FILES[@]}"; do + if [ ! -f "release-assets/$file" ]; then + MISSING_FILES+=("$file") + fi + done + + if [ ${#MISSING_FILES[@]} -gt 0 ]; then + echo "ERROR: Missing expected files:" + printf '%s\n' "${MISSING_FILES[@]}" + exit 1 + fi + + echo "✓ All expected files are present" + + echo "Total number of files: $(ls -1 release-assets/ | wc -l)" + echo "Total size: $(du -sh release-assets/ | cut -f1)" + + - name: Create or update GitHub release + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # pinned commit for 2.4.1 + with: + files: release-assets/* + draft: false + prerelease: false + fail_on_unmatched_files: true + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.kiro/specs/github-release-automation/design.md b/.kiro/specs/github-release-automation/design.md new file mode 100644 index 0000000..7ac3181 --- /dev/null +++ b/.kiro/specs/github-release-automation/design.md @@ -0,0 +1,402 @@ +# Design Document + +## Overview + +This design implements a GitHub Actions workflow that automatically builds and publishes binary packages when version tags are pushed to the repository. The workflow leverages the existing test infrastructure, builds optimized binaries for five target platforms, packages them according to platform conventions, and uploads them as GitHub release assets. + +The solution uses a single workflow file that orchestrates testing, building, packaging, and publishing steps. It reuses the existing test workflow to ensure quality gates are met before creating releases. + +## Architecture + +### Workflow Structure + +The release workflow consists of three main stages: + +1. **Validation Stage**: Triggers existing test workflow and waits for completion +2. **Build Stage**: Compiles release binaries for all target platforms in parallel +3. **Publish Stage**: Creates packages, generates checksums, and uploads to GitHub release + +### Workflow Trigger + +```yaml +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' +``` + +This pattern matches semantic version tags like `1.5.0`, `2.0.1`, etc. + +### Build Matrix + +The workflow uses a matrix strategy to build for multiple platforms: + +| Target Triple | OS Runner | Package Extension | Runner Rationale | +|--------------|-----------|-------------------|------------------| +| x86_64-unknown-linux-gnu | ubuntu-22.04 | .tar.gz | Native x86_64 Linux runner, explicit version for stability | +| aarch64-unknown-linux-gnu | ubuntu-22.04-arm | .tar.gz | Native ARM64 Linux runner, explicit version for stability | +| aarch64-apple-darwin | macos-14 | .tar.gz | Native Apple Silicon runner (M1/M2), explicit version for stability | +| x86_64-pc-windows-msvc | windows-2022 | .zip | Native x86_64 Windows runner, explicit version for stability | +| aarch64-pc-windows-msvc | windows-11-arm | .zip | Native ARM64 Windows runner (only ARM64 option available) | + +**Runner Selection Rationale**: + +#### General Principles +1. **Native Compilation**: Each runner matches the target architecture for optimal performance and to leverage architecture-specific CPU features (AVX-512, NEON, etc.) +2. **Binary Compatibility**: Binaries built on older OS versions are forward-compatible with newer versions, but not backward-compatible +3. **Single Binary Per Architecture**: One binary package per architecture is sufficient due to OS forward compatibility +4. **Explicit Versions**: Using explicit runner versions (ubuntu-22.04, windows-2022, macos-14) instead of -latest provides stability and predictability, preventing unexpected changes when GitHub updates their -latest pointers + +#### Platform-Specific Decisions + +**Linux (ubuntu-22.04 for x86_64, ubuntu-22.04-arm for aarch64)**: +- Explicit version (22.04) ensures consistent build environment over time +- Ubuntu 22.04 uses glibc 2.35, providing good compatibility with modern distributions +- Ubuntu binaries have excellent forward compatibility across distributions (Ubuntu, Debian, RHEL, etc.) +- When ubuntu-latest moves to 24.04 or newer, our release builds remain stable on 22.04 +- No need for multiple Linux versions; one binary per architecture covers the ecosystem +- Using explicit versions allows intentional upgrades rather than automatic changes + +**macOS (macos-14 for aarch64)**: +- `macos-14` is the first stable Apple Silicon (M1/M2) runner +- macOS binaries built on older versions run on newer versions (forward compatible) +- `macos-15` and `macos-latest` would work but offer no compatibility advantage +- Using `macos-14` provides maximum compatibility (works on macOS 14, 15, and future versions) +- No x86_64 macOS build needed: Apple Silicon Macs can run x86_64 binaries via Rosetta 2, but native ARM64 is preferred +- Single ARM64 binary covers all Apple Silicon Macs (M1, M2, M3, M4, etc.) + +**Windows (windows-2022 for x86_64, windows-11-arm for aarch64)**: +- Explicit version (2022) ensures consistent build environment over time +- Windows Server 2022 binaries have excellent forward compatibility (run on Windows 11, future versions) +- MSVC runtime is statically linked in Rust release builds, eliminating runtime dependencies +- When windows-latest moves to Server 2025 or newer, our release builds remain stable on 2022 +- `windows-11-arm` is the only ARM64 Windows runner available (no version choice) +- Using explicit versions allows intentional upgrades rather than automatic changes +- No need for multiple Windows versions; one binary per architecture covers the ecosystem + +#### Why Not Multiple OS Versions? + +**Rejected Approach**: Building separate packages for each OS version (e.g., macos-14, macos-15, ubuntu-22.04, ubuntu-24.04) + +**Reasons**: +1. **Forward Compatibility**: Binaries built on older OS versions work on newer versions +2. **Maintenance Burden**: Multiple packages per architecture increases complexity without benefit +3. **User Confusion**: Users would need to choose between multiple packages for the same architecture +4. **Storage Costs**: More packages means more storage and bandwidth +5. **Testing Overhead**: The existing test workflow already validates across multiple OS versions (macos-14, macos-15, ubuntu-22.04, ubuntu-24.04, etc.), proving binary compatibility + +**Conclusion**: One binary package per architecture provides maximum compatibility with minimum complexity. The test workflow validates compatibility across OS versions, while the release workflow builds on the oldest supported version for maximum forward compatibility. + +## Components and Interfaces + +### 1. Release Workflow File + +**Location**: `.github/workflows/release.yml` + +**Responsibilities**: +- Trigger on version tags +- Wait for test workflow completion +- Build release binaries for all targets +- Create platform-specific packages +- Generate SHA256 checksums +- Upload packages and checksums to GitHub release + +**Key Jobs**: + +#### Job: `validate` +- Waits for the existing test workflow to complete for the tagged commit +- Uses an action like `lewagon/wait-on-check-action` to poll test workflow status +- Fails the release if tests don't pass +- Includes timeout to prevent infinite waiting (e.g., 60 minutes) +- Runs quality checks (fmt, clippy) after tests pass + +#### Job: `build` (matrix) +- Depends on `validate` job +- Checks out code +- Sets up Rust toolchain (stable) +- Runs quality checks (fmt, clippy) +- Builds release binaries with `cargo build --release` (LTO and stripping handled by Cargo.toml profile) +- Builds CLI binaries (checksum, arch-check, get-custom-params) +- Stages files in platform-specific directory structure +- Creates compressed package +- Generates SHA256 checksum +- Uploads package and checksum as artifacts + +#### Job: `publish` +- Depends on all `build` jobs +- Downloads all artifacts +- Checks if GitHub release already exists for the tag +- If release exists, uploads packages and checksums to existing release +- If release doesn't exist, creates new release and uploads assets +- Allows manual release creation with custom notes before workflow runs + +### 2. Package Structure + +#### Linux Packages (x86_64 and aarch64) + +``` +crc-fast-{version}-linux-{arch}/ +├── lib/ +│ ├── libcrc_fast.so +│ └── libcrc_fast.a +├── include/ +│ └── libcrc_fast.h +├── bin/ +│ ├── checksum +│ ├── arch-check +│ └── get-custom-params +├── LICENSE-MIT +├── LICENSE-Apache +├── VERSION +└── README.txt +``` + +#### macOS Package (aarch64) + +``` +crc-fast-{version}-macos-aarch64/ +├── lib/ +│ ├── libcrc_fast.dylib +│ └── libcrc_fast.a +├── include/ +│ └── libcrc_fast.h +├── bin/ +│ ├── checksum +│ ├── arch-check +│ └── get-custom-params +├── LICENSE-MIT +├── LICENSE-Apache +├── VERSION +└── README.txt +``` + +#### Windows Packages (x86_64 and aarch64) + +``` +crc-fast-{version}-windows-{arch}/ +├── bin/ +│ ├── crc_fast.dll +│ ├── checksum.exe +│ ├── arch-check.exe +│ └── get-custom-params.exe +├── lib/ +│ ├── crc_fast.dll.lib (import library for linking against DLL) +│ └── crc_fast.lib (static library) +├── include/ +│ └── libcrc_fast.h +├── LICENSE-MIT +├── LICENSE-Apache +├── VERSION +└── README.txt +``` + +**Windows Library Files Explained**: +- `crc_fast.dll`: The dynamic library containing the actual code +- `crc_fast.dll.lib`: Import library needed to link against the DLL at compile time +- `crc_fast.lib`: Static library for static linking (standalone, doesn't need DLL) + +### 3. Package Metadata Files + +#### VERSION File +Contains the version number extracted from the git tag: +``` +1.5.0 +``` + +#### README.txt File +Provides basic installation and usage instructions: +``` +crc-fast Binary Distribution +Version: {version} +Platform: {platform}-{arch} + +Contents: +- Library files (dynamic and static) +- Header file for C/C++ integration +- Command-line utilities + +Installation: +[Platform-specific instructions] + +Usage: +See https://github.com/awesomized/crc-fast-rust for documentation + +License: MIT OR Apache-2.0 +``` + +### 4. Checksum Files + +Each package has an accompanying `.sha256` file: +``` +{sha256_hash} crc-fast-{version}-{platform}-{arch}.{ext} +``` + +## Data Models + +### Build Artifact Structure + +```yaml +artifacts: + - name: "crc-fast-{version}-{platform}-{arch}.{ext}" + type: "package" + checksum: "{sha256_hash}" + - name: "crc-fast-{version}-{platform}-{arch}.{ext}.sha256" + type: "checksum" +``` + +### Matrix Configuration + +```yaml +matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-22.04 + platform: linux + arch: x86_64 + ext: tar.gz + + - target: aarch64-unknown-linux-gnu + os: ubuntu-22.04-arm + platform: linux + arch: aarch64 + ext: tar.gz + + - target: aarch64-apple-darwin + os: macos-14 + platform: macos + arch: aarch64 + ext: tar.gz + + - target: x86_64-pc-windows-msvc + os: windows-2022 + platform: windows + arch: x86_64 + ext: zip + + - target: aarch64-pc-windows-msvc + os: windows-11-arm + platform: windows + arch: aarch64 + ext: zip +``` + +## Error Handling + +### Test Failure +- If the test workflow fails, the `validate` job fails +- The `build` jobs don't execute (dependency chain) +- No release is created +- GitHub Actions shows clear failure status + +### Build Failure +- If any platform build fails, that specific matrix job fails +- Other platform builds continue +- The `publish` job waits for all builds +- If any build failed, `publish` job is skipped +- Partial releases are prevented + +### Quality Check Failure +- If `cargo fmt --check` fails, build stops +- If `cargo clippy` produces warnings, build stops +- Ensures all released binaries meet quality standards + +### Missing Files +- Build script validates all expected files exist before packaging +- Fails with clear error message if files are missing +- Prevents incomplete packages + +### Upload Failure +- If GitHub release creation fails, workflow fails +- If asset upload fails, workflow retries (GitHub Actions default) +- All artifacts remain available for manual inspection + +## Testing Strategy + +### Pre-Release Testing +1. The existing test workflow must pass completely +2. All platforms in the test matrix must succeed +3. Format and clippy checks must pass + +### Build Verification +1. Verify all expected files are present in target/release +2. Check file sizes are reasonable (not empty) +3. Validate binary executables are actually executable +4. Confirm library files have correct extensions + +### Package Verification +1. Verify package structure matches specification +2. Check all metadata files are included +3. Validate checksums are generated correctly +4. Ensure package names follow naming convention + +### Integration Testing +1. Test workflow on a feature branch with test tags +2. Verify packages can be downloaded +3. Test that libraries can be linked +4. Verify CLI binaries execute correctly +5. Validate checksums match package contents + +### Manual Testing Checklist +Before first production release: +- [ ] Create test tag on feature branch +- [ ] Verify workflow triggers correctly +- [ ] Download each platform package +- [ ] Extract and verify contents +- [ ] Test linking dynamic library +- [ ] Test linking static library +- [ ] Run each CLI binary +- [ ] Verify checksums +- [ ] Test on actual target platforms + +## Design Decisions + +### Decision 1: Single Package Per Platform +**Rationale**: Combining libraries and CLI tools in one package simplifies distribution and ensures users get everything they need. Separate packages would create confusion about which package to download. + +**Alternative Considered**: Separate library and CLI packages. Rejected because it adds complexity for minimal benefit. + +### Decision 2: Include Both Dynamic and Static Libraries +**Rationale**: Different projects have different linking requirements. Providing both gives maximum flexibility without requiring users to build from source. + +**Alternative Considered**: Separate packages for dynamic and static. Rejected because the size overhead is minimal and user experience is worse. + +### Decision 3: Platform-Specific Directory Layouts +**Rationale**: Following platform conventions makes integration easier for users familiar with their platform's standards. Windows expects DLLs in bin/, while Unix expects shared libraries in lib/. + +**Alternative Considered**: Unified layout across all platforms. Rejected because it would be non-standard on all platforms. + +### Decision 4: Reuse Existing Test Workflow +**Rationale**: The existing test workflow is comprehensive and already validates all target platforms. Duplicating tests would be wasteful and create maintenance burden. + +**Alternative Considered**: Inline tests in release workflow. Rejected because it duplicates logic and increases workflow complexity. + +### Decision 5: Matrix Build Strategy +**Rationale**: Building all platforms in parallel maximizes speed and allows independent failure handling. GitHub Actions provides excellent matrix support. + +**Alternative Considered**: Sequential builds. Rejected because it would significantly increase release time. + +### Decision 6: SHA256 Checksums +**Rationale**: SHA256 is the industry standard for verifying download integrity. It's widely supported and provides strong security guarantees. + +**Alternative Considered**: MD5 or SHA1. Rejected because they're cryptographically weak. Multiple checksums rejected as unnecessary. + +### Decision 7: Separate Checksum Files +**Rationale**: Separate files allow users to download and verify checksums independently. This is the standard practice for software distribution. + +**Alternative Considered**: Checksums in release notes. Rejected because it's less convenient for automated verification. + +### Decision 8: Tag Pattern Without 'v' Prefix +**Rationale**: The project already uses tags like `1.5.0` without the 'v' prefix. Maintaining consistency with existing practice is important. + +**Alternative Considered**: Requiring 'v' prefix. Rejected because it would break existing tagging convention. + +### Decision 9: Update Existing Release Rather Than Create +**Rationale**: Allowing manual release creation before the workflow runs gives maintainers control over release notes, descriptions, and other metadata. The workflow focuses on building and uploading binaries, not managing release content. + +**Workflow**: +1. Maintainer creates release manually with desired notes +2. Maintainer creates and pushes tag +3. Workflow builds binaries and uploads to existing release +4. If no release exists, workflow creates a minimal one + +**Alternative Considered**: Workflow always creates release. Rejected because it forces auto-generated release notes and removes maintainer control over release presentation. diff --git a/.kiro/specs/github-release-automation/requirements.md b/.kiro/specs/github-release-automation/requirements.md new file mode 100644 index 0000000..b43106b --- /dev/null +++ b/.kiro/specs/github-release-automation/requirements.md @@ -0,0 +1,101 @@ +# Requirements Document + +## Introduction + +This feature automates the creation and distribution of binary packages for the crc-fast library when a tagged version release is created on GitHub. The system will build platform-specific packages containing both library files (dynamic and static) and command-line utilities, following industry-standard packaging conventions for each target platform and architecture combination. + +## Glossary + +- **Release Workflow**: A GitHub Actions workflow that triggers on git tag creation and produces binary artifacts +- **Binary Package**: A compressed archive containing compiled library files, headers, and executables for a specific platform-architecture combination +- **Dynamic Library**: A shared library file (.so, .dylib, or .dll) that can be linked at runtime +- **Static Library**: A library archive (.a or .lib) that is linked at compile time +- **CLI Binary**: Command-line executable programs (checksum, arch-check, get-custom-params) +- **Target Triple**: A Rust platform identifier (e.g., x86_64-unknown-linux-gnu) +- **Package Layout**: The directory structure and file organization within a binary package +- **Release Asset**: A file attached to a GitHub release that users can download + +## Requirements + +### Requirement 1 + +**User Story:** As a library consumer, I want to download pre-built binaries for my platform, so that I can use crc-fast without compiling from source + +#### Acceptance Criteria + +1. WHEN a git tag matching the pattern {MAJOR}.{MINOR}.{PATCH} is pushed, THE Release Workflow SHALL trigger automatically +2. THE Release Workflow SHALL build binaries for x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-pc-windows-msvc, and aarch64-pc-windows-msvc targets +3. THE Release Workflow SHALL execute all tests successfully before building release binaries +4. THE Release Workflow SHALL produce release-optimized builds using the existing Cargo.toml release profile (LTO, strip, opt-level 3) +5. THE Release Workflow SHALL upload all binary packages as GitHub release assets + +### Requirement 2 + +**User Story:** As a C/C++ developer, I want library packages with both dynamic and static libraries, so that I can choose the linking strategy that fits my project + +#### Acceptance Criteria + +1. THE Release Workflow SHALL include both dynamic library files and static library files in library packages +2. WHERE the target is Linux, THE Release Workflow SHALL include libcrc_fast.so and libcrc_fast.a files +3. WHERE the target is macOS, THE Release Workflow SHALL include libcrc_fast.dylib and libcrc_fast.a files +4. WHERE the target is Windows, THE Release Workflow SHALL include crc_fast.dll, crc_fast.dll.lib (import library), and crc_fast.lib (static library) files +5. THE Release Workflow SHALL include the libcrc_fast.h header file in all library packages + +### Requirement 3 + +**User Story:** As a command-line user, I want executable binaries for the checksum utilities, so that I can use crc-fast tools directly from my terminal + +#### Acceptance Criteria + +1. THE Release Workflow SHALL build checksum, arch-check, and get-custom-params executable binaries +2. THE Release Workflow SHALL include CLI binaries in library packages +3. WHERE the target is Windows, THE Release Workflow SHALL include .exe extensions on executable files +4. WHERE the target is Linux or macOS, THE Release Workflow SHALL set executable permissions on binary files + +### Requirement 4 + +**User Story:** As a package consumer, I want packages organized according to platform conventions, so that I can easily integrate them into my build system + +#### Acceptance Criteria + +1. WHERE the target is Linux, THE Release Workflow SHALL organize files in lib/ and include/ directories +2. WHERE the target is macOS, THE Release Workflow SHALL organize files in lib/ and include/ directories +3. WHERE the target is Windows, THE Release Workflow SHALL organize files in bin/ and include/ directories +4. THE Release Workflow SHALL place CLI executables in bin/ subdirectory for Linux and macOS targets +5. THE Release Workflow SHALL place CLI executables in bin/ subdirectory for Windows targets alongside the DLL + +### Requirement 5 + +**User Story:** As a release maintainer, I want descriptive package names, so that users can easily identify the correct package for their platform + +#### Acceptance Criteria + +1. THE Release Workflow SHALL name packages using the format crc-fast-{version}-{os}-{arch}.{extension} +2. WHERE the target is Linux, THE Release Workflow SHALL use .tar.gz extension +3. WHERE the target is macOS, THE Release Workflow SHALL use .tar.gz extension +4. WHERE the target is Windows, THE Release Workflow SHALL use .zip extension +5. THE Release Workflow SHALL include the git tag version in the package filename + +### Requirement 6 + +**User Story:** As a quality-conscious maintainer, I want the release workflow to reuse existing test infrastructure, so that releases are only created when all tests pass + +#### Acceptance Criteria + +1. THE Release Workflow SHALL depend on successful completion of the existing test workflow +2. THE Release Workflow SHALL use the same Rust toolchain version as the test workflow +3. THE Release Workflow SHALL fail and prevent release creation if any test fails +4. THE Release Workflow SHALL run cargo fmt and cargo clippy checks before building +5. THE Release Workflow SHALL only trigger on tags matching [0-9]+.[0-9]+.[0-9]+ pattern + +### Requirement 7 + +**User Story:** As a downstream consumer, I want packages to include version information, so that I can verify I'm using the correct release + +#### Acceptance Criteria + +1. THE Release Workflow SHALL include a VERSION or version.txt file in each package +2. THE Release Workflow SHALL include a README or INSTALL file with basic usage instructions +3. THE Release Workflow SHALL include LICENSE files in each package +4. THE Release Workflow SHALL generate a checksum file (SHA256) for each package +5. THE Release Workflow SHALL upload checksum files as separate release assets diff --git a/.kiro/specs/github-release-automation/tasks.md b/.kiro/specs/github-release-automation/tasks.md new file mode 100644 index 0000000..6398787 --- /dev/null +++ b/.kiro/specs/github-release-automation/tasks.md @@ -0,0 +1,156 @@ +# Implementation Plan + +- [x] 1. Create GitHub Actions release workflow file + - Create `.github/workflows/release.yml` with workflow structure + - Configure workflow to trigger on tags matching `[0-9]+.[0-9]+.[0-9]+` pattern + - Set up workflow permissions for creating releases and uploading assets + - _Requirements: 1.1, 6.5_ + +- [x] 2. Implement validation job + - [x] 2.1 Create validate job that waits for test workflow completion + - Use `lewagon/wait-on-check-action` or similar to wait for test workflow to complete + - Poll test workflow status until it completes (success or failure) + - Fail the validate job if test workflow fails + - Set reasonable timeout (e.g., 60 minutes) to prevent infinite waiting + - _Requirements: 6.1, 6.3_ + + - [x] 2.2 Add quality checks to validate job + - Run `cargo fmt --check` to verify code formatting + - Run `cargo clippy -- -D warnings` to catch linting issues + - _Requirements: 6.4_ + +- [x] 3. Implement build matrix job + - [x] 3.1 Define build matrix with all target platforms + - Configure matrix with 5 targets: x86_64-linux, aarch64-linux, aarch64-macos, x86_64-windows, aarch64-windows + - Map each target to appropriate runner: ubuntu-22.04, ubuntu-22.04-arm, macos-14, windows-2022, windows-11-arm + - Include platform, arch, and extension variables in matrix + - _Requirements: 1.2_ + + - [x] 3.2 Set up Rust toolchain in build job + - Use `actions-rust-lang/setup-rust-toolchain` action + - Configure stable toolchain + - _Requirements: 6.2_ + + - [x] 3.3 Build release binaries + - Run `cargo build --release` to build library and CLI binaries + - Verify all expected files exist in target/release directory + - _Requirements: 1.4, 2.1, 3.1_ + +- [x] 4. Create platform-specific package staging + - [x] 4.1 Implement Linux package staging + - Create directory structure: lib/, include/, bin/ + - Copy libcrc_fast.so and libcrc_fast.a to lib/ + - Copy libcrc_fast.h to include/ + - Copy checksum, arch-check, get-custom-params to bin/ + - Set executable permissions on binaries + - _Requirements: 2.2, 3.2, 3.4, 4.1, 4.4_ + + - [x] 4.2 Implement macOS package staging + - Create directory structure: lib/, include/, bin/ + - Copy libcrc_fast.dylib and libcrc_fast.a to lib/ + - Copy libcrc_fast.h to include/ + - Copy checksum, arch-check, get-custom-params to bin/ + - Set executable permissions on binaries + - _Requirements: 2.3, 3.2, 3.4, 4.2, 4.4_ + + - [x] 4.3 Implement Windows package staging + - Create directory structure: bin/, lib/, include/ + - Copy crc_fast.dll to bin/ + - Copy crc_fast.dll.lib and crc_fast.lib to lib/ + - Copy libcrc_fast.h to include/ + - Copy checksum.exe, arch-check.exe, get-custom-params.exe to bin/ + - _Requirements: 2.4, 3.3, 4.3, 4.5_ + +- [ ] 5. Add package metadata files + - [x] 5.1 Create VERSION file + - Extract version from git tag + - Write version number to VERSION file in package root + - _Requirements: 7.1_ + + - [x] 5.2 Create README.txt file + - Generate platform-specific installation instructions + - Include basic usage information and link to documentation + - Add to package root + - _Requirements: 7.2_ + + - [x] 5.3 Copy license files + - Copy LICENSE-MIT and LICENSE-Apache to package root + - _Requirements: 7.3_ + +- [x] 6. Create compressed packages + - [x] 6.1 Implement tar.gz creation for Linux and macOS + - Use tar command to create compressed archive + - Name package: crc-fast-{version}-{platform}-{arch}.tar.gz + - _Requirements: 5.2, 5.3, 5.5_ + + - [x] 6.2 Implement zip creation for Windows + - Use PowerShell Compress-Archive or 7zip + - Name package: crc-fast-{version}-windows-{arch}.zip + - _Requirements: 5.4, 5.5_ + +- [X] 7. Generate and upload checksums + - [x] 7.1 Generate SHA256 checksums + - Calculate SHA256 hash for each package + - Create .sha256 file with hash and filename + - _Requirements: 7.4_ + + - [x] 7.2 Upload packages and checksums as artifacts + - Use `actions/upload-artifact` to upload package files + - Upload checksum files as separate artifacts + - _Requirements: 7.5_ + +- [X] 8. Implement publish job + - [x] 8.1 Download all build artifacts + - Use `actions/download-artifact` to retrieve all packages and checksums + - Organize files for upload + - _Requirements: 1.5_ + + - [x] 8.2 Check for existing release + - Use GitHub API to check if release exists for the tag + - Get release ID if it exists + - _Requirements: Decision 9_ + + - [x] 8.3 Create or update GitHub release + - If release doesn't exist, create new release with basic information + - If release exists, prepare to update it with assets + - Use `softprops/action-gh-release` or similar action + - _Requirements: 1.5_ + + - [x] 8.4 Upload packages and checksums to release + - Upload all .tar.gz and .zip packages as release assets + - Upload all .sha256 checksum files as release assets + - _Requirements: 1.5, 7.5_ + +- [ ]* 9. Add workflow documentation + - [ ]* 9.1 Create workflow README + - Document how to trigger releases + - Explain manual release creation workflow + - Document package naming conventions + - _Requirements: All_ + + - [ ]* 9.2 Add inline workflow comments + - Comment complex workflow steps + - Explain matrix configuration + - Document platform-specific logic + - _Requirements: All_ + +- [ ]* 10. Test release workflow + - [ ]* 10.1 Test on feature branch + - Create test tag on feature branch + - Verify workflow triggers correctly + - Check that all jobs complete successfully + - _Requirements: All_ + + - [ ]* 10.2 Validate package contents + - Download each platform package + - Extract and verify directory structure + - Check that all files are present + - Verify file permissions on Unix platforms + - _Requirements: 2.1-2.5, 3.1-3.4, 4.1-4.5, 7.1-7.3_ + + - [ ]* 10.3 Test package functionality + - Test linking against dynamic library + - Test linking against static library + - Run each CLI binary to verify functionality + - Verify checksums match package contents + - _Requirements: 2.1-2.5, 3.1-3.4_