Skip to content
Open
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
62 changes: 62 additions & 0 deletions .github/workflows/publish-factorykit-binary.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Publish FactoryKit Binary

on:
workflow_dispatch:
inputs:
release_tag:
description: Git tag and release name for the published binary artifact
required: true
type: string
xcode_version:
description: Exact Xcode version used to build the artifact
required: true
default: "26.3"
type: string

permissions:
contents: write

jobs:
publish:
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Select Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ inputs.xcode_version }}

- name: Build FactoryKit XCFramework
id: build
run: |
Scripts/build_factorykit_xcframework.sh \
--output-dir "$RUNNER_TEMP/factorykit-binary" \
--artifact-name "FactoryKit-xcode${{ inputs.xcode_version }}.xcframework"

- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: factorykit-xcode${{ inputs.xcode_version }}
path: |
${{ steps.build.outputs.zip_path }}
${{ steps.build.outputs.sha256_path }}

- name: Publish GitHub release asset
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.release_tag }}
target_commitish: ${{ github.sha }}
files: |
${{ steps.build.outputs.zip_path }}
${{ steps.build.outputs.sha256_path }}
body: |
FactoryKit binary artifact built from `${{ github.sha }}`.

Xcode: `${{ steps.build.outputs.xcode_version }}`
Swift: `${{ steps.build.outputs.swift_version }}`
SHA256: `${{ steps.build.outputs.sha256 }}`
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ playground.xcworkspace
.swiftpm

.build/
BuildArtifacts/

# CocoaPods
#
Expand Down
203 changes: 203 additions & 0 deletions Scripts/build_factorykit_xcframework.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/env bash

set -euo pipefail

usage() {
cat <<'EOF'
Usage: Scripts/build_factorykit_xcframework.sh [options]

Build a release-mode XCFramework for FactoryKit and package it as a zip.

Options:
--output-dir PATH Directory for the built artifact. Default: ./BuildArtifacts
--artifact-name NAME XCFramework directory name. Default: FactoryKit.xcframework
--bundle-id ID Bundle identifier for the generated framework target
--ios-version VERSION iOS deployment target. Default: 13.0
--macos-version VER macOS deployment target. Default: 10.15
--help Show this help text
EOF
}

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

OUTPUT_DIR="$REPO_ROOT/BuildArtifacts"
ARTIFACT_NAME="FactoryKit.xcframework"
BUNDLE_ID="ai.perplexity.factorykit.binary"
IOS_VERSION="13.0"
MACOS_VERSION="10.15"

while [[ $# -gt 0 ]]; do
case "$1" in
--output-dir)
OUTPUT_DIR="$2"
shift 2
;;
--artifact-name)
ARTIFACT_NAME="$2"
shift 2
;;
--bundle-id)
BUNDLE_ID="$2"
shift 2
;;
--ios-version)
IOS_VERSION="$2"
shift 2
;;
--macos-version)
MACOS_VERSION="$2"
shift 2
;;
--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
done

if [[ "$ARTIFACT_NAME" != *.xcframework ]]; then
echo "--artifact-name must end with .xcframework" >&2
exit 1
fi

if ! command -v xcodegen >/dev/null 2>&1; then
echo "xcodegen is required but was not found on PATH" >&2
exit 1
fi

TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/factorykit-binary.XXXXXX")"
cleanup() {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT

PROJECT_SPEC="$TEMP_DIR/project.yml"
PROJECT_NAME="FactoryKitBinaryBuilder.xcodeproj"
ARTIFACT_DIR="$OUTPUT_DIR/$ARTIFACT_NAME"
ZIP_PATH="$OUTPUT_DIR/$ARTIFACT_NAME.zip"
SHA_PATH="$ZIP_PATH.sha256"
TEMP_ARTIFACT_DIR="$TEMP_DIR/$ARTIFACT_NAME"

mkdir -p "$OUTPUT_DIR"
rm -rf "$ARTIFACT_DIR" "$ZIP_PATH" "$SHA_PATH"

cat > "$PROJECT_SPEC" <<EOF
name: FactoryKitBinaryBuilder
options:
createIntermediateGroups: true
usesTabs: false
indentWidth: 2
tabWidth: 2
deploymentTarget:
iOS: '$IOS_VERSION'
macOS: '$MACOS_VERSION'
targets:
FactoryKit:
type: framework.static
supportedDestinations: [iOS, macOS]
sources:
- path: Sources/FactoryKit/FactoryKit
- path: Sources/FactoryKit/PrivacyInfo.xcprivacy
buildPhase: resources
settings:
base:
GENERATE_INFOPLIST_FILE: YES
SKIP_INSTALL: NO
BUILD_LIBRARY_FOR_DISTRIBUTION: YES
DEFINES_MODULE: YES
PRODUCT_BUNDLE_IDENTIFIER: $BUNDLE_ID
CODE_SIGNING_ALLOWED: NO
CODE_SIGNING_REQUIRED: NO
CODE_SIGN_IDENTITY: ''
OTHER_SWIFT_FLAGS: '\$(inherited) -enable-experimental-feature StrictConcurrency'
schemes:
FactoryKit:
build:
targets:
FactoryKit: all
EOF

xcodegen \
--quiet \
--spec "$PROJECT_SPEC" \
--project-root "$REPO_ROOT" \
--project "$TEMP_DIR"

xcodebuild \
-quiet \
archive \
-project "$TEMP_DIR/$PROJECT_NAME" \
-scheme FactoryKit \
-destination 'generic/platform=iOS' \
-archivePath "$TEMP_DIR/device.xcarchive" \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY=

xcodebuild \
-quiet \
archive \
-project "$TEMP_DIR/$PROJECT_NAME" \
-scheme FactoryKit \
-destination 'generic/platform=iOS Simulator' \
-archivePath "$TEMP_DIR/simulator.xcarchive" \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
ARCHS='arm64 x86_64' \
ONLY_ACTIVE_ARCH=NO

xcodebuild \
-quiet \
archive \
-project "$TEMP_DIR/$PROJECT_NAME" \
-scheme FactoryKit \
-destination 'generic/platform=macOS' \
-archivePath "$TEMP_DIR/macos.xcarchive" \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY=

xcodebuild \
-quiet \
-create-xcframework \
-framework "$TEMP_DIR/device.xcarchive/Products/Library/Frameworks/FactoryKit.framework" \
-framework "$TEMP_DIR/simulator.xcarchive/Products/Library/Frameworks/FactoryKit.framework" \
-framework "$TEMP_DIR/macos.xcarchive/Products/Library/Frameworks/FactoryKit.framework" \
-output "$TEMP_ARTIFACT_DIR"

mv "$TEMP_ARTIFACT_DIR" "$ARTIFACT_DIR"

/usr/bin/zip -qry "$ZIP_PATH" "$ARTIFACT_DIR"
CHECKSUM="$(/usr/bin/shasum -a 256 "$ZIP_PATH" | awk '{print $1}')"
printf '%s %s\n' "$CHECKSUM" "$(basename "$ZIP_PATH")" > "$SHA_PATH"

XCODE_VERSION="$(xcodebuild -version | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
SWIFT_VERSION="$(
swift --version 2>&1 | grep -o 'Apple Swift version[^[:cntrl:]]*' | head -n 1
)"

echo "Built $ZIP_PATH"
echo "SHA256: $CHECKSUM"
echo "Xcode: $XCODE_VERSION"
echo "Swift: $SWIFT_VERSION"

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
{
echo "artifact_dir=$ARTIFACT_DIR"
echo "zip_path=$ZIP_PATH"
echo "sha256=$CHECKSUM"
echo "sha256_path=$SHA_PATH"
echo "xcode_version=$XCODE_VERSION"
echo "swift_version=$SWIFT_VERSION"
} >> "$GITHUB_OUTPUT"
fi
Loading