Build Non-App Store Release #105
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build Non-App Store Release | |
on: | |
create: | |
workflow_dispatch: | |
inputs: | |
releaseVersion: | |
description: "Version to title release with (like: 1.0rc3), blank for project's version" | |
type: string | |
required: false | |
githubRelease: | |
description: "Make full GitHub Release" | |
type: boolean | |
required: true | |
default: true | |
updateAppcast: | |
description: "Update Sparkle" | |
type: boolean | |
required: true | |
default: true | |
isPrerelease: | |
description: "Prerelease" | |
type: boolean | |
required: true | |
default: false | |
env: | |
githubReleaseDefault: true | |
updateAppcastDefault: true | |
isPrereleaseDefault: false | |
projectfile: Maccy.xcodeproj | |
buildscheme: Cleepp | |
productname: "Batch Clipboard" | |
bundlename: "Batch Clipboard.app" | |
builddir: Build/Products/Release | |
branch: forkmain | |
jobs: | |
build: | |
name: Build and Release Cleepp Non-AppStore Variant | |
runs-on: macos-15 | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 # required for 'git show-ref --tags' to work | |
ref: "${{ env.branch }}" | |
# - name: Patch Xcode 15.3 | |
# uses: 2sem/patch-package-resolved@v2 | |
# # this fixes a mysterious build failure | |
# # xcodebuild: error: Could not resolve package dependencies: | |
# # Package.resolved file is corrupted or malformed; fix or delete the file | |
# # to continue: unknown 'PinsStorage' version '3' | |
# # should probably remove this when upgrading the "runs-on" platform | |
- name: Install tools | |
# pandoc is used by sparkle step and by one of the xcode project's build rules | |
# create-dmg is to define dmg entirely from script below instead of using a tempplate | |
run: | | |
: | |
brew update | |
brew install pandoc create-dmg # needed coreutils at one point but no longer IIRC | |
if ! command -v xcodebuild >/dev/null 2>&1 || ! command -v xcbeautify >/dev/null 2>&1 \ | |
|| ! command -v plutil >/dev/null 2>&1 || ! command -v security >/dev/null 2>&1 \ | |
|| ! command -v xcrun >/dev/null 2>&1 | |
then | |
echo "::error::Required executables not found: xcodebuild, xcbeautify, plutil, security, xcrun" | |
exit 1 | |
fi | |
if ! xcrun --find codesign >/dev/null 2>&1 | |
then | |
echo "::error::Required developer executables not found: codesign" | |
exit 1 | |
fi | |
if ! command -v pandoc >/dev/null 2>&1 || ! command -v create-dmg >/dev/null 2>&1 | |
then | |
echo "::error::Required homebrew executables not found: pandoc, create-dmg" | |
exit 1 | |
fi | |
- name: Validate | |
id: version | |
run: | | |
: | |
echo "- Extract version and bundle id from the project" | |
xcodebuild -scheme "${{ env.buildscheme }}" -configuration Release \ | |
-project "${{ env.projectfile }}" -showBuildSettings 2>/dev/null > buildsettings.txt | |
version=$(sed -nr 's/^.*MARKETING_VERSION = (.*)$/\1/p' < buildsettings.txt) | |
if [[ -z $version ]] ; then | |
echo "::error::Unable to determine a version number for the current state of the xcode project" | |
exit 1 | |
fi | |
bundleid=$(sed -nr 's/^.*PRODUCT_BUNDLE_IDENTIFIER = (.*)$/\1/p' < buildsettings.txt) | |
if [[ -z $bundleid ]] ; then | |
echo "::error::Unable to extract bundle id from the xcode project" | |
exit 1 | |
fi | |
echo "- Check script inputs" | |
if [[ -z "${{ inputs.releaseVersion }}" || $version == "${{ inputs.releaseVersion }}" ]] ; then | |
echo "- Build version is $version" | |
else | |
echo "- Build version is $version but overriding with ${{ inputs.releaseVersion }} for release & file names" | |
version="${{ inputs.releaseVersion }}" | |
fi | |
releasename="${{ env.productname }} $version" | |
releasenameNoSpaces="$(echo "${{ env.productname }}" | sed "s/ /./").$version" | |
if [[ -z "${{ inputs.isPrerelease }}" ]] ; then | |
echo "- Use default value for isPrerelease: ${{ env.isPrereleaseDefault }}" | |
isPrerelease=${{ env.isPrereleaseDefault }} | |
else | |
echo "- Use supplied value for isPrerelease: ${{ inputs.isPrerelease }}" | |
isPrerelease=${{ inputs.isPrerelease }} | |
fi | |
if [[ -z "${{ inputs.githubRelease }}" ]] ; then | |
echo "- Use default value for githubRelease: ${{ env.githubReleaseDefault }}" | |
githubRelease=${{ env.githubReleaseDefault }} | |
else | |
echo "- Use supplied value for githubRelease: ${{ inputs.githubRelease }}" | |
githubRelease=${{ inputs.githubRelease }} | |
fi | |
if [[ -z "${{ inputs.updateAppcast }}" ]] ; then | |
echo "- Use default value for updateAppcast: ${{ env.updateAppcastDefault }}" | |
updateAppcast=${{ env.updateAppcastDefault }} | |
else | |
echo "- Use supplied value for updateAppcast: ${{ inputs.updateAppcast }}" | |
updateAppcast=${{ inputs.updateAppcast }} | |
fi | |
echo "- Parse trigger" | |
if [[ "${{ github.event_name }}" == workflow_dispatch ]] ; then | |
if [[ "${{ github.ref }}" != "refs/heads/${{ env.branch }}" ]] ; then | |
echo "::error::Manually triggered workflow supports ${{ env.branch }} only, gihub.ref == ${{ github.ref }})" | |
exit 1 | |
fi | |
if git show-ref --tags --verify refs/tags/v$version --quiet ; then | |
tag="v$version" | |
if [[ $githubRelease != 'true' ]] ; then | |
echo "- Will build and save release \"$releasename\" as artifact" | |
else | |
echo "- Will build and draft release \"$releasename\" with matching tag \"$tag\"" | |
fi | |
elif [[ $githubRelease != 'true' ]] ; then | |
echo "- Will build and save release \"$releasename\" as artifact" | |
else | |
echo "- Will build and save release \"$releasename\" as artifact because no tag \"v$version\" was found" | |
fi | |
elif [[ "${{ github.ref }}" == refs/tags/* ]] ; then | |
# TODO: how do we verify the branch of this tag and ensure its ${{ env.branch }} ? | |
ref="${{ github.ref }}" | |
tag="${ref:10}" # magic number 10 being the length of "refs/tags/", stripping that to leave just the tag name | |
echo "- Will build and draft release \"$releasename\" because triggered by pushed tag \"$tag\"" | |
else | |
echo "::error::Not triggered manually or by a tag (github.event_name == ${{ github.event_name }}, gihub.ref == ${{ github.ref }})" | |
exit 1 | |
fi | |
if [[ -z $tag && $githubRelease == 'true' ]] ; then | |
echo "::notice::Creating a github release skipped because no tag \"v$version\" was found" | |
fi | |
if [[ $updateAppcast == 'true' && ( -z $tag || $githubRelease != 'true' ) ]] ; then | |
echo "::notice::Updating the sparkle appcast is always skipped for untagged or non-github release builds" | |
fi | |
echo "version=$version" >> $GITHUB_OUTPUT | |
echo "bundleid=$bundleid" >> $GITHUB_OUTPUT | |
echo "releasename=$releasename" >> $GITHUB_OUTPUT | |
echo "archivename=$releasenameNoSpaces" >> $GITHUB_OUTPUT | |
echo "githubRelease=$githubRelease" >> $GITHUB_OUTPUT | |
echo "updateAppcast=$updateAppcast" >> $GITHUB_OUTPUT | |
echo "prerelease=$isPrerelease" >> $GITHUB_OUTPUT | |
if [[ -n $tag ]] ; then | |
echo "tag=$tag" >> $GITHUB_OUTPUT | |
fi | |
- name: Build | |
id: build | |
run: | | |
: | |
buildlogfile=xcodebuild-out.txt | |
echo "- Build with xcodebuild from $(xcodebuild -version)" | |
# requires that env.projectfile is the name of the .xcodeproj, env.buildscheme is | |
# a valid build scheme, and and env.bundlename is name of the produced .app | |
# note: not sure why ONLY_ACTIVE_ARCH=NO is required for xcodebuild, it should | |
# already be NO for Release configuration | |
set -o pipefail && xcodebuild ONLY_ACTIVE_ARCH=NO clean build analyze \ | |
-scheme "${{ env.buildscheme }}" -configuration Release \ | |
-project "${{ env.projectfile }}" -derivedDataPath . | \ | |
tee "$buildlogfile" | xcbeautify --renderer github-actions | |
if [[ ! -d "${{ env.builddir }}/${{ env.bundlename }}" ]]; then | |
echo "::error::Unable to find the built app bundle" | |
exit 1 | |
fi | |
echo "- Extract bundle version from app" | |
plutil -extract CFBundleVersion raw \ | |
"Build/Products/Release/${{ env.bundlename }}/Contents/Info.plist" | |
bundleVersion=$(plutil -extract CFBundleVersion raw \ | |
"Build/Products/Release/${{ env.bundlename }}/Contents/Info.plist" 2> /dev/null) | |
if [[ -z $bundleVersion && "${{ steps.version.outputs.updateAppcast }}" == "true" ]] ; then | |
echo "::error::Unable to find the app's bundle version" | |
exit 1 | |
elif [[ -z $bundleVersion ]] ; then | |
echo "::warning::Unable to find the app's bundle version, workflows updating Sparkle appcast require this, but this one can continue" | |
fi | |
echo "version=$bundleVersion" >> $GITHUB_OUTPUT | |
echo "log=$buildlogfile" >> $GITHUB_OUTPUT | |
echo "appbundle=${{ env.builddir }}/${{ env.bundlename }}" >> $GITHUB_OUTPUT | |
- name: Save Build Log as Artifact | |
if: ${{ success() || failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: Build log | |
path: | | |
${{ steps.build.outputs.log }} | |
- name: Setup Keychain | |
run: | | |
: | |
if [[ -z "${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_CI_KEYCHAIN_PWD not defined" | |
exit 1 | |
fi | |
if [[ -z "${{ secrets.PROD_MACOS_CERTIFICATE }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_CERTIFICATE not defined" | |
exit 1 | |
fi | |
if [[ -z "${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_CERTIFICATE_PWD not defined" | |
exit 1 | |
fi | |
# Turn our base64-encoded certificate back to a regular .p12 file | |
echo "- Base64-decode certificate to make \"certificate.p12\"" | |
echo "${{ secrets.PROD_MACOS_CERTIFICATE }}" | base64 --decode > certificate.p12 | |
# We need to create a new keychain, one that we can keep unlocked, | |
# otherwise using the certificate will prompt with a UI dialog asking for | |
# the certificate password, which won't work in a headless CI environment | |
echo "- Create unlocked keychain \"build.keychain\"" | |
security create-keychain -p "${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}" build.keychain | |
security default-keychain -s build.keychain | |
security set-keychain-settings build.keychain # omitted '-t N' option means no timeout | |
security unlock-keychain -p "${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}" build.keychain | |
echo "- Import \"certificate.p12\" into \"build.keychain\"" | |
security import certificate.p12 -P "${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}" \ | |
-T "$(xcrun --find codesign)" -t cert -f pkcs12 -k build.keychain | |
security list-keychain -d user -s build.keychain | |
security set-key-partition-list -S apple-tool:,apple: \ | |
-k "${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}" build.keychain | |
- name: Codesign App Bundle | |
run: | | |
: | |
if [[ -z "${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_CERTIFICATE_NAME not defined" | |
exit 1 | |
fi | |
# Codesign our app bundle, specifying the Hardened runtime option | |
echo "- Sign subcomponents..." | |
# this is thanks to https://stackoverflow.com/a/11284404/592739 | |
# within this section change the Internal Field Separator (IFS) to | |
# iterate over newline-separated paths that contain spaces | |
savedIFS=$IFS | |
IFS=$(echo -en "\n\b") | |
subitems="" | |
addsubitems() | |
{ | |
if [ -z "$subitems" ] ; then | |
subitems="$1" | |
else | |
subitems="$subitems"$'\n'"$1" | |
fi | |
} | |
frameworksdir="${{ steps.build.outputs.appbundle }}/Contents/Frameworks" | |
if [ -d "$frameworksdir" ] ; then | |
frameworksdirdylibs=$(find "$frameworksdir" -depth -name "*.dylib") | |
if [ -n "$frameworksdirdylibs" ] ; then | |
addsubitems "$frameworksdirdylibs" | |
fi | |
frameworksdirbundles=$(find "$frameworksdir" -depth -type d -name "*.bundle") | |
if [ -n "$frameworksdirbundles" ] ; then | |
addsubitems "$frameworksdirbundles" | |
fi | |
frameworksdirframeworks=$(find "$frameworksdir" -depth -type d -name "*.framework") | |
if [ -n "$frameworksdirframeworks" ] ; then | |
for framework in $frameworksdirframeworks; do | |
frameworksubapp=$(find "$framework" -depth -type d -name "*.app") | |
if [ -n "$frameworksubapp" ] ; then | |
addsubitems "$frameworksubapp" | |
fi | |
frameworksubapp=$(find "$framework" -depth -type d -name "*.xpc") | |
if [ -n "$frameworksubapp" ] ; then | |
addsubitems "$frameworksubapp" | |
fi | |
# search for executables with limited depth to avoid ones within an .app | |
frameworkname=$(basename -s ".framework" "$framework") | |
frameworksubexecutable=$(find "$framework" -maxdepth 4 -type f -perm +111 \ | |
-not -name "$frameworkname") | |
if [ -n "$frameworksubexecutable" ] ; then | |
addsubitems "$frameworksubexecutable" | |
fi | |
done | |
addsubitems "$frameworksdirframeworks" | |
fi | |
fi | |
# potentially grab more subitems from other places within the .app here | |
# ie. resourcesdir="${{ steps.build.outputs.appbundle }}/Contents/Resources" | |
for subitem in $subitems; do | |
xcrun codesign --force --sign "${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}" \ | |
--options runtime -v "$subitem" | |
done | |
# would instead do this to if any subcomponents themselves included entitlements: | |
# for subitem in $subitems; do | |
# echo -n "" > subentitlements.xml # codesign doesn't erase prev contents but appends, avoid this problem | |
# xcrun codesign -d --entitlements subentitlements.xml --xml "$subitem" | |
# if [ -s subentitlements.xml ] ; then | |
# xcrun codesign --force --sign "${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}" \ | |
# --entitlements subentitlements.xml --options runtime -v "$subitem" | |
# else | |
# xcrun codesign --force --sign "${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}" \ | |
# --options runtime -v "$subitem" | |
# fi | |
# done | |
IFS=$savedIFS | |
echo "- Sign app" | |
xcrun codesign -d --entitlements entitlements.xml --xml "${{ steps.build.outputs.appbundle }}" | |
xcrun codesign --force --sign "${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}" \ | |
--entitlements entitlements.xml --options runtime -v "${{ steps.build.outputs.appbundle }}" | |
- name: Notarize app bundle | |
run: | | |
: | |
if [[ -z "${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_NOTARIZATION_APPLE_ID not defined" | |
exit 1 | |
fi | |
if [[ -z "${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_NOTARIZATION_TEAM_ID not defined" | |
exit 1 | |
fi | |
if [[ -z "${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}" ]] ; then | |
echo "::error::Secret PROD_MACOS_NOTARIZATION_PWD not defined" | |
exit 1 | |
fi | |
# Store the notarization credentials so that we can prevent a UI password dialog | |
# from blocking the CI | |
echo "- Create keychain profile" | |
xcrun notarytool store-credentials "notarytool-profile" \ | |
--apple-id "${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}" \ | |
--team-id "${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}" \ | |
--password "${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}" | |
# We can't notarize an app bundle directly, but we need to compress it as an archive. | |
# Therefore, we create a zip file containing our app bundle, so that we can send it to the | |
# notarization service | |
echo "- Create temp notarization archive" | |
ditto -c -k --keepParent "${{ steps.build.outputs.appbundle }}" "notarization.zip" | |
# Here we send the notarization request to the Apple's Notarization service, waiting for the result. | |
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App | |
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if | |
# you're curious | |
echo "- Notarize app" | |
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait \ | |
2>&1 | tee notarytool-out.txt | |
if [ ${PIPESTATUS[0]} -ne 0 ] || grep -q Invalid notarytool-out.txt ; then | |
if sed -nr '/^[[:space:]]*id: (.*)$/{s//\1/p;q;}' notarytool-out.txt > notarytool-id.txt ; then | |
echo "- Extract notarytool failure log" | |
xcrun notarytool log "$(<notarytool-id.txt)" --keychain-profile "notarytool-profile" | |
fi | |
exit 1 | |
fi | |
# Finally, we need to "attach the staple" to our executable, which will allow our app to be | |
# validated by macOS even when an internet connection is not available. | |
echo "- Attach staple" | |
xcrun stapler staple "${{ steps.build.outputs.appbundle }}" | |
- name: Build Zip File | |
id: zip | |
run: | | |
: | |
archiveDir="${{ env.builddir }}/Sparkle" | |
archiveFileName="${{ steps.version.outputs.archivename }}.zip" | |
echo "- Compress built app to \"$(basename $archiveDir)/$archiveFileName\"" | |
mkdir "$archiveDir" | |
ditto -c -k --sequesterRsrc --keepParent "${{ steps.build.outputs.appbundle }}" \ | |
"$archiveDir/$archiveFileName" | |
echo "file=$archiveDir/$archiveFileName" >> $GITHUB_OUTPUT | |
echo "directory=$archiveDir" >> $GITHUB_OUTPUT | |
- name: Disk Image | |
id: dmg | |
run: | | |
: | |
if ! command -v create-dmg >/dev/null 2>&1 ; then | |
echo "::warning::Required helper script not found: create-dmg. Skipping dmg creation" | |
# not sure if need to do `echo "file=whatever" >> $GITHUB_OUTPUT` | |
# to make release step work, or if empty/missing steps.dmg.output.file is ok | |
exit 0 | |
fi | |
imageDir="${{ env.builddir }}/Image" | |
imageFileName="${{ steps.version.outputs.archivename }}.dmg" | |
readmeFilename="${{ env.productname }} version ${{ steps.version.outputs.version }} read me.rtf" | |
echo "- Copy built app to \"$(basename $imageDir)/${{ env.bundlename }}\"" | |
mkdir "$imageDir" | |
ditto "${{ steps.build.outputs.appbundle }}" "$imageDir/${{ env.bundlename }}" | |
echo "- Copy readme file from source repo to \"$(basename $imageDir)/$readmeFilename\"" | |
cp "Designs/Cleepp/Cleepp download read me.rtf" "$imageDir/$readmeFilename" | |
echo "- Build disk image \"${{ env.builddir }}/$imageFileName\"" | |
create-dmg --hdiutil-quiet \ | |
--volname "${{ steps.version.outputs.releasename }}" \ | |
--window-size 540 160 --icon-size 64 \ | |
--icon "$readmeFilename" 40 60 \ | |
--icon "${{ env.bundlename }}" 200 60 \ | |
--app-drop-link 360 60 \ | |
"${{ env.builddir }}/$imageFileName" "$imageDir" | |
echo "filename=${{ env.bundlename }}" >> $GITHUB_OUTPUT | |
echo "file=${{ env.builddir }}/$imageFileName" >> $GITHUB_OUTPUT | |
- name: Sign and notarize disk image | |
run: | | |
: | |
echo "- Notarize disk image" | |
xcrun notarytool submit "${{ steps.dmg.outputs.file }}" --keychain-profile "notarytool-profile" --wait \ | |
2>&1 | tee notarytool-out.txt | |
if [ ${PIPESTATUS[0]} -ne 0 ] || grep -q Invalid notarytool-out.txt ; then | |
if sed -nr '/^[[:space:]]*id: (.*)$/{s//\1/p;q;}' notarytool-out.txt > notarytool-id.txt ; then | |
echo "- Extract notarytool failure log" | |
xcrun notarytool log "$(<notarytool-id.txt)" --keychain-profile "notarytool-profile" | |
fi | |
exit 1 | |
fi | |
echo "- Attach staple" | |
xcrun stapler staple "${{ steps.dmg.outputs.file }}" | |
- name: Release Notes | |
id: notes | |
run: | | |
: | |
echo "- Collect release notes" | |
changeLogFilename=CHANGELOG.md | |
tempNotesFilename="${{ steps.version.outputs.releasename }}.temp.md" | |
currentNotesFilename="${{ steps.version.outputs.releasename }}.md" | |
if [[ ! -f $changeLogFilename ]] ; then | |
echo "::warning::Change log file is missing" | |
numlines=0 | |
else | |
echo -n "" > "${{ env.builddir }}/$tempNotesFilename" | |
thisversion='' | |
prevversion='' | |
while read line || [[ -n $line ]] ; do | |
if [[ -z $thisversion ]]; then | |
thisversion=$(echo $line | sed -n -E 's/^#+ version ([0-9.dabrc]+) .*$/\1/p') | |
if [[ -n $thisversion ]] ; then | |
if [[ $thisversion != "${{ steps.version.outputs.version }}" ]] ; then | |
echo "::warning::Version $thisversion at the top of the change log doesn't match build version ${{ steps.version.outputs.version }}" | |
break | |
fi | |
echo "- Found section for build version ${{ steps.version.outputs.version }} at the top of the change log" | |
fi | |
continue | |
fi | |
prevversion=$(echo $line | sed -n -E 's/^#+ version ([0-9.dabrc]+) .*$/\1/p') | |
if [[ -n $prevversion ]] ; then | |
break | |
fi | |
echo $line >> "${{ env.builddir }}/$tempNotesFilename" | |
done < "$changeLogFilename" | |
# sed command removes initial and trailing blank lines, don't ask me how it works | |
# from https://unix.stackexchange.com/a/552195 | |
cat "${{ env.builddir }}/$tempNotesFilename" | sed -e '/./,$!d' -e :a -e '/^\n*$/{$d;N;ba' -e '}' \ | |
> "${{ env.builddir }}/$currentNotesFilename" | |
numlines=$(wc -l "${{ env.builddir }}/$currentNotesFilename" | cut -w -f2) | |
fi | |
if [[ $numlines -gt 0 ]] ; then | |
echo "- Save $numlines lines of release notes to \"$currentNotesFilename\"" | |
else | |
echo "- Save placeholder release notes to \"$currentNotesFilename\"" | |
echo "Release notes unavailable at this time" > "${{ env.builddir }}/$currentNotesFilename" | |
fi | |
echo "filename=$currentNotesFilename" >> $GITHUB_OUTPUT | |
echo "file=${{ env.builddir }}/$currentNotesFilename" >> $GITHUB_OUTPUT | |
- name: Setup Sparkle | |
if: ${{ success() && steps.version.outputs.githubRelease == 'true' && steps.version.outputs.updateAppcast == 'true' && steps.version.outputs.tag }} | |
uses: jozefizso/setup-sparkle@v1 | |
with: | |
version: 2.6.0 | |
- name: Generate Sparkle appcast.xml | |
id: sparkle | |
if: ${{ success() && steps.version.outputs.githubRelease == 'true' && steps.version.outputs.updateAppcast == 'true' && steps.version.outputs.tag }} | |
run: | | |
: | |
echo "::add-mask::${{ secrets.SPARKLE_PRIVATE_KEY }}" | |
if [[ -z "${{ secrets.SPARKLE_PRIVATE_KEY }}" ]] ; then | |
echo "::warning::Secret SPARKLE_PRIVATE_KEY not defined. Skipping Sparkle step" | |
exit 0 | |
fi | |
if ! command -v pandoc >/dev/null 2>&1 || ! command -v generate_appcast >/dev/null 2>&1 ; then | |
echo "::warning::Required executables not found: pandoc, generate_appcast. Skipping Sparkle step" | |
exit 0 | |
fi | |
htmlNotesFilename="$(basename -s .zip "${{ steps.zip.outputs.file }}")".html | |
htmlTemplateFilename=htmlnotestemplate.html | |
releasesURL="https://github.com/${{ github.repository }}/releases" | |
downloadURLPrefix="https://github.com/${{ github.repository }}/releases/download/v${{ steps.version.outputs.version }}/" | |
echo "- Convert release notes to html" | |
echo '$body$' > "$htmlTemplateFilename" | |
if ! pandoc --standalone --template "$htmlTemplateFilename" --metadata title="Release Notes" \ | |
"${{ steps.notes.outputs.file }}" \ | |
> "${{ steps.zip.outputs.directory }}/$htmlNotesFilename" | |
then | |
echo "::warning::pandoc failed, no new appcast.xml file generated" | |
exit 0 | |
fi | |
echo "- Update appcast" | |
cp ./appcast.xml "${{ steps.zip.outputs.directory }}/appcast.xml" # dir needs current xml file | |
if ! echo "${{ secrets.SPARKLE_PRIVATE_KEY }}" | generate_appcast --ed-key-file - \ | |
--link "$releasesURL" --download-url-prefix "$downloadURLPrefix" \ | |
--embed-release-notes -o ./appcast.xml "${{ steps.zip.outputs.directory }}" | |
then | |
echo "::warning::generate_appcast failed, no new appcast.xml file generated" | |
echo | |
echo "ls -alF \"${{ steps.zip.outputs.directory }}\"" | |
ls -alF "${{ steps.zip.outputs.directory }}" | |
echo | |
echo "cat \"${{ steps.zip.outputs.directory }}/$htmlNotesFilename\"" | |
cat "${{ steps.zip.outputs.directory }}/$htmlNotesFilename" | |
echo | |
exit 0 | |
fi | |
echo "appcastGenerated=true" >> $GITHUB_OUTPUT | |
- name: Commit appcast.xml | |
if: ${{ success() && steps.version.outputs.githubRelease == 'true' && steps.version.outputs.updateAppcast == 'true' && steps.version.outputs.tag && steps.sparkle.outputs.appcastGenerated }} | |
uses: stefanzweifel/git-auto-commit-action@v5 | |
with: | |
commit_message: Automated Change to appcast.xml | |
file_pattern: "appcast.xml" | |
status_options: "--untracked-files=no" | |
- name: Save build as artifact | |
if: ${{ success() && !(steps.version.outputs.githubRelease == 'true' && steps.version.outputs.tag) }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: Non-App Store build | |
path: | | |
${{ steps.dmg.outputs.file }} | |
- name: Save release notes as artifact | |
if: ${{ success() && !(steps.version.outputs.githubRelease == 'true' && steps.version.outputs.tag) }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: Release notes | |
path: | | |
${{ steps.notes.outputs.file }} | |
- name: Draft Tagged Release | |
if: ${{ success() && steps.version.outputs.githubRelease == 'true' && steps.version.outputs.tag }} | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: ${{ steps.version.outputs.releasename }} | |
tag_name: ${{ steps.version.outputs.tag }} | |
draft: true | |
prerelease: ${{ steps.version.outputs.prerelease }} | |
body_path: ${{ steps.notes.outputs.file }} | |
files: | | |
${{ steps.zip.outputs.file }} | |
${{ steps.dmg.outputs.file }} | |
fail_on_unmatched_files: false | |
- name: Fin | |
run: | | |
: | |
if [[ steps.version.outputs.githubRelease == 'true' && -n "${{ steps.version.outputs.tag }}" ]] ; then | |
echo "::notice::Release \"${{ steps.version.outputs.releasename }}\" draft created continaing \"${{ steps.dmg.outputs.filename }}\"" | |
else | |
echo "::notice::Release \"${{ steps.version.outputs.releasename }}\" built and saved as artifact \"${{ steps.dmg.outputs.filename }}\"" | |
fi |