From b0a03a705d6dcb295914431efcc8028dbfdfc12f Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 28 Feb 2026 00:33:09 +0900 Subject: [PATCH] Migrate container registry from Docker Hub to GHCR Switch all image references from gjkim42/ (Docker Hub) to ghcr.io/kelos-dev/ (GitHub Container Registry) and update the release workflow to authenticate with GHCR using GITHUB_TOKEN. --- .github/workflows/ci.yaml | 12 ++--- .github/workflows/release.yaml | 8 ++-- Makefile | 2 +- install.yaml | 14 +++--- internal/cli/install_test.go | 44 +++++++++---------- internal/controller/job_builder.go | 8 ++-- .../taskspawner_deployment_builder.go | 4 +- internal/manifests/install.yaml | 14 +++--- local-run.sh | 2 +- 9 files changed, 55 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 18ee303..3fd2d51 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -86,12 +86,12 @@ jobs: - name: Load images into kind run: | - kind load docker-image gjkim42/kelos-controller:e2e - kind load docker-image gjkim42/kelos-spawner:e2e - kind load docker-image gjkim42/claude-code:e2e - kind load docker-image gjkim42/codex:e2e - kind load docker-image gjkim42/gemini:e2e - kind load docker-image gjkim42/opencode:e2e + kind load docker-image ghcr.io/kelos-dev/kelos-controller:e2e + kind load docker-image ghcr.io/kelos-dev/kelos-spawner:e2e + kind load docker-image ghcr.io/kelos-dev/claude-code:e2e + kind load docker-image ghcr.io/kelos-dev/codex:e2e + kind load docker-image ghcr.io/kelos-dev/gemini:e2e + kind load docker-image ghcr.io/kelos-dev/opencode:e2e - name: Build CLI run: make build WHAT=cmd/kelos diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b8ffb8b..34a3e44 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,6 +10,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + packages: write steps: - uses: actions/checkout@v4 @@ -17,11 +18,12 @@ jobs: with: go-version-file: go.mod - - name: Login to Docker Hub + - name: Login to GHCR uses: docker/login-action@v3 with: - username: gjkim42 - password: ${{ secrets.DOCKERHUB_KELOS_TOKEN }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Determine version id: version diff --git a/Makefile b/Makefile index 0fa71d8..095393c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Image configuration -REGISTRY ?= gjkim42 +REGISTRY ?= ghcr.io/kelos-dev VERSION ?= latest IMAGE_DIRS ?= cmd/kelos-controller cmd/kelos-spawner cmd/kelos-token-refresher claude-code codex gemini opencode diff --git a/install.yaml b/install.yaml index facfec6..446a86b 100644 --- a/install.yaml +++ b/install.yaml @@ -252,15 +252,15 @@ spec: runAsNonRoot: true containers: - name: manager - image: gjkim42/kelos-controller:latest + image: ghcr.io/kelos-dev/kelos-controller:latest args: - --leader-elect - - --claude-code-image=gjkim42/claude-code:latest - - --codex-image=gjkim42/codex:latest - - --gemini-image=gjkim42/gemini:latest - - --opencode-image=gjkim42/opencode:latest - - --spawner-image=gjkim42/kelos-spawner:latest - - --token-refresher-image=gjkim42/kelos-token-refresher:latest + - --claude-code-image=ghcr.io/kelos-dev/claude-code:latest + - --codex-image=ghcr.io/kelos-dev/codex:latest + - --gemini-image=ghcr.io/kelos-dev/gemini:latest + - --opencode-image=ghcr.io/kelos-dev/opencode:latest + - --spawner-image=ghcr.io/kelos-dev/kelos-spawner:latest + - --token-refresher-image=ghcr.io/kelos-dev/kelos-token-refresher:latest securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/internal/cli/install_test.go b/internal/cli/install_test.go index 8ca4116..1cab96d 100644 --- a/internal/cli/install_test.go +++ b/internal/cli/install_test.go @@ -213,7 +213,7 @@ func TestUninstallCommand_RejectsExtraArgs(t *testing.T) { } func TestVersionedManifest_Latest(t *testing.T) { - data := []byte("image: gjkim42/kelos-controller:latest") + data := []byte("image: ghcr.io/kelos-dev/kelos-controller:latest") result := versionedManifest(data, "latest") if !bytes.Equal(result, data) { t.Errorf("expected manifest unchanged for latest version, got %s", string(result)) @@ -221,19 +221,19 @@ func TestVersionedManifest_Latest(t *testing.T) { } func TestVersionedManifest_Tagged(t *testing.T) { - data := []byte("image: gjkim42/kelos-controller:latest") + data := []byte("image: ghcr.io/kelos-dev/kelos-controller:latest") result := versionedManifest(data, "v0.1.0") - expected := []byte("image: gjkim42/kelos-controller:v0.1.0") + expected := []byte("image: ghcr.io/kelos-dev/kelos-controller:v0.1.0") if !bytes.Equal(result, expected) { t.Errorf("expected %s, got %s", string(expected), string(result)) } } func TestVersionedManifest_MultipleImages(t *testing.T) { - data := []byte(`image: gjkim42/kelos-controller:latest + data := []byte(`image: ghcr.io/kelos-dev/kelos-controller:latest args: - - --spawner-image=gjkim42/kelos-spawner:latest - - --claude-code-image=gjkim42/claude-code:latest`) + - --spawner-image=ghcr.io/kelos-dev/kelos-spawner:latest + - --claude-code-image=ghcr.io/kelos-dev/claude-code:latest`) result := versionedManifest(data, "v0.2.0") if bytes.Contains(result, []byte(":latest")) { t.Errorf("expected all :latest tags to be replaced, got %s", string(result)) @@ -256,12 +256,12 @@ func TestVersionedManifest_EmbeddedController(t *testing.T) { func TestVersionedManifest_EmbeddedControllerImageArgs(t *testing.T) { // Verify the embedded manifest contains image flags that will be versioned. expectedArgs := []string{ - "--claude-code-image=gjkim42/claude-code:", - "--codex-image=gjkim42/codex:", - "--gemini-image=gjkim42/gemini:", - "--opencode-image=gjkim42/opencode:", - "--spawner-image=gjkim42/kelos-spawner:", - "--token-refresher-image=gjkim42/kelos-token-refresher:", + "--claude-code-image=ghcr.io/kelos-dev/claude-code:", + "--codex-image=ghcr.io/kelos-dev/codex:", + "--gemini-image=ghcr.io/kelos-dev/gemini:", + "--opencode-image=ghcr.io/kelos-dev/opencode:", + "--spawner-image=ghcr.io/kelos-dev/kelos-spawner:", + "--token-refresher-image=ghcr.io/kelos-dev/kelos-token-refresher:", } for _, arg := range expectedArgs { if !bytes.Contains(manifests.InstallController, []byte(arg)) { @@ -272,12 +272,12 @@ func TestVersionedManifest_EmbeddedControllerImageArgs(t *testing.T) { // Verify all image args get the pinned version after substitution. result := versionedManifest(manifests.InstallController, "v0.3.0") versionedArgs := []string{ - "--claude-code-image=gjkim42/claude-code:v0.3.0", - "--codex-image=gjkim42/codex:v0.3.0", - "--gemini-image=gjkim42/gemini:v0.3.0", - "--opencode-image=gjkim42/opencode:v0.3.0", - "--spawner-image=gjkim42/kelos-spawner:v0.3.0", - "--token-refresher-image=gjkim42/kelos-token-refresher:v0.3.0", + "--claude-code-image=ghcr.io/kelos-dev/claude-code:v0.3.0", + "--codex-image=ghcr.io/kelos-dev/codex:v0.3.0", + "--gemini-image=ghcr.io/kelos-dev/gemini:v0.3.0", + "--opencode-image=ghcr.io/kelos-dev/opencode:v0.3.0", + "--spawner-image=ghcr.io/kelos-dev/kelos-spawner:v0.3.0", + "--token-refresher-image=ghcr.io/kelos-dev/kelos-token-refresher:v0.3.0", } for _, arg := range versionedArgs { if !bytes.Contains(result, []byte(arg)) { @@ -289,14 +289,14 @@ func TestVersionedManifest_EmbeddedControllerImageArgs(t *testing.T) { func TestWithImagePullPolicy(t *testing.T) { data := []byte(` containers: - name: manager - image: gjkim42/kelos-controller:v0.1.0 + image: ghcr.io/kelos-dev/kelos-controller:v0.1.0 args: - --leader-elect - - --claude-code-image=gjkim42/claude-code:v0.1.0 - - --spawner-image=gjkim42/kelos-spawner:v0.1.0`) + - --claude-code-image=ghcr.io/kelos-dev/claude-code:v0.1.0 + - --spawner-image=ghcr.io/kelos-dev/kelos-spawner:v0.1.0`) result := withImagePullPolicy(data, "Always") // Verify container imagePullPolicy appears right after the image line. - expected := []byte(" image: gjkim42/kelos-controller:v0.1.0\n imagePullPolicy: Always\n") + expected := []byte(" image: ghcr.io/kelos-dev/kelos-controller:v0.1.0\n imagePullPolicy: Always\n") if !bytes.Contains(result, expected) { t.Errorf("expected imagePullPolicy right after image line, got:\n%s", string(result)) } diff --git a/internal/controller/job_builder.go b/internal/controller/job_builder.go index 00ce644..19592d1 100644 --- a/internal/controller/job_builder.go +++ b/internal/controller/job_builder.go @@ -16,16 +16,16 @@ import ( const ( // ClaudeCodeImage is the default image for Claude Code agent. - ClaudeCodeImage = "gjkim42/claude-code:latest" + ClaudeCodeImage = "ghcr.io/kelos-dev/claude-code:latest" // CodexImage is the default image for OpenAI Codex agent. - CodexImage = "gjkim42/codex:latest" + CodexImage = "ghcr.io/kelos-dev/codex:latest" // GeminiImage is the default image for Google Gemini CLI agent. - GeminiImage = "gjkim42/gemini:latest" + GeminiImage = "ghcr.io/kelos-dev/gemini:latest" // OpenCodeImage is the default image for OpenCode agent. - OpenCodeImage = "gjkim42/opencode:latest" + OpenCodeImage = "ghcr.io/kelos-dev/opencode:latest" // AgentTypeClaudeCode is the agent type for Claude Code. AgentTypeClaudeCode = "claude-code" diff --git a/internal/controller/taskspawner_deployment_builder.go b/internal/controller/taskspawner_deployment_builder.go index 99292c7..f317f5b 100644 --- a/internal/controller/taskspawner_deployment_builder.go +++ b/internal/controller/taskspawner_deployment_builder.go @@ -15,10 +15,10 @@ import ( const ( // DefaultSpawnerImage is the default image for the spawner binary. - DefaultSpawnerImage = "gjkim42/kelos-spawner:latest" + DefaultSpawnerImage = "ghcr.io/kelos-dev/kelos-spawner:latest" // DefaultTokenRefresherImage is the default image for the token refresher sidecar. - DefaultTokenRefresherImage = "gjkim42/kelos-token-refresher:latest" + DefaultTokenRefresherImage = "ghcr.io/kelos-dev/kelos-token-refresher:latest" // SpawnerServiceAccount is the service account used by spawner Deployments. SpawnerServiceAccount = "kelos-spawner" diff --git a/internal/manifests/install.yaml b/internal/manifests/install.yaml index facfec6..446a86b 100644 --- a/internal/manifests/install.yaml +++ b/internal/manifests/install.yaml @@ -252,15 +252,15 @@ spec: runAsNonRoot: true containers: - name: manager - image: gjkim42/kelos-controller:latest + image: ghcr.io/kelos-dev/kelos-controller:latest args: - --leader-elect - - --claude-code-image=gjkim42/claude-code:latest - - --codex-image=gjkim42/codex:latest - - --gemini-image=gjkim42/gemini:latest - - --opencode-image=gjkim42/opencode:latest - - --spawner-image=gjkim42/kelos-spawner:latest - - --token-refresher-image=gjkim42/kelos-token-refresher:latest + - --claude-code-image=ghcr.io/kelos-dev/claude-code:latest + - --codex-image=ghcr.io/kelos-dev/codex:latest + - --gemini-image=ghcr.io/kelos-dev/gemini:latest + - --opencode-image=ghcr.io/kelos-dev/opencode:latest + - --spawner-image=ghcr.io/kelos-dev/kelos-spawner:latest + - --token-refresher-image=ghcr.io/kelos-dev/kelos-token-refresher:latest securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/local-run.sh b/local-run.sh index c754d4f..3584639 100755 --- a/local-run.sh +++ b/local-run.sh @@ -5,7 +5,7 @@ set -o nounset set -o pipefail KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" -REGISTRY="${REGISTRY:-gjkim42}" +REGISTRY="${REGISTRY:-ghcr.io/kelos-dev}" LOCAL_IMAGE_TAG="${LOCAL_IMAGE_TAG:-local-dev}" if ! command -v kind >/dev/null 2>&1; then echo "Kind CLI not found in PATH" >&2