Revert "Initial commit with task details" #65
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: Rust CI/CD | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - issue-* | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| workflow_dispatch: | |
| inputs: | |
| release_mode: | |
| description: "Manual release mode" | |
| required: true | |
| type: choice | |
| default: "instant" | |
| options: | |
| - instant | |
| - changelog-pr | |
| bump_type: | |
| description: "Version bump type" | |
| required: true | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| description: | |
| description: "Release description (optional)" | |
| required: false | |
| type: string | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref == 'refs/heads/main' }} | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUSTFLAGS: -Dwarnings | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || secrets.CARGO_TOKEN }} | |
| CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }} | |
| jobs: | |
| detect-changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'workflow_dispatch' | |
| outputs: | |
| rs-changed: ${{ steps.changes.outputs.rs-changed }} | |
| toml-changed: ${{ steps.changes.outputs.toml-changed }} | |
| docs-changed: ${{ steps.changes.outputs.docs-changed }} | |
| workflow-changed: ${{ steps.changes.outputs.workflow-changed }} | |
| any-rust-code-changed: ${{ steps.changes.outputs.any-rust-code-changed }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24.x" | |
| - name: Detect changes | |
| id: changes | |
| env: | |
| GITHUB_EVENT_NAME: ${{ github.event_name }} | |
| GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| GITHUB_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: node scripts/shared/detect-code-changes.mjs | |
| rust-changelog-check: | |
| name: Rust Changelog Check | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| if: github.event_name == 'pull_request' && needs.detect-changes.outputs.any-rust-code-changed == 'true' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for changelog fragments | |
| working-directory: rust | |
| run: | | |
| FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l) | |
| CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) | |
| SOURCE_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^rust/(src/|tests/|Cargo\.toml)" | wc -l) | |
| if [ "$SOURCE_CHANGED" -gt 0 ] && [ "$FRAGMENTS" -eq 0 ]; then | |
| echo "::warning::No changelog fragment found. Please add a changelog entry in rust/changelog.d/" | |
| fi | |
| echo "Found $FRAGMENTS changelog fragment(s)" | |
| rust-lint: | |
| name: Rust Lint | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| if: | | |
| always() && !cancelled() && ( | |
| github.event_name == 'push' || | |
| github.event_name == 'workflow_dispatch' || | |
| needs.detect-changes.outputs.rs-changed == 'true' || | |
| needs.detect-changes.outputs.toml-changed == 'true' || | |
| needs.detect-changes.outputs.docs-changed == 'true' || | |
| needs.detect-changes.outputs.workflow-changed == 'true' | |
| ) | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo registry | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| rust/target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Check formatting | |
| working-directory: rust | |
| run: cargo fmt --all -- --check | |
| - name: Run Clippy | |
| working-directory: rust | |
| run: cargo clippy --all-targets --all-features | |
| - name: Check file size limit | |
| working-directory: rust | |
| run: node ../scripts/rust/check-file-size.mjs | |
| rust-test: | |
| name: Rust Test (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| needs: [detect-changes, rust-changelog-check] | |
| if: | | |
| always() && !cancelled() && | |
| (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.rust-changelog-check.result == 'success' || needs.rust-changelog-check.result == 'skipped') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| rust/target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Run tests | |
| working-directory: rust | |
| run: cargo test --all-features --verbose | |
| - name: Run doc tests | |
| working-directory: rust | |
| run: cargo test --doc --verbose | |
| rust-build: | |
| name: Rust Build | |
| runs-on: ubuntu-latest | |
| needs: [rust-lint, rust-test] | |
| if: always() && !cancelled() && needs.rust-lint.result == 'success' && needs.rust-test.result == 'success' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| rust/target | |
| key: ${{ runner.os }}-cargo-build-${{ hashFiles('rust/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-build- | |
| - name: Build release | |
| working-directory: rust | |
| run: cargo build --release --verbose | |
| - name: Check package | |
| working-directory: rust | |
| run: cargo package --list --allow-dirty | |
| rust-release: | |
| name: Rust Release | |
| needs: [rust-lint, rust-test, rust-build] | |
| if: | | |
| always() && !cancelled() && | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| needs.rust-build.result == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24.x" | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Determine bump type from changelog fragments | |
| id: bump_type | |
| working-directory: rust | |
| run: node ../scripts/rust/get-bump-type.mjs | |
| - name: Check if version already released or no fragments | |
| id: check | |
| working-directory: rust | |
| env: | |
| HAS_FRAGMENTS: ${{ steps.bump_type.outputs.has_fragments }} | |
| run: node ../scripts/rust/check-release-needed.mjs | |
| - name: Collect changelog and bump version | |
| id: version | |
| if: steps.check.outputs.should_release == 'true' && steps.check.outputs.skip_bump != 'true' | |
| working-directory: rust | |
| run: node ../scripts/rust/version-and-commit.mjs --bump-type "${{ steps.bump_type.outputs.bump_type }}" | |
| - name: Get current version | |
| id: current_version | |
| if: steps.check.outputs.should_release == 'true' | |
| working-directory: rust | |
| run: | | |
| CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml) | |
| echo "version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release | |
| if: steps.check.outputs.should_release == 'true' | |
| working-directory: rust | |
| run: cargo build --release | |
| - name: Publish to Crates.io | |
| if: steps.check.outputs.should_release == 'true' | |
| id: publish-crate | |
| working-directory: rust | |
| run: | | |
| PACKAGE_NAME=$(grep '^name = ' Cargo.toml | head -1 | sed 's/name = "\(.*\)"/\1/') | |
| PACKAGE_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') | |
| TOKEN="${CARGO_REGISTRY_TOKEN:-$CARGO_TOKEN}" | |
| echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION" | |
| set -o pipefail | |
| set +e | |
| if [ -n "$TOKEN" ]; then | |
| cargo publish --token "$TOKEN" --allow-dirty 2>&1 | tee publish_output.txt | |
| else | |
| echo "::warning::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set" | |
| cargo publish --allow-dirty 2>&1 | tee publish_output.txt | |
| fi | |
| PUBLISH_EXIT_CODE=$? | |
| set -e | |
| if [ $PUBLISH_EXIT_CODE -eq 0 ]; then | |
| echo "Successfully published $PACKAGE_NAME@$PACKAGE_VERSION to crates.io" | |
| echo "publish_result=success" >> "$GITHUB_OUTPUT" | |
| elif grep -q "already uploaded" publish_output.txt || grep -q "already exists" publish_output.txt; then | |
| echo "Version $PACKAGE_VERSION already exists on crates.io" | |
| echo "publish_result=already_exists" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| else | |
| echo "Failed to publish for unknown reason" | |
| cat publish_output.txt | |
| echo "publish_result=failed" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| fi | |
| - name: Create GitHub Release | |
| if: steps.publish-crate.outputs.publish_result == 'success' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| working-directory: rust | |
| run: | | |
| PACKAGE_NAME=$(grep '^name = ' Cargo.toml | head -1 | sed 's/name = "\(.*\)"/\1/') | |
| RELEASE_VERSION="${{ steps.version.outputs.new_version }}" | |
| if [ -z "$RELEASE_VERSION" ]; then | |
| RELEASE_VERSION="${{ steps.current_version.outputs.version }}" | |
| fi | |
| node ../scripts/rust/create-github-release.mjs \ | |
| --release-version "$RELEASE_VERSION" \ | |
| --repository "${{ github.repository }}" \ | |
| --crates-io-url "https://crates.io/crates/$PACKAGE_NAME" | |
| manual-release: | |
| name: Rust Instant Release | |
| needs: [rust-lint, rust-test, rust-build] | |
| if: | | |
| always() && !cancelled() && | |
| github.event_name == 'workflow_dispatch' && | |
| github.event.inputs.release_mode == 'instant' && | |
| needs.rust-build.result == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24.x" | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Version and commit | |
| id: version | |
| working-directory: rust | |
| run: node ../scripts/rust/version-and-commit.mjs --bump-type "${{ github.event.inputs.bump_type }}" --description "${{ github.event.inputs.description }}" | |
| - name: Build release | |
| if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true' | |
| working-directory: rust | |
| run: cargo build --release | |
| - name: Publish to Crates.io | |
| if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true' | |
| id: publish-crate | |
| working-directory: rust | |
| run: | | |
| PACKAGE_NAME=$(grep '^name = ' Cargo.toml | head -1 | sed 's/name = "\(.*\)"/\1/') | |
| PACKAGE_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') | |
| TOKEN="${CARGO_REGISTRY_TOKEN:-$CARGO_TOKEN}" | |
| echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION" | |
| set -o pipefail | |
| set +e | |
| if [ -n "$TOKEN" ]; then | |
| cargo publish --token "$TOKEN" --allow-dirty 2>&1 | tee publish_output.txt | |
| else | |
| echo "::warning::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set" | |
| cargo publish --allow-dirty 2>&1 | tee publish_output.txt | |
| fi | |
| PUBLISH_EXIT_CODE=$? | |
| set -e | |
| if [ $PUBLISH_EXIT_CODE -eq 0 ]; then | |
| echo "Successfully published $PACKAGE_NAME@$PACKAGE_VERSION to crates.io" | |
| echo "publish_result=success" >> "$GITHUB_OUTPUT" | |
| elif grep -q "already uploaded" publish_output.txt || grep -q "already exists" publish_output.txt; then | |
| echo "Version $PACKAGE_VERSION already exists on crates.io" | |
| echo "publish_result=already_exists" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| else | |
| echo "Failed to publish for unknown reason" | |
| cat publish_output.txt | |
| echo "publish_result=failed" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| fi | |
| - name: Create GitHub Release | |
| if: steps.publish-crate.outputs.publish_result == 'success' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| working-directory: rust | |
| run: | | |
| PACKAGE_NAME=$(grep '^name = ' Cargo.toml | head -1 | sed 's/name = "\(.*\)"/\1/') | |
| node ../scripts/rust/create-github-release.mjs \ | |
| --release-version "${{ steps.version.outputs.new_version }}" \ | |
| --repository "${{ github.repository }}" \ | |
| --crates-io-url "https://crates.io/crates/$PACKAGE_NAME" | |
| changelog-pr: | |
| name: Create Rust Changelog PR | |
| if: github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'changelog-pr' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Create changelog fragment | |
| working-directory: rust | |
| run: | | |
| BUMP_TYPE="${{ github.event.inputs.bump_type }}" | |
| DESCRIPTION="${{ github.event.inputs.description }}" | |
| TIMESTAMP=$(date +%Y%m%d%H%M%S) | |
| FRAGMENT_FILE="changelog.d/${TIMESTAMP}-manual-${BUMP_TYPE}.md" | |
| case "$BUMP_TYPE" in | |
| major) | |
| CATEGORY="### Breaking Changes" | |
| ;; | |
| minor) | |
| CATEGORY="### Added" | |
| ;; | |
| patch) | |
| CATEGORY="### Fixed" | |
| ;; | |
| esac | |
| mkdir -p changelog.d | |
| cat > "$FRAGMENT_FILE" << EOF | |
| --- | |
| bump: $BUMP_TYPE | |
| --- | |
| $CATEGORY | |
| EOF | |
| if [ -n "$DESCRIPTION" ]; then | |
| echo "- ${DESCRIPTION}" >> "$FRAGMENT_FILE" | |
| else | |
| echo "- Manual ${BUMP_TYPE} release" >> "$FRAGMENT_FILE" | |
| fi | |
| echo "Created changelog fragment: $FRAGMENT_FILE" | |
| - name: Create Pull Request | |
| uses: peter-evans/create-pull-request@v8 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| commit-message: "chore: add changelog for manual ${{ github.event.inputs.bump_type }} release" | |
| branch: changelog-manual-release-${{ github.run_id }} | |
| delete-branch: true | |
| title: "chore: manual ${{ github.event.inputs.bump_type }} release" | |
| body: | | |
| ## Manual Release Request | |
| This PR was created by a manual workflow trigger to prepare a **${{ github.event.inputs.bump_type }}** Rust release. | |
| ### Release Details | |
| - **Description:** ${{ github.event.inputs.description || 'Manual release' }} | |
| - **Triggered by:** @${{ github.actor }} | |
| ### Next Steps | |
| 1. Review the changelog fragment in this PR | |
| 2. Merge this PR to main | |
| 3. The automated Rust release workflow will publish to crates.io and create a GitHub release |