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/detect

Says what a g1t.page build of the checkout would find, without building or deploying anything: the same rules g1t/deploy builds with.

steps:
  - uses: actions/checkout@v5
  - id: detect
    uses: g1t/detect@v1
  - if: steps.detect.outputs.kind == 'none'
    run: echo "::error::Nothing here would be served."
  - if: steps.detect.outputs.framework == 'hugo'
    run: echo "A Hugo site, served from ${{ steps.detect.outputs.output-dir }}"
  - if: steps.detect.outputs.install != ''
    run: ${{ steps.detect.outputs.install }}
yaml

Inputs

InputDefault
root-dirThe checkoutWhere the project is in the checkout.
build-commandNoneA build command of your own, as g1t/deploy takes it.
output-dirNoneAn output folder of your own, as g1t/deploy takes it.
build-asautoWhat it is built as: auto, static, workers or docker (Container), as g1t/deploy takes it.
dockerfileNoneA Dockerfile's path of your own, as g1t/deploy takes it.

Outputs

Output
kindworkers (a wrangler.jsonc, wrangler.json or wrangler.toml, a framework's Cloudflare adapter, or a Node.js server run as a Worker), static (a site a build writes), html (the files as they are), container (a server that is not run as a Worker, a framework set up to need a server, or a Dockerfile with nothing else to serve) or none (nothing to serve).
frameworkThe framework it is built with, from the table below; node for a Node.js server run as a Worker; for a container without one, dockerfile, or the server g1t writes a Dockerfile for (node, django, flask, fastapi, python, go, rails, rack, laravel, php). Empty when there is none.
dockerfileA container's own Dockerfile, relative to the project. Empty when g1t writes one, and for anything that is not a container.
portThe port a container listens on: its Dockerfile's EXPOSE, else 8080.
entryFor a Node.js server run as a Worker, the file that starts it, relative to the project. Empty for anything else.
installHow dependencies are installed, from the lockfile: npm ci, pnpm install --frozen-lockfile, and so on, and bundle install for Jekyll. Empty when there is nothing to install.
buildWhat builds it: your command, the package's build script (npm run build), the framework's own command, or wrangler deploy --dry-run for Workers. Empty when nothing does.
output-dirThe folder served, relative to the project (. for the project itself): yours, a Workers project's assets directory, the folder the framework writes, or the first of dist, build, out, public, _site, .output/public that exists. Empty when a build has yet to write it, as for a Cloudflare adapter's Worker.

When kind is none, the step's log says what to change or add, such as a Dockerfile. Docker deployments has the order in which a project's kind is decided.

Frameworks

frameworkFound byServes
vite, vite-react, vite-vue, vite-svelte, vite-solid, vite-preactvite, and the framework's Vite plugindist, or build.outDir
astroastrodist, or outDir; a Worker with @astrojs/cloudflare
nextjsnextout with output: 'export'; a Worker with @opennextjs/cloudflare and a wrangler.jsonc
sveltekit@sveltejs/kitbuild with adapter-static; a Worker with adapter-cloudflare
nuxtnuxt.output/public, from nuxi generate
react-router, remix@react-router/dev, @remix-run/devbuild/client with ssr: false
gatsbygatsbypublic
docusaurus@docusaurus/corebuild
eleventy@11ty/eleventy_site
angular@angular/core and angular.jsondist/<project>/browser
create-react-appreact-scriptsbuild
hugohugo.toml, or Hugo's config.tomlpublic
jekyll_config.yml and a Gemfile_site
mdbookbook.tomlbook

What deploys has each one's build command. Your build-command and output-dir win over what is detected.

It runs g1t-runner detect, found or downloaded as g1t/deploy finds it.