Skip to content
182 changes: 182 additions & 0 deletions .github/workflows/update-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
name: Update manifest.json on release

on:
release:
types: [published, edited, deleted]

permissions:
contents: write

concurrency:
group: update-manifest
cancel-in-progress: false

jobs:
update-manifest:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }}

- name: Extract version from release tag
id: version
run: |
TAG="${{ github.event.release.tag_name }}"
# Strip leading 'v' if present
VERSION="${TAG#v}"
# Ensure four-part version (append .0 if needed)
DOTS=$(echo "$VERSION" | tr -cd '.' | wc -c | tr -d ' ')
if [ "$DOTS" -eq 2 ]; then
VERSION="${VERSION}.0"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Version: $VERSION"

# ── Delete: remove the version entry ──────────────────────────────

- name: Remove version from manifest.json
if: github.event.action == 'deleted'
run: |
jq --arg ver "${{ steps.version.outputs.version }}" \
'.[0].versions |= map(select(.version != $ver))' \
manifest.json > manifest.tmp && mv manifest.tmp manifest.json

echo "Removed version ${{ steps.version.outputs.version }} from manifest.json"

# ── Published / Edited: upsert the version entry ──────────────────

- name: Extract targetAbi from .csproj
if: github.event.action != 'deleted'
id: abi
run: |
ABI=$(grep -oP 'Jellyfin\.Controller"[^>]*Version="\K[^"]+' backend/Moonfin.Server.csproj)
echo "targetAbi=${ABI}.0" >> "$GITHUB_OUTPUT"
echo "Target ABI: ${ABI}.0"

- name: Download release zip asset
if: github.event.action != 'deleted'
id: asset
env:
GH_TOKEN: ${{ github.token }}
RELEASE_ASSETS: ${{ toJSON(github.event.release.assets) }}
run: |
ASSET_URL=$(echo "$RELEASE_ASSETS" | jq -r '.[] | select(.name | startswith("Moonfin.Server-") and endswith(".zip")) | .browser_download_url' | head -1)

if [ -z "$ASSET_URL" ]; then
echo "::error::No Moonfin.Server-*.zip asset found on this release"
exit 1
fi

ASSET_NAME=$(basename "$ASSET_URL")
echo "Downloading $ASSET_NAME from $ASSET_URL"
curl -fSL -o "$ASSET_NAME" "$ASSET_URL"

echo "asset_name=$ASSET_NAME" >> "$GITHUB_OUTPUT"
echo "source_url=$ASSET_URL" >> "$GITHUB_OUTPUT"

- name: Compute MD5 checksum
if: github.event.action != 'deleted'
id: checksum
run: |
CHECKSUM=$(md5sum "${{ steps.asset.outputs.asset_name }}" | awk '{print toupper($1)}')
echo "checksum=$CHECKSUM" >> "$GITHUB_OUTPUT"
echo "MD5: $CHECKSUM"

- name: Format timestamp
if: github.event.action != 'deleted'
id: timestamp
env:
RELEASE_CREATED_AT: ${{ github.event.release.created_at }}
run: |
TIMESTAMP=$(date -u -d "$RELEASE_CREATED_AT" +"%Y-%m-%dT%H:%M:%S")
echo "timestamp=$TIMESTAMP" >> "$GITHUB_OUTPUT"
echo "Timestamp: $TIMESTAMP"

- name: Sanitize changelog
if: github.event.action != 'deleted'
id: changelog
env:
RELEASE_BODY: ${{ github.event.release.body }}
run: |
# Write release body to file to preserve multi-line content
printf '%s' "$RELEASE_BODY" > /tmp/raw_changelog.txt

# Convert markdown release body to clean plain text for Jellyfin manifest:
# 1. Strip carriage returns
# 2. Remove badge images: [![alt](img)](url)
# 3. Remove inline images: ![alt](url)
# 4. Convert links to text: [text](url) → text
# 5. Strip bold markers: **text** → text
# 6. Strip inline code: `text` → text
# 7. Strip header prefixes: ## Title → Title
# 8. Remove horizontal rules: ---
# 9. Trim trailing whitespace
# 10. Remove leading/trailing blank lines
# 11. Collapse consecutive blank lines
sed -i 's/\r$//' /tmp/raw_changelog.txt
cat /tmp/raw_changelog.txt \
| sed -E 's/\[!\[[^]]*\]\([^)]*\)\]\([^)]*\)//g' \
| sed -E 's/!\[[^]]*\]\([^)]*\)//g' \
| sed -E 's/\[([^]]*)\]\([^)]*\)/\1/g' \
| sed -E 's/\*\*([^*]*)\*\*/\1/g' \
| sed -E 's/`([^`]*)`/\1/g' \
| sed -E 's/^#{1,6} +//' \
| sed -E 's/[[:space:]]+$//' \
| sed -E '/^---$/d' \
| sed -E '/^$/N;/^\n$/d' \
| sed '/./,$!d' \
> /tmp/changelog.txt

echo "Sanitized changelog:"
cat /tmp/changelog.txt

- name: Upsert version in manifest.json
if: github.event.action != 'deleted'
run: |
CHANGELOG=$(cat /tmp/changelog.txt)

jq --arg ver "${{ steps.version.outputs.version }}" \
--arg abi "${{ steps.abi.outputs.targetAbi }}" \
--arg url "${{ steps.asset.outputs.source_url }}" \
--arg sum "${{ steps.checksum.outputs.checksum }}" \
--arg time "${{ steps.timestamp.outputs.timestamp }}" \
--arg log "$CHANGELOG" \
'.[0].versions |= (
map(select(.version != $ver)) |
[{
version: $ver,
changelog: $log,
targetAbi: $abi,
sourceUrl: $url,
checksum: $sum,
timestamp: $time,
dependencies: []
}] + .
)' \
manifest.json > manifest.tmp && mv manifest.tmp manifest.json

echo "Upserted version ${{ steps.version.outputs.version }} in manifest.json"

# ── Commit ────────────────────────────────────────────────────────

- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add manifest.json

ACTION="${{ github.event.action }}"
VERSION="${{ steps.version.outputs.version }}"

if [ "$ACTION" = "deleted" ]; then
MSG="chore: remove v${VERSION} from manifest.json"
else
MSG="chore: update manifest.json for v${VERSION}"
fi

git diff --cached --quiet && echo "No changes to commit" && exit 0
git commit -m "$MSG"
git pull --rebase
git push