From 45aaaa5c9618a7dd6df0cc40b6ef2912d46f251c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 01:26:26 +0000 Subject: [PATCH 1/2] Initial plan From cd98c9e46ee88218033053e491cb80f121177926 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 01:30:27 +0000 Subject: [PATCH 2/2] ci: add automated PR review workflow, PR template, and script header Co-authored-by: LucienSong <44640337+LucienSong@users.noreply.github.com> Agent-Logs-Url: https://github.com/ShellDAO/shell-chain/sessions/e0ed7d1b-8abd-4175-92ee-12c05aeffdd7 --- .github/pull_request_template.md | 32 +++ .github/workflows/pr-review.yml | 412 +++++++++++++++++++++++++++++++ scripts/pr-review-checks.sh | 11 + 3 files changed, 455 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/pr-review.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..390fee1 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,32 @@ +## Overview + + + +## Type of Change + + + +- [ ] Bug Fix +- [ ] New Feature +- [ ] Documentation Update +- [ ] Refactor +- [ ] Other (please specify): + +## Checklist + + + +- [ ] Code follows the project's style guidelines (`cargo fmt` passes) +- [ ] No Clippy warnings (`cargo clippy -- -D warnings` passes) +- [ ] Unit tests have been added or modified (`cargo test` passes) +- [ ] Documentation has been updated (`cargo doc --no-deps` passes) +- [ ] No sensitive information (keys, secrets, PII) is exposed +- [ ] Commit messages follow the conventional commit format (e.g., `feat:`, `fix:`, `chore:`) + +## Related Issues + + + +## Additional Notes + + diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml new file mode 100644 index 0000000..ce4815d --- /dev/null +++ b/.github/workflows/pr-review.yml @@ -0,0 +1,412 @@ +# PR Review Workflow +# Automatically triggered when a pull request is opened, updated, or reopened +# targeting the main branch. Runs the same checks as scripts/pr-review-checks.sh +# plus additional PR-quality checks (commit messages, description validation). +# +# See also: +# .github/REVIEW_CHECKLIST.md — full review checklist reference +# .github/RUST_CRYPTO_REVIEW.md — Rust & cryptography guidelines +# .github/PR_REVIEW_TEMPLATE.md — PR description guidelines + +name: PR Review + +on: + pull_request: + branches: + - main + types: + - opened + - synchronize + - reopened + +# Cancel in-progress runs for the same PR when a new commit is pushed. +concurrency: + group: pr-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-D warnings" + +# Limit the default GITHUB_TOKEN permissions for all jobs. +# Individual jobs that need no repository access override with permissions: {}. +permissions: + contents: read + +# --------------------------------------------------------------------------- +# Helper: detect whether a Cargo workspace / package is present. +# All Rust jobs use `if: steps.cargo_check.outputs.exists == 'true'` so that +# the workflow still passes on PRs that only touch docs or scripts. +# --------------------------------------------------------------------------- +jobs: + # ------------------------------------------------------------------------- + # 1. Formatting check + # ------------------------------------------------------------------------- + format: + name: Formatting Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping Rust formatting check." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Run cargo fmt + if: steps.cargo_check.outputs.exists == 'true' + run: cargo fmt -- --check + + # ------------------------------------------------------------------------- + # 2. Lint check + # ------------------------------------------------------------------------- + lint: + name: Lint Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping Clippy lint check." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Run cargo clippy + if: steps.cargo_check.outputs.exists == 'true' + run: cargo clippy -- -D warnings + + # ------------------------------------------------------------------------- + # 3. Tests + # ------------------------------------------------------------------------- + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping cargo test." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Run cargo test + if: steps.cargo_check.outputs.exists == 'true' + run: cargo test + + # ------------------------------------------------------------------------- + # 4. Documentation build + # ------------------------------------------------------------------------- + docs: + name: Documentation Build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping cargo doc." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Build documentation + if: steps.cargo_check.outputs.exists == 'true' + run: cargo doc --no-deps + + # ------------------------------------------------------------------------- + # 5. Security audit + # ------------------------------------------------------------------------- + security-audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping cargo audit." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Install cargo-audit + if: steps.cargo_check.outputs.exists == 'true' + run: cargo install cargo-audit --locked + + - name: Run cargo audit + if: steps.cargo_check.outputs.exists == 'true' + run: cargo audit + + # ------------------------------------------------------------------------- + # 6. Build verification + # ------------------------------------------------------------------------- + build: + name: Build Verification + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for Cargo.toml + id: cargo_check + run: | + if [ -f "Cargo.toml" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::No Cargo.toml found — skipping release build." + fi + + - name: Install Rust stable toolchain + if: steps.cargo_check.outputs.exists == 'true' + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo registry + if: steps.cargo_check.outputs.exists == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Build release + if: steps.cargo_check.outputs.exists == 'true' + run: cargo build --release + + # ------------------------------------------------------------------------- + # 7. Commit message check + # Validates that every commit in the PR follows the Conventional Commits + # specification, as required by REVIEW_CHECKLIST.md §7. + # Accepted types: feat, fix, chore, docs, refactor, test, ci, style, perf, + # build, revert + # ------------------------------------------------------------------------- + commit-message-check: + name: Commit Message Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + # Fetch enough history to inspect all commits in the PR. + fetch-depth: 0 + + - name: Validate conventional commit messages + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + PATTERN="^(feat|fix|chore|docs|refactor|test|ci|style|perf|build|revert)(\(.+\))?(!)?: *.+" + FAILED=0 + while IFS= read -r msg; do + # Skip merge commits + if echo "$msg" | grep -qE "^Merge "; then + continue + fi + if ! echo "$msg" | grep -qE "$PATTERN"; then + echo "::error::Commit message does not follow conventional format: \"$msg\"" + FAILED=1 + fi + done < <(git log --format="%s" "${BASE_SHA}..${HEAD_SHA}") + if [ "$FAILED" -ne 0 ]; then + echo "" + echo "Commit messages must follow the Conventional Commits format:" + echo " [optional scope]: " + echo "" + echo "Accepted types: feat, fix, chore, docs, refactor, test, ci, style, perf, build, revert" + echo "Examples:" + echo " feat: add new transaction validation" + echo " fix(crypto): correct key derivation edge case" + echo " chore: update dependencies" + exit 1 + fi + echo "All commit messages follow the conventional commit format." + + # ------------------------------------------------------------------------- + # 8. PR description check + # Ensures the PR body is not empty and contains an Overview section, + # consistent with pull_request_template.md. + # ------------------------------------------------------------------------- + pr-description-check: + name: PR Description Check + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Validate PR description + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: | + MIN_LENGTH=50 + + if [ -z "$PR_BODY" ]; then + echo "::error::PR description is empty. Please fill in the pull request template." + exit 1 + fi + + BODY_LENGTH=${#PR_BODY} + if [ "$BODY_LENGTH" -lt "$MIN_LENGTH" ]; then + echo "::error::PR description is too short (${BODY_LENGTH} chars). Please provide a meaningful description (minimum ${MIN_LENGTH} characters)." + exit 1 + fi + + if ! echo "$PR_BODY" | grep -qi "overview\|description\|summary\|changes"; then + echo "::error::PR description must contain an overview or description of the changes. Please fill in the pull request template." + exit 1 + fi + + echo "PR description looks good (${BODY_LENGTH} characters)." + + # ------------------------------------------------------------------------- + # 9. Summary (branch protection gate) + # This job depends on every other job. Branch protection rules can require + # only this single job instead of listing each check individually. + # ------------------------------------------------------------------------- + summary: + name: PR Review Summary + runs-on: ubuntu-latest + permissions: {} + needs: + - format + - lint + - test + - docs + - security-audit + - build + - commit-message-check + - pr-description-check + if: always() + steps: + - name: Check all jobs passed + run: | + results='${{ toJSON(needs) }}' + failed=0 + + for job in format lint test docs security_audit build commit_message_check pr_description_check; do + result=$(echo "$results" | python3 -c " + import sys, json + data = json.load(sys.stdin) + key = '$job' + if key in data: + print(data[key]['result']) + else: + print('unknown') + ") + echo "Job '$job': $result" + if [ "$result" = "failure" ] || [ "$result" = "cancelled" ]; then + failed=1 + fi + done + + if [ "$failed" -ne 0 ]; then + echo "::error::One or more PR review checks failed. Please fix the issues above." + exit 1 + fi + + echo "All PR review checks passed successfully!" diff --git a/scripts/pr-review-checks.sh b/scripts/pr-review-checks.sh index 2580076..0de020e 100644 --- a/scripts/pr-review-checks.sh +++ b/scripts/pr-review-checks.sh @@ -1,4 +1,15 @@ #!/bin/bash +# +# pr-review-checks.sh — Local pre-push PR review checks +# +# This script runs the same checks that are automatically executed by the +# GitHub Actions workflow at .github/workflows/pr-review.yml whenever a pull +# request is opened or updated. You can run it locally before pushing to catch +# issues early: +# +# bash scripts/pr-review-checks.sh +# +# Requirements: cargo, cargo-audit (install with `cargo install cargo-audit`) set -e