Skip to content

store/gcs: GCS object-store adapter + integration harness (+ Go 1.26.4)#10

Merged
dratner merged 3 commits into
mainfrom
feat/store-gcs
Jun 7, 2026
Merged

store/gcs: GCS object-store adapter + integration harness (+ Go 1.26.4)#10
dratner merged 3 commits into
mainfrom
feat/store-gcs

Conversation

@dratner

@dratner dratner commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

store/gcs is the last Phase-1 library slice — with it, extraction, chunking, content, embedding, and storage all live on the library, so Morris can fully cut over and retire its bespoke storage + embedding code.

Adapter

  • Implements store.ObjectStore (Get/Put/Delete/Exists) over cloud.google.com/go/storage. Keys are GCS object names verbatim (no prefix/normalization), per the opaque-key contract. storage.ErrObjectNotExiststore.ErrObjectNotFound.
  • Deliberate divergence from Morris: Delete reports ErrObjectNotFound on a missing key per our interface, rather than Morris's idempotent delete — matching the testcms memory fake.
  • Constructors: New(ctx, bucket, ...option.ClientOption), NewWithClient, plus Close/Bucket.
  • Opt-in subpackage (ADR 0006): core store stays stdlib-only — verified go list -deps ./store has 0 cloud deps; the existing depguard rule already denies core from importing it. The SDK's large transitive tree is fully confined here.

Integration harness (closes deferred-tooling #1)

  • There's no official Google GCS emulator (unlike Pub/Sub, Firestore, etc.), so tests run against a Dockerized fsouza/fake-gcs-server — the de-facto standard, also used by Google's own Go client tests.
  • Build-tagged (//go:build integration) round-trip tests: Put/Get/Exists/Delete, overwrite, and not-found mapping (incl. the divergent Delete).
  • New make test-integration starts the emulator (-public-host so downloads resolve), waits for readiness, runs the tagged tests via STORAGE_EMULATOR_HOST, and tears it down. Manual-dispatch integration.yml runs the same on CI.
  • Default make test and CI stay network-/Docker-free: the tests are tag-gated and t.Skip without the emulator.

Piggybacked: Go 1.26.3 → 1.26.4 (security)

govulncheck flagged two newly-published stdlib CVEsnet/textproto (GO-2026-5039) and crypto/x509 (GO-2026-5037) — present in 1.26.3, repo-wide and reachable from ordinary code, fixed in 1.26.4. These pass yesterday's CI but would now fail govulncheck on any PR (and the weekly security scan on main). Bumped the go directive; verified govulncheck is clean under 1.26.4. (Per the discussion, folded into this PR rather than a separate bump.)

Verification

make lint, go test ./..., go vet ./..., govulncheck (clean), and make test-integration (real fake-gcs-server round-trips) all pass under go1.26.4.

🤖 Generated with Claude Code

store/gcs is the GCS-backed store.ObjectStore — the last Phase-1 library slice
before Morris can fully cut over (extraction, chunking, content, embedding, and
now storage all land on the library). A clean lift from Morris, conformed to
maestro-cms's interface.

- store/gcs: Get/Put/Delete/Exists over cloud.google.com/go/storage. Keys are
  GCS object names verbatim (no prefix/normalization), storage.ErrObjectNotExist
  maps to store.ErrObjectNotFound. Deliberate divergence from Morris: Delete
  reports ErrObjectNotFound on a missing key per OUR interface contract, not
  Morris's idempotent delete. New(...option)/NewWithClient/Close/Bucket.
  Opt-in subpackage (ADR 0006): core `store` stays stdlib-only (verified: 0 cloud
  deps in `go list -deps ./store`); depguard already denies core from importing
  it.

- Integration harness (closes deferred-tooling #1): build-tagged
  (//go:build integration) round-trip tests against a Dockerized
  fsouza/fake-gcs-server (no official Google GCS emulator exists; fsouza is the
  de-facto standard, also used by Google's own Go client tests). New
  `make test-integration` starts the emulator with -public-host (so downloads
  resolve), waits for readiness, runs the tagged tests via STORAGE_EMULATOR_HOST,
  and tears it down. Manual-dispatch integration.yml workflow runs the same on
  CI. Default `make test` and CI stay network-/Docker-free (tests are tag-gated
  and t.Skip without the emulator).

- Bump Go 1.26.3 -> 1.26.4: govulncheck flagged two newly-published stdlib CVEs
  (net/textproto GO-2026-5039, crypto/x509 GO-2026-5037), repo-wide and reachable
  from ordinary code; 1.26.4 patches both. Verified govulncheck is clean under
  1.26.4.

Verified: make lint, go test ./..., go vet ./..., govulncheck (clean), and
make test-integration (real fake-gcs-server round-trips) all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 7, 2026 20:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in Google Cloud Storage adapter (store/gcs) implementing the core store.ObjectStore interface, plus a Docker-backed integration-test harness to validate real round-trips against a GCS-compatible emulator. This completes the Phase-1 “storage” slice while keeping the core store package dependency-free (per ADR 0006).

Changes:

  • Add store/gcs adapter implementing Get/Put/Delete/Exists over cloud.google.com/go/storage, including not-found mapping to store.ErrObjectNotFound.
  • Add build-tagged integration tests and a make test-integration harness (Dockerized fsouza/fake-gcs-server), plus a manual-dispatch GitHub Actions workflow.
  • Update docs/spec + deferred-tooling status to reflect the adapter and integration harness landing.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
store/gcs/gcs.go New GCS-backed store.ObjectStore implementation and constructors.
store/gcs/gcs_integration_test.go Build-tagged integration tests exercising the adapter against an emulator endpoint.
Makefile Adds test-integration target to orchestrate fake-gcs-server + tagged tests.
go.mod Adds required GCS SDK dependencies and updates the Go toolchain directive.
go.sum Records new dependency checksums pulled in by the GCS SDK.
docs/spec-v1.md Updates spec to mention the landed store/gcs adapter + integration test harness.
docs/deferred-tooling.md Marks the integration harness/workflow item as DONE and documents approach.
.github/workflows/integration.yml Adds manual-dispatch workflow to run make test-integration in CI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread store/gcs/gcs_integration_test.go Outdated
Comment thread Makefile Outdated
Comment thread store/gcs/gcs.go Outdated
Comment thread store/gcs/gcs.go Outdated
dratner and others added 2 commits June 7, 2026 16:05
…; review nits

Addresses external review + Copilot on #10.

- Put no longer finalizes a partial object on a mid-stream reader error. A GCS
  Writer buffers and uploads on Close, so the prior `_ = w.Close()` after a
  failed io.Copy could commit a truncated object. Now the Writer gets a child
  context that is canceled before Close on copy failure, aborting the upload.
  New integration test TestGCSPutAbortsOnReaderError proves no object is left.

- Client ownership is explicit: New owns the client it creates and closes it;
  NewWithClient does NOT (the caller keeps ownership), so sharing one client
  across Stores is safe. NewWithClient now panics on empty bucket / nil client
  (wiring bugs) instead of yielding a Store that panics on first use.

- New's doc no longer overstates ctx lifetime: ctx is used only to construct the
  client and is not retained.

- test-integration is idempotent: it pre-removes any leftover container and
  tolerates stop failures, so an interrupted run doesn't wedge the next.

- Integration test checks the io.ReadAll error it previously dropped.

Verified: make lint, go test ./..., go vet ./..., and make test-integration
(all 6 store/gcs round-trips incl. the abort path) pass under go1.26.4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ror (review nit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dratner dratner merged commit c9bc8e2 into main Jun 7, 2026
6 checks passed
@dratner dratner deleted the feat/store-gcs branch June 7, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants