-
Notifications
You must be signed in to change notification settings - Fork 644
161 lines (140 loc) · 5.88 KB
/
Copy pathscanoss.yml
File metadata and controls
161 lines (140 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
name: "SCANOSS License & BOM Check"
# Runs only when a PR is APPROVED — i.e. after review and all other checks are
# green — so the scan happens once, last, right before merge. Scans only the
# files the PR changed (not the whole tree). Both choices minimize osskb quota
# (limits are per-file: 5000/day, 7500/week, 15000/month).
on:
pull_request_review:
types: [submitted]
workflow_dispatch:
env:
REPORTS_DIR: scanoss-reports
jobs:
scanoss:
runs-on: ubuntu-latest
# Only on an approving review of a PR targeting master/main (or manual dispatch).
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event.review.state == 'approved' &&
(github.event.pull_request.base.ref == 'master' ||
github.event.pull_request.base.ref == 'main'))
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# pull_request_review checks out the base branch by default; force the
# PR head so we scan the code being merged. fetch-depth 0 so we can
# diff against the base branch. (Empty ref on manual dispatch -> the
# dispatched ref.)
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install scanoss-py
run: pip install scanoss
- name: Create reports directory
run: mkdir -p ${{ env.REPORTS_DIR }}
- name: Run SCANOSS scan (changed files only)
run: |
set -euo pipefail
OUT=${{ env.REPORTS_DIR }}/results.json
BASE_REF="${{ github.event.pull_request.base.ref }}"
if [ -z "$BASE_REF" ]; then
# Manual dispatch: no PR context -> scan the whole tree.
echo "No PR context (manual dispatch): scanning full tree."
scanoss-py scan --apiurl https://api.osskb.org -o "$OUT" . || true
else
git fetch --no-tags --quiet origin "$BASE_REF" || true
BASE_SHA=$(git merge-base "origin/$BASE_REF" HEAD 2>/dev/null || echo "${{ github.event.pull_request.base.sha }}")
git diff --name-only --diff-filter=ACMR "$BASE_SHA" HEAD > changed_files.txt || true
echo "Changed files in this PR ($(wc -l < changed_files.txt)):"
cat changed_files.txt || true
if [ -s changed_files.txt ]; then
# scanoss-py applies scanoss.json skip patterns to this list too,
# so tests/vendor/generated/docs are filtered out automatically.
scanoss-py scan \
--apiurl https://api.osskb.org \
--files-from changed_files.txt \
-o "$OUT" || true
else
echo "No changed files to scan."
fi
fi
# Ensure a valid (possibly empty) results file exists for downstream steps.
[ -s "$OUT" ] || echo '{}' > "$OUT"
- name: Generate CycloneDX BOM (convert, no re-scan)
run: |
scanoss-py convert \
-i ${{ env.REPORTS_DIR }}/results.json \
-f cyclonedx \
-o ${{ env.REPORTS_DIR }}/bom_cyclonedx.json || true
- name: Inspect - Copyleft
run: |
scanoss-py inspect raw copyleft \
-i ${{ env.REPORTS_DIR }}/results.json \
-f md \
-o ${{ env.REPORTS_DIR }}/copyleft.md \
-s ${{ env.REPORTS_DIR }}/copyleft_status.md
- name: Inspect - License Summary
run: |
scanoss-py inspect raw license-summary \
-i ${{ env.REPORTS_DIR }}/results.json \
-o ${{ env.REPORTS_DIR }}/license_summary.md
- name: Inspect - Component Summary
run: |
scanoss-py inspect raw component-summary \
-i ${{ env.REPORTS_DIR }}/results.json \
-o ${{ env.REPORTS_DIR }}/component_summary.md
- name: Inspect - Undeclared Components
run: |
scanoss-py inspect raw undeclared \
-i ${{ env.REPORTS_DIR }}/results.json \
-f md \
-o ${{ env.REPORTS_DIR }}/undeclared.md \
-s ${{ env.REPORTS_DIR }}/undeclared_status.md
- name: Check for non-allowed licenses
run: |
python3 - <<'PYEOF'
import json, re, sys
from collections import defaultdict
ALLOWED = {'MIT', 'Apache-2.0', 'BSD-3-Clause'}
def spdx_tokens(expr):
return set(re.split(r'\s+(?:OR|AND|WITH)\s+|\(|\)', expr)) - {''}
with open('${{ env.REPORTS_DIR }}/results.json') as f:
data = json.load(f)
violations = defaultdict(set)
for file_path, matches in data.items():
for match in matches:
for lic in match.get('licenses', []):
name = lic.get('name', '')
if name and not spdx_tokens(name).issubset(ALLOWED):
violations[name].add(file_path)
if violations:
print("::error::Non-allowed licenses found:")
for lic, files in sorted(violations.items()):
print(f" {lic}: {len(files)} file(s)")
for f in sorted(files)[:5]:
print(f" - {f}")
sys.exit(1)
else:
print("License check passed — all licenses within MIT, Apache-2.0, BSD-3-Clause")
PYEOF
- name: Check for copyleft
run: |
STATUS=$(cat ${{ env.REPORTS_DIR }}/copyleft_status.md)
echo "$STATUS"
if echo "$STATUS" | grep -qv "^0 "; then
echo "::error::Copyleft licenses detected"
exit 1
fi
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
with:
name: scanoss-reports
path: ${{ env.REPORTS_DIR }}/
retention-days: 30