Config Repository
The config repo contains the shared definitions and file rules used across repositories.
Layout
config-repo/
├── config/
│ ├── docs.yaml
│ ├── docs/
│ │ ├── domain.yaml
│ │ └── enabled.yaml
│ └── license.yaml
├── outputs/
│ └── LICENSE.gitrepoforge
└── templates/
└── licenses/
├── apache-2.0.tmpl
└── mit.tmpl
config/
Each file defines one allowed config key. The filename, without .yaml, is the key name.
config/license.yaml
type: string
required: true
default: mit
enum:
- mit
- apache-2.0
description: License template to apply.
config/enable_license.yaml
type: boolean
default: true
description: Whether a LICENSE file should be managed.
Object definitions can also declare nested attributes from a same-named folder.
config/docs.yaml
type: object
required: true
description: Documentation settings.
config/docs/enabled.yaml
type: boolean
default: true
description: Whether documentation hosting is enabled.
config/docs/domain.yaml
type: string
required: true
description: Canonical docs hostname.
Config Definition Fields
| Field | Required | Description |
|---|---|---|
type | yes | Supported values are string, boolean, number, list, and object. |
required | no | If true, the repo must provide the key unless a default is defined. |
default | no | Typed default value used when the repo omits the key. |
enum | no | Allowed values for string definitions. |
pattern | no | Regex with named capture groups for string definitions. Used for input validation and to extract named parts accessible in templates via the capture function. |
description | no | Human-readable description. |
Type Notes
stringaccepts YAML strings and may also useenumand/orpattern.booleanacceptstrueorfalse.numberaccepts numeric YAML values.listaccepts YAML sequences.objectaccepts YAML mappings. Nested attributes are loaded fromconfig/<key>/using the same per-file format as top-level definitions.
Pattern Matching
The pattern field accepts a regular expression with named capture groups using the (?P<name>...) syntax. It is only supported for string definitions.
When a pattern is defined:
- The value provided by the repo (or the default) must match the pattern. A validation error is produced if it does not.
- Named capture groups are extracted from the matched value and made available to templates through the
capturefunction.
The pattern must contain at least one named capture group. If a default is also set, the default value must match the pattern.
config/goversion.yaml
type: string
required: false
default: "1.26.1"
pattern: "^(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)$"
description: "The version of Go."
With the value 1.26.1, the named groups resolve to:
| Group | Value |
|---|---|
major | 1 |
minor | 26 |
patch | 1 |
Templates can then use capture to access individual groups. See Template Files for usage details.
pattern and enum may be combined. The value must satisfy both constraints.
Reserved Config Keys
These names are reserved because they already exist as top-level fields in .gitrepoforge:
namedefault_branchmanifestconfig
They cannot be declared under config/ and must not appear inside the repo’s config: map.
outputs/
Each output rule maps to one target file. The relative path under outputs/, without the .gitrepoforge suffix, becomes the managed file path in the repo. Every file under outputs/ must end with .gitrepoforge; unexpected filenames are treated as config errors so typos are not silently ignored. gitrepoforge writes a generated managed-files manifest for each managed repo. By default the file is .managedfiles. A workspace or repo manifest setting can choose a different relative path, with the repo value taking precedence.
outputs/LICENSE.gitrepoforge
templates:
- condition: license == "mit"
template: licenses/mit.tmpl
- condition: license == "apache-2.0"
template: licenses/apache-2.0.tmpl
outputs/justfile.gitrepoforge
templates:
- condition: justfile
template: justfile.tmpl
evaluate: true
- absent: true
Output Fields
| Field | Required | Description |
|---|---|---|
mode | no | create or delete. Defaults to create. |
templates | yes for create | Ordered list of candidates. The first matching candidate is selected and evaluation stops. |
Candidate Fields
| Field | Required | Description |
|---|---|---|
condition | no | Boolean selector for the candidate. Empty means always matches, which is useful for a fallthrough entry. Supported forms are documented in Condition Syntax. |
template | yes unless absent is true | Path to a file under templates/. |
evaluate | no | If true, render the template file with template data. If false or omitted, copy the file verbatim. |
template_mode | no | Controls how template delimiters are recognized when evaluate is true. DOUBLE_BRACKET is the default. DOUBLE_BRACKET_STRICT only recognizes {{ when it is at the start of the file or preceded by whitespace. |
absent | no | If true, the selected result is that the target file must not exist. |
Selection Rules
- Candidates are checked in order.
- The first candidate whose
conditionmatches is selected. - A candidate with no
conditionis unconditional and usually belongs at the end as a fallback. absent: trueis the fallback form for “the file should not exist”.
Common Patterns
Use a single unconditional candidate when the same template should always apply:
templates:
- template: .github/workflows/add-to-project.yml
Use verbatim copy for static assets such as license files:
templates:
- condition: license == "MIT"
template: LICENSE/MIT
- condition: license == "Apache-2.0"
template: LICENSE/Apache-2.0
Use evaluation for generated files:
templates:
- condition: justfile
template: justfile.tmpl
evaluate: true
- absent: true
Use strict delimiter matching when the file also contains other {{ ... }}-style syntax, such as GitHub Actions expressions:
templates:
- template: .github/workflows/ci.yml.tmpl
evaluate: true
template_mode: DOUBLE_BRACKET_STRICT
With DOUBLE_BRACKET_STRICT, ${{ runner.os }} remains literal because the {{ is preceded by $, while template directives like {{- if eq .Config.codecov true }} still evaluate when they begin on their own lines.
The evaluated template can branch on other config values internally:
# Commands for {{.Name}}
default:
@just --list
{{- if eq .Config.language "go" }}
# Build {{.Name}} with Go
build:
go build ./...
{{- end }}
{{- if eq .Config.language "java" }}
# Build {{.Name}} with Maven
build:
mvn package
{{- end }}
Nested config values can also be referenced in conditions with dotted keys:
templates:
- condition: docs.enabled
template: docs/CNAME.tmpl
evaluate: true
- absent: true
When a value must be emitted as a quoted scalar, use quote_double or quote_single instead of adding literal quotes around the interpolation:
description: {{ .Config.description | quote_double }}
summary: {{ .Config.summary | quote_single }}
go-version: {{ .Config.versions.go | quote_double }}
Use exists when selection should depend on whether a repo explicitly set a value rather than whether a default resolved value is available:
templates:
- condition: exists docs.domain
template: docs/CNAME.tmpl
evaluate: true
- absent: true
exists docs.domain only matches when config.docs.domain is present in the repo’s .gitrepoforge file. It does not match a value that only came from a schema default.
Conditions can also be combined with && and ||:
templates:
- condition: docs.enabled && exists docs.domain
template: docs/CNAME.tmpl
evaluate: true
- absent: true
Use parentheses when grouping is needed, for example (docs.enabled || preview_docs) && exists docs.domain.
mode: delete is still available when a file is always forbidden and does not need conditional selection.
Section-Based Patterns
Templates can use section directives to manage only part of a file instead of replacing the entire file. The directives are placed in the template file itself. See Template Files for the full directive reference.
Manage a README header while letting users write the body:
outputs/README.md.gitrepoforge
templates:
- condition: readme
template: README.md.tmpl
templates/README.md.tmpl
{{ section start=start_of_file end=contains("<!-- END MANAGED -->") }}
# My Project
<!-- END MANAGED -->
{{ end }}
Ensure a file exists without managing its content. Useful for files like go.sum that should be present but are maintained by other tools:
outputs/go.sum.gitrepoforge
templates:
- condition: manage_gosum
template: go.sum.tmpl
templates/go.sum.tmpl
{{ bootstrap }}
{{ end }}
Manage both a header and footer while preserving user content in between:
templates/README.md.tmpl
{{ section start=start_of_file end=content("<!-- END HEADER -->") }}
# Managed Header
<!-- END HEADER -->
{{ end }}
{{ section start=contains("<!-- START FOOTER -->") end=end_of_file }}
<!-- START FOOTER -->
Managed Footer Content
{{ end }}
Provide default body content on first creation, then manage only the header afterwards:
templates/README.md.tmpl
{{ section start=start_of_file end=contains("<!-- END MANAGED -->") }}
# Managed Header
<!-- END MANAGED -->
{{ end }}
{{ bootstrap }}
Default body content goes here.
{{ end }}
templates/
The templates/ folder stores the file content referenced by output rules. See Template Files for rendering details.