Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ee4e887
Introduce iOS RN artifacts workflow and adjust existing code
mateuuszzzzz Apr 22, 2026
ce54945
Make both workflows more DRY
mateuuszzzzz Apr 22, 2026
0845c4a
Use blacksmith runners
mateuuszzzzz Apr 22, 2026
bf4b546
Cleanup workflow files
mateuuszzzzz Apr 22, 2026
808d17c
Refactor resolve-hermes-flag and move script to .github
mateuuszzzzz Apr 22, 2026
4dad881
Update comment in resolve-hermes-flag
mateuuszzzzz Apr 22, 2026
267c83b
Refresh PR after being on hold
mateuuszzzzz Jul 13, 2026
c6e45a3
fix types and lint
mateuuszzzzz Jul 14, 2026
0ec0c25
Remove redundant gradle utils
mateuuszzzzz Jul 14, 2026
c6c7606
Use octokit and adjust old paths
mateuuszzzzz Jul 14, 2026
1e65a31
Use isRecord guard from libs
mateuuszzzzz Jul 14, 2026
beead08
Resolve conflicts
mateuuszzzzz Jul 14, 2026
7aea5b9
Fix type error in PatchedArtifactsSettings
mateuuszzzzz Jul 14, 2026
1e69fdb
Create one workflow that orchestrates iOS and Android builds
mateuuszzzzz Jul 14, 2026
b2e3b69
Make iOS builds verbose
mateuuszzzzz Jul 16, 2026
579a4e6
Fix artifacts resolver test
mateuuszzzzz Jul 16, 2026
2b2eaef
Fix ruby consumer to pin proper RN version
mateuuszzzzz Jul 16, 2026
fb074b6
Restrict this PR to building iOS artifacts
mateuuszzzzz Jul 16, 2026
1e117a8
Merge branch 'main' into add-publish-rn-ios-artifacts-workflow
mateuuszzzzz Jul 16, 2026
d547a06
Add client-side code that consumes iOS artifacts and extract common l…
mateuuszzzzz Jul 16, 2026
119997d
Add popen to cspell
mateuuszzzzz Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Determine new patched React Native version
description: >
Runs getNewPatchedRNVersion.sh with the required env wiring
(GITHUB_TOKEN, IS_HYBRID_BUILD, ARTIFACT_ID) and exposes the
resulting version string as an output.

inputs:
artifact_id:
description: Artifact id registered in GitHub Packages Maven (e.g. react-android, react-native-artifacts)
required: true
is_hybrid_build:
description: Whether this is a HybridApp build (true) or Standalone (false)
required: true

outputs:
NEW_PATCHED_VERSION:
description: The computed next patched React Native version (e.g. 0.83.1-3)
value: ${{ steps.run.outputs.NEW_PATCHED_VERSION }}

runs:
using: composite
steps:
- id: run
shell: bash
run: echo "NEW_PATCHED_VERSION=$(./.github/scripts/getNewPatchedRNVersion.sh)" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ github.token }}
IS_HYBRID_BUILD: ${{ inputs.is_hybrid_build }}
ARTIFACT_ID: ${{ inputs.artifact_id }}
23 changes: 23 additions & 0 deletions .github/actions/composite/fetchReactNativePrebuild/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Fetch React Native prebuild files
description: >
Copies the `ios-prebuild*` scripts and `Package.swift` from the upstream
facebook/react-native repo (matching the version pinned in package.json)
into the local node_modules/react-native/ folder.

runs:
using: composite
steps:
- name: Fetch prebuild files from upstream React Native
shell: bash
run: |
RN_VERSION="v$(jq -r '.dependencies["react-native"]' package.json)"
COMMIT=$(git ls-remote https://github.com/facebook/react-native.git "${RN_VERSION}^{}" | cut -f1)
git clone --depth=1 --filter=blob:none --sparse --no-checkout \
https://github.com/facebook/react-native.git /tmp/rn-prebuild
git -C /tmp/rn-prebuild sparse-checkout set \
packages/react-native/scripts \
packages/react-native/Package.swift
git -C /tmp/rn-prebuild checkout "${COMMIT}"
cp -r /tmp/rn-prebuild/packages/react-native/scripts/ios-prebuild* node_modules/react-native/scripts/
cp /tmp/rn-prebuild/packages/react-native/Package.swift node_modules/react-native/
rm -rf /tmp/rn-prebuild
7 changes: 6 additions & 1 deletion .github/scripts/getNewPatchedRNVersion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ if [ -z "$GITHUB_TOKEN" ]; then
exit 1
fi

if [ -z "$ARTIFACT_ID" ]; then
echo "ARTIFACT_ID env variable is not set"
exit 1
fi

if [[ "$IS_HYBRID_BUILD" == "true" ]]; then
readonly PACKAGE="react-hybrid"
else
Expand All @@ -15,7 +20,7 @@ VERSION="$(jq -r '.dependencies["react-native"]' package.json)"
readonly VERSION

# List all versions of the package
PACKAGE_VERSIONS="$(gh api "/orgs/Expensify/packages/maven/com.expensify.${PACKAGE}.react-android/versions" --paginate --jq '.[].name')"
PACKAGE_VERSIONS="$(gh api "/orgs/Expensify/packages/maven/com.expensify.${PACKAGE}.${ARTIFACT_ID}/versions" --paginate --jq '.[].name')"

# Filter only versions matching the base React Native version
PACKAGE_VERSIONS="$(echo "$PACKAGE_VERSIONS" | grep "$VERSION")"
Expand Down
185 changes: 29 additions & 156 deletions .github/workflows/publishReactNativeAndroidArtifacts.yml
Original file line number Diff line number Diff line change
@@ -1,194 +1,67 @@
name: Publish React Native Android Artifacts

on:
workflow_dispatch:
workflow_call:
inputs:
build_standalone:
description: 'Build Standalone artifacts'
required: false
type: boolean
build_hybridapp:
description: 'Build HybridApp artifacts'
required: false
type: boolean
app_pull_request_url:
description: 'The Expensify/App pull request URL.'
build_targets:
description: JSON array of `is_hybrid` values to build (e.g. ["true","false"]).
required: true
type: string
app_ref:
description: Expensify/App ref to checkout (empty = triggering ref).
required: false
type: string
default: ''
mobile_expensify_pull_request_url:
description: 'The Expensify/Mobile-Expensify pull request URL to check out the submodule.'
mobile_expensify_ref:
description: Mobile-Expensify ref to checkout for hybrid builds (empty = pinned submodule).
required: false
type: string
default: ''
reviewed_code:
description: I reviewed this pull request and verified that it does not contain any malicious code.
type: boolean
secrets:
OS_BOTIFY_TOKEN:
description: Token used to checkout private submodules.
required: true
SLACK_WEBHOOK:
description: Webhook used to announce failures.
required: true
default: false
push:
branches:
- main
paths:
- package.json
- patches/react-native+*.patch
- patches/@react-native+*.patch
- patches/react-native/react-native+*.patch
- patches/react-native/@react-native+*.patch
- Mobile-Expensify

jobs:
verifyPatches:
name: Verify React Native Patches
runs-on: 'blacksmith-4vcpu-ubuntu-2404'
outputs:
build_targets: ${{ steps.getArtifactBuildTargets.outputs.BUILD_TARGETS }}
steps:
- name: Checkout
uses: useblacksmith/checkout@1c9394c220d293645707b625ba9d79685f093a8f # v1
with:
submodules: true
ref: ${{ github.event.before || 'main' }}
token: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Get previous patches hash
if: ${{ github.event_name != 'workflow_dispatch' }}
id: getOldPatchesHash
run: |
echo "HYBRID_APP_HASH=$(./scripts/compute-patches-hash.sh patches Mobile-Expensify/patches)" >> "$GITHUB_OUTPUT"
echo "STANDALONE_APP_HASH=$(./scripts/compute-patches-hash.sh patches)" >> "$GITHUB_OUTPUT"

- name: Get previous react-native version
if: ${{ github.event_name != 'workflow_dispatch' }}
id: getOldVersion
run: echo "VERSION=$(jq -r '.dependencies["react-native"]' package.json)" >> "$GITHUB_OUTPUT"

- name: Checkout new ref
if: ${{ github.event_name != 'workflow_dispatch' }}
run: |
git fetch origin ${{ github.event.after }} --depth=1
git checkout ${{ github.event.after }}
git submodule update

- name: Get new patches hash
id: getNewPatchesHash
run: |
echo "HYBRID_APP_HASH=$(./scripts/compute-patches-hash.sh patches Mobile-Expensify/patches)" >> "$GITHUB_OUTPUT"
echo "STANDALONE_APP_HASH=$(./scripts/compute-patches-hash.sh patches)" >> "$GITHUB_OUTPUT"

- name: Get new react-native version
if: ${{ github.event_name != 'workflow_dispatch' }}
id: getNewVersion
run: echo "VERSION=$(jq -r '.dependencies["react-native"]' package.json)" >> "$GITHUB_OUTPUT"

- name: Check if version changed
if: ${{ github.event_name != 'workflow_dispatch' }}
id: didVersionChange
run: |
readonly DID_VERSION_CHANGE=${{ steps.getOldVersion.outputs.VERSION != steps.getNewVersion.outputs.VERSION && 'true' || 'false' }}
echo "DID_VERSION_CHANGE=$DID_VERSION_CHANGE" >> "$GITHUB_OUTPUT"
if [[ "$DID_VERSION_CHANGE" == 'true' ]]; then
echo "::notice::Detected react-native version bump (${{ steps.getOldVersion.outputs.VERSION }} -> ${{ steps.getNewVersion.outputs.VERSION }})"
fi

- name: Check if patches changed
if: ${{ github.event_name != 'workflow_dispatch' }}
id: didPatchesChange
run: |
readonly DID_HYBRID_APP_PATCHES_CHANGE=${{ steps.getOldPatchesHash.outputs.HYBRID_APP_HASH != steps.getNewPatchesHash.outputs.HYBRID_APP_HASH && 'true' || 'false' }}
readonly DID_STANDALONE_APP_PATCHES_CHANGE=${{ steps.getOldPatchesHash.outputs.STANDALONE_APP_HASH != steps.getNewPatchesHash.outputs.STANDALONE_APP_HASH && 'true' || 'false' }}
echo "DID_HYBRID_APP_PATCHES_CHANGE=$DID_HYBRID_APP_PATCHES_CHANGE" >> "$GITHUB_OUTPUT"
echo "DID_STANDALONE_APP_PATCHES_CHANGE=$DID_STANDALONE_APP_PATCHES_CHANGE" >> "$GITHUB_OUTPUT"

if [[ "$DID_HYBRID_APP_PATCHES_CHANGE" == 'true' ]]; then
echo "::notice::Detected changes in HybridApp patches (${{ steps.getOldPatchesHash.outputs.HYBRID_APP_HASH }} -> ${{ steps.getNewPatchesHash.outputs.HYBRID_APP_HASH }})"
fi

if [[ "$DID_STANDALONE_APP_PATCHES_CHANGE" == 'true' ]]; then
echo "::notice::Detected changes in Standalone NewDot patches (${{ steps.getOldPatchesHash.outputs.STANDALONE_APP_HASH }} -> ${{ steps.getNewPatchesHash.outputs.STANDALONE_APP_HASH }})"
fi

- name: Get artifact build targets
id: getArtifactBuildTargets
run: |
if [[ '${{ github.event_name }}' == 'workflow_dispatch' ]]; then
BUILD_TARGETS=()

if [[ '${{ inputs.build_standalone }}' == 'true' ]]; then
BUILD_TARGETS+=(false)
fi

if [[ '${{ inputs.build_hybridapp }}' == 'true' ]]; then
BUILD_TARGETS+=(true)
fi

if [[ ${#BUILD_TARGETS[@]} -ne 0 ]]; then
echo "BUILD_TARGETS=$(printf '%s\n' "${BUILD_TARGETS[@]}" | jq -R . | jq -c -s .)" >> "$GITHUB_OUTPUT"
fi
exit 0
fi

# When there is a version change or standalone app patches change, we need to build for both hybrid and standalone
if [[ '${{ steps.didVersionChange.outputs.DID_VERSION_CHANGE }}' == 'true' || '${{ steps.didPatchesChange.outputs.DID_STANDALONE_APP_PATCHES_CHANGE }}' == 'true' ]]; then
echo "BUILD_TARGETS=[\"true\", \"false\"]" >> "$GITHUB_OUTPUT"
elif [[ '${{ steps.didPatchesChange.outputs.DID_HYBRID_APP_PATCHES_CHANGE }}' == 'true' ]]; then
echo "BUILD_TARGETS=[\"true\"]" >> "$GITHUB_OUTPUT"
fi

# Validates the actor and that they reviewed the PR before any token is used or untrusted App PR code is checked out.
prep:
uses: ./.github/workflows/validateBuildRequest.yml
with:
REVIEWED_CODE: ${{ inputs.reviewed_code || false }}
secrets:
OS_BOTIFY_COMMIT_TOKEN: ${{ secrets.OS_BOTIFY_COMMIT_TOKEN }}

resolveRefs:
name: Resolve build refs
needs: [prep]
uses: ./.github/workflows/resolveBuildRefs.yml
with:
APP_PULL_REQUEST_URL: ${{ inputs.app_pull_request_url }}
MOBILE_EXPENSIFY_PULL_REQUEST_URL: ${{ inputs.mobile_expensify_pull_request_url }}
secrets:
OS_BOTIFY_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

buildAndPublishReactNativeArtifacts:
name: Build and Publish React Native Artifacts
runs-on: ${{ github.repository_owner == 'Expensify' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-2vcpu-ubuntu-2404' }}
needs: [verifyPatches, prep, resolveRefs]
if: needs.verifyPatches.outputs.build_targets != ''
strategy:
# Disable fail-fast to prevent cancelling both jobs when only one needs to be stopped due to concurrency limits
fail-fast: false
matrix:
# Dynamically build the matrix based on the build targets
is_hybrid: ${{ fromJSON(needs.verifyPatches.outputs.build_targets) }}
is_hybrid: ${{ fromJSON(inputs.build_targets) }}
concurrency:
group: ${{ github.workflow }}-${{ github.job }}-${{ matrix.is_hybrid }}
group: ${{ github.workflow }}-android-build-${{ matrix.is_hybrid }}
cancel-in-progress: true
steps:
- name: Checkout Code
uses: useblacksmith/checkout@1c9394c220d293645707b625ba9d79685f093a8f # v1
with:
ref: ${{ needs.resolveRefs.outputs.APP_REF }}
ref: ${{ inputs.app_ref }}
submodules: ${{ matrix.is_hybrid }}
token: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Checkout Mobile-Expensify to specified branch or commit
if: ${{ matrix.is_hybrid == 'true' && needs.resolveRefs.outputs.MOBILE_EXPENSIFY_REF != '' }}
if: ${{ matrix.is_hybrid == 'true' && inputs.mobile_expensify_ref != '' }}
run: |
cd Mobile-Expensify
git fetch origin ${{ needs.resolveRefs.outputs.MOBILE_EXPENSIFY_REF }}
git checkout ${{ needs.resolveRefs.outputs.MOBILE_EXPENSIFY_REF }}
git fetch origin ${{ inputs.mobile_expensify_ref }}
git checkout ${{ inputs.mobile_expensify_ref }}

# Recompute the patches hash from the actually checked-out tree (which may include a custom Mobile-Expensify
# ref) so the published artifact is tagged with a hash that consumers will match against.
- name: Compute patches hash
id: computePatchesHash
run: |
if [[ '${{ matrix.is_hybrid }}' == 'true' ]]; then
echo "PATCHES_HASH=$(./scripts/compute-patches-hash.sh patches Mobile-Expensify/patches)" >> "$GITHUB_OUTPUT"
echo "PATCHES_HASH=$(./scripts/artifacts-utils/compute-patches-hash.sh patches Mobile-Expensify/patches)" >> "$GITHUB_OUTPUT"
else
echo "PATCHES_HASH=$(./scripts/compute-patches-hash.sh patches)" >> "$GITHUB_OUTPUT"
echo "PATCHES_HASH=$(./scripts/artifacts-utils/compute-patches-hash.sh patches)" >> "$GITHUB_OUTPUT"
fi

- name: Setup Node
Expand All @@ -212,10 +85,10 @@ jobs:

- name: Determine new patched RN version
id: getNewPatchedVersion
run: echo "NEW_PATCHED_VERSION=$(./.github/scripts/getNewPatchedRNVersion.sh)" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ github.token }}
IS_HYBRID_BUILD: ${{ matrix.is_hybrid }}
uses: ./.github/actions/composite/determineNewPatchedRNVersion
with:
artifact_id: react-android
is_hybrid_build: ${{ matrix.is_hybrid }}

- name: Build and publish React Native artifacts
working-directory: ${{ matrix.is_hybrid == 'true' && 'Mobile-Expensify/Android' || 'android' }}
Expand Down
Loading
Loading