capacitor-ci-cd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CI/CD for Capacitor Applications

Capacitor应用的CI/CD实现

Automate building, testing, and deploying Capacitor apps.
自动化Capacitor应用的构建、测试与部署。

When to Use This Skill

何时使用此技能

  • User wants to automate builds
  • User needs CI/CD pipeline
  • User asks about GitHub Actions
  • User needs app signing automation
  • User wants automated releases
  • 用户希望自动化构建流程
  • 用户需要搭建CI/CD流水线
  • 用户咨询GitHub Actions相关问题
  • 用户需要应用签名自动化
  • 用户希望实现自动化发布

GitHub Actions

GitHub Actions

Complete Workflow

完整工作流

yaml
undefined
yaml
undefined

.github/workflows/build.yml

.github/workflows/build.yml

name: Build and Deploy
on: push: branches: [main, develop] pull_request: branches: [main]
env: NODE_VERSION: '20'
jobs:

Run tests and linting

test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Install dependencies
    run: bun install

  - name: Lint
    run: bun run lint

  - name: Type check
    run: bun run typecheck

  - name: Unit tests
    run: bun test --coverage

  - name: Upload coverage
    uses: codecov/codecov-action@v4

Security scan

security: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bunx capsec scan --ci

Build web assets

build-web: runs-on: ubuntu-latest needs: [test, security] steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Install dependencies
    run: bun install

  - name: Build
    run: bun run build

  - name: Upload build artifacts
    uses: actions/upload-artifact@v4
    with:
      name: web-build
      path: dist/

Build iOS

build-ios: runs-on: macos-latest needs: build-web steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Install dependencies
    run: bun install

  - name: Sync Capacitor
    run: bunx cap sync ios

  - name: Setup Ruby
    uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true
      working-directory: ios/App

  - name: Install CocoaPods
    run: cd ios/App && pod install

  - name: Import certificates
    env:
      CERTIFICATE_P12: ${{ secrets.CERTIFICATE_P12 }}
      CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
      PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
    run: |
      # Create keychain
      security create-keychain -p "" build.keychain
      security default-keychain -s build.keychain
      security unlock-keychain -p "" build.keychain
      security set-keychain-settings -t 3600 -u build.keychain

      # Import certificate
      echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
      security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
      security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain

      # Install provisioning profile
      mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
      echo "$PROVISIONING_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision

  - name: Build iOS
    run: |
      cd ios/App
      xcodebuild -workspace App.xcworkspace \
        -scheme App \
        -configuration Release \
        -archivePath build/App.xcarchive \
        archive

  - name: Export IPA
    run: |
      cd ios/App
      xcodebuild -exportArchive \
        -archivePath build/App.xcarchive \
        -exportPath build/ \
        -exportOptionsPlist ExportOptions.plist

  - name: Upload IPA
    uses: actions/upload-artifact@v4
    with:
      name: ios-build
      path: ios/App/build/*.ipa

Build Android

build-android: runs-on: ubuntu-latest needs: build-web steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Setup Java
    uses: actions/setup-java@v4
    with:
      java-version: '17'
      distribution: 'temurin'

  - name: Setup Android SDK
    uses: android-actions/setup-android@v3

  - name: Install dependencies
    run: bun install

  - name: Sync Capacitor
    run: bunx cap sync android

  - name: Decode keystore
    env:
      KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
    run: |
      echo "$KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore

  - name: Build APK
    env:
      KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
      KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
      KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
    run: |
      cd android
      ./gradlew assembleRelease \
        -Pandroid.injected.signing.store.file=release.keystore \
        -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
        -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
        -Pandroid.injected.signing.key.password=$KEY_PASSWORD

  - name: Build AAB
    env:
      KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
      KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
      KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
    run: |
      cd android
      ./gradlew bundleRelease \
        -Pandroid.injected.signing.store.file=release.keystore \
        -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
        -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
        -Pandroid.injected.signing.key.password=$KEY_PASSWORD

  - name: Upload APK
    uses: actions/upload-artifact@v4
    with:
      name: android-apk
      path: android/app/build/outputs/apk/release/*.apk

  - name: Upload AAB
    uses: actions/upload-artifact@v4
    with:
      name: android-aab
      path: android/app/build/outputs/bundle/release/*.aab

Deploy to Capgo

deploy-capgo: runs-on: ubuntu-latest needs: build-web if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Deploy to Capgo
    run: bunx @capgo/cli upload
    env:
      CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}

Deploy to App Store

deploy-ios: runs-on: macos-latest needs: build-ios if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - name: Download IPA
    uses: actions/download-artifact@v4
    with:
      name: ios-build
      path: build/

  - name: Upload to App Store Connect
    env:
      APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
    run: |
      xcrun altool --upload-app \
        --type ios \
        --file build/*.ipa \
        --apiKey ${{ secrets.API_KEY_ID }} \
        --apiIssuer ${{ secrets.API_ISSUER_ID }}

Deploy to Play Store

deploy-android: runs-on: ubuntu-latest needs: build-android if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - name: Download AAB
    uses: actions/download-artifact@v4
    with:
      name: android-aab
      path: build/

  - name: Upload to Play Store
    uses: r0adkll/upload-google-play@v1
    with:
      serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
      packageName: com.yourapp.id
      releaseFiles: build/*.aab
      track: internal
undefined
name: Build and Deploy
on: push: branches: [main, develop] pull_request: branches: [main]
env: NODE_VERSION: '20'
jobs:

Run tests and linting

test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Install dependencies
    run: bun install

  - name: Lint
    run: bun run lint

  - name: Type check
    run: bun run typecheck

  - name: Unit tests
    run: bun test --coverage

  - name: Upload coverage
    uses: codecov/codecov-action@v4

Security scan

security: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bunx capsec scan --ci

Build web assets

build-web: runs-on: ubuntu-latest needs: [test, security] steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Install dependencies
    run: bun install

  - name: Build
    run: bun run build

  - name: Upload build artifacts
    uses: actions/upload-artifact@v4
    with:
      name: web-build
      path: dist/

Build iOS

build-ios: runs-on: macos-latest needs: build-web steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Install dependencies
    run: bun install

  - name: Sync Capacitor
    run: bunx cap sync ios

  - name: Setup Ruby
    uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true
      working-directory: ios/App

  - name: Install CocoaPods
    run: cd ios/App && pod install

  - name: Import certificates
    env:
      CERTIFICATE_P12: ${{ secrets.CERTIFICATE_P12 }}
      CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
      PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
    run: |
      # Create keychain
      security create-keychain -p "" build.keychain
      security default-keychain -s build.keychain
      security unlock-keychain -p "" build.keychain
      security set-keychain-settings -t 3600 -u build.keychain

      # Import certificate
      echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
      security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
      security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain

      # Install provisioning profile
      mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
      echo "$PROVISIONING_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision

  - name: Build iOS
    run: |
      cd ios/App
      xcodebuild -workspace App.xcworkspace \
        -scheme App \
        -configuration Release \
        -archivePath build/App.xcarchive \
        archive

  - name: Export IPA
    run: |
      cd ios/App
      xcodebuild -exportArchive \
        -archivePath build/App.xcarchive \
        -exportPath build/ \
        -exportOptionsPlist ExportOptions.plist

  - name: Upload IPA
    uses: actions/upload-artifact@v4
    with:
      name: ios-build
      path: ios/App/build/*.ipa

Build Android

build-android: runs-on: ubuntu-latest needs: build-web steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Setup Java
    uses: actions/setup-java@v4
    with:
      java-version: '17'
      distribution: 'temurin'

  - name: Setup Android SDK
    uses: android-actions/setup-android@v3

  - name: Install dependencies
    run: bun install

  - name: Sync Capacitor
    run: bunx cap sync android

  - name: Decode keystore
    env:
      KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
    run: |
      echo "$KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore

  - name: Build APK
    env:
      KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
      KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
      KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
    run: |
      cd android
      ./gradlew assembleRelease \
        -Pandroid.injected.signing.store.file=release.keystore \
        -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
        -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
        -Pandroid.injected.signing.key.password=$KEY_PASSWORD

  - name: Build AAB
    env:
      KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
      KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
      KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
    run: |
      cd android
      ./gradlew bundleRelease \
        -Pandroid.injected.signing.store.file=release.keystore \
        -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
        -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
        -Pandroid.injected.signing.key.password=$KEY_PASSWORD

  - name: Upload APK
    uses: actions/upload-artifact@v4
    with:
      name: android-apk
      path: android/app/build/outputs/apk/release/*.apk

  - name: Upload AAB
    uses: actions/upload-artifact@v4
    with:
      name: android-aab
      path: android/app/build/outputs/bundle/release/*.aab

Deploy to Capgo

deploy-capgo: runs-on: ubuntu-latest needs: build-web if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v1

  - name: Download web build
    uses: actions/download-artifact@v4
    with:
      name: web-build
      path: dist/

  - name: Deploy to Capgo
    run: bunx @capgo/cli upload
    env:
      CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}

Deploy to App Store

deploy-ios: runs-on: macos-latest needs: build-ios if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - name: Download IPA
    uses: actions/download-artifact@v4
    with:
      name: ios-build
      path: build/

  - name: Upload to App Store Connect
    env:
      APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
    run: |
      xcrun altool --upload-app \
        --type ios \
        --file build/*.ipa \
        --apiKey ${{ secrets.API_KEY_ID }} \
        --apiIssuer ${{ secrets.API_ISSUER_ID }}

Deploy to Play Store

deploy-android: runs-on: ubuntu-latest needs: build-android if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4
  - name: Download AAB
    uses: actions/download-artifact@v4
    with:
      name: android-aab
      path: build/

  - name: Upload to Play Store
    uses: r0adkll/upload-google-play@v1
    with:
      serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
      packageName: com.yourapp.id
      releaseFiles: build/*.aab
      track: internal
undefined

Fastlane Integration

Fastlane集成

yaml
undefined
yaml
undefined

.github/workflows/fastlane.yml

.github/workflows/fastlane.yml

name: Fastlane Build
on: push: tags: ['v*']
jobs: ios: runs-on: macos-latest steps: - uses: actions/checkout@v4
  - uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true

  - name: Install Fastlane
    run: gem install fastlane

  - name: Build and Deploy
    run: fastlane ios release
    env:
      MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
      APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true

  - name: Install Fastlane
    run: gem install fastlane

  - name: Build and Deploy
    run: fastlane android release
    env:
      PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
undefined
name: Fastlane Build
on: push: tags: ['v*']
jobs: ios: runs-on: macos-latest steps: - uses: actions/checkout@v4
  - uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true

  - name: Install Fastlane
    run: gem install fastlane

  - name: Build and Deploy
    run: fastlane ios release
    env:
      MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
      APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - uses: ruby/setup-ruby@v1
    with:
      ruby-version: '3.2'
      bundler-cache: true

  - name: Install Fastlane
    run: gem install fastlane

  - name: Build and Deploy
    run: fastlane android release
    env:
      PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
undefined

Fastlane Setup

Fastlane配置

iOS Fastfile

iOS Fastfile

ruby
undefined
ruby
undefined

ios/App/fastlane/Fastfile

ios/App/fastlane/Fastfile

default_platform(:ios)
platform :ios do desc "Build and deploy to TestFlight" lane :release do setup_ci
# Match for code signing
match(
  type: "appstore",
  readonly: true
)

# Increment build number
increment_build_number(
  build_number: ENV["GITHUB_RUN_NUMBER"]
)

# Build
build_app(
  workspace: "App.xcworkspace",
  scheme: "App",
  export_method: "app-store"
)

# Upload to TestFlight
upload_to_testflight(
  skip_waiting_for_build_processing: true
)
end
desc "Build for development" lane :build do match(type: "development", readonly: true)
build_app(
  workspace: "App.xcworkspace",
  scheme: "App",
  export_method: "development"
)
end end
undefined
default_platform(:ios)
platform :ios do desc "Build and deploy to TestFlight" lane :release do setup_ci
# Match for code signing
match(
  type: "appstore",
  readonly: true
)

# Increment build number
increment_build_number(
  build_number: ENV["GITHUB_RUN_NUMBER"]
)

# Build
build_app(
  workspace: "App.xcworkspace",
  scheme: "App",
  export_method: "app-store"
)

# Upload to TestFlight
upload_to_testflight(
  skip_waiting_for_build_processing: true
)
end
desc "Build for development" lane :build do match(type: "development", readonly: true)
build_app(
  workspace: "App.xcworkspace",
  scheme: "App",
  export_method: "development"
)
end end
undefined

Android Fastfile

Android Fastfile

ruby
undefined
ruby
undefined

android/fastlane/Fastfile

android/fastlane/Fastfile

default_platform(:android)
platform :android do desc "Build and deploy to Play Store" lane :release do # Increment version code increment_version_code( version_code: ENV["GITHUB_RUN_NUMBER"].to_i )
# Build AAB
gradle(
  task: "bundle",
  build_type: "Release"
)

# Upload to Play Store
upload_to_play_store(
  track: "internal",
  aab: lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
)
end
desc "Build APK" lane :build do gradle( task: "assemble", build_type: "Release" ) end end
undefined
default_platform(:android)
platform :android do desc "Build and deploy to Play Store" lane :release do # Increment version code increment_version_code( version_code: ENV["GITHUB_RUN_NUMBER"].to_i )
# Build AAB
gradle(
  task: "bundle",
  build_type: "Release"
)

# Upload to Play Store
upload_to_play_store(
  track: "internal",
  aab: lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
)
end
desc "Build APK" lane :build do gradle( task: "assemble", build_type: "Release" ) end end
undefined

GitLab CI

GitLab CI

yaml
undefined
yaml
undefined

.gitlab-ci.yml

.gitlab-ci.yml

stages:
  • test
  • build
  • deploy
variables: BUN_VERSION: "1.0"
.bun-cache: &bun-cache cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ - ~/.bun/install/cache
test: stage: test image: oven/bun:${BUN_VERSION} <<: bun-cache script: - bun install - bun run lint - bun test --coverage coverage: '/All files[^|]|[^|]*\s+([\d.]+)/' artifacts: reports: coverage_report: coverage_format: cobertura path: coverage/cobertura-coverage.xml
security: stage: test image: oven/bun:${BUN_VERSION} script: - bunx capsec scan --ci --output json --output-file security.json artifacts: reports: security: security.json
build-web: stage: build image: oven/bun:${BUN_VERSION} <<: *bun-cache script: - bun install - bun run build artifacts: paths: - dist/ expire_in: 1 day
build-ios: stage: build tags: - macos needs: [build-web] script: - bun install - bunx cap sync ios - cd ios/App && fastlane build artifacts: paths: - ios/App/build/*.ipa only: - main - tags
build-android: stage: build image: thyrlian/android-sdk needs: [build-web] script: - bun install - bunx cap sync android - cd android && ./gradlew assembleRelease artifacts: paths: - android/app/build/outputs/apk/release/*.apk only: - main - tags
deploy-capgo: stage: deploy image: oven/bun:${BUN_VERSION} needs: [build-web] script: - bunx @capgo/cli upload --channel production only: - main environment: name: production
undefined
stages:
  • test
  • build
  • deploy
variables: BUN_VERSION: "1.0"
.bun-cache: &bun-cache cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ - ~/.bun/install/cache
test: stage: test image: oven/bun:${BUN_VERSION} <<: bun-cache script: - bun install - bun run lint - bun test --coverage coverage: '/All files[^|]|[^|]*\s+([\d.]+)/' artifacts: reports: coverage_report: coverage_format: cobertura path: coverage/cobertura-coverage.xml
security: stage: test image: oven/bun:${BUN_VERSION} script: - bunx capsec scan --ci --output json --output-file security.json artifacts: reports: security: security.json
build-web: stage: build image: oven/bun:${BUN_VERSION} <<: *bun-cache script: - bun install - bun run build artifacts: paths: - dist/ expire_in: 1 day
build-ios: stage: build tags: - macos needs: [build-web] script: - bun install - bunx cap sync ios - cd ios/App && fastlane build artifacts: paths: - ios/App/build/*.ipa only: - main - tags
build-android: stage: build image: thyrlian/android-sdk needs: [build-web] script: - bun install - bunx cap sync android - cd android && ./gradlew assembleRelease artifacts: paths: - android/app/build/outputs/apk/release/*.apk only: - main - tags
deploy-capgo: stage: deploy image: oven/bun:${BUN_VERSION} needs: [build-web] script: - bunx @capgo/cli upload --channel production only: - main environment: name: production
undefined

Secrets Management

密钥管理

Required Secrets

必要密钥

SecretDescription
CERTIFICATE_P12
iOS distribution certificate (base64)
CERTIFICATE_PASSWORD
Certificate password
PROVISIONING_PROFILE
iOS provisioning profile (base64)
KEYSTORE_BASE64
Android keystore (base64)
KEYSTORE_PASSWORD
Keystore password
KEY_ALIAS
Signing key alias
KEY_PASSWORD
Signing key password
CAPGO_TOKEN
Capgo API token
APP_STORE_CONNECT_API_KEY
App Store Connect API key
PLAY_SERVICE_ACCOUNT
Play Store service account JSON
密钥名称说明
CERTIFICATE_P12
iOS发布证书(base64编码)
CERTIFICATE_PASSWORD
证书密码
PROVISIONING_PROFILE
iOS配置文件(base64编码)
KEYSTORE_BASE64
Android密钥库(base64编码)
KEYSTORE_PASSWORD
密钥库密码
KEY_ALIAS
签名密钥别名
KEY_PASSWORD
签名密钥密码
CAPGO_TOKEN
Capgo API令牌
APP_STORE_CONNECT_API_KEY
App Store Connect API密钥
PLAY_SERVICE_ACCOUNT
Play Store服务账号JSON

Encoding Secrets

密钥编码

bash
undefined
bash
undefined

iOS certificate

iOS证书

base64 -i certificate.p12 | pbcopy
base64 -i certificate.p12 | pbcopy

iOS provisioning profile

iOS配置文件

base64 -i profile.mobileprovision | pbcopy
base64 -i profile.mobileprovision | pbcopy

Android keystore

Android密钥库

base64 -i release.keystore | pbcopy
undefined
base64 -i release.keystore | pbcopy
undefined

Version Management

版本管理

Semantic Release

Semantic Release配置

bash
bun add -D semantic-release @semantic-release/git @semantic-release/changelog
json
// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    [
      "@semantic-release/npm",
      { "npmPublish": false }
    ],
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version}"
      }
    ],
    "@semantic-release/github"
  ]
}
bash
bun add -D semantic-release @semantic-release/git @semantic-release/changelog
json
// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    [
      "@semantic-release/npm",
      { "npmPublish": false }
    ],
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version}"
      }
    ],
    "@semantic-release/github"
  ]
}

Version Bumping

版本号自动递增

yaml
undefined
yaml
undefined

.github/workflows/release.yml

.github/workflows/release.yml

name: Release
on: push: branches: [main]
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false
  - uses: oven-sh/setup-bun@v1

  - run: bun install

  - name: Release
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    run: bunx semantic-release
undefined
name: Release
on: push: branches: [main]
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false
  - uses: oven-sh/setup-bun@v1

  - run: bun install

  - name: Release
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    run: bunx semantic-release
undefined

Build Caching

构建缓存

Gradle Cache

Gradle缓存

yaml
- name: Cache Gradle
  uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: gradle-${{ runner.os }}-
yaml
- name: Cache Gradle
  uses: actions/cache@v4
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: gradle-${{ runner.os }}-

CocoaPods Cache

CocoaPods缓存

yaml
- name: Cache CocoaPods
  uses: actions/cache@v4
  with:
    path: ios/App/Pods
    key: pods-${{ runner.os }}-${{ hashFiles('ios/App/Podfile.lock') }}
    restore-keys: pods-${{ runner.os }}-
yaml
- name: Cache CocoaPods
  uses: actions/cache@v4
  with:
    path: ios/App/Pods
    key: pods-${{ runner.os }}-${{ hashFiles('ios/App/Podfile.lock') }}
    restore-keys: pods-${{ runner.os }}-

Resources

参考资源