Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 71 additions & 30 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,70 +10,111 @@ concurrency:
cancel-in-progress: false

jobs:
release:
determine-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Determine version
id: version
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
else
echo "version=main" >> "$GITHUB_OUTPUT"
fi

build-images:
needs: determine-version
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
permissions:
contents: write
contents: read
packages: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Determine version
id: version
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
else
echo "version=main" >> "$GITHUB_OUTPUT"
fi

- name: Build images
- name: Build and push image
env:
VERSION: ${{ steps.version.outputs.version }}
run: make image VERSION="$VERSION"
VERSION: ${{ needs.determine-version.outputs.version }}
run: make image IMAGE_PLATFORMS=linux/${{ matrix.arch }} PUSH=true VERSION="$VERSION-${{ matrix.arch }}"

- name: Push images
merge-manifests:
needs: [determine-version, build-images]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push multi-arch manifests
env:
VERSION: ${{ steps.version.outputs.version }}
run: make push VERSION="$VERSION"
VERSION: ${{ needs.determine-version.outputs.version }}
run: make manifest IMAGE_PLATFORMS=linux/amd64,linux/arm64 VERSION="$VERSION"

- name: Push latest tags for releases
- name: Create and push latest manifests
if: startsWith(github.ref, 'refs/tags/v')
run: |
make image VERSION=latest
make push VERSION=latest
env:
VERSION: ${{ needs.determine-version.outputs.version }}
run: make manifest IMAGE_PLATFORMS=linux/amd64,linux/arm64 VERSION=latest SOURCE_VERSION="$VERSION"

release-artifacts:
needs: [determine-version, merge-manifests]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Build CLI binaries
if: startsWith(github.ref, 'refs/tags/v')
env:
VERSION: ${{ steps.version.outputs.version }}
VERSION: ${{ needs.determine-version.outputs.version }}
run: make release-binaries VERSION="$VERSION"

- name: Generate release notes
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
VERSION: ${{ needs.determine-version.outputs.version }}
run: go run ./hack/release-notes "$VERSION" > /tmp/release-notes.md

- name: Upload CLI binaries to GitHub release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
VERSION: ${{ needs.determine-version.outputs.version }}
run: |
gh release create "$VERSION" --verify-tag --draft --title "$VERSION" --notes-file /tmp/release-notes.md || true
gh release upload "$VERSION" --clobber \
Expand All @@ -83,7 +124,7 @@ jobs:
publish-helm-chart:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs: release
needs: release-artifacts
permissions:
contents: read
packages: write
Expand Down
36 changes: 28 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Image configuration
REGISTRY ?= ghcr.io/kelos-dev
VERSION ?= latest
IMAGE_DIRS ?= cmd/kelos-controller cmd/kelos-spawner cmd/kelos-token-refresher cmd/kelos-webhook-server cmd/ghproxy claude-code codex gemini opencode cursor
IMAGE_DIRS ?= cmd/kelos-controller cmd/kelos-spawner cmd/kelos-token-refresher cmd/ghproxy cmd/kelos-webhook-server claude-code codex gemini opencode cursor
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: cmd/ghproxy and cmd/kelos-webhook-server are back in IMAGE_DIRS, but their Dockerfiles weren't updated for multi-arch. The image build loop (line 89) renames binaries to bin/$name-linux-$arch, so bin/ghproxy no longer exists — only bin/ghproxy-linux-amd64. Their Dockerfiles still COPY bin/ghproxy . and COPY bin/kelos-webhook-server ., which will fail.

Needs the same ARG TARGETARCH + renamed COPY treatment as the other Dockerfiles:

ARG TARGETARCH
COPY bin/ghproxy-linux-${TARGETARCH} ghproxy

LOCAL_ARCH ?= $(shell go env GOARCH)

# Version injection for the kelos CLI – only set ldflags when an explicit
# version is given so that dev builds fall through to runtime/debug info.
Expand Down Expand Up @@ -79,20 +80,39 @@ build: ## Build binaries (use WHAT=cmd/kelos to build specific binary).
run: ## Run a controller from your host.
go run ./cmd/kelos-controller

IMAGE_PLATFORMS ?= linux/$(LOCAL_ARCH)
IMAGE_ARCHES = $(shell echo "$(IMAGE_PLATFORMS)" | tr ',' '\n' | cut -d'/' -f2 | tr '\n' ' ')
PUSH ?= false

.PHONY: image
image: ## Build docker images (use WHAT to build specific image).
image: ## Build docker images (use WHAT, IMAGE_PLATFORMS, PUSH=true to customize).
@for dir in $(filter cmd/%,$(or $(WHAT),$(IMAGE_DIRS))); do \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: cmd/kelos-token-refresher matches cmd/% here, so it gets pre-built and renamed to bin/kelos-token-refresher-linux-$arch. But its Dockerfile is a multi-stage build that rebuilds from source inside Docker — these pre-built binaries are never used. Not a correctness issue (the Docker build still works), but it wastes CI time. Consider either updating its Dockerfile to match the others, or excluding it from this loop.

GOOS=linux GOARCH=amd64 $(MAKE) build WHAT=$$dir; \
name=$$(basename $$dir); \
for arch in $(IMAGE_ARCHES); do \
GOOS=linux GOARCH=$$arch $(MAKE) build WHAT=$$dir; \
mv bin/$$name bin/$${name}-linux-$$arch; \
done; \
done
@for arch in $(IMAGE_ARCHES); do \
GOOS=linux GOARCH=$$arch $(MAKE) build WHAT=cmd/kelos-capture; \
mv bin/kelos-capture bin/kelos-capture-linux-$$arch; \
done
@GOOS=linux GOARCH=amd64 $(MAKE) build WHAT=cmd/kelos-capture
@for dir in $(or $(WHAT),$(IMAGE_DIRS)); do \
docker build -t $(REGISTRY)/$$(basename $$dir):$(VERSION) -f $$dir/Dockerfile .; \
docker buildx build --platform $(IMAGE_PLATFORMS) \
$(if $(filter true,$(PUSH)),--push,--load) \
-t $(REGISTRY)/$$(basename $$dir):$(VERSION) \
-f $$dir/Dockerfile .; \
done

.PHONY: push
push: ## Push docker images (use WHAT to push specific image).
.PHONY: manifest
SOURCE_VERSION ?= $(VERSION)

manifest: ## Create and push multi-arch manifest from per-arch images (use WHAT, IMAGE_PLATFORMS, SOURCE_VERSION).
@for dir in $(or $(WHAT),$(IMAGE_DIRS)); do \
docker push $(REGISTRY)/$$(basename $$dir):$(VERSION); \
name=$$(basename $$dir); \
docker buildx imagetools create \
-t $(REGISTRY)/$$name:$(VERSION) \
$(foreach arch,$(IMAGE_ARCHES),$(REGISTRY)/$$name:$(SOURCE_VERSION)-$(arch) ); \
done

RELEASE_PLATFORMS ?= linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
Expand Down
3 changes: 2 additions & 1 deletion claude-code/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
COPY claude-code/kelos_entrypoint.sh /kelos_entrypoint.sh
RUN chmod +x /kelos_entrypoint.sh

COPY bin/kelos-capture /kelos/kelos-capture
ARG TARGETARCH
COPY bin/kelos-capture-linux-${TARGETARCH} /kelos/kelos-capture

RUN useradd -u 61100 -m -s /bin/bash claude
RUN mkdir -p /home/claude/.claude && chown -R claude:claude /home/claude
Expand Down
3 changes: 2 additions & 1 deletion cmd/kelos-controller/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM gcr.io/distroless/static:nonroot
ARG TARGETARCH
WORKDIR /
COPY bin/kelos-controller .
COPY bin/kelos-controller-linux-${TARGETARCH} kelos-controller
USER 65532:65532
ENTRYPOINT ["/kelos-controller"]
3 changes: 2 additions & 1 deletion cmd/kelos-spawner/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM gcr.io/distroless/static:nonroot
ARG TARGETARCH
WORKDIR /
COPY bin/kelos-spawner .
COPY bin/kelos-spawner-linux-${TARGETARCH} kelos-spawner
USER 65532:65532
ENTRYPOINT ["/kelos-spawner"]
3 changes: 2 additions & 1 deletion codex/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ RUN npm install -g @openai/codex@${CODEX_VERSION}
COPY codex/kelos_entrypoint.sh /kelos_entrypoint.sh
RUN chmod +x /kelos_entrypoint.sh

COPY bin/kelos-capture /kelos/kelos-capture
ARG TARGETARCH
COPY bin/kelos-capture-linux-${TARGETARCH} /kelos/kelos-capture

RUN useradd -u 61100 -m -s /bin/bash agent
RUN mkdir -p /home/agent/.codex && chown -R agent:agent /home/agent
Expand Down
3 changes: 2 additions & 1 deletion cursor/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ ENV PATH="/usr/local/go/bin:${PATH}"
COPY cursor/kelos_entrypoint.sh /kelos_entrypoint.sh
RUN chmod +x /kelos_entrypoint.sh

COPY bin/kelos-capture /kelos/kelos-capture
ARG TARGETARCH
COPY bin/kelos-capture-linux-${TARGETARCH} /kelos/kelos-capture

RUN useradd -u 61100 -m -s /bin/bash agent
RUN mkdir -p /home/agent/.cursor && chown -R agent:agent /home/agent
Expand Down
3 changes: 2 additions & 1 deletion gemini/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ RUN npm install -g @google/gemini-cli@${GEMINI_CLI_VERSION}
COPY gemini/kelos_entrypoint.sh /kelos_entrypoint.sh
RUN chmod +x /kelos_entrypoint.sh

COPY bin/kelos-capture /kelos/kelos-capture
ARG TARGETARCH
COPY bin/kelos-capture-linux-${TARGETARCH} /kelos/kelos-capture

RUN useradd -u 61100 -m -s /bin/bash agent
RUN mkdir -p /home/agent/.gemini && chown -R agent:agent /home/agent
Expand Down
3 changes: 2 additions & 1 deletion opencode/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ RUN npm install -g opencode-ai@${OPENCODE_VERSION}
COPY opencode/kelos_entrypoint.sh /kelos_entrypoint.sh
RUN chmod +x /kelos_entrypoint.sh

COPY bin/kelos-capture /kelos/kelos-capture
ARG TARGETARCH
COPY bin/kelos-capture-linux-${TARGETARCH} /kelos/kelos-capture

RUN useradd -u 61100 -m -s /bin/bash agent
RUN mkdir -p /home/agent/.opencode && chown -R agent:agent /home/agent
Expand Down
Loading