Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

syntaqxBlame is kept by commit and path, reads ahead and stops in time, and crawlers are asked to leave it alone; a push works out its repository's About, and a run's reads are kept for the next. Workers Logs at full sampling (2026-10-14, ~21:30-22:15 UTC) had blame as 56 of 101 repos answers over a second (median 2.4 s, up to 20.3 s), and the site's own logs showed who asked: 1,154 blame pages in four hours, about 1,015 of them ClaudeBot, 93 Googlebot, the rest Amazonbot, GPTBot and Reflectionbot, and a handful from people, each a file at a commit (`/flagon-io/g1t/blob/<sha>/<path>?blame=1`, `/pulls/<id>/blob/...`). Nothing was kept, so every one walked up to 400 commits again, reading the trees to the file one commit at a time, each read a round trip. Now repos keeps a blame in the colo's cache by repository, commit and path (blame.rs `cache_url`, for good; one cut short for ten minutes), finding the commit first when asked by branch; the walk reads the trees of the next 32 commits level by level at once, then at once the texts it will compare, reads each tree and text once, stops as soon as every line has its commit, and stops after 6 s (BLAME_WALK_MS) or 2,500 reads, giving what is left to the oldest commit it reached them in, marked partial as a history past 400 commits already was. robots.txt disallows `/*?blame=` and `/*&blame=` (app/lib/robots.ts), the Blame links are `nofollow`, and a blame page says `noindex`; the file itself stays open to crawlers. The About's 22.7 s and 22.0 s "about" invocations were its background run hitting its 22 s budget in the answer's waitUntil (the answer itself was quick): its history was read 200 commits a page, and each page after the first starts wherever the last ended, which moves with every push, so g1t's 728 first-parent commits were four reads with three of them read whole from the store again each time; pages are now 1,000, the length lib.rs already reads (MAX_ANCESTRY), so most repositories are one spliced read. And refresh_later never waited for the cache writes its run started (store.rs keeps trees, sizes and logs through Deferred, which only push handling settled), so a run could leave the next to read the same again; it now settles them. A push that moves the default branch works the About out after recording the push, within what is left of 24 s, so a view after a push finds it current; a run that is slow or partial logs `about_run` with `took_ms`. last_commits no longer waits on its two writes (the memo, which also goes to KV, and the finished answer) before answering. Blob had no waste worth taking out: its slow answers are the store's first read of a file, which is then kept. Repos and work log a quarter of requests (head_sampling_rate 0.25), down from every one for the stall hunt (no ~60 s stall recurred); slow RPC answers still log `answered_ms`. The deploy guide's Logs section, why-blame and managing repositories say so. Checked: cargo test -p g1t-repos (356 passing, 11 new), cargo test -p g1t-api openapi (9), npm test in apps/web (944 passing, 2 new), npx tsc -b --force, npm run test:deploy (92), node scripts/deploy.mjs manifest --check, and the docs build.

README.md

g1t/deploy

Builds the checkout a g1t Actions job is in and puts it on g1t.page: production when the run is on the project's production branch, the pull request's preview for a pull request's run, and the branch's preview for a run on any other branch.

name: Deploy

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  deployments: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - id: deploy
        uses: g1t/deploy@v1
      - run: echo "Live at ${{ steps.deploy.outputs.url }}"
yaml

The project must have Deployments on, and the workspace's plan must include them. The guide, Deploying from a workflow, says what is built and when.

Inputs

InputDefault
projectThe repository's one project with Deployments onThe project's slug. Needed when the repository has several.
root-dirThe project's settingWhere the project is in the checkout.
build-commandThe project's setting, else what the build findsWhat builds it.
output-dirThe project's setting, else the folder the framework writes, else the first of dist, build, out, public, _site, .output/publicThe folder a static build writes.
build-asThe project's setting, else autoWhat it is built as: auto, static, workers or docker. A container is built with the docker on the job's PATH.
dockerfileThe project's setting, else Dockerfile or ContainerfileThe Dockerfile a container is built from, relative to the project.
shaThe run's commitThe commit this is a build of, for its g1t / deploy check.
token${{ github.token }}The job's own token. It needs deployments: write; any other token is refused.

Outputs

Output
urlWhere the app is served.
deployment-idThe build's id, dpl_….
kindproduction or preview.

How it works

  1. It calls POST /repos/{owner}/{repo}/deployments/builds with the job's token. g1t checks the plan and the spend limit, decides production or preview from the run, sets the commit's g1t / deploy check to pending, and answers with a token for this build alone and the project's variables and secrets for builds of that kind.
  2. It masks that token and every secret with ::add-mask::.
  3. It runs g1t-runner deploy in the checkout, which installs, builds and uploads the app exactly as g1t's own builds do, and reports to g1t with the build's token. The build's log is in the step's.

A pull request from a fork or from someone without the Write role, and a run that had to be approved, build without the project's secrets. The build's time is the job's Actions time; it is not charged again.

g1t-runner is on g1t's machines. Elsewhere (a self-hosted runner), the action uses the one on PATH, or downloads the newest release from https://g1t.sh/downloads/runner/latest/ and checks it against the release's SHA256SUMS. It runs on Linux and macOS, with curl and either jq or python3.