diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e8ff5e6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,77 @@ +name: Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + version: + description: "Release version, for example 0.1.0" + required: true + default: "0.1.0" + +permissions: + contents: write + +jobs: + macos-arm64-dmg: + name: Build macOS ARM64 DMG + runs-on: macos-15 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Resolve version + id: version + shell: bash + run: | + if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then + VERSION="${GITHUB_REF_NAME#v}" + else + VERSION="${{ inputs.version }}" + fi + + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Version: $VERSION" + + - name: Show build environment + run: | + uname -a + swift --version + + - name: Build app bundle + env: + MARKETING_VERSION: ${{ steps.version.outputs.version }} + BUILD_NUMBER: ${{ github.run_number }} + run: Scripts/create_app_bundle.sh + + - name: Verify ARM64 binary + run: | + file build/Firelink.app/Contents/MacOS/Firelink + lipo -archs build/Firelink.app/Contents/MacOS/Firelink | grep -qx arm64 + + - name: Create DMG + env: + VERSION: ${{ steps.version.outputs.version }} + ARCH: arm64 + run: Scripts/create_dmg.sh + + - name: Upload workflow artifact + uses: actions/upload-artifact@v4 + with: + name: Firelink-${{ steps.version.outputs.version }}-mac-arm64-dmg + path: dist/*.dmg + if-no-files-found: error + + - name: Publish GitHub release + if: github.ref_type == 'tag' + env: + GH_TOKEN: ${{ github.token }} + run: | + if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then + gh release upload "$GITHUB_REF_NAME" dist/*.dmg --clobber + else + gh release create "$GITHUB_REF_NAME" dist/*.dmg --generate-notes --verify-tag + fi diff --git a/.gitignore b/.gitignore index fdfc629..914384b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store .build/ build/ +dist/ DerivedData/ .vscode/ *.xcuserdata/ diff --git a/Makefile b/Makefile index c9a44e6..5f513cf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build app run clean +.PHONY: build app dmg run clean build: swift build -c release @@ -6,9 +6,12 @@ build: app: Scripts/create_app_bundle.sh +dmg: app + Scripts/create_dmg.sh + run: swift run Firelink clean: swift package clean - rm -rf build + rm -rf build dist diff --git a/README.md b/README.md index 843e137..e3dd804 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,18 @@ Or build a production `.app` bundle: make app && open build/Firelink.app ``` +Create a local Apple Silicon DMG: +```bash +make dmg +``` + +### Release +GitHub Actions builds and publishes the macOS ARM64 DMG when a version tag is pushed: +```bash +git tag v0.1.0 +git push origin v0.1.0 +``` + --- ## 🗺️ Roadmap diff --git a/Scripts/create_app_bundle.sh b/Scripts/create_app_bundle.sh index 40a3c51..9b894d1 100755 --- a/Scripts/create_app_bundle.sh +++ b/Scripts/create_app_bundle.sh @@ -4,6 +4,8 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" APP_NAME="Firelink" CONFIGURATION="${CONFIGURATION:-release}" +MARKETING_VERSION="${MARKETING_VERSION:-0.1.0}" +BUILD_NUMBER="${BUILD_NUMBER:-1}" APP_DIR="$ROOT_DIR/build/$APP_NAME.app" CONTENTS_DIR="$APP_DIR/Contents" MACOS_DIR="$CONTENTS_DIR/MacOS" @@ -34,9 +36,9 @@ cat > "$CONTENTS_DIR/Info.plist" <CFBundlePackageType APPL CFBundleShortVersionString - 0.1.0 + $MARKETING_VERSION CFBundleVersion - 1 + $BUILD_NUMBER LSMinimumSystemVersion 14.0 LSApplicationCategoryType diff --git a/Scripts/create_dmg.sh b/Scripts/create_dmg.sh new file mode 100755 index 0000000..94bc65c --- /dev/null +++ b/Scripts/create_dmg.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +APP_NAME="Firelink" +VERSION="${VERSION:-0.1.0}" +ARCH="${ARCH:-arm64}" +APP_DIR="$ROOT_DIR/build/$APP_NAME.app" +DIST_DIR="$ROOT_DIR/dist" +DMG_STAGING_DIR="$ROOT_DIR/build/dmg" +DMG_PATH="$DIST_DIR/$APP_NAME-$VERSION-mac-$ARCH.dmg" + +if [[ ! -d "$APP_DIR" ]]; then + echo "Missing app bundle: $APP_DIR" >&2 + echo "Run Scripts/create_app_bundle.sh first." >&2 + exit 1 +fi + +rm -rf "$DMG_STAGING_DIR" +mkdir -p "$DMG_STAGING_DIR" "$DIST_DIR" +cp -R "$APP_DIR" "$DMG_STAGING_DIR/" +ln -s /Applications "$DMG_STAGING_DIR/Applications" + +rm -f "$DMG_PATH" +hdiutil create \ + -volname "$APP_NAME $VERSION" \ + -srcfolder "$DMG_STAGING_DIR" \ + -ov \ + -format UDZO \ + "$DMG_PATH" + +echo "Created $DMG_PATH"