GitLab Workflow Skill은(는) 무엇을 하나요?
glab CLI를 사용한 GitLab merge request, issue, Git 모범 사례 관리 스킬입니다.
여러 GitLab 인스턴스
glab은 git remote에서 GitLab 호스트를 자동으로 감지합니다. 저장소 내에서 작업할 때는 GITLAB_HOST가 필요하지 않습니다.
origin이 아닌 remote(예: 보조 remote로 추가된 로컬 GDK 인스턴스)의 경우 glab config set remote_alias <remote>를 사용하세요.
GITLAB_HOST는 Git 저장소 외부에서 실행하거나 특정 인스턴스를 대상으로 하는 일회성 명령어를 실행할 때만 설정하세요.
origin이 아닌 remote 설정 및 remote URL에서 호스트명 파생 방법은 references/multi-host.md를 참조하세요.
⚠️ 메시지 이스케이프 — 흔한 함정
메시지에 백틱(`), $ 또는 기타 쉘 특수 문자가 포함된 경우 절대 -m "..." 안에 직접 넣지 마세요. 쉘은 백틱을 명령어 치환으로 해석하여 메시지를 조용히 망가뜨리고 /bin/bash: line 1: client_name: command not found와 같은 오류를 발생시킵니다.
이로 인해 실제 프로덕션 장애가 발생한 적이 있습니다. 에이전트가 GitLab MR/issue에 잘못된 형식의 댓글을 게시한 후, 사과하는 수정 노트를 추가로 게시하는 상황이 있었습니다.
❌ 따라하지 마세요 — 큰따옴표 -m 안에 백틱을 인라인으로 넣기
# 문제 발생: 쉘이 `client_name`을 명령어로 실행하려고 시도함
glab mr note 100 -m "Use `client_name` and `wor/` here." -R org/repo
# 오류: /bin/bash: line 1: client_name: command not found
# 댓글이 "Use and here."로 게시됨 (식별자가 조용히 제거됨)
# 또한 문제 발생: 백슬래시로 이스케이프한 백틱은 중첩/스크립팅 컨텍스트에서 깨짐
glab mr note 100 -m "Use \`client_name\`" -R org/repo
# 간단한 경우에는 작동하지만 호출자가 명령어를 큰따옴표로 감싸면 실패함:
# bash -c "glab mr note 100 -m \"Use \`client_name\`\"" → 여전히 client_name 실행
✅ 권장 — 먼저 파일에 작성한 다음 $(cat ...)로 전달
환경에 맞는 경로를 선택하세요 (mktemp 결과, 범위가 지정된 작업 공간 tmp 파일 등 — 스킬은 특정 경로를 지정하지 않습니다. 에이전트는 병렬 실행 충돌을 피하기 위해 자신의 호출에 고유한 경로를 선택합니다):
# MSG = 선택한 경로 (예: mktemp, ~/workspace/tmp/note-$$.md 등)
MSG=<에이전트가 선택>
cat > "$MSG" << 'EOF'
Use `client_name` and `wor/` here. The `glab` tool handles this.
EOF
glab mr note 100 -m "$(cat "$MSG")" -R org/repo
작은따옴표로 묶인 'EOF' 히어독 구분 기호는 파일을 쓸 때 모든 변수/백틱 확장을 방지합니다. $(cat "$MSG") 치환은 파일 내용이 이미 문자 그대로 기록되었기 때문에 안전합니다. 역따옴표 세 개짜리 코드 블록(```)도 <<'EOF' 히어독 내에서 안전합니다 — 이스케이프가 필요 없습니다. 오직 백틱 하나와 $만이 명령어 치환을 트리거합니다.
✅ 또한 안전함 — glab api와 -f 플래그 사용
glab api --method POST "projects/org%2Frepo/merge_requests/100/notes" \
-f "body=$(cat "$MSG")"
⚠️ 따옴표 없는 히어독은 여전히 백틱을 해석함
# 문제 발생: 따옴표 없는 EOF 구분 기호 — 본문의 백틱이 여전히 해석됨
cat > "$MSG" << EOF
Use `client_name` here. # ← 쉘이 파일을 쓸 때 client_name을 실행함
EOF
⚠️ 히어독 안의 히어독은 쉘 파싱을 깨뜨림
콘텐츠 자체에 들여쓰기 되지 않은 EOF 종결자가 있는 히어독 예제가 포함된 경우, 내부 EOF가 0열에 있으면 외부 히어독을 일찍 종료시킵니다:
# 문제 발생: 내부 EOF가 0열에 있음 — 외부 히어독을 일찍 종료시킴
cat > "$OUTER" << 'EOF'
Here is the safe pattern:
cat > "$MSG" << 'EOF'
Use `client_name` here.
EOF
# ↑ 이 EOF가 외부 히어독을 종료시킴 — 아래 줄이 쉘 명령어로 실행됨!
echo "more content..."
EOF
# ↑ 이 남은 EOF가 명령어가 됨: "EOF: command not found"
해결 방법 — 외부 히어독에 다른 구분 기호 사용:
cat > "$OUTER" << 'OUTEREOF'
Here is the safe pattern:
cat > "$MSG" << 'EOF'
Use `client_name` here.
EOF
OUTEREOF
또는 파일을 여러 청크로 나누어 씁니다. 첫 번째 청크는 >를 사용하고, 이후 청크는 각각 자체 구분 기호와 함께 >>를 사용합니다.
경험 법칙: 메시지에 `, $, !, 또는 \가 포함된 경우 — 항상 먼저 << 'EOF'로 파일에 작성하세요. 콘텐츠 자체에 히어독 구문이 포함된 경우 본문에 나타나지 않는 고유한 외부 구분 기호(예: OUTEREOF, MSGEOF)를 사용하세요.
Merge Request 생성
항상 --push와 -H <owner/repo>를 전달하세요. --push가 없으면 브랜치가 아직 어떤 remote에도 존재하지 않을 수 있습니다.
-H가 없으면 glab이 잘못된 remote(예: 보안 미러)를 소스 프로젝트로 선택하여 잘못된 포크에서 MR을 생성할 수 있습니다.
동일 계열의 두 번째 remote?
origin(gitlab-org/gitlab)과security미러(gitlab-org/security/gitlab)가 모두 있는 체크아웃에서glab mr create를 비대화식(에이전트가 실행하는 방식인 TTY 없음)으로 실행하면 조용히security포크를 헤드로 선택하고400 {source_branch: [does not exist]}로 실패합니다. 영구적인 수정(체크아웃당 한 번 설정):git config remote.origin.glab-resolved-head head. 새 체크아웃을 위한 명령별 대체:-H <owner/repo>(예:-H gitlab-org/gitlab)를 전달하세요.-R은 이 문제를 해결하지 못합니다 — 기본 저장소만 고정할 뿐 헤드는 고정하지 않습니다.
# 간단한 MR
glab mr create --push -H <owner/repo> --title "Add feature" --description "Brief description" --assignee <username>
# 복잡한 MR - 설명을 먼저 파일에 작성 (자신의 경로 선택)
glab mr create --push -H <owner/repo> --title "Add feature" --description "$(cat "$DESC")" --assignee <username>
템플릿: 프로젝트별 템플릿이 있는지 .gitlab/merge_request_templates/를 확인하세요.
전체 플래그 목록(Claude Code는 자동으로 주입하며, 다른 에이전트는 직접 실행해야 합니다):
!glab mr create --help
Merge Request 업데이트
glab mr update <number> --description "$(cat "$DESC")"
glab mr view <number> -R <owner>/<repo>
Issue 관리
전체 최신 플래그 목록(Claude Code는 자동으로 주입하며, 다른 에이전트는 직접 실행해야 합니다):
!glab issue --help
view, note, list, create, update는 예상대로 작동합니다. --help가 경고하지 않는 비자명한 함정:
# list는 기본적으로 열린 항목을 표시하며 --state 플래그가 없습니다. 대신 --closed / --all을 사용하세요.
glab issue list --closed -R <owner>/<repo>
glab issue list --all -R <owner>/<repo>
# 레이블 — --label / --unlabel을 사용하고, 절대 +label 또는 -label 구문을 사용하지 마세요.
glab issue update 123 --label "new-label"
glab issue update 123 --unlabel "old-label"
# 범위가 지정된 레이블은 해당 범위 내에서 자동으로 교체됩니다. --unlabel이 필요 없습니다:
glab issue update 123 --label "status::doing" # 기존 status:: 레이블을 제거합니다.
# 백틱/$가 있는 메시지 — 먼저 파일에 작성 (위의 메시지 이스케이프 참조)
glab issue note <number> -m "$(cat "$MSG")" -R <owner>/<repo>
Issue 상태 전환(API를 통한 닫기/재열기) 및 glab api를 통한 노트 게시: references/issue-api.md
Work Items
GitLab은 issue를 work item으로 마이그레이션 중입니다. URL이 /work_items/<iid>를 표시하지만 REST API는 동일합니다.
# ✅ Issues API 사용 — 동일한 IID, 동일한 엔드포인트
glab api "projects/org%2Fproject/issues/<iid>"
# ❌ /work_items/ REST 엔드포인트는 존재하지 않음
glab api "projects/org%2Fproject/work_items/<iid>" # → 404
URL 파싱: https://gitlab.com/org/project/-/work_items/539076
→ glab api "projects/org%2Fproject/issues/539076"
전체 세부 정보, GraphQL 대안, 그룹 수준 work item, 에이전트 계획(Workplan) 위젯: references/work-items.md
MR 리뷰
glab v1.94.0부터 glab mr note는 일반적인 모든 MR 댓글 형태(목록, 일반, diff 라인, 답글, 해결/재열기)를 raw glab api 호출이나 수동으로 작성된 position 객체 없이 처리합니다. 단일 댓글 워크플로우에는 이를 선호하세요. MR IID는 위치 인수입니다(--mr 아님). 생략하면 현재 브랜치에서 자동으로 감지합니다.
# ✅ 읽기/쓰기/답글/해결에 glab mr note 사용 — 작업당 단일 명령어
glab mr note list 123 -F json # 토론 읽기
glab mr note create 123 -m "comment" # 일반
glab mr note create 123 --file main.go --line 42 -m "..." # diff 댓글
glab mr note create 123 --reply abc12345 -m "..." # 답글
glab mr note resolve 123 abc12345 # 스레드 해결
# ❌ 이러한 작업에 glab api .../discussions를 사용하지 마세요.
glab api "projects/<id>/merge_requests/123/discussions" # mr note list 사용
glab api --method PUT ".../discussions/<id>" -f resolved=true # mr note resolve 사용
위 하위 명령어의 전체 플래그 목록(Claude Code는 자동으로 주입하며, 다른 에이전트는 직접 실행해야 합니다):
!glab mr note --help
배치 초안 리뷰(여러 인라인 댓글을 bulk_publish로 함께 게시)에만 raw glab api .../draft_notes로 대체하세요 — glab mr note에는 초안 모드가 없습니다.
전체 참조(모든 플래그, 코드 제안, 초안/배치 대체, position 객체): references/mr-review.md
Issue 링크, 에픽 및 중첩 그룹
- Issue 링크 (
blocked_by,relates_to): references/issue-links.md - 에픽 CRUD (생성, 목록, 업데이트, 닫기): references/epics.md
- 에픽 댓글 (GraphQL 읽기/쓰기, 페이지네이션 — REST는 404 반환): references/epic-comments.md
- 중첩 그룹 (
%2F인코딩): references/nested-groups.md
MR 목록 및 필터링
전체 플래그 목록(Claude Code는 자동으로 주입하며, 다른 에이전트는 직접 실행해야 합니다):
!glab mr list --help
참고: glab mr list는 기본적으로 열린 MR을 표시하며 --state 또는 --status 플래그가 없습니다. 상태 필터를 변경하려면 --all, --merged, 또는 --closed를 사용하세요.
검색
전체 검색 예제(인스턴스 / 그룹 / 프로젝트, 범위 표, 페이지네이션): references/search.md
빠른 참조:
glab api "search?scope=issues&search=<query>" | jq '.[] | {iid, title}'
glab api "groups/<group>/search?scope=merge_requests&search=<query>" | jq '.[]'
glab api "projects/<org>%2F<repo>/search?scope=issues&search=<query>" | jq '.[]'
Git 및 커밋 규칙
해당 저장소의 자체 git 규칙이 있는 경우 이를 따르세요(프로젝트가 문서화하고 커밋 린터로 강제할 수 있음). GitLab 기본값:
git checkout -b feature/description # 기능 브랜치
git checkout -b fix/description # 버그 수정
- 커밋 제목은 대문자로 시작하고 명령형을 사용하세요("Added feature" 또는 "feat: add feature"가 아닌 "Add feature"). GitLab은 conventional-commit 제목을 사용하지 않습니다.
- Issue/MR을 전체 URL로 참조하세요:
Closes https://gitlab.com/org/project/-/issues/123. - 특수 문자가 포함된 커밋 메시지는 작은따옴표로 감싸세요:
git commit -m 'Add note from https://gitlab.com/org/project/-/merge_requests/123'.
에이전트 가이드라인
위의 섹션은 일반적인 워크플로우를 다룹니다. 다음은 비자명한 함정과 API 특성으로, 실수하기 쉽고 glab <cmd> --help로는 알 수 없는 것들입니다:
- 먼저 컨텍스트를 읽으세요 — 구현 전에
glab issue view/glab mr view를 실행하고 템플릿을 위해.gitlab/issue_templates/및.gitlab/merge_request_templates/를 확인하세요. --jq는 하위 명령어에서 작동하며glab api에서는 작동하지 않습니다 —glab issue list/glab mr list/glab ci list는 전역--jq플래그(JSON 출력 필터링)를 허용합니다.glab api는 허용하지 않습니다 — 대신 출력을| jq '...'로 파이프하세요.--body플래그는 없습니다 — glab은--body(이는gh플래그) 대신--description을 사용합니다. 서로 바꿔 사용할 수 없습니다.- Work item은 issues API를 사용합니다 —
/work_items/<iid>URL →projects/.../issues/<iid>;/work_items/REST 엔드포인트는 404입니다. - 에픽 댓글에는 GraphQL이 필요합니다 — REST
/notesGET+POST 모두 → 404(GitLab 19.1에서도 여전히 유효).body/noteableId를 GraphQL 변수로 전달하고 문자열을 절대 인터폴레이션하지 마세요. references/epic-comments.md 참조. - 그룹 수준 API에는
-R이 없습니다 —-R은OWNER/REPO를 기대합니다. 그룹 엔드포인트는glab api "groups/..."를 직접 사용하세요. - 중첩 그룹 REST:
%2F—groups/org%2Fsubgroup/epics; 인코딩되지 않은 슬래시 → 404. - GraphQL iid는 문자열입니다 —
workItem(iid: "16428")이지workItem(iid: 16428)가 아닙니다. groups/<id>/work_items는 404입니다 —groups/<id>/epics(REST) 또는 GraphQL을 사용하세요.project는workItems(복수형)를 노출하고workItem은 노출하지 않습니다 —project아래에서는filter:인수 없이workItems(first: 1, iid: "IID")를 사용하세요. 단수형workItem(iid:)필드는group/namespace아래에만 존재합니다.- 에픽 닫기/재열기는 REST를 통해 —
PUT groups/<id>/epics/<iid>에서state_event=close/reopen이 작동합니다. GraphQL이 필요하지 않습니다. - 범위가 지정된 레이블은 자동 교체됩니다 —
--label "status::doing"는 기존status::*레이블을 제거합니다.--unlabel이 필요 없습니다(이는 플랫폼 사실이며, 일반적으로 API를 통해 glab뿐만 아니라 적용됩니다). - 멱등 댓글 →
--unique—glab mr note create -m "..." --unique는 동일한 본문이 이미 존재하면 게시를 건너뜁니다. 본문만 일치하므로 다른 diff 라인의 동일한 본문은 여전히 게시됩니다. origin이 아닌 remote →remote_alias—origin이 한 인스턴스를 가리키지만 glab이 다른 remote(예:gdk로 추가된 로컬 GDK)를 대상으로 하게 하려면 명령어마다GITLAB_HOST를 설정하는 대신glab config set remote_alias gdk를 실행하세요. references/multi-host.md 참조.- 메시지의 백틱 → 먼저 파일에 작성 —
-m "..."또는--description "..."에`/$를 절대 인라인으로 넣지 마세요.<< 'EOF'(작은따옴표로 묶인 구분 기호, 중요)를 사용하여 자신이 이름을 지정한 파일(호출당 고유)에 작성한 다음$(cat "$FILE")로 전달하세요. 위의 메시지 이스케이프 섹션을 참조하세요. - 동일 계열의 두 번째 remote가
mr create를 잘못 라우팅합니다 —security미러가origin과 함께 있을 때, 비대화식mr create(TTY 없음)는 조용히 포크를 헤드로 선택합니다. 체크아웃당 한 번 수정:git config remote.origin.glab-resolved-head head; 명령별 대체:-H <owner/repo>(-R은 해결하지 않습니다). "Merge Request 생성" 섹션을 참조하세요. glab api노트 본문: 파일 →-F "body=@file"
이 문서는 GitLab 워크플로우 스킬입니다.