Skip to content

CI Cached Indexes (Team Tier)

import { Aside, Tabs, TabItem } from ‘@astrojs/starlight/components’;

On large repos, a fresh forge index in CI can take 60–120 seconds. Team tier includes a remote cache that stores the index after the first build and restores it on subsequent runs. When only a small number of files change between commits, the restore + incremental update is typically under 5 seconds.

The cache key is a hash of three inputs:

  • The git commit SHA of the repo being indexed
  • The Forge binary version
  • The hash of .forge/config.toml and .forge/team.yml

When these three inputs match a stored cache, Forge skips re-indexing entirely and restores the stored index directly. When files change between commits, Forge restores the closest ancestor cache and runs an incremental update — only changed files are re-parsed.

Cache integrity is verified automatically. A corrupted or tampered cache is rejected and Forge falls back to a full index.

  • Team tier license (forge license --show should display tier: team)
  • Team ID (found in the Forge admin portal at admin.forge.ironpinelabs.com)
  • FORGE_LICENSE_KEY stored as a CI secret (GitHub Actions) or CI/CD variable (GitLab)

The simplest way to use remote cache is to pass --team to forge ci. Forge handles upload and restoration automatically:

Terminal window
forge ci --team <team_id> --format github --fail-on-p0

No separate forge index step needed. Forge checks the remote cache, restores if available, runs an incremental update if needed, runs health checks, and uploads the updated index back to the cache.

name: Forge Health Check
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
forge-health:
runs-on: ubuntu-latest
permissions:
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Forge
run: |
curl -fsSL https://downloads.forge.ironpinelabs.com/releases/latest/forge-linux-x86_64 \
-o forge && chmod +x forge && sudo mv forge /usr/local/bin/forge
- name: Run Forge CI with remote cache
run: forge ci --team ${{ vars.FORGE_TEAM_ID }} --format github --fail-on-p0
env:
FORGE_LICENSE_KEY: ${{ secrets.FORGE_LICENSE_KEY }}

Store FORGE_TEAM_ID as a repository variable (not a secret — it’s not sensitive) and FORGE_LICENSE_KEY as a secret.

forge-health:
stage: quality
image: ubuntu:22.04
before_script:
- apt-get update -qq && apt-get install -y -qq curl
- curl -fsSL https://downloads.forge.ironpinelabs.com/releases/latest/forge-linux-x86_64
-o /usr/local/bin/forge
- chmod +x /usr/local/bin/forge
script:
- forge ci --team $FORGE_TEAM_ID --format gitlab --fail-on-p0 | tee forge-report.json
- exit ${PIPESTATUS[0]}
artifacts:
reports:
codequality: forge-report.json
when: always

Set FORGE_TEAM_ID as a CI/CD variable (unmasked — not sensitive) and FORGE_LICENSE_KEY as a masked variable.

For more control, use --cache-to and --use-cache explicitly:

Upload after indexing:

Terminal window
forge index . --with-search --with-git \
--cache-to r2://<team_id>/<repo-hash>

Restore before indexing:

Terminal window
forge index . --with-search --with-git \
--use-cache r2://<team_id>/<repo-hash>

<repo-hash> is a stable identifier for the repository — typically $(git remote get-url origin | sha256sum | cut -c1-16) to make it consistent across runners.

Full workflow using explicit cache control:

- name: Restore Forge index from remote cache
run: |
REPO_HASH=$(git remote get-url origin | sha256sum | cut -c1-16)
forge index . --with-search --with-git \
--use-cache r2://${{ vars.FORGE_TEAM_ID }}/${REPO_HASH} || true
# || true: if no cache exists (first run), fall through to full index
- name: Full index if cache miss
run: forge index . --with-search --with-git
- name: Upload index to remote cache
run: |
REPO_HASH=$(git remote get-url origin | sha256sum | cut -c1-16)
forge index . --cache-to r2://${{ vars.FORGE_TEAM_ID }}/${REPO_HASH}
- name: Run health check
run: forge ci --repo . --format github --fail-on-p0

When --team is used, Forge logs cache performance to stdout:

forge ci: checking remote cache...
forge ci: cache HIT (key: abc123def456, age: 4h32m)
forge ci: restoring index (187 MB, 2.3s)
forge ci: incremental update (14 files changed, 0.8s)
forge ci: running health checks...

A cold miss (no cache available):

forge ci: checking remote cache...
forge ci: cache MISS — full index required
forge ci: indexing 8,432 files (47.2s)
forge ci: uploading to remote cache (187 MB, 3.1s)
forge ci: running health checks...

Remote cache entries expire after 30 days of no access. Active repos (daily CI runs) never expire in practice. Manually purge a cache entry from the admin portal if you need to force a full re-index (e.g., after significant refactoring that breaks incremental updates).

Cache never hits The cache key includes the Forge binary version. If your CI downloads latest each run and Forge releases a patch, the version changes and every run is a cache miss. Pin to a specific version:

Terminal window
curl -fsSL https://downloads.forge.ironpinelabs.com/releases/v1.4.0/forge-linux-x86_64 \
-o forge

Cache hit but index seems stale Verify that fetch-depth: 0 is set in the checkout step. With shallow clones (fetch-depth: 1), forge index --with-git can’t read full git history, which affects the git activity data in forge_prepare results. It doesn’t affect the cache key — that’s based on commit SHA, not history depth.

“Team not found” error The team ID in FORGE_TEAM_ID doesn’t match your account. Log in to admin.forge.ironpinelabs.com and copy the team ID exactly as shown.