Skip to content

Commit eba0f65

Browse files
committed
Release 6.9.0
1 parent 1d0fcfa commit eba0f65

25 files changed

Lines changed: 409 additions & 174 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import os
2+
import subprocess
3+
import requests
4+
import datetime
5+
import json
6+
from pathlib import Path
7+
8+
REPO = "digital-preservation/droid"
9+
RELEASE_TAG = "develop"
10+
API_BASE = "https://api.github.com"
11+
TOKEN = os.environ["GITHUB_TOKEN"]
12+
HEADERS = {
13+
"Authorization": f"Bearer {TOKEN}",
14+
"Accept": "application/vnd.github+json"
15+
}
16+
17+
def run(cmd):
18+
return subprocess.check_output(cmd, text=True).strip()
19+
20+
def get_commit_sha(branch):
21+
return run(["git", "rev-parse", f"origin/{branch}"])
22+
23+
def is_ancestor(ancestor, descendant):
24+
return subprocess.call(["git", "merge-base", "--is-ancestor", ancestor, descendant]) == 0
25+
26+
def fetch_merged_prs():
27+
url = f"{API_BASE}/repos/{REPO}/pulls"
28+
params = {"state": "closed", "base": "develop", "per_page": 100}
29+
r = requests.get(url, headers=HEADERS, params=params)
30+
r.raise_for_status()
31+
return [pr for pr in r.json() if pr.get("merged_at")]
32+
33+
def get_release():
34+
url = f"{API_BASE}/repos/{REPO}/releases/tags/{RELEASE_TAG}"
35+
r = requests.get(url, headers=HEADERS)
36+
r.raise_for_status()
37+
return r.json()
38+
39+
def update_release_notes(release_id, body):
40+
url = f"{API_BASE}/repos/{REPO}/releases/{release_id}"
41+
data = {"body": body}
42+
r = requests.patch(url, headers=HEADERS, json=data)
43+
r.raise_for_status()
44+
45+
def delete_assets(asset_ids):
46+
for asset_id in asset_ids:
47+
url = f"{API_BASE}/repos/{REPO}/releases/assets/{asset_id}"
48+
r = requests.delete(url, headers=HEADERS)
49+
r.raise_for_status()
50+
print(f"🗑️ Deleted asset {asset_id}")
51+
52+
def upload_asset(upload_url, file_path):
53+
file_name = Path(file_path).name
54+
with open(file_path, "rb") as f:
55+
print(f"⬆️ Uploading {file_name}...")
56+
headers = HEADERS.copy()
57+
headers["Content-Type"] = "application/zip"
58+
r = requests.post(f"{upload_url}?name={file_name}", headers=headers, data=f)
59+
r.raise_for_status()
60+
61+
def main():
62+
# Ensure Git branches are up to date
63+
run(["git", "fetch", "origin", "develop", "main"])
64+
develop_sha = get_commit_sha("develop")
65+
main_sha = get_commit_sha("main")
66+
67+
print("🔍 Fetching merged PRs...")
68+
prs = fetch_merged_prs()
69+
new_prs = ""
70+
71+
for pr in prs:
72+
pr_number = pr["number"]
73+
pr_title = pr["title"]
74+
pr_author = pr["user"]["login"]
75+
merge_commit = pr["merge_commit_sha"]
76+
77+
if not is_ancestor(merge_commit, main_sha):
78+
new_prs += f"- PR [#{pr_number}] {pr_title} (@{pr_author})\n"
79+
80+
if not new_prs:
81+
print("✅ No new PRs to include.")
82+
return
83+
84+
timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")
85+
body = f"### Changes not yet in main:\n\n{new_prs}\n_Last updated: {timestamp}_"
86+
87+
release = get_release()
88+
release_id = release["id"]
89+
upload_url = release["upload_url"].split("{")[0]
90+
91+
update_release_notes(release_id, body)
92+
93+
print("📦 Deleting existing assets...")
94+
asset_ids = [a["id"] for a in release.get("assets", [])]
95+
delete_assets(asset_ids)
96+
97+
print("📦 Uploading new assets...")
98+
windows_jar = next(Path("droid-binary/target").glob("*win64-with-jre.zip"))
99+
generic_jar = next(Path("droid-binary/target").glob("*bin.zip"))
100+
upload_asset(upload_url, windows_jar)
101+
upload_asset(upload_url, generic_jar)
102+
103+
print("✅ Release updated successfully.")
104+
105+
if __name__ == "__main__":
106+
main()
Lines changed: 116 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,121 @@
11
import os
2+
import sys
23
import subprocess
34
import requests
4-
import datetime
5-
import json
6-
from pathlib import Path
7-
8-
REPO = "digital-preservation/droid"
9-
RELEASE_TAG = "develop"
10-
API_BASE = "https://api.github.com"
11-
TOKEN = os.environ["GITHUB_TOKEN"]
12-
HEADERS = {
13-
"Authorization": f"Bearer {TOKEN}",
14-
"Accept": "application/vnd.github+json"
15-
}
16-
17-
def run(cmd):
18-
return subprocess.check_output(cmd, text=True).strip()
19-
20-
def get_commit_sha(branch):
21-
return run(["git", "rev-parse", f"origin/{branch}"])
22-
23-
def is_ancestor(ancestor, descendant):
24-
return subprocess.call(["git", "merge-base", "--is-ancestor", ancestor, descendant]) == 0
25-
26-
def fetch_merged_prs():
27-
url = f"{API_BASE}/repos/{REPO}/pulls"
28-
params = {"state": "closed", "base": "develop", "per_page": 100}
29-
r = requests.get(url, headers=HEADERS, params=params)
30-
r.raise_for_status()
31-
return [pr for pr in r.json() if pr.get("merged_at")]
32-
33-
def get_release():
34-
url = f"{API_BASE}/repos/{REPO}/releases/tags/{RELEASE_TAG}"
35-
r = requests.get(url, headers=HEADERS)
36-
r.raise_for_status()
37-
return r.json()
38-
39-
def update_release_notes(release_id, body):
40-
url = f"{API_BASE}/repos/{REPO}/releases/{release_id}"
41-
data = {"body": body}
42-
r = requests.patch(url, headers=HEADERS, json=data)
43-
r.raise_for_status()
44-
45-
def delete_assets(asset_ids):
46-
for asset_id in asset_ids:
47-
url = f"{API_BASE}/repos/{REPO}/releases/assets/{asset_id}"
48-
r = requests.delete(url, headers=HEADERS)
49-
r.raise_for_status()
50-
print(f"🗑️ Deleted asset {asset_id}")
51-
52-
def upload_asset(upload_url, file_path):
53-
file_name = Path(file_path).name
54-
with open(file_path, "rb") as f:
55-
print(f"⬆️ Uploading {file_name}...")
56-
headers = HEADERS.copy()
57-
headers["Content-Type"] = "application/zip"
58-
r = requests.post(f"{upload_url}?name={file_name}", headers=headers, data=f)
59-
r.raise_for_status()
60-
61-
def main():
62-
# Ensure Git branches are up to date
63-
run(["git", "fetch", "origin", "develop", "main"])
64-
develop_sha = get_commit_sha("develop")
65-
main_sha = get_commit_sha("main")
66-
67-
print("🔍 Fetching merged PRs...")
68-
prs = fetch_merged_prs()
69-
new_prs = ""
5+
from datetime import datetime, timezone
6+
import glob
7+
8+
# === CONFIGURATION ===
9+
REPO = "digital-preservation/droid" # e.g., "octocat/Hello-World"
10+
ASSET_FILES = sorted(
11+
glob.glob("droid-binary/target/droid-binary-*-bin-win64-with-jre.zip") +
12+
glob.glob("droid-binary/target/droid-binary-*-bin.zip")
13+
)
14+
15+
if not ASSET_FILES:
16+
print("No matching files found for upload.")
17+
sys.exit(1)
18+
19+
TOKEN = os.getenv("GITHUB_TOKEN")
20+
21+
if not TOKEN:
22+
print("GITHUB_TOKEN environment variable not set.")
23+
sys.exit(1)
24+
25+
if len(sys.argv) != 2:
26+
print("Usage: python create_github_release_raw.py <release_version>")
27+
sys.exit(1)
28+
29+
RELEASE_VERSION = sys.argv[1]
30+
API_BASE = f"https://api.github.com/repos/{REPO}"
31+
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Accept": "application/vnd.github+json"}
32+
33+
# === FETCH RELEASES ===
34+
print("Fetching existing releases...")
35+
releases_resp = requests.get(f"{API_BASE}/releases", headers=HEADERS)
36+
releases_resp.raise_for_status()
37+
releases = releases_resp.json()
38+
39+
def parse_github_datetime(dt_str):
40+
# Format: "2023-12-01T10:00:00Z"
41+
return datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
42+
43+
last_release_date = datetime(1970, 1, 1, tzinfo=timezone.utc)
44+
for r in releases:
45+
if r["tag_name"] != RELEASE_VERSION and r["tag_name"] != 'develop':
46+
last_release_date = parse_github_datetime(r["created_at"])
47+
break
48+
49+
print(f"Last release date: {last_release_date.isoformat()}")
50+
51+
# === GET MERGED PRs SINCE LAST RELEASE ===
52+
print("Fetching merged pull requests...")
53+
merged_prs = []
54+
page = 1
55+
while True:
56+
resp = requests.get(
57+
f"{API_BASE}/pulls",
58+
headers=HEADERS,
59+
params={"state": "closed", "sort": "updated", "direction": "desc", "per_page": 100, "page": page}
60+
)
61+
resp.raise_for_status()
62+
prs = resp.json()
63+
if not prs:
64+
break
7065

7166
for pr in prs:
72-
pr_number = pr["number"]
73-
pr_title = pr["title"]
74-
pr_author = pr["user"]["login"]
75-
merge_commit = pr["merge_commit_sha"]
76-
77-
if not is_ancestor(merge_commit, main_sha):
78-
new_prs += f"- PR [#{pr_number}] {pr_title} (@{pr_author})\n"
79-
80-
if not new_prs:
81-
print("✅ No new PRs to include.")
82-
return
83-
84-
timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC")
85-
body = f"### Changes not yet in main:\n\n{new_prs}\n_Last updated: {timestamp}_"
86-
87-
release = get_release()
88-
release_id = release["id"]
89-
upload_url = release["upload_url"].split("{")[0]
90-
91-
update_release_notes(release_id, body)
92-
93-
print("📦 Deleting existing assets...")
94-
asset_ids = [a["id"] for a in release.get("assets", [])]
95-
delete_assets(asset_ids)
96-
97-
print("📦 Uploading new assets...")
98-
windows_jar = next(Path("droid-binary/target").glob("*win64-with-jre.zip"))
99-
generic_jar = next(Path("droid-binary/target").glob("*bin.zip"))
100-
upload_asset(upload_url, windows_jar)
101-
upload_asset(upload_url, generic_jar)
102-
103-
print("✅ Release updated successfully.")
104-
105-
if __name__ == "__main__":
106-
main()
67+
merged_at = pr.get("merged_at")
68+
if not merged_at:
69+
continue
70+
merged_dt = parse_github_datetime(merged_at)
71+
if merged_dt <= last_release_date:
72+
break
73+
if pr["user"]["login"].startswith("dependabot"):
74+
continue
75+
merged_prs.append(f"- #{pr['number']} {pr['title']}")
76+
77+
page += 1
78+
79+
release_notes = "\n".join(reversed(merged_prs)) or "No non-Dependabot PRs since last release."
80+
81+
# === CREATE RELEASE ===
82+
print(f"Creating GitHub release for {RELEASE_VERSION}...")
83+
release_resp = requests.post(
84+
f"{API_BASE}/releases",
85+
headers=HEADERS,
86+
json={
87+
"tag_name": RELEASE_VERSION,
88+
"name": f"Release {RELEASE_VERSION}",
89+
"body": release_notes,
90+
"draft": False,
91+
"prerelease": False
92+
}
93+
)
94+
release_resp.raise_for_status()
95+
release = release_resp.json()
96+
upload_url = release["upload_url"].split("{")[0]
97+
98+
# === UPLOAD FILES ===
99+
for file_path in ASSET_FILES:
100+
if not os.path.exists(file_path):
101+
print(f"File not found: {file_path}")
102+
continue
103+
104+
file_name = os.path.basename(file_path)
105+
print(f"Uploading {file_name}...")
106+
with open(file_path, "rb") as f:
107+
upload_resp = requests.post(
108+
f"{upload_url}?name={file_name}",
109+
headers={
110+
"Authorization": f"Bearer {TOKEN}",
111+
"Content-Type": "application/octet-stream"
112+
},
113+
data=f.read()
114+
)
115+
116+
if upload_resp.status_code == 201:
117+
print(f"Uploaded {file_name}")
118+
else:
119+
print(f"Failed to upload {file_name}: {upload_resp.status_code}\n{upload_resp.text}")
120+
121+
print(f"✅ Release {RELEASE_VERSION} created and assets uploaded.")

.github/scripts/settings.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
4+
<servers>
5+
<server>
6+
<id>ossrh</id>
7+
<username>${env.MAVEN_USERNAME}</username>
8+
<password>${env.MAVEN_CENTRAL_TOKEN}</password>
9+
</server>
10+
</servers>
11+
</settings>

.github/workflows/develop_release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
run: |
2929
mvn -V -B package -DnvdApiKey=${{ secrets.NVD_API_KEY }}
3030
pip install requests
31-
python .github/scripts/generate_release.py
31+
python .github/scripts/generate_develop_release.py
3232
3333
env:
3434
AWS_REGION: eu-west-2

0 commit comments

Comments
 (0)