super-productivity/fastlane/Fastfile
Johannes Millan 2aacba1c98
fix(ci): repair v18.9.0 release pipeline (MAS build, iOS submit, flaky lock test) (#8040)
* fix(build): keep @electron/asar on minimatch v3 for MAS universal build

The `overrides.app-builder-lib.minimatch` pin to v10 cascaded into
@electron/asar (a child of both app-builder-lib and @electron/universal),
which only works with minimatch v3. minimatch v9/v10 ship as ESM with
`__esModule: true` and no `default` export, so @electron/asar's compiled
default import (`minimatch_1.default(...)`) resolved to undefined and threw
`TypeError: (0 , minimatch_1.default) is not a function` during the
universal-app asar merge (makeUniversalApp -> mergeASARs -> shouldUnpackPath),
failing `dist:mac:mas:buildOnly` in the Mac Store release workflow.

Carve @electron/asar back to minimatch 3.1.2 via nested overrides while
app-builder-lib (and @electron/universal) keep minimatch v10 as intended.

* fix(ci): declare export compliance for iOS App Store submission

The iOS release build, upload and processing all succeed, but
`upload_to_app_store` fails at submit-for-review with:

  [!] Export compliance is required to submit
      Example: submission_information: { export_compliance_uses_encryption: false }

Super Productivity only relies on exempt encryption (HTTPS / standard OS
crypto), so declare that the app does not use non-exempt encryption:

- ios/App/App/Info.plist: ITSAppUsesNonExemptEncryption=false (canonical,
  build-time declaration; also covers TestFlight, auto-resolved by ASC).
- fastlane/Fastfile: export_compliance_uses_encryption: false in
  submission_information, so the submit-for-review API call carries the
  declaration regardless of the binary.

* refactor(build): simplify @electron/asar minimatch override to top-level

Follow-up to the minimatch carve-out. Hoisting a top-level
`overrides["@electron/asar"].minimatch` pin produces a byte-identical
package-lock.json to the previous nested form, but is simpler and covers
every @electron/asar consumer (app-builder-lib, @electron/universal,
electron-winstaller) regardless of which parent wins hoisting — rather than
relying on a nested branch under app-builder-lib (whose @electron/universal
sub-branch was dead config given asar dedupes to a single instance).

* test(op-log): de-flake LockService mutex-invariant timeout test

"should preserve mutex invariant after timeout" flaked on the macOS CI
runner (TZ=America/Los_Angeles leg): it asserted that C always times out
waiting for the lock, but on a slow/loaded runner A can release the lock
before C's 50ms timeout fires, so C legitimately acquires it and runs —
after A has finished. The observed `['a-start','a-end','c-start']` actually
satisfies the invariant (C ran after A, never concurrently); only the
brittle "C must time out" assertion failed.

Assert the real no-concurrent-execution invariant instead: C must never
start before A ends. This still catches the original regression (concurrent
C would push 'c-start' before 'a-end') but is independent of runner timing.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-05 22:26:42 +02:00

96 lines
4.2 KiB
Ruby

# iOS and macOS App Store submission lanes.
#
# The signed release binaries are produced in CI:
# - iOS: .github/workflows/build-ios.yml -> .ipa
# - macOS: .github/workflows/build-publish-to-mac-store-on-release.yml -> MAS .pkg
#
# These lanes take the finished artifact, upload it to App Store Connect, set
# the "What's New" text, submit the version for review and flag it to be
# released automatically once Apple approves it. The only step that cannot be
# automated is Apple's human review itself.
#
# Authentication uses an App Store Connect API key (not an Apple ID +
# app-specific password), which is far more robust in CI.
#
# IMPORTANT: the API key must belong to a user with the "App Manager" role (or
# higher). A key with only the "Developer" role can upload/notarize but cannot
# create a version or submit it for review.
#
# SECURITY: never enable verbose mode (--verbose / FASTLANE_VERBOSE / verbose:
# true) in these lanes. Verbose output can dump the deliver options hash, which
# contains the App Store Connect API key material.
#
# Listing metadata (description, keywords, screenshots, ...) is managed manually
# in App Store Connect and is intentionally NOT overwritten here. METADATA_PATH
# is a dedicated dir that contains ONLY <locale>/release_notes.txt, so deliver's
# load_from_filesystem reads just the release notes (it skips fields with no
# local file: `next unless File.exist?`, and upload skips nil fields). There is
# no remote read-back, so other listing fields are left untouched. Do NOT set
# `skip_metadata` here: it makes deliver return early and upload no release notes
# at all. `skip_screenshots` is safe — screenshots live elsewhere.
#
# Required env vars (wired from GitHub secrets in the workflows):
# ASC_KEY_ID App Store Connect API key id (secrets.mac_api_key_id)
# ASC_ISSUER_ID App Store Connect API issuer id (secrets.mac_api_key_issuer_id)
# ASC_KEY_CONTENT Contents of the .p8 key file (secrets.mac_api_key)
# IPA_PATH Path to the built .ipa (ios lane)
# PKG_PATH Path to the built MAS .pkg (mac lane)
# Optional:
# METADATA_PATH release-notes dir (default: fastlane/appstore_metadata)
# SUBMIT_FOR_REVIEW "false" to only upload the build without submitting
APP_IDENTIFIER = 'com.super-productivity.app'
def asc_api_key
app_store_connect_api_key(
key_id: ENV.fetch('ASC_KEY_ID'),
issuer_id: ENV.fetch('ASC_ISSUER_ID'),
key_content: ENV.fetch('ASC_KEY_CONTENT'),
is_key_content_base64: false,
)
end
def submit_for_review?
ENV.fetch('SUBMIT_FOR_REVIEW', 'true') != 'false'
end
# Shared deliver options. `extra` carries the platform-specific binary path.
def deliver_options(extra)
{
api_key: asc_api_key,
app_identifier: APP_IDENTIFIER,
# Dir holding only <locale>/release_notes.txt -> deliver uploads just the
# "What's New" text and leaves every other listing field as-is. (Do not add
# skip_metadata; see the header comment.)
metadata_path: ENV.fetch('METADATA_PATH', 'fastlane/appstore_metadata'),
skip_screenshots: true,
submit_for_review: submit_for_review?,
automatic_release: true,
# Precheck inspects listing metadata we don't manage from here.
run_precheck_before_submit: false,
submission_information: {
add_id_info_uses_idfa: false,
# The app only relies on exempt encryption (HTTPS/standard OS crypto), so
# it does not use non-exempt encryption. Without this, App Store Connect
# rejects the submission with "Export compliance is required to submit".
# Mirrors ITSAppUsesNonExemptEncryption=false in ios/App/App/Info.plist.
export_compliance_uses_encryption: false,
},
# Non-interactive: skip the HTML report confirmation prompt.
force: true,
}.merge(extra)
end
platform :ios do
desc 'Upload the prebuilt iOS .ipa to App Store Connect and submit for review'
lane :release do
upload_to_app_store(deliver_options(platform: 'ios', ipa: ENV.fetch('IPA_PATH')))
end
end
platform :mac do
desc 'Upload the prebuilt macOS .pkg to App Store Connect and submit for review'
lane :release do
upload_to_app_store(deliver_options(platform: 'osx', pkg: ENV.fetch('PKG_PATH')))
end
end