xiaohongshu
xiaohongshu
fastlane
iOS and Android app deployment automation with Fastlane. Use when building, signing, and distributing apps to TestFlight, App Store, or Google Play. Covers Match code signing, CI/CD keychain setup, and Tauri integration. Triggers on Fastfile, Appfile, Matchfile, fastlane commands.
Full skill instructions
Automate iOS and Android app building, code signing, and distribution.
Reference this skill when:
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
match(type: "appstore", readonly: true)
build_app(scheme: "MyApp")
upload_to_testflight
end
end
platform :android do
desc "Build and upload to Play Store beta"
lane :beta do
gradle(task: "bundle", build_type: "Release")
upload_to_play_store(track: "beta")
end
end
Fastlane provides shorter aliases for common actions:
| Alias | Action | Purpose |
|---|---|---|
gym | build_app | Build and sign iOS/macOS apps |
pilot | upload_to_testflight | Upload to TestFlight |
deliver | upload_to_app_store | Submit to App Store |
supply | upload_to_play_store | Upload to Google Play |
match | sync_code_signing | Sync certificates and profiles |
cert | get_certificates | Download signing certificates |
sigh | get_provisioning_profile | Download provisioning profiles |
scan | run_tests | Run unit and UI tests |
snapshot | capture_screenshots | Automated App Store screenshots |
frameit | frame_screenshots | Add device frames to screenshots |
produce | create_app_online | Create app in App Store Connect |
pem | get_push_certificate | Download push notification certs |
precheck | check_app_store_metadata | Validate metadata before submission |
lane :beta do
# Sync code signing
match(type: "appstore", readonly: true)
# Increment build number
increment_build_number(
build_number: Time.now.utc.strftime("%y%m%d%H%M")
)
# Build the app
build_app(
scheme: "MyApp",
export_method: "app-store",
output_directory: "./build"
)
# Upload to TestFlight
upload_to_testflight(
skip_waiting_for_build_processing: true,
uses_non_exempt_encryption: false
)
end
lane :release do
match(type: "appstore", readonly: true)
build_app(
scheme: "MyApp",
export_method: "app-store"
)
upload_to_app_store(
skip_screenshots: true,
skip_metadata: true,
submit_for_review: false
)
end
def api_key
app_store_connect_api_key(
key_id: ENV['APP_STORE_CONNECT_API_KEY_KEY_ID'],
issuer_id: ENV['APP_STORE_CONNECT_API_KEY_ISSUER_ID'],
key_content: ENV['APP_STORE_CONNECT_API_KEY_KEY'],
is_key_content_base64: true
)
end
lane :beta do
upload_to_testflight(api_key: api_key)
end
platform :android do
lane :beta do
gradle(
task: "bundle",
build_type: "Release",
project_dir: "./android"
)
upload_to_play_store(
track: "beta",
aab: "./android/app/build/outputs/bundle/release/app-release.aab",
skip_upload_metadata: true,
skip_upload_images: true
)
end
end
lane :release do
gradle(task: "bundle", build_type: "Release")
upload_to_play_store(
track: "production",
aab: "./android/app/build/outputs/bundle/release/app-release.aab"
)
end
# Initialize Match configuration
fastlane match init
# Generate certificates (run once per team)
fastlane match appstore
fastlane match development
# fastlane/Matchfile
git_url("[email protected]:your-org/certificates.git")
storage_mode("git")
type("appstore")
app_identifier("com.example.app")
team_id("TEAM_ID")
# Matchfile for S3-compatible storage
storage_mode("s3")
s3_region("us-east-1")
s3_bucket("certificates")
s3_access_key(ENV['AWS_ACCESS_KEY_ID'])
s3_secret_access_key(ENV['AWS_SECRET_ACCESS_KEY'])
# For MinIO, set endpoint
# ENV['AWS_ENDPOINT_URL'] = "https://minio.example.com"
lane :beta do
match(
type: "appstore",
readonly: true, # Don't create new certs
keychain_name: ENV['CI'] ? "fastlane_ci" : nil,
keychain_password: ENV['CI'] ? "fastlane_ci_password" : nil
)
end
CI_KEYCHAIN_NAME = "fastlane_ci"
CI_KEYCHAIN_PASSWORD = "fastlane_ci_password"
def setup_ci_keychain
if ENV['CI']
create_keychain(
name: CI_KEYCHAIN_NAME,
password: CI_KEYCHAIN_PASSWORD,
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: false,
add_to_search_list: true
)
end
end
def cleanup_ci_keychain
if ENV['CI']
delete_keychain(name: CI_KEYCHAIN_NAME)
end
end
lane :beta do
setup_ci_keychain
match(
type: "appstore",
keychain_name: CI_KEYCHAIN_NAME,
keychain_password: CI_KEYCHAIN_PASSWORD
)
# ... build and upload
cleanup_ci_keychain
end
# .github/workflows/ios.yml
name: iOS Build
on: [push]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install Fastlane
run: brew install fastlane
- name: Build and Deploy
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.ASC_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.ASC_KEY_CONTENT }}
CI: true
run: fastlane ios beta
| Variable | Description |
|---|---|
APP_STORE_CONNECT_API_KEY_KEY_ID | API Key ID from App Store Connect |
APP_STORE_CONNECT_API_KEY_ISSUER_ID | Issuer ID from App Store Connect |
APP_STORE_CONNECT_API_KEY_KEY | Base64-encoded .p8 key content |
MATCH_PASSWORD | Encryption password for Match |
MATCH_GIT_URL | Git repository URL for certificates |
| Variable | Description |
|---|---|
SUPPLY_JSON_KEY_DATA | Google Play service account JSON (base64) |
SUPPLY_JSON_KEY | Path to service account JSON file |
# Encode .p8 file to base64
base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n'
# Encode Google Play JSON
base64 -i play-store-key.json | tr -d '\n'
| Field | Limit |
|---|---|
| App Name | 30 characters |
| Subtitle | 30 characters |
| Keywords | 100 characters |
| Description | 4000 characters |
| Release Notes | 4000 characters |
| Promotional Text | 170 characters |
| Device | Size | Required |
|---|---|---|
| iPhone 6.7" | 1290 x 2796 | Yes (primary) |
| iPhone 6.5" | 1284 x 2778 | Alternative |
| iPhone 5.5" | 1242 x 2208 | Optional |
| iPad Pro 12.9" | 2048 x 2732 | If iPad supported |
| iPad Pro 11" | 1668 x 2388 | Alternative |
| Issue | Root Cause | Solution |
|---|---|---|
| "Multiple commands produce" | Duplicate files in build | Remove duplicates from sources |
| Code signing fails in CI | No keychain access | Use create_keychain + Match |
| Build number rejected | Duplicate build number | Use timestamp: Time.now.utc.strftime("%y%m%d%H%M") |
| Profile not found | Wrong Match type | Use appstore for TestFlight/App Store |
| Invalid PEM format | Wrong key encoding | Ensure base64 with is_key_content_base64: true |
| "Missing compliance" | Encryption declaration | Set uses_non_exempt_encryption: false |
| Gradle build fails | Missing SDK/NDK | Set ANDROID_HOME and ANDROID_NDK_HOME |
ROOT_DIR = File.expand_path("..", __dir__)
def get_app_version
cargo_toml = File.read("#{ROOT_DIR}/src-tauri/Cargo.toml")
if cargo_toml =~ /^version\s*=\s*"([^"]+)"/
$1
else
"1.0.0"
end
end
def get_next_build_number
Time.now.utc.strftime("%y%m%d%H%M").to_i
end
def update_tauri_config_version
app_version = get_app_version
build_number = get_next_build_number
tauri_conf_path = "#{ROOT_DIR}/src-tauri/tauri.conf.json"
tauri_conf = JSON.parse(File.read(tauri_conf_path))
tauri_conf["version"] = app_version
tauri_conf["bundle"] ||= {}
tauri_conf["bundle"]["iOS"] ||= {}
tauri_conf["bundle"]["iOS"]["bundleVersion"] = build_number.to_s
File.write(tauri_conf_path, JSON.pretty_generate(tauri_conf))
end
def fix_tauri_project_yml
project_yml_path = "#{ROOT_DIR}/src-tauri/gen/apple/project.yml"
return unless File.exist?(project_yml_path)
content = File.read(project_yml_path)
# Remove "- path: Externals" to prevent duplicate libapp.a
if content.include?("- path: Externals")
content.gsub!(/^\s*- path: Externals\n/, "")
File.write(project_yml_path, content)
sh("cd #{ROOT_DIR}/src-tauri/gen/apple && xcodegen generate")
end
end
lane :beta do
match(type: "appstore", readonly: true)
update_tauri_config_version
fix_tauri_project_yml
# Configure signing
update_code_signing_settings(
use_automatic_signing: false,
path: "#{ROOT_DIR}/src-tauri/gen/apple/MyApp.xcodeproj",
team_id: TEAM_ID,
bundle_identifier: APP_IDENTIFIER,
profile_name: "match AppStore #{APP_IDENTIFIER}",
code_sign_identity: "Apple Distribution"
)
# Build with Tauri
sh("cd #{ROOT_DIR}/src-tauri && npx tauri ios build --export-method app-store-connect")
upload_to_testflight(
ipa: "#{ROOT_DIR}/src-tauri/gen/apple/build/arm64/MyApp.ipa",
skip_waiting_for_build_processing: true
)
end
platform :android do
lane :beta do
ENV['ANDROID_HOME'] = "/opt/homebrew/share/android-commandlinetools"
ENV['ANDROID_NDK_HOME'] = "#{ENV['ANDROID_HOME']}/ndk/28.2.13676358"
# Update version in tauri.conf.json
app_version = get_app_version
build_number = get_next_build_number
tauri_conf_path = "#{ROOT_DIR}/src-tauri/tauri.conf.json"
tauri_conf = JSON.parse(File.read(tauri_conf_path))
tauri_conf["version"] = app_version
tauri_conf["bundle"]["android"] ||= {}
tauri_conf["bundle"]["android"]["versionCode"] = build_number
File.write(tauri_conf_path, JSON.pretty_generate(tauri_conf))
# Build with Tauri
sh("cd #{ROOT_DIR}/src-tauri && npx tauri android build")
upload_to_play_store(
track: "beta",
aab: "#{ROOT_DIR}/src-tauri/gen/android/app/build/outputs/bundle/release/app-release.aab"
)
end
end
# Initialize Fastlane
fastlane init
# List available actions
fastlane actions
# Run a specific lane
fastlane ios beta
fastlane android release
# Debug with verbose output
fastlane ios beta --verbose
# Run Match commands
fastlane match appstore
fastlane match development
fastlane match nuke distribution # Reset all distribution certs
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience