Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 16 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ on:
jobs:
library:
name: Lint · Typecheck · Build · Codegen
runs-on: ubuntu-latest
# Public repo: fork pull_request runs never reach the self-hosted Macs (fork-PR approval is on too).
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
# Every job runs on the org's self-hosted Mac minis (no GitHub-hosted minutes).
runs-on: [self-hosted, macOS]
steps:
- uses: actions/checkout@v4

Expand All @@ -34,7 +37,9 @@ jobs:

example-android:
name: Example · JS + Android build
runs-on: ubuntu-latest
# Public repo: fork pull_request runs never reach the self-hosted Macs (fork-PR approval is on too).
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
runs-on: [self-hosted, macOS]
steps:
- uses: actions/checkout@v4

Expand All @@ -48,8 +53,17 @@ jobs:
distribution: temurin
java-version: 17

# No Actions cache on the self-hosted Macs: ~/.gradle persists there and Gradle locks it for
# concurrent builds, while restoring a cache over it from several jobs at once would not be safe.
- name: Set up Gradle cache
uses: gradle/actions/setup-gradle@v4
with:
cache-disabled: true

# The SDK packages example/android pins (compileSdk 35, build-tools 35.0.0, NDK 27.1, and the
# CMake RN's new-architecture build uses), reused when present. The hosted image had them.
- name: Android SDK
run: bash scripts/ci/android-sdk.sh platform-tools "platforms;android-35" "build-tools;35.0.0" "ndk;27.1.12297006" "cmake;3.22.1"

- name: Install library dependencies
run: npm ci
Expand Down
22 changes: 16 additions & 6 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ on:
jobs:
ios:
name: Build example (iOS simulator)
runs-on: macos-15
# Public repo: fork pull_request runs never reach the self-hosted Macs (fork-PR approval is on too).
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
# Every job runs on the org's self-hosted Mac minis (no GitHub-hosted minutes).
runs-on: [self-hosted, macOS]
steps:
- uses: actions/checkout@v4

Expand All @@ -17,12 +20,18 @@ jobs:
node-version: 20
cache: npm

# ruby/setup-ruby's prebuilt Rubies only install on GitHub-hosted images. scripts/ci/ruby.sh uses
# Homebrew's Ruby with every gem in RUNNER_TEMP (nothing global); bundler then installs the
# Gemfile's CocoaPods there.
- name: Set up Ruby + CocoaPods (bundler)
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
working-directory: example
run: bash scripts/ci/ruby.sh

- name: Install CocoaPods (bundler)
working-directory: example
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
run: bundle install --jobs 4 --retry 3

- name: Show Xcode version
run: xcodebuild -version
Expand Down Expand Up @@ -57,6 +66,7 @@ jobs:
-configuration Debug \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath "$RUNNER_TEMP/DerivedData" \
CODE_SIGNING_ALLOWED=NO \
-quiet \
build
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ permissions:
jobs:
publish:
name: Build & publish
# The one job in the org still on a GitHub-hosted runner, on purpose: `npm publish --provenance`
# only works on GitHub-hosted runners (the npm registry rejects provenance built on a self-hosted one).
# Moving it means publishing without provenance; that is a product decision, not a CI change.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
78 changes: 78 additions & 0 deletions scripts/ci/android-sdk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# Make the Android SDK packages a CI job needs available on a shared self-hosted Mac.
#
# bash scripts/ci/android-sdk.sh <package>... e.g. platform-tools "platforms;android-34"
#
# SDK: $ANDROID_HOME, else $ANDROID_SDK_ROOT, else ~/Library/Android/sdk (where React Native and
# Android Studio put it). Packages already there are reused; missing ones are installed with a pinned
# cmdline-tools. The SDK is a versioned store (every package lives under its own versioned path), so
# sharing it between jobs is like sharing a tool cache: each job still names the exact versions it
# builds with. Installs run under a machine-wide lock, so two jobs never run sdkmanager over the
# same SDK at once. Nothing outside the SDK directory is touched.
#
# In GitHub Actions it exports ANDROID_HOME / ANDROID_SDK_ROOT and puts platform-tools on PATH.
set -euo pipefail

CMDLINE_TOOLS_ZIP="commandlinetools-mac-11076708_latest.zip" # cmdline-tools 12.0, same as the Android SDK repo
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Library/Android/sdk}}"
LOCK="${ANDROID_SDK_LOCK_FILE:-$HOME/.linktrail-android-sdk.lock}"

LOCKED=0
[[ "${1:-}" == --locked ]] && { LOCKED=1; shift; }
[[ $# -gt 0 ]] || { echo "usage: $0 <sdk package>..." >&2; exit 2; }

missing() {
local pkg out=()
for pkg in "$@"; do
# "platforms;android-34" lives in $SDK/platforms/android-34; every package has a package.xml.
[[ -f "$SDK/${pkg//;//}/package.xml" ]] || out+=("$pkg")
done
printf '%s\n' ${out[@]+"${out[@]}"}
}

install_missing() {
local todo=()
while IFS= read -r pkg; do [[ -n "$pkg" ]] && todo+=("$pkg"); done < <(missing "$@")
if [[ ${#todo[@]} -eq 0 ]]; then
echo "==> Android SDK $SDK: all packages present"
return 0
fi
local sdkmanager="$SDK/cmdline-tools/latest/bin/sdkmanager"
if [[ ! -x "$sdkmanager" ]]; then
echo "==> installing the command-line tools into $SDK"
local tmp
tmp="$(mktemp -d "${RUNNER_TEMP:-${TMPDIR:-/tmp}}/android-cmdline.XXXXXX")"
curl -fsSL --retry 3 -o "$tmp/tools.zip" "https://dl.google.com/android/repository/$CMDLINE_TOOLS_ZIP"
unzip -q "$tmp/tools.zip" -d "$tmp"
mkdir -p "$SDK/cmdline-tools"
rm -rf "$SDK/cmdline-tools/latest"
mv "$tmp/cmdline-tools" "$SDK/cmdline-tools/latest"
rm -rf "$tmp"
fi
yes | "$sdkmanager" --sdk_root="$SDK" --licenses >/dev/null 2>&1 || true
echo "==> installing into $SDK: ${todo[*]}"
"$sdkmanager" --sdk_root="$SDK" --install "${todo[@]}"
}

# Re-entered with the lock held: install, then return to the caller, which does the exports.
if [[ $LOCKED -eq 1 ]]; then
install_missing "$@"
exit 0
fi

mkdir -p "$SDK"
if [[ -n "$(missing "$@")" ]]; then
# macOS has no flock(1): perl takes the lock and keeps the fd open across exec into this script,
# so it is released the moment the install finishes or dies.
echo "==> waiting for the Android SDK lock ($LOCK)"
ANDROID_HOME="$SDK" perl -MFcntl=:flock -e '
$^F = 255; # keep the lock fd open across exec
open(my $fh, ">>", shift) or die "open lock: $!";
flock($fh, LOCK_EX) or die "flock: $!";
exec @ARGV or die "exec: $!";
' "$LOCK" bash "$0" --locked "$@"
fi

[[ -n "${GITHUB_ENV:-}" ]] && { echo "ANDROID_HOME=$SDK"; echo "ANDROID_SDK_ROOT=$SDK"; } >> "$GITHUB_ENV"
[[ -n "${GITHUB_PATH:-}" ]] && echo "$SDK/platform-tools" >> "$GITHUB_PATH"
echo "==> Android SDK: $SDK"
41 changes: 41 additions & 0 deletions scripts/ci/ruby.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# A Ruby + bundler for this CI job only, on a shared self-hosted Mac. Replaces ruby/setup-ruby, whose
# prebuilt Rubies only install on GitHub-hosted images.
#
# bash scripts/ci/ruby.sh then: bundle install / bundle exec pod … (in example/)
#
# Gems go to $RUNNER_TEMP (GEM_HOME, BUNDLE_PATH, BUNDLE_APP_CONFIG), so nothing is installed globally
# and nothing is left on the Mac. The system Ruby (2.6) is too old for CocoaPods' dependencies, so this
# uses Homebrew's Ruby: the `ruby` formula when installed, else the portable Ruby Homebrew ships for
# itself. Exports to the later steps through $GITHUB_ENV and $GITHUB_PATH.
set -euo pipefail
: "${RUNNER_TEMP:?RUNNER_TEMP is not set}"

BREW="$(command -v brew || true)"
for c in /opt/homebrew/bin/brew /usr/local/bin/brew; do
[ -n "$BREW" ] && break
[ -x "$c" ] && BREW="$c"
done
[ -n "$BREW" ] || { echo "::error::Homebrew not found on this runner; it provides the Ruby for CocoaPods."; exit 1; }

RUBY_BIN=""
for d in "$("$BREW" --prefix ruby 2>/dev/null || true)/bin" "$("$BREW" --repository)/Library/Homebrew/vendor/portable-ruby/current/bin"; do
if [ -x "$d/ruby" ] && "$d/ruby" -e 'exit(RUBY_VERSION >= "3.1" ? 0 : 1)'; then RUBY_BIN="$d"; break; fi
done
[ -n "$RUBY_BIN" ] || { echo "::error::No Ruby >= 3.1 from Homebrew on this runner (brew install ruby, or run any brew command once to fetch its portable Ruby)."; exit 1; }

ROOT="$RUNNER_TEMP/ruby"
rm -rf "$ROOT"; mkdir -p "$ROOT/gems/bin"
if [ -n "${GITHUB_ENV:-}" ]; then
{
echo "GEM_HOME=$ROOT/gems"
echo "GEM_PATH=$ROOT/gems"
echo "BUNDLE_PATH=$ROOT/bundle"
echo "BUNDLE_APP_CONFIG=$ROOT/bundle-config"
echo "CP_HOME_DIR=$ROOT/cocoapods-home"
} >> "$GITHUB_ENV"
fi
if [ -n "${GITHUB_PATH:-}" ]; then
{ echo "$ROOT/gems/bin"; echo "$RUBY_BIN"; } >> "$GITHUB_PATH"
fi
echo "Ruby $("$RUBY_BIN/ruby" -e 'print RUBY_VERSION') ($RUBY_BIN), gems in $ROOT"
Loading