mirror of
https://github.com/darkzoul5/YoutubePlaylistSync.git
synced 2026-07-03 04:23:59 +03:00
e97c419b47
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
331 lines
11 KiB
YAML
331 lines
11 KiB
YAML
name: Build & Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: "Tag to create (e.g., v2.0.0)"
|
|
required: true
|
|
default: "v2.0.0"
|
|
type: string
|
|
force_tag:
|
|
description: "Recreate tag if it already exists"
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
draft:
|
|
description: "Create release as draft"
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
tag:
|
|
name: Create tag
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Validate input tag
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${{ inputs.tag }}"
|
|
if [[ -z "$TAG" ]]; then
|
|
echo "tag is required" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "$TAG" != v* ]]; then
|
|
echo "tag must start with 'v' (got: $TAG)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Create/push tag
|
|
shell: bash
|
|
env:
|
|
TAG: ${{ inputs.tag }}
|
|
FORCE: ${{ inputs.force_tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
|
if [[ "${FORCE}" != "true" ]]; then
|
|
echo "Tag ${TAG} already exists. Re-run with force_tag=true to recreate." >&2
|
|
exit 1
|
|
fi
|
|
git tag -d "${TAG}" || true
|
|
git push origin ":refs/tags/${TAG}" || true
|
|
fi
|
|
|
|
git tag "${TAG}" "${GITHUB_SHA}"
|
|
git push origin "${TAG}"
|
|
|
|
test:
|
|
name: Unit tests
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
cache: "pip"
|
|
- name: Install
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -e ".[dev]" || pip install -e .
|
|
pip install pytest
|
|
- name: Run tests
|
|
env:
|
|
PYTHONPATH: ${{ github.workspace }}/src
|
|
run: pytest
|
|
|
|
build:
|
|
name: Build packages
|
|
needs: tag
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [windows-latest, ubuntu-latest]
|
|
ffmpeg: [bundled, none]
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- name: Derive version
|
|
id: version
|
|
shell: bash
|
|
run: |
|
|
TAG="${{ inputs.tag }}"
|
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Write bundled version file
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
printf '%s\n' "${{ steps.version.outputs.version }}" > version.txt
|
|
|
|
- uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
cache: "pip"
|
|
|
|
- name: Install build deps
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -e .
|
|
pip install pyinstaller
|
|
|
|
- name: Build binary (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$ErrorActionPreference = "Stop"
|
|
$ws = "${{ github.workspace }}"
|
|
pyinstaller --noconfirm --onefile --noconsole --name "ytpl-sync" --icon "$ws/assets/icon.ico" --add-data "$ws/assets/icon.png;assets" --add-data "$ws/version.txt;." --distpath "dist/pyinstaller" --workpath "build/pyinstaller" --specpath "build/pyinstaller" --paths "src" "ytpl-sync-entry.py"
|
|
|
|
- name: Build binary (Linux)
|
|
if: runner.os == 'Linux'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
pyinstaller --noconfirm --onefile --noconsole --name "ytpl-sync" --icon "${GITHUB_WORKSPACE}/assets/icon.png" --add-data "${GITHUB_WORKSPACE}/assets/icon.png:assets" --add-data "${GITHUB_WORKSPACE}/version.txt:." --distpath "dist/pyinstaller" --workpath "build/pyinstaller" --specpath "build/pyinstaller" --paths "src" "ytpl-sync-entry.py"
|
|
|
|
- name: Stage package
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
OS="${{ runner.os }}"
|
|
FFMPEG="${{ matrix.ffmpeg }}"
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
|
|
PKG_ROOT="$GITHUB_WORKSPACE/package"
|
|
rm -rf "$PKG_ROOT"
|
|
mkdir -p "$PKG_ROOT/config" "$PKG_ROOT/bin"
|
|
|
|
# Binary
|
|
if [[ "$OS" == "Windows" ]]; then
|
|
cp "$GITHUB_WORKSPACE/dist/pyinstaller/ytpl-sync.exe" "$PKG_ROOT/ytpl-sync.exe"
|
|
else
|
|
cp "$GITHUB_WORKSPACE/dist/pyinstaller/ytpl-sync" "$PKG_ROOT/ytpl-sync.exe"
|
|
chmod +x "$PKG_ROOT/ytpl-sync.exe"
|
|
fi
|
|
|
|
# Config: ship example as the default config
|
|
python - <<'PY'
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
workspace = Path(os.environ["GITHUB_WORKSPACE"])
|
|
pkg_root = workspace / "package"
|
|
src = workspace / "config" / "yt-playlist-config.example.json"
|
|
dst = pkg_root / "config" / "yt-playlist-config.json"
|
|
data = json.loads(src.read_text(encoding="utf-8"))
|
|
|
|
os_name = os.environ["RUNNER_OS"]
|
|
ffmpeg_mode = os.environ["FFMPEG_MODE"]
|
|
if ffmpeg_mode == "bundled":
|
|
data["ffmpeg_path"] = "./bin/ffmpeg.exe" if os_name == "Windows" else "./bin/ffmpeg"
|
|
else:
|
|
data["ffmpeg_path"] = "ffmpeg"
|
|
|
|
dst.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
PY
|
|
env:
|
|
FFMPEG_MODE: ${{ matrix.ffmpeg }}
|
|
|
|
- name: Bundle FFmpeg (Windows)
|
|
if: runner.os == 'Windows' && matrix.ffmpeg == 'bundled'
|
|
shell: pwsh
|
|
run: |
|
|
$ErrorActionPreference = "Stop"
|
|
New-Item -ItemType Directory -Force -Path "package/bin" | Out-Null
|
|
Invoke-WebRequest -Uri "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip" -OutFile "ffmpeg.zip"
|
|
Expand-Archive "ffmpeg.zip" -DestinationPath "ffmpeg_tmp"
|
|
$ffmpegExe = Get-ChildItem -Path "ffmpeg_tmp" -Filter "ffmpeg.exe" -Recurse | Select-Object -First 1
|
|
if (-not $ffmpegExe) { throw "ffmpeg.exe not found in archive" }
|
|
Copy-Item $ffmpegExe.FullName "package/bin/ffmpeg.exe"
|
|
Remove-Item -Force "ffmpeg.zip"
|
|
Remove-Item -Recurse -Force "ffmpeg_tmp"
|
|
|
|
- name: Bundle FFmpeg (Linux)
|
|
if: runner.os == 'Linux' && matrix.ffmpeg == 'bundled'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p package/bin ffmpeg_tmp
|
|
|
|
primary_url="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"
|
|
curl_common=(
|
|
--fail
|
|
--location
|
|
--retry 5
|
|
--retry-all-errors
|
|
--retry-delay 2
|
|
--user-agent "Mozilla/5.0 (GitHub Actions; ytpl-sync release workflow)"
|
|
)
|
|
|
|
curl "${curl_common[@]}" "$primary_url" -o ffmpeg.tar.xz
|
|
curl "${curl_common[@]}" "${primary_url}.md5" -o ffmpeg.tar.xz.md5
|
|
|
|
expected_md5="$(awk '{print $1}' ffmpeg.tar.xz.md5)"
|
|
printf '%s *ffmpeg.tar.xz\n' "$expected_md5" | md5sum -c -
|
|
|
|
if ! tar -tf ffmpeg.tar.xz >/dev/null 2>&1; then
|
|
echo "Downloaded FFmpeg payload is not a valid tar archive" >&2
|
|
ls -l ffmpeg.tar.xz >&2 || true
|
|
head -c 256 ffmpeg.tar.xz >&2 || true
|
|
exit 1
|
|
fi
|
|
|
|
tar -xf ffmpeg.tar.xz -C ffmpeg_tmp --strip-components=1
|
|
mv ffmpeg_tmp/ffmpeg package/bin/ffmpeg
|
|
chmod +x package/bin/ffmpeg
|
|
|
|
- name: Archive (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$ErrorActionPreference = "Stop"
|
|
$version = "${{ steps.version.outputs.version }}"
|
|
$variant = "${{ matrix.ffmpeg }}"
|
|
if ($variant -eq "bundled") {
|
|
$name = "ytpl-sync-windows-$version-ffmpeg.zip"
|
|
} else {
|
|
$name = "ytpl-sync-windows-$version.zip"
|
|
}
|
|
Compress-Archive -Path "package/*" -DestinationPath $name
|
|
|
|
- name: Archive (Linux)
|
|
if: runner.os == 'Linux'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version="${{ steps.version.outputs.version }}"
|
|
variant="${{ matrix.ffmpeg }}"
|
|
if [[ "$variant" == "bundled" ]]; then
|
|
name="ytpl-sync-linux-${version}-ffmpeg.tar.gz"
|
|
else
|
|
name="ytpl-sync-linux-${version}.tar.gz"
|
|
fi
|
|
tar -C package -czf "$name" .
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: packages-${{ runner.os }}-${{ matrix.ffmpeg }}
|
|
path: |
|
|
ytpl-sync-*.zip
|
|
ytpl-sync-*.tar.gz
|
|
|
|
release:
|
|
name: Create Release
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Generate release notes
|
|
shell: bash
|
|
env:
|
|
TAG: ${{ inputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
git fetch --tags --force
|
|
|
|
VERSION="${TAG#v}"
|
|
REPO="${GITHUB_REPOSITORY}"
|
|
|
|
prev_tag="$(git tag --sort=-creatordate | grep -Fxv "$TAG" | head -n 1 || true)"
|
|
|
|
{
|
|
echo "### Changes"
|
|
echo
|
|
if [[ -n "$prev_tag" ]]; then
|
|
echo "Compared to \`$prev_tag\`:"
|
|
echo
|
|
git log "${prev_tag}..${TAG}" --no-merges --pretty=format:'- %s (%h)' || true
|
|
else
|
|
echo "First tagged release:"
|
|
echo
|
|
git log "${TAG}" --no-merges --pretty=format:'- %s (%h)' || true
|
|
fi
|
|
|
|
echo
|
|
echo
|
|
echo "### Reports"
|
|
echo ""
|
|
echo ""
|
|
echo ""
|
|
echo ""
|
|
echo ""
|
|
echo
|
|
} > release-notes.md
|
|
|
|
- name: Create release
|
|
uses: softprops/action-gh-release@v3
|
|
with:
|
|
tag_name: ${{ inputs.tag }}
|
|
draft: ${{ inputs.draft }}
|
|
body_path: release-notes.md
|
|
files: |
|
|
artifacts/**/*.zip
|
|
artifacts/**/*.tar.gz
|