name: Release # release.sh commits a manifest.json whose sourceUrl points at the release # asset, so the asset has to exist the moment the tag is pushed — otherwise # every server that has the repository added downloads a 404 (see #19). # This job owns the artifact: release.sh no longer builds one, because a ZIP # built on the tagging machine is never byte-identical to the published one and # its checksum described bytes nobody could download (#49). on: push: tags: ["v*"] permissions: contents: write env: DOTNET_NOLOGO: "1" DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1" DOTNET_CLI_TELEMETRY_OPTOUT: "1" jobs: release: runs-on: ubuntu-latest timeout-minutes: 15 steps: # The annotated tag's own message is the fallback for the release notes, # and it only arrives with the tag object itself. - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-dotnet@v4 with: dotnet-version: "10.0.x" - name: Cache NuGet packages uses: actions/cache@v4 with: path: ~/.nuget/packages key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Build.props') }} restore-keys: nuget-${{ runner.os }}- - name: Check the tag against meta.json run: | meta=$(python3 -c "import json;print(json.load(open('Jellyfin.Plugin.Chaosflix/meta.json'))['version'])") tagged="${GITHUB_REF_NAME#v}" if [[ "${meta%.*}" != "$tagged" ]]; then echo "::warning::tag ${GITHUB_REF_NAME} does not match meta.json version ${meta}" fi # Same output as release.sh: publish to ./artifacts, drop meta.json next # to the DLL, and zip exactly those two files flat. - name: Build the plugin run: | dotnet publish Jellyfin.Plugin.Chaosflix/Jellyfin.Plugin.Chaosflix.csproj -c Release -o ./artifacts cp Jellyfin.Plugin.Chaosflix/meta.json artifacts/ - name: Create the ZIP id: zip run: | zip_name="chaosflix-jellyfin-${GITHUB_REF_NAME}.zip" (cd artifacts && zip -j "../${zip_name}" Jellyfin.Plugin.Chaosflix.dll meta.json) md5=$(md5sum "${zip_name}" | cut -d' ' -f1) echo "name=${zip_name}" >> "$GITHUB_OUTPUT" echo "md5=${md5}" >> "$GITHUB_OUTPUT" { echo "### ${GITHUB_REF_NAME}" echo echo "- asset: \`${zip_name}\`" echo "- md5: \`${md5}\` — recorded as \`checksum\` in manifest.json" } >> "$GITHUB_STEP_SUMMARY" # release.sh writes release-notes-v.md but .gitignore keeps it # out of the commit, so in practice the tag message is what is used. - name: Collect the release notes id: notes run: | notes_file="release-notes-${GITHUB_REF_NAME}.md" if [[ -s "${notes_file}" ]]; then cp "${notes_file}" notes.md else git tag -l --format='%(contents)' "${GITHUB_REF_NAME}" > notes.md fi if [[ ! -s notes.md ]]; then echo "Release ${GITHUB_REF_NAME}" > notes.md fi # Re-runnable on purpose: a failure in a later step used to be # unrecoverable, because re-running the job started here and gh refused an # existing release, leaving the checksum unrecorded (#100). - name: Create or update the GitHub release env: GH_TOKEN: ${{ github.token }} ASSET: ${{ steps.zip.outputs.name }} run: | if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then echo "release ${GITHUB_REF_NAME} exists; uploading the asset again" gh release upload "${GITHUB_REF_NAME}" "${ASSET}" --clobber else gh release create "${GITHUB_REF_NAME}" "${ASSET}" \ --title "${GITHUB_REF_NAME}" \ --notes-file notes.md fi # The checksum is taken from the asset as it was downloaded back from the # release, and it lands on the default branch rather than on the tag: a # tag is immutable, and what servers read is manifest.json on that branch. - name: Record the checksum in manifest.json env: GH_TOKEN: ${{ github.token }} ASSET: ${{ steps.zip.outputs.name }} TAG: ${{ github.ref_name }} BRANCH: ${{ github.event.repository.default_branch }} run: | git fetch origin "${BRANCH}" git checkout "${BRANCH}" gh release download "${TAG}" --pattern "${ASSET}" --dir published --clobber MD5=$(md5sum "published/${ASSET}" | cut -d' ' -f1) && export MD5 rm -rf published python3 - <<'PYTHON' import json, os, sys with open("manifest.json") as f: data = json.load(f) wanted = os.environ["TAG"].lstrip("v") for entry in data[0]["versions"]: if entry["version"] == wanted or entry["version"].startswith(wanted + "."): entry["checksum"] = os.environ["MD5"] break else: sys.exit(f"manifest.json has no entry for {wanted}") with open("manifest.json", "w") as f: json.dump(data, f, indent=2) f.write("\n") PYTHON if git diff --quiet manifest.json; then echo "manifest.json already carries this checksum" exit 0 fi git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git add manifest.json git commit -m "release: record the ${TAG} checksum [skip ci]" git push origin "HEAD:${BRANCH}"