Build for macOS #486
This file contains hidden or 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 for macOS | |
| on: | |
| push: | |
| branches: | |
| - main | |
| # PR branches that alter the build process should be prefixed with `build/`, so | |
| # that this workflow runs. | |
| - 'build/**' | |
| schedule: | |
| # Run this workflow every 8 hours to repackage the pre-built Drupal CMS site | |
| # with the latest dependencies. | |
| - cron: 0 */8 * * * | |
| workflow_dispatch: | |
| jobs: | |
| # The PHP interpreter needs to have its own job so it can be built in parallel for all | |
| # supported architectures, before being merged into a universal binary while building | |
| # the app itself. | |
| php: | |
| name: PHP on ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: | |
| # x64, supported until 2027, at which point GitHub Actions will drop support for | |
| # macOS on Intel. | |
| # @see https://github.com/actions/runner-images/issues/13046 | |
| - macos-15-intel | |
| - macos-latest # arm64 | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| # The extensions and libraries needed to build PHP. These need to be variables so we can | |
| # use them to generate a cache key. | |
| # @see https://static-php.dev/en/guide/cli-generator.html | |
| PHP_EXTENSIONS: bz2,ctype,curl,dom,filter,gd,iconv,mbstring,opcache,openssl,pcntl,pdo,pdo_sqlite,phar,posix,session,simplexml,sodium,sqlite3,tokenizer,xml,xmlwriter,yaml,zip,zlib | |
| PHP_VERSION: 8.3 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Cache the built binary so we can skip the build steps if there is a cache hit. | |
| - name: Generate cache key | |
| run: | | |
| CACHE_KEY=${{ runner.os }}-${{ runner.arch }}-$PHP_VERSION--$(echo $PHP_EXTENSIONS | tr ',' '-') | |
| echo "CACHE_KEY=${CACHE_KEY}" >> $GITHUB_ENV | |
| - id: cache | |
| name: Cache PHP interpreter | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/php-${{ runner.arch }} | |
| key: php-${{ env.CACHE_KEY }} | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: "Set up PHP" | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| # Install PHP with the tools needed to build the interpreter, if necessary. | |
| php-version: latest | |
| tools: pecl, composer | |
| extensions: curl, openssl, mbstring, tokenizer | |
| ini-values: memory_limit=-1 | |
| - if: steps.cache.outputs.cache-hit != 'true' | |
| name: Install dependencies and build PHP | |
| run: | | |
| brew install automake gzip | |
| composer install | |
| composer exec spc -- doctor | |
| composer exec spc -- download --with-php=${{ env.PHP_VERSION }} --for-extensions=${{ env.PHP_EXTENSIONS }} --prefer-pre-built | |
| composer exec spc -- build ${{ env.PHP_EXTENSIONS }} --build-cli --with-libs=freetype,libavif,libjpeg,libwebp --debug | |
| mv ./buildroot/bin/php ./php-${{ runner.arch }} | |
| env: | |
| # Allows static-php-cli to download its many dependencies more smoothly. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| working-directory: build | |
| - name: Upload PHP binary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: php-${{ runner.arch }} | |
| path: build/php-${{ runner.arch }} | |
| retention-days: 1 | |
| app: | |
| name: App | |
| runs-on: macos-latest | |
| needs: | |
| - php | |
| env: | |
| # Don't publish by default. This is overridden for the release branch, below. | |
| PUBLISH: never | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check out latest tag | |
| if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }} | |
| run: | | |
| LATEST_TAG=$(git describe --tags --abbrev=0) | |
| git checkout $LATEST_TAG | |
| echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV | |
| - name: Download PHP binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: build | |
| pattern: php-* | |
| merge-multiple: true | |
| - name: Set up PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| tools: composer | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: latest | |
| - name: Cache dependencies | |
| id: cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: npm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package-lock.json') }} | |
| - name: Install dependencies | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: npm install | |
| # This was copied from the example in | |
| # https://docs.github.com/en/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development. | |
| - name: Set up code signing | |
| env: | |
| BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
| BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| # Set up some useful variables. | |
| CERTIFICATE_PATH=$RUNNER_TEMP/build.p12 | |
| PROVISION_PROFILE_PATH=$RUNNER_TEMP/build.provisionprofile | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| # Import the signing certificate and provisioning profile from our secrets. | |
| echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH | |
| echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PROVISION_PROFILE_PATH | |
| # Create a temporary keychain which will hold the signing certificate. | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| # Add the certificate to the keychain. | |
| security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
| security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| # Apply the provisioning profile. | |
| # This path is based on what I found at https://stackoverflow.com/questions/45625347/xcode-provisioning-profiles-location#45642752 | |
| mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles | |
| cp $PROVISION_PROFILE_PATH ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles | |
| - name: Build app | |
| run: | | |
| composer run assets --working-dir=build | |
| # Generate a universal binary of the PHP interpreter. | |
| lipo -create ./build/php-X64 ./build/php-ARM64 -output ./bin/php | |
| chmod +x ./bin/php | |
| npx electron-vite build | |
| - name: Prepare to publish | |
| # If we're on a PR branch, we don't want Electron Builder to publish the app. | |
| if: github.ref_name == 'main' | |
| run: | | |
| # Configure Electron Builder to notarize and publish. | |
| echo "APPLE_ID=${{ secrets.APPLE_ID }}" >> $GITHUB_ENV | |
| echo "APPLE_APP_SPECIFIC_PASSWORD=${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" >> $GITHUB_ENV | |
| echo "APPLE_TEAM_ID=${{ secrets.APPLE_TEAM_ID }}" >> $GITHUB_ENV | |
| echo "PUBLISH=onTagOrDraft" >> $GITHUB_ENV | |
| # Electron Builder needs a token to publish releases. | |
| echo "GH_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV | |
| # Pre-build the Drupal site. | |
| npx electron . --root=drupal --no-server | |
| find drupal -depth -type d -name tests -exec rm -r -f {} ';' | |
| tar -c -z -f prebuilt.tar.gz --directory=drupal . | |
| rm -r -f drupal | |
| - name: Make application | |
| run: npx electron-builder --universal --publish=${{ env.PUBLISH }} | |
| # For manual testing, upload the final artifact if we're not in a release branch. | |
| - name: Upload distributable | |
| if: ${{ env.PUBLISH == 'never' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app | |
| path: dist/*.zip | |
| retention-days: 7 | |
| - name: Update prebuilt Drupal code base | |
| if: ${{ env.LATEST_TAG }} | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| dist/Drupal_CMS-macOS.zip | |
| dist/Drupal_CMS-macOS.zip.blockmap | |
| dist/latest-mac.yml | |
| tag_name: ${{ env.LATEST_TAG }} |