Skip to content

Commit 062b624

Browse files
committed
update licenses
1 parent 58e2bc0 commit 062b624

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

licenses/check_licenses.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Check and update licenses
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
license_update:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v3
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: '3.X'
23+
24+
- name: Run license script and generate patch
25+
env:
26+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27+
run: |
28+
python update_licenses.py --source=pypi TensorFlow
29+
python update_licenses.py --source=github:easybuilders/easybuild EasyBuild
30+
if [ -f license_update.patch ] && [ -s license_update.patch ]; then
31+
PATCH_CONTENT=$(cat license_update.patch)
32+
echo "patch=$PATCH_CONTENT" >> $GITHUB_OUTPUT
33+
fi
34+
35+
- name: Create a PR (if changes detected)
36+
uses: peter-evans/create-pull-request@v5
37+
if: steps.check_licenses.outputs.patch != ''
38+
with:
39+
commit-message: "Auto PR: Update licenses"
40+
title: "Auto PR: Update licenses"
41+
body: ${{ steps.check_licenses.outputs.patch }}
42+
branch: main #fork branch
43+
base: main #specify right brancg here
44+
45+
- name: Apply patch (if no PR created)
46+
if: steps.create_pull_request.outputs.pull-request-number == '' && steps.check_licenses.outputs.patch != ''
47+
run: |
48+
if [ -f license_update.patch ] && [ -s license_update.patch ]; then
49+
git apply license_update.patch
50+
else
51+
echo "No changes to apply"
52+
fi
53+
git add licenses.json
54+
git diff --cached --exit-code || git commit -m "Update licenses.json"
55+
git push
56+
env:
57+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

licenses/update_licenses.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import requests
2+
import argparse
3+
import json
4+
import os
5+
from datetime import datetime
6+
7+
parser = argparse.ArgumentParser(description='Script to ingest licenses')
8+
parser.add_argument('--source', help='Source (GitHub, PyPI, CRAN, Repology) or user')
9+
parser.add_argument('projects', nargs='+', help='List of project names')
10+
parser.add_argument('--manual', help='Manually provided license', required=False)
11+
parser.add_argument('--spdx', help='SPDX identifier for the license', required=False)
12+
args = parser.parse_args()
13+
14+
# Retrieve license from various sources
15+
def github(source):
16+
repo = source.removeprefix('github:')
17+
url = (
18+
"https://api.github.com/repos/{repo}/license".format(repo=repo)
19+
)
20+
headers = {
21+
"Accept": "application/vnd.github+json",
22+
"Authorization": "Bearer {}".format(os.getenv('GITHUB_TOKEN')),
23+
"X-GitHub-Api-Version": "2022-11-28",
24+
}
25+
r = requests.get(url, headers=headers)
26+
if r.status_code != 200:
27+
return "not found", None, None
28+
data = r.json()
29+
return data['license']['spdx_id'], 'GitHub', data['license']['url']
30+
31+
def pypi(project):
32+
url = "https://pypi.org/pypi/{project}/json".format(project=project)
33+
r = requests.get(url)
34+
if r.status_code != 200:
35+
return "not found", None, None
36+
data = r.json()
37+
return data['info']['license'], 'PyPI', data['info'].get('project_url')
38+
39+
def cran(project):
40+
url = "http://crandb.r-pkg.org/{project}".format(project=project)
41+
r = requests.get(url)
42+
if r.status_code != 200:
43+
return "not found", None, None
44+
data = r.json()
45+
return data['License'], 'CRAN', None
46+
47+
def repology(project):
48+
url = "https://repology.org/api/v1/project/{project}".format(
49+
project=project
50+
)
51+
r = requests.get(url)
52+
if r.status_code != 200:
53+
return "not found", None, None
54+
data = r.json()
55+
return data.get('license', 'not found'), 'Repology', None
56+
57+
def ecosysteDotms_pypi(project):
58+
url = "https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/{project}".format(
59+
project=project
60+
)
61+
r = requests.get(url)
62+
if r.status_code != 200:
63+
return "not found", None, None
64+
data = r.json()
65+
return data.get('license', 'not found'), 'Ecosyste.ms (PyPI)', None
66+
67+
def ecosysteDotms_github(source):
68+
repo = source.removeprefix('github:')
69+
url = "https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/{repo}".format(
70+
repo=repo
71+
)
72+
r = requests.get(url)
73+
if r.status_code != 200:
74+
return "not found", None, None
75+
data = r.json()
76+
return data.get('license', 'not found'), 'Ecosyste.ms (GitHub)', None
77+
78+
# Main license retrieval function
79+
def license_info(project):
80+
if args.source == 'pypi':
81+
lic, source, url = ecosysteDotms_pypi(project)
82+
elif "github" in args.source:
83+
lic, source, url = ecosysteDotms_github(args.source)
84+
elif args.manual:
85+
lic = args.manual
86+
source = args.source
87+
url = None
88+
else:
89+
lic, source, url = "not found", None, None
90+
91+
spdx_id = args.spdx if args.spdx else (lic if lic and lic != "not found" else None)
92+
93+
info = {
94+
"license": lic,
95+
"source": source,
96+
"spdx_id": spdx_id,
97+
"retrieved_at": datetime.now().isoformat(),
98+
}
99+
return info
100+
101+
102+
def update_json(licenses, project, info):
103+
if project in licenses:
104+
if 'history' not in licenses[project]:
105+
licenses[project]['history'] = []
106+
licenses[project]['history'].append(info)
107+
licenses[project]['current'] = info
108+
print('Updated license for project {project}'.format(project=project))
109+
else:
110+
licenses[project] = {
111+
"current": info,
112+
"history": [info],
113+
}
114+
print('Added new license for project {project}'.format(project=project))
115+
116+
lic_json = json.dumps(licenses, indent=4)
117+
with open('licenses.json', 'w') as lic_file:
118+
lic_file.write(lic_json)
119+
120+
return licenses
121+
122+
# Create patch output
123+
def generate_patch(licenses):
124+
patch = json.dumps(licenses, indent=4)
125+
return patch
126+
127+
# Function to save patch to a file
128+
def save_patch(patch_content, filename="license_update.patch"):
129+
with open(filename, 'w') as patch_file:
130+
patch_file.write(patch_content)
131+
print("Patch saved to {filename}".format(filename=filename))
132+
133+
def main():
134+
if os.path.exists('licenses.json'):
135+
with open('licenses.json', 'r') as lic_dict:
136+
licenses = json.loads(lic_dict.read())
137+
else:
138+
licenses = {}
139+
140+
for project in args.projects:
141+
info = license_info(project)
142+
update_json(licenses, project, info)
143+
144+
patch = generate_patch(licenses)
145+
save_patch(patch)
146+
147+
with open('licenses.json', 'w') as lic_file:
148+
lic_file.write(patch)
149+
150+
print("Patch output:\n{patch}".format(patch=patch))
151+
152+
if __name__ == "__main__":
153+
main()
154+

0 commit comments

Comments
 (0)