Skip to content
fastlane logo

Fastlane Deployment Skill

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.

SKILL.md

Full skill instructions

Fastlane Deployment Skill

Automate iOS and Android app building, code signing, and distribution.

When to Apply

Reference this skill when:

  • Setting up Fastlane for a new project
  • Configuring code signing with Match
  • Building lanes for TestFlight or App Store distribution
  • Setting up Android Play Store deployment
  • Debugging code signing or build failures
  • Configuring CI/​CD pipelines for mobile apps
  • Integrating Fastlane with Tauri v2 projects

Quick Start

Minimal Fastfile Structure

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

Tool Aliases

Fastlane provides shorter aliases for common actions:

AliasActionPurpose
gymbuild_appBuild and sign iOS/​macOS apps
pilotupload_to_testflightUpload to TestFlight
deliverupload_to_app_storeSubmit to App Store
supplyupload_to_play_storeUpload to Google Play
matchsync_code_signingSync certificates and profiles
certget_certificatesDownload signing certificates
sighget_provisioning_profileDownload provisioning profiles
scanrun_testsRun unit and UI tests
snapshotcapture_screenshotsAutomated App Store screenshots
frameitframe_screenshotsAdd device frames to screenshots
producecreate_app_onlineCreate app in App Store Connect
pemget_push_certificateDownload push notification certs
precheckcheck_app_store_metadataValidate metadata before submission

iOS Workflows

TestFlight Deployment

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

App Store Release

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

App Store Connect API Key

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

Android Workflows

Play Store Beta

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

Play Store Production

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

Code Signing with Match

Initial Setup

# Initialize Match configuration
fastlane match init

# Generate certificates (run once per team)
fastlane match appstore
fastlane match development

Matchfile Configuration

# fastlane/​Matchfile
git_url("[email protected]:your-org/​certificates.git")
storage_mode("git")
type("appstore")
app_identifier("com.example.app")
team_id("TEAM_ID")

S3/​MinIO Storage (Alternative to Git)

# 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"

Using Match in Lanes

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/​CD Integration

Keychain Setup for CI

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 Actions Example

# .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

Environment Variables

iOS (App Store Connect)

VariableDescription
APP_STORE_CONNECT_API_KEY_KEY_IDAPI Key ID from App Store Connect
APP_STORE_CONNECT_API_KEY_ISSUER_IDIssuer ID from App Store Connect
APP_STORE_CONNECT_API_KEY_KEYBase64-encoded .p8 key content
MATCH_PASSWORDEncryption password for Match
MATCH_GIT_URLGit repository URL for certificates

Android (Google Play)

VariableDescription
SUPPLY_JSON_KEY_DATAGoogle Play service account JSON (base64)
SUPPLY_JSON_KEYPath to service account JSON file

Encoding API Keys

# 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'

App Store Requirements

Metadata Character Limits

FieldLimit
App Name30 characters
Subtitle30 characters
Keywords100 characters
Description4000 characters
Release Notes4000 characters
Promotional Text170 characters

Screenshot Requirements (2024)

DeviceSizeRequired
iPhone 6.7"1290 x 2796Yes (primary)
iPhone 6.5"1284 x 2778Alternative
iPhone 5.5"1242 x 2208Optional
iPad Pro 12.9"2048 x 2732If iPad supported
iPad Pro 11"1668 x 2388Alternative

Known Issues Prevention

IssueRoot CauseSolution
"Multiple commands produce"Duplicate files in buildRemove duplicates from sources
Code signing fails in CINo keychain accessUse create_keychain + Match
Build number rejectedDuplicate build numberUse timestamp: Time.now.utc.strftime("%y%m%d%H%M")
Profile not foundWrong Match typeUse appstore for TestFlight/​App Store
Invalid PEM formatWrong key encodingEnsure base64 with is_key_content_base64: true
"Missing compliance"Encryption declarationSet uses_non_exempt_encryption: false
Gradle build failsMissing SDK/​NDKSet ANDROID_HOME and ANDROID_NDK_HOME

Tauri Integration

Version from Cargo.toml

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

Update tauri.conf.json

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

Fix Tauri project.yml (Duplicate libapp.a)

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

Tauri iOS Build Lane

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

Tauri Android Build Lane

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

Essential Commands

# 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

Sources

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

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...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

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...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

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.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View