Skip to content

Commit 62ce9f2

Browse files
committed
workflow, action, script and deny.toml
1 parent b9320a3 commit 62ce9f2

File tree

4 files changed

+575
-0
lines changed

4 files changed

+575
-0
lines changed

.github/actions/action.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: 'Get Changed Crates'
5+
description: 'Detects which Rust crates have changed files in a PR or push'
6+
7+
inputs:
8+
include-non-publishable:
9+
description: 'Include crates with publish = false'
10+
required: false
11+
default: 'false'
12+
base-ref:
13+
description: 'Base ref for comparison (defaults to PR base or HEAD~1 for push)'
14+
required: false
15+
default: ''
16+
17+
outputs:
18+
crates:
19+
description: 'JSON array of changed crate directory paths'
20+
value: ${{ steps.detect.outputs.crates }}
21+
crates_count:
22+
description: 'Number of changed crates'
23+
value: ${{ steps.detect.outputs.crates_count }}
24+
base_ref:
25+
description: 'The base ref used for comparison'
26+
value: ${{ steps.detect.outputs.base_ref }}
27+
28+
runs:
29+
using: 'composite'
30+
steps:
31+
- id: detect
32+
shell: bash
33+
env:
34+
INCLUDE_NON_PUBLISHABLE: ${{ inputs.include-non-publishable }}
35+
INPUT_BASE_REF: ${{ inputs.base-ref }}
36+
EVENT_NAME: ${{ github.event_name }}
37+
PR_BASE_REF: ${{ github.base_ref }}
38+
run: |
39+
# Determine base ref for comparison
40+
if [[ -n "$INPUT_BASE_REF" ]]; then
41+
BASE_REF="$INPUT_BASE_REF"
42+
elif [[ "$EVENT_NAME" == "pull_request" ]]; then
43+
BASE_REF="origin/$PR_BASE_REF"
44+
else
45+
# For push events, compare with previous commit
46+
BASE_REF="HEAD~1"
47+
fi
48+
49+
echo "Using base ref: $BASE_REF"
50+
echo "base_ref=$BASE_REF" >> "$GITHUB_OUTPUT"
51+
52+
# Get list of changed files
53+
CHANGED_FILES=$(git diff --name-only "$BASE_REF" HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD)
54+
55+
# Extract unique crate directories from changed files
56+
CRATES=()
57+
for file in $CHANGED_FILES; do
58+
# Check if file is in a crate directory (has Cargo.toml)
59+
dir=$(dirname "$file")
60+
while [[ "$dir" != "." && "$dir" != "/" ]]; do
61+
if [[ -f "$dir/Cargo.toml" ]]; then
62+
# Check if this crate should be included
63+
if [[ "$INCLUDE_NON_PUBLISHABLE" == "true" ]]; then
64+
CRATES+=("$dir")
65+
elif ! grep -qE '^\s*publish\s*=\s*false' "$dir/Cargo.toml"; then
66+
CRATES+=("$dir")
67+
fi
68+
break
69+
fi
70+
dir=$(dirname "$dir")
71+
done
72+
done
73+
74+
# Remove duplicates and sort
75+
UNIQUE_CRATES=$(printf '%s\n' "${CRATES[@]}" | sort -u | tr '\n' ' ')
76+
77+
# Convert to JSON array for output
78+
JSON_ARRAY=$(printf '%s\n' "${CRATES[@]}" | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
79+
80+
echo "Changed crates: $UNIQUE_CRATES"
81+
echo "crates=$JSON_ARRAY" >> "$GITHUB_OUTPUT"
82+
echo "crates_count=$(echo "$JSON_ARRAY" | jq 'length')" >> "$GITHUB_OUTPUT"
83+
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
name: Metadata, docs and deps
2+
permissions:
3+
contents: read
4+
pull-requests: read
5+
on:
6+
pull_request:
7+
branches-ignore:
8+
- "v[0-9]+.[0-9]+.[0-9]+.[0-9]+"
9+
- release
10+
11+
env:
12+
FAIL_IF_CARGO_DENY: false # Set to true to make cargo-deny errors fail the job
13+
FAIL_IF_MISSING_DOCS: false # Set to true to make missing docs errors fail the job
14+
15+
jobs:
16+
# Get crates changed in the PR that are not flagged as publish = false
17+
changed-crates:
18+
runs-on: ubuntu-latest
19+
outputs:
20+
crates: ${{ steps.changed-crates.outputs.crates }}
21+
crates_count: ${{ steps.changed-crates.outputs.crates_count }}
22+
base_ref: ${{ steps.changed-crates.outputs.base_ref }}
23+
steps:
24+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
25+
with:
26+
fetch-depth: 0 # Need full history for git diff
27+
- id: changed-crates
28+
uses: ./.github/actions/changed-crates
29+
30+
# Check cargo metadata for crates that are not flagged as publish = false
31+
cargo-metadata:
32+
needs: changed-crates
33+
if: needs.changed-crates.outputs.crates_count > 0
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
37+
with:
38+
fetch-depth: 0 # Need full history for git diff
39+
- name: Check Cargo.toml version is not changed in the PR
40+
run: |
41+
# Get the base ref for comparison
42+
BASE_REF='${{ needs.changed-crates.outputs.base_ref }}'
43+
44+
# Get the list of changed crates
45+
CRATES='${{ needs.changed-crates.outputs.crates }}'
46+
47+
FAILED=0
48+
for crate in $(echo "$CRATES" | jq -r '.[]'); do
49+
if [[ -f "$crate/Cargo.toml" ]]; then
50+
echo "Checking $crate version is not changed in the PR..."
51+
52+
# Get current version from Cargo.toml
53+
CURRENT_VERSION=$(grep -E '^\s*version\s*=' "$crate/Cargo.toml" | head -1 | sed -E 's/.*=\s*"([^"]+)".*/\1/')
54+
55+
# Get base version from Cargo.toml at base ref
56+
BASE_VERSION=$(git show "$BASE_REF:$crate/Cargo.toml" 2>/dev/null | grep -E '^\s*version\s*=' | head -1 | sed -E 's/.*=\s*"([^"]+)".*/\1/' || echo "")
57+
58+
if [[ -z "$BASE_VERSION" ]]; then
59+
echo " ✓ $crate is a new crate (no base version found)"
60+
elif [[ "$CURRENT_VERSION" != "$BASE_VERSION" ]]; then
61+
echo " ✗ ERROR: $crate version changed from $BASE_VERSION to $CURRENT_VERSION"
62+
echo " Version changes should only happen through the release process."
63+
FAILED=1
64+
else
65+
echo " ✓ $crate version unchanged ($CURRENT_VERSION)"
66+
fi
67+
fi
68+
done
69+
70+
if [[ $FAILED -eq 1 ]]; then
71+
echo ""
72+
echo "ERROR: One or more crates have version changes in this PR."
73+
echo "Version bumps should be done through the release process, not in feature PRs."
74+
exit 1
75+
fi
76+
77+
- name: Check cargo metadata for changed crates
78+
run: |
79+
# Get the list of changed crates
80+
CRATES='${{ needs.changed-crates.outputs.crates }}'
81+
82+
# Convert JSON array to space-separated arguments
83+
CRATE_ARGS=$(echo "$CRATES" | jq -r '.[]' | tr '\n' ' ')
84+
85+
if [[ -n "$CRATE_ARGS" ]]; then
86+
echo "Checking crates: $CRATE_ARGS"
87+
./scripts/check_cargo_metadata.sh $CRATE_ARGS
88+
else
89+
echo "No crates to check"
90+
fi
91+
92+
# Check for missing documentation and post results as PR comment
93+
missing-docs:
94+
needs: changed-crates
95+
if: needs.changed-crates.outputs.crates_count > 0
96+
runs-on: ubuntu-latest
97+
permissions:
98+
pull-requests: write
99+
steps:
100+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
101+
- uses: dtolnay/rust-toolchain@stable
102+
- name: Check missing docs
103+
id: missing-docs
104+
run: |
105+
# Get the list of changed crates
106+
CRATES='${{ needs.changed-crates.outputs.crates }}'
107+
108+
# Capture doc warnings for each crate
109+
OUTPUT=""
110+
TOTAL_WARNINGS=0
111+
112+
for crate in $(echo "$CRATES" | jq -r '.[]'); do
113+
echo "Checking docs for $crate..."
114+
115+
# Run cargo check with missing_docs warning and capture output
116+
WARNINGS=$(RUSTFLAGS="-W missing_docs" cargo check -p "$crate" --message-format=json | jq -r 'select(.reason == "compiler-message") | select(.message.code.code == "missing_docs") | .message.rendered' 2>&1 || true)
117+
118+
echo "WARNINGS: $WARNINGS"
119+
120+
WARNING_COUNT=$(echo "$WARNINGS" | grep -c "missing documentation" 2>/dev/null || true)
121+
WARNING_COUNT=${WARNING_COUNT:-0}
122+
123+
if [[ "$WARNING_COUNT" -gt 0 ]]; then
124+
OUTPUT="${OUTPUT}\n### 📦 \`${crate}\` - ${WARNING_COUNT} warning(s)\n\n<details>\n<summary>Show warnings</summary>\n\n\`\`\`\n${WARNINGS}\n\`\`\`\n\n</details>\n"
125+
TOTAL_WARNINGS=$((TOTAL_WARNINGS + WARNING_COUNT))
126+
else
127+
OUTPUT="${OUTPUT}\n### 📦 \`${crate}\` - ✅ No warnings\n"
128+
fi
129+
done
130+
131+
# Create the comment body
132+
if [[ $TOTAL_WARNINGS -gt 0 ]]; then
133+
HEADER="## 📚 Documentation Check Results\n\n⚠️ **${TOTAL_WARNINGS} documentation warning(s) found**\n"
134+
else
135+
HEADER="## 📚 Documentation Check Results\n\n✅ **No documentation warnings found!**\n"
136+
fi
137+
138+
COMMENT_BODY="${HEADER}${OUTPUT}\n\n---\n*Updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | Commit: ${{ github.sha }}*"
139+
140+
# Write to file for the comment action (handle multi-line)
141+
echo -e "$COMMENT_BODY" > doc-check-results.md
142+
143+
echo "total_warnings=$TOTAL_WARNINGS" >> "$GITHUB_OUTPUT"
144+
145+
- name: Fail if warnings found and FAIL_IF_MISSING_DOCS is true
146+
if: env.FAIL_IF_MISSING_DOCS == 'true' && steps.missing-docs.outputs.total_warnings > 0
147+
run: |
148+
echo "missing-docs found ${{ steps.missing-docs.outputs.total_warnings }} warning(s) and FAIL_IF_MISSING_DOCS is enabled"
149+
exit 1
150+
151+
- name: Find existing comment
152+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
153+
id: find-comment
154+
with:
155+
issue-number: ${{ github.event.pull_request.number }}
156+
comment-author: 'github-actions[bot]'
157+
body-includes: '## 📚 Documentation Check Results'
158+
159+
- name: Create or update PR comment
160+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
161+
with:
162+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
163+
issue-number: ${{ github.event.pull_request.number }}
164+
body-path: doc-check-results.md
165+
edit-mode: replace
166+
167+
dependency-check:
168+
needs: changed-crates
169+
if: needs.changed-crates.outputs.crates_count > 0
170+
runs-on: ubuntu-latest
171+
permissions:
172+
pull-requests: write
173+
steps:
174+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
175+
- uses: dtolnay/rust-toolchain@stable
176+
- uses: taiki-e/cache-cargo-install-action@7447f04c51f2ba27ca35e7f1e28fab848c5b3ba7 # 2.3.1
177+
with:
178+
tool: cargo-deny
179+
- name: Run cargo-deny on changed crates
180+
id: cargo-deny
181+
run: |
182+
# Get the list of changed crates
183+
CRATES='${{ needs.changed-crates.outputs.crates }}'
184+
185+
OUTPUT=""
186+
TOTAL_ISSUES=0
187+
TOTAL_ERRORS=0
188+
189+
for crate in $(echo "$CRATES" | jq -r '.[]'); do
190+
echo "Running cargo-deny for $crate..."
191+
192+
# Run cargo-deny and capture output (always continue, results go to PR comment)
193+
DENY_OUTPUT=$(cargo deny --manifest-path "$crate/Cargo.toml" --color never check advisories bans sources 2>&1 || true)
194+
195+
echo "DENY_OUTPUT: $DENY_OUTPUT"
196+
197+
# Count errors and warnings
198+
ERROR_COUNT=$(echo "$DENY_OUTPUT" | grep -cE "^error" 2>/dev/null || true)
199+
ERROR_COUNT=${ERROR_COUNT:-0}
200+
WARNING_COUNT=$(echo "$DENY_OUTPUT" | grep -cE "^warning" 2>/dev/null || true)
201+
WARNING_COUNT=${WARNING_COUNT:-0}
202+
ISSUE_COUNT=$((ERROR_COUNT + WARNING_COUNT))
203+
204+
if [[ "$ISSUE_COUNT" -gt 0 ]]; then
205+
OUTPUT="${OUTPUT}\n### 📦 \`${crate}\` - ${ERROR_COUNT} error(s), ${WARNING_COUNT} warning(s)\n\n<details>\n<summary>Show output</summary>\n\n\`\`\`\n${DENY_OUTPUT}\n\`\`\`\n\n</details>\n"
206+
TOTAL_ISSUES=$((TOTAL_ISSUES + ISSUE_COUNT))
207+
else
208+
OUTPUT="${OUTPUT}\n### 📦 \`${crate}\` - ✅ No issues\n"
209+
fi
210+
done
211+
212+
# Create the comment body
213+
if [[ $TOTAL_ISSUES -gt 0 ]]; then
214+
HEADER="## 🔒 Cargo Deny Results\n\n⚠️ **${TOTAL_ISSUES} issue(s) found** (advisories, bans, sources)\n"
215+
else
216+
HEADER="## 🔒 Cargo Deny Results\n\n✅ **No issues found!**\n"
217+
fi
218+
219+
COMMENT_BODY="${HEADER}${OUTPUT}\n\n---\n*Updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | Commit: ${{ github.sha }}*"
220+
221+
# Write to file for the comment action
222+
echo -e "$COMMENT_BODY" > cargo-deny-results.md
223+
224+
echo "total_issues=$TOTAL_ISSUES" >> "$GITHUB_OUTPUT"
225+
echo "total_errors=$TOTAL_ERRORS" >> "$GITHUB_OUTPUT"
226+
227+
- name: Find existing comment
228+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
229+
id: find-comment
230+
with:
231+
issue-number: ${{ github.event.pull_request.number }}
232+
comment-author: 'github-actions[bot]'
233+
body-includes: '## 🔒 Cargo Deny Results'
234+
235+
- name: Create or update PR comment
236+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
237+
with:
238+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
239+
issue-number: ${{ github.event.pull_request.number }}
240+
body-path: cargo-deny-results.md
241+
edit-mode: replace
242+
243+
- name: Fail if errors found and FAIL_IF_CARGO_DENY is true
244+
if: env.FAIL_IF_CARGO_DENY == 'true' && steps.cargo-deny.outputs.total_errors > 0
245+
run: |
246+
echo "cargo-deny found ${{ steps.cargo-deny.outputs.total_errors }} error(s) and FAIL_IF_CARGO_DENY is enabled"
247+
exit 1

deny.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[advisories]
2+
unmaintained = "workspace"
3+
4+
[bans]
5+
multiple-versions = "warn"

0 commit comments

Comments
 (0)