Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 57s
deploy community / deploy (push) Successful in 1s
CI / MSI-installer (push) Successful in 38s
Forgejo 10 has no releases/latest/download/ URL, so the versionless second copy of the MSI in each release was useless. Releases now carry the MSI once; get-latest-msi.ps1 finds it through GET /releases/latest (no login). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
69 lines
2.8 KiB
Python
Executable file
69 lines
2.8 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.",
|
|
"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()
|