diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 554a475..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Build - -on: - workflow_dispatch: - push: - branches: [main] - pull_request: - -jobs: - build: - name: Build - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - include: - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - - os: macos-latest - target: x86_64-apple-darwin - - os: windows-latest - target: x86_64-pc-windows-msvc - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - target: ${{ matrix.target }} - override: true - components: clippy, rustfmt - - - name: Check formatting - run: cargo fmt -- --check - - - name: Run clippy - run: cargo clippy -- -D warnings - - - name: Build - run: cargo build --target ${{ matrix.target }} - - - name: Check no-default-features - run: cargo check --no-default-features --workspace --target ${{ matrix.target }} - - - name: Run tests - run: cargo test --target ${{ matrix.target }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2193629 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy --workspace --all-features -- -D warnings + + build: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo build --workspace --all-features + - run: cargo check --no-default-features --workspace + - run: cargo test --workspace --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e29a525..b129cff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,148 +3,174 @@ name: Release on: push: tags: - - 'v*' + - "v*" workflow_dispatch: inputs: - version: - description: 'Version to use (e.g. v1.0.0)' + tag: + description: "Tag to release (e.g., v1.0.0)" required: true - default: 'test-release' -# Add permissions at workflow level permissions: contents: write +env: + CARGO_TERM_COLOR: always + RELEASE_TAG: ${{ inputs.tag || github.ref_name }} + jobs: - create-release: - name: Create Release + changelog: + name: Generate Changelog runs-on: ubuntu-latest - # You can also set permissions at the job level if needed - # permissions: - # contents: write outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} + changelog: ${{ steps.changelog.outputs.changelog }} steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Setup Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - - name: Install git-cliff + - name: Validate tag format run: | - cargo install git-cliff --force - - - name: Generate Changelog - run: | - # Debug: Show current state - echo "Current ref: ${{ github.ref_name }}" - echo "Input version: ${{ github.event.inputs.version }}" - echo "All tags:" - git tag --sort=-version:refname | head -10 - - # Generate changelog from the current tag to the previous version tag - CURRENT_TAG="${{ github.event.inputs.version || github.ref_name }}" - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep "^v" | head -2 | tail -1) - - echo "Current tag: $CURRENT_TAG" - echo "Previous tag: $PREVIOUS_TAG" - - if [ -n "$PREVIOUS_TAG" ] && [ "$PREVIOUS_TAG" != "$CURRENT_TAG" ]; then - echo "Generating changelog for range: $PREVIOUS_TAG..$CURRENT_TAG" - git-cliff --tag "$CURRENT_TAG" "$PREVIOUS_TAG..$CURRENT_TAG" --output CHANGELOG.md - else - echo "Generating latest changelog for tag: $CURRENT_TAG" - git-cliff --tag "$CURRENT_TAG" --latest --output CHANGELOG.md + if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then + echo "::error::Tag '${RELEASE_TAG}' does not match expected format vX.Y.Z" + exit 1 fi - - echo "Generated changelog:" - cat CHANGELOG.md - - - name: Create Release - id: create_release - uses: softprops/action-gh-release@v1 - with: - name: "wrkflw ${{ github.event.inputs.version || github.ref_name }}" - body_path: CHANGELOG.md - draft: false - prerelease: false - tag_name: ${{ github.event.inputs.version || github.ref_name }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Validate tag exists + run: | + if ! git tag -l "${RELEASE_TAG}" | grep -q .; then + echo "::error::Tag ${RELEASE_TAG} does not exist as a git tag" + exit 1 + fi + - name: Generate changelog + id: changelog + run: | + # Get the previous tag + all_tags=$(git tag --sort=-v:refname | grep "^v") + if ! echo "$all_tags" | grep -F -x -q "${RELEASE_TAG}"; then + echo "::warning::Tag ${RELEASE_TAG} not found in tag list; generating changelog from all commits" + fi + prev_tag=$(echo "$all_tags" | grep -F -x -A1 "${RELEASE_TAG}" | tail -1) + [ "$prev_tag" = "${RELEASE_TAG}" ] && prev_tag="" # No previous tag found + if [ -z "$prev_tag" ]; then + # First release: use all commits + changelog=$(git log --pretty=format:"- %s (%h)" --no-merges --max-count=100) + else + changelog=$(git log "${prev_tag}..${RELEASE_TAG}" --pretty=format:"- %s (%h)" --no-merges) + fi + # Escape for GitHub Actions output + { + echo "changelog<> "$GITHUB_OUTPUT" - build-release: - name: Build Release - needs: [create-release] + build: + name: Build (${{ matrix.target }}) runs-on: ${{ matrix.os }} - # You can also set permissions at the job level if needed - # permissions: - # contents: write strategy: + fail-fast: false matrix: include: - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - artifact_name: wrkflw - asset_name: wrkflw-${{ github.event.inputs.version || github.ref_name }}-linux-x86_64 - - os: macos-latest - target: x86_64-apple-darwin - artifact_name: wrkflw - asset_name: wrkflw-${{ github.event.inputs.version || github.ref_name }}-macos-x86_64 - - os: macos-latest - target: aarch64-apple-darwin - artifact_name: wrkflw - asset_name: wrkflw-${{ github.event.inputs.version || github.ref_name }}-macos-arm64 - + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-apple-darwin + os: macos-latest + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-pc-windows-msvc + os: windows-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: stable - target: ${{ matrix.target }} - override: true - - - name: Build Release Binary - uses: actions-rs/cargo@v1 - with: - command: build - args: --release --target ${{ matrix.target }} - - - name: Compress Release Binary (Unix) + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + + - 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 + + - name: Install musl tools + if: matrix.target == 'x86_64-unknown-linux-musl' + run: | + sudo apt-get update + sudo apt-get install -y musl-tools + + - name: Configure linker for aarch64-linux-gnu + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + mkdir -p .cargo + echo '[target.aarch64-unknown-linux-gnu]' > .cargo/config.toml + echo 'linker = "aarch64-linux-gnu-gcc"' >> .cargo/config.toml + + - name: Build + run: cargo build --release --locked --target ${{ matrix.target }} + + - name: Package (Unix) if: runner.os != 'Windows' - run: | - mkdir -p compressed - cp target/${{ matrix.target }}/release/${{ matrix.artifact_name }} compressed/ - cd compressed - tar czvf ${{ matrix.asset_name }}.tar.gz ${{ matrix.artifact_name }} - echo "ASSET=${{ matrix.asset_name }}.tar.gz" >> $GITHUB_ENV - echo "ASSET_PATH=compressed/${{ matrix.asset_name }}.tar.gz" >> $GITHUB_ENV - - - name: Compress Release Binary (Windows) + run: tar czf wrkflw-${{ matrix.target }}.tar.gz -C target/${{ matrix.target }}/release wrkflw + + - name: Package (Windows) if: runner.os == 'Windows' - run: | - mkdir -p compressed - copy target\${{ matrix.target }}\release\${{ matrix.artifact_name }} compressed\ - cd compressed - 7z a ${{ matrix.asset_name }}.zip ${{ matrix.artifact_name }} - echo "ASSET=${{ matrix.asset_name }}.zip" >> $env:GITHUB_ENV - echo "ASSET_PATH=compressed\${{ matrix.asset_name }}.zip" >> $env:GITHUB_ENV shell: pwsh - - - name: Upload Release Asset - uses: softprops/action-gh-release@v1 + run: | + Compress-Archive -Path "target/${{ matrix.target }}/release/wrkflw.exe" -DestinationPath "wrkflw-${{ matrix.target }}.zip" + + - name: Upload artifact + uses: actions/upload-artifact@v4 with: - files: ${{ env.ASSET_PATH }} - tag_name: ${{ github.event.inputs.version || github.ref_name }} + name: wrkflw-${{ matrix.target }} + path: wrkflw-${{ matrix.target }}.* + + publish: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: [build, changelog] + # Only publish on tag push — manual dispatch uses a branch ref, intentionally excluded + # because publishing to crates.io is irreversible + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Cache cargo-workspaces + id: cache-cargo-ws + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/cargo-workspaces + key: cargo-workspaces-0.4.2 + - name: Install cargo-workspaces + if: steps.cache-cargo-ws.outputs.cache-hit != 'true' + run: cargo install cargo-workspaces@0.4.2 + - name: Publish all crates + run: cargo workspaces publish --from-git --yes env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: [build, changelog] + steps: + - uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.RELEASE_TAG }} + name: wrkflw ${{ env.RELEASE_TAG }} + body: | + ## What's Changed + ${{ needs.changelog.outputs.changelog }} + files: artifacts/* + generate_release_notes: false