Skip to content

release: prepare 0.2.1 and harden retries #2

release: prepare 0.2.1 and harden retries

release: prepare 0.2.1 and harden retries #2

Workflow file for this run

name: release
# Trigger a release whenever the version file is edited on the main branch, or
# manually rerun the current main release after fixing a workflow failure.
on:
workflow_dispatch:
push:
branches: [main]
paths:
- "plexmuxy/VERSION"
permissions:
contents: write
jobs:
release:
if: github.ref == 'refs/heads/main'
runs-on: windows-latest
env:
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Read version from VERSION file
id: version
shell: pwsh
run: |
$version = (Get-Content plexmuxy/VERSION).Trim()
echo "version=$version" >> $env:GITHUB_OUTPUT
- run: python -m pip install -e ".[dev,build,gui]" cyclonedx-bom
- name: Install Inno Setup
run: choco install innosetup -y --no-progress
- run: python -m pytest -m "not integration" --basetemp .pytest-tmp/release -o cache_dir=.pytest-tmp/cache
- run: python -m build
- name: Generate PyInstaller version-info resource
shell: python
run: |
import re, pathlib
version = pathlib.Path("plexmuxy/VERSION").read_text(encoding="utf-8").strip()
m = re.match(r"^(\d+)\.(\d+)\.(\d+)", version)
if not m:
raise SystemExit(f"Cannot parse version '{version}' as MAJOR.MINOR.PATCH")
filevers = f"{int(m[1])}, {int(m[2])}, {int(m[3])}, 0"
info = f"""# UTF-8
#
VSVersionInfo(
ffi=FixedFileInfo(
filevers=({filevers}),
prodvers=({filevers}),
mask=0x3f,
flags=0x0,
OS=0x40004,
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
StringFileInfo([
StringTable(
u'040904B0',
[StringStruct(u'CompanyName', u'PlexMuxy contributors'),
StringStruct(u'FileDescription', u'PlexMuxy'),
StringStruct(u'FileVersion', u'{version}'),
StringStruct(u'InternalName', u'plexmuxy'),
StringStruct(u'LegalCopyright', u'MIT License'),
StringStruct(u'OriginalFilename', u'plexmuxy-gui.exe'),
StringStruct(u'ProductName', u'PlexMuxy'),
StringStruct(u'ProductVersion', u'{version}')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)
"""
pathlib.Path("packaging").mkdir(exist_ok=True)
pathlib.Path("packaging/version_info.txt").write_text(info, encoding="utf-8")
- run: python -m PyInstaller --clean --noconfirm plexmuxy-cli.spec
- run: python -m PyInstaller --clean --noconfirm plexmuxy-gui.spec
- name: Sign portable executables when a certificate is configured
if: env.WINDOWS_CERTIFICATE_BASE64 != ''
shell: pwsh
run: |
$certificate = Join-Path $env:RUNNER_TEMP "plexmuxy-signing.pfx"
[IO.File]::WriteAllBytes($certificate, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_BASE64))
try {
Get-ChildItem dist -Recurse -Filter *.exe | ForEach-Object {
signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $certificate /p $env:WINDOWS_CERTIFICATE_PASSWORD $_.FullName
}
} finally {
Remove-Item -Force -ErrorAction SilentlyContinue $certificate
}
- name: Build Windows installer
shell: pwsh
run: iscc "/DMyAppVersion=${{ steps.version.outputs.version }}" packaging/plexmuxy.iss
- name: Sign installer when a certificate is configured
if: env.WINDOWS_CERTIFICATE_BASE64 != ''
shell: pwsh
run: |
$certificate = Join-Path $env:RUNNER_TEMP "plexmuxy-signing.pfx"
[IO.File]::WriteAllBytes($certificate, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_BASE64))
try {
Get-ChildItem dist -File -Filter *-setup.exe | ForEach-Object {
signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $certificate /p $env:WINDOWS_CERTIFICATE_PASSWORD $_.FullName
}
} finally {
Remove-Item -Force -ErrorAction SilentlyContinue $certificate
}
- name: Rename release artifacts
shell: pwsh
run: |
$version = "${{ steps.version.outputs.version }}"
Move-Item -Force "dist/plexmuxy-gui/plexmuxy-gui.exe" "dist/PlexMuxy-windows-amd64-$version.exe"
Move-Item -Force "dist/plexmuxy-$version-windows-x64-setup.exe" "dist/PlexMuxy-windows-amd64-$version-setup.exe"
- name: Generate release SBOM
run: python -m cyclonedx_py environment --output-format JSON --output-file dist/plexmuxy-sbom.cdx.json
- name: Generate checksums and manifest
shell: pwsh
run: |
$artifacts = Get-ChildItem dist -File
$manifest = [ordered]@{
version = "${{ steps.version.outputs.version }}"
commit = $env:GITHUB_SHA
python = (python --version)
artifacts = @($artifacts | ForEach-Object { [ordered]@{ name = $_.Name; size = $_.Length; sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLower() } })
}
$manifest | ConvertTo-Json -Depth 5 | Set-Content -Encoding utf8 dist/release-manifest.json
Get-ChildItem dist -File | Where-Object Name -ne 'SHA256SUMS.txt' | ForEach-Object {
$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLower()
"$hash $($_.Name)" | Add-Content -Encoding ascii dist/SHA256SUMS.txt
}
- name: Create or refresh GitHub release
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$version = "${{ steps.version.outputs.version }}"
$tag = "v$version"
$assets = @(Get-ChildItem dist -File | ForEach-Object FullName)
$tagCommit = gh api "repos/$env:GITHUB_REPOSITORY/commits/$tag" --jq .sha 2>$null
$tagExists = $LASTEXITCODE -eq 0
if ($tagExists -and $tagCommit.Trim() -ne $env:GITHUB_SHA) {
throw "Tag $tag already points to $($tagCommit.Trim()), not $env:GITHUB_SHA. Bump plexmuxy/VERSION instead of moving a published tag."
}
gh release view $tag *> $null
$releaseExists = $LASTEXITCODE -eq 0
if ($releaseExists) {
gh release upload $tag $assets --clobber
gh release edit $tag --title "PlexMuxy $tag"
} elseif ($tagExists) {
gh release create $tag $assets --verify-tag --generate-notes --title "PlexMuxy $tag"
} else {
gh release create $tag $assets --target $env:GITHUB_SHA --generate-notes --title "PlexMuxy $tag"
}
publish-pypi:
name: Publish source and wheel with PyPI Trusted Publishing
needs: release
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/plexmuxy
permissions:
id-token: write
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- run: python -m pip install build
- run: python -m build
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
with:
skip-existing: true