diff --git a/.github/workflows/build_updatexml.yml b/.github/workflows/build_updatexml.yml index e69de29..8801fdc 100644 --- a/.github/workflows/build_updatexml.yml +++ b/.github/workflows/build_updatexml.yml @@ -0,0 +1,158 @@ +name: Generate updates.xml from template + +on: + release: + types: [published] + + workflow_dispatch: + inputs: + asset_download_url: + description: "Direct download URL to the release ZIP asset. Required for manual runs." + required: false + default: "" + asset_name: + description: "Filename to save the downloaded ZIP as. Required when asset_download_url is provided." + required: false + default: "" + +permissions: + contents: read + +jobs: + build-updates-xml: + name: Generate updates.xml artifact + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Validate template exists + shell: bash + run: | + set -euo pipefail + TPL="templates/update_template.xml" + if [ ! -f "$TPL" ]; then + echo "Template not found: $TPL" >&2 + exit 1 + fi + + - name: Resolve ZIP download URL and name + id: resolve + shell: bash + env: + MANUAL_URL: ${{ github.event.inputs.asset_download_url }} + MANUAL_NAME: ${{ github.event.inputs.asset_name }} + run: | + set -euo pipefail + + EVENT_NAME="${{ github.event_name }}" + + if [ "$EVENT_NAME" = "release" ]; then + ASSET_URL=$(jq -r '.release.assets[] | select(.name | test("\\.zip$"; "i")) | .browser_download_url' "$GITHUB_EVENT_PATH" | head -n 1) + ASSET_NAME=$(jq -r '.release.assets[] | select(.name | test("\\.zip$"; "i")) | .name' "$GITHUB_EVENT_PATH" | head -n 1) + + if [ -z "$ASSET_URL" ] || [ "$ASSET_URL" = "null" ] || [ -z "$ASSET_NAME" ] || [ "$ASSET_NAME" = "null" ]; then + echo "No .zip release asset found on the published release" >&2 + exit 1 + fi + + echo "download_url=$ASSET_URL" >> "$GITHUB_OUTPUT" + echo "asset_name=$ASSET_NAME" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # workflow_dispatch path + if [ -n "$MANUAL_URL" ]; then + if [ -z "$MANUAL_NAME" ]; then + echo "asset_name is required when asset_download_url is provided" >&2 + exit 1 + fi + echo "download_url=$MANUAL_URL" >> "$GITHUB_OUTPUT" + echo "asset_name=$MANUAL_NAME" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "Manual run requires asset_download_url + asset_name." >&2 + exit 1 + + - name: Download ZIP asset + shell: bash + run: | + set -euo pipefail + curl -L \ + -H "Authorization: Bearer ${{ github.token }}" \ + -H "Accept: application/octet-stream" \ + "${{ steps.resolve.outputs.download_url }}" \ + -o "${{ steps.resolve.outputs.asset_name }}" + + - name: Calculate SHA256 + id: sha + shell: bash + run: | + set -euo pipefail + SHA256=$(sha256sum "${{ steps.resolve.outputs.asset_name }}" | awk '{print $1}') + if [ -z "$SHA256" ]; then + echo "Failed to compute SHA256" >&2 + exit 1 + fi + echo "sha256=$SHA256" >> "$GITHUB_OUTPUT" + + - name: Generate updates.xml from template + shell: bash + env: + TEMPLATE_PATH: templates/update_template.xml + OUTPUT_PATH: dist/updates.xml + DOWNLOAD_URL: ${{ steps.resolve.outputs.download_url }} + SHA256: ${{ steps.sha.outputs.sha256 }} + run: | + set -euo pipefail + + python3 - <<'PY' +import os +import xml.etree.ElementTree as ET +from pathlib import Path + +template_path = Path(os.environ["TEMPLATE_PATH"]) +out_path = Path(os.environ["OUTPUT_PATH"]) +download_url = os.environ["DOWNLOAD_URL"].strip() +sha256 = os.environ["SHA256"].strip() + +if not download_url: + raise SystemExit("Missing DOWNLOAD_URL") +if not sha256: + raise SystemExit("Missing SHA256") + +# Parse as XML and set the fields directly to avoid brittle string matching. +xml_text = template_path.read_text(encoding="utf-8") +root = ET.fromstring(xml_text) + +# Support templates that either use or are nested differently. +# Expected paths from your template: +# /updates/update/downloads/downloadurl +# /updates/update/downloads/sha256 + +downloadurl_el = root.find("./update/downloads/downloadurl") +sha256_el = root.find("./update/downloads/sha256") + +if downloadurl_el is None: + raise SystemExit("Template missing ./update/downloads/downloadurl") +if sha256_el is None: + raise SystemExit("Template missing ./update/downloads/sha256") + +downloadurl_el.text = download_url +sha256_el.text = sha256 + +out_path.parent.mkdir(parents=True, exist_ok=True) +ET.ElementTree(root).write(out_path, encoding="utf-8", xml_declaration=True) + +print(f"Wrote: {out_path}") +PY + + - name: Upload updates.xml artifact + uses: actions/upload-artifact@v4 + with: + name: updates-xml + path: dist/updates.xml + if-no-files-found: error + retention-days: 7