sqlinsight-pro/installer/publish-release.py
Søren Eggert Lundsteen Olsen a66e3f2b3b Review 1.3: upgrade note on AX findings to the hub (now opt-in per database)
1.2 sent Dynamics AX findings to the hub with the rest; 1.3 sends them only for
databases where AX hub sharing is ticked, default off, so sharing stops at the
upgrade until it is turned on. docs/opdatering.md gets an upgrade section that
says so first, with the other 1.3 changes, and the release text points to it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00

69 lines
2.9 KiB
Python
Executable file

#!/usr/bin/env python3
"""Attaches the MSI to a Forgejo release for the current tag (created if missing). Run by ci.yml on tags v*.
publish-release.py dist/SQLInsight-Pro-1.2.0-x64.msi
Environment (set by Forgejo Actions): GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_REF_NAME, and TOKEN with write
access to the repository. Forgejo has no .../releases/latest/download/ URL; installer/get-latest-msi.ps1 finds the
newest MSI through the API (GET /repos/{repo}/releases/latest) instead.
"""
import json
import os
import sys
import urllib.error
import urllib.request
import uuid
SERVER = os.environ["GITHUB_SERVER_URL"].rstrip("/")
REPO = os.environ["GITHUB_REPOSITORY"]
TAG = os.environ["GITHUB_REF_NAME"]
TOKEN = os.environ["TOKEN"]
API = f"{SERVER}/api/v1/repos/{REPO}"
def call(method, url, body=None, content_type="application/json"):
data = json.dumps(body).encode() if isinstance(body, dict) else body
request = urllib.request.Request(url, data=data, method=method, headers={
"Authorization": f"token {TOKEN}", "Accept": "application/json", "Content-Type": content_type,
})
try:
with urllib.request.urlopen(request, timeout=300) as response:
text = response.read().decode()
return json.loads(text) if text else {}
except urllib.error.HTTPError as error:
if error.code == 404 and method == "GET":
return None
sys.exit(f"{method} {url}: {error.code} {error.read().decode()[:300]}")
def upload(release, path, name):
for asset in release.get("assets") or []:
if asset["name"] == name: # a re-run of the same tag replaces the file
call("DELETE", f"{API}/releases/{release['id']}/assets/{asset['id']}")
boundary = uuid.uuid4().hex
with open(path, "rb") as f:
content = f.read()
body = (f"--{boundary}\r\nContent-Disposition: form-data; name=\"attachment\"; filename=\"{name}\"\r\n"
f"Content-Type: application/octet-stream\r\n\r\n").encode() + content + f"\r\n--{boundary}--\r\n".encode()
call("POST", f"{API}/releases/{release['id']}/assets?name={name}", body, f"multipart/form-data; boundary={boundary}")
print(f"vedhæftet {name} ({len(content) // 1024 // 1024} MB)")
def main():
if len(sys.argv) != 2:
sys.exit(__doc__)
msi = sys.argv[1]
release = call("GET", f"{API}/releases/tags/{TAG}")
if release is None:
release = call("POST", f"{API}/releases", {
"tag_name": TAG, "name": f"SQLInsight Pro {TAG.removeprefix('v')}",
"body": "MSI-installer til Windows (agent og web). Se docs/installation.md, og læs docs/opdatering.md (Ændringer ved opgradering) før en opgradering.",
"draft": False, "prerelease": "-" in TAG,
})
print(f"release {TAG} oprettet")
upload(release, msi, os.path.basename(msi))
print(f"{SERVER}/{REPO}/releases/tag/{TAG}")
if __name__ == "__main__":
main()