mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
ci: require docs lint when docs change (#25608)
Move docs linting into the required CI umbrella and reuse the existing `changes` job so docs lint runs when docs or CI files change, plus on `main` as a backstop. This is motivated by the docs lint failures on #25601. That PR touched `.claude/docs/TESTING.md`; the standalone `Docs CI` workflow picked it up because `docs-ci.yaml` used broad `**.md` matching, but local `pnpm lint-docs` and `make lint` did not catch the same file because they only scanned `docs/**` plus root `*.md`. The first failed Docs CI run reported markdownlint errors in `.claude/docs/TESTING.md` (`MD040` and `MD031`), and the next run reported a markdown table formatter failure in the same file. That mismatch is why this PR exists: prevent unrelated PRs from being surprised by stale `.claude/docs/**` lint drift only after they happen to touch one of those files. The local docs scripts now include `.claude/docs/**`, and the old standalone `Docs CI` workflow is removed so we do not maintain separate path-filter logic outside the required CI workflow. > Generated by mux, but reviewed by a human
This commit is contained in:
@@ -8,14 +8,14 @@ not add new readiness or debug endpoints for these workflows.
|
|||||||
|
|
||||||
`scripts/develop/main.go` defines these base defaults:
|
`scripts/develop/main.go` defines these base defaults:
|
||||||
|
|
||||||
| Resource | Base default | Override |
|
| Resource | Base default | Override |
|
||||||
|----------|--------------|----------|
|
|--------------------------|--------------|--------------------------------------------------|
|
||||||
| API server | `3000` | `--port`, `CODER_DEV_PORT` |
|
| API server | `3000` | `--port`, `CODER_DEV_PORT` |
|
||||||
| Frontend dev server | `8080` | `--web-port`, `CODER_DEV_WEB_PORT` |
|
| Frontend dev server | `8080` | `--web-port`, `CODER_DEV_WEB_PORT` |
|
||||||
| Workspace proxy | `3010` | `--proxy-port`, `CODER_DEV_PROXY_PORT` |
|
| Workspace proxy | `3010` | `--proxy-port`, `CODER_DEV_PROXY_PORT` |
|
||||||
| Coder Prometheus metrics | `2114` | `--prometheus-port`, `CODER_DEV_PROMETHEUS_PORT` |
|
| Coder Prometheus metrics | `2114` | `--prometheus-port`, `CODER_DEV_PROMETHEUS_PORT` |
|
||||||
| Embedded Prometheus UI | `9090` | Fixed in `scripts/develop/main.go` |
|
| Embedded Prometheus UI | `9090` | Fixed in `scripts/develop/main.go` |
|
||||||
| Delve debugger | `12345` | Fixed when `--debug` is used |
|
| Delve debugger | `12345` | Fixed when `--debug` is used |
|
||||||
|
|
||||||
By default, plain `./scripts/develop.sh` uses the base defaults exactly:
|
By default, plain `./scripts/develop.sh` uses the base defaults exactly:
|
||||||
`3000`, `8080`, `3010`, and `2114` for Coder Prometheus metrics. Set
|
`3000`, `8080`, `3010`, and `2114` for Coder Prometheus metrics. Set
|
||||||
@@ -37,15 +37,15 @@ Linux. The Prometheus UI port `9090` and Delve port `12345` remain hardcoded.
|
|||||||
The develop script also supports these existing flags and environment
|
The develop script also supports these existing flags and environment
|
||||||
variables:
|
variables:
|
||||||
|
|
||||||
| Purpose | Flag | Environment variable |
|
| Purpose | Flag | Environment variable |
|
||||||
|---------|------|----------------------|
|
|-----------------------------------|----------------------|------------------------------|
|
||||||
| Per-worktree port offset | `--port-offset` | `CODER_DEV_PORT_OFFSET` |
|
| Per-worktree port offset | `--port-offset` | `CODER_DEV_PORT_OFFSET` |
|
||||||
| Access URL | `--access-url` | `CODER_DEV_ACCESS_URL` |
|
| Access URL | `--access-url` | `CODER_DEV_ACCESS_URL` |
|
||||||
| Admin password | `--password` | `CODER_DEV_ADMIN_PASSWORD` |
|
| Admin password | `--password` | `CODER_DEV_ADMIN_PASSWORD` |
|
||||||
| Starter template | `--starter-template` | `CODER_DEV_STARTER_TEMPLATE` |
|
| Starter template | `--starter-template` | `CODER_DEV_STARTER_TEMPLATE` |
|
||||||
| Roll back missing migrations | `--db-rollback` | `CODER_DEV_DB_ROLLBACK` |
|
| Roll back missing migrations | `--db-rollback` | `CODER_DEV_DB_ROLLBACK` |
|
||||||
| Reset the development database | `--db-reset` | `CODER_DEV_DB_RESET` |
|
| Reset the development database | `--db-reset` | `CODER_DEV_DB_RESET` |
|
||||||
| Accept changed migration tracking | `--db-continue` | `CODER_DEV_DB_CONTINUE` |
|
| Accept changed migration tracking | `--db-continue` | `CODER_DEV_DB_CONTINUE` |
|
||||||
|
|
||||||
Extra `coder server` flags can be passed after `--`. For example,
|
Extra `coder server` flags can be passed after `--`. For example,
|
||||||
`./scripts/develop.sh -- --trace` passes `--trace` to the API server.
|
`./scripts/develop.sh -- --trace` passes `--trace` to the API server.
|
||||||
|
|||||||
+57
-57
@@ -92,69 +92,69 @@ The left column reflects common patterns from pre-1.22 Go. Write the
|
|||||||
right column instead. The "Since" column tells you the minimum `go`
|
right column instead. The "Since" column tells you the minimum `go`
|
||||||
directive version required in `go.mod`.
|
directive version required in `go.mod`.
|
||||||
|
|
||||||
| Old pattern | Modern replacement | Since |
|
| Old pattern | Modern replacement | Since |
|
||||||
|---|---|---|
|
|---------------------------------------------------------------------|-------------------------------------------------------------------------|-----------|
|
||||||
| `interface{}` | `any` | 1.18 |
|
| `interface{}` | `any` | 1.18 |
|
||||||
| `v := v` inside loops | remove it | 1.22 |
|
| `v := v` inside loops | remove it | 1.22 |
|
||||||
| `for i := 0; i < n; i++` | `for i := range n` | 1.22 |
|
| `for i := 0; i < n; i++` | `for i := range n` | 1.22 |
|
||||||
| `for i := 0; i < b.N; i++` (benchmarks) | `for b.Loop()` (correct timing, future-proof) | 1.24 |
|
| `for i := 0; i < b.N; i++` (benchmarks) | `for b.Loop()` (correct timing, future-proof) | 1.24 |
|
||||||
| `sort.Slice(s, func(i,j int) bool{…})` | `slices.SortFunc(s, cmpFn)` | 1.21 |
|
| `sort.Slice(s, func(i,j int) bool{…})` | `slices.SortFunc(s, cmpFn)` | 1.21 |
|
||||||
| `wg.Add(1); go func(){ defer wg.Done(); … }()` | `wg.Go(func(){…})` | 1.25 |
|
| `wg.Add(1); go func(){ defer wg.Done(); … }()` | `wg.Go(func(){…})` | 1.25 |
|
||||||
| `func ptr[T any](v T) *T { return &v }` | `new(expr)` e.g. `new(time.Now())` | 1.26 |
|
| `func ptr[T any](v T) *T { return &v }` | `new(expr)` e.g. `new(time.Now())` | 1.26 |
|
||||||
| `var target *E; errors.As(err, &target)` | `t, ok := errors.AsType[*E](err)` | 1.26 |
|
| `var target *E; errors.As(err, &target)` | `t, ok := errors.AsType[*E](err)` | 1.26 |
|
||||||
| Custom multi-error type | `errors.Join(err1, err2, …)` | 1.20 |
|
| Custom multi-error type | `errors.Join(err1, err2, …)` | 1.20 |
|
||||||
| Single `%w` for multiple causes | `fmt.Errorf("…: %w, %w", e1, e2)` | 1.20 |
|
| Single `%w` for multiple causes | `fmt.Errorf("…: %w, %w", e1, e2)` | 1.20 |
|
||||||
| `rand.Seed(time.Now().UnixNano())` | delete it (auto-seeded); prefer `math/rand/v2` | 1.20/1.22 |
|
| `rand.Seed(time.Now().UnixNano())` | delete it (auto-seeded); prefer `math/rand/v2` | 1.20/1.22 |
|
||||||
| `sync.Once` + captured variable | `sync.OnceValue(func() T {…})` / `OnceValues` | 1.21 |
|
| `sync.Once` + captured variable | `sync.OnceValue(func() T {…})` / `OnceValues` | 1.21 |
|
||||||
| Custom `min`/`max` helpers | `min(a, b)` / `max(a, b)` builtins (any ordered type) | 1.21 |
|
| Custom `min`/`max` helpers | `min(a, b)` / `max(a, b)` builtins (any ordered type) | 1.21 |
|
||||||
| `for k := range m { delete(m, k) }` | `clear(m)` (also zeroes slices) | 1.21 |
|
| `for k := range m { delete(m, k) }` | `clear(m)` (also zeroes slices) | 1.21 |
|
||||||
| Index+slice or `SplitN(s, sep, 2)` | `strings.Cut(s, sep)` / `bytes.Cut` | 1.18 |
|
| Index+slice or `SplitN(s, sep, 2)` | `strings.Cut(s, sep)` / `bytes.Cut` | 1.18 |
|
||||||
| `TrimPrefix` + check if anything was trimmed | `strings.CutPrefix` / `CutSuffix` (returns ok bool) | 1.20 |
|
| `TrimPrefix` + check if anything was trimmed | `strings.CutPrefix` / `CutSuffix` (returns ok bool) | 1.20 |
|
||||||
| `strings.Split` + loop when no slice is needed | `strings.SplitSeq` / `Lines` / `FieldsSeq` (iterator, no alloc) | 1.24 |
|
| `strings.Split` + loop when no slice is needed | `strings.SplitSeq` / `Lines` / `FieldsSeq` (iterator, no alloc) | 1.24 |
|
||||||
| `"2006-01-02"` / `"2006-01-02 15:04:05"` / `"15:04:05"` | `time.DateOnly` / `time.DateTime` / `time.TimeOnly` | 1.20 |
|
| `"2006-01-02"` / `"2006-01-02 15:04:05"` / `"15:04:05"` | `time.DateOnly` / `time.DateTime` / `time.TimeOnly` | 1.20 |
|
||||||
| Manual `Before`/`After`/`Equal` chains for comparison | `time.Time.Compare` (returns -1/0/+1; works with `slices.SortFunc`) | 1.20 |
|
| Manual `Before`/`After`/`Equal` chains for comparison | `time.Time.Compare` (returns -1/0/+1; works with `slices.SortFunc`) | 1.20 |
|
||||||
| Loop collecting map keys into slice | `slices.Sorted(maps.Keys(m))` | 1.23 |
|
| Loop collecting map keys into slice | `slices.Sorted(maps.Keys(m))` | 1.23 |
|
||||||
| `fmt.Sprintf` + append to `[]byte` | `fmt.Appendf(buf, …)` (also `Append`, `Appendln`) | 1.18 |
|
| `fmt.Sprintf` + append to `[]byte` | `fmt.Appendf(buf, …)` (also `Append`, `Appendln`) | 1.18 |
|
||||||
| `reflect.TypeOf((*T)(nil)).Elem()` | `reflect.TypeFor[T]()` | 1.22 |
|
| `reflect.TypeOf((*T)(nil)).Elem()` | `reflect.TypeFor[T]()` | 1.22 |
|
||||||
| `*(*[4]byte)(slice)` unsafe cast | `[4]byte(slice)` direct conversion | 1.20 |
|
| `*(*[4]byte)(slice)` unsafe cast | `[4]byte(slice)` direct conversion | 1.20 |
|
||||||
| `atomic.LoadInt64` / `AddInt64` / `StoreInt64` etc. | `atomic.Int64` (also `Int32`, `Uint32`, `Uint64`, `Bool`, `Pointer[T]`) | 1.19 |
|
| `atomic.LoadInt64` / `AddInt64` / `StoreInt64` etc. | `atomic.Int64` (also `Int32`, `Uint32`, `Uint64`, `Bool`, `Pointer[T]`) | 1.19 |
|
||||||
| `crypto/rand.Read(buf)` + hex/base64 encode | `crypto/rand.Text()` (one call) | 1.24 |
|
| `crypto/rand.Read(buf)` + hex/base64 encode | `crypto/rand.Text()` (one call) | 1.24 |
|
||||||
| Checking `crypto/rand.Read` error | don't: return is always nil | 1.24 |
|
| Checking `crypto/rand.Read` error | don't: return is always nil | 1.24 |
|
||||||
| `time.Sleep` in tests | `testing/synctest` (deterministic fake clock) | 1.24/1.25 |
|
| `time.Sleep` in tests | `testing/synctest` (deterministic fake clock) | 1.24/1.25 |
|
||||||
| `json:",omitempty"` on zero-value structs like `time.Time{}` | `json:",omitzero"` (uses `IsZero()` method) | 1.24 |
|
| `json:",omitempty"` on zero-value structs like `time.Time{}` | `json:",omitzero"` (uses `IsZero()` method) | 1.24 |
|
||||||
| `strings.Title` | `golang.org/x/text/cases` | 1.18 |
|
| `strings.Title` | `golang.org/x/text/cases` | 1.18 |
|
||||||
| `net.IP` in new code | `net/netip.Addr` (immutable, comparable, lighter) | 1.18 |
|
| `net.IP` in new code | `net/netip.Addr` (immutable, comparable, lighter) | 1.18 |
|
||||||
| `tools.go` with blank imports | `tool` directive in `go.mod` | 1.24 |
|
| `tools.go` with blank imports | `tool` directive in `go.mod` | 1.24 |
|
||||||
| `runtime.SetFinalizer` | `runtime.AddCleanup` (multiple per object, no pointer cycles) | 1.24 |
|
| `runtime.SetFinalizer` | `runtime.AddCleanup` (multiple per object, no pointer cycles) | 1.24 |
|
||||||
| `httputil.ReverseProxy.Director` | `.Rewrite` hook + `ProxyRequest` (Director deprecated in 1.26) | 1.20 |
|
| `httputil.ReverseProxy.Director` | `.Rewrite` hook + `ProxyRequest` (Director deprecated in 1.26) | 1.20 |
|
||||||
| `sql.NullString`, `sql.NullInt64`, etc. | `sql.Null[T]` | 1.22 |
|
| `sql.NullString`, `sql.NullInt64`, etc. | `sql.Null[T]` | 1.22 |
|
||||||
| Manual `ctx, cancel := context.WithCancel(…)` + `t.Cleanup(cancel)` | `t.Context()` (auto-canceled when test ends) | 1.24 |
|
| Manual `ctx, cancel := context.WithCancel(…)` + `t.Cleanup(cancel)` | `t.Context()` (auto-canceled when test ends) | 1.24 |
|
||||||
| `if d < 0 { d = -d }` on durations | `d.Abs()` (handles `math.MinInt64`) | 1.19 |
|
| `if d < 0 { d = -d }` on durations | `d.Abs()` (handles `math.MinInt64`) | 1.19 |
|
||||||
| Implement only `TextMarshaler` | also implement `TextAppender` for alloc-free marshaling | 1.24 |
|
| Implement only `TextMarshaler` | also implement `TextAppender` for alloc-free marshaling | 1.24 |
|
||||||
| Custom `Unwrap() error` on multi-cause errors | `Unwrap() []error` (slice form; required for tree traversal) | 1.20 |
|
| Custom `Unwrap() error` on multi-cause errors | `Unwrap() []error` (slice form; required for tree traversal) | 1.20 |
|
||||||
|
|
||||||
## New capabilities
|
## New capabilities
|
||||||
|
|
||||||
These enable things that weren't practical before. Reach for them in the
|
These enable things that weren't practical before. Reach for them in the
|
||||||
described situations.
|
described situations.
|
||||||
|
|
||||||
| What | Since | When to use it |
|
| What | Since | When to use it |
|
||||||
|---|---|---|
|
|--------------------------------------------------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `cmp.Or(a, b, c)` | 1.22 | Defaults/fallback chains: returns first non-zero value. Replaces verbose `if a != "" { return a }` cascades. |
|
| `cmp.Or(a, b, c)` | 1.22 | Defaults/fallback chains: returns first non-zero value. Replaces verbose `if a != "" { return a }` cascades. |
|
||||||
| `context.WithoutCancel(ctx)` | 1.21 | Background work that must outlive the request (e.g. async cleanup after HTTP response). Derived context keeps parent's values but ignores cancellation. |
|
| `context.WithoutCancel(ctx)` | 1.21 | Background work that must outlive the request (e.g. async cleanup after HTTP response). Derived context keeps parent's values but ignores cancellation. |
|
||||||
| `context.AfterFunc(ctx, fn)` | 1.21 | Register cleanup that fires on context cancellation without spawning a goroutine that blocks on `<-ctx.Done()`. |
|
| `context.AfterFunc(ctx, fn)` | 1.21 | Register cleanup that fires on context cancellation without spawning a goroutine that blocks on `<-ctx.Done()`. |
|
||||||
| `context.WithCancelCause` / `Cause` | 1.20 | When callers need to know WHY a context was canceled, not just that it was. Retrieve cause with `context.Cause(ctx)`. |
|
| `context.WithCancelCause` / `Cause` | 1.20 | When callers need to know WHY a context was canceled, not just that it was. Retrieve cause with `context.Cause(ctx)`. |
|
||||||
| `context.WithDeadlineCause` / `WithTimeoutCause` | 1.21 | Attach a domain-specific error to deadline/timeout expiry (e.g. distinguish "DB query timed out" from "HTTP request timed out"). |
|
| `context.WithDeadlineCause` / `WithTimeoutCause` | 1.21 | Attach a domain-specific error to deadline/timeout expiry (e.g. distinguish "DB query timed out" from "HTTP request timed out"). |
|
||||||
| `errors.ErrUnsupported` | 1.21 | Standard sentinel for "not supported." Use instead of per-package custom sentinels. Check with `errors.Is`. |
|
| `errors.ErrUnsupported` | 1.21 | Standard sentinel for "not supported." Use instead of per-package custom sentinels. Check with `errors.Is`. |
|
||||||
| `http.ResponseController` | 1.20 | Per-request flush, hijack, and deadline control without type-asserting `ResponseWriter` to `http.Flusher` or `http.Hijacker`. |
|
| `http.ResponseController` | 1.20 | Per-request flush, hijack, and deadline control without type-asserting `ResponseWriter` to `http.Flusher` or `http.Hijacker`. |
|
||||||
| Enhanced `ServeMux` routing | 1.22 | `"GET /items/{id}"` patterns in `http.ServeMux`. Access with `r.PathValue("id")`. Wildcards: `{name}`, catch-all: `{path...}`, exact: `{$}`. Eliminates many third-party router dependencies. |
|
| Enhanced `ServeMux` routing | 1.22 | `"GET /items/{id}"` patterns in `http.ServeMux`. Access with `r.PathValue("id")`. Wildcards: `{name}`, catch-all: `{path...}`, exact: `{$}`. Eliminates many third-party router dependencies. |
|
||||||
| `os.Root` / `OpenRoot` | 1.24 | Confined directory access that prevents symlink escape. 1.25 adds `MkdirAll`, `ReadFile`, `WriteFile` for real use. |
|
| `os.Root` / `OpenRoot` | 1.24 | Confined directory access that prevents symlink escape. 1.25 adds `MkdirAll`, `ReadFile`, `WriteFile` for real use. |
|
||||||
| `os.CopyFS` | 1.23 | Copy an entire `fs.FS` to local filesystem in one call. |
|
| `os.CopyFS` | 1.23 | Copy an entire `fs.FS` to local filesystem in one call. |
|
||||||
| `os/signal.NotifyContext` with cause | 1.26 | Cancellation cause identifies which signal (SIGTERM vs SIGINT) triggered shutdown. |
|
| `os/signal.NotifyContext` with cause | 1.26 | Cancellation cause identifies which signal (SIGTERM vs SIGINT) triggered shutdown. |
|
||||||
| `io/fs.SkipAll` / `filepath.SkipAll` | 1.20 | Return from `WalkDir` callback to stop walking entirely. Cleaner than a sentinel error. |
|
| `io/fs.SkipAll` / `filepath.SkipAll` | 1.20 | Return from `WalkDir` callback to stop walking entirely. Cleaner than a sentinel error. |
|
||||||
| `GOMEMLIMIT` env / `debug.SetMemoryLimit` | 1.19 | Soft memory limit for GC. Use alongside or instead of `GOGC` in memory-constrained containers. |
|
| `GOMEMLIMIT` env / `debug.SetMemoryLimit` | 1.19 | Soft memory limit for GC. Use alongside or instead of `GOGC` in memory-constrained containers. |
|
||||||
| `net/url.JoinPath` | 1.19 | Join URL path segments correctly. Replaces error-prone string concatenation. |
|
| `net/url.JoinPath` | 1.19 | Join URL path segments correctly. Replaces error-prone string concatenation. |
|
||||||
| `go test -skip` | 1.20 | Skip tests matching a pattern. Useful when running a subset of a large test suite. |
|
| `go test -skip` | 1.20 | Skip tests matching a pattern. Useful when running a subset of a large test suite. |
|
||||||
|
|
||||||
## Key packages
|
## Key packages
|
||||||
|
|
||||||
|
|||||||
@@ -23,48 +23,48 @@
|
|||||||
|
|
||||||
### Testing Issues
|
### Testing Issues
|
||||||
|
|
||||||
3. **"package should be X_test"**
|
1. **"package should be X_test"**
|
||||||
- **Solution**: Use `package_test` naming for test files
|
- **Solution**: Use `package_test` naming for test files
|
||||||
- Example: `identityprovider_test` for black-box testing
|
- Example: `identityprovider_test` for black-box testing
|
||||||
|
|
||||||
4. **Race conditions in tests**
|
2. **Race conditions in tests**
|
||||||
- **Solution**: Use unique identifiers instead of hardcoded names
|
- **Solution**: Use unique identifiers instead of hardcoded names
|
||||||
- Example: `fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())`
|
- Example: `fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())`
|
||||||
- Never use hardcoded names in concurrent tests
|
- Never use hardcoded names in concurrent tests
|
||||||
|
|
||||||
5. **Missing newlines**
|
3. **Missing newlines**
|
||||||
- **Solution**: Ensure files end with newline character
|
- **Solution**: Ensure files end with newline character
|
||||||
- Most editors can be configured to add this automatically
|
- Most editors can be configured to add this automatically
|
||||||
|
|
||||||
### OAuth2 Issues
|
### OAuth2 Issues
|
||||||
|
|
||||||
6. **OAuth2 endpoints returning wrong error format**
|
1. **OAuth2 endpoints returning wrong error format**
|
||||||
- **Solution**: Ensure OAuth2 endpoints return RFC 6749 compliant errors
|
- **Solution**: Ensure OAuth2 endpoints return RFC 6749 compliant errors
|
||||||
- Use standard error codes: `invalid_client`, `invalid_grant`, `invalid_request`
|
- Use standard error codes: `invalid_client`, `invalid_grant`, `invalid_request`
|
||||||
- Format: `{"error": "code", "error_description": "details"}`
|
- Format: `{"error": "code", "error_description": "details"}`
|
||||||
|
|
||||||
7. **Resource indicator validation failing**
|
2. **Resource indicator validation failing**
|
||||||
- **Solution**: Ensure database stores and retrieves resource parameters correctly
|
- **Solution**: Ensure database stores and retrieves resource parameters correctly
|
||||||
- Check both authorization code storage and token exchange handling
|
- Check both authorization code storage and token exchange handling
|
||||||
|
|
||||||
8. **PKCE tests failing**
|
3. **PKCE tests failing**
|
||||||
- **Solution**: Verify both authorization code storage and token exchange handle PKCE fields
|
- **Solution**: Verify both authorization code storage and token exchange handle PKCE fields
|
||||||
- Check `CodeChallenge` and `CodeChallengeMethod` field handling
|
- Check `CodeChallenge` and `CodeChallengeMethod` field handling
|
||||||
|
|
||||||
### RFC Compliance Issues
|
### RFC Compliance Issues
|
||||||
|
|
||||||
9. **RFC compliance failures**
|
1. **RFC compliance failures**
|
||||||
- **Solution**: Verify against actual RFC specifications, not assumptions
|
- **Solution**: Verify against actual RFC specifications, not assumptions
|
||||||
- Use WebFetch tool to get current RFC content for compliance verification
|
- Use WebFetch tool to get current RFC content for compliance verification
|
||||||
- Read the actual RFC specifications before implementation
|
- Read the actual RFC specifications before implementation
|
||||||
|
|
||||||
10. **Default value mismatches**
|
2. **Default value mismatches**
|
||||||
- **Solution**: Ensure database migrations match application code defaults
|
- **Solution**: Ensure database migrations match application code defaults
|
||||||
- Example: RFC 7591 specifies `client_secret_basic` as default, not `client_secret_post`
|
- Example: RFC 7591 specifies `client_secret_basic` as default, not `client_secret_post`
|
||||||
|
|
||||||
### Authorization Issues
|
### Authorization Issues
|
||||||
|
|
||||||
11. **Authorization context errors in public endpoints**
|
1. **Authorization context errors in public endpoints**
|
||||||
- **Solution**: Use `dbauthz.AsSystemRestricted(ctx)` pattern
|
- **Solution**: Use `dbauthz.AsSystemRestricted(ctx)` pattern
|
||||||
- Example:
|
- Example:
|
||||||
|
|
||||||
@@ -75,17 +75,17 @@
|
|||||||
|
|
||||||
### Authentication Issues
|
### Authentication Issues
|
||||||
|
|
||||||
12. **Bearer token authentication issues**
|
1. **Bearer token authentication issues**
|
||||||
- **Solution**: Check token extraction precedence and format validation
|
- **Solution**: Check token extraction precedence and format validation
|
||||||
- Ensure proper RFC 6750 Bearer Token Support implementation
|
- Ensure proper RFC 6750 Bearer Token Support implementation
|
||||||
|
|
||||||
13. **URI validation failures**
|
2. **URI validation failures**
|
||||||
- **Solution**: Support both standard schemes and custom schemes per protocol requirements
|
- **Solution**: Support both standard schemes and custom schemes per protocol requirements
|
||||||
- Native OAuth2 apps may use custom schemes
|
- Native OAuth2 apps may use custom schemes
|
||||||
|
|
||||||
### General Development Issues
|
### General Development Issues
|
||||||
|
|
||||||
14. **Log message formatting errors**
|
1. **Log message formatting errors**
|
||||||
- **Solution**: Use lowercase, descriptive messages without special characters
|
- **Solution**: Use lowercase, descriptive messages without special characters
|
||||||
- Follow Go logging conventions
|
- Follow Go logging conventions
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ jobs:
|
|||||||
- "**"
|
- "**"
|
||||||
docs:
|
docs:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
- "README.md"
|
- ".claude/docs/**"
|
||||||
|
- "*.md"
|
||||||
- "examples/web-server/**"
|
- "examples/web-server/**"
|
||||||
- "examples/monitoring/**"
|
- "examples/monitoring/**"
|
||||||
- "examples/lima/**"
|
- "examples/lima/**"
|
||||||
@@ -120,6 +121,28 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
FILTER_JSON: ${{ toJSON(steps.filter.outputs) }}
|
FILTER_JSON: ${{ toJSON(steps.filter.outputs) }}
|
||||||
|
|
||||||
|
lint-docs:
|
||||||
|
needs: changes
|
||||||
|
if: needs.changes.outputs.docs == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Check docs
|
||||||
|
run: pnpm check-docs
|
||||||
|
|
||||||
# Disabled due to instability. See: https://github.com/coder/coder/issues/14553
|
# Disabled due to instability. See: https://github.com/coder/coder/issues/14553
|
||||||
# Re-enable once the flake hash calculation is stable.
|
# Re-enable once the flake hash calculation is stable.
|
||||||
# update-flake:
|
# update-flake:
|
||||||
@@ -1132,6 +1155,7 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
- fmt
|
- fmt
|
||||||
- lint
|
- lint
|
||||||
|
- lint-docs
|
||||||
- lint-actions
|
- lint-actions
|
||||||
- gen
|
- gen
|
||||||
- test-go-pg
|
- test-go-pg
|
||||||
@@ -1157,6 +1181,7 @@ jobs:
|
|||||||
echo "- changes: ${{ needs.changes.result }}"
|
echo "- changes: ${{ needs.changes.result }}"
|
||||||
echo "- fmt: ${{ needs.fmt.result }}"
|
echo "- fmt: ${{ needs.fmt.result }}"
|
||||||
echo "- lint: ${{ needs.lint.result }}"
|
echo "- lint: ${{ needs.lint.result }}"
|
||||||
|
echo "- lint-docs: ${{ needs.lint-docs.result }}"
|
||||||
echo "- lint-actions: ${{ needs.lint-actions.result }}"
|
echo "- lint-actions: ${{ needs.lint-actions.result }}"
|
||||||
echo "- gen: ${{ needs.gen.result }}"
|
echo "- gen: ${{ needs.gen.result }}"
|
||||||
echo "- test-go-pg: ${{ needs.test-go-pg.result }}"
|
echo "- test-go-pg: ${{ needs.test-go-pg.result }}"
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
name: Docs CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
# Self-reference removed from both push and pull_request: the `lint`
|
|
||||||
# and `fmt` steps gate on `tj-actions/changed-files` matching
|
|
||||||
# `docs/**` or `**.md`, so a workflow-only edit produced an empty
|
|
||||||
# run. `actionlint` and `make lint/actions` catch YAML problems
|
|
||||||
# before merge regardless. See DOCS-129.
|
|
||||||
paths:
|
|
||||||
- "docs/**"
|
|
||||||
- "**.md"
|
|
||||||
|
|
||||||
pull_request:
|
|
||||||
# Self-reference removed; see comment under `push:` above.
|
|
||||||
paths:
|
|
||||||
- "docs/**"
|
|
||||||
- "**.md"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: ./.github/actions/setup-node
|
|
||||||
|
|
||||||
# Per-tool changed-files filters. Each tool gets its own `changed-*`
|
|
||||||
# step scoped to the files it processes, keeping workflow-level `paths:`
|
|
||||||
# broad. Adding a tool (e.g. image linter) only needs a new step pair.
|
|
||||||
- uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v45.0.7
|
|
||||||
id: changed-md
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
**.md
|
|
||||||
separator: ","
|
|
||||||
|
|
||||||
# Both downstream tools take file paths as argv. `tj-actions/changed-files`
|
|
||||||
# joins paths with `separator: ","`, which the shell does not split on, so
|
|
||||||
# run the output through `tr ',' '\n' | xargs -d '\n'` to hand each path to
|
|
||||||
# the tool as a distinct argument. This tolerates filenames containing
|
|
||||||
# spaces and prevents silent fallbacks: `markdownlint-cli2` would treat a
|
|
||||||
# comma-joined string as a single non-matching glob, and
|
|
||||||
# `markdown-table-formatter` would fall back to scanning every `.md` in
|
|
||||||
# the working tree when invoked with no positional args.
|
|
||||||
#
|
|
||||||
# `printf '%s\n'` is used instead of `echo` so a hypothetical leading
|
|
||||||
# `-e` or `-n` in a path is treated as data, not a bash builtin flag.
|
|
||||||
|
|
||||||
- name: lint
|
|
||||||
if: steps.changed-md.outputs.any_changed == 'true'
|
|
||||||
run: |
|
|
||||||
printf '%s\n' "$ALL_CHANGED_FILES" | tr ',' '\n' | xargs -d '\n' pnpm exec markdownlint-cli2
|
|
||||||
env:
|
|
||||||
ALL_CHANGED_FILES: ${{ steps.changed-md.outputs.all_changed_files }}
|
|
||||||
|
|
||||||
- name: fmt
|
|
||||||
if: steps.changed-md.outputs.any_changed == 'true'
|
|
||||||
run: |
|
|
||||||
printf '%s\n' "$ALL_CHANGED_FILES" | tr ',' '\n' | xargs -d '\n' pnpm exec markdown-table-formatter --check
|
|
||||||
env:
|
|
||||||
ALL_CHANGED_FILES: ${{ steps.changed-md.outputs.all_changed_files }}
|
|
||||||
+3
-2
@@ -4,8 +4,9 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8",
|
"packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format-docs": "markdown-table-formatter $(find docs -name '*.md') *.md",
|
"format-docs": "markdown-table-formatter $(find docs .claude/docs examples/web-server examples/monitoring examples/lima -name '*.md' 2>/dev/null) *.md",
|
||||||
"lint-docs": "markdownlint-cli2 --fix $(find docs -name '*.md') *.md",
|
"lint-docs": "markdownlint-cli2 --fix $(find docs .claude/docs examples/web-server examples/monitoring examples/lima -name '*.md' 2>/dev/null) *.md",
|
||||||
|
"check-docs": "markdownlint-cli2 $(find docs .claude/docs examples/web-server examples/monitoring examples/lima -name '*.md' 2>/dev/null) *.md && markdown-table-formatter --check $(find docs .claude/docs examples/web-server examples/monitoring examples/lima -name '*.md' 2>/dev/null) *.md",
|
||||||
"storybook": "pnpm run -C site/ storybook"
|
"storybook": "pnpm run -C site/ storybook"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user