동일한 빌드 스크립트가 SSH 터미널에서는 정상적으로 실행되지만 클라우드 Mac CI에서는 Permission denied 오류를 내는 경우, 가장 흔한 오판은 “chmod +x를 한 번 더 실행하면 된다”는 것입니다. 실제 원인은 대개 작업 진입점의 umask, Git 인덱스에 저장된 실행 비트, 복사 및 압축 해제 도구의 권한 처리 방식, 스크립트가 실제로 사용하는 인터프리터라는 네 계층에 걸쳐 있습니다. 작업 공간의 파일 하나만 수정하면 다음번 클린 체크아웃에서 같은 문제가 다시 발생합니다.
먼저 권한 문제를 네 가지로 분류하기
문제를 조사하기 전에 실패한 객체와 작업 유형을 기록해야 합니다. 스크립트를 실행할 수 없는 경우, 디렉터리에 쓸 수 없는 경우, 키 권한이 지나치게 넓은 경우, 아카이브 압축 해제 후 모드가 달라진 경우는 각각 해결 방향이 완전히 다릅니다.
stat로 8진수 모드, 소유자, 파일 유형을 한 번에 확인합니다.
target="${1:?path required}"
stat -f 'mode=%Sp octal=%OLp owner=%Su group=%Sg type=%HT path=%N' "$target"
ls -ldeO@ "$target"
두 번째 명령은 ACL, 파일 플래그, 확장 속성도 드러냅니다. 일반 권한이 정상으로 보이는데도 실패한다면 즉시 권한을 상승시키지 마십시오. 먼저 상위 디렉터리를 탐색할 수 있는지, 파일이 위치한 볼륨에서 실행이 허용되는지, 프로세스가 어떤 계정으로 실행되는지 확인해야 합니다.
rwx만이 유일한 판단 기준은 아닙니다. 경로를 구성하는 모든 디렉터리에 적절한 권한이 필요하며, ACL이 일반 모드 비트를 덮어쓸 수도 있습니다.sudo를 계속 사용하면 작업 공간의 소유자가 뒤섞여 이후 작업을 재현하기가 더 어려워질 뿐입니다.
최소한의 현장 정보 기록하기
최소한 id, umask, 현재 디렉터리, 대상 파일의 stat 결과와 마운트 정보를 저장하십시오. 환경 변수 전체를 로그에 기록해서는 안 됩니다. 토큰이 포함되어 있을 수 있기 때문입니다. 현장 기록은 자격 증명의 내용이 아니라 ID, 경로, 권한에 초점을 맞춰야 합니다.
작업 진입점에서 umask 고정하기
umask는 새로 생성되는 객체에만 영향을 주며 기존 파일을 소급해 변경하지 않습니다. 일반적인 기준값인 022를 사용하면 보통 파일은 기본적으로 644, 디렉터리는 755가 됩니다. 통제된 동일 그룹 협업 디렉터리에서는 002를 검토할 수 있지만 이를 범용 해결책으로 사용해서는 안 됩니다. 작업 진입점에서 값을 명시적으로 설정하고 새 탐색 객체를 만들어 검증해야 합니다.
set -euo pipefail
umask 022
probe_dir="$(mktemp -d)"
probe_file="$probe_dir/probe"
: > "$probe_file"
file_mode="$(stat -f '%OLp' "$probe_file")"
dir_mode="$(stat -f '%OLp' "$probe_dir")"
test "$file_mode" = "644"
test "$dir_mode" = "700"
rm -rf "$probe_dir"
mktemp -d는 임시 콘텐츠를 보호하기 위해 일반적으로 700 디렉터리를 생성하므로, 일반 디렉터리가 755여야 한다고 단정하는 데 사용할 수 없습니다. 디렉터리 기준값을 테스트하려면 알려진 상위 디렉터리에서 mkdir를 실행한 뒤 별도로 확인해야 합니다. 이 차이는 권한 테스트에서 오탐이 자주 발생하는 이유이기도 합니다.
Git에 올바른 실행 의도 저장하기
Git은 주로 파일의 실행 가능 여부만 추적하며 모든 Unix 권한을 완전하게 저장하지는 않습니다. 특정 머신에서 스크립트를 수동으로 755로 변경했다고 해서 해당 상태가 인덱스에 기록된 것은 아닙니다. 작업 공간만 보지 말고 인덱스를 확인하십시오.
git ls-files --stage |
awk '$1 == "100755" {print $4}' |
sort
실행 비트를 커밋해야 한다면 다음 명령을 사용합니다.
git update-index --chmod=+x scripts/build.sh
git diff --summary
git diff --cached --summary
반대로 설정 파일이나 문서에 실행 비트가 실수로 설정된 경우에도 git update-index --chmod=-x로 수정해야 합니다. .sh 파일은 첫 줄도 확인해야 합니다. #!/bin/zsh 또는 #!/usr/bin/env bash처럼 해당 환경에 확실히 존재하는 인터프리터를 사용하는 것이 좋으며, 스크립트에 CRLF 줄 끝이 포함되지 않도록 해야 합니다.
병합 게이트에서 스크립트 검사하기
실행 가능한 스크립트의 명시적인 목록을 관리하고 이를 인덱스 결과와 비교할 수 있습니다. “모든 .sh 파일은 실행 가능해야 한다”는 식으로 단순하게 규정해서는 안 됩니다. source로 불러오는 라이브러리 파일에는 실행 비트가 필요하지 않을 수 있기 때문입니다. 규칙은 확장자를 근거로 추측하는 대신 파일의 용도를 표현해야 합니다.
복사, 압축, 압축 해제의 권한 처리 방식 제어하기
cp, ditto, rsync와 각 아카이브 형식은 모드 비트, ACL, 확장 속성을 서로 다르게 처리합니다. 빌드 캐시에는 다시 생성할 수 있는 데이터만 저장해야 하며, 배포 패키지는 압축을 푼 뒤 권한을 다시 검수해야 합니다.
작업 공간을 복사할 때는 먼저 메타데이터를 보존해야 하는지 결정하십시오. 소스 파일의 내용만 필요하다면 이전 소유자, ACL 또는 확장 속성을 의도치 않게 상속하지 않도록 해야 합니다. 실행 비트를 반드시 유지해야 한다면 임시 디렉터리에서 한 차례 왕복 테스트를 수행합니다.
src="scripts/build.sh"
tmp="$(mktemp -d)"
ditto "$src" "$tmp/build.sh"
before="$(stat -f '%OLp' "$src")"
after="$(stat -f '%OLp' "$tmp/build.sh")"
test "$before" = "$after"
rm -rf "$tmp"
아카이브를 검수할 때는 파일의 존재 여부만 확인해서는 안 됩니다. 최소한 진입점 스크립트가 실행 가능하고, 일반 설정 파일은 실행할 수 없으며, 민감한 파일은 그룹이나 다른 사용자가 읽을 수 없는지 검증해야 합니다. 압축 해제 디렉터리는 새로 만든 디렉터리여야 하며, 그래야 기존 파일의 모드가 아카이브 자체의 문제를 가리는 일을 방지할 수 있습니다.
수정 사항을 실행 가능한 정책으로 만들기
안정적인 해결책은 실패 후 chmod -R 777을 일괄 실행하는 것이 아니라, 적고 명확하게 설명할 수 있는 검증 조건을 마련하는 것입니다. 다음 검사는 다른 사용자가 쓸 수 있는 파일을 거부하고 진입점 스크립트가 실행 가능한지 확인합니다.
set -euo pipefail
entry="scripts/build.sh"
test -f "$entry"
test -x "$entry"
while IFS= read -r -d '' file; do
mode="$(stat -f '%OLp' "$file")"
other_write=$((8#$mode & 2))
if (( other_write != 0 )); then
printf 'world-writable file: %s mode=%s\n' "$file" "$mode" >&2
exit 1
fi
done < <(find . -type f -not -path './.git/*' -print0)
정책 스크립트는 저장소에 넣고 로컬 사전 검사와 CI에서 함께 사용하십시오. 실패 출력에는 경로, 예상 모드, 실제 모드만 기록해야 합니다. 이렇게 하면 콘텐츠를 노출하지 않으면서도 문제를 쉽게 찾을 수 있습니다. RentMini의 전용 물리 노드에서 작업을 실행할 때도 이전 실행 환경이 그대로 유지된다고 가정하지 말고, 새 작업 공간마다 이 검증을 다시 수행해야 합니다.
마지막으로 클린 체크아웃을 검수합니다. 임시 작업 공간을 삭제하고 저장소를 다시 클론한 다음, 수동 수정 없이 곧바로 권한 검사와 최소 빌드를 실행하십시오. 이 경로가 통과해야만 권한 수정이 특정 원격 세션에 남은 것이 아니라 실제로 버전 관리에 반영된 것입니다.
자주 묻는 질문
chmod +x를 실행했는데도 CI에서 Permission denied가 발생하는 이유는 무엇인가요?
실제로 실행되는 파일에 chmod가 적용됐는지, 볼륨에서 실행이 허용되는지, shebang 인터프리터가 존재하는지, 이후 복사나 압축 해제가 실행 비트를 없애지 않았는지 확인해야 합니다.
클라우드 Mac CI에는 어떤 umask가 적합한가요?
단일 사용자 빌드에는 보통 022가 실용적인 기준입니다. 그룹 쓰기가 필요한 통제된 작업 공간에서만 002를 검토하고, 최종 파일 모드는 별도로 검증해야 합니다.
권한 드리프트가 기본 브랜치에 들어가는 것을 어떻게 막나요?
Git 인덱스의 실행 비트, 스크립트 shebang, 민감 파일 모드와 압축 해제 결과를 검사하고 정책 위반 시 작업을 0이 아닌 상태로 종료합니다.
다음 빌드를 위한 독점 물리 Mac mini 준비
M4 구성, 대여 기간, 노드를 선택하세요. 주문마다 독점 물리 장치가 할당되며, 실제 사용 가능 상태는 콘솔에서 실시간으로 확인할 수 있습니다.