Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reusable-ios-build action #2837

Open
wants to merge 73 commits into
base: master
Choose a base branch
from
Open

Conversation

chrismclarke
Copy link
Member

@chrismclarke chrismclarke commented Mar 7, 2025

PR Checklist

  • PR title descriptive (can be used in release notes)

Description

  • Add reusable-ios-build action to handle build of ios app

  • Improve reusable build action to support custom post-build scripts and artifact upload. This enables triggering things like ios configuration scripts following the app build, so that follow-up runner doesn't have to manage deployment-specific code.

Author Notes

Once this PR is merged similar actions as the one in the debug repo PR can be added to other content repos to generate ios simulator builds. As the action depends on the latest version of the main code repo, the content repo variable for APP_CODE_BRANCH will also need to be set to one that includes these merged changes

The action does not (currently) upload automatically to testflight, nor can they generated artifacts be uploaded to the app store (requires follow-up work to handle code signing). So for now the main use-case is manually triggering the build action and then manually uploading the generated artifact to appetize for testing on ios simulator device

The content repo will also need and additional GOOGLE_SERVICES_PLIST secret if using the android-equivalent GOOGLE_SERVICES_JSON to enable firebase features. This file can be generated from the firebase console for the associated project.

Review Notes

The action is designed to be triggered by a content repo. A testing PR has been added to the debug repo used to trigger this action: IDEMSInternational/app-debug-content#154

See output artifacts from workflow run.
Example Deployment Repo Workflow Run

This should be a simulator build, able to run locally on a mac or uploaded directly to testflight.

Dev Notes

There's quite a lot going on in the action, and a whole lot more that will still require further work/follow-up. For now this really just seeks out to lay the groundwork for triggering ios builds, and is likely not yet in a state where it will drastically reduce the amount of ongoing manual work.

It should however at least be a decent proof-of-concept, and lead to a clearer understanding on the next steps required (the number of debug workflow runs on the content repo should be a good indicator of the learning involved!).

General action overview

  1. Trigger the reusable web build to set and build code for deployment. Use an additional post-build script to sync the generated www folder to the ios app via the npx cap sync command. This is done as part of the build action to avoid having to reconfigure deployment and populate ios config in step 2. Upload the full ios folder as an artifact for use in next step

  2. Checkout the builder repo and replace the default ios folder with the artifact generated in the build step (will contain public www contents as well as compiled capacitor.config.json). Install node_modules again as native plugins store podfiles in node_modules folders, and are required for build

This step could possibly be optimised if there were a cleaner way to store just the node_modules required for ios build in an artifact, so that we don't need to run a yarn install at all in this step.

  1. Generate an unsigned build for simulator and upload.

It would also be possible to generate an unsigned .xcarchive that could be imported locally into xcode, signed, and uploaded, although I think it would probably still be better to focus on integrating signing processes into CI instead. So for now the main purpose is generating builds capable of uploading to appetize.

Github Actions - Choice of Tools

When it comes to building IOS via CI, there are generally 3 approaches

  1. Use raw commands to trigger various xcrun or xcodebuild commands directly on the runner, with additional steps to provision apple certificates
  2. Use a utility like fastlane as a wrapper around commands and certificate provisioning
  3. Use a 3rd party action, the most popular being sparkfabrik/ios-build-action and yukiarr/ios-build-action. On github marketplace these are called build-ios-action and ios-build-action respectively which is super confusing

I initially went for option 3. however found both actions were just thin wrappers around fastlane scripts (option 2), and included a mix of both a bunch of functionality that we don't want (e.g. direct upload to browserstack, enforce appstore login even when not uploading build), as well as lacking some functionality we may want (e.g. generate simulator/xcarchive builds, upload to appetize, generate screenshots etc.). I also noted the sparkfabrik repo started as a fork of the yukiarr repo, however both are loosely maintained/active.

Next I went for option 1. however quickly found there to just be a huge amount of boilerplate code required to achieve basic builds and very difficult to deubg/test locally (no access to xcode cli without a mac to test examples). Here's an example gist that should give an idea

So I settled on option 2. There is a follow-up choice of how to manage fastlane (namely whether to define scripts in a fastfile or execute inline. For now I've opted to execute inline although we may want to setup specific tracks in a fastfile in the future

Certificate Management
Building for IOS is typically more involved than android, as both certificates and provisioning profiles are required that need to be provided from a registered apple developer account

There's a few different approaches to this, as outlined in the fastlane codesigning docs, although the only viable for CI are:

  1. Using fastlane's match tool which stores all certificates in external storage (e.g. private git repo or google cloud bucket) for import into workflows
  2. Provide files directly via github secrets as base64 encoded files

I've experimented with both options, although limited in what can be done without access to various certs and keys that require developer account admin access as well as a local mac device. For now I've skipped the signing to process to generate only unsiged builds, which can still be exported as .app files to run on simulator or .xcarchive which can later be signed and exported as .ipa via xcode locally.
I'd recommend a follow-up discussion to decide best way to handle signing builds before following up with action code to automate deploying to testflight.

For now I think the best option would likely be using fastlane match (1), authenticating via app-store-connect API. The benefit of this over (2) is the amount of secrets that would need encoding into the github action is fewer (1-2 vs 4-5). There would then be a follow-up choice to decide if fastlane match would store credentials in a private git repo or a google cloud storage bucket, here I would be leaning towards GCS as it has slightly better options for user-management (uses service accounts which are reasonably easy to set-up whereas github relies on personal access tokens which have much broader scope)

Debugging workflow
To speed up development workflow for triggered actions, I noted it would be possible to just run the build action once and then reference the generated artifacts in future builds via the run-id (instead of rebuilding each time). Local modification to the workflow looks like below (should note that build artifacts expire after 24h, so needs at least one new run per day while testing)

 #   build_web:
  #     uses: ./.github/workflows/reusable-app-build.yml
  #     with:
  #       branch: ${{ inputs.branch }}
  #       post-build-cmd: npx cap sync ios
  #       additional-artifact: ios
  #     secrets: inherit

  debug:
    runs-on: macos-latest
    # needs: build_web
    steps:
      - name: Download IOS artifact
        uses: actions/download-artifact@v4
        with:
          name: ios
          #   NOTE - need to explicitly include token to download from a different workflow run
          # https://github.com/actions/download-artifact/issues/320
          github-token: ${{ github.token }}
          run-id: 13727815102

In order to trigger build, typically new commits need to be pushed on the debug repo. Empty commits can be added via command line

git commit --allow-empty -m "chore: Trigger Build"

There is added complication that any changes made to builder repo files will only be available to the action after they have been merged into master, due to the way the APP_CODE_BRANCH variable specifies which builder branch to checkout. As such if making changes to builder repo files (e.g. creating fastlane files in the ios folder), then this should temporarily be updated to target the current pr branch

Misc Notes
I've registered an ios app in the debug-app firebase project and added the google services plist to the debug content repo GOOGLE_SERVICES_PLIST secret for use in testing

TODO - Follow-up

  • Remove debug action from debug repo and replace with named action.
  • Revert debug app APP_CODE_BRANCH variable in debug repo (currently targeting this branch, should target master)
  • Fully automate all ios config workflow to setup deployment build as required (migrate manual methods, handle splash/icon and anything else as required)
  • Create additional workflow to upload output to testflight directly from action (likely refactoring ios_build workflow to accept custom input args that could be used to generate either simulator or production build)
  • Organise IDEMS org certificates and include in test action (migrating any existing certs). If exporting credentials from local machine see example commands from blog post, although might be better to create new credentials for use throughout
  • Setup fastlane locally to remove the amount of boilerplate required for actions (e.g. fastlane init files in ios folder)
  • Replace testing credentials with updates on debug repo
  • Extend actions to also submit to testflight using fastlane with app-store-connect-api
  • Migrate changes to standalone actions repo
  • Refactor android actions to use post-build and additional artifacts to improve efficiency
  • Consider migrating to fastlane match (with google storage bucket)
  • Documentation

Git Issues

Closes #

Screenshots/Videos

Example - content repo action artifacts. The simulator_app artifact contains app for upload to appetize
image

Example - Debug App built and (manually) uploaded to appetize
image

@github-actions github-actions bot added the feature Work on app features/modules label Mar 7, 2025
@chrismclarke chrismclarke marked this pull request as ready for review March 9, 2025 05:28
@github-actions github-actions bot added feature Work on app features/modules and removed feature Work on app features/modules labels Mar 9, 2025
@chrismclarke chrismclarke changed the title feat: wip reusable-ios-build feat: reusable-ios-build action Mar 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Work on app features/modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant