From 4ebaead9f90c38e8a4aaeb799778452729f219fc Mon Sep 17 00:00:00 2001 From: gerrystev Date: Thu, 16 Apr 2026 15:22:19 +0800 Subject: [PATCH] feat: dockerfile heavy as container --- docker/Dockerfile.heavy | 13 +++++++++---- docker/docker-compose.yml | 2 +- infrastructure/index.ts | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile.heavy b/docker/Dockerfile.heavy index a9ea40ff5..588ee9644 100644 --- a/docker/Dockerfile.heavy +++ b/docker/Dockerfile.heavy @@ -3,7 +3,7 @@ # ============================================================ FROM golang:1.26.0-alpine AS builder -RUN apk add --no-cache git make +RUN apk add --no-cache git make nodejs npm && npm i -g pnpm WORKDIR /src @@ -14,7 +14,9 @@ RUN go mod download # Copy source and build COPY . . ARG GO_BUILD_TAGS=goolm,stdjson,bedrock -RUN make build GO_BUILD_TAGS=${GO_BUILD_TAGS} +RUN make build GO_BUILD_TAGS=${GO_BUILD_TAGS} && \ + make build-launcher GO_BUILD_TAGS=${GO_BUILD_TAGS} && \ + make build-launcher-tui GO_BUILD_TAGS=${GO_BUILD_TAGS} # ============================================================ # Stage 2: Node.js runtime with Python + MCP support @@ -48,6 +50,8 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ # Copy binary COPY --from=builder /src/build/picoclaw /usr/local/bin/picoclaw +COPY --from=builder /src/build/picoclaw-launcher /usr/local/bin/picoclaw-launcher +COPY --from=builder /src/build/picoclaw-launcher-tui /usr/local/bin/picoclaw-launcher-tui # Run onboard to create initial directories and config RUN /usr/local/bin/picoclaw onboard @@ -57,5 +61,6 @@ COPY workspace/ /root/.picoclaw/workspace/ VOLUME /root/.picoclaw/workspace -ENTRYPOINT ["picoclaw"] -CMD ["gateway"] +ENTRYPOINT ["picoclaw-launcher"] +CMD ["-console", "-public", "-no-browser"] + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 264a33cc4..b26346e0f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -56,7 +56,7 @@ services: picoclaw-launcher: build: context: .. - dockerfile: docker/Dockerfile.launcher + dockerfile: docker/Dockerfile.heavy args: GO_BUILD_TAGS: goolm,stdjson,bedrock image: docker-picoclaw-launcher:local diff --git a/infrastructure/index.ts b/infrastructure/index.ts index abb92dd38..cc88c3966 100644 --- a/infrastructure/index.ts +++ b/infrastructure/index.ts @@ -44,6 +44,14 @@ const gatewayServiceAccount = new gcp.serviceaccount.Account("picoclaw-gateway-s displayName: "PicoClaw Gateway Service Account", }); +// Persistent storage for /root/.picoclaw on Cloud Run. +const picoclawStateBucket = new gcp.storage.Bucket("picoclaw-volume", { + project, + location: region.toUpperCase(), + uniformBucketLevelAccess: true, + forceDestroy: false, +}); + // Grant the service account secretAccessor at the project level so it can // read all pre-existing secrets without needing setIamPolicy on each one. const iamSecretAccessor = new gcp.projects.IAMMember("picoclaw-sa-secret-accessor", { @@ -52,6 +60,12 @@ const iamSecretAccessor = new gcp.projects.IAMMember("picoclaw-sa-secret-accesso member: pulumi.interpolate`serviceAccount:${gatewayServiceAccount.email}`, }); +const stateBucketObjectAdmin = new gcp.storage.BucketIAMMember("picoclaw-sa-state-bucket-object-admin", { + bucket: picoclawStateBucket.name, + role: "roles/storage.objectAdmin", + member: pulumi.interpolate`serviceAccount:${gatewayServiceAccount.email}`, +}); + // ───────────────────────────────────────────── // Cloud Run v2 service — picoclaw gateway // ───────────────────────────────────────────── @@ -127,6 +141,12 @@ const gatewayService = new gcp.cloudrunv2.Service("picoclaw-gateway", { }, cpuIdle: true, }, + volumeMounts: [ + { + name: "picoclaw-home", + mountPath: "/root/.picoclaw", + }, + ], // startupProbe: { // httpGet: { // path: "/health", @@ -146,9 +166,18 @@ const gatewayService = new gcp.cloudrunv2.Service("picoclaw-gateway", { // }, }, ], + volumes: [ + { + name: "picoclaw-home", + gcs: { + bucket: picoclawStateBucket.name, + readOnly: false, + }, + }, + ], }, }, { - dependsOn: [iamSecretAccessor], + dependsOn: [iamSecretAccessor, stateBucketObjectAdmin], }); // Temporarily disable access filtering and allow unauthenticated access.