Skip to main content

semaphore-config-schema

Generate or update the JSON Schema (in YAML form) that describes the Semaphore configuration file. The schema lives at `config.schema.yaml` in the repository root and is derived from the Go struct `util.ConfigType` (with nested types in `util/config.go`, `util/config_auth.go`, `util/App.go`, `util/OdbcProvider.go`). Use this skill whenever the user asks to "regenerate the config schema", "update config.schema.yaml", "sync the schema with ConfigType", "add a new field to the schema", "the schema is out of date", or after a Go struct change that affects configuration. Also trigger when the user adds a new top-level field, new nested struct, or changes a `json:`, `default:`, `rule:`, or `env:` tag on a config struct field.

소스 정보

저장소
semaphoreui/semaphore
최근 소스 활동
2026년 5월 27일 07:55
감지된 SKILL.md 언어
영어
스타
14,268
포크
1,332

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
semaphore-config-schema
description
Generate or update the JSON Schema (in YAML form) that describes the Semaphore configuration file. The schema lives at `config.schema.yaml` in the repository root and is derived from the Go struct `util.ConfigType` (with nested types in `util/config.go`, `util/config_auth.go`, `util/App.go`, `util/OdbcProvider.go`). Use this skill whenever the user asks to "regenerate the config schema", "update config.schema.yaml", "sync the schema with ConfigType", "add a new field to the schema", "the schema is out of date", or after a Go struct change that affects configuration. Also trigger when the user adds a new top-level field, new nested struct, or changes a `json:`, `default:`, `rule:`, or `env:` tag on a config struct field.
# Semaphore — Config Schema Generator This skill produces and maintains `config.schema.yaml` — a **JSON Schema draft 2020-12** document (written in YAML) describing every valid key in a Semaphore `config.json` / `config.yaml`. The schema is the source of truth for IDE autocomplete (via `yaml-language-server`) and for any future server-side config validation. The schema is **derived from Go code**, never hand-edited speculatively. If `ConfigType` doesn't have a field, the schema must not have it. If a field has a `default:` or `rule:` tag, the schema must reflect it. ## Inputs (source of truth) All Go types live in the `util` package. Read these files **in full** before writing anything: | File | What's in it | |------|--------------| | `util/config.go` | `ConfigType` (root) + most nested types: `DbConfig`, `TLSConfig`, `RunnerConfig`, `TotpConfig`, `EventLogType`, `TaskLogType`, `ConfigLog`, `SyslogConfig`, `ConfigProcess`, `ConfigAppNamespaces`, `ScheduleConfig`, `DebuggingConfig`, `HAConfig`, `HARedisConfig`, `TeamsConfig`, `ConfigDirs`, `SubscriptionConfig`, `LdapMappings`, `oidcEndpoint` | | `util/config_auth.go` | `MultifactorAuthConfig`, `EmailAuthConfig`, `TotpConfig`, `RecaptchaConfig` | | `util/App.go` | `App` | | `util/OdbcProvider.go` | `OidcProvider` (note: filename is `Odbc...`, typo'd — don't "fix" it) | If a struct moves or a new file appears, find it with: ```bash grep -rn "^type .* struct" util/ | grep -v _test ``` ## Output A single file: **`config.schema.yaml`** in the repository root. Header (top of file) — keep exactly this shape: ```yaml $schema: "https://json-schema.org/draft/2020-12/schema" $id: "https://semaphoreui.com/schemas/config.schema.json" title: Semaphore configuration description: Schema for Semaphore UI config.json / config.yaml. Generated from util.ConfigType. type: object ``` ## Field-mapping rules These are non-negotiable — apply them mechanically. ### Property name Use the **`json:` tag's first segment** (before any comma): ```go Port string `json:"port,omitempty" ...` // → property name "port" ``` Skip fields where the `json:` tag is `"-"` (e.g. `RunnerConfig.RegistrationToken`, `SubscriptionConfig.Key` — those are env-only, not file-configurable). Document this in the schema's `description` for the containing struct if a sensitive token is reachable only via env. Skip unexported fields (lowercase first letter) — but include their **referenced types** if they appear via exported fields. Example: `oidcEndpoint` is unexported but referenced from `OidcProvider.Endpoint`, so define it under `$defs` as `OidcEndpoint`. ### JSON Schema `type` | Go type | Schema type | |---------|-------------| | `string` | `string` | | `bool` | `boolean` | | `int`, `int64`, `uint32` | `integer` | | `float64` | `number` | | `[]T` | `array`, with `items: { ... }` | | `map[string]T` | `object`, with `additionalProperties: { ... }` | | `*T` (pointer) | same as `T`. If `*int`, allow `null`: `type: [integer, "null"]` | | `*Struct` | `{ $ref: "#/$defs/Struct" }` | ### Constraints from tags - **`default:"X"`** → emit `default: X` (cast to the right type — `default:"10"` on `int` becomes `default: 10`). - **`rule:"^...$"`** → emit `pattern: "^...$"`. **Don't** also add a `format:` — the regex is more authoritative. - **`env:"NAME,sensitive"`** → append `(sensitive).` to the `description`. The schema doesn't have a native "sensitive" flag, so this is documentation-only. - **`env:"NAME"`** without `sensitive` → no change to schema. - URL-looking string fields (web hooks, redirect URLs) → add `format: uri` **unless** a `pattern` is already set from a `rule:` tag. ### `enum` discovery `enum` is not always in tags — sometimes it's in code: 1. Check for a `rule:"^a|b|c$"` regex — convert that to `enum: [a, b, c]` instead of a pattern (more useful in IDE). 2. Check for typed constants near the struct (e.g. `SyslogFormat` has `SyslogDefault = ""` and `SyslogRFC5424 = "rfc5424"` → `enum: ["", rfc5424]`). 3. `TeamInviteType` constants (`TeamInviteEmail`, `TeamInviteUsername`, `TeamInviteBoth`) → enum. ### Reusable types → `$defs` Every named struct that appears as a field type gets a `$defs/<TypeName>` entry. Reference via `$ref: "#/$defs/<TypeName>"`. Top-level `properties` should reference, not inline. Exception: `lumberjack.Logger` is external — define a minimal stub `$defs/LumberjackLogger` with the common fields (`filename`, `maxsize`, `maxage`, `maxbackups`, `localtime`, `compress`). Do not try to fully model it. ### `additionalProperties` Leave it **unset** (defaults to `true`). The schema is informational, not strict — making it strict would break existing configs that have stray keys (legacy fields, typos used as comments, etc.). ## Workflow ### 1. Read all four Go files Use `Read` on each file. Do not skim. Note every exported field on every struct. ### 2. Build a mental map: struct → properties For `ConfigType` (the root), list every exported field with its `json:` name, Go type, and any `default:` / `rule:` / `env:` tags. Then for each referenced struct, do the same. Stop when no new structs appear. ### 3. Diff against existing `config.schema.yaml` Before writing, compare what Go has vs. what the schema has: ```bash # List all json tag names in Go grep -hoE 'json:"[^,"]+' util/*.go | sed 's/json:"//' | sort -u > /tmp/go-keys.txt # List all property keys in the schema yq '.properties | keys | .[]' config.schema.yaml | sort -u > /tmp/schema-keys.txt diff /tmp/go-keys.txt /tmp/schema-keys.txt ``` Identify three categories: - **In Go, missing from schema** → add. - **In schema, missing from Go** → remove (the Go struct is the source of truth). - **In both** → check that type / default / pattern still match. Show the user the diff before applying changes if it's larger than ~5 lines. ### 4. Write `config.schema.yaml` Use the **YAML formatting conventions** already established in the file: - Section comments split top-level `properties` into visual groups: `# === Database ===`, `# === HTTP server ===`, `# === Authentication ===`, etc. - Inline-object syntax `{ type: string, default: foo }` for one-property entries. - Multi-line block form when there are 3+ properties or a `description`. - Quote string values that contain `:`, `/`, `+`, `=`, or start with a digit. Don't quote bare identifiers like `string`, `boolean`, `integer`. - `$ref` values are quoted: `{ $ref: "#/$defs/DbConfig" }`. ### 5. Validate After writing, run two checks: ```bash python3 - <<'PY' import yaml, jsonschema with open("config.schema.yaml") as f: schema = yaml.safe_load(f) jsonschema.Draft202012Validator.check_schema(schema) # meta-validation print("schema is valid") # Confirm an existing config.yaml still passes try: with open("config.yaml") as f: cfg = yaml.safe_load(f) errors = list(jsonschema.Draft202012Validator(schema).iter_errors(cfg)) print(f"config.yaml: {len(errors)} error(s)") for e in errors: print(f" {list(e.path)}: {e.message}") except FileNotFoundError: pass PY ``` Both must pass. If `config.yaml` doesn't exist locally, skip the second check — the meta-validation alone is sufficient. ### 6. Don't touch `config.yaml` The user's `config.yaml` may contain stray legacy keys or comments. Do not "clean it up" as part of this skill — the schema generation is a one-way operation Go → schema. If the user wants a cleaner config, that's a separate task. ## Common pitfalls - **`json:"-"` fields**: easy to accidentally include. Always check the tag, not the field name. - **Pointer vs. value**: `*int` is nullable, plain `int` is not. Reflect that in `type`. - **Typo'd filenames**: `util/OdbcProvider.go` (should be `Oidc`) — preserve it; renaming files is out of scope. - **Tag inheritance illusion**: nested structs don't inherit tags from their parent field. The `default:` on `ConfigType.MaxParallelTasks` is on the field, not on `int`. - **`sensitive` is not standard**: JSON Schema has no built-in sensitive marker. Encode it in `description` text only. - **Existing keys with apostrophes** in `config.yaml` (e.g. `"'email_sender"`) are user-side comment hacks — ignore them, they're not in any Go struct. ## When to refuse If the user asks to add a property to the schema that **doesn't exist in `ConfigType`** ("just add a `foo` field so my config validates"), refuse and explain: the schema is generated from Go. The fix is to add the field to the Go struct first, then regenerate. If `ConfigType` has been heavily refactored and a previous schema entry no longer maps, propose the deletion explicitly before doing it — never silently drop entries that the user might have been relying on.
GitHub에서 보기