Skip to content

Commit 0f94738

Browse files
authored
workflow, action, script and deny.toml (#1425)
workflow, action, script and deny.toml Merge branch 'main' into igor/versioning/pr-metadata-docs-and-deps Co-authored-by: igor.unanua <[email protected]>
1 parent 46b05c4 commit 0f94738

5 files changed

Lines changed: 614 additions & 0 deletions

File tree

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ datadog-sidecar @Datadog/libdatadog-php @Datadog/libdatadog
3131
datadog-sidecar-ffi @Datadog/libdatadog-php @Datadog/libdatadog-apm
3232
datadog-sidecar-macros @Datadog/libdatadog-php
3333
datadog-tracer-flare @Datadog/libdatadog-apm
34+
deny.toml @Datadog/libdatadog
3435
docker-bake.hcl @Datadog/apm-common-components-core
3536
docs @Datadog/libdatadog
3637
examples @Datadog/libdatadog
@@ -57,6 +58,7 @@ README.md @Datadog/libdatadog
5758
repository.datadog.yml @Datadog/apm-common-components-core
5859
ruby/ @Datadog/ruby-guild
5960
rustfmt.toml @Datadog/libdatadog-core
61+
scripts/check_cargo_metadata.sh @Datadog/libdatadog-core
6062
scripts/semver-level.sh @Datadog/libdatadog-core
6163
scripts/reformat_copyright.sh @Datadog/libdatadog-core
6264
scripts/update_license_3rdparty.sh @Datadog/libdatadog-core
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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 crates with name, version, path and manifest'
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))
76+
77+
# Build JSON array of objects with name, version, and path
78+
JSON_ARRAY="["
79+
FIRST=true
80+
for crate_path in "${UNIQUE_CRATES[@]}"; do
81+
if [[ -z "$crate_path" ]]; then
82+
continue
83+
fi
84+
85+
# Extract name and version from Cargo.toml
86+
CARGO_TOML="$crate_path/Cargo.toml"
87+
CRATE_NAME=$(grep -E '^\s*name\s*=' "$CARGO_TOML" | head -1 | sed -E 's/.*=\s*"([^"]+)".*/\1/')
88+
CRATE_VERSION=$(grep -E '^\s*version\s*=' "$CARGO_TOML" | head -1 | sed -E 's/.*=\s*"([^"]+)".*/\1/')
89+
90+
# Handle version.workspace = true
91+
if [[ -z "$CRATE_VERSION" ]] || [[ "$CRATE_VERSION" == *"workspace"* ]]; then
92+
CRATE_VERSION="workspace"
93+
fi
94+
95+
if [[ "$FIRST" == "true" ]]; then
96+
FIRST=false
97+
else
98+
JSON_ARRAY+=","
99+
fi
100+
101+
JSON_ARRAY+="{\"name\":\"$CRATE_NAME\",\"version\":\"$CRATE_VERSION\",\"path\":\"$crate_path\",\"manifest\":\"$CARGO_TOML\"}"
102+
done
103+
JSON_ARRAY+="]"
104+
105+
# Ensure JSON is compact (single line) for GITHUB_OUTPUT
106+
JSON_ARRAY=$(echo "$JSON_ARRAY" | jq -c .)
107+
108+
echo "Changed crates: $(echo "$JSON_ARRAY" | tr '\n' ' ')"
109+
echo "crates=$JSON_ARRAY" >> "$GITHUB_OUTPUT"
110+
echo "crates_count=$(echo "$JSON_ARRAY" | jq 'length')" >> "$GITHUB_OUTPUT"
111+
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
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+
while read -r crate; do
49+
NAME=$(echo "$crate" | jq -r '.name')
50+
CURRENT_VERSION=$(echo "$crate" | jq -r '.version')
51+
MANIFEST=$(echo "$crate" | jq -r '.manifest')
52+
53+
echo "Checking $NAME version is not changed in the PR..."
54+
55+
# Get base version from Cargo.toml at base ref
56+
BASE_VERSION=$(git show "$BASE_REF:$MANIFEST" 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 " ✓ $NAME is a new crate (no base version found)"
60+
elif [[ "$CURRENT_VERSION" != "$BASE_VERSION" ]]; then
61+
echo " ✗ ERROR: $NAME 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 " ✓ $NAME version unchanged ($CURRENT_VERSION)"
66+
fi
67+
done < <(echo "$CRATES" | jq -c '.[]')
68+
69+
if [[ $FAILED -eq 1 ]]; then
70+
echo ""
71+
echo "ERROR: One or more crates have version changes in this PR."
72+
echo "Version bumps should be done through the release process, not in feature PRs."
73+
exit 1
74+
fi
75+
76+
- name: Check cargo metadata for changed crates
77+
run: |
78+
# Get the list of changed crates
79+
CRATES='${{ needs.changed-crates.outputs.crates }}'
80+
81+
# Convert JSON array to space-separated arguments
82+
CRATE_ARGS=$(echo "$CRATES" | jq -r '.[].path' | tr '\n' ' ')
83+
84+
if [[ -n "$CRATE_ARGS" ]]; then
85+
echo "Checking crates: $CRATE_ARGS"
86+
./scripts/check_cargo_metadata.sh $CRATE_ARGS
87+
else
88+
echo "No crates to check"
89+
fi
90+
91+
# Check for missing documentation and post results as PR comment
92+
missing-docs:
93+
needs: changed-crates
94+
if: needs.changed-crates.outputs.crates_count > 0
95+
runs-on: ubuntu-latest
96+
permissions:
97+
pull-requests: write
98+
steps:
99+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
100+
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
101+
with:
102+
cache-targets: true
103+
- uses: dtolnay/rust-toolchain@stable
104+
- name: Check missing docs
105+
id: missing-docs
106+
run: |
107+
# Get the list of changed crates
108+
CRATES='${{ needs.changed-crates.outputs.crates }}'
109+
110+
# Capture doc warnings for each crate
111+
OUTPUT=""
112+
TOTAL_WARNINGS=0
113+
114+
for crate in $(echo "$CRATES" | jq -r '.[].name'); do
115+
echo "Checking docs for $crate..."
116+
117+
# Run cargo check with missing_docs warning and capture output
118+
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)
119+
120+
echo "WARNINGS: $WARNINGS"
121+
122+
WARNING_COUNT=$(echo "$WARNINGS" | grep -c "missing documentation" 2>/dev/null || true)
123+
WARNING_COUNT=${WARNING_COUNT:-0}
124+
125+
if [[ "$WARNING_COUNT" -gt 0 ]]; then
126+
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"
127+
TOTAL_WARNINGS=$((TOTAL_WARNINGS + WARNING_COUNT))
128+
else
129+
OUTPUT="${OUTPUT}\n### 📦 \`${crate}\` - ✅ No warnings\n"
130+
fi
131+
done
132+
133+
# Create the comment body
134+
if [[ $TOTAL_WARNINGS -gt 0 ]]; then
135+
HEADER="## 📚 Documentation Check Results\n\n⚠️ **${TOTAL_WARNINGS} documentation warning(s) found**\n"
136+
else
137+
HEADER="## 📚 Documentation Check Results\n\n✅ **No documentation warnings found!**\n"
138+
fi
139+
140+
COMMENT_BODY="${HEADER}${OUTPUT}\n\n---\n*Updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | Commit: ${{ github.sha }}*"
141+
142+
# Write to file for the comment action (handle multi-line)
143+
echo -e "$COMMENT_BODY" > doc-check-results.md
144+
145+
echo "total_warnings=$TOTAL_WARNINGS" >> "$GITHUB_OUTPUT"
146+
147+
- name: Fail if warnings found and FAIL_IF_MISSING_DOCS is true
148+
if: env.FAIL_IF_MISSING_DOCS == 'true' && steps.missing-docs.outputs.total_warnings > 0
149+
run: |
150+
echo "missing-docs found ${{ steps.missing-docs.outputs.total_warnings }} warning(s) and FAIL_IF_MISSING_DOCS is enabled"
151+
exit 1
152+
153+
- name: Find existing comment
154+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
155+
id: find-comment
156+
with:
157+
issue-number: ${{ github.event.pull_request.number }}
158+
comment-author: 'github-actions[bot]'
159+
body-includes: '## 📚 Documentation Check Results'
160+
161+
- name: Create or update PR comment
162+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
163+
with:
164+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
165+
issue-number: ${{ github.event.pull_request.number }}
166+
body-path: doc-check-results.md
167+
edit-mode: replace
168+
169+
dependency-check:
170+
needs: changed-crates
171+
if: needs.changed-crates.outputs.crates_count > 0
172+
runs-on: ubuntu-latest
173+
permissions:
174+
pull-requests: write
175+
steps:
176+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
177+
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
178+
with:
179+
cache-targets: true
180+
- uses: dtolnay/rust-toolchain@stable
181+
- uses: taiki-e/cache-cargo-install-action@7447f04c51f2ba27ca35e7f1e28fab848c5b3ba7 # 2.3.1
182+
with:
183+
tool: cargo-deny
184+
- name: Run cargo-deny on changed crates
185+
id: cargo-deny
186+
run: |
187+
# Get the list of changed crates
188+
CRATES='${{ needs.changed-crates.outputs.crates }}'
189+
190+
OUTPUT=""
191+
TOTAL_ISSUES=0
192+
TOTAL_ERRORS=0
193+
194+
while read -r crate; do
195+
NAME=$(echo "$crate" | jq -r '.name')
196+
MANIFEST=$(echo "$crate" | jq -r '.manifest')
197+
198+
echo "Running cargo-deny for $NAME..."
199+
200+
# Run cargo-deny and capture output (always continue, results go to PR comment)
201+
DENY_OUTPUT=$(cargo deny --manifest-path "$MANIFEST" --color never check advisories bans sources 2>&1 || true)
202+
203+
echo "DENY_OUTPUT: $DENY_OUTPUT"
204+
205+
# Count errors and warnings
206+
ERROR_COUNT=$(echo "$DENY_OUTPUT" | grep -cE "^error" 2>/dev/null || true)
207+
ERROR_COUNT=${ERROR_COUNT:-0}
208+
WARNING_COUNT=$(echo "$DENY_OUTPUT" | grep -cE "^warning" 2>/dev/null || true)
209+
WARNING_COUNT=${WARNING_COUNT:-0}
210+
ISSUE_COUNT=$((ERROR_COUNT + WARNING_COUNT))
211+
212+
if [[ "$ISSUE_COUNT" -gt 0 ]]; then
213+
OUTPUT="${OUTPUT}\n### 📦 \`${NAME}\` - ${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"
214+
TOTAL_ISSUES=$((TOTAL_ISSUES + ISSUE_COUNT))
215+
TOTAL_ERRORS=$((TOTAL_ERRORS + ERROR_COUNT))
216+
else
217+
OUTPUT="${OUTPUT}\n### 📦 \`${NAME}\` - ✅ No issues\n"
218+
fi
219+
done < <(echo "$CRATES" | jq -c '.[]')
220+
221+
# Create the comment body
222+
if [[ $TOTAL_ISSUES -gt 0 ]]; then
223+
HEADER="## 🔒 Cargo Deny Results\n\n⚠️ **${TOTAL_ISSUES} issue(s) found** (advisories, bans, sources)\n"
224+
else
225+
HEADER="## 🔒 Cargo Deny Results\n\n✅ **No issues found!**\n"
226+
fi
227+
228+
COMMENT_BODY="${HEADER}${OUTPUT}\n\n---\n*Updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | Commit: ${{ github.sha }}*"
229+
230+
# Write to file for the comment action
231+
echo -e "$COMMENT_BODY" > cargo-deny-results.md
232+
233+
echo "total_issues=$TOTAL_ISSUES" >> "$GITHUB_OUTPUT"
234+
echo "total_errors=$TOTAL_ERRORS" >> "$GITHUB_OUTPUT"
235+
236+
- name: Find existing comment
237+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
238+
id: find-comment
239+
with:
240+
issue-number: ${{ github.event.pull_request.number }}
241+
comment-author: 'github-actions[bot]'
242+
body-includes: '## 🔒 Cargo Deny Results'
243+
244+
- name: Create or update PR comment
245+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
246+
with:
247+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
248+
issue-number: ${{ github.event.pull_request.number }}
249+
body-path: cargo-deny-results.md
250+
edit-mode: replace
251+
252+
- name: Fail if errors found and FAIL_IF_CARGO_DENY is true
253+
if: env.FAIL_IF_CARGO_DENY == 'true' && steps.cargo-deny.outputs.total_errors > 0
254+
run: |
255+
echo "cargo-deny found ${{ steps.cargo-deny.outputs.total_errors }} error(s) and FAIL_IF_CARGO_DENY is enabled"
256+
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)