-
Notifications
You must be signed in to change notification settings - Fork 27
/
write_appcast.py
154 lines (120 loc) · 5.62 KB
/
write_appcast.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import base64
import os
import subprocess
import tempfile
from datetime import datetime, timezone
import markdown as markdown
import requests
from lxml import etree as ET
access_token = os.getenv("APPCAST_ACCESS_TOKEN")
def download_file(url, save_path):
response = requests.get(url)
response.raise_for_status()
with open(save_path, 'wb') as file:
file.write(response.content)
def get_latest_git_tag():
try:
git_tag = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode().strip()
return git_tag
except subprocess.CalledProcessError:
return None
def get_current_time():
now = datetime.now(timezone.utc).astimezone()
formatted = now.strftime('%Y%m%d %H:%M:%S')
tz_offset = int(now.utcoffset().total_seconds() / 3600)
formatted += '%+d' % tz_offset
return formatted
def add_release(filename, version, sparkle_signature):
# Load the XML file
tree = ET.parse(filename)
root = tree.getroot()
# Define the new item data
new_item_data = {
'title': f'Version {version}',
'releaseNotesLink': f'https://christofmuc.github.io/appcasts/KnobKraft-Orm/{version}.html',
'pubDate': get_current_time(),
'url': f'https://github.com/christofmuc/KnobKraft-orm/releases/download/{version}/knobkraft_orm_setup_{version}.exe',
'version': f'{version}',
'dsaSignature': sparkle_signature,
'installerArguments': '/SILENT /SP- /NOICONS /restartapplications',
'length': '0',
'type': 'application/octet-stream'
}
# Create a new item
channel = root.find('channel')
new_item = ET.Element('item')
channel.insert(0, new_item) # insert the new item at the beginning
# Add sub-elements to the new item
ET.SubElement(new_item, 'title').text = new_item_data['title']
ET.SubElement(new_item, '{http://www.andymatuschak.org/xml-namespaces/sparkle}releaseNotesLink').text = new_item_data['releaseNotesLink']
ET.SubElement(new_item, 'pubDate').text = new_item_data['pubDate']
enclosure = ET.SubElement(new_item, 'enclosure')
enclosure.set('url', new_item_data['url'])
enclosure.set('{http://www.andymatuschak.org/xml-namespaces/sparkle}version', new_item_data['version'])
enclosure.set('{http://www.andymatuschak.org/xml-namespaces/sparkle}dsaSignature', new_item_data['dsaSignature'])
enclosure.set('{http://www.andymatuschak.org/xml-namespaces/sparkle}installerArguments', new_item_data['installerArguments'])
enclosure.set('length', new_item_data['length'])
enclosure.set('type', new_item_data['type'])
# Pretty-print the entire tree
ET.indent(tree, space=" ")
# Write the updated XML back to the file
tree.write(filename, encoding='utf-8', xml_declaration=True, pretty_print=True)
return ET.tostring(tree, encoding='utf-8', xml_declaration=True)
def get_file_sha(repo_owner, repo_name, file_path, access_token):
# Create the API URL to get the file information
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/contents/{file_path}"
# Prepare the headers for the API request
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/vnd.github.v3+json"
}
# Send the API request to get the file information
response = requests.get(api_url, headers=headers)
response.raise_for_status()
# Get the SHA of the file
file_info = response.json()
sha = file_info.get('sha')
return sha
def upload_to_github(updated_xml, repo_owner, repo_name, file_path, is_update: bool):
# Encode the XML content as base64
base64_content = base64.b64encode(updated_xml).decode().strip()
# Create the API URL to update the file
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/contents/{file_path}"
# Prepare the headers and data for the API request
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/vnd.github.v3+json"
}
data = {
"message": f"Update {file_path}",
"content": base64_content,
}
if is_update:
# The file already exists
data["sha"] = get_file_sha(repo_owner, repo_name, file_path, access_token)
# Send the API request to update the file
response = requests.put(api_url, json=data, headers=headers)
response.raise_for_status()
def convert_markdown_to_html(markdown_file):
# Read the Markdown file
with open(markdown_file, 'r', encoding='utf-8') as file:
markdown_text = file.read()
# Convert Markdown to HTML
return markdown.markdown(markdown_text)
if __name__ == "__main__":
new_version = get_latest_git_tag()
print(f"Latest Git tag used as appcast version: {new_version}")
if new_version is None:
raise Exception("Can't create release without version tag!")
# Read signature file
with open("update.sig", 'r') as file:
sparkle_signature = file.read()
print(f"Got sparkle signature as {sparkle_signature}")
with tempfile.TemporaryDirectory() as tmpdir:
tmpfile = os.path.join(tmpdir, "appcast.xml")
download_file("https://raw.githubusercontent.com/christofmuc/appcasts/master/KnobKraft-Orm/appcast.xml", tmpfile)
new_file = add_release(tmpfile, new_version, sparkle_signature)
upload_to_github(new_file, "christofmuc", "appcasts", "KnobKraft-Orm/appcast.xml", True)
release_notes = os.path.join("release_notes", f"{new_version}.md")
release_notes_as_html = convert_markdown_to_html(release_notes)
upload_to_github(release_notes_as_html.encode('utf-8'), "christofmuc", "appcasts", f"KnobKraft-Orm/{new_version}.html", False)