Skip to content

Fix YouTube handle case in README #17

Fix YouTube handle case in README

Fix YouTube handle case in README #17

name: Desktop Release
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: write
concurrency:
group: desktop-release-${{ github.ref_name }}
cancel-in-progress: false
jobs:
validate_release_ref:
name: Validate Release Ref
runs-on: ubuntu-latest
steps:
- name: Enforce Supported Release Branches
shell: bash
run: |
if [[ "${GITHUB_REF_NAME}" == "main" ]]; then
exit 0
fi
echo "Desktop releases may only run from main." >&2
exit 1
prepare_release:
name: Prepare Release
runs-on: ubuntu-latest
needs:
- validate_release_ref
outputs:
application_version: ${{ steps.resolve_version.outputs.application_version }}
previous_tag: ${{ steps.previous_tag.outputs.value }}
release_tag: ${{ steps.resolve_version.outputs.release_tag }}
release_version: ${{ steps.resolve_version.outputs.display_version }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Dependencies
uses: "./.github/steps/install_dependencies"
- name: Fetch Tags
shell: bash
run: git fetch --tags --force
- name: Capture Previous Tag
id: previous_tag
shell: bash
run: |
previous_tag="$(git tag --list 'v*' --sort=-version:refname | head -n 1)"
echo "value=${previous_tag}" >> "$GITHUB_OUTPUT"
- name: Resolve Release Version
id: resolve_version
shell: bash
run: |
version_prefix="$(dotnet msbuild ./DotPilot/DotPilot.csproj -getProperty:DotPilotVersionPrefix | tail -n 1 | tr -d '\r')"
if [[ ! "${version_prefix}" =~ ^[0-9]+\.[0-9]+$ ]]; then
echo "DotPilotVersionPrefix must be a two-segment numeric prefix. Found: '${version_prefix}'." >&2
exit 1
fi
display_version="${version_prefix}.${{ github.run_number }}"
{
echo "display_version=${display_version}"
echo "application_version=${{ github.run_number }}"
echo "release_tag=v${display_version}"
} >> "$GITHUB_OUTPUT"
publish_macos:
name: Publish macOS Release Asset
runs-on: macos-latest
timeout-minutes: 60
needs:
- prepare_release
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.sha }}
- name: Install Dependencies
timeout-minutes: 60
uses: "./.github/steps/install_dependencies"
- name: Resolve macOS RID
id: macos_rid
shell: bash
run: |
architecture="$(uname -m)"
case "${architecture}" in
arm64)
rid="osx-arm64"
asset_suffix="macos-arm64"
;;
x86_64)
rid="osx-x64"
asset_suffix="macos-x64"
;;
*)
echo "Unsupported macOS runner architecture: ${architecture}" >&2
exit 1
;;
esac
echo "rid=${rid}" >> "$GITHUB_OUTPUT"
echo "asset_suffix=${asset_suffix}" >> "$GITHUB_OUTPUT"
- name: Publish macOS Disk Image
shell: bash
working-directory: ./DotPilot
run: |
dotnet publish DotPilot.csproj \
-c Release \
-f net10.0-desktop \
-r "${{ steps.macos_rid.outputs.rid }}" \
-warnaserror \
-m:1 \
-p:BuildInParallel=false \
-p:CI=true \
-p:SelfContained=true \
-p:PackageFormat=dmg \
-p:CodesignKey=- \
-p:DiskImageSigningKey=- \
-p:DotPilotReleaseVersion=${{ needs.prepare_release.outputs.release_version }} \
-p:DotPilotBuildNumber=${{ needs.prepare_release.outputs.application_version }}
- name: Stage macOS Release Asset
shell: bash
run: |
mkdir -p ./artifacts/releases
source_path="./DotPilot/bin/Release/net10.0-desktop/${{ steps.macos_rid.outputs.rid }}/publish/DotPilot.dmg"
target_path="./artifacts/releases/dotpilot-${{ needs.prepare_release.outputs.release_version }}-${{ steps.macos_rid.outputs.asset_suffix }}.dmg"
if [[ ! -f "${source_path}" ]]; then
echo "Expected macOS release asset was not produced: ${source_path}" >&2
exit 1
fi
cp "${source_path}" "${target_path}"
- name: Upload macOS Release Artifact
uses: actions/upload-artifact@v7
with:
name: dotpilot-release-macos
path: ./artifacts/releases/*.dmg
if-no-files-found: error
retention-days: 14
publish_windows:
name: Publish Windows Release Asset
runs-on: windows-latest
timeout-minutes: 60
needs:
- prepare_release
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.sha }}
- name: Install Dependencies
timeout-minutes: 60
uses: "./.github/steps/install_dependencies"
- name: Publish Windows Single-File Executable
shell: pwsh
working-directory: .\DotPilot
run: >
dotnet publish DotPilot.csproj
-c Release
-f net10.0-desktop
-r win-x64
-warnaserror
-m:1
-p:BuildInParallel=false
-p:CI=true
-p:SelfContained=true
-p:PublishSingleFile=true
-p:IncludeNativeLibrariesForSelfExtract=true
-p:IncludeAllContentForSelfExtract=true
-p:DotPilotReleaseVersion=${{ needs.prepare_release.outputs.release_version }}
-p:DotPilotBuildNumber=${{ needs.prepare_release.outputs.application_version }}
- name: Stage Windows Release Asset
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path .\artifacts\releases | Out-Null
$sourcePath = ".\DotPilot\bin\Release\net10.0-desktop\win-x64\publish\DotPilot.exe"
$targetPath = ".\artifacts\releases\dotpilot-${{ needs.prepare_release.outputs.release_version }}-windows-x64.exe"
if (-not (Test-Path $sourcePath)) {
throw "Expected Windows release asset was not produced: $sourcePath"
}
Copy-Item $sourcePath $targetPath -Force
- name: Upload Windows Release Artifact
uses: actions/upload-artifact@v7
with:
name: dotpilot-release-windows
path: ./artifacts/releases/*.exe
if-no-files-found: error
retention-days: 14
publish_linux:
name: Publish Linux Snap Release Asset
runs-on: ubuntu-24.04
timeout-minutes: 60
needs:
- prepare_release
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.sha }}
- name: Install Dependencies
timeout-minutes: 60
uses: "./.github/steps/install_dependencies"
- name: Install Linux Snap Packaging Prerequisites
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y snapd
sudo systemctl start snapd.socket
sudo snap wait system seed.loaded
sudo snap install core24
sudo snap install multipass
sudo snap install lxd
sudo snap install snapcraft --classic
sudo lxd init --minimal
- name: Publish Linux Snap Package
shell: bash
working-directory: ./DotPilot
run: |
sudo dotnet publish DotPilot.csproj \
-c Release \
-f net10.0-desktop \
-r linux-x64 \
-warnaserror \
-m:1 \
-p:BuildInParallel=false \
-p:CI=true \
-p:SelfContained=true \
-p:PackageFormat=snap \
-p:UnoSnapcraftAdditionalParameters=--destructive-mode \
-p:DotPilotReleaseVersion=${{ needs.prepare_release.outputs.release_version }} \
-p:DotPilotBuildNumber=${{ needs.prepare_release.outputs.application_version }}
sudo chown -R "$USER:$USER" ./bin/Release/net10.0-desktop/linux-x64
- name: Stage Linux Release Asset
shell: bash
run: |
mkdir -p ./artifacts/releases
source_path="$(find ./DotPilot/bin/Release/net10.0-desktop/linux-x64/publish -maxdepth 1 -type f -name '*.snap' | sort | head -n 1)"
target_path="./artifacts/releases/dotpilot-${{ needs.prepare_release.outputs.release_version }}-linux-x64.snap"
if [[ -z "${source_path}" || ! -f "${source_path}" ]]; then
echo "Expected Linux snap release asset was not produced." >&2
exit 1
fi
cp "${source_path}" "${target_path}"
- name: Upload Linux Release Artifact
uses: actions/upload-artifact@v7
with:
name: dotpilot-release-linux
path: ./artifacts/releases/*.snap
if-no-files-found: error
retention-days: 14
create_release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs:
- prepare_release
- publish_macos
- publish_windows
- publish_linux
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.sha }}
- name: Fetch Tags
shell: bash
run: git fetch --tags --force
- name: Download Release Artifacts
uses: actions/download-artifact@v8
with:
path: ./artifacts/release-assets
- name: Generate Feature Summary
shell: bash
env:
PREVIOUS_TAG: ${{ needs.prepare_release.outputs.previous_tag }}
RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }}
REPOSITORY: ${{ github.repository }}
run: |
mkdir -p ./artifacts
commit_range="HEAD"
if [[ -n "${PREVIOUS_TAG}" ]]; then
commit_range="${PREVIOUS_TAG}..HEAD"
fi
git log --no-merges --pretty=format:%s "${commit_range}" |
awk 'NF && tolower($0) !~ /^chore\(release\):/ && !seen[$0]++' > ./artifacts/release-commits.txt
if [[ ! -s ./artifacts/release-commits.txt ]]; then
printf '%s\n' "Maintenance and release preparation changes." > ./artifacts/release-commits.txt
fi
: > ./artifacts/release-feature-docs.txt
if [[ -n "${PREVIOUS_TAG}" ]]; then
git diff --name-only "${PREVIOUS_TAG}..HEAD" -- 'docs/Features/*.md' |
awk '/^docs\/Features\// && !seen[$0]++' > ./artifacts/release-feature-docs.txt
elif [[ -d ./docs/Features ]]; then
find ./docs/Features -maxdepth 1 -type f -name '*.md' | sed 's#^\./##' | sort > ./artifacts/release-feature-docs.txt
fi
{
echo "## Feature Summary"
while IFS= read -r subject; do
if [[ -n "${subject}" ]]; then
echo "- ${subject}"
fi
done < ./artifacts/release-commits.txt
} > ./artifacts/release-summary.md
if [[ -s ./artifacts/release-feature-docs.txt ]]; then
{
echo
echo "## Feature Specs"
while IFS= read -r feature_doc; do
if [[ -z "${feature_doc}" ]]; then
continue
fi
title="$(sed -n 's/^# //p' "${feature_doc}" | head -n 1)"
if [[ -z "${title}" ]]; then
title="$(basename "${feature_doc}" .md)"
fi
printf -- '- [%s](https://github.com/%s/blob/%s/%s)\n' \
"${title}" \
"${REPOSITORY}" \
"${RELEASE_TAG}" \
"${feature_doc}"
done < ./artifacts/release-feature-docs.txt
} >> ./artifacts/release-summary.md
fi
- name: Publish GitHub Release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
PREVIOUS_TAG: ${{ needs.prepare_release.outputs.previous_tag }}
RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }}
RELEASE_TARGET_SHA: ${{ github.sha }}
RELEASE_VERSION: ${{ needs.prepare_release.outputs.release_version }}
REPOSITORY: ${{ github.repository }}
run: |
mapfile -t release_assets < <(find ./artifacts/release-assets -type f | sort)
if [[ ${#release_assets[@]} -eq 0 ]]; then
echo "No release assets were downloaded." >&2
exit 1
fi
release_notes="$(cat ./artifacts/release-summary.md)"
release_command=(
gh release create "${RELEASE_TAG}"
"${release_assets[@]}"
--repo "${REPOSITORY}"
--target "${RELEASE_TARGET_SHA}"
--title "DotPilot ${RELEASE_VERSION}"
--generate-notes
--notes "${release_notes}"
)
if [[ -n "${PREVIOUS_TAG}" ]]; then
release_command+=(--notes-start-tag "${PREVIOUS_TAG}")
fi
"${release_command[@]}"