Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8696137
style: 정확하게 테플이라는 이름 명시
opficdev Mar 9, 2026
6772079
refactor: 앱 빌드 시간 단축
opficdev Mar 9, 2026
d4fc32f
chore: fastlane/README.md 추적 끄기
opficdev Mar 9, 2026
1822757
test: lint 모듈 validation 스킵
opficdev Mar 9, 2026
e8dc7d4
fix: 내부 testflight 이름과 겹치는 현상 수정
opficdev Mar 9, 2026
c02c960
fix: export_method 수정
opficdev Mar 9, 2026
0c61521
chore: 테스트플라이트 CI 배포 서명 설정 수정
opficdev Mar 9, 2026
ae00be7
chore: 테스트플라이트 CI 서명 옵션 보정
opficdev Mar 9, 2026
2ba7707
fix: fastlane 배포용 인증서 설치 흐름 수정
opficdev Mar 10, 2026
51c4b57
feat: Apple Store Connect에 올라간 최신 빌드 번호 + 1 형태로 새 빌드 번호를 규정하도록 추가
opficdev Mar 10, 2026
e4fef3f
feat: 타깃의 Release 서명을 수동으로 고정
opficdev Mar 10, 2026
34b6453
style: 문자열 상수화
opficdev Mar 10, 2026
96ecc93
fix: Apple Store Connect에서 최신 빌드번호를 조회하지 못하는 이슈 해결
opficdev Mar 10, 2026
550568b
fix: fastlane 대신 spaceship을 통해 가져오도록 변경
opficdev Mar 10, 2026
345eccb
fix: CI 환경에서 시크릿키로 생성되는 p8 파일 위치를 찾지 못하는 현상 해결
opficdev Mar 10, 2026
47765c9
fix: 경로 이슈 해결
opficdev Mar 10, 2026
fe1a1e0
test: 토큰 재생성 대신 기존에 생성한 것을 그대로 사용하도록 수정
opficdev Mar 10, 2026
db8f37c
refactor: api_key 자체를 넘기도록 개선
opficdev Mar 10, 2026
064633c
test: 해시값 디버깅
opficdev Mar 10, 2026
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
5 changes: 3 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

env:
SCHEME: DevLog
XCODE_VERSION: latest

permissions:
contents: read
Expand All @@ -22,7 +23,7 @@ jobs:
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
xcode-version: ${{ env.XCODE_VERSION }}

- name: Cache SwiftPM
uses: actions/cache@v4
Expand Down Expand Up @@ -230,4 +231,4 @@ jobs:
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body
});
});
29 changes: 18 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ on:
branches:
- main

env:
RUBY_VERSION: "3.2"
XCODE_VERSION: latest
APP_STORE_TEAM_ID: ${{ secrets.APP_STORE_TEAM_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_PATH: fastlane/AuthKey.p8
SPACESHIP_CONNECT_API_IN_HOUSE: "false"
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}

permissions:
contents: read

Expand All @@ -15,15 +27,6 @@ jobs:
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && github.event.pull_request.head.ref == 'develop'
runs-on: macos-latest
timeout-minutes: 45
env:
APP_STORE_TEAM_ID: ${{ secrets.APP_STORE_TEAM_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_PATH: fastlane/AuthKey.p8
SPACESHIP_CONNECT_API_IN_HOUSE: "false"
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}

steps:
- name: Checkout merge commit
Expand All @@ -35,18 +38,22 @@ jobs:
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: "3.2"
ruby-version: ${{ env.RUBY_VERSION }}

- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
xcode-version: ${{ env.XCODE_VERSION }}

- name: Write App Store Connect API key
env:
ASC_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }}
run: |
printf '%s' "$ASC_KEY_CONTENT" | base64 -D > "$ASC_KEY_PATH"

- name: Debug ASC key fingerprint
run: |
shasum -a 256 "$ASC_KEY_PATH"

- name: Release to App Store Connect
run: bundle exec fastlane release
33 changes: 20 additions & 13 deletions .github/workflows/testflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ on:
- develop
workflow_dispatch:

env:
RUBY_VERSION: "3.2"
XCODE_VERSION: latest
APP_STORE_TEAM_ID: ${{ secrets.APP_STORE_TEAM_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_PATH: fastlane/AuthKey.p8
SPACESHIP_CONNECT_API_IN_HOUSE: "false"
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}

permissions:
contents: read

jobs:
beta:
testflight:
runs-on: macos-latest
timeout-minutes: 45
env:
APP_STORE_TEAM_ID: ${{ secrets.APP_STORE_TEAM_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_PATH: fastlane/AuthKey.p8
SPACESHIP_CONNECT_API_IN_HOUSE: "false"
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}

steps:
- name: Checkout
Expand All @@ -31,18 +34,22 @@ jobs:
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: "3.2"
ruby-version: ${{ env.RUBY_VERSION }}

- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
xcode-version: ${{ env.XCODE_VERSION }}

- name: Write App Store Connect API key
env:
ASC_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }}
run: |
printf '%s' "$ASC_KEY_CONTENT" | base64 -D > "$ASC_KEY_PATH"

- name: Debug ASC key fingerprint
run: |
shasum -a 256 "$ASC_KEY_PATH"

- name: Upload to TestFlight
run: bundle exec fastlane beta
run: bundle exec fastlane deploy_testflight
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ vendor/bundle/

# Fastlane
.fastlane/
fastlane/README.md
fastlane/report.xml
fastlane/test_output/
fastlane/logs/
Expand Down
96 changes: 86 additions & 10 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
XCODE_PROJ = "DevLog.xcodeproj"
APP_IDENTIFIER = "opfic.DevLog"
TARGET_NAME = "DevLog"

default_platform(:ios)

platform :ios do
private_lane :fetch_latest_testflight_build_number do |options|
require "spaceship"

apiKey = options[:api_key]
versionNumber = options[:version]

Spaceship::ConnectAPI.token = Spaceship::ConnectAPI::Token.create(**apiKey)

app = Spaceship::ConnectAPI::App.find(APP_IDENTIFIER)
UI.user_error!("Could not find app for #{APP_IDENTIFIER}") if app.nil?

filter = {
"state" => "PROCESSING,FAILED,COMPLETE",
"cfBundleShortVersionString" => versionNumber,
"platform" => Spaceship::ConnectAPI::Platform.map("ios")
}
Comment on lines +11 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Ruby에서는 변수명에 스네이크 케이스(snake_case)를 사용하는 것이 일반적인 컨벤션입니다. apiKeyversionNumber를 각각 api_keyversion_number로 변경하는 것을 제안합니다. 이렇게 하면 코드의 일관성과 가독성이 향상됩니다.

    api_key = options[:api_key]
    version_number = options[:version]

    Spaceship::ConnectAPI.token = Spaceship::ConnectAPI::Token.create(**api_key)

    app = Spaceship::ConnectAPI::App.find(APP_IDENTIFIER)
    UI.user_error!("Could not find app for #{APP_IDENTIFIER}") if app.nil?

    filter = {
      "state" => "PROCESSING,FAILED,COMPLETE",
      "cfBundleShortVersionString" => version_number,
      "platform" => Spaceship::ConnectAPI::Platform.map("ios")
    }


build = Spaceship::ConnectAPI.get_build_uploads(
app_id: app.id,
filter: filter,
sort: "-uploadedDate",
limit: 1
).first

next 0 if build.nil?

next build.cf_build_version.to_i
end

private_lane :asc_api_key do
app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
Expand All @@ -11,33 +44,72 @@ platform :ios do
end

private_lane :build_for_store do
if ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"].to_s.strip.empty?
ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "30"
end

if ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"].to_s.strip.empty?
ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "5"
end
Comment on lines +47 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

if 블록들은 후행 if문을 사용하여 각각 한 줄로 더 간결하게 표현할 수 있습니다.

    ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "30" if ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"].to_s.strip.empty?

    ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "5" if ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"].to_s.strip.empty?


api_key = asc_api_key

versionNumber = get_version_number(
xcodeproj: XCODE_PROJ,
target: TARGET_NAME
)

latestTestflightBuildNumber = fetch_latest_testflight_build_number(
api_key: api_key,
version: versionNumber
)

setup_ci if ENV["CI"]

if ENV["GITHUB_RUN_NUMBER"]
increment_build_number(
xcodeproj: "DevLog.xcodeproj",
build_number: ENV["GITHUB_RUN_NUMBER"]
)
end
increment_build_number(
xcodeproj: XCODE_PROJ,
build_number: latestTestflightBuildNumber + 1
)
Comment on lines +57 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Ruby 컨벤션에 따라 변수명에 스네이크 케이스(snake_case)를 사용하는 것이 좋습니다. versionNumberlatestTestflightBuildNumber를 각각 version_numberlatest_testflight_build_number로 변경하는 것을 제안합니다.

    version_number = get_version_number(
      xcodeproj: XCODE_PROJ,
      target: TARGET_NAME
    )

    latest_testflight_build_number = fetch_latest_testflight_build_number(
      api_key: api_key,
      version: version_number
    )

    setup_ci if ENV["CI"]

    increment_build_number(
      xcodeproj: XCODE_PROJ,
      build_number: latest_testflight_build_number + 1
    )


match(
api_key: api_key,
type: "development",
readonly: ENV["CI"] == "true"
)

match(
api_key: api_key,
type: "appstore",
readonly: ENV["CI"] == "true"
)

if ENV["CI"] == "true"
provisioningProfileSpecifier = lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING][APP_IDENTIFIER].to_s
UI.user_error!("Missing App Store provisioning profile mapping for #{APP_IDENTIFIER}") if provisioningProfileSpecifier.empty?

update_code_signing_settings(
use_automatic_signing: false,
path: XCODE_PROJ,
sdk: "iphoneos*",
team_id: ENV["APP_STORE_TEAM_ID"],
targets: [TARGET_NAME],
build_configurations: ["Release"],
code_sign_identity: "Apple Distribution",
profile_name: provisioningProfileSpecifier
)
Comment on lines +87 to +99
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Ruby 컨벤션에 따라 변수명에 스네이크 케이스(snake_case)를 사용하는 것이 좋습니다. provisioningProfileSpecifierprovisioning_profile_specifier로 변경하는 것을 제안합니다.

      provisioning_profile_specifier = lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING][APP_IDENTIFIER].to_s
      UI.user_error!("Missing App Store provisioning profile mapping for #{APP_IDENTIFIER}") if provisioning_profile_specifier.empty?

      update_code_signing_settings(
        use_automatic_signing: false,
        path: XCODE_PROJ,
        sdk: "iphoneos*",
        team_id: ENV["APP_STORE_TEAM_ID"],
        targets: [TARGET_NAME],
        build_configurations: ["Release"],
        code_sign_identity: "Apple Distribution",
        profile_name: provisioning_profile_specifier
      )

end

build_app(
project: "DevLog.xcodeproj",
scheme: "DevLog",
export_method: "app-store"
project: XCODE_PROJ,
scheme: TARGET_NAME,
export_method: "app-store",
xcargs: "-skipPackagePluginValidation"
)

next api_key
end

lane :beta do
lane :deploy_testflight do
api_key = build_for_store

upload_to_testflight(
Expand All @@ -46,6 +118,10 @@ platform :ios do
)
end

lane :testflight_build_only do
build_for_store
end
Comment on lines +121 to +123
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

testflight_build_only라는 lane 이름이 약간 혼동을 줄 수 있습니다. 이 lane은 build_for_store를 호출하여 스토어용 빌드를 생성하지만, TestFlight에 업로드하는 과정은 포함하지 않습니다. 이름이 하는 역할과 다른 인상을 줄 수 있습니다. 예를 들어 build_for_distribution 또는 build_only와 같이 역할을 더 명확하게 나타내는 이름으로 변경하는 것을 고려해볼 수 있습니다.


lane :release do
api_key = build_for_store

Expand Down
40 changes: 0 additions & 40 deletions fastlane/README.md

This file was deleted.

Loading