Skip to content

feat(worktrees): support .worktreeinclude for copying gitignored files #418

feat(worktrees): support .worktreeinclude for copying gitignored files

feat(worktrees): support .worktreeinclude for copying gitignored files #418

Workflow file for this run

name: CI
on:
push:
branches:
- development
- main
tags:
- 'v*.*.*'
pull_request:
workflow_call:
inputs:
repository:
default: desktop/desktop
required: false
type: string
ref:
required: true
type: string
upload-artifacts:
default: false
required: false
type: boolean
environment:
type: string
required: true
sign:
type: boolean
default: true
required: false
secrets:
AZURE_CODE_SIGNING_TENANT_ID:
AZURE_CODE_SIGNING_CLIENT_ID:
DESKTOP_OAUTH_CLIENT_ID:
DESKTOP_OAUTH_CLIENT_SECRET:
DESKTOP_OAUTH_CLIENT_ID_BITBUCKET:
DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET:
DESKTOP_OAUTH_CLIENT_ID_GITLAB:
DESKTOP_OAUTH_CLIENT_SECRET_GITLAB:
APPLE_ID:
APPLE_ID_PASSWORD:
APPLE_TEAM_ID:
APPLE_APPLICATION_CERT:
APPLE_APPLICATION_CERT_PASSWORD:
env:
NODE_VERSION: 24.11.1
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read
env:
RELEASE_CHANNEL: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v6
with:
repository: ${{ inputs.repository || github.repository }}
ref: ${{ inputs.ref }}
submodules: recursive
- uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
- run: yarn
- run: yarn validate-electron-version
- run: yarn lint
- run: yarn validate-changelog
- name: Ensure a clean working directory
run: git diff --name-status --exit-code
compute_version:
name: Compute app version
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
APP_VERSION: ${{ env.APP_VERSION }}
SEMVER_COMPATIBLE_VERSION: ${{ env.SEMVER_COMPATIBLE_VERSION }}
steps:
- name: Compute app version
run: |
VERSION="0.0.0"
if [[ "$GITHUB_REF" =~ ^refs/tags/ ]]; then
# 'refs/tags/v1.2.3' -> '1.2.3'
VERSION=$(echo "${GITHUB_REF/refs\/tags\//}" | sed -E "s/v(.*)/\\1/")
fi
echo "Computed version: ${VERSION}"
echo "APP_VERSION=$VERSION" >> $GITHUB_ENV
# Squirrel (and therefore the Windows release) requires strict semver
IFS='.' read -ra parts <<< "$VERSION"
if [ "${#parts[@]}" -gt 3 ]; then
major="${parts[0]}"
minor="${parts[1]}"
patch="${parts[2]}"
rest=$(IFS='-'; echo "${parts[*]:3}")
SEMVER_COMPATIBLE_VERSION="${major}.${minor}.${patch}-r${rest}"
else
SEMVER_COMPATIBLE_VERSION="$VERSION"
fi
echo "Semver compatible version: ${SEMVER_COMPATIBLE_VERSION}"
echo "SEMVER_COMPATIBLE_VERSION=$SEMVER_COMPATIBLE_VERSION" >> $GITHUB_ENV
build:
name: ${{ matrix.friendlyName }} ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
needs: compute_version
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
os: [macos-14, windows-2022, ubuntu-latest]
arch: [x64, arm64]
include:
- os: macos-14
friendlyName: macOS
- os: windows-2022
friendlyName: Windows
- os: ubuntu-latest
friendlyName: Linux
timeout-minutes: 60
environment: ${{ inputs.environment }}
env:
RELEASE_CHANNEL: ${{ inputs.environment }}
# build:prod expects APP_VERSION to be set in the environment
APP_VERSION: ${{ needs.compute_version.outputs.APP_VERSION }}
SEMVER_COMPATIBLE_VERSION:
${{ needs.compute_version.outputs.SEMVER_COMPATIBLE_VERSION }}
steps:
- uses: actions/checkout@v6
with:
repository: ${{ inputs.repository || github.repository }}
ref: ${{ inputs.ref }}
submodules: recursive
- uses: ./.github/actions/setup-ci-environment
with:
node-version: ${{ env.NODE_VERSION }}
arch: ${{ matrix.arch }}
- name: Validate macOS version
if: runner.os == 'macOS'
run: yarn validate-macos-version
- name: Run desktop-trampoline tests
run: |
cd vendor/desktop-trampoline
yarn install
yarn test
- name: Build production app
run: yarn build:prod
env:
DESKTOP_OAUTH_CLIENT_ID: ${{ secrets.DESKTOP_OAUTH_CLIENT_ID }}
DESKTOP_OAUTH_CLIENT_SECRET:
${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET }}
DESKTOP_OAUTH_CLIENT_ID_BITBUCKET:
${{ secrets.DESKTOP_OAUTH_CLIENT_ID_BITBUCKET }}
DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET:
${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET }}
DESKTOP_OAUTH_CLIENT_ID_GITLAB:
${{ secrets.DESKTOP_OAUTH_CLIENT_ID_GITLAB }}
DESKTOP_OAUTH_CLIENT_SECRET_GITLAB:
${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET_GITLAB }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APPLICATION_CERT: ${{ secrets.APPLE_APPLICATION_CERT }}
KEY_PASSWORD: ${{ secrets.APPLE_APPLICATION_CERT_PASSWORD }}
npm_config_arch: ${{ matrix.arch }}
TARGET_ARCH: ${{ matrix.arch }}
- name: Prepare testing environment
run: yarn test:setup
env:
npm_config_arch: ${{ matrix.arch }}
- name: Run unit tests
if: |
(runner.os == 'Windows' && matrix.arch == 'x64') || (runner.os == 'macOS' && matrix.arch == 'arm64') || (runner.os == 'Linux' && matrix.arch == 'x64')
run: yarn test:unit
- name: Run script tests
run: yarn test:script
- if: runner.os == 'Windows'
uses: ./.github/actions/setup-windows-signing
with:
enabled: ${{ inputs.sign }}
azure-client-id: ${{ secrets.AZURE_CODE_SIGNING_CLIENT_ID }}
azure-tenant-id: ${{ secrets.AZURE_CODE_SIGNING_TENANT_ID }}
- name: Package production app
run: yarn package
env:
npm_config_arch: ${{ matrix.arch }}
AZURE_TENANT_ID: ${{ secrets.AZURE_CODE_SIGNING_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CODE_SIGNING_CLIENT_ID }}
- name: Generate AppImage zsync
if: ${{ runner.os == 'Linux' }}
run:
script/generate-appimage-zsync.sh dist/*.AppImage ${{ matrix.arch }}
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: ${{matrix.friendlyName}}-${{matrix.arch}}
path: |
dist/*-macOS-*.zip
dist/*.exe
dist/*.msi
dist/*.nupkg
dist/*.AppImage
dist/*.AppImage.zsync
dist/*.deb
dist/*.rpm
dist/*.sha256
dist/bundle-size.json
if-no-files-found: error
e2e-smoke:
name: E2E Smoke ${{ matrix.friendlyName }} ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
include:
- os: macos-14
friendlyName: macOS
arch: arm64
- os: windows-2022
friendlyName: Windows
arch: x64
timeout-minutes: 60
environment: ${{ inputs.environment }}
env:
RELEASE_CHANNEL: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
with:
repository: ${{ inputs.repository || github.repository }}
ref: ${{ inputs.ref }}
submodules: recursive
- uses: ./.github/actions/setup-ci-environment
with:
node-version: ${{ env.NODE_VERSION }}
arch: ${{ matrix.arch }}
install-ffmpeg: 'true'
- name: Build production app
run: yarn build:prod
env:
DESKTOP_E2E_UPDATES_URL: http://127.0.0.1:51789/update
DESKTOP_OAUTH_CLIENT_ID: ${{ secrets.DESKTOP_OAUTH_CLIENT_ID }}
DESKTOP_OAUTH_CLIENT_SECRET:
${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APPLICATION_CERT: ${{ secrets.APPLE_APPLICATION_CERT }}
KEY_PASSWORD: ${{ secrets.APPLE_APPLICATION_CERT_PASSWORD }}
npm_config_arch: ${{ matrix.arch }}
TARGET_ARCH: ${{ matrix.arch }}
- name: Prepare testing environment
run: yarn test:setup
env:
npm_config_arch: ${{ matrix.arch }}
- if: runner.os == 'Windows'
uses: ./.github/actions/setup-windows-signing
with:
enabled: ${{ inputs.sign }}
azure-client-id: ${{ secrets.AZURE_CODE_SIGNING_CLIENT_ID }}
azure-tenant-id: ${{ secrets.AZURE_CODE_SIGNING_TENANT_ID }}
- name: Package production app
run: yarn package
env:
npm_config_arch: ${{ matrix.arch }}
AZURE_TENANT_ID: ${{ secrets.AZURE_CODE_SIGNING_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CODE_SIGNING_CLIENT_ID }}
- name: Install app on macOS
if: runner.os == 'macOS'
run: |
rm -rf "/Applications/GitHub Desktop Plus.app"
ditto "dist/GitHub Desktop Plus-darwin-arm64/GitHub Desktop Plus.app" "/Applications/GitHub Desktop Plus.app"
echo "DESKTOP_E2E_APP_PATH=/Applications/GitHub Desktop Plus.app/Contents/MacOS/GitHub Desktop Plus" >> "$GITHUB_ENV"
- name: Install app on Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
function Write-SquirrelLogs {
$logPaths = @(
"$env:LOCALAPPDATA\SquirrelSetup.log",
"$env:LOCALAPPDATA\GitHubDesktopPlus\SquirrelSetup.log"
)
foreach ($logPath in $logPaths) {
if (Test-Path $logPath) {
Write-Host "Showing log: $logPath"
Get-Content $logPath -Tail 200
}
}
}
$setupExe = Get-ChildItem "dist/GitHubDesktopPlus-*-windows-${{ matrix.arch }}.exe" -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending |
Select-Object -First 1 -ExpandProperty FullName
if (-not $setupExe) {
throw "Unable to locate Windows installer executable"
}
$installer = Start-Process -FilePath $setupExe -ArgumentList "/S" -PassThru
try {
Wait-Process -Id $installer.Id -Timeout 300 -ErrorAction Stop
} catch {
Write-SquirrelLogs
throw "Windows installer timed out after 300 seconds"
}
Get-Process GitHubDesktopPlus -ErrorAction SilentlyContinue | Stop-Process -Force
$installedExe = $null
for ($attempt = 0; $attempt -lt 30 -and -not $installedExe; $attempt++) {
$installedExe = Get-ChildItem "$env:LOCALAPPDATA\GitHubDesktopPlus\app-*\GitHubDesktopPlus.exe" -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending |
Select-Object -First 1 -ExpandProperty FullName
if (-not $installedExe) {
Start-Sleep -Seconds 2
}
}
if (-not $installedExe) {
Write-SquirrelLogs
throw "Unable to locate installed GitHub Desktop executable"
}
Add-Content -Path $env:GITHUB_ENV -Value "DESKTOP_E2E_APP_PATH=$installedExe"
- name: Run packaged E2E smoke tests
run: yarn test:e2e:run:packaged
- name: Upload E2E artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: e2e-${{matrix.friendlyName}}-${{matrix.arch}}
path: playwright-videos/**
if-no-files-found: warn
release_github:
name: Create GitHub release
needs: [build, compute_version]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
env:
APP_VERSION: ${{ needs.compute_version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v6
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: './artifacts'
- name: Display structure of downloaded files
run: ls -R
working-directory: './artifacts'
- name: Read release notes
id: release_notes
run: |
RELEASE_NOTES_FILE=".github/github-desktop-plus-release-notes.md"
TITLE=$(head -n 1 "$RELEASE_NOTES_FILE")
BODY=$(tail -n +3 "$RELEASE_NOTES_FILE")
echo "title=$TITLE" >> $GITHUB_OUTPUT
{
echo 'body<<EOF'
echo "$BODY"
echo EOF
} >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.release_notes.outputs.title }}
body: ${{ steps.release_notes.outputs.body }}
files: |
artifacts/**/*.AppImage
artifacts/**/*.AppImage.zsync
artifacts/**/*.deb
artifacts/**/*.rpm
artifacts/**/*.exe
artifacts/**/*.msi
artifacts/**/*-macOS-*.zip
draft: false
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release_aur:
name: Publish AUR package
needs: [release_github, compute_version]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: read
env:
APP_VERSION: ${{ needs.compute_version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v6
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: './artifacts'
- name: Display structure of downloaded files
run: ls -R
working-directory: './artifacts'
- name: Prepare PKGBUILD files
run: |
_obfuscate() {
echo "$1" | rev | sed 's/./&@/g'
}
AUR_DIR=./publish/aur
echo "AUR_DIR=$AUR_DIR" >> $GITHUB_ENV
PKGBUILD_BIN=$AUR_DIR/PKGBUILD-bin.sh
PKGBUILD=$AUR_DIR/PKGBUILD.sh
PKGBUILD_GIT=$AUR_DIR/PKGBUILD-git.sh
echo "PKGBUILD_BIN=$PKGBUILD_BIN" >> $GITHUB_ENV
echo "PKGBUILD=$PKGBUILD" >> $GITHUB_ENV
echo "PKGBUILD_GIT=$PKGBUILD_GIT" >> $GITHUB_ENV
node_major=$(head -n 1 .node-version | cut -d. -f1)
NODE_CODENAME="$(
curl -fsSL https://raw.githubusercontent.com/nodejs/Release/main/schedule.json |
jq -r --arg v "v${node_major}" '.[$v].codename // empty' |
tr '[:upper:]' '[:lower:]'
)"
for PKGBUILD_FILE in "$PKGBUILD_BIN" "$PKGBUILD" "$PKGBUILD_GIT"; do
if [[ ! -f "$PKGBUILD_FILE" ]]; then
echo "$PKGBUILD_FILE does not exist. Contents of current directory:"
ls -la
exit 1
fi
sed -i "s/\[\[APP_VERSION\]\]/${APP_VERSION}/" $PKGBUILD_FILE
sed -i "s/\[\[NODE_CODENAME\]\]/${NODE_CODENAME}/" $PKGBUILD_FILE
desktop_file_sha256=$(sha256sum $AUR_DIR/github-desktop-plus.desktop | awk '{ print $1 }')
sed -i "s/\[\[DESKTOP_FILE_SHA256\]\]/$desktop_file_sha256/" $PKGBUILD_FILE
launch_script_sha256=$(sha256sum $AUR_DIR/launch-app.sh | awk '{ print $1 }')
sed -i "s/\[\[LAUNCH_SCRIPT_SHA256\]\]/$launch_script_sha256/" $PKGBUILD_FILE
x86_64_sha256=$(sha256sum artifacts/**/*-x86_64.deb | awk '{ print $1 }')
sed -i "s/\[\[X86_64_SHA256\]\]/$x86_64_sha256/" $PKGBUILD_FILE
aarch64_sha256=$(sha256sum artifacts/**/*-arm64.deb | awk '{ print $1 }')
sed -i "s/\[\[AARCH64_SHA256\]\]/$aarch64_sha256/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_ID")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_SECRET")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID_BITBUCKET_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_ID_BITBUCKET")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID_GITLAB_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_ID_GITLAB")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET_GITLAB_NAME\]\]/$(_obfuscate "DESKTOP_OAUTH_CLIENT_SECRET_GITLAB")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_ID }}")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET }}")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID_BITBUCKET\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_ID_BITBUCKET }}")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET_BITBUCKET }}")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_ID_GITLAB\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_ID_GITLAB }}")/" $PKGBUILD_FILE
sed -i "s/\[\[DESKTOP_OAUTH_CLIENT_SECRET_GITLAB\]\]/$(_obfuscate "${{ secrets.DESKTOP_OAUTH_CLIENT_SECRET_GITLAB }}")/" $PKGBUILD_FILE
done
- name: Upload PKGBUILD files
uses: actions/upload-artifact@v6
with:
name: PKGBUILDs
path: |
${{ env.PKGBUILD_BIN }}
${{ env.PKGBUILD }}
${{ env.PKGBUILD_GIT }}
retention-days: 5
if-no-files-found: error
- name: Publish AUR package github-desktop-plus-bin
uses: KSXGitHub/github-actions-deploy-aur@v4.1.1
with:
pkgname: github-desktop-plus-bin
pkgbuild: ${{ env.PKGBUILD_BIN }}
assets: |
${{ env.AUR_DIR }}/.gitignore
${{ env.AUR_DIR }}/github-desktop-plus.desktop
${{ env.AUR_DIR }}/launch-app.sh
commit_username: ${{ secrets.AUR_USERNAME }}
commit_email: ${{ secrets.AUR_EMAIL }}
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message:
'Update AUR package to version v${{ env.APP_VERSION }}'
ssh_keyscan_types: rsa,ecdsa,ed25519
- name: Publish AUR package github-desktop-plus
uses: KSXGitHub/github-actions-deploy-aur@v4.1.1
with:
pkgname: github-desktop-plus
pkgbuild: ${{ env.PKGBUILD }}
assets: |
${{ env.AUR_DIR }}/.gitignore
${{ env.AUR_DIR }}/github-desktop-plus.desktop
${{ env.AUR_DIR }}/launch-app.sh
commit_username: ${{ secrets.AUR_USERNAME }}
commit_email: ${{ secrets.AUR_EMAIL }}
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message:
'Update AUR package to version v${{ env.APP_VERSION }}'
ssh_keyscan_types: rsa,ecdsa,ed25519
- name: Publish AUR package github-desktop-plus-git
uses: KSXGitHub/github-actions-deploy-aur@v4.1.1
with:
pkgname: github-desktop-plus-git
pkgbuild: ${{ env.PKGBUILD_GIT }}
assets: |
${{ env.AUR_DIR }}/.gitignore
${{ env.AUR_DIR }}/github-desktop-plus.desktop
${{ env.AUR_DIR }}/launch-app.sh
commit_username: ${{ secrets.AUR_USERNAME }}
commit_email: ${{ secrets.AUR_EMAIL }}
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message:
'Update AUR package to version v${{ env.APP_VERSION }}'
ssh_keyscan_types: rsa,ecdsa,ed25519
release_rpm:
name: Publish RPM package
needs: release_github
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: read
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
path: './artifacts'
- name: Install RPM package tools and s3cmd
run: |
sudo apt-get update
sudo apt-get install -y createrepo-c rpm s3cmd
- name: Import GPG private key and configure RPM signing
run: |
echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
echo "default-key $(gpg --list-keys --with-colons | grep pub | head -n1 | cut -d: -f5)" >> ~/.gnupg/gpg.conf
# https://unix.stackexchange.com/a/329107
echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf
gpg-connect-agent reloadagent /bye
echo '%_signature gpg' >> ~/.rpmmacros
echo '%_gpg_name ${{ secrets.GPG_KEY_NAME }}' >> ~/.rpmmacros
echo '%_gpgbin /usr/bin/gpg' >> ~/.rpmmacros
KEYGRIP=$(gpg --list-keys --with-keygrip --with-colons | awk -F: -v name="${{ secrets.GPG_KEY_NAME }}" '$1=="grp"{kg=$10} $1=="uid" && index($10,name){print kg; exit}')
"$(gpgconf --list-dirs libexecdir)"/gpg-preset-passphrase --passphrase "${{ secrets.GPG_KEY_PASSPHRASE }}" --preset $KEYGRIP
- name: Prepare RPM repository
run: |
mkdir dist
cd dist
mkdir Packages
# Copy and sign RPM packages
find ../artifacts -type f -name "*.rpm" -exec cp {} Packages/ \;
for rpm in Packages/*.rpm; do
rpm --addsign "$rpm"
done
# Create and sign release
createrepo_c --update .
gpg --batch --yes --detach-sign -o repodata/repomd.xml.asc repodata/repomd.xml
- name: Configure s3cmd for Cloudflare R2
run: |
cat > ~/.s3cfg <<EOF
[default]
access_key = ${{ secrets.R2_ACCESS_KEY_ID }}
secret_key = ${{ secrets.R2_SECRET_ACCESS_KEY }}
host_base = ${{ secrets.R2_ENDPOINT }}
host_bucket = ${{ secrets.R2_ENDPOINT }}
use_https = True
signature_v2 = False
EOF
- name: Sync RPM repo to Cloudflare R2
working-directory: dist
run: |
s3cmd sync --delete-removed ./ s3://${{ secrets.R2_BUCKET_RPM }}/
release_deb:
name: Publish DEB package
needs: release_github
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: read
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
path: './artifacts'
- name: Install Debian repo tools and s3cmd
run: |
sudo apt-get update
sudo apt-get install -y dpkg-dev apt-utils s3cmd
- name: Import GPG private key
run: |
echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
echo "default-key $(gpg --list-keys --with-colons | grep pub | head -n1 | cut -d: -f5)" >> ~/.gnupg/gpg.conf
- name: Prepare APT repository
run: |
mkdir dist
cd dist
# Copy .deb packages into pool
mkdir -p pool/main
find ../artifacts -type f -name "*.deb" -exec cp {} pool/main/ \;
# Normalize package names
dpkg-name pool/main/*.deb
# Generate the Packages index for each architecture
for arch in amd64 arm64 armhf; do
mkdir -p dists/stable/main/binary-$arch
dpkg-scanpackages --arch $arch pool/main /dev/null > dists/stable/main/binary-$arch/Packages
gzip -k dists/stable/main/binary-$arch/Packages
done
# Create the Release file
apt-ftparchive -o APT::FTPArchive::Release::Codename=stable release dists/stable > dists/stable/Release
# Sign release file
gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.GPG_KEY_PASSPHRASE }}" --clearsign -o dists/stable/InRelease dists/stable/Release
gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.GPG_KEY_PASSPHRASE }}" --detach-sign -o dists/stable/Release.gpg dists/stable/Release
- name: Configure s3cmd for Cloudflare R2
run: |
cat > ~/.s3cfg <<EOF
[default]
access_key = ${{ secrets.R2_ACCESS_KEY_ID }}
secret_key = ${{ secrets.R2_SECRET_ACCESS_KEY }}
host_base = ${{ secrets.R2_ENDPOINT }}
host_bucket = ${{ secrets.R2_ENDPOINT }}
use_https = True
signature_v2 = False
EOF
- name: Sync DEB repo to Cloudflare R2
working-directory: dist
run: |
s3cmd sync --delete-removed ./ s3://${{ secrets.R2_BUCKET_APT }}/
release_winget:
name: Publish winget package
needs: [release_github, compute_version]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions: {}
steps:
- name: Publish to winget
uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: polrivero.GitHubDesktopPlus
version:
${{ needs.compute_version.outputs.SEMVER_COMPATIBLE_VERSION }}
installers-regex: '\.exe$'
token: ${{ secrets.WINGET_TOKEN }}
release_homebrew:
name: Publish Homebrew cask
needs: [release_github, compute_version]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: read
env:
APP_VERSION: ${{ needs.compute_version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v6
- name: Download macOS artifacts
uses: actions/download-artifact@v4
with:
path: './artifacts'
pattern: 'macOS-*'
- name: Compute SHA256 checksums
run: |
SHA256_ARM64=$(sha256sum artifacts/**/*-macOS-arm64.zip | awk '{ print $1 }')
SHA256_X64=$(sha256sum artifacts/**/*-macOS-x64.zip | awk '{ print $1 }')
echo "SHA256_ARM64=$SHA256_ARM64" >> $GITHUB_ENV
echo "SHA256_X64=$SHA256_X64" >> $GITHUB_ENV
- name: Clone Homebrew Tap
uses: actions/checkout@v6
with:
repository: 'pol-rivero/homebrew-tap'
token: ${{ secrets.HOMEBREW_TAP_PERSONAL_ACCESS_TOKEN }}
path: homebrew-tap
- name: Generate Homebrew cask
run: |
mkdir -p homebrew-tap/Casks
sed -e "s/\[\[VERSION\]\]/${APP_VERSION}/" \
-e "s/\[\[SHA256_ARM64\]\]/${SHA256_ARM64}/" \
-e "s/\[\[SHA256_X64\]\]/${SHA256_X64}/" \
publish/homebrew/github-desktop-plus.rb > homebrew-tap/Casks/github-desktop-plus.rb
- name: Push to Homebrew Tap
working-directory: homebrew-tap
run: |
git config --global user.name "Formula Updater"
git config --global user.email "formula-updater@polrivero.com"
git add Casks/github-desktop-plus.rb
git commit -m "Update github-desktop-plus to version ${APP_VERSION}"
git push origin main