From 41ad8c4ca936173a1e0e2b8716b240a603ad791a Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Thu, 15 Oct 2026 18:57:49 +0900 Subject: [PATCH 1/3] feat(base): install gh-stack and gh-actions-lock GitHub CLI extensions Add install-gh-extensions.sh (coder user) that installs github/gh-stack and github/gh-actions-lock. Both install without authentication, but unauthenticated API calls are limited to 60/hour per IP, which shared GitHub-hosted runners can exhaust, so the publish workflow passes GITHUB_TOKEN as an optional BuildKit secret (gh_token). The secret is mounted only for that RUN step and never lands in an image layer. --- .github/workflows/docker-publish.yml | 3 +++ CLAUDE.md | 3 ++- README.md | 2 ++ images/CLAUDE.md | 18 +++++++++++++-- images/base/Dockerfile | 8 +++++++ images/base/scripts/install-gh-extensions.sh | 24 ++++++++++++++++++++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100755 images/base/scripts/install-gh-extensions.sh diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 0e00d99..64a2b39 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -101,6 +101,9 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + # gh extension install의 비인증 API rate limit 회피용 (이미지 레이어에는 남지 않음) + secrets: | + gh_token=${{ secrets.GITHUB_TOKEN }} # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker diff --git a/CLAUDE.md b/CLAUDE.md index e7558db..9a1172d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,7 @@ This is a Docker base image for Coder workspaces, built on top of `codercom/ente - Docker with coder user in docker group - Homebrew package manager - Graphite CLI for stacked PRs +- GitHub CLI extensions: `gh stack` (github/gh-stack) and `gh actions-lock` (github/gh-actions-lock) - AWS CLI, Google Cloud CLI, and Qodana CLI - Orca headless server (`orca-server` wrapper; launch via Coder template startup script) - Custom Nerd Fonts for terminal display @@ -52,7 +53,7 @@ The repository uses a two-phase Docker build with clear separation between root **Build Phases:** - **Phase 1 (Root)**: System packages, CLI tools (gh, gcloud), fonts, Docker, root-level tools -- **Phase 2 (Coder User)**: Development SDKs, Zimfw modules + Powerlevel10k prompt, Homebrew, Graphite CLI +- **Phase 2 (Coder User)**: Development SDKs, Zimfw modules + Powerlevel10k prompt, Homebrew, Graphite CLI, GitHub CLI extensions **For detailed build phases, script organization, and configuration files, see [images/CLAUDE.md](images/CLAUDE.md)** diff --git a/README.md b/README.md index 2150397..f2f903d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ A comprehensive Docker base image for [Coder](https://coder.com/) workspaces, bu - **Docker**: Containerization with coder user in docker group - **Homebrew**: Package manager for Linux - **Graphite CLI**: Stacked pull request workflow +- **GitHub CLI extensions**: `gh stack` (native stacked PRs) and `gh actions-lock` (lock and verify GitHub Actions dependencies) - **GitHub CLI (gh)**: GitHub operations from terminal - **AWS CLI**: Amazon Web Services command line interface - **Qodana CLI**: JetBrains code quality platform @@ -105,6 +106,7 @@ All installation scripts are located in the `scripts/` directory: | `install-prompt.sh` | Zimfw modules (`.zimrc`), Powerlevel10k prompt, Orca history | coder | | `install-brew.sh` | Homebrew package manager | coder | | `install-graphite.sh` | Graphite CLI | coder | +| `install-gh-extensions.sh` | GitHub CLI extensions (gh-stack, gh-actions-lock) | coder | ## Customization diff --git a/images/CLAUDE.md b/images/CLAUDE.md index b53ca75..1e21a38 100644 --- a/images/CLAUDE.md +++ b/images/CLAUDE.md @@ -20,7 +20,8 @@ images/ ├── install-sdk.sh # 사용자 개발 도구 (mise 글로벌 런타임, Claude Code) ├── install-prompt.sh # Zimfw 모듈 + Powerlevel10k 프롬프트 ├── install-brew.sh # Homebrew 패키지 매니저 - └── install-graphite.sh # Graphite CLI (스택 PR) + ├── install-graphite.sh # Graphite CLI (스택 PR) + └── install-gh-extensions.sh # GitHub CLI 확장 (gh-stack, gh-actions-lock) ``` ## base/ 이미지 @@ -53,6 +54,7 @@ images/ **기타:** - Homebrew 패키지 매니저 - Graphite CLI (스택 PR 관리) +- GitHub CLI 확장: `gh stack`(GitHub 네이티브 stacked PR), `gh actions-lock`(Actions 의존성 lock·검증) - 커스텀 Nerd Fonts (MesloLGS NF, JetBrainsMono, D2Coding) - Qodana CLI @@ -238,6 +240,17 @@ Graphite CLI 설치: - 스택 PR 관리 도구 - **중요**: mise가 관리하는 node(`install-sdk.sh`)가 먼저 설치되어야 함 (shims로 npm 사용) +#### install-gh-extensions.sh (Coder User) + +GitHub CLI 확장 설치: +- `gh extension install github/gh-stack`, `gh extension install github/gh-actions-lock` +- 설치 위치: `~/.local/share/gh/extensions/` +- 공개 저장소라 인증 없이 설치되지만, 비인증 API rate limit(IP당 시간당 60회)을 피하려고 + CI는 `GITHUB_TOKEN`을 BuildKit secret(`gh_token`)으로 넘긴다. secret은 해당 `RUN`에만 + 마운트되고 이미지 레이어에는 남지 않는다. 로컬 빌드에서는 생략해도 된다 + (`docker build --secret id=gh_token,env=GH_TOKEN ...`로 넘길 수도 있다) +- `gh extension list`는 인증이 필요하므로 설치 확인은 각 확장을 직접 실행해서 한다 + ## 중요 사항 ### 실행 순서 @@ -259,7 +272,8 @@ Graphite CLI 설치: 1. SDK (`install-sdk.sh`) - mise 글로벌 런타임(node/bun/deno/java), npm 전역 패키지, Claude Code 2. 프롬프트 (`install-prompt.sh`) - Zimfw 설치 이후 3. Graphite (`install-graphite.sh`) - mise node(npm) 필요 -4. Homebrew +4. GitHub CLI 확장 (`install-gh-extensions.sh`) - root 단계의 GitHub CLI 필요 +5. Homebrew ### 사용자 컨텍스트 diff --git a/images/base/Dockerfile b/images/base/Dockerfile index 258fee1..810fbbf 100644 --- a/images/base/Dockerfile +++ b/images/base/Dockerfile @@ -137,6 +137,14 @@ RUN chmod +x /tmp/install-graphite.sh && \ /bin/bash /tmp/install-graphite.sh && \ rm /tmp/install-graphite.sh +# GitHub CLI 확장 설치 (gh-stack, gh-actions-lock) +# gh_token secret은 선택 사항 — 없으면 비인증으로 설치한다 +COPY --chown=coder:coder scripts/install-gh-extensions.sh /tmp/install-gh-extensions.sh +RUN --mount=type=secret,id=gh_token,mode=0444 \ + chmod +x /tmp/install-gh-extensions.sh && \ + /bin/bash /tmp/install-gh-extensions.sh && \ + rm /tmp/install-gh-extensions.sh + # Homebrew 설치 RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" diff --git a/images/base/scripts/install-gh-extensions.sh b/images/base/scripts/install-gh-extensions.sh new file mode 100755 index 0000000..16a97f0 --- /dev/null +++ b/images/base/scripts/install-gh-extensions.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -e + +echo "🧩 Installing GitHub CLI extensions..." + +# 공개 저장소의 확장은 인증 없이 설치되지만, 비인증 API는 IP당 시간당 60회로 제한된다. +# CI는 BuildKit secret(gh_token)으로 토큰을 넘겨 공유 러너 IP의 rate limit을 피한다. +if [ -s /run/secrets/gh_token ]; then + GH_TOKEN="$(cat /run/secrets/gh_token)" + export GH_TOKEN +fi + +# gh-stack: GitHub 네이티브 stacked PR / gh-actions-lock: Actions 의존성 lock·검증 +gh extension install github/gh-stack +gh extension install github/gh-actions-lock + +# 설치 확인 (`gh extension list`는 인증이 필요하므로 각 확장을 직접 실행한다) +if gh stack --version &> /dev/null && gh actions-lock --help &> /dev/null; then + echo "✅ GitHub CLI extensions installation completed!" + gh stack --version +else + echo "❌ GitHub CLI extensions installation failed!" + exit 1 +fi From 7c2629229f9dff73b54f03e9ceb9dee5acff8de5 Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Thu, 15 Oct 2026 19:07:59 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore(base):=20gh=20=ED=99=95=EC=9E=A5=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98=20=EC=A0=84=20gh=20CLI=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=20=EC=97=AC=EB=B6=80=20=EC=82=AC=EC=A0=84=20=EA=B2=80=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- images/base/scripts/install-gh-extensions.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/images/base/scripts/install-gh-extensions.sh b/images/base/scripts/install-gh-extensions.sh index 16a97f0..5faf986 100755 --- a/images/base/scripts/install-gh-extensions.sh +++ b/images/base/scripts/install-gh-extensions.sh @@ -3,6 +3,12 @@ set -e echo "🧩 Installing GitHub CLI extensions..." +# gh는 root 단계의 apt 설치에 의존한다 — 없으면 원인을 바로 알리고 중단한다 +if ! command -v gh &> /dev/null; then + echo "❌ GitHub CLI (gh) not found; the root-phase GitHub CLI install must run first" >&2 + exit 1 +fi + # 공개 저장소의 확장은 인증 없이 설치되지만, 비인증 API는 IP당 시간당 60회로 제한된다. # CI는 BuildKit secret(gh_token)으로 토큰을 넘겨 공유 러너 IP의 rate limit을 피한다. if [ -s /run/secrets/gh_token ]; then From 3a7a7f92fdde4bdcd776ebd194b13a0cc4a89988 Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Thu, 15 Oct 2026 19:12:43 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore(base):=20gh=20=ED=99=95=EC=9E=A5=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=EC=A0=81=20=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- images/base/scripts/install-gh-extensions.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/images/base/scripts/install-gh-extensions.sh b/images/base/scripts/install-gh-extensions.sh index 5faf986..a01f0a1 100755 --- a/images/base/scripts/install-gh-extensions.sh +++ b/images/base/scripts/install-gh-extensions.sh @@ -17,14 +17,14 @@ if [ -s /run/secrets/gh_token ]; then fi # gh-stack: GitHub 네이티브 stacked PR / gh-actions-lock: Actions 의존성 lock·검증 -gh extension install github/gh-stack -gh extension install github/gh-actions-lock +gh extension install github/gh-stack || { echo "❌ Failed to install github/gh-stack" >&2; exit 1; } +gh extension install github/gh-actions-lock || { echo "❌ Failed to install github/gh-actions-lock" >&2; exit 1; } # 설치 확인 (`gh extension list`는 인증이 필요하므로 각 확장을 직접 실행한다) if gh stack --version &> /dev/null && gh actions-lock --help &> /dev/null; then echo "✅ GitHub CLI extensions installation completed!" gh stack --version else - echo "❌ GitHub CLI extensions installation failed!" + echo "❌ GitHub CLI extensions verification failed!" >&2 exit 1 fi