Skip to content

feat(cli): pluggable image storage with Vercel Blob impl (PR 1/3)#52

Merged
znat merged 3 commits into
mainfrom
feat/image-storage
May 11, 2026
Merged

feat(cli): pluggable image storage with Vercel Blob impl (PR 1/3)#52
znat merged 3 commits into
mainfrom
feat/image-storage

Conversation

@znat
Copy link
Copy Markdown
Owner

@znat znat commented May 11, 2026

Summary

First slice of the AI-image work — introduces a pluggable storage layer for binary assets. No image generation yet; that lands in PR 2.

  • ImageStorage interface (cli/src/image/storage/types.ts): four methods — upload, urlFor, list, delete — audited against Vercel Blob, Cloudflare R2, AWS S3, and Supabase Storage. All four providers implement all four methods natively.
  • VercelBlobStorage (cli/src/image/storage/vercel-blob.ts): only provider implemented in this PR. Uses deterministic public URLs (https://<lowercase-storeId>.public.blob.vercel-storage.com/<key>) so we never need to round-trip through the SDK just to know where a blob lives.
  • StorageConfig discriminated union reserves r2 | s3 | supabase shapes in .gitpulse.json so future providers slot in without breaking parsers. createStorage() factory throws clearly for unimplemented providers.
  • Vitest split: yarn test runs unit tests (network-free, fully mocked). yarn test:integration runs a real round-trip against a Vercel Blob store, gated by describe.skipIf so missing creds are a clean skip, not a failure.
  • CI workflow (.github/workflows/storage-integration.yml) runs the integration test on pull_request and push: main. Secret BLOB_READ_WRITE_TOKEN is already configured on the repo; the store ID store_ryXhp9N7MsNEbMh1 is hardcoded in the workflow (it's public — appears in every blob URL).

Architecture context

The original plan considered per-branch Blob stores for preview isolation, but Vercel's auto-link mechanism only allows one BLOB_READ_WRITE_TOKEN env var per project — multi-store-per-project fails on env var conflict. We pivoted to one shared store, path-namespaced by branch, which preserves the isolation property without fighting the platform. PR 2 will introduce the _preview/<branch>/... key convention; PR 3 will add an on: pull_request.closed cleanup workflow that calls list(prefix) + delete(keys).

Test plan

  • CI's Storage Integration workflow passes on this PR
  • yarn workspace @gitpulse/cli test continues to pass (66 tests, no integration)
  • yarn workspace @gitpulse/cli typecheck clean
  • Locally: BLOB_READ_WRITE_TOKEN=... GITPULSE_TEST_STORE_ID=store_ryXhp9N7MsNEbMh1 yarn workspace @gitpulse/cli test:integration uploads → fetches 200 → lists → deletes → 404 (verified during development)

Out of scope (future PRs)

  • PR 2: Gemini image generation, processCommit hook, Story.imageKey schema field, site rendering in PRFeedItem + commit detail page
  • PR 3: PR-close cleanup workflow
  • PR 4+: R2 / S3 / Supabase implementations; release images

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Per-project image storage configuration with built-in Vercel Blob support (public URLs, upload/list/delete) and placeholders for other providers.
  • Tests

    • New integration tests exercising real blob lifecycle (upload, fetch, list, delete, eventual consistency) and unit tests for storage behavior and ID-to-host normalization.
  • Chores

    • Added CI workflow to run integration tests and a package script plus runtime dependency to support blob storage testing.

Review Change Stack

Lays groundwork for AI-generated PR illustrations: a four-method
ImageStorage interface (upload, urlFor, list, delete) audited against
Vercel Blob, R2, S3, and Supabase. PR 1 ships only the Vercel Blob
implementation; the StorageConfig discriminated union reserves the
other three so .gitpulse.json schemas can stage migrations later.

Includes a vitest config split so the network-touching integration
test runs only on demand (yarn test:integration) and a CI workflow
that exercises it on PRs from the same repo. Fork PRs skip via
describe.skipIf without failing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

Adds a pluggable ImageStorage contract, a Vercel Blob provider implementation, Zod project config support, unit and integration Vitest suites, Vitest configs separating integration tests, and a GitHub Actions workflow that runs integration tests with Node 22 and Yarn.

Changes

Pluggable Image Storage with Vercel Blob

Layer / File(s) Summary
Storage Interface & Types
cli/src/image/storage/types.ts
ImageStorage interface defines upload, URL resolution, listing, and deletion contracts. StorageConfig discriminated union covers vercel-blob, r2, s3, and supabase with provider-specific fields.
VercelBlobStorage Implementation
cli/src/image/storage/vercel-blob.ts
VercelBlobStorage class implements ImageStorage using @vercel/blob API. Manages token validation, public uploads with stable addressing, URL construction via storeIdToHost, cursor-paginated listing, and bulk deletion.
Storage Factory & Public Exports
cli/src/image/storage/index.ts
Centralizes module exports and introduces createStorage(config) factory that instantiates VercelBlobStorage for vercel-blob provider, throws for unimplemented providers, and enforces exhaustive type checking.
Project Configuration Schema
cli/src/project-config.ts
Adds Zod schemas StorageProviderSchema and ImagesSchema to validate .gitpulse.json storage configuration. Extends ProjectConfigSchema with optional images field, updating ProjectConfig type.
Vitest Configuration
cli/vitest.config.ts, cli/vitest.integration.config.ts
Dual configurations: vitest.config.ts excludes integration tests; vitest.integration.config.ts targets integration tests with 30-second timeout.
VercelBlobStorage Unit Tests
cli/src/image/storage/vercel-blob.test.ts
Mocked test suite for storeIdToHost normalization, constructor token validation, upload argument mapping, deterministic urlFor generation, cursor-paginated list aggregation, and delete behavior.
VercelBlobStorage Integration Tests
cli/src/image/storage/vercel-blob.integration.test.ts
Live integration test performing upload/fetch/list/delete round-trip against Vercel Blob store, requires environment credentials and performs automatic cleanup via afterEach.
CI Workflow & Dependencies
.github/workflows/storage-integration.yml, cli/package.json
GitHub Actions workflow runs integration tests on PR and main push with Node.js 22 and Yarn cache; cli/package.json adds test:integration script and @vercel/blob dependency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit hops through storage clouds today, 🐰
Blobs find homes where public URLs sway,
Tests upload, then fetch, then tidy the mess,
CI listens closely to ensure no regress,
Soon more providers may join this playful array.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(cli): pluggable image storage with Vercel Blob impl (PR 1/3)' accurately and concisely summarizes the main change: introducing a pluggable storage layer for images with a Vercel Blob implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/image-storage

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 11, 2026

Vercel preview — built from 324929cc54498f23ff025f7504701952f2982a0f

https://gitpulse-demo-3y2s7canh-znats-projects.vercel.app

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 11, 2026

Greptile Summary

Introduces a pluggable ImageStorage abstraction with a VercelBlobStorage implementation, a discriminated-union config schema in .gitpulse.json, and a split Vitest setup that keeps unit tests network-free while running real Vercel Blob round-trips under test:integration.

  • ImageStorage interface + StorageConfig union (types.ts, project-config.ts): four-method interface audited against four providers; r2/s3/supabase shapes reserved in the Zod schema so future providers land without breaking parsers.
  • VercelBlobStorage (vercel-blob.ts): deterministic public URL construction via storeIdToHost, paginated list, and key-to-URL mapping in delete — all wired up with an explicit token guard and injected env for testability.
  • Vitest split + CI workflow: yarn test excludes integration files; yarn test:integration runs the real round-trip gated by a same-repo PR check so fork PRs can't access the write token.

Confidence Score: 5/5

Safe to merge — the storage abstraction is self-contained, no existing functionality is modified, and all four core operations are covered by both unit and integration tests.

The change adds a net-new storage layer with no mutations to existing code paths. The VercelBlobStorage implementation is straightforward, the token guard is explicit, and the Zod schema extension is additive. The only notable gap is the waitFor404 poll window in the integration test being shorter than the documented CDN propagation ceiling, but this is a test reliability concern rather than a defect in the storage implementation itself.

The integration test (vercel-blob.integration.test.ts) and vitest.integration.config.ts are worth a second look given the 10 s poll window vs. the 30 s overall test timeout — narrow margin if CDN propagation is slow.

Important Files Changed

Filename Overview
cli/src/image/storage/vercel-blob.ts Core VercelBlobStorage implementation — clean constructor token guard, deterministic public URL construction, paginated list, and URL-mapped delete. Logic is sound.
cli/src/image/storage/types.ts Thin ImageStorage interface and StorageConfig discriminated union — well-structured and forward-compatible with future providers.
cli/src/image/storage/index.ts Factory createStorage() with exhaustive switch and never guard — correct and type-safe.
cli/src/image/storage/vercel-blob.integration.test.ts Real round-trip integration test — comprehensive flow (upload → fetch → list → delete → 404 check), but waitFor404 timeout (10 s) is shorter than documented CDN propagation ceiling (~60 s), creating a flake risk.
cli/src/image/storage/vercel-blob.test.ts Thorough unit tests covering storeIdToHost edge cases, URL encoding, pagination, and no-op delete — all mocked correctly.
cli/src/project-config.ts Adds StorageProviderSchema discriminated union to the project config with proper validation — clean extension that doesn't break existing parsers.
.github/workflows/storage-integration.yml CI workflow correctly gates on same-repo PRs to protect secrets, passes required env vars, and separates integration from unit runs.
cli/vitest.integration.config.ts Integration vitest config with 30 s timeout — margin may be tight if CDN propagation reaches its documented upper bound during the 404 poll.
cli/vitest.config.ts Unit test config correctly excludes *.integration.test.ts files so network-free tests stay isolated.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant createStorage
    participant VercelBlobStorage
    participant VercelBlobSDK as @vercel/blob SDK
    participant CDN as Vercel CDN

    Caller->>createStorage: createStorage(config)
    createStorage->>VercelBlobStorage: "new VercelBlobStorage({ storeId })"
    Note over VercelBlobStorage: validates BLOB_READ_WRITE_TOKEN<br/>derives host via storeIdToHost()

    Caller->>VercelBlobStorage: upload(key, body, contentType)
    VercelBlobStorage->>VercelBlobSDK: "put(key, body, { access:'public', token })"
    VercelBlobSDK-->>CDN: store blob

    Caller->>VercelBlobStorage: urlFor(key)
    VercelBlobStorage-->>Caller: https://host/encodePath(key)

    Caller->>VercelBlobStorage: list(prefix)
    loop paginated
        VercelBlobStorage->>VercelBlobSDK: "blobList({ prefix, cursor, token })"
        VercelBlobSDK-->>VercelBlobStorage: "{ blobs[], cursor }"
    end
    VercelBlobStorage-->>Caller: pathname[]

    Caller->>VercelBlobStorage: delete(keys[])
    VercelBlobStorage->>VercelBlobStorage: keys.map(urlFor)
    VercelBlobStorage->>VercelBlobSDK: "del(urls[], { token })"
    VercelBlobSDK-->>CDN: remove blobs
Loading

Fix All in Claude Code

Reviews (3): Last reviewed commit: "refactor(cli): tighten storage integrati..." | Re-trigger Greptile

Comment thread cli/src/image/storage/vercel-blob.ts
Comment thread .github/workflows/storage-integration.yml Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/src/image/storage/vercel-blob.integration.test.ts`:
- Around line 41-42: The test currently asserts an immediate 404 after deletion
(variable afterDelete and the fetch(url, { cache: 'no-store' }) call), which
flakes due to eventual consistency; replace the single
expect(afterDelete.status).toBe(404) with retry logic that re-fetches the URL
until a 404 is observed or a timeout elapses (e.g., up to 60s with a short
interval like 1s), returning success as soon as response.status === 404 and
throwing/failing the test if the timeout is reached; keep using cache:
'no-store' and ensure the retry loop wraps the fetch call used to set
afterDelete.

In `@cli/src/image/storage/vercel-blob.ts`:
- Around line 35-37: The urlFor method currently interpolates the raw key into
the URL which breaks for spaces, #, ?, or unicode; update urlFor(key: string) to
URL-encode path segments (e.g., split key on '/' and apply encodeURIComponent to
each segment, then join with '/') before building `https://${this.host}/${...}`
and apply the same encoding approach wherever the key is used to build public
URLs (including the delete() URL construction referenced in the diff) so both
urlFor and delete use the same encoded-key logic.
- Around line 60-63: storeIdToHost should validate and normalize storeId: trim
whitespace, ensure it's not empty, remove a case-insensitive "store_" prefix,
convert to lowercase, replace any characters not allowed in DNS labels with
hyphens, collapse consecutive hyphens and trim leading/trailing hyphens, and
throw an error if the resulting slug is empty; update the function storeIdToHost
to perform these checks and normalization before returning
`${slug}.public.blob.vercel-storage.com` (refer to the storeIdToHost function to
locate the change).

In `@cli/vitest.config.ts`:
- Around line 1-6: The current defineConfig call replaces Vitest's default
exclude patterns by setting test.exclude directly; update the configuration to
merge your additional patterns with Vitest's defaults by importing and using
configDefaults.exclude and spreading it into test.exclude so that defineConfig({
test: { exclude: [...configDefaults.exclude, '**/*.integration.test.ts'] } })
preserves defaults while adding your custom patterns (reference defineConfig,
test.exclude, and configDefaults.exclude when making the change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e6f7b706-3eda-4166-a189-d04885d20a2d

📥 Commits

Reviewing files that changed from the base of the PR and between e607e12 and 806526a.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • .github/workflows/storage-integration.yml
  • cli/package.json
  • cli/src/image/storage/index.ts
  • cli/src/image/storage/types.ts
  • cli/src/image/storage/vercel-blob.integration.test.ts
  • cli/src/image/storage/vercel-blob.test.ts
  • cli/src/image/storage/vercel-blob.ts
  • cli/src/project-config.ts
  • cli/vitest.config.ts
  • cli/vitest.integration.config.ts

Comment thread cli/src/image/storage/vercel-blob.integration.test.ts Outdated
Comment thread cli/src/image/storage/vercel-blob.ts
Comment thread cli/src/image/storage/vercel-blob.ts
Comment thread cli/vitest.config.ts Outdated
- Drop unused storeId field on VercelBlobStorage (greptile)
- URL-encode path segments in urlFor() so keys with spaces, ?, # or
  unicode produce valid URLs — required once PR 2/3 introduce branch
  names in key paths (coderabbit)
- Harden storeIdToHost(): trim whitespace, throw on empty or prefix-only
  input (coderabbit)
- Retry the post-delete 404 check up to 10s in the integration test;
  Vercel Blob is eventually consistent at the CDN, so an immediate
  re-fetch can briefly return cached bytes (coderabbit)
- Extend (not replace) vitest default excludes via configDefaults.exclude
  so node_modules, dist, .idea, etc. stay excluded by default (coderabbit)

Skipped: SHA-pinning GitHub Actions in storage-integration.yml —
would be inconsistent with the rest of the repo (ci.yml, deploy-vercel*.yml
all use floating @v6 tags). Belongs in a separate repo-wide PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@znat
Copy link
Copy Markdown
Owner Author

znat commented May 11, 2026

Review fixes (commit e148118)

Addressed:

  • vercel-blob.ts — Removed unused storeId field (greptile)
  • vercel-blob.tsurlFor() now URL-encodes path segments. Becomes relevant once PR 2/3 introduce branch names in keys, but worth fixing now (coderabbit)
  • vercel-blob.tsstoreIdToHost() now trims input and throws on empty/whitespace-only/store_-only values (coderabbit)
  • vercel-blob.integration.test.ts — Post-delete 404 check now retries for up to 10s. Vercel Blob is eventually consistent at the CDN (~60s per docs), and the immediate-re-fetch path can flake (coderabbit)
  • vitest.config.ts — Now spreads configDefaults.exclude instead of replacing it, so default patterns (.idea, node_modules, etc.) stay excluded (coderabbit)
  • Unit test coverage added for the encoding behavior and the new storeIdToHost error paths

Skipped:

  • storage-integration.yml SHA-pinning (greptile, security) — This is a real concern but pinning only this one workflow would be inconsistent with the rest of the repo (ci.yml, deploy-vercel*.yml, lint-pr-title.yml, etc. all use floating @v6 tags). Belongs in a separate repo-wide PR rather than landing partial coverage here.
  • storeIdToHost() DNS-label normalization (coderabbit) — Overkill for our case. Vercel store IDs are guaranteed alphanumeric and short (16 chars). Trim + empty check is enough hardening.

70 unit tests pass; integration test still passes locally and in CI.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
cli/src/image/storage/vercel-blob.integration.test.ts (1)

41-44: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Post-delete wait window is still shorter than the documented propagation window.

Line 43 waits only 10_000ms even though Line 41 notes cache/CDN propagation can be ~60s. This can still flake in CI; increase the wait budget (or make it configurable) and align integration test timeout accordingly.

What does Vercel Blob documentation state about cache/CDN propagation time after delete or overwrite operations, and is a 10-second post-delete retry window typically sufficient?

Also applies to: 48-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/image/storage/vercel-blob.integration.test.ts` around lines 41 - 44,
The post-delete retry window used by waitFor404(url, 10_000) is too short
compared to Vercel Blob's documented CDN propagation (~60s); update the test to
increase the wait budget to at least 60_000ms (or make the timeout configurable
via a constant/env var) and adjust the integration test timeout accordingly so
the expect(status).toBe(404) is retried long enough; apply the same change to
the other similar block around lines 48-58 that also calls waitFor404 or similar
post-delete checks.
cli/src/image/storage/vercel-blob.ts (1)

58-63: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden storeIdToHost normalization before composing hostname.

Line 61 strips only lowercase store_. Inputs like STORE_abc keep the underscore in the host label and produce malformed URLs. Make prefix removal case-insensitive and normalize/validate slug characters before returning the host.

♻️ Proposed fix
 export function storeIdToHost(storeId: string): string {
   const trimmed = storeId.trim();
   if (!trimmed) throw new Error('storeId is required');
-  const slug = trimmed.replace(/^store_/, '').toLowerCase();
+  const slug = trimmed
+    .replace(/^store_/i, '')
+    .toLowerCase()
+    .replace(/[^a-z0-9-]/g, '-')
+    .replace(/-+/g, '-')
+    .replace(/^-|-$/g, '');
   if (!slug) throw new Error(`Invalid storeId: ${storeId}`);
   return `${slug}.public.blob.vercel-storage.com`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/image/storage/vercel-blob.ts` around lines 58 - 63, The storeIdToHost
function currently only strips a lowercase "store_" prefix and can produce
invalid hostnames for inputs like "STORE_abc"; update storeIdToHost to remove
the "store_" prefix case-insensitively, then normalize and validate the
resulting slug (lowercase it, replace or remove invalid hostname characters,
collapse consecutive non-alphanumerics into single hyphens, trim
leading/trailing hyphens) and throw an error if the final slug is empty or
contains invalid characters; make these changes inside the storeIdToHost
implementation so the returned value is always a valid hostname label (used when
composing `${slug}.public.blob.vercel-storage.com`).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@cli/src/image/storage/vercel-blob.integration.test.ts`:
- Around line 41-44: The post-delete retry window used by waitFor404(url,
10_000) is too short compared to Vercel Blob's documented CDN propagation
(~60s); update the test to increase the wait budget to at least 60_000ms (or
make the timeout configurable via a constant/env var) and adjust the integration
test timeout accordingly so the expect(status).toBe(404) is retried long enough;
apply the same change to the other similar block around lines 48-58 that also
calls waitFor404 or similar post-delete checks.

In `@cli/src/image/storage/vercel-blob.ts`:
- Around line 58-63: The storeIdToHost function currently only strips a
lowercase "store_" prefix and can produce invalid hostnames for inputs like
"STORE_abc"; update storeIdToHost to remove the "store_" prefix
case-insensitively, then normalize and validate the resulting slug (lowercase
it, replace or remove invalid hostname characters, collapse consecutive
non-alphanumerics into single hyphens, trim leading/trailing hyphens) and throw
an error if the final slug is empty or contains invalid characters; make these
changes inside the storeIdToHost implementation so the returned value is always
a valid hostname label (used when composing
`${slug}.public.blob.vercel-storage.com`).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1965fca1-05ea-4dee-a1bc-6af4a00ad00d

📥 Commits

Reviewing files that changed from the base of the PR and between 806526a and e148118.

📒 Files selected for processing (4)
  • cli/src/image/storage/vercel-blob.integration.test.ts
  • cli/src/image/storage/vercel-blob.test.ts
  • cli/src/image/storage/vercel-blob.ts
  • cli/vitest.config.ts

- Pin actions/checkout to v6.0.2 and actions/setup-node to v6.4.0
  (latest releases). Tighter than floating @v6 without going full SHA.
- Drop describe.skipIf — the test now hard-fails when env vars are
  missing instead of silently passing as skipped. Both BLOB_READ_WRITE_TOKEN
  and GITPULSE_TEST_STORE_ID are required at import time.
- Add a job-level if-guard to the workflow so fork PRs (which can't
  read secrets) skip the workflow entirely rather than failing every PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
cli/src/image/storage/vercel-blob.integration.test.ts (1)

42-43: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Increase post-delete polling window to match documented propagation behavior.

Line 42 uses a 10s wait, but Lines 40-41 state propagation can be ~60s. This can still produce flaky failures in CI.

Proposed patch
-  it('uploads, fetches, lists, and deletes a blob', async () => {
+  it('uploads, fetches, lists, and deletes a blob', async () => {
@@
-    const status = await waitFor404(url, 10_000);
+    const status = await waitFor404(url, 60_000);
     expect(status).toBe(404);
-  });
+  }, 75_000);
What does Vercel Blob document as cache/CDN propagation delay after delete, and can reads still succeed during that window?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/image/storage/vercel-blob.integration.test.ts` around lines 42 - 43,
The test's post-delete polling window is too short: replace the 10_000 timeout
used in waitFor404(url, 10_000) with a longer window that matches Vercel Blob's
documented ~60s propagation (e.g., 60_000 or 65_000) so CI doesn't flake; update
the call to waitFor404 and any nearby comment that mentions propagation to
reflect the new timeout and rationale.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/storage-integration.yml:
- Line 13: The job currently declares "runs-on: ubuntu-latest" without a
timeout; add a job-level timeout by inserting a "timeout-minutes: <N>" entry
(e.g. 20–60 minutes) alongside the "runs-on" key in the same job definition so
the network integration workflow cannot hang indefinitely; update the job that
contains runs-on: ubuntu-latest (the network integration job) to include the
timeout-minutes setting.

---

Duplicate comments:
In `@cli/src/image/storage/vercel-blob.integration.test.ts`:
- Around line 42-43: The test's post-delete polling window is too short: replace
the 10_000 timeout used in waitFor404(url, 10_000) with a longer window that
matches Vercel Blob's documented ~60s propagation (e.g., 60_000 or 65_000) so CI
doesn't flake; update the call to waitFor404 and any nearby comment that
mentions propagation to reflect the new timeout and rationale.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e0d42e85-555c-41c2-9cde-ce8b5f1f2bc4

📥 Commits

Reviewing files that changed from the base of the PR and between e148118 and 324929c.

📒 Files selected for processing (2)
  • .github/workflows/storage-integration.yml
  • cli/src/image/storage/vercel-blob.integration.test.ts


jobs:
test:
runs-on: ubuntu-latest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add a job timeout for this network integration workflow.

Line 13 defines a network-dependent job without timeout-minutes; a stuck call can tie up runners unnecessarily.

Proposed patch
   test:
     runs-on: ubuntu-latest
+    timeout-minutes: 15
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
runs-on: ubuntu-latest
test:
runs-on: ubuntu-latest
timeout-minutes: 15
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/storage-integration.yml at line 13, The job currently
declares "runs-on: ubuntu-latest" without a timeout; add a job-level timeout by
inserting a "timeout-minutes: <N>" entry (e.g. 20–60 minutes) alongside the
"runs-on" key in the same job definition so the network integration workflow
cannot hang indefinitely; update the job that contains runs-on: ubuntu-latest
(the network integration job) to include the timeout-minutes setting.

@znat znat merged commit e4b166a into main May 11, 2026
7 checks passed
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.

1 participant