Skip to content

Commit 6bea969

Browse files
authored
release: publish installable v0.4.0 (#53)
* release: publish installable v0.4.0 * fix: make workflow release checks deterministic * fix: run workflow contracts in clean CI * fix: preserve legacy database upgrade paths * fix: keep legacy guards offline-compatible * fix: harden public release installers * fix: exclude frontend environment files from images --------- Co-authored-by: 2233admin <2233admin@users.noreply.github.com>
1 parent a37a1a7 commit 6bea969

43 files changed

Lines changed: 805 additions & 325 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.docker.example

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
COMPOSE_PROJECT_NAME=opencli-admin
2+
DOCKER_REGISTRY=ghcr.io/
3+
DOCKER_IMAGE_NAMESPACE=2233admin
4+
IMAGE_TAG=0.4.0
5+
6+
FRONTEND_PORT=3010
7+
API_PORT=8031
8+
PUBLIC_URL=http://localhost:8031
9+
10+
# Required. The installers generate all four values automatically.
11+
API_AUTH_TOKEN=
12+
BOOTSTRAP_ADMIN_TOKEN=
13+
SECRET_KEY=
14+
CREDENTIAL_ENCRYPTION_KEY=
15+
16+
DATABASE_URL=sqlite+aiosqlite:////data/opencli_admin.db
17+
TASK_EXECUTOR=local
18+
COLLECTION_MODE=local
19+
DEBUG=false
20+
21+
# Interactive browser is available at http://localhost:6080.
22+
OPENCLI_CDP_ENDPOINT=http://agent-1:19222
23+
NOVNC_PORT=6080
24+
NOVNC_BASE_PORT=6080
25+
26+
# Remote-agent defaults.
27+
AGENT_MODE=bridge
28+
OPENCLI_DAEMON_PORT=19825
29+
CHROME_SUFFIX=

.github/workflows/ci.yml

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,10 @@ on:
99
pull_request:
1010
workflow_dispatch:
1111

12-
jobs:
13-
frontend:
14-
runs-on: ubuntu-latest
15-
name: Frontend (Next.js)
16-
defaults:
17-
run:
18-
working-directory: frontend
19-
steps:
20-
- name: Checkout
21-
uses: actions/checkout@v4
22-
23-
- name: Setup pnpm
24-
uses: pnpm/action-setup@v4
25-
with:
26-
version: 11
27-
28-
- name: Setup Node
29-
uses: actions/setup-node@v4
30-
with:
31-
node-version: "20"
32-
cache: pnpm
33-
cache-dependency-path: frontend/pnpm-lock.yaml
34-
35-
- name: Install dependencies
36-
run: pnpm install --frozen-lockfile
37-
38-
- name: Typecheck
39-
run: pnpm exec tsc --noEmit
40-
41-
# TODO: add pnpm test once a frontend test suite exists
42-
43-
- name: Lint
44-
run: pnpm run lint
45-
46-
- name: Build
47-
run: pnpm run build
12+
permissions:
13+
contents: read
4814

15+
jobs:
4916
extension:
5017
runs-on: ubuntu-latest
5118
name: Browser Extension
@@ -99,8 +66,8 @@ jobs:
9966
with:
10067
python-version: "3.13"
10168

102-
- name: Install workflow contract dependency
103-
run: python -m pip install "pydantic>=2.10.0"
69+
- name: Install backend compiler dependencies
70+
run: python -m pip install -e ..
10471

10572
- name: Install dependencies
10673
run: |
@@ -126,6 +93,45 @@ jobs:
12693
- name: Browser smoke test
12794
run: pnpm test:smoke
12895

96+
release-contract:
97+
runs-on: ubuntu-latest
98+
name: Public Install Smoke
99+
env:
100+
API_AUTH_TOKEN: ci-release-token
101+
BOOTSTRAP_ADMIN_TOKEN: ci-bootstrap-admin-token
102+
SECRET_KEY: ci-release-secret
103+
CREDENTIAL_ENCRYPTION_KEY: MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA=
104+
COMPOSE_PROJECT_NAME: opencli-admin-ci
105+
steps:
106+
- name: Checkout
107+
uses: actions/checkout@v6
108+
with:
109+
persist-credentials: false
110+
111+
- name: Validate Compose
112+
run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml config --quiet
113+
114+
- name: Build and start public stack
115+
run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml up -d --build --wait api frontend agent-1
116+
117+
- name: Verify public endpoints
118+
run: |
119+
curl --fail --silent --show-error http://localhost:3010/login >/dev/null
120+
curl --fail --silent --show-error http://localhost:8031/health >/dev/null
121+
curl --fail --silent --show-error \
122+
-H "Authorization: Bearer ci-bootstrap-admin-token" \
123+
-H "X-API-Token: ci-release-token" \
124+
http://localhost:8031/api/v1/auth/me |
125+
python -c 'import json,sys; assert json.load(sys.stdin)["data"]["subject"] == "bootstrap-admin"'
126+
127+
- name: Show logs on failure
128+
if: failure()
129+
run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml logs --tail=200 api frontend agent-1
130+
131+
- name: Stop public stack
132+
if: always()
133+
run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml down -v
134+
129135
backend:
130136
runs-on: ubuntu-latest
131137
name: Backend Quality

.github/workflows/release.yml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
images:
13+
name: ${{ matrix.name }}
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
packages: write
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
include:
22+
- name: API image
23+
image: opencli-admin-api
24+
context: .
25+
file: Dockerfile
26+
build_args: |
27+
IMAGE_TAG=__VERSION__
28+
suffix: ""
29+
- name: Frontend image
30+
image: opencli-admin-frontend
31+
context: ./frontend
32+
file: ./frontend/Dockerfile
33+
build_args: |
34+
BACKEND_URL=http://api:8000
35+
suffix: ""
36+
- name: Agent image
37+
image: opencli-admin-agent
38+
context: .
39+
file: ./agent/Dockerfile
40+
build_args: ""
41+
suffix: ""
42+
- name: Interactive Chrome image
43+
image: opencli-admin-chrome
44+
context: .
45+
file: ./chrome/Dockerfile
46+
build_args: ""
47+
suffix: ""
48+
- name: Agent Chrome image
49+
image: opencli-admin-agent
50+
context: .
51+
file: ./agent/Dockerfile
52+
suffix: "-chrome"
53+
build_args: |
54+
INSTALL_CHROME=true
55+
steps:
56+
- name: Checkout
57+
uses: actions/checkout@v4
58+
with:
59+
persist-credentials: false
60+
61+
- name: Resolve release version
62+
id: version
63+
shell: bash
64+
run: echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
65+
66+
- name: Resolve build arguments
67+
id: build_args
68+
shell: bash
69+
env:
70+
MATRIX_BUILD_ARGS: ${{ matrix.build_args }}
71+
RELEASE_VERSION: ${{ steps.version.outputs.value }}
72+
run: |
73+
{
74+
echo "value<<EOF"
75+
printf '%s\n' "$MATRIX_BUILD_ARGS" | sed "s/__VERSION__/$RELEASE_VERSION/g"
76+
echo "EOF"
77+
} >> "$GITHUB_OUTPUT"
78+
79+
- name: Set up QEMU
80+
uses: docker/setup-qemu-action@v3
81+
82+
- name: Set up Buildx
83+
uses: docker/setup-buildx-action@v3
84+
85+
- name: Log in to GHCR
86+
uses: docker/login-action@v3
87+
with:
88+
registry: ghcr.io
89+
username: ${{ github.actor }}
90+
password: ${{ secrets.GITHUB_TOKEN }}
91+
92+
- name: Build and push
93+
uses: docker/build-push-action@v6
94+
with:
95+
context: ${{ matrix.context }}
96+
file: ${{ matrix.file }}
97+
platforms: linux/amd64,linux/arm64
98+
push: true
99+
build-args: ${{ steps.build_args.outputs.value }}
100+
tags: |
101+
ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:${{ steps.version.outputs.value }}${{ matrix.suffix }}
102+
ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:latest${{ matrix.suffix }}
103+
cache-from: type=gha,scope=${{ matrix.name }}
104+
cache-to: type=gha,mode=max,scope=${{ matrix.name }}
105+
provenance: mode=max
106+
sbom: true
107+
108+
github-release:
109+
name: GitHub Release
110+
runs-on: ubuntu-latest
111+
needs: images
112+
permissions:
113+
contents: write
114+
steps:
115+
- name: Checkout
116+
uses: actions/checkout@v4
117+
with:
118+
persist-credentials: false
119+
120+
- name: Create release
121+
uses: softprops/action-gh-release@v2
122+
with:
123+
generate_release_notes: true
124+
files: |
125+
docker-compose.yml
126+
docker-compose.build.yml
127+
.env.docker.example
128+
scripts/install.sh
129+
scripts/install.ps1

Dockerfile

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,22 @@ FROM ${REGISTRY}python:3.13-slim AS builder
44

55
WORKDIR /app
66

7-
# Switch to Aliyun apt mirror for faster downloads in China
8-
RUN sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \
9-
sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list 2>/dev/null || true
10-
117
# Install build deps
128
RUN apt-get update && apt-get install -y --no-install-recommends \
139
gcc libpq-dev \
1410
&& rm -rf /var/lib/apt/lists/*
1511

1612
# Install Python deps into a prefix so we can copy them cleanly
1713
COPY pyproject.toml .
18-
RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/ && \
19-
pip install --prefix=/install . -i https://mirrors.aliyun.com/pypi/simple/
14+
RUN pip install --upgrade pip \
15+
&& pip install --prefix=/install .
2016

2117
# ── Stage 2: runtime ──────────────────────────────────────────────────────────
2218
ARG REGISTRY=
2319
FROM ${REGISTRY}python:3.13-slim AS runtime
2420

2521
WORKDIR /app
2622

27-
# Switch to Aliyun apt mirror for faster downloads in China
28-
RUN sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \
29-
sed -i 's|http://deb.debian.org|http://mirrors.aliyun.com|g' /etc/apt/sources.list 2>/dev/null || true
30-
3123
# Runtime system deps (psycopg2 needs libpq, opencli needs Node.js 22+)
3224
RUN apt-get update && apt-get install -y --no-install-recommends \
3325
libpq5 curl ca-certificates git \
@@ -44,23 +36,13 @@ RUN npm install -g @jackwener/opencli@${OPENCLI_VERSION} \
4436
&& rm /tmp/patch-opencli.js \
4537
&& rm -rf /root/.npm
4638

47-
ARG OHMYOPENCLI_REPO=https://github.com/2233admin/OhMyOpenCLI.git
48-
ARG OHMYOPENCLI_COMMIT=73cc60c83586ef2c95469b3b70d6cfc80fa5bc53
49-
ARG OFFICIAL_SITE_CAPABILITY_COMMIT=73cc60c83586ef2c95469b3b70d6cfc80fa5bc53
50-
RUN git clone ${OHMYOPENCLI_REPO} /opt/ohmyopencli \
51-
&& cd /opt/ohmyopencli \
52-
&& git checkout --detach ${OHMYOPENCLI_COMMIT} \
53-
&& git merge-base --is-ancestor ${OFFICIAL_SITE_CAPABILITY_COMMIT} HEAD \
54-
&& npm ci \
55-
&& test "$(git rev-parse HEAD)" = "${OHMYOPENCLI_COMMIT}"
56-
5739
# Copy installed packages from builder
5840
COPY --from=builder /install /usr/local
5941

6042
# Copy application source
6143
COPY backend/ ./backend/
6244
COPY scripts/patch-opencli.js ./scripts/patch-opencli.js
63-
COPY scripts/verify_managed_opencli_runtime.py ./scripts/verify_managed_opencli_runtime.py
45+
COPY scripts/install-agent.sh ./scripts/install-agent.sh
6446
COPY alembic.ini .
6547

6648
# Entrypoint handles migrations
@@ -70,16 +52,12 @@ RUN sed -i 's/\r$//' /entrypoint.sh && chmod +x /entrypoint.sh
7052
# Non-root user for security; pre-create /data so the SQLite volume is writable
7153
RUN useradd -m -u 1000 appuser && \
7254
mkdir -p /data && \
73-
chown -R appuser:appuser /app /data /opt/ohmyopencli \
74-
&& cd /opt/ohmyopencli \
75-
&& HOME=/home/appuser npm run bootstrap \
76-
&& chown -R appuser:appuser /home/appuser/.opencli /opt/ohmyopencli
55+
chown -R appuser:appuser /app /data
7756
USER appuser
7857

7958
ENV PYTHONPATH=/app \
8059
PYTHONDONTWRITEBYTECODE=1 \
81-
PYTHONUNBUFFERED=1 \
82-
OHMYOPENCLI_ROOT=/opt/ohmyopencli
60+
PYTHONUNBUFFERED=1
8361
# Bake the image tag so the system config API can serve it to clients.
8462
ARG IMAGE_TAG=latest
8563
ENV IMAGE_TAG=${IMAGE_TAG}

0 commit comments

Comments
 (0)