Cicd: Fix: Pull icon images in LFS #13
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: 🍏 🏁 macOS and Windows Build, Bundle, and Publish | |
on: | |
push: | |
branches: | |
- main | |
- 'cicd/**' | |
jobs: | |
build_macos: | |
runs-on: macos-latest | |
outputs: | |
zip_filename: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
steps: | |
- name: Get repository name | |
id: get_repo_name | |
run: echo "repo_name=$(basename $GITHUB_REPOSITORY)" >> "$GITHUB_OUTPUT" | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Fetch all history for all tags and branches instead of `1`, which fetches only the current branch. | |
fetch-depth: 0 | |
# Include icon images. | |
lfs: true | |
- name: Install Rust toolchain with M1 chip support | |
uses: dtolnay/rust-toolchain@stable | |
with: | |
toolchain: stable | |
targets: aarch64-apple-darwin, x86_64-apple-darwin | |
- name: Set up Rust Build Caching | |
uses: Swatinem/rust-cache@v2 | |
# Build binaries in parallel to speed up build times. | |
- name: Build x86_64 binary and Apple Silicon binaries | |
run: cargo build --release --target x86_64-apple-darwin --target aarch64-apple-darwin | |
# Cargo Bundle can't create universal binaries, so make one with `lipo` and put it where Cargo Bundle expects to find it (`target/release/`). | |
- name: Create a universal binary from the x86_64 and ARM64 binaries and put it in `target/release/` | |
run: lipo -create -output target/release/${{ steps.get_repo_name.outputs.repo_name }} -arch x86_64 target/x86_64-apple-darwin/release/${{ steps.get_repo_name.outputs.repo_name }} -arch arm64 target/aarch64-apple-darwin/release/${{ steps.get_repo_name.outputs.repo_name }} | |
- name: Install `cargo-bundle` for creating `.app` directory structure and `.plist` with `cargo bundle` | |
uses: baptiste0928/cargo-install@v2 | |
with: | |
crate: cargo-bundle | |
# Future: Cargo Bundle will build a universal binary and bundle it into an `*.app`. | |
- name: Create `*.app` directory structure and `.plist` file | |
env: | |
# Skip building binaries because they've already been built and combined. | |
CARGO_BUNDLE_SKIP_BUILD: "true" | |
run: cargo bundle --release --format osx | |
# Move the `*.app` directory to the top level of the repo so it's easier to access for code signing and notarization. | |
- name: Move bundled `*.app` directory to top level | |
run: mv target/release/bundle/osx/${{ steps.get_repo_name.outputs.repo_name }}.app . | |
- name: Codesign app bundle | |
env: | |
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} | |
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} | |
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} | |
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} | |
run: | | |
# Convert base64-encoded certificate back to .p12 file. | |
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 | |
# Create a new keychain so no UI dialogs are generated. | |
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
security default-keychain -s build.keychain | |
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign | |
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
# Codesign the app bundle with hardened runtime so it passes notarization. | |
/usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime ${{ steps.get_repo_name.outputs.repo_name }}.app -v | |
- name: "Notarize app bundle" | |
env: | |
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} | |
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} | |
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} | |
run: | | |
# Prevent UI password dialog by storing notarization credentials. | |
echo "Create keychain profile" | |
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" | |
# We can't notarize an app bundle directly, but we need to compress it as an archive. | |
# Compress app bundle to zip file for notarization (because we can't notarize app bundles directly). | |
echo "Creating temporary notarization archive" | |
ditto -c -k --keepParent "${{ steps.get_repo_name.outputs.repo_name }}.app" "notarization.zip" | |
# Send the notarization request to the Apple's Notarization service. | |
echo "Notarizing compressed app bundle" | |
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait | |
# "Attach the staple" to executable for offline MacOS validation. | |
echo "Attach staple" | |
xcrun stapler staple "${{ steps.get_repo_name.outputs.repo_name }}.app" | |
# Get the current SemVer from Cargo.toml. Ex. 2.0.3 would make v2.0.3 in this step. | |
- name: Get current SemVer version from Cargo.toml | |
id: get_current_semver | |
run: echo "semver=v$(cargo metadata --format-version 1 | jq -r '.packages | .[] | select(.name=="${{ steps.get_repo_name.outputs.repo_name }}") | .version')" >> "$GITHUB_OUTPUT" | |
- name: Create a filename for the macOS binary's zip file | |
id: create_zip_filename | |
run: echo "zip_filename=${{ steps.get_repo_name.outputs.repo_name }}_${{ steps.get_current_semver.outputs.semver }}_macos_universal.zip" >> "$GITHUB_OUTPUT" | |
- name: Compress *.app directory to *.zip, preserving resource fork and Finder information | |
run: ditto -c -k --sequesterRsrc --keepParent ${{ steps.get_repo_name.outputs.repo_name }}.app ${{ steps.create_zip_filename.outputs.zip_filename }} | |
- name: Create job summary about macOS binary creation | |
run: echo "## Created universal macOS binary" >> $GITHUB_STEP_SUMMARY | |
- name: Upload macOS executable for release | |
uses: actions/upload-artifact@v4 | |
with: | |
# Give the artifact the same name as the zip file, otherwise it'll default to "artifact" and fail b/c already exists. | |
name: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
path: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
# Fail the Action if no zip was found. | |
if-no-files-found: error | |
build_windows: | |
runs-on: ubuntu-latest | |
outputs: | |
zip_filename: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
steps: | |
- name: Get repository name | |
id: get_repo_name | |
run: echo "repo_name=$(basename $GITHUB_REPOSITORY)" >> "$GITHUB_OUTPUT" | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Fetch all history for all tags and branches instead of `1`, which fetches only the current branch. | |
fetch-depth: 0 | |
# Include icon images. | |
lfs: true | |
- name: Install Rust toolchain with M1 chip support | |
uses: dtolnay/rust-toolchain@stable | |
with: | |
toolchain: stable | |
targets: x86_64-pc-windows-gnu | |
- name: Install cross-compilation tools for building Windows executables on Ubuntu | |
run: | | |
sudo apt-get update | |
sudo apt-get install mingw-w64 | |
- name: Set up rust build caching | |
uses: Swatinem/rust-cache@v2 | |
- name: Build x86_64 Windows binary | |
run: cargo build --release --target x86_64-pc-windows-gnu | |
# Move the `*.exe` to the top level of the repo so it's easier to access. | |
- name: Move bundled `*.app` directory to top level | |
run: mv target/x86_64-pc-windows-gnu/release/${{ steps.get_repo_name.outputs.repo_name }}.exe . | |
- name: Get current SemVer version from Cargo.toml | |
id: get_current_semver | |
run: echo "semver=v$(cargo metadata --format-version 1 | jq -r '.packages | .[] | select(.name=="${{ steps.get_repo_name.outputs.repo_name }}") | .version')" >> "$GITHUB_OUTPUT" | |
- name: Create a filename for the Windows executable's zip file | |
id: create_zip_filename | |
run: echo "zip_filename=${{ steps.get_repo_name.outputs.repo_name }}_${{ steps.get_current_semver.outputs.semver }}_windows_gnu_x86_64.zip" >> "$GITHUB_OUTPUT" | |
- name: Zip the Windows executable | |
id: zip_executable | |
run: zip -j ${{ steps.create_zip_filename.outputs.zip_filename }} ${{ steps.get_repo_name.outputs.repo_name }}.exe | |
- name: Create job summary about executable creation | |
run: echo "## Created Windows executable" >> $GITHUB_STEP_SUMMARY | |
- name: Upload Windows executable for release | |
uses: actions/upload-artifact@v4 | |
with: | |
# Give the artifact the same name as the zip file, otherwise it'll default to "artifact" and fail b/c already exists. | |
name: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
path: ${{ steps.create_zip_filename.outputs.zip_filename }} | |
# Fail the Action if no zip was found. | |
if-no-files-found: error | |
publish_release: | |
needs: [build_macos, build_windows] | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
steps: | |
# We still need to check out the code for this job so we can get the SemVer. | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Fetch all history for all tags and branches instead of `1`, which fetches only the current branch. | |
fetch-depth: 0 | |
- name: Get repository name | |
id: get_repo_name | |
run: echo "repo_name=$(basename $GITHUB_REPOSITORY)" >> "$GITHUB_OUTPUT" | |
- name: Get current SemVer version from Cargo.toml | |
id: get_current_semver | |
run: echo "semver=v$(cargo metadata --format-version 1 | jq -r '.packages | .[] | select(.name=="${{ steps.get_repo_name.outputs.repo_name }}") | .version')" >> "$GITHUB_OUTPUT" | |
# Download all artifacts for this run. | |
- name: download artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
# Put artifacts in root instead of giving them their own directories. | |
merge-multiple: true | |
- name: Publish a new release | |
id: publish_release | |
uses: ncipollo/release-action@v1 | |
with: | |
tag: ${{ steps.get_current_semver.outputs.semver }} | |
# Gate release publishing to only happen on main branch. Otherwise, releasing will fail due to duplicate release names. | |
draft: ${{ github.ref == 'refs/heads/main' && 'false' || 'true' }} | |
commit: main | |
# Expect the macOS and windows zips to be available. | |
artifacts: "${{ needs.build_macos.outputs.zip_filename }},${{ needs.build_windows.outputs.zip_filename }}" | |
token: ${{ secrets.GITHUB_TOKEN }} | |
body: | | |
# New Release: ${{ steps.get_repo_name.outputs.repo_name }} ${{ steps.get_current_semver.outputs.semver }} | |
This is a new release of FolSum triggered by commit `${{ github.sha }}` on branch `${{ github.ref_name}}`. | |
## Changelog | |
- Add feature X | |
- Fix bug Y | |
- name: Generate "new release" message with a hyperlink to the release page | |
id: generate_release_message | |
run: echo "message=Created new release ${{ steps.get_current_semver.outputs.semver }} at ${{ steps.publish_release.outputs.html_url }}" >> $GITHUB_OUTPUT | |
- name: Create job summary about new release | |
run: echo "## ${{ steps.generate_release_message.outputs.message }}" >> $GITHUB_STEP_SUMMARY |