chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [develop, master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
# ─── Fast gates (fail early, save CI minutes) ───
|
||||
|
||||
check-test-presence:
|
||||
name: test presence
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 50
|
||||
- name: Check filter modules have tests
|
||||
run: |
|
||||
git fetch origin "${{ github.base_ref }}" --depth=1 || true
|
||||
bash scripts/check-test-presence.sh "origin/${{ github.base_ref }}"
|
||||
|
||||
fmt:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
name: clippy
|
||||
needs: fmt
|
||||
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 --all-targets
|
||||
|
||||
# ─── Parallel gates (all need code to compile) ───
|
||||
|
||||
test:
|
||||
name: test (${{ matrix.os }})
|
||||
needs: clippy
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo test --all
|
||||
|
||||
security:
|
||||
name: Security Scan
|
||||
needs: clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Install cargo-audit
|
||||
run: cargo install cargo-audit
|
||||
|
||||
- name: Cargo Audit (CVE check)
|
||||
run: |
|
||||
echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Dependency Vulnerabilities" >> $GITHUB_STEP_SUMMARY
|
||||
if cargo audit 2>&1 | tee audit.log; then
|
||||
echo "No known vulnerabilities detected" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Vulnerabilities found:" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
cat audit.log >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "::warning::Dependency vulnerabilities detected - review required"
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Critical files check
|
||||
run: |
|
||||
echo "### Critical Files Modified" >> $GITHUB_STEP_SUMMARY
|
||||
CRITICAL=$(git diff --name-only origin/master...HEAD | grep -E "(runner|summary|tracking|init|pnpm_cmd|container)\.rs|Cargo\.toml|workflows/.*\.yml" || true)
|
||||
if [ -n "$CRITICAL" ]; then
|
||||
echo "**HIGH RISK**: The following critical files were modified:" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$CRITICAL" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Required Actions:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Manual security review by 2 maintainers" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Verify no shell injection vectors" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Check input validation remains intact" >> $GITHUB_STEP_SUMMARY
|
||||
echo "::warning::Critical RTK files modified - enhanced review required"
|
||||
else
|
||||
echo "No critical files modified" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Dangerous patterns scan
|
||||
run: |
|
||||
echo "### Dangerous Code Patterns" >> $GITHUB_STEP_SUMMARY
|
||||
PATTERNS=$(git diff origin/master...HEAD | grep -E "Command::new\(\"sh\"|Command::new\(\"bash\"|\.env\(\"LD_PRELOAD|\.env\(\"PATH|reqwest::|std::net::|TcpStream|UdpSocket|unsafe \{|\.unwrap\(\) |panic!\(|todo!\(|unimplemented!\(" || true)
|
||||
if [ -n "$PATTERNS" ]; then
|
||||
echo "**Potentially dangerous patterns detected:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$PATTERNS" | head -30 >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Security Concerns:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "$PATTERNS" | grep -q "Command::new" && echo "- Shell command execution detected" >> $GITHUB_STEP_SUMMARY || true
|
||||
echo "$PATTERNS" | grep -q "\.env\(\"" && echo "- Environment variable manipulation" >> $GITHUB_STEP_SUMMARY || true
|
||||
echo "$PATTERNS" | grep -q "reqwest::\|std::net::\|TcpStream\|UdpSocket" && echo "- Network operations added" >> $GITHUB_STEP_SUMMARY || true
|
||||
echo "$PATTERNS" | grep -q "unsafe" && echo "- Unsafe code blocks" >> $GITHUB_STEP_SUMMARY || true
|
||||
echo "$PATTERNS" | grep -q "\.unwrap\(\)\|panic!\(" && echo "- Panic-inducing code" >> $GITHUB_STEP_SUMMARY || true
|
||||
echo "::warning::Dangerous code patterns detected - manual review required"
|
||||
else
|
||||
echo "No dangerous patterns detected" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: New dependencies check
|
||||
run: |
|
||||
echo "### Dependencies Changes" >> $GITHUB_STEP_SUMMARY
|
||||
if git diff origin/master...HEAD Cargo.toml | grep -E "^\+.*=" | grep -v "^\+\+\+" > new_deps.txt; then
|
||||
echo "**New dependencies added:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```toml' >> $GITHUB_STEP_SUMMARY
|
||||
cat new_deps.txt >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Required Actions:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Audit each new dependency on crates.io" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Check maintainer reputation and download counts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Verify no typosquatting (e.g., 'reqwest' vs 'request')" >> $GITHUB_STEP_SUMMARY
|
||||
echo "::warning::New dependencies require supply chain audit"
|
||||
else
|
||||
echo "No new dependencies added" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Clippy security lints
|
||||
run: |
|
||||
echo "### Clippy Security Lints" >> $GITHUB_STEP_SUMMARY
|
||||
if cargo clippy --all-targets -- -W clippy::unwrap_used -W clippy::panic -W clippy::expect_used 2>&1 | tee clippy.log | grep -E "warning:|error:"; then
|
||||
echo "Security-related lints triggered:" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
grep -E "warning:|error:" clippy.log | head -20 >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "::warning::Clippy security lints failed"
|
||||
else
|
||||
echo "All security lints passed" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Summary verdict
|
||||
run: |
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Security Review Verdict" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**This is an automated security scan. A human maintainer must:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "1. Review all warnings above" >> $GITHUB_STEP_SUMMARY
|
||||
echo "2. Verify PR intent matches actual code changes" >> $GITHUB_STEP_SUMMARY
|
||||
echo "3. Check for subtle backdoors or logic bombs" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**For high-risk PRs (critical files modified):**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Require approval from 2 maintainers" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Test in isolated environment before merge" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
semgrep:
|
||||
name: semgrep security scan
|
||||
needs: clippy
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: semgrep/semgrep
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: semgrep scan --config .semgrep.yml --baseline-commit ${{ github.event.pull_request.base.sha }} --error
|
||||
|
||||
benchmark:
|
||||
name: benchmark
|
||||
needs: clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build rtk
|
||||
run: cargo build --release
|
||||
|
||||
- name: Install system tools
|
||||
run: sudo apt-get install -y tree
|
||||
|
||||
- name: Install Python tools
|
||||
run: pip install ruff pytest mypy
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "stable"
|
||||
|
||||
- name: Install Go tools
|
||||
run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
- name: Run benchmark
|
||||
run: ./scripts/benchmark.sh
|
||||
|
||||
# ─── AI Doc Review: develop PRs only ───
|
||||
|
||||
doc-review:
|
||||
name: doc review
|
||||
if: github.base_ref == 'develop'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Gather PR context
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_NUM=${{ github.event.pull_request.number }}
|
||||
gh pr diff "$PR_NUM" --name-only > changed_files.txt
|
||||
gh pr diff "$PR_NUM" | head -c 12000 > diff.txt
|
||||
gh pr view "$PR_NUM" --json title,body --jq '"PR Title: \(.title)\nPR Description: \(.body)"' > pr_info.txt
|
||||
|
||||
- name: Build prompt files
|
||||
run: |
|
||||
# System prompt
|
||||
cat <<'EOF' > system_prompt.txt
|
||||
You are a documentation reviewer for the RTK project.
|
||||
You will receive the project's CONTRIBUTING.md (which contains the documentation rules), the PR info, changed files, and diff.
|
||||
Your job: based ONLY on the documentation rules in CONTRIBUTING.md, decide if the PR includes the required documentation updates.
|
||||
|
||||
IMPORTANT:
|
||||
- CI/CD changes, test-only changes, and refactors with no user-facing impact do NOT require doc updates.
|
||||
- Be practical, not pedantic. Small obvious fixes don't need CHANGELOG entries.
|
||||
- Only flag missing docs when there is a clear user-facing change.
|
||||
EOF
|
||||
|
||||
# User prompt: concatenate files (no printf, no variable expansion issues)
|
||||
{
|
||||
cat pr_info.txt
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "CONTRIBUTING.md:"
|
||||
cat CONTRIBUTING.md
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "Changed files:"
|
||||
cat changed_files.txt
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "Diff (may be truncated):"
|
||||
cat diff.txt
|
||||
} > user_prompt.txt
|
||||
|
||||
- name: AI documentation review
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.RTK_DOCS_ANTHROPIC_KEY }}
|
||||
run: |
|
||||
echo "## Documentation Review (AI)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -z "$ANTHROPIC_API_KEY" ]; then
|
||||
echo "::warning::ANTHROPIC_API_KEY not configured — skipping AI doc review"
|
||||
echo "Skipped: ANTHROPIC_API_KEY secret not configured." >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::group::Preparing API request"
|
||||
echo "System prompt: $(wc -c < system_prompt.txt) bytes"
|
||||
echo "User prompt: $(wc -c < user_prompt.txt) bytes"
|
||||
SYSTEM_JSON=$(jq -Rs . < system_prompt.txt)
|
||||
USER_JSON=$(jq -Rs . < user_prompt.txt)
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Calling Claude API (claude-sonnet-4-6)"
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" https://api.anthropic.com/v1/messages \
|
||||
-H "content-type: application/json" \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-d "{
|
||||
\"model\": \"claude-sonnet-4-6\",
|
||||
\"max_tokens\": 1024,
|
||||
\"messages\": [{\"role\": \"user\", \"content\": $USER_JSON}],
|
||||
\"system\": $SYSTEM_JSON,
|
||||
\"output_config\": {
|
||||
\"format\": {
|
||||
\"type\": \"json_schema\",
|
||||
\"schema\": {
|
||||
\"type\": \"object\",
|
||||
\"properties\": {
|
||||
\"status\": {\"type\": \"string\", \"enum\": [\"PASS\", \"FAIL\"]},
|
||||
\"reasoning\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},
|
||||
\"files_to_update\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}}
|
||||
},
|
||||
\"required\": [\"status\", \"reasoning\", \"files_to_update\"],
|
||||
\"additionalProperties\": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}")
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
echo "HTTP status: $HTTP_CODE"
|
||||
echo "::endgroup::"
|
||||
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "::warning::Claude API returned HTTP $HTTP_CODE — skipping doc review"
|
||||
echo "Skipped: API error (HTTP $HTTP_CODE)" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$BODY" | head -10 >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse structured JSON response
|
||||
REVIEW_JSON=$(echo "$BODY" | jq -r '.content[0].text // empty')
|
||||
|
||||
if [ -z "$REVIEW_JSON" ]; then
|
||||
echo "::warning::Empty response from Claude API — skipping doc review"
|
||||
echo "Skipped: empty API response" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Raw response:"
|
||||
echo "$BODY" | head -20
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::group::AI Review Result"
|
||||
echo "$REVIEW_JSON" | jq .
|
||||
echo "::endgroup::"
|
||||
|
||||
STATUS=$(echo "$REVIEW_JSON" | jq -r '.status')
|
||||
REASONING=$(echo "$REVIEW_JSON" | jq -r '.reasoning[]' 2>/dev/null)
|
||||
FILES=$(echo "$REVIEW_JSON" | jq -r '.files_to_update[]' 2>/dev/null)
|
||||
|
||||
echo "### Verdict: ${STATUS}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -n "$REASONING" ]; then
|
||||
echo "**Reasoning:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "$REASONING" | while IFS= read -r line; do
|
||||
echo "- $line" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$STATUS" = "FAIL" ] && [ -n "$FILES" ]; then
|
||||
echo "**Files to update:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "$FILES" | while IFS= read -r f; do
|
||||
echo "- \`$f\`" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$STATUS" = "PASS" ]; then
|
||||
echo "Documentation review passed."
|
||||
elif [ "$STATUS" = "FAIL" ]; then
|
||||
echo "::error::Documentation review failed — see summary for details"
|
||||
exit 1
|
||||
else
|
||||
echo "::warning::Unexpected status '${STATUS}' — skipping"
|
||||
echo "Unexpected AI response status: ${STATUS}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
Reference in New Issue
Block a user