@@ -10,6 +10,11 @@ name: Publish Release (Update Appcast)
1010on :
1111 release :
1212 types : [published]
13+ workflow_dispatch :
14+ inputs :
15+ tag :
16+ description : ' Release tag to process (e.g. v2.2.0)'
17+ required : true
1318
1419jobs :
1520 publish :
2732 - name : Extract version and build info
2833 id : info
2934 run : |
30- TAG="${{ github.event.release.tag_name }}"
35+ TAG="${{ github.event.release.tag_name || inputs.tag }}"
3136 # Get build number from the project file in main
3237 BUILD=$(grep -m1 'CURRENT_PROJECT_VERSION' \
3338 "Volume Control.xcodeproj/project.pbxproj" \
3742 echo "build=$BUILD" >> "$GITHUB_OUTPUT"
3843 echo "zip=$ZIP" >> "$GITHUB_OUTPUT"
3944
45+ - name : Download Sparkle tools
46+ run : |
47+ # Fetch the browser_download_url for Sparkle-for-Swift-Package-Manager.zip
48+ # from the latest Sparkle release
49+ DOWNLOAD_URL=$(curl -fsSL \
50+ "https://api.github.com/repos/sparkle-project/Sparkle/releases/latest" \
51+ | python3 -c "
52+ import sys, json
53+ assets = json.load(sys.stdin)['assets']
54+ url = next((a['browser_download_url'] for a in assets
55+ if 'Swift-Package-Manager' in a['name']), None)
56+ if not url :
57+ raise SystemExit('ERROR : Sparkle-for-Swift-Package-Manager.zip not found in latest release')
58+ print(url)
59+ " )
60+ echo " Downloading: $DOWNLOAD_URL"
61+ curl -fsSL "$DOWNLOAD_URL" -o /tmp/sparkle-spm.zip
62+ unzip -q /tmp/sparkle-spm.zip -d /tmp/sparkle-spm
63+ TOOL=$(find /tmp/sparkle-spm -name generate_appcast | head -1)
64+ if [[ -z "$TOOL" ]]; then
65+ echo "ERROR : generate_appcast not found in archive"
66+ find /tmp/sparkle-spm -type f | head -20
67+ exit 1
68+ fi
69+ mkdir -p bin
70+ cp "$TOOL" bin/generate_appcast
71+ chmod +x bin/generate_appcast
72+ echo "Using generate_appcast from : $TOOL"
73+
4074 - name : Download ZIP from release
4175 env :
4276 GH_TOKEN : ${{ github.token }}
@@ -83,43 +117,7 @@ jobs:
83117
84118 # Inject the HTML as <description><![CDATA[…]]></description>
85119 # into the <item> that matches the current build number
86- python3 - "$BUILD" 'Releases/VolumeControlCast.xml' /tmp/notes.html << 'PYEOF'
87- import sys, re
88-
89- build, appcast_path, notes_path = sys.argv[1], sys.argv[2], sys.argv[3]
90-
91- with open(notes_path, encoding='utf-8') as f :
92- notes_html = f.read().strip()
93-
94- with open(appcast_path, encoding='utf-8') as f :
95- content = f.read()
96-
97- desc_tag = '<description><![CDATA[\n' + notes_html + '\n ]]></description>'
98-
99- item_re = re.compile(
100- r'(<item>.*?<sparkle:version>' + re.escape(build) + r'</sparkle:version>.*?)(</item>)',
101- re.DOTALL
102- )
103-
104- def inject(m) :
105- inner = m.group(1)
106- if '<description>' in inner :
107- inner = re.sub(r'<description>.*?</description>', desc_tag, inner, flags=re.DOTALL)
108- else :
109- inner += '\n ' + desc_tag + '\n '
110- return inner + m.group(2)
111-
112- new_content = item_re.sub(inject, content)
113-
114- if new_content == content :
115- print(f'WARNING : no <item> found for build {build}', file=sys.stderr)
116- sys.exit(1)
117-
118- with open(appcast_path, 'w', encoding='utf-8') as f :
119- f.write(new_content)
120-
121- print(f'Release notes injected for build {build}.')
122- PYEOF
120+ python3 bin/inject_appcast_notes.py "$BUILD" 'Releases/VolumeControlCast.xml' /tmp/notes.html
123121
124122 - name : Commit and push changes to main
125123 env :
0 commit comments