Skip to content

Commit 481db64

Browse files
committed
Split UI from Docker: UI never touches Docker, all ops via workers
Remove orchestrator import and Docker SDK from UI. All container operations now go through workers via REST API. No standalone mode. Workers must have Docker socket by design. - Remove orchestrator usage from main.py (17 call sites) - Remove docker SDK from requirements.txt - Add _get_all_worker_containers() for heartbeat-based status - Add _proxy_worker_deploy() for forwarding deploys to workers - All container management routes require worker_id - Update docker-compose files for UI + Worker architecture - Independent CI builds: only affected image gets rebuilt - Update AGENTS.md for new architecture
1 parent 82258ba commit 481db64

7 files changed

Lines changed: 336 additions & 285 deletions

File tree

.github/workflows/build.yml

Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,66 @@ on:
44
push:
55
tags: ["v*"]
66
workflow_dispatch:
7+
inputs:
8+
build_ui:
9+
description: 'Build UI image'
10+
type: boolean
11+
default: true
12+
build_worker:
13+
description: 'Build Worker image'
14+
type: boolean
15+
default: true
716

817
concurrency:
918
group: build-${{ github.ref }}
1019
cancel-in-progress: true
1120

1221
jobs:
22+
detect:
23+
runs-on: ubuntu-latest
24+
outputs:
25+
build_ui: ${{ steps.check.outputs.ui }}
26+
build_worker: ${{ steps.check.outputs.worker }}
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 2
32+
33+
- name: Detect what to build
34+
id: check
35+
run: |
36+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
37+
echo "ui=${{ inputs.build_ui }}" >> "$GITHUB_OUTPUT"
38+
echo "worker=${{ inputs.build_worker }}" >> "$GITHUB_OUTPUT"
39+
exit 0
40+
fi
41+
42+
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "all")
43+
BUILD_UI=false
44+
BUILD_WORKER=false
45+
46+
if echo "$CHANGED" | grep -qE '^(Dockerfile|requirements\.txt|app/(main|database|catalog|auth|compose_generator|exchange_rates|collectors|templates|static)|services/)'; then
47+
BUILD_UI=true
48+
fi
49+
if echo "$CHANGED" | grep -qE '^(Dockerfile\.worker|requirements-worker\.txt|app/(worker_api|orchestrator)\.py)'; then
50+
BUILD_WORKER=true
51+
fi
52+
if echo "$CHANGED" | grep -qE '^app/__init__\.py'; then
53+
BUILD_UI=true
54+
BUILD_WORKER=true
55+
fi
56+
57+
# Default: if no specific detection, build both (safety net)
58+
if [ "$BUILD_UI" = "false" ] && [ "$BUILD_WORKER" = "false" ]; then
59+
BUILD_UI=true
60+
BUILD_WORKER=true
61+
fi
62+
63+
echo "ui=$BUILD_UI" >> "$GITHUB_OUTPUT"
64+
echo "worker=$BUILD_WORKER" >> "$GITHUB_OUTPUT"
65+
echo "Build UI: $BUILD_UI, Build Worker: $BUILD_WORKER"
66+
1367
lint:
1468
runs-on: ubuntu-latest
1569
steps:
@@ -27,20 +81,58 @@ jobs:
2781
- name: Lint and format check
2882
run: ruff check app/ && ruff format --check app/
2983

30-
build:
31-
needs: lint
84+
build-ui:
85+
needs: [lint, detect]
86+
if: needs.detect.outputs.build_ui == 'true'
3287
runs-on: ubuntu-latest
3388
permissions:
3489
contents: read
3590
packages: write
36-
strategy:
37-
matrix:
38-
include:
39-
- image: drumsergio/cashpilot
40-
dockerfile: Dockerfile
41-
- image: drumsergio/cashpilot-worker
42-
dockerfile: Dockerfile.worker
91+
steps:
92+
- name: Checkout
93+
uses: actions/checkout@v4
94+
95+
- name: Set up QEMU
96+
uses: docker/setup-qemu-action@v3
97+
98+
- name: Set up Docker Buildx
99+
uses: docker/setup-buildx-action@v3
100+
101+
- name: Login to Docker Hub
102+
uses: docker/login-action@v3
103+
with:
104+
username: ${{ secrets.DOCKERHUB_USERNAME }}
105+
password: ${{ secrets.DOCKERHUB_TOKEN }}
106+
107+
- name: Extract metadata
108+
id: meta
109+
uses: docker/metadata-action@v5
110+
with:
111+
images: drumsergio/cashpilot
112+
tags: |
113+
type=raw,value=latest
114+
type=semver,pattern={{version}}
115+
type=semver,pattern={{major}}.{{minor}}
43116
117+
- name: Build and push UI image
118+
uses: docker/build-push-action@v6
119+
with:
120+
context: .
121+
file: Dockerfile
122+
platforms: linux/amd64,linux/arm64
123+
push: true
124+
tags: ${{ steps.meta.outputs.tags }}
125+
labels: ${{ steps.meta.outputs.labels }}
126+
cache-from: type=gha,scope=cashpilot-ui
127+
cache-to: type=gha,mode=max,scope=cashpilot-ui
128+
129+
build-worker:
130+
needs: [lint, detect]
131+
if: needs.detect.outputs.build_worker == 'true'
132+
runs-on: ubuntu-latest
133+
permissions:
134+
contents: read
135+
packages: write
44136
steps:
45137
- name: Checkout
46138
uses: actions/checkout@v4
@@ -57,24 +149,24 @@ jobs:
57149
username: ${{ secrets.DOCKERHUB_USERNAME }}
58150
password: ${{ secrets.DOCKERHUB_TOKEN }}
59151

60-
- name: Extract metadata (tags, labels)
152+
- name: Extract metadata
61153
id: meta
62154
uses: docker/metadata-action@v5
63155
with:
64-
images: ${{ matrix.image }}
156+
images: drumsergio/cashpilot-worker
65157
tags: |
66158
type=raw,value=latest
67159
type=semver,pattern={{version}}
68160
type=semver,pattern={{major}}.{{minor}}
69161
70-
- name: Build and push
162+
- name: Build and push Worker image
71163
uses: docker/build-push-action@v6
72164
with:
73165
context: .
74-
file: ${{ matrix.dockerfile }}
166+
file: Dockerfile.worker
75167
platforms: linux/amd64,linux/arm64
76168
push: true
77169
tags: ${{ steps.meta.outputs.tags }}
78170
labels: ${{ steps.meta.outputs.labels }}
79-
cache-from: type=gha,scope=${{ matrix.image }}
80-
cache-to: type=gha,mode=max,scope=${{ matrix.image }}
171+
cache-from: type=gha,scope=cashpilot-worker
172+
cache-to: type=gha,mode=max,scope=cashpilot-worker

.github/workflows/release.yml

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
- 'requirements-worker.txt'
1111
- 'app/**'
1212
- 'services/**'
13+
- 'docker-compose*.yml'
1314
workflow_dispatch:
1415

1516
permissions:
@@ -19,25 +20,61 @@ jobs:
1920
release:
2021
name: Bump version and release
2122
runs-on: ubuntu-latest
22-
# Skip releases triggered by the release workflow itself
2323
if: "!contains(github.event.head_commit.message, '[skip ci]')"
24+
outputs:
25+
new_tag: ${{ steps.version.outputs.new_tag }}
26+
build_ui: ${{ steps.changes.outputs.ui }}
27+
build_worker: ${{ steps.changes.outputs.worker }}
2428
steps:
2529
- name: Checkout code
2630
uses: actions/checkout@v4
2731
with:
2832
fetch-depth: 0
2933

34+
- name: Detect what changed
35+
id: changes
36+
run: |
37+
# Compare against the previous commit
38+
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
39+
echo "Changed files: $CHANGED"
40+
41+
BUILD_UI=false
42+
BUILD_WORKER=false
43+
44+
# UI changes: main.py, templates, static, collectors, Dockerfile, requirements.txt
45+
if echo "$CHANGED" | grep -qE '^(Dockerfile|requirements\.txt|app/(main|database|catalog|auth|compose_generator|exchange_rates|collectors|templates|static))' ; then
46+
BUILD_UI=true
47+
fi
48+
# Also trigger UI on services/ changes (catalog data)
49+
if echo "$CHANGED" | grep -qE '^services/'; then
50+
BUILD_UI=true
51+
fi
52+
53+
# Worker changes: worker_api.py, orchestrator.py, Dockerfile.worker, requirements-worker.txt
54+
if echo "$CHANGED" | grep -qE '^(Dockerfile\.worker|requirements-worker\.txt|app/(worker_api|orchestrator)\.py)'; then
55+
BUILD_WORKER=true
56+
fi
57+
58+
# If both Dockerfiles or shared files changed, build both
59+
if echo "$CHANGED" | grep -qE '^app/__init__\.py'; then
60+
BUILD_UI=true
61+
BUILD_WORKER=true
62+
fi
63+
64+
echo "ui=$BUILD_UI" >> "$GITHUB_OUTPUT"
65+
echo "worker=$BUILD_WORKER" >> "$GITHUB_OUTPUT"
66+
echo "Build UI: $BUILD_UI, Build Worker: $BUILD_WORKER"
67+
3068
- name: Get latest version tag
3169
id: version
70+
if: steps.changes.outputs.ui == 'true' || steps.changes.outputs.worker == 'true'
3271
run: |
33-
# Get the latest semver tag, default to v0.0.0 if none
3472
LATEST=$(git tag -l 'v*.*.*' --sort=-v:refname | head -1)
3573
if [ -z "$LATEST" ]; then
3674
LATEST="v0.0.0"
3775
fi
3876
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
3977
40-
# Increment patch version
4178
VERSION="${LATEST#v}"
4279
MAJOR=$(echo "$VERSION" | cut -d. -f1)
4380
MINOR=$(echo "$VERSION" | cut -d. -f2)
@@ -48,13 +85,15 @@ jobs:
4885
echo "Bumping $LATEST -> $NEW_TAG"
4986
5087
- name: Create and push tag
88+
if: steps.version.outputs.new_tag
5189
run: |
5290
git config user.name "github-actions[bot]"
5391
git config user.email "github-actions[bot]@users.noreply.github.com"
5492
git tag -a "${{ steps.version.outputs.new_tag }}" -m "Release ${{ steps.version.outputs.new_tag }}"
5593
git push origin "${{ steps.version.outputs.new_tag }}"
5694
5795
- name: Create GitHub Release
96+
if: steps.version.outputs.new_tag
5897
uses: softprops/action-gh-release@v2
5998
with:
6099
tag_name: ${{ steps.version.outputs.new_tag }}

0 commit comments

Comments
 (0)