Skip to content

fix: ensure templates fallback to stale cache in case GH_TOKEN expired#2397

Merged
stalniy merged 1 commit intomainfrom
refactor/templates-fallback
Dec 22, 2025
Merged

fix: ensure templates fallback to stale cache in case GH_TOKEN expired#2397
stalniy merged 1 commit intomainfrom
refactor/templates-fallback

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Dec 19, 2025

Why

Useful for templates resilience and for local development

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling: errors now include richer response details for easier diagnosis.
    • More resilient caching: falls back to existing cache files when remote metadata is unavailable.
  • Refactor

    • Cache naming and lookup now use repository identifiers with commit SHA suffixes for consistent cache paths.
    • Simplified fetching and generation flow with clearer diagnostic logging.

✏️ Tip: You can customize this high-level summary in your review settings.

@stalniy stalniy requested a review from a team as a code owner December 19, 2025 13:41
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Walkthrough

Refactors template fetching and caching: fetchLatestCommitSha() now returns a SHA string and includes richer error causes; gallery caching uses repo mapping plus SHA-suffixed cache filenames with a fallback that discovers existing cache files when the GitHub fetch fails.

Changes

Cohort / File(s) Summary
Template Fetcher Service Simplification
apps/api/src/services/external/templates/template-fetcher.service.ts
fetchLatestCommitSha() now returns string (commit SHA) instead of an object; enhances error handling by attaching the full HTTP response as cause on non-200 responses.
Template Gallery Caching & Resilience
apps/api/src/services/external/templates/template-gallery.service.ts
Reworks cache resolution to derive owner/name from REPOSITORIES and build a cachePathPrefix; obtains latestCommitSha via fetchLatestCommitSha() with a fallback that searches existing cache files and extracts the SHA from filenames. Cache filenames now use repoOwner-repoName-<sha>.json; lookup uses cacheFileExists with the new path; generation, persistence, in-flight-task handling and logging updated to use the SHA-based naming.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Gallery as TemplateGalleryService
    participant Fetcher as TemplateFetcherService
    participant GitHub as GitHub API
    participant FS as Filesystem (cache)
    participant Generator as TemplateGenerator (in-flight)

    Gallery->>Fetcher: request latestCommitSha(repoOwner, repoName)
    Fetcher->>GitHub: GET /repos/{owner}/{repo}/commits/{branch}
    alt GitHub responds 200
        GitHub-->>Fetcher: 200 + commit.sha
        Fetcher-->>Gallery: return sha
    else GitHub fails
        GitHub-->>Fetcher: error
        Fetcher-->>Gallery: throw error
    end

    Gallery->>FS: check exists `cachePrefix-<sha>.json`
    alt cache exists
        FS-->>Gallery: return file -> serve cached categories
    else cache missing
        Gallery->>Generator: is generation in-flight?
        alt in-flight
            Generator-->>Gallery: await result -> return categories
        else not in-flight
            Gallery->>Generator: start generation with sha (or determine sha via fallback)
            alt fetcher failed earlier
                Gallery->>FS: find files matching cachePrefix-* -> extract sha
                FS-->>Gallery: maybe return existing sha or none
            end
            Generator-->>Gallery: generated categories
            Gallery->>FS: write `cachePrefix-<sha>.json`
            Gallery-->>Client: return categories
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Check all call sites of fetchLatestCommitSha() for updated return-type handling.
  • Review fallback cache-file discovery and filename parsing for edge cases.
  • Validate error propagation (error.cause) and logging messages for clarity.

Possibly related PRs

Suggested reviewers

  • baktun14

Poem

🐰 I hopped to fetch a SHA so bright,
Swapped bundled bits for stringy light.
When GitHub sleeps, I scour the store,
Old cache whispers what came before.
Templates bloom — I thump for more! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly indicates the main change: implementing a fallback mechanism to stale cache when GitHub token expires, which aligns with the core modifications in template-fetcher and template-gallery services.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/templates-fallback

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

@stalniy stalniy force-pushed the refactor/templates-fallback branch from 59d8913 to 52c387f Compare December 19, 2025 13:44
Copy link
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

🧹 Nitpick comments (1)
apps/api/src/services/external/templates/template-gallery.service.ts (1)

91-91: String slicing logic may be fragile for SHA extraction.

The slice calculation files[0].slice(path.normalize(cachePathPrefix).length + 1, -1 * ".json".length) assumes the filename structure matches exactly. Consider using a more explicit approach:

🔎 Proposed refactor using regex or path.basename
-      return files[0].slice(path.normalize(cachePathPrefix).length + 1, -1 * ".json".length);
+      const filename = path.basename(files[0], ".json");
+      const prefix = `${repoOwner}-${repoName}-`;
+      return filename.startsWith(prefix) ? filename.slice(prefix.length) : filename;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a62b55 and 59d8913.

📒 Files selected for processing (2)
  • apps/api/src/services/external/templates/template-fetcher.service.ts (1 hunks)
  • apps/api/src/services/external/templates/template-gallery.service.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.
Never use deprecated methods from libraries.
Don't add unnecessary comments to the code.

Files:

  • apps/api/src/services/external/templates/template-fetcher.service.ts
  • apps/api/src/services/external/templates/template-gallery.service.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: jzsfkzm
Repo: akash-network/console PR: 1498
File: apps/api/src/services/external/templates/template-fetcher.service.ts:0-0
Timestamp: 2025-07-03T14:40:49.886Z
Learning: In the TemplateFetcherService class in apps/api/src/services/external/templates/template-fetcher.service.ts, the error handling strategy should maintain process resilience by catching all errors and returning null rather than re-throwing critical errors, to avoid breaking the whole template fetching process.
📚 Learning: 2025-07-03T14:40:49.886Z
Learnt from: jzsfkzm
Repo: akash-network/console PR: 1498
File: apps/api/src/services/external/templates/template-fetcher.service.ts:0-0
Timestamp: 2025-07-03T14:40:49.886Z
Learning: In the TemplateFetcherService class in apps/api/src/services/external/templates/template-fetcher.service.ts, the error handling strategy should maintain process resilience by catching all errors and returning null rather than re-throwing critical errors, to avoid breaking the whole template fetching process.

Applied to files:

  • apps/api/src/services/external/templates/template-fetcher.service.ts
  • apps/api/src/services/external/templates/template-gallery.service.ts
🧬 Code graph analysis (1)
apps/api/src/services/external/templates/template-gallery.service.ts (1)
apps/api/src/services/external/templates/template-fetcher.service.ts (1)
  • REPOSITORIES (14-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/api/src/services/external/templates/template-gallery.service.ts (2)

85-93: Good resilience pattern for stale cache fallback.

The fallback mechanism that discovers existing cache files when fetchLatestCommitSha fails (e.g., due to expired GH_TOKEN) aligns well with the PR objective and the error handling strategy from learnings. This ensures templates remain available during GitHub API failures.


87-92: No action needed. fs.promises.glob is available in Node.js from version 22.0.0 behind the flag --experimental-glob and became un-flagged in v22.2.0. This codebase requires Node.js ^24.11.1, which fully supports both fs.promises.glob and Array.fromAsync—both are stable and cause no compatibility issues.

Likely an incorrect or invalid review comment.

apps/api/src/services/external/templates/template-fetcher.service.ts (2)

73-77: Clean simplification of return type.

The change from returning an object { repoOwner, repoName, repoVersion } to just the commit SHA string is a good simplification. The caller now correctly derives repoOwner/repoName from the REPOSITORIES constant, reducing redundancy.

The addition of { cause: response } to the error is helpful for debugging failed requests.


63-78: Method throws but caller handles gracefully - aligns with learnings.

While the retrieved learning suggests catching errors and returning null for resilience, in this case the throwing behavior is appropriate because the caller (getTemplatesFromRepo) implements the fallback logic with .catch() that falls back to stale cache. This maintains process resilience at the appropriate layer.

() => false
);

console.log("Cache file exists:--------->", cacheFileExists, cacheFilePath, latestCommitSha);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove debug artifacts before merging.

The console.log statement with "Cache file exists:--------->" appears to be a debug artifact. Consider removing or replacing with structured logging consistent with the rest of the file.

🔎 Proposed fix
-    console.log("Cache file exists:--------->", cacheFileExists, cacheFilePath, latestCommitSha);
+    console.log("Cache check:", { exists: cacheFileExists, path: cacheFilePath, sha: latestCommitSha });

Or remove entirely if not needed for production logging.

📝 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
console.log("Cache file exists:--------->", cacheFileExists, cacheFilePath, latestCommitSha);
console.log("Cache check:", { exists: cacheFileExists, path: cacheFilePath, sha: latestCommitSha });
🤖 Prompt for AI Agents
In apps/api/src/services/external/templates/template-gallery.service.ts at line
99 there is a console.log debug statement ("Cache file exists:--------->",
cacheFileExists, cacheFilePath, latestCommitSha); remove this debug artifact
before merging — either delete the line entirely if not needed or replace it
with the module's structured logger (e.g., logger.debug or processLogger.debug)
using a clear message and structured fields (cacheExists, cachePath, commitSha)
to match the file's existing logging conventions.

Copy link
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: 0

🧹 Nitpick comments (2)
apps/api/src/services/external/templates/template-gallery.service.ts (2)

91-91: Improve SHA extraction robustness with validation.

The slice operation extracts the SHA from the filename but doesn't validate the result. If the filename format is unexpected, this could fail silently or produce an invalid SHA.

🔎 Suggested improvement with validation
-      return files[0].slice(path.normalize(cachePathPrefix).length + 1, -1 * ".json".length);
+      const filename = path.basename(files[0], ".json");
+      const sha = filename.slice(`${repoOwner}-${repoName}-`.length);
+      
+      // Validate SHA format (Git SHAs are 40-character hex strings)
+      if (!/^[0-9a-f]{40}$/i.test(sha)) {
+        throw new Error(`Invalid SHA extracted from cache filename: ${sha}`);
+      }
+      
+      return sha;

This approach:

  • Uses path.basename for cleaner path handling
  • Validates the extracted SHA format
  • Provides a clear error message if validation fails

88-88: Consider more specific glob pattern.

The glob pattern ${cachePathPrefix}-*.json could potentially match unexpected files if other similarly-named files exist in the directory.

🔎 More specific pattern suggestion
-      const files = await Array.fromAsync(fs.promises.glob(`${cachePathPrefix}-*.json`));
+      const files = await Array.fromAsync(fs.promises.glob(`${cachePathPrefix}-[0-9a-f]*.json`));

This pattern only matches files with hexadecimal characters after the prefix, which aligns with Git SHA format and reduces false matches.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59d8913 and 52c387f.

📒 Files selected for processing (2)
  • apps/api/src/services/external/templates/template-fetcher.service.ts (1 hunks)
  • apps/api/src/services/external/templates/template-gallery.service.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/services/external/templates/template-fetcher.service.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.
Never use deprecated methods from libraries.
Don't add unnecessary comments to the code.

Files:

  • apps/api/src/services/external/templates/template-gallery.service.ts
🧠 Learnings (2)
📚 Learning: 2025-07-03T14:40:49.886Z
Learnt from: jzsfkzm
Repo: akash-network/console PR: 1498
File: apps/api/src/services/external/templates/template-fetcher.service.ts:0-0
Timestamp: 2025-07-03T14:40:49.886Z
Learning: In the TemplateFetcherService class in apps/api/src/services/external/templates/template-fetcher.service.ts, the error handling strategy should maintain process resilience by catching all errors and returning null rather than re-throwing critical errors, to avoid breaking the whole template fetching process.

Applied to files:

  • apps/api/src/services/external/templates/template-gallery.service.ts
📚 Learning: 2025-11-25T17:45:39.561Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-11-25T17:45:39.561Z
Learning: Applies to **/*.{ts,tsx,js} : Don't add unnecessary comments to the code.

Applied to files:

  • apps/api/src/services/external/templates/template-gallery.service.ts
🧬 Code graph analysis (1)
apps/api/src/services/external/templates/template-gallery.service.ts (1)
apps/api/src/services/external/templates/template-fetcher.service.ts (1)
  • REPOSITORIES (14-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (2)
apps/api/src/services/external/templates/template-gallery.service.ts (2)

108-108: LGTM: Consistent SHA usage.

The change to use latestCommitSha is correct and aligns with the new cache naming scheme and fallback mechanism.


87-92: No changes required. The code uses Array.fromAsync correctly and is fully compatible with the project's Node.js 24.11.1 requirement. The fallback logic for extracting the SHA from cached filenames works as intended with the controlled glob pattern.

@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 60.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.89%. Comparing base (0a62b55) to head (52c387f).
⚠️ Report is 8 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ces/external/templates/template-gallery.service.ts 62.50% 2 Missing and 1 partial ⚠️
...ces/external/templates/template-fetcher.service.ts 50.00% 1 Missing ⚠️

❌ Your patch status has failed because the patch coverage (60.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2397      +/-   ##
==========================================
- Coverage   51.18%   50.89%   -0.30%     
==========================================
  Files        1072     1062      -10     
  Lines       29245    28901     -344     
  Branches     6412     6404       -8     
==========================================
- Hits        14970    14709     -261     
+ Misses      13862    13778      -84     
- Partials      413      414       +1     
Flag Coverage Δ *Carryforward flag
api 80.20% <60.00%> (-0.03%) ⬇️
deploy-web 31.17% <ø> (ø) Carriedforward from 0a62b55
log-collector ?
notifications 87.94% <ø> (ø) Carriedforward from 0a62b55
provider-console 81.48% <ø> (ø) Carriedforward from 0a62b55
provider-proxy 84.35% <ø> (ø) Carriedforward from 0a62b55

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...ces/external/templates/template-fetcher.service.ts 91.33% <50.00%> (ø)
...ces/external/templates/template-gallery.service.ts 81.35% <62.50%> (-3.83%) ⬇️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@stalniy stalniy merged commit 62690c0 into main Dec 22, 2025
64 of 65 checks passed
@stalniy stalniy deleted the refactor/templates-fallback branch December 22, 2025 17:51
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

Comments