Skip to content

Commit c14f1bb

Browse files
committed
.github: add check-license-header workflow
this workflow checks the first 10 lines for "LicenseRef-ScyllaDB-Source-Available-1.0" in newly introduced files when a new pull request is created against "master" or "next". if "LicenseRef-ScyllaDB-Source-Available-1.0" is not found, the workflow fails. for the sake of simplicity, instead of parsing the header for SPDX License ID, we just check to see if the "LicenseRef-ScyllaDB-Source-Available-1.0" is included. Signed-off-by: Kefu Chai <[email protected]>
1 parent 3e22998 commit c14f1bb

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

.github/scripts/check-license.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import sys
5+
from pathlib import Path
6+
from typing import Set
7+
8+
9+
def parse_args() -> argparse.Namespace:
10+
"""Parses command-line arguments."""
11+
parser = argparse.ArgumentParser(description='Check license headers in files')
12+
parser.add_argument('--files', required=True, nargs="+", type=Path,
13+
help='List of files to check')
14+
parser.add_argument('--license', required=True,
15+
help='License to check for')
16+
parser.add_argument('--check-lines', type=int, default=10,
17+
help='Number of lines to check (default: %(default)s)')
18+
parser.add_argument('--extensions', required=True, nargs="+",
19+
help='List of file extensions to check')
20+
parser.add_argument('--verbose', action='store_true',
21+
help='Print verbose output (default: %(default)s)')
22+
return parser.parse_args()
23+
24+
25+
def should_check_file(file_path: Path, allowed_extensions: Set[str]) -> bool:
26+
return file_path.suffix in allowed_extensions
27+
28+
29+
def check_license_header(file_path: Path, license_header: str, check_lines: int) -> bool:
30+
try:
31+
with open(file_path, 'r', encoding='utf-8') as f:
32+
for _ in range(check_lines):
33+
line = f.readline()
34+
if license_header in line:
35+
return True
36+
return False
37+
except (UnicodeDecodeError, StopIteration):
38+
# Handle files that can't be read as text or have fewer lines
39+
return False
40+
41+
42+
def main() -> int:
43+
args = parse_args()
44+
45+
if not args.files:
46+
print("No files to check")
47+
return 0
48+
49+
num_errors = 0
50+
51+
for file_path in args.files:
52+
# Skip non-existent files
53+
if not file_path.exists():
54+
continue
55+
56+
# Skip files with non-matching extensions
57+
if not should_check_file(file_path, args.extensions):
58+
print(f"ℹ️ Skipping file with unchecked extension: {file_path}")
59+
continue
60+
61+
# Check license header
62+
if check_license_header(file_path, args.license, args.check_lines):
63+
if args.verbose:
64+
print(f"✅ License header found in: {file_path}")
65+
else:
66+
print(f"❌ Missing license header in: {file_path}")
67+
num_errors += 1
68+
69+
if num_errors > 0:
70+
sys.exit(1)
71+
72+
73+
if __name__ == '__main__':
74+
main()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: License Header Check
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
branches:
7+
- master
8+
- next
9+
10+
env:
11+
HEADER_CHECK_LINES: 10
12+
LICENSE: "LicenseRef-ScyllaDB-Source-Available-1.0"
13+
CHECKED_EXTENSIONS: ".cc .hh .py"
14+
15+
jobs:
16+
check-license-headers:
17+
name: Check License Headers
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 0
25+
26+
- name: Get changed files
27+
id: changed-files
28+
run: |
29+
# Get list of added files comparing with base branch
30+
echo "files=$(git diff --name-only --diff-filter=A ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | tr '\n' ' ')" >> $GITHUB_OUTPUT
31+
32+
- name: Check license headers
33+
run: |
34+
.github/scripts/check-license.py \
35+
--files ${{ steps.changed-files.outputs.files }} \
36+
--license "${{ env.LICENSE }}" \
37+
--check-lines "${{ env.HEADER_CHECK_LINES }}" \
38+
--extensions ${{ env.CHECKED_EXTENSIONS }}
39+
40+
- name: Comment on PR if check fails
41+
if: failure()
42+
uses: actions/github-script@v7
43+
with:
44+
script: |
45+
const license = '${{ env.LICENSE }}';
46+
await github.rest.issues.createComment({
47+
issue_number: context.issue.number,
48+
owner: context.repo.owner,
49+
repo: context.repo.repo,
50+
body: `❌ License header check failed. Please ensure all new files include the header within the first ${{ env.HEADER_CHECK_LINES }} lines:\n\`\`\`\n${license}\n\`\`\`\nSee action logs for details.`
51+
});

0 commit comments

Comments
 (0)