A repository with no .gitignore gets one for its stack: from the proposal, or from its files before the first code commit #107

Closed
opened 2026-09-29 01:27:40 -04:00 by cmoriarty · 1 comment
Owner

What happened

cmoriarty/scratch was created by New Project with no .gitignore. Its first work run (PR #8 there, the one that wrote the whole codebase) committed backend/app/__pycache__/*.pyc, backend/tests/__pycache__/*.pyc and backend/scratch_backend.egg-info/ in its very first feature commit. Every run after that committed changes to them, until they were removed by hand (scratch 874eba9).

#105's exclude block keeps environments and caches out of a run's own commits, but it lives only in the run's clone. It isn't in the repository, so it doesn't help a person or CI working in a checkout. It also doesn't cover packaging metadata such as *.egg-info/.

Expected

  • A run whose repository has no root .gitignore adds one. It is built from Forgejo's .gitignore templates (the github/gitignore set, served at /api/v1/gitignore/templates) for the languages and tools the repository uses. A repository that already has a root .gitignore is never touched.
  • With the proposal: the proposal names its stack in .osf/proposal.json as template names (Python, Node, …). The .gitignore is written from them and committed with the proposal, before any code exists.
  • Before the first code commit: if a run gets there with still no root .gitignore (a quick-fix or docs pipeline, or a stack the proposal didn't name), the .gitignore is built from the manifests in the worktree: pyproject.toml → Python, package.json → Node, go.mod → Go, Cargo.toml → Rust, and so on.
  • If the forge can't serve the templates, the step says so and the run carries on without one.

Adding nodes to the built-in pipelines runs into #102 for runs already in flight, so deploy when idle.

Related: #105, #100.

## What happened `cmoriarty/scratch` was created by New Project with no `.gitignore`. Its first work run (PR #8 there, the one that wrote the whole codebase) committed `backend/app/__pycache__/*.pyc`, `backend/tests/__pycache__/*.pyc` and `backend/scratch_backend.egg-info/` in its very first feature commit. Every run after that committed changes to them, until they were removed by hand (scratch `874eba9`). #105's exclude block keeps environments and caches out of a run's own commits, but it lives only in the run's clone. It isn't in the repository, so it doesn't help a person or CI working in a checkout. It also doesn't cover packaging metadata such as `*.egg-info/`. ## Expected - A run whose repository has **no root `.gitignore`** adds one. It is built from Forgejo's `.gitignore` templates (the github/gitignore set, served at `/api/v1/gitignore/templates`) for the languages and tools the repository uses. A repository that already has a root `.gitignore` is never touched. - **With the proposal:** the proposal names its stack in `.osf/proposal.json` as template names (`Python`, `Node`, …). The `.gitignore` is written from them and committed with the proposal, before any code exists. - **Before the first code commit:** if a run gets there with still no root `.gitignore` (a quick-fix or docs pipeline, or a stack the proposal didn't name), the `.gitignore` is built from the manifests in the worktree: `pyproject.toml` → Python, `package.json` → Node, `go.mod` → Go, `Cargo.toml` → Rust, and so on. - If the forge can't serve the templates, the step says so and the run carries on without one. Adding nodes to the built-in pipelines runs into #102 for runs already in flight, so deploy when idle. Related: #105, #100.
Author
Owner

Shipped in 1d596b7, as the OpenSpec change gitignore-from-stack. It's archived in 96cc8da and adds a requirement to the run-worktree spec.

  • With the proposal: the proposal step now names its stack in .osf/proposal.json as .gitignore template names, for example "stack": ["Python", "Node"]. A new step, script.gitignore.proposal, runs before git.commit.proposal. When the repository has no root .gitignore, it writes one from the forge's templates (the github/gitignore set Forgejo serves) for that stack and for any manifests already present. The proposal commit carries it, before any code exists.
  • Before the first code commit: script.gitignore.feature runs before git.commit.feature in every pipeline that has one, quick-fix and docs included. If there's still no root .gitignore, it builds one from the manifests in the worktree: pyproject.toml → Python, package.json → Node, go.mod → Go, Cargo.toml → Rust, and so on.
  • An existing .gitignore is never changed: both steps are skipped with the reason "the repository has its own .gitignore". If the forge can't serve the templates, the step says so and the run carries on. Each step records what it did in .osf/gitignore.json.
  • Fixed on the way: a ForgejoClient with no token sent an empty Authorization: token header, which httpx refuses, so no tokenless request could be made. It now sends no header.

Verified:

  • A New Project run on a local osfd: the proposal named ["Python"], and its commit carried the Python template. script.gitignore.feature was skipped, and the feature commit left out the .pytest_cache, greet.egg-info and __pycache__ the tests made.
  • On production, with cmoriarty/scratch now having its own .gitignore: a run showed both steps skipped as not needed, and its proposal named its stack as ["Python"].
Shipped in 1d596b7, as the OpenSpec change `gitignore-from-stack`. It's archived in 96cc8da and adds a requirement to the `run-worktree` spec. - **With the proposal:** the proposal step now names its stack in `.osf/proposal.json` as `.gitignore` template names, for example `"stack": ["Python", "Node"]`. A new step, `script.gitignore.proposal`, runs before `git.commit.proposal`. When the repository has no root `.gitignore`, it writes one from the forge's templates (the github/gitignore set Forgejo serves) for that stack and for any manifests already present. The proposal commit carries it, before any code exists. - **Before the first code commit:** `script.gitignore.feature` runs before `git.commit.feature` in every pipeline that has one, quick-fix and docs included. If there's still no root `.gitignore`, it builds one from the manifests in the worktree: `pyproject.toml` → Python, `package.json` → Node, `go.mod` → Go, `Cargo.toml` → Rust, and so on. - **An existing `.gitignore` is never changed:** both steps are skipped with the reason "the repository has its own .gitignore". If the forge can't serve the templates, the step says so and the run carries on. Each step records what it did in `.osf/gitignore.json`. - **Fixed on the way:** a `ForgejoClient` with no token sent an empty `Authorization: token ` header, which httpx refuses, so no tokenless request could be made. It now sends no header. **Verified:** - A New Project run on a local osfd: the proposal named `["Python"]`, and its commit carried the Python template. `script.gitignore.feature` was skipped, and the feature commit left out the `.pytest_cache`, `greet.egg-info` and `__pycache__` the tests made. - On production, with `cmoriarty/scratch` now having its own `.gitignore`: a run showed both steps skipped as not needed, and its proposal named its stack as `["Python"]`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cmoriarty/braid#107
No description provided.