[#255] fastlane 배포 시 빌드 자동화를 우선 시행하도록 한다#256
Conversation
opficdev
commented
Mar 11, 2026
- closed fastlane 배포 시 빌드 자동화를 우선 시행하도록 한다 #255
… Connect에 올라가지 않도록 추가
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 Fastlane을 통한 iOS 앱 배포 프로세스의 빌드 자동화를 개선합니다. 새로운 GitHub Action을 도입하여 iOS 시뮬레이터 빌드를 표준화하고, Fastlane Highlights
Changelog
Ignored Files
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request aims to prioritize build automation during fastlane distribution, separating build and upload steps to enhance CI/CD pipeline flexibility. However, a critical security concern has been identified: potential command injection vulnerabilities in the new GitHub Action due to the direct use of expressions in bash scripts. Additionally, the GitHub Action script has a flaw in its simulator selection logic, and the Fastfile duplicates API key generation, which could be improved. It is recommended to use environment variables to pass inputs and step outputs to shell scripts to prevent arbitrary command execution and address the other identified issues.
| # macOS 메인 버전에 맞는 iOS 버전 중 최신 버전의 iPhone 선택 | ||
| RESULT=$(python3 - <<'PY' | ||
| import re, subprocess, sys | ||
|
|
||
| xcode_ver = subprocess.check_output(["xcodebuild", "-version"], text=True).splitlines()[0].strip() | ||
| xcode_major = xcode_ver.split()[1].split('.')[0] | ||
| try: | ||
| xcode_major_num = int(xcode_major) | ||
| except ValueError: | ||
| xcode_major_num = None | ||
| if xcode_major_num is not None and xcode_major_num <= 15: | ||
| xcode_major = "26" | ||
|
|
||
| text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True) | ||
| lines = text.splitlines() | ||
|
|
||
| def ver_key(v): | ||
| return tuple(int(x) for x in v.split('.')) | ||
|
|
||
| # 1) 최신 iOS 버전(해당 mac 메이저) 찾기 | ||
| latest_ver = None | ||
| for line in lines: | ||
| header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip()) | ||
| if not header: | ||
| continue | ||
| ver = header.group(1) | ||
| if not ver.startswith(f"{xcode_major}."): | ||
| continue | ||
| if latest_ver is None or ver_key(ver) > ver_key(latest_ver): | ||
| latest_ver = ver |
There was a problem hiding this comment.
iOS 시뮬레이터 버전을 선택하는 Python 스크립트 로직에 문제가 있어, 의도와 다르게 동작하여 시뮬레이터를 찾지 못하고 실패할 수 있습니다.
xcodebuild -version으로 얻은 Xcode 메이저 버전(예: 15)과simctl이 목록으로 보여주는 iOS SDK 버전(예: 17.2)은 직접적인 숫자 연관성이 없습니다.xcode_major를 특정 조건에서"26"으로 하드코딩하는 부분은 iOS 버전 규칙과 맞지 않아 원하는 결과를 얻기 어렵습니다.
xcrun simctl list devices는 현재 Xcode 환경에서 사용 가능한 시뮬레이터 목록을 보여주므로, 복잡한 버전 확인 없이 목록에서 가장 최신 버전의 iOS 시뮬레이터를 선택하는 것이 더 간단하고 안정적인 방법입니다. 아래와 같이 스크립트를 수정하는 것을 제안합니다.
# 설치된 iOS 버전 중 최신 버전의 iPhone 선택
RESULT=$(python3 - <<'PY'
import re, subprocess, sys
text = subprocess.check_output(["xcrun", "simctl", "list", "devices"], text=True)
lines = text.splitlines()
def ver_key(v):
return tuple(int(x) for x in v.split('.'))
# 1) 최신 iOS 버전 찾기
latest_ver = None
for line in lines:
header = re.match(r"^-- iOS ([0-9]+(?:\.[0-9]+)*) --$", line.strip())
if not header:
continue
ver = header.group(1)
if latest_ver is None or ver_key(ver) > ver_key(latest_ver):
latest_ver = ver| IOS_VER="${{ steps.pick_ios.outputs.ios_version }}" | ||
| DEVICE_NAME="${{ steps.pick_ios.outputs.device_name }}" |
There was a problem hiding this comment.
The GitHub Action uses expressions like ${{ steps.pick_ios.outputs.ios_version }} and ${{ steps.pick_ios.outputs.device_name }} directly within a bash run script. GitHub Actions expressions are substituted with their literal values before the shell script is executed. If the output of a previous step contains malicious characters (like double quotes or semicolons), it can lead to command injection. It is a security best practice to pass step outputs to the shell script using environment variables to prevent injection vulnerabilities.
|
|
||
| xcodebuild -version | ||
|
|
||
| echo "Using scheme: ${{ inputs.scheme }}" |
There was a problem hiding this comment.
The GitHub Action uses the expression ${{ inputs.scheme }} directly within a bash run script. If an attacker can control the scheme input, they can inject arbitrary shell commands into the CI environment. To remediate this, pass the input to the shell script using an environment variable.
Example:
- name: Build
shell: bash
env:
SCHEME: ${{ inputs.scheme }}
run: |
xcodebuild -scheme "$SCHEME" ...| lane :upload_testflight_build do | ||
| api_key = asc_api_key | ||
| ipa_output_path = File.expand_path(TESTFLIGHT_IPA_OUTPUT_PATH, Dir.pwd) | ||
|
|
||
| UI.user_error!("Missing built ipa at #{ipa_output_path}") if !File.exist?(ipa_output_path) | ||
|
|
||
| upload_to_testflight( | ||
| api_key: api_key, | ||
| ipa: ipa_output_path, | ||
| skip_waiting_for_build_processing: true | ||
| ) | ||
| end |
There was a problem hiding this comment.
deploy_testflight lane에서 api_key를 전달받을 수 있도록 이 lane의 시그니처를 수정하는 것을 권장합니다. 이렇게 하면 asc_api_key가 중복으로 호출되는 것을 방지하여 코드를 더 효율적으로 만들 수 있습니다.
lane :upload_testflight_build do |options|
options ||= {}
api_key = options[:api_key] || asc_api_key
ipa_output_path = File.expand_path(TESTFLIGHT_IPA_OUTPUT_PATH, Dir.pwd)
UI.user_error!("Missing built ipa at #{ipa_output_path}") if !File.exist?(ipa_output_path)
upload_to_testflight(
api_key: api_key,
ipa: ipa_output_path,
skip_waiting_for_build_processing: true
)
end
|
❌ iOS CI build failed. build.log not found. |