diff --git a/action.yml b/action.yml index 1060d9a..98d31fb 100644 --- a/action.yml +++ b/action.yml @@ -4,14 +4,42 @@ branding: icon: 'box' color: 'purple' inputs: + production: + description: "PRODUCTION env var for wails build" + default: "false" + required: false build: description: "Platform to build for" required: false default: "true" + build-ldflags: + description: "LDFLAGS for go build" + required: false sign: description: "Sign the build" required: false default: "false" + sign-sparkle: + description: "Sign the sparkle framework" + required: false + sparkle-signing-key: + description: "Signing key for Sparkle" + required: false + default: "true" + appcast-url: + description: "URL to check for app updates" + required: false + app-upload-url: + description: "URL to send appcasts" + required: false + app-upload-handshake: + description: "handshake for app upload" + required: false + default: "true" + notarize: + description: "notarize the build" + required: false + default: "false" package: description: "Uploads workflow & uploads tag builds to a release" required: false @@ -31,6 +59,10 @@ inputs: description: "Platform to build for" required: false default: "darwin/universal" + build-version: + description: "Version of app being built" + required: false + default: "1.0.0" build-tags: description: "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated" required: false @@ -75,6 +107,14 @@ inputs: description: "MacOS Application Certificate id" required: false default: '' + sign-macos-app-email: + description: "Apple ID email" + required: false + default: '' + sign-macos-app-team-id: + description: "Apple ID team" + required: false + default: '' sign-macos-apple-password: description: "MacOS Apple password" required: false @@ -184,24 +224,22 @@ runs: # install wails - name: Install Wails if: inputs.build == 'true' && inputs.wails-dev-build == 'false' - run: go install github.com/wailsapp/wails/v2/cmd/wails@${{inputs.wails-version}} + run: go install github.com/wailsapp/wails/v3/cmd/wails3@${{inputs.wails-version}} shell: bash - name: Install macOS Wails deps if: runner.os == 'macOS' - run: brew install mitchellh/gon/gon + run: brew install create-dmg jq shell: bash # Building step - - name: Build App + - name: Set App Version & Updates if: inputs.build == 'true' env: - BUILD_OPTIONS: ${{ steps.build_options.outputs.BUILD_OPTIONS }} - working-directory: ${{ inputs.app-working-directory }} - run: wails build --platform ${{inputs.build-platform}} -webview2 ${{inputs.wails-build-webview2}} -o ${{inputs.build-name}} $BUILD_OPTIONS - shell: bash - - name: Add macOS perms - if: inputs.build == 'true' && runner.os == 'macOS' - working-directory: ${{ inputs.app-working-directory }} - run: chmod +x build/bin/*/Contents/MacOS/* + VERSION: ${{ inputs.build-version }} + APPCAST_URL: ${{inputs.appcast-url}} + run: | + /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" build/darwin/Info.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" build/darwin/Info.plist + /usr/libexec/PlistBuddy -c "Set :SUFeedURL $APPCAST_URL" build/darwin/Info.plist shell: bash - name: Add Linux perms if: inputs.build == 'true' && runner.os == 'Linux' @@ -209,6 +247,15 @@ runs: run: chmod +x build/bin/* shell: bash # Package and Sign MacOS + - name: Package App + if: inputs.build == 'true' + env: + PRODUCTION: ${{inputs.production}} + BUILD_OPTIONS: ${{ steps.build_options.outputs.BUILD_OPTIONS }} + LDFLAGS: ${{inputs.build-ldflags}} + working-directory: ${{ inputs.app-working-directory }} + run: wails3 package + shell: bash - name: Import Code-Signing Certificates for macOS if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/') uses: Apple-Actions/import-codesign-certs@v1 @@ -224,41 +271,103 @@ runs: p12-file-base64: ${{ inputs.sign-macos-installer-cert }} p12-password: ${{ inputs.sign-macos-installer-cert-password }} create-keychain: false - - name: Sign our macOS binary - if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/') + - name: Sign sparkle framework + if: runner.os == 'macOS' && inputs.sign-sparkle != 'false' && startsWith(github.ref, 'refs/tags/') shell: bash working-directory: ${{ inputs.app-working-directory }} env: - APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }} + CODE_SIGN_IDENTITY: ${{ inputs.sign-macos-app-id }} run: | - echo "Signing Package" - gon -log-level=info ./build/darwin/gon-sign.json - - name: Build .app zip file + codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime bin/${{inputs.build-name}}.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc + codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime --preserve-metadata=entitlements bin/${{inputs.build-name}}.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc + codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime bin/${{inputs.build-name}}.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate + codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime bin/${{inputs.build-name}}.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app + codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime bin/${{inputs.build-name}}.app/Contents/Frameworks/Sparkle.framework + + codesign -f -s "$CODE_SIGN_IDENTITY" --timestamp --entitlements build/darwin/entitlements.plist -o runtime bin/${{inputs.build-name}}.app + - name: Build .app DMG file if: runner.os == 'macOS' working-directory: ${{ inputs.app-working-directory }} + env: + APP_NAME: ${{inputs.build-name}} shell: bash run: | - ditto -c -k --keepParent ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app.zip - - name: Building Installer - if: runner.os == 'macOS' && inputs.sign != 'false' && inputs.sign-macos-installer-id != '' && startsWith(github.ref, 'refs/tags/') - shell: bash - working-directory: ${{ inputs.app-working-directory }} - run: | - productbuild --sign '${{inputs.sign-macos-installer-id}}' --component ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.pkg - - name: Building Installer - if: runner.os == 'macOS' && inputs.sign-macos-installer-id == '' && startsWith(github.ref, 'refs/tags/') + DMG_FILE_NAME="${APP_NAME}.dmg" + VOLUME_NAME="${APP_NAME}" + SOURCE_FOLDER_PATH="bin/" + + # remove loose binary + rm "bin/${APP_NAME}" + + # Create the DMG + create-dmg \ + --volname "${VOLUME_NAME}" \ + --background "build/darwin/installer_background.png" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon-size 100 \ + --icon "${APP_NAME}.app" 200 190 \ + --hide-extension "${APP_NAME}.app" \ + --app-drop-link 600 185 \ + "${DMG_FILE_NAME}" \ + "${SOURCE_FOLDER_PATH}" + + mv $DMG_FILE_NAME bin/ + - name: Notarising and stapling DMG + if: runner.os == 'macOS' && inputs.sign != 'false' && inputs.notarize != 'false' && startsWith(github.ref, 'refs/tags/') shell: bash working-directory: ${{ inputs.app-working-directory }} + env: + APPLE_ID: ${{ inputs.sign-macos-app-email }} + TEAM_ID: ${{ inputs.sign-macos-app-team-id }} + APPLE_DEVELOPER_PASSWORD: ${{ inputs.sign-macos-apple-password }} run: | - productbuild --component ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.pkg - - name: Notarising Installer and zip - if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/') + xcrun notarytool submit --apple-id $APPLE_ID --team-id $TEAM_ID --password $APPLE_DEVELOPER_PASSWORD --wait --timeout 5m bin/${{inputs.build-name}}.dmg + xcrun stapler staple bin/${{inputs.build-name}}.dmg + + - name: Upload app for sparkle + if: runner.os == 'macOS' && inputs.sign != 'false' && inputs.notarize != 'false' && startsWith(github.ref, 'refs/tags/') shell: bash working-directory: ${{ inputs.app-working-directory }} env: - APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }} + SPARKLE_KEY: ${{inputs.sparkle-signing-key}} + URL: ${{inputs.app-upload-url}} + HANDSHAKE: ${{inputs.app-upload-handshake}} run: | - gon -log-level=info ${{ inputs.app-working-directory }}/build/darwin/gon-notarize.json + # sign dmg for sparkle and format to json + echo "signing with sparkle sign_update..." + echo $SPARKLE_KEY > sparkle.key + SIG=$(./sparkle/bin/sign_update -f ./sparkle.key bin/${{inputs.build-name}}.dmg) + echo "signature output is $SIG" + echo "parsing..." + JSON=$(echo $SIG | sed -E 's/sparkle:edSignature=([^ ]*) length=(.*)/{"signature":\1,"byteLength":\2}/') + + # Create a temporary file for the JSON payload + TEMP_JSON=$(mktemp) + + # Base64 encode the file directly to a temporary file + base64 -i "bin/${{inputs.build-name}}.dmg" > temp_b64.txt + + # Create the initial JSON structure + echo $JSON | jq -c --arg version "${{ inputs.build-version }}" --arg msg "${{ github.event.head_commit.message }}" '. + { "version": $version, "releaseNotes": $msg }' > "$TEMP_JSON" + + # Use jq with slurp to read the base64 content from file + jq -c --rawfile file temp_b64.txt '.file = $file' "$TEMP_JSON" > "${TEMP_JSON}.tmp" + mv "${TEMP_JSON}.tmp" "$TEMP_JSON" + + # Wrap in the final structure + jq -c '{ version: . }' "$TEMP_JSON" > "${TEMP_JSON}.tmp" + mv "${TEMP_JSON}.tmp" "$TEMP_JSON" + + # Make the curl request using the temporary file + curl -X POST \ + -H "Content-Type: application/json" \ + -H "x-app-version-handshake: $HANDSHAKE" \ + -d "@$TEMP_JSON" \ + "$URL" + + # Clean up temporary files + rm -f "$TEMP_JSON" temp_b64.txt # Windows signing - name: Sign Windows binaries shell: powershell @@ -278,12 +387,4 @@ runs: if: inputs.package == 'true' with: name: Wails Build ${{runner.os}} ${{inputs.build-name}} - path: | - */bin/ - *\bin\* - - name: Release - uses: softprops/action-gh-release@v1 - if: inputs.package == 'true' && startsWith(github.ref, 'refs/tags/') - with: - files: | - */bin/* + path: ./bin/${{inputs.build-name}}.dmg