diff --git a/.commitlintrc.yml b/.commitlintrc.yml new file mode 100644 index 0000000..9d61732 --- /dev/null +++ b/.commitlintrc.yml @@ -0,0 +1,80 @@ +# Commitlint configuration for conventional commits +# Based on: https://www.conventionalcommits.org/ + +extends: + - '@commitlint/config-conventional' + +rules: + # Type enum - allowed commit types + type-enum: + - 2 # Level: error + - always + - # Allowed types: + - feat # New feature + - fix # Bug fix + - docs # Documentation only changes + - style # Code style changes (formatting, missing semi-colons, etc) + - refactor # Code refactoring (neither fixes a bug nor adds a feature) + - perf # Performance improvements + - test # Adding or updating tests + - build # Changes to build system or dependencies + - ci # CI/CD configuration changes + - chore # Other changes that don't modify src or test files + - revert # Revert a previous commit + + # Type case should be lowercase + type-case: + - 2 + - always + - lower-case + + # Type must not be empty + type-empty: + - 2 + - never + + # Scope case should be lowercase + scope-case: + - 2 + - always + - lower-case + + # Subject must not be empty + subject-empty: + - 2 + - never + + # Subject must not end with a period + subject-full-stop: + - 2 + - never + - '.' + + # Disable subject-case to allow uppercase abbreviations (PR, API, CLI, etc.) + subject-case: + - 0 + + # Header (first line) max length + header-max-length: + - 2 + - always + - 72 + + # Body should have a blank line before it + body-leading-blank: + - 1 # Warning level + - always + + # Footer should have a blank line before it + footer-leading-blank: + - 1 # Warning level + - always + + # Body max line length + body-max-line-length: + - 1 # Warning level + - always + - 100 + +# Help URL shown in error messages +helpUrl: 'https://www.conventionalcommits.org/' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..997217f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,176 @@ +name: Build & Test + +on: + workflow_dispatch: + push: + branches: [ main ] + paths-ignore: + - '**.md' + - '.github/ISSUE_TEMPLATE/**' + - '.github/workflows/contributors.yml' + - '.idea/**' + - '.claude/**' + - '.gitignore' + - '.commitlintrc.yml' + - 'LICENSE' + pull_request: + branches: [ main ] + paths-ignore: + - '**.md' + - '.github/ISSUE_TEMPLATE/**' + - '.github/workflows/contributors.yml' + - '.idea/**' + - '.claude/**' + - '.gitignore' + - '.commitlintrc.yml' + - 'LICENSE' + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Check formatting + run: cargo fmt --all -- --check + + clippy: + name: Clippy (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + test: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run tests + run: cargo test --verbose + + - name: Generate test summary + if: always() + shell: bash + run: | + echo "## Test Results - ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ Tests completed" >> $GITHUB_STEP_SUMMARY + + build: + name: Build ${{ matrix.platform }} + needs: [fmt, clippy, test] + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + platform: linux-amd64 + binary: rnr + + - os: macos-latest + target: x86_64-apple-darwin + platform: macos-amd64 + binary: rnr + + - os: macos-latest + target: aarch64-apple-darwin + platform: macos-arm64 + binary: rnr + + - os: windows-latest + target: x86_64-pc-windows-msvc + platform: windows-amd64 + binary: rnr.exe + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.target }}-cargo- + + - name: Build release binary + run: cargo build --release --target ${{ matrix.target }} + + - name: Verify build + if: matrix.target != 'aarch64-apple-darwin' + shell: bash + run: | + ./target/${{ matrix.target }}/release/${{ matrix.binary }} --version + ./target/${{ matrix.target }}/release/${{ matrix.binary }} --help + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: rnr-${{ matrix.platform }} + path: target/${{ matrix.target }}/release/${{ matrix.binary }} + retention-days: 7 diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml new file mode 100644 index 0000000..e765427 --- /dev/null +++ b/.github/workflows/commit-lint.yml @@ -0,0 +1,59 @@ +name: Lint Commit Messages + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + pull-requests: read + +jobs: + lint-pr-title: + name: Lint PR Title + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install commitlint + run: | + npm install --save-dev @commitlint/cli@18.4.3 @commitlint/config-conventional@18.4.3 + + - name: Validate PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + echo "Validating PR title: $PR_TITLE" + echo "$PR_TITLE" | npx commitlint --verbose + + commitlint: + name: Lint Commit Messages + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install commitlint + run: | + npm install --save-dev @commitlint/cli@18.4.3 @commitlint/config-conventional@18.4.3 + + - name: Validate PR commits + run: | + # Get the base branch (usually main) + BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD) + + # Lint all commits in the PR + npx commitlint --from $BASE_SHA --to HEAD --verbose diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml new file mode 100644 index 0000000..3d381f2 --- /dev/null +++ b/.github/workflows/contributors.yml @@ -0,0 +1,11 @@ +name: Update Contributors + +on: + schedule: + - cron: '0 0 * * *' # Run daily at midnight UTC + workflow_dispatch: # Allow manual trigger + +jobs: + contributors: + uses: CodingWithCalvin/.github/.github/workflows/contributors.yml@main + secrets: inherit diff --git a/.github/workflows/preview-changelog.yml b/.github/workflows/preview-changelog.yml new file mode 100644 index 0000000..ca8d17c --- /dev/null +++ b/.github/workflows/preview-changelog.yml @@ -0,0 +1,12 @@ +name: Preview Changelog + +run-name: Preview release notes for next release + +on: + workflow_dispatch: + +jobs: + preview: + name: Preview + uses: CodingWithCalvin/.github/.github/workflows/generate-changelog.yml@main + secrets: inherit