fix(deploy): align Docker defaults with GHCR releases (#4327)

* fix(deploy): align Docker defaults with GHCR releases

Generated-By: looper 0.9.9 (runner=worker, agent=opencode)

* fix(ci): publish stable Docker tags from release workflow

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(ci): fold reusable workflow guard expression

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(ci): gate Docker release publish

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(ci): publish stable Docker tags after release

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(ci): guard Docker latest tag enable expression

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(deploy): update Helm chart GHCR defaults

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)

* fix(ci): publish latest from release workflow inputs

Generated-By: looper 0.9.9 (runner=fixer, agent=opencode)
This commit is contained in:
Marc Chan
2026-06-17 12:25:18 +08:00
committed by GitHub
parent 3c2c3e2cec
commit 7abb7888df
27 changed files with 106 additions and 54 deletions

View File

@@ -6,8 +6,27 @@ name: Docker image
# - tag (v*.*.*) → ghcr.io/<owner>/od:<tag> + :latest
on:
workflow_call:
inputs:
ref:
description: "Git ref to build when invoked as a reusable workflow."
required: false
type: string
default: ""
release_version:
description: "Stable Docker tag to publish when invoked from release automation."
required: false
type: string
default: ""
publish_latest:
description: "Whether to also publish :latest when invoked from release automation."
required: false
type: boolean
default: false
push:
branches: [main]
tags: ['v*.*.*']
pull_request:
jobs:
build-and-push:
@@ -19,6 +38,8 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -42,15 +63,17 @@ jobs:
with:
images: ghcr.io/${{ github.repository_owner }}/od
# spec §15.1 tag scheme:
# - main → :edge + :sha-<short>
# - vX.Y.Z → :X.Y.Z + :latest
# - any branch → :branch-<name>
# - main → :edge + :sha-<short>
# - vX.Y.Z push → :X.Y.Z + :latest
# - workflow_call (stable) → :<release_version> + optional :latest
# - pull_request → metadata only, no push
tags: |
type=ref,event=branch,suffix=-{{date 'YYYYMMDD-HHmmss' tz='UTC'}}
type=ref,event=tag
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }}
type=semver,pattern={{version}}
type=raw,value=${{ inputs.release_version }},enable=${{ inputs.release_version != '' }}
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' && inputs.release_version == '' }}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=sha,prefix=sha-,format=short
type=raw,value=latest,enable=${{ inputs.publish_latest == true }}
type=sha,prefix=sha-,format=short,enable=${{ github.ref == 'refs/heads/main' && inputs.release_version == '' }}
- name: Build and push
uses: docker/build-push-action@v6

View File

@@ -104,6 +104,7 @@ on:
permissions:
actions: write
contents: write
packages: write
concurrency:
group: open-design-release-stable-${{ inputs.channel }}-${{ inputs.dry_run && 'dry-run' || 'publish' }}
@@ -1011,6 +1012,31 @@ jobs:
path: ${{ runner.temp }}/release-assets
retention-days: 1
publish_docker_image:
name: Publish stable Docker image
needs:
- metadata
- publish
if: >-
${{
always() &&
!cancelled() &&
needs.metadata.outputs.dry_run != 'true' &&
needs.metadata.outputs.github_release_enabled == 'true' &&
needs.metadata.result == 'success' &&
needs.publish.result == 'success'
}}
permissions:
contents: read
packages: write
id-token: write
uses: ./.github/workflows/docker-image.yml
secrets: inherit
with:
ref: ${{ needs.metadata.outputs.commit }}
release_version: ${{ needs.metadata.outputs.stable_version }}
publish_latest: true
publish:
name: Publish ${{ needs.metadata.outputs.channel }} release
needs:

View File

@@ -88,7 +88,7 @@ Create a `deploy/.env` file:
OPEN_DESIGN_PORT=7456
OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
```
> Projects and database data are persisted automatically using Docker volumes.

View File

@@ -142,7 +142,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker image tag
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Required API token for daemon security
# Generate one with: openssl rand -hex 32

View File

@@ -58,7 +58,7 @@ This chart adheres to strict security defaults:
| Name | Description | Value |
| ------------------ | ----------------------------------------- | ---------------------------- |
| `commonLabels` | Custom labels injected into all resources | `{app.kubernetes.io/environment: production}` |
| `image.repository` | Open Design image repository | `vanjayak/open-design` |
| `image.repository` | Open Design image repository | `ghcr.io/nexu-io/od` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `image.tag` | Image tag (overrides AppVersion) | `latest` |

View File

@@ -5,7 +5,7 @@ commonLabels:
app.kubernetes.io/environment: "production"
image:
repository: vanjayak/open-design
repository: ghcr.io/nexu-io/od
pullPolicy: IfNotPresent
tag: "latest"

View File

@@ -1,5 +1,5 @@
# Image published by deploy/scripts/publish-images.sh.
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
# Image published by this repository's Docker workflow.
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Host port exposed on 127.0.0.1 by docker compose.
# Keep Compose bound to localhost; use an authenticated reverse proxy, SSH tunnel,

View File

@@ -25,10 +25,13 @@ Before starting:
Then pull and start the service:
```bash
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest docker compose pull
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest docker compose up -d --no-build
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest docker compose pull
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest docker compose up -d --no-build
```
Use `ghcr.io/nexu-io/od:latest` for the latest stable image, or
`ghcr.io/nexu-io/od:<version>` to pin a supported release.
Defaults:
- Host port: `127.0.0.1:7456` (`OPEN_DESIGN_PORT=8080` to publish on `127.0.0.1:8080`)
@@ -54,7 +57,7 @@ OPEN_DESIGN_ALLOWED_ORIGINS=https://od.example.com,http://203.0.113.10:7456 dock
Pin a specific published image with a digest instead of the mutable `latest` tag:
```bash
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design@sha256:<digest> docker compose up -d --no-build
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od@sha256:<digest> docker compose up -d --no-build
```
The image intentionally does not bundle Claude/Codex/Gemini CLI binaries. Keep
those outside the image, or build a separate private runtime layer if a server
@@ -74,7 +77,7 @@ without the workspace-write sandbox, which is useful when the container host
blocks unprivileged user namespaces, but it gives the Codex process broader
filesystem access inside the container.
## Publish to Docker Hub
## Manual image publish override
```bash
deploy/scripts/publish-images.sh --image_tag latest
@@ -83,15 +86,15 @@ deploy/scripts/publish-images.sh --image_tag latest
Useful overrides:
```bash
IMAGE_NAMESPACE=your-dockerhub-user deploy/scripts/publish-images.sh --arch arm64
deploy/scripts/publish-images.sh --image docker.io/your-user/open-design:0.1.0
IMAGE_NAMESPACE=your-ghcr-org deploy/scripts/publish-images.sh --arch arm64
deploy/scripts/publish-images.sh --image ghcr.io/your-org/od:0.1.0
```
The script defaults to:
- `docker.io/vanjayak/open-design:<tag>`
- `ghcr.io/nexu-io/od:<tag>`
- `linux/amd64,linux/arm64`
- `skopeo` push strategy with Docker credentials read from `~/.docker/config.json`
- `skopeo` push strategy with registry credentials read from `~/.docker/config.json`
- preloading base images through `skopeo` to reduce Docker Hub pull flakiness
If `127.0.0.1:7890` is available and no proxy is already set, the script uses it

View File

@@ -11,7 +11,7 @@ variables:
deploymentName: open-design-aci
location: eastus
templateFile: deploy/azure/container-instance.bicep
openDesignImage: docker.io/vanjayak/open-design:latest
openDesignImage: ghcr.io/nexu-io/od:latest
dnsNameLabel: open-design-$(Build.BuildId)
browserOrigin: https://od.example.com

View File

@@ -10,7 +10,7 @@ param containerGroupName string = 'open-design'
param dnsNameLabel string = toLower('open-design-${uniqueString(resourceGroup().id, location)}')
@description('Open Design container image.')
param image string = 'docker.io/vanjayak/open-design:latest'
param image string = 'ghcr.io/nexu-io/od:latest'
@secure()
@description('Required Open Design API token. Generate with: openssl rand -hex 32')

View File

@@ -3,7 +3,7 @@ name: open-design
services:
open-design:
container_name: open-design
image: ${OPEN_DESIGN_IMAGE:-docker.io/vanjayak/open-design:latest}
image: ${OPEN_DESIGN_IMAGE:-ghcr.io/nexu-io/od:latest}
build:
context: ..
dockerfile: deploy/Dockerfile

View File

@@ -11,7 +11,7 @@ set -euo pipefail
# Configuration
# ---------------------------------------------------------------------------
DEFAULT_PORT=7456
DEFAULT_IMAGE="docker.io/vanjayak/open-design:latest"
DEFAULT_IMAGE="ghcr.io/nexu-io/od:latest"
DEFAULT_MEM_LIMIT="384m"
HEALTH_TIMEOUT=60

View File

@@ -4,9 +4,9 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
IMAGE_TAG="${IMAGE_TAG:-latest}"
REGISTRY="${REGISTRY:-docker.io}"
IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-vanjayak}"
IMAGE_REPOSITORY="${IMAGE_REPOSITORY:-open-design}"
REGISTRY="${REGISTRY:-ghcr.io}"
IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-nexu-io}"
IMAGE_REPOSITORY="${IMAGE_REPOSITORY:-od}"
NODE_BASE_IMAGE="${NODE_BASE_IMAGE:-docker.io/library/node:24-alpine}"
RUNTIME_BASE_IMAGE="${RUNTIME_BASE_IMAGE:-docker.io/library/node:24-alpine}"
PUSH_STRATEGY="${PUSH_STRATEGY:-skopeo}"
@@ -103,9 +103,9 @@ Options:
--platforms <list> default: linux/amd64,linux/arm64
--arch <amd64|arm64> publish a single platform as <tag>-<arch>
--image_tag <tag> default: latest
--registry <registry> default: docker.io
--image_namespace <namespace> default: vanjayak
--image_repository <name> default: open-design
--registry <registry> default: ghcr.io
--image_namespace <namespace> default: nexu-io
--image_repository <name> default: od
--image <image-ref> override full image ref
--node_base_image <image-ref> default: docker.io/library/node:24-alpine
--runtime_base_image <image-ref> default: docker.io/library/node:24-alpine

View File

@@ -120,7 +120,7 @@ az deployment group create \
odApiToken="$OD_API_TOKEN" \
dnsNameLabel="$DNS_LABEL" \
allowedOrigins="$BROWSER_ORIGIN" \
image="docker.io/vanjayak/open-design:latest" \
image="ghcr.io/nexu-io/od:latest" \
cpuCores=1 \
memoryInGB=1 \
fileShareQuotaGB=10

View File

@@ -88,7 +88,7 @@ docker compose up -d
OPEN_DESIGN_PORT=7456
OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
```
> 프로젝트와 데이터베이스 데이터는 Docker 볼륨에 자동으로 보존됩니다.

View File

@@ -185,7 +185,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker-Image-Tag
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Erforderliches API-Token für die Daemon-Sicherheit
# Erzeugen Sie eines mit: openssl rand -hex 32

View File

@@ -186,7 +186,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Tag de l'image Docker
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Token API requis pour la sécurité du daemon
# Générez-en un avec : openssl rand -hex 32

View File

@@ -185,7 +185,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker イメージタグ
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Daemon セキュリティに必要な API トークン
# 次のコマンドで生成openssl rand -hex 32

View File

@@ -142,7 +142,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker 이미지 태그
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# daemon 보안에 필요한 API 토큰
# 생성 방법: openssl rand -hex 32

View File

@@ -185,7 +185,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Tag da imagem Docker
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Token de API obrigatório para segurança do daemon
# Gere um com: openssl rand -hex 32

View File

@@ -185,7 +185,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker 镜像标签
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Daemon 安全所需的 API 令牌
# 使用以下命令生成openssl rand -hex 32

View File

@@ -185,7 +185,7 @@ OPEN_DESIGN_MEM_LIMIT=384m
OPEN_DESIGN_ALLOWED_ORIGINS=https://yourdomain.com
# Docker 映像檔標籤
OPEN_DESIGN_IMAGE=docker.io/vanjayak/open-design:latest
OPEN_DESIGN_IMAGE=ghcr.io/nexu-io/od:latest
# Daemon 安全所需的 API 令牌
# 使用以下命令產生openssl rand -hex 32

View File

@@ -42,12 +42,12 @@ Running the installer without flags launches an interactive wizard:
[open-design] Docker: Docker version 26.1.3, build b72abbb
[open-design] Compose: Docker Compose version v2.27.1
Docker image [docker.io/vanjayak/open-design:latest]:
Docker image [ghcr.io/nexu-io/od:latest]:
Port [7456]:
Allowed origins (CORS, comma-separated, or empty) []:
Memory limit [384m]:
[open-design] Pulling image: docker.io/vanjayak/open-design:latest
[open-design] Pulling image: ghcr.io/nexu-io/od:latest
[open-design] Starting Open Design...
[open-design] Waiting for health check (up to 60s)...
[open-design] Daemon is healthy (200 OK)
@@ -57,7 +57,7 @@ Memory limit [384m]:
| Prompt | Default | Notes |
|--------|---------|-------|
| **Docker image** | `docker.io/vanjayak/open-design:latest` | Pin a digest for reproducibility: `docker.io/vanjayak/open-design@sha256:<digest>` |
| **Docker image** | `ghcr.io/nexu-io/od:latest` | Use `:latest` for the newest stable image, `:<version>` for a pinned release, or `@sha256:<digest>` for reproducibility |
| **Port** | `7456` | The port the daemon listens on. Must not be in use. |
| **Allowed origins** | _(empty)_ | CORS origins for reverse-proxy setups. See [`network-security.md`](network-security.md). Leave empty for localhost-only use. |
| **Memory limit** | `384m` | Container memory cap. Raise for large concurrent agent runs. |
@@ -141,7 +141,7 @@ bash deploy/scripts/update.sh
To update to a specific image:
```bash
bash deploy/scripts/update.sh --image=docker.io/vanjayak/open-design@sha256:<digest>
bash deploy/scripts/update.sh --image=ghcr.io/nexu-io/od@sha256:<digest>
```
The update script:
@@ -173,7 +173,7 @@ All settings live in `deploy/.env`. Edit it directly or re-run the installer to
| Variable | Default | Description |
|----------|---------|-------------|
| `OPEN_DESIGN_IMAGE` | `docker.io/vanjayak/open-design:latest` | Full image reference |
| `OPEN_DESIGN_IMAGE` | `ghcr.io/nexu-io/od:latest` | Full image reference |
| `OPEN_DESIGN_PORT` | `7456` | Host-side port (bound to `127.0.0.1`) |
| `OPEN_DESIGN_ALLOWED_ORIGINS` | _(empty)_ | CORS origins for reverse-proxy setups |
| `OPEN_DESIGN_MEM_LIMIT` | `384m` | Container memory cap |

View File

@@ -1608,7 +1608,7 @@ OD ships as a single multi-arch Docker image so the full plugin/marketplace syst
### 15.1 Image shape
- **Tag**: `ghcr.io/open-design/od:<version>` plus moving `:latest` and `:edge`.
- **Tag**: `ghcr.io/nexu-io/od:<version>` plus moving `:latest` and `:edge`.
- **Architectures**: `linux/amd64` and `linux/arm64` (single manifest list).
- **Contents**:
- Node 24 runtime + the daemon `dist/` bundle.
@@ -1657,7 +1657,7 @@ Anything settable via the desktop UI is also settable via `docker exec od od con
Local laptop:
```bash
docker run --rm -p 17456:17456 ghcr.io/open-design/od:latest
docker run --rm -p 17456:17456 ghcr.io/nexu-io/od:latest
open http://localhost:17456
```

View File

@@ -1603,7 +1603,7 @@ OD 以单个 multi-arch Docker image 发布,使完整 plugin/marketplace syste
### 15.1 Image shape
- **Tag**`ghcr.io/open-design/od:<version>`,以及 moving `:latest` 与 `:edge`。
- **Tag**`ghcr.io/nexu-io/od:<version>`,以及 moving `:latest` 与 `:edge`。
- **Architectures**`linux/amd64` 与 `linux/arm64`single manifest list
- **Contents**
- Node 24 runtime + daemon `dist/` bundle。
@@ -1646,7 +1646,7 @@ TAVILY_API_KEY=...
本地 laptop
```bash
docker run --rm -p 17456:17456 ghcr.io/open-design/od:latest
docker run --rm -p 17456:17456 ghcr.io/nexu-io/od:latest
open http://localhost:17456
```

View File

@@ -11,7 +11,7 @@
#
# Or pull a published tag:
#
# docker pull ghcr.io/open-design/od:edge
# docker pull ghcr.io/nexu-io/od:edge
#
# Then `docker compose -f tools/pack/docker-compose.yml up -d`.
@@ -20,9 +20,9 @@ version: "3.9"
services:
od:
# The image tag the operator wants. Spec §15.5 expects multi-arch
# ghcr.io/open-design/od:<tag>; the build path above produces
# ghcr.io/nexu-io/od:<tag>; the build path above produces
# `open-design/od:dev` locally.
image: ${OD_IMAGE:-ghcr.io/open-design/od:edge}
image: ${OD_IMAGE:-ghcr.io/nexu-io/od:edge}
container_name: od
# Spec §15.3 — every dimension the daemon respects in headless

View File

@@ -15,7 +15,7 @@
# revisiting the deployment shape.
image:
repository: ghcr.io/open-design/od
repository: ghcr.io/nexu-io/od
tag: edge
pullPolicy: IfNotPresent