Commit Graph
298 Commits
Author SHA1 Message Date
Jake Howell 18b37e64f5 chore(site/e2e): re-enable externalAuth suite (#26648)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook`
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook`
5. #26648 `chore(site/e2e): re-enable externalAuth suite` ← this PR

Removes the `test.describe.skip` introduced in #17235 so the
externalAuth suite runs in CI again.

Splitting the five commits keeps the bisection signal clean if CI
behaviour changes after the un-skip.

Local verification of the full stack: `pnpm playwright:test
e2e/tests/externalAuth.spec.ts --repeat-each=20 --workers=1` → 61/61
passing in 4m48s.

Large whitespace churn in the diff is biome reformatting the body to its
natural indentation after the single-line `test.describe(...)` opener.
The only semantic change is removing `.skip`.

Refs https://linear.app/codercom/issue/DEVEX-413
Refs https://github.com/coder/internal/issues/356
2026-08-24 23:24:13 +07:00
Jake Howell cfc66b5de3 fix: reset both providers in external auth hook (#26798)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook`
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook` ←
this PR
5. #26648 `chore(site/e2e): re-enable externalAuth suite`

Surfaced by stress-testing the leaf state of the stack locally with
`--repeat-each=N`. The suite passes on iter 1, then deterministically
fails the device test on every subsequent iteration:

| iter | device | web | workspace |
|---|---|---|---|
| 1 | ✓ 2.8s | ✓ 2.5s | ✓ 6.2s |
| 2 | **✘ 30.1s** | ✓ 2.6s | ✓ 6.2s |
| 3 | **✘ 30.1s** | ✓ ~2.5s | ✓ ~6s |
| … | … | … | … |

`resetExternalAuthKey` in `site/e2e/hooks.ts` only deletes the link for
`gitAuth.webProvider`, never `gitAuth.deviceProvider`. So:

```ts
path: `/api/v2/external-auth/${gitAuth.webProvider}?coder_session_token=${sessionCookie?.value}`,
```

Iter 1 of the device test creates a link for the `device` provider
against the test user. The cleanup hook between tests resets `web` but
not `device`. On iter 2 the page hits `/external-auth/device`,
`externalAuthProviderQuery.data.authenticated` is already `true`, the
page renders the authorized state immediately, and the test's
`sentPending.wait()` hangs forever because the mock token endpoint is
never polled. The whole test times out at 30s.

Fix: reset every provider the suite exercises. Refactored the per-link
delete into a Promise-returning helper so errors actually propagate; the
previous `throw` inside the response and `error` callbacks crashed the
worker rather than rejecting the call.

Doesn't affect CI (single run per PR, fresh `--ephemeral` coderd), but
it makes the suite robust to local stress testing and to anyone
re-running it in the same coderd process. Filed before the un-skip lands
so the un-skipped suite is repeatable from day one.

Local verification: `pnpm playwright:test e2e/tests/externalAuth.spec.ts
--repeat-each=20 --workers=1` → 61/61 passing in 4m48s. Same command
before this fix → 1 pass, then deterministic device-test timeouts.

Refs https://linear.app/codercom/issue/DEVEX-413

<details>
<summary>Why a separate PR</summary>

This is the fourth distinct root cause that piled up while the suite was
skipped, and it's orthogonal to the other three. #26575 fixes the
listener leak, #26793 fixes the 404 hook contract drift, #26795 fixes
the dropped invalidation, and this PR fixes the per-test cleanup so the
suite is actually repeatable. #26648 then just flips `.skip`.

</details>
2026-08-24 23:12:52 +07:00
Jeremy RuppelandSamuel Volin 46ec620767 fix(site): deflake adjust user theme preference (#28219)
## Summary

Deflakes the `adjust user theme preference` Playwright test
(`site/e2e/tests/users/userSettings.spec.ts`), tracked in DEVEX-415.

The test selected the Light theme and then hard-navigated with
`page.goto`
before the optimistic appearance update was persisted. The navigation
could
cancel the in-flight `PUT /api/v2/users/me/appearance`, so the reloaded
document embedded the stale `dark` preference and the final assertion
flaked.
`toPass` retries could not help because retrying the reload only
re-reads the
still-stale persisted state.

## Fix

Wait for the appearance form's save spinner to clear before the hard
reload,
mirroring how other settings tests wait for a visible save confirmation.
Asserting the optimistic light class first guarantees the spinner is
already
showing if a save started; a repeat run that is already light never
shows it,
so the test is idempotent and there are no direct API calls.

To give the test a UI signal, `Spinner` gets an opt-in `label` prop that
exposes it as a `role="status"` live region with an `aria-label`
(decorative
otherwise). `Loader` moves its label onto its own status container so it
keeps
a single status region.

## Changes

- `site/src/components/Spinner/Spinner.tsx`: opt-in `label` prop.
- `site/src/components/Loader/Loader.tsx`: label on its own status
region.
- `site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx`:
label
  both appearance save spinners.
- `site/e2e/tests/users/userSettings.spec.ts`: wait for the save
spinner.

## Validation

- `biome check` and `tsc -p .` pass.
- Loader + AppearancePage unit tests and the affected storybook tests
pass.
- The e2e test passed 20/20 under
`pnpm playwright:test -g "adjust user theme preference" --repeat-each
20`.

<details>
<summary>Implementation plan & decision log</summary>

# DEVEX-415: Fix flake in "adjust user theme preference" e2e test

## Problem

Playwright test `site/e2e/tests/users/userSettings.spec.ts` → `adjust
user
theme preference` flakes. After selecting the Light theme and
hard-navigating
to `/`, the reloaded page sometimes stays `dark`, failing the final
assertion.

CI Flake Bot has recorded repeated recurrences on `main` (latest
2026-08-17,
runs `32004239187`, `31745261984`) even after PR #25183 added `toPass`
retries.

## Root cause (confirmed by reading the code)

The appearance update is optimistic and its persistence is not awaited
before
navigation:

- `updateAppearanceSettings` (`site/src/api/queries/users.ts`) has an
`onMutate` that optimistically writes the new theme into the React Query
  cache. The `<html>` class flips to `light` immediately, before the
  `PUT /api/v2/users/me/appearance` completes.
- `useQueuedAppearanceSubmit`
  (`site/src/pages/UserSettingsPage/AppearancePage/AppearancePage.tsx`)
serializes submits: if a request is in flight, the next is queued and
only
  fires after the first settles.
- A fresh `member` user has **empty** appearance settings.
`migrateLegacyPreference` (`site/src/theme/themeMode.ts`) maps empty
settings
to `{ mode: "single", theme: "dark" }` (`DEFAULT_THEME = "dark"`). So
the
"Theme mode" dropdown already starts on **Single theme** and `<html>`
starts
  `dark`. Selecting "Single theme" in the test is therefore a **no-op**
(`onChangeMode` early-returns when `mode === draft.mode`) and fires
**no**
  PUT. The only appearance PUT in the test is the one from clicking
"Light default" (`onSelectSingle("light")` → `theme_preference:
"light"`).
- The test's first `expectLightThemeClasses(page)` passes purely from
the
  optimistic cache. It then calls `page.goto("/")` almost immediately
  (~20 ms after the click). If PUT #2 (the light one) has not persisted
server-side, the new document loads the still-persisted `dark`
preference
from embedded metadata, and every retry of the post-navigation assertion
  sees `dark` for the full 10 s window.

`toPass` cannot help post-navigation because it only re-reads stale,
already
persisted state; it cannot make an unfinished/queued PUT complete.

## Implemented fix: wait for the saving spinner (UI signal) before
navigation

> Iteration history: (1) An earlier attempt waited on the appearance
`PUT` via
> a `waitForApiCall` helper, but that couples the test to a state
transition
> and is non-idempotent (a repeat run that is already light fires no
PUT, so
> the wait times out). CI retries reuse the same ephemeral server, so
this is a
> real hazard. (2) A second attempt confirmed persistence with
> `page.request.get(...)`, but that calls the API directly and stops
being a
> site test. Both were rejected.

The repo's non-flaky settings tests wait for a **visible save
confirmation**
(e.g. "settings updated successfully" toasts) before trusting the
result. The
appearance theme form has no toast; its only save-in-progress feedback
is the
`<Spinner>`. So the fix mirrors that pattern using the spinner:

1. Make the theme section's spinner identifiable via a new opt-in
`label` prop
   on `Spinner` (sets `role="status"` + `aria-label` only when provided;
   decorative otherwise). `Loader` moves its label onto its own `status`
   container so it keeps a single status region and its `getByLabelText`
   queries keep working.
2. In the test, after clicking "Light default", assert the optimistic
light
class, then wait for that spinner to be hidden before `page.goto("/")`.

Why this is correct and idempotent:

- React Query sets `isPending` before `onMutate` applies the optimistic
cache
update, so by the time the optimistic light class is visible the spinner
is
already showing if a save started. Waiting for it to clear guarantees
the PUT
  settled (and was not canceled by navigation) before the reload.
- A repeat run that is already light: clicking "Light default" is a
no-op radio
  change, no PUT fires, the spinner never shows, and `toBeHidden` passes
  immediately. The reload still shows light.
- No direct API calls: the test only observes site UI.

### Implemented changes

`Spinner` gains an opt-in `label` prop
(`site/src/components/Spinner/Spinner.tsx`):

```tsx
role={label ? "status" : undefined}
aria-label={label}
```

`Loader` carries the label on its own status container
(`site/src/components/Loader/Loader.tsx`), and the appearance save
spinners use
the new prop (`AppearanceForm.tsx`):

```tsx
<Spinner loading={isUpdating} size="sm" label="Saving theme preference" />
<Spinner loading={isUpdating} size="sm" label="Saving terminal font" />
```

`site/e2e/tests/users/userSettings.spec.ts`:

```ts
await expect(
	page.getByRole("combobox", { name: /theme mode/i }),
).toContainText("Single theme"); // precondition: single mode

const singleThemeGroup = page.getByRole("group", { name: "Theme" });
await expect(singleThemeGroup).toBeVisible();
await singleThemeGroup.getByText("Light default", { exact: true }).click();

await expectLightThemeClasses(page); // optimistic DOM => spinner showing if saving
await expect(
	page.getByRole("status", { name: "Saving theme preference" }),
).toBeHidden(); // save settled before the hard reload

await page.goto("/", { waitUntil: "domcontentloaded" });
await expectLightThemeClasses(page);
```

Validation: `biome check`, `tsc -p .` pass; Loader + AppearancePage unit
tests
and the affected storybook tests pass; the e2e test passed 20/20 under
`pnpm playwright:test -g "adjust user theme preference" --repeat-each
20`
(idempotent).

## Alternatives considered

- **Wait on the appearance `PUT` via a `waitForApiCall` helper**:
rejected. It
couples the test to a state transition and is non-idempotent, a repeat
run
that is already light fires no PUT so the wait times out (14/15 repeats
  failed). CI retries reuse the same ephemeral server, so this is a real
  hazard, not just a local-repeat artifact.
- **Confirm persistence with `page.request.get(...)`**: rejected. It
calls the
  API directly and stops being a site test.
- **Default `role="status"` on the shared `Spinner`**: rejected.
`Loader` wraps
  `Spinner` in its own `status` div, so a default would nest two status
regions and break `Loader.test.tsx`. The opt-in `label` prop avoids
this.
- **Add a networkidle wait or sleep before navigation**: rejected.
Violates
the repo guidance against `time.Sleep`-style timing hacks and is
inherently
  racy.
- **Fix product behavior instead of the test**: out of scope. Related
bug
DEVEX-94 ("Light Theme setting not respected until Appearance page
opened")
tracks product-side persistence/embedding behavior; this task is scoped
to
  stabilizing the e2e test.

## Files touched

- `site/src/components/Spinner/Spinner.tsx` (new opt-in `label` prop).
- `site/src/components/Loader/Loader.tsx` (label on its own status
region).
- `site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx`
(use the
  `label` prop on both appearance save spinners).
- `site/e2e/tests/users/userSettings.spec.ts` (wait for the spinner).


</details>

---
This PR was created by Coder Agents on behalf of @jeremyruppel.

---------

Co-authored-by: Samuel Volin <sam.volin@coder.com>
2026-08-17 15:36:46 -04:00
McKayla はな e1fa247e59 feat: redirect to the template builder after first time setup (#27670) 2026-08-13 11:57:29 -06:00
Danielle Maywood c424a76a12 feat: wire chat search box to full-text search (#27973) 2026-08-12 15:04:01 +01:00
Bobby HoandClaude Opus 5 4e512f786f fix: prefetch outdated Coder CLI in e2e setup instead of in the test (#27629)
## Summary

`e2e/tests/outdatedCLI.spec.ts` has a 30 second budget in which it must
create a template and workspace, start an agent, download an 84 MiB
release binary from GitHub, and then exercise the actual thing under
test: whether a `v2.8.0` client can still SSH into a workspace served by
HEAD. In the run that filed this ticket, `install.sh` spent **20.04
seconds** of that budget on an HTTP request the test does not need,
leaving 5.4 seconds for the download. The SSH flow never executed.

Worth being precise about the shape, because it changes the fix. The
stall is not in the code under test and it is not an SSH problem.
`install.sh` resolves the latest stable release version
**unconditionally**, even when `--version 2.8.0` is passed explicitly,
and on the pinned path that value feeds nothing but a cosmetic
post-install advisory string. Two thirds of the test's budget went to
producing one sentence of console output that the test discards.

Refs: https://github.com/coder/internal/issues/1571

## Problem

### What the test is for

This is a backward-compatibility test, and `v2.8.0` is the compatibility
floor it enforces rather than a "supported version" in the
release-channel sense. The pin traces to one code comment, `we no longer
support versions prior to Tailnet v2 API support`, citing 059e533544;
that commit first shipped in v2.8.0, so the pin sits exactly on the
boundary it names. Worth stating plainly: this is the oldest client
expected to still interoperate, not a version that receives patches.
Release support is mainline / stable / n-2 / ESR, all far newer.

The test's value is that it runs the *real* historical binary, compiled
in Feb 2024, against a current server: `codersdk` REST compatibility,
tailnet coordination v2, DERP negotiation, `coder ssh --stdio` as an SSH
transport, and the agent accepting a session. Nobody gets to assert what
that old client sends over the wire, which is exactly why the binary has
to be downloaded rather than faked.

The structural defect is that the download shares a timeout with the
assertion:

```text
┌─────────────────────────────────────────────────────────────────┐
│  ONE 30-second Playwright test budget                           │
├──────────────────────────────┬──────────────────────────────────┤
│  What we want to measure     │  Incidental setup                │
│  (deterministic, local)      │  (network, non-deterministic)    │
│                              │                                  │
│  • template + workspace      │  • HTTP HEAD to github.com       │
│  • agent connect             │  • 84 MiB download from          │
│  • coder ssh --stdio         │    GitHub release CDN            │
│  • SSH handshake + exec      │  • tar extraction                │
│  • workspace stop            │                                  │
└──────────────────────────────┴──────────────────────────────────┘
      ~7-14 s, stable                   0 s (cached) .. ∞ (unbounded)
```

A test that asserts protocol compatibility should not be able to fail
because `github.com` was slow.

### The evidence

CI runs Playwright with `DEBUG: pw:api`, and `downloadCoderVersion`
passes `TRACE=1` to `install.sh`, which makes it `set -x`. The job log
therefore stamps every phase. Reconstructed from [job
80049661296](https://github.com/coder/coder/actions/runs/27124540074/job/80049661296),
`t=` relative to test start:

```text
t=+0.000  08:17:44.671  browserContext.newPage                      <- test starts
t=+0.882  08:17:45.553  login complete
t=+4.228  08:17:48.899  workspace create submitted
t=+4.519  08:17:49.190  agent-status-ready visible                  <- startAgent returns
t=+4.526  08:17:49.197  install.sh: parse_arg --version 2.8.0 ...   <- downloadCoderVersion
t=+4.531  08:17:49.202  curl -sSLI https://github.com/coder/coder/releases/latest
                        :
                        :   20.042 SECONDS OF NOTHING
                        :   (agent logs keepalives; the page sits idle)
                        :
t=+24.573 08:18:09.244  response= 200 .../releases/tag/v2.33.6      <- probe returns
t=+24.575 08:18:09.246  STABLE_VERSION=2.33.6                       <- feeds a log line
t=+24.582 08:18:09.253  curl -#fL -o .../coder_2.8.0_linux_amd64.tar.gz.incomplete
                        :   5.4 s of an 84 MiB download
t=+30.000 08:18:14.671  Playwright kills the test
```

Three observations rule out the originally suspected cause (slow SSH
readiness or general runner slowness):

- **The SSH flow never started.** `sshIntoWorkspace` is called after
`downloadCoderVersion` returns, and it never returned. There is no
`coder ssh --stdio` process in the log.
- **The agent was healthy.** `agent-status-ready` resolved in 88 ms, and
through the entire 20 second stall the agent logs a live DERP
connection, successful STUN, and a completed wireguard handshake.
- **The runner was fast, not slow.** Login plus template plus workspace
plus agent took 4.5 seconds.

### Where the 20 seconds goes

```text
install.sh main()
  ...
  L431   STABLE_VERSION=$(echo_latest_stable_version)   <- ALWAYS runs
                |
                +-- echo_latest_stable_version()  (install.sh:94)
                      curl -sSLI https://github.com/coder/coder/releases/latest
                      #  no --connect-timeout
                      #  no --max-time
                      #  non-200 => exit 1  (hard failure)

  L454-461  the only consumers when --version is pinned:
              if VERSION == STABLE_VERSION: STABLE=1

  L148      advisory="To install our stable release (v${STABLE_VERSION}), ..."
  L159      "Coder ${channel}release v${VERSION} installed. ${advisory}"
```

That is the whole dependency chain. `-sSLI` also follows redirects and
`/releases/latest` *is* a redirect, so this is at minimum two
round-trips to `github.com` with no timeout ceiling on either.

### Why 30 seconds and not 60

`test.setTimeout(60_000)` used to be on this test. #16236 removed it,
and that removal was deliberate: it was itself a flake fix
(coder/internal#204, #279) whose thesis was that `go run` compiling
inside a resource-constrained test run was the problem. Having pre-built
the binary, it consistently stripped the allowances that existed to
absorb compile time:

| File | Change in #16236 | Was that allowance really compile time? |
|---|---|---|
| `app.spec.ts` | `setTimeout(75_000)` removed, click timeout `60_000`
-> `10_000` | Yes |
| `webTerminal.spec.ts` | `setTimeout(75_000)` removed | Yes |
| `helpers.ts` | agent-ready wait `45_000` -> `15_000` | Yes |
| `outdatedCLI.spec.ts` | `setTimeout(60_000)` removed | **No: also an
84 MiB download** |
| `outdatedAgent.spec.ts` | timeout untouched, 60 s survives | n/a |

The reasoning was sound and the sweep internally consistent. It had one
blind spot: for `app.spec.ts` and `webTerminal.spec.ts` that budget
genuinely was the compiler's, but here it covered compile time **plus**
a release download, and only the compile half went away. With 60
seconds, the failing run above would have finished in roughly 31 to 43
seconds and passed.

### Budget arithmetic

At `t=+24.58` the test still had to do:

| Remaining work | Realistic cost |
|---|---:|
| Download 84 MiB tarball | 2 - 8 s |
| `tar` extract | 0.3 - 1 s |
| `coder ssh --stdio` cold start | 0.5 - 2 s |
| Tailnet dial + SSH handshake | 1 - 3 s |
| `stopWorkspace` | 2 - 4 s |
| **Needed** | **~6 - 18 s** |
| **Available** | **5.42 s** |

## Fix

Move the download into the existing `testsSetup` Playwright project,
where it gets a 300 second budget and where a failure is attributed to
the download rather than to SSH.

```mermaid
flowchart TB
    subgraph BEFORE["BEFORE: one budget, two concerns"]
        direction TB
        T1["tests project, timeout 30s"]
        T1A["outdatedCLI.spec.ts<br/>login / template / workspace / agent<br/><b>downloadCoderVersion &lt;- NETWORK</b><br/>sshIntoWorkspace / exec / stopWorkspace"]
        T1 --> T1A
    end

    subgraph AFTER["AFTER: network work has its own clock"]
        direction TB
        S2["testsSetup project, timeout 300s"]
        S2A["downloadCoderVersions.spec.ts<br/>stable-version probe + 84 MiB + retries<br/>all live HERE"]
        T2["tests project, timeout 60s"]
        T2A["outdatedCLI.spec.ts<br/>downloadCoderVersion = cache hit, ~300ms<br/>SSH path gets the whole budget"]
        S2 --> S2A
        S2A -- "dependencies" --> T2
        T2 --> T2A
    end

    BEFORE ~~~ AFTER

    style T1A fill:#ffe5e5,stroke:#cc0000,stroke-width:2px
    style S2A fill:#e5ffe5,stroke:#007700,stroke-width:2px
    style T2A fill:#e5ffe5,stroke:#007700,stroke-width:2px
```

### Why it works

`downloadCoderVersion` was already idempotent and cache-checking: it
spawns `<binaryPath> version` first and returns early on exit 0. So the
test keeps its existing call and that call simply becomes a no-op
costing a few hundred milliseconds. **No test logic changes.**

```mermaid
sequenceDiagram
    autonumber
    participant S as testsSetup:<br/>downloadCoderVersions
    participant IS as install.sh
    participant GH as github.com
    participant T as tests:<br/>outdatedCLI
    participant CD as coderd + agent

    Note over S: budget 300s
    S->>IS: downloadCoderVersion(v2.8.0)
    IS->>GH: stable-version probe (unbounded)
    IS->>GH: fetch 84 MiB asset
    GH-->>IS: /tmp/coder-e2e-cache/bin/coder-e2e-2.8.0
    IS-->>S: binaryPath

    Note over T: budget 60s, local only
    T->>T: downloadCoderVersion(v2.8.0)
    Note right of T: spawn "<bin> version" -> exit 0<br/>returns early, ~300ms, no network
    T->>CD: coder ssh --stdio, handshake, exec "exit 0"
    CD-->>T: exit code 0
```

### Why the prefetch is non-fatal

The obvious implementation raises on failure. That would be wrong here,
and I verified why rather than assuming: `tests` declares `dependencies:
["testsSetup"]`, and a failing setup project stops dependent tests from
**running at all**. Adding a deliberately-throwing setup spec produced:

```text
✓  1 [testsSetup] › addUsersAndLicense.spec.ts › setup deployment (11.7s)
✓  2 [testsSetup] › downloadCoderVersions.spec.ts › download outdated CLI (353ms)
✘  3 [testsSetup] › zzTempFail.spec.ts › temporary blast radius probe (0ms)
  1 failed
  1 did not run      <- outdatedCLI never ran
  2 passed
```

So raising would convert a one-test flake into a whole-suite outage on
any GitHub hiccup. Instead the prefetch logs a warning and returns, and
the test's own `downloadCoderVersion` call fetches inline as it does
today. The failure path is therefore no worse than the status quo, and
the success path removes the network from the test entirely.

Of the three policies available (fail hard, fall back inline, or skip
the test), this is the only one that cannot regress anything: it never
blocks the suite, and it never silently drops coverage the way an
auto-skip would.

### Restoring the 60 second budget

This is the second half of the change, and it exists for the fallback
path above. It cannot reintroduce what #16236 fixed: the timeout value
has no causal relationship to how the binary is produced, `coderBinary`
stays pre-built, `go run` stays gone, and only `outdatedCLI.spec.ts` is
touched.

It does give back a bounded sliver of the CI-latency goal, and the bound
is small. A passing run is unaffected. The cost lands only when this one
test hangs, and then it is +30 s once: `--workers 1` so there is no
fan-out, `CODER_E2E_TEST_RETRIES` is unset in CI so `retries` is 0 and
nothing multiplies it, and the job budget is `timeout-minutes: 20`.

## Measurements

Four scenarios, locally on darwin/arm64 against a freshly built
`site/e2e/bin/coder`:

| Scenario | setup spec | `outdatedCLI` | `install.sh` inside the test?
| Result |
|---|---:|---:|---|---|
| Cold, empty cache | 5.7 s | 10.0 s | **no**, ran in setup | ✓ passed |
| Warm cache | 340 ms | 11.9 s | **no**, 0 invocations | ✓ passed |
| Prefetch fails, cache empty | 1 ms | 15.4 s | yes, inline fallback | ✓
passed |
| Setup spec throws | n/a | did not run | n/a | blast radius above |

The cold run is the load-bearing one: `install.sh` is invoked from the
setup spec and the test runs local-only in 10.0 s, so the 84 MiB
download and the 20 s probe are no longer on the assertion's clock.

For context on what "local only" costs, eight consecutive `main` runs
where the CI cache already made `install.sh` a no-op:

| Job | duration |
|---|---:|
| 90382498172 | 11.7 s |
| 90352398170 | 12.1 s |
| 90335547858 | 8.1 s |
| 90317232684 | 13.7 s |
| 90297156949 | 8.4 s |
| 90280065546 | 6.9 s |
| 90265047082 | 6.9 s |
| 90250803989 | 6.6 s |

6.6 to 13.7 seconds. This change makes that the only path rather than
the lucky one.

Also checked: the test name is byte identical (`ssh with client v2.8.0`)
so flake tracking keeps matching it, `outdatedAgent` remains skipped,
and `webTerminal`, `auditLogs`, and `updateTemplate` still pass, so the
added setup dependency disturbs nothing. The full 60-test suite was not
run locally because the premium tests need `CODER_E2E_LICENSE`.

## Also in this change

The pinned versions move to `site/e2e/constants.ts` as
`oldestSupportedCLIVersion` and `oldestSupportedAgentVersion`, so the
setup spec and the tests share one source of truth, and the comments
explaining *why* those particular versions travel with them. The CI
cache key follows them there: it previously hashed the two spec files,
and now hashes `constants.ts`, so it still invalidates exactly when a
pinned version changes.

## Not addressed here

The 20 second probe is relocated, not removed. `install.sh` still
resolves the latest stable version on every pinned install, with no
`--connect-timeout` or `--max-time`, and still treats a non-200 as
fatal, so a GitHub hiccup can fail an install whose target tarball is
already cached locally. That is a user-facing bug in its own right and
wants its own PR, since fixing it means deciding what a pinned install
should print when we no longer look up what "stable" currently is.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 16:26:13 -07:00
McKayla はな 00d134ebfd chore: remove classic parameter UI (#25014) 2026-07-27 19:02:28 -06:00
Andrew Aquino 5699f1cdfb fix: retry and cache e2e Coder release downloads to reduce test-e2e ssh flake (#27470)
closes DEVEX-651

## Summary

Fixes coder/internal#218 (`flake: e2e-test / test ssh`).

Despite the title, the `ssh with client v2.8.0` / `ssh with agent
v2.12.1` cases (`site/e2e/tests/outdatedCLI.spec.ts`,
`outdatedAgent.spec.ts`) are not failing because of a bug in SSH. They
fail during **setup**, in `downloadCoderVersion()`, which runs
`install.sh` to fetch an old Coder release from GitHub. Transient GitHub
errors (HTTP 403/503, surfacing as nonzero `curl` exit codes such as 22
or 1) make `install.sh` fail and take the whole ssh test down with it.

This is an external-download flake, confirmed by the recurring
`install.sh failed with code {22,1}` evidence in the issue thread and
Ethan's note ("Networking issues again").

## Changes

1. **Retry-with-backoff** (`site/e2e/helpers.ts`):
`downloadCoderVersion()` now retries `install.sh` up to 5 times with
exponential backoff and jitter (~1s, 2s, 4s, 8s). A single transient
download failure no longer fails the test. `install.sh` already reuses
completed binaries and resumes partial downloads (`curl -C -`), so
retries are cheap.
2. **Cross-run cache** (`.github/workflows/ci.yaml`): the `test-e2e` job
now persists `/tmp/coder-e2e-cache` with `actions/cache`, so most runs
skip the GitHub download entirely. The key is derived from the spec
files that pin the downloaded versions, so it invalidates when those
versions change. Saves are restricted to `main` (`restore` runs
everywhere), matching the existing cache-poisoning convention used for
the Vale and golangci-lint caches.

Before this change, neither retry, mirror, nor cross-run caching
protected this path; the only caching was within a single run.

## Testing

- `biome check e2e/helpers.ts` passes.
- `tsc --noEmit` introduces no new errors.
- CI `test-e2e` exercises the changed path.

<details>
<summary>Investigation notes</summary>

- The failure always originates in `downloadCoderVersion` ->
`install.sh` -> `fetch()` (`curl -#fL ...
https://github.com/coder/coder/releases/download/vX.Y.Z/...`).
- `curl` exit 22 = server returned an HTTP error (403 seen in logs);
exit 1 = other transient failure. GitHub also returned 503s across the
workflow in some occurrences.
- `/tmp/coder-e2e-cache` was not persisted by any `actions/cache` step
in `ci.yaml`, so every fresh job re-downloaded from GitHub and was
exposed to the flake.
- Retry addresses transient failures; the cache removes the dependency
on GitHub for most runs. Combined, they target the root cause at two
layers.

</details>

---

This PR was generated by Coder Agents on behalf of @aqandrew.
2026-07-27 09:40:43 -07:00
McKayla はな b8727d9c23 fix: don't show admin settings dropdown to everyone (#27481) 2026-07-23 22:59:40 -06:00
Faur Ioan-Aurel 0caaac5d27 fix: prebuild coderd before Playwright startup for e2e tests (#27136)
The test-e2e job could flake while Playwright waited for its webServer
to become ready. CI already built site/e2e/bin/coder, but that binary
was built before pnpm build and without the embed tag, so Playwright
ignored it and started coderd with go run -tags embed instead. That put
Go compilation, including frontend asset embedding, inside Playwright's
webServer timeout.

Local runs had a similar ordering trap. pnpm playwright:test invoked
Playwright directly, and Playwright starts webServer before globalSetup,
so the existing preflight build could not guarantee a binary existed
before webServer startup.

Build the E2E coder binary with the embed tag and make it depend on
site/out/index.html. In CI, build the frontend first, then build the E2E
binary, then run Playwright. For local runs, have pnpm playwright:test
and pnpm playwright:test-ui run make site/e2e/bin/coder before invoking
Playwright.

Playwright now starts the prebuilt site/e2e/bin/coder binary and waits
on the public /healthz endpoint instead of an authenticated API
endpoint. This keeps compilation outside the webServer readiness timeout
and makes the readiness probe quieter and more direct. I moved to
/healtz because the existing endpoint was returning 401, which still
means “server is up,” but it is a strange readiness signal. /healthz is
explicitly public and returns 200 OK once the HTTP server is accepting
requests.

Caveat: running pnpm exec playwright test
--config=e2e/playwright.config.ts directly bypasses the package script
and therefore bypasses the prebuild. Use pnpm playwright:test or make
test-e2e for the supported local flow.

Because the site package scripts now call make, add make to knip's
ignored
system binaries.

Tests:
- pnpm lint:types
- pnpm exec playwright test --config=e2e/playwright.config.ts --list
-
PATH="/opt/homebrew/opt/make/libexec/gnubin:/opt/homebrew/opt/gnu-getopt/bin:$PATH"
make gen/mark-fresh
-
PATH="/opt/homebrew/opt/make/libexec/gnubin:/opt/homebrew/opt/gnu-getopt/bin:$PATH"
pnpm playwright:test --list

<!--

If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

-->
2026-07-15 21:21:59 +03:00
Jeremy Ruppel 580ba19a0c fix(site/e2e): fix login helper race condition causing navigation flake (#27107)
Fixes flake reported in
[DEVEX-538](https://linear.app/codercom/issue/DEVEX-538/flake-create-user-with-password).

## Problem

The `login()` e2e helper had a race condition causing intermittent
navigation failures:

```
page.goto: Navigation to "/deployment/users" is interrupted by
another navigation to "/"
```

After clicking Sign In, `LoginPage.tsx` does a hard navigation via
`location.href = sanitizeRedirect(redirectTo)`. With no `?redirect=`
param, `retrieveRedirect` defaults to `"/"`, so login navigates to `/`.
The browser loads `/`, fires the `load` event, then React boots and the
router does a client-side redirect from `/` to `/workspaces` (via
`<Navigate to="/workspaces" replace />`).

The old helper waited with
`expectUrl(page).toHavePathName("/workspaces")`, which polls
`page.url()` and resolves the moment the pathname matches. It has no
awareness of page load state. So it resolved after the client-side
redirect changed the URL, but before the `/workspaces` page components
had mounted. When a test immediately called `page.goto()` afterward,
pending React rendering could trigger a competing navigation.

## Fix

Replace the URL polling with two Playwright-idiomatic waits:

1. `page.waitForURL(/\/workspaces/)` hooks into the browser's navigation
lifecycle: it waits for the URL to match AND for the page to reach a
load state (`"load"` by default), unlike `expectUrl` which is purely a
string poll.

2. `await expect(page).toHaveTitle(/Workspaces/)` waits for the page
title, which is set by the `WorkspacesPage` component. This proves React
booted, auth resolved, and the page fully rendered, closing the window
where pending React work could interfere with the next navigation.

Also adds `{ waitUntil: "domcontentloaded" }` to `page.goto("/login")`
for consistency with every other navigation helper in the file.

> 🤖 Generated by Coder Agents on behalf of @jeremyruppel
2026-07-13 09:47:01 -04:00
Jake Howell 39da38b189 fix(site/e2e): accept 404 from external auth reset hook (#26793)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook` ←
this PR
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook`
5. #26648 `chore(site/e2e): re-enable externalAuth suite`

`deleteExternalAuthByID` used to be inverted: `sql.ErrNoRows` (link
doesn't exist for this user/provider) fell through to the `500` path,
while non-`ErrNoRows` DB errors went to `httpapi.ResourceNotFound`.
#19775 (Sep 2025) refactored it to return `404` for not-found and `500`
for real DB errors, which is the contract you'd expect.

The relevant lines from #19775 in `coderd/externalauth.go`:

```diff
-	err := api.Database.DeleteExternalAuthLink(ctx, ...)
+	link, err := api.Database.GetExternalAuthLink(ctx, ...)
 	if err != nil {
-		if !errors.Is(err, sql.ErrNoRows) {
+		if errors.Is(err, sql.ErrNoRows) {
 			httpapi.ResourceNotFound(w)
 			return
 		}
 		httpapi.Write(ctx, w, http.StatusInternalServerError, ...)
 		return
 	}
```

`resetExternalAuthKey` in `site/e2e/hooks.ts` still treats `500` as the
not-found code, so the first `beforeEach` in the externalAuth suite
throws. The suite was skipped at the time #19775 landed (#17235), so
nobody noticed the contract drift until #26648 tried to re-enable it.

This just flips the accepted status codes to `200 || 404` and rewrites
the stale comment. The 401/403/500 paths still surface as failures,
which is what we want.

Refs https://linear.app/codercom/issue/DEVEX-413
Refs https://github.com/coder/coder/pull/19775

<details>
<summary>Why a separate PR</summary>

Keeps the bisection signal clean: #26575 proves the EADDRINUSE flake is
fixed, this PR fixes the hook contract drift surfaced by re-enabling the
suite, and #26648 just flips `.skip`. Squashing into #26648 would
conflate two unrelated fixes.

The CI run on #26648 already confirms the flake fix is doing its job:
`successful external auth from workspace` passes (5.6s) and the
`beforeAll`/`afterAll` mock servers come up and tear down cleanly with
no EADDRINUSE. The only failures are this 404 hook drift.

</details>
2026-07-06 03:56:50 +00:00
Jake Howell b341ce63fb fix(site/e2e): close mock external-auth servers in teardown (#26575)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
← this PR
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook`
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook`
5. #26648 `chore(site/e2e): re-enable externalAuth suite`

The externalAuth e2e suite has been skipped since #17235 because
`createServer` in `site/e2e/helpers.ts` started an express server but
never gave callers a way to close it. On retries or repeated runs, the
listener from the previous invocation was still bound to the hardcoded
port and the next `beforeAll` failed with `EADDRINUSE`, eventually
timing out in `waitForPort`.

`createServer` now returns a `{ app, close }` pair. The web flow closes
in `afterAll`; the device flow uses `try/finally`.
`closeAllConnections()` is called before `close()` so teardown stays
bounded if keep-alive connections linger.

The suite remains `test.describe.skip` here; #26648 flips the skip off
once the rest of the stack is in.

Refs https://linear.app/codercom/issue/DEVEX-413
Refs https://github.com/coder/internal/issues/356

<details>
<summary>Decision log</summary>

Discussed the full options list with @jakehwll before drafting. Picked
option A (minimal teardown) because:

- Two prior PRs (#15537, #16528) attacked symptoms (port probing, longer
timeout) without addressing the leaked listener.
- Kayla's diagnosis on coder/internal#356 pointed at exactly this case:
nothing else in CI is grabbing the port, the listener from the previous
run is still bound.
- A is mechanical and orthogonal: it adds a real teardown without
changing port allocation, fixture wiring, or what gets mocked. If the
flake persists after A, we know to escalate to a worker-scoped fixture
or dynamically allocated ports.

Returning `close` rather than the raw `http.Server` encapsulates the
`closeAllConnections` + `close` choreography so callers don't repeat it.
`closeAllConnections` is optional-chained because it landed in Node
18.2; coder/coder runs newer, but the chain costs nothing.

The device test uses `try/finally` rather than a shared `afterEach` to
keep per-test state local. The web flow's `afterAll` mirrors its
`beforeAll`.

</details>
2026-07-01 13:14:23 +10:00
dylanhuff-at-coder 7887cff9d0 feat: add user secrets management page (#25371)
Adds the account settings UI for managing user secrets, including the
table, add/edit/delete dialog, Storybook coverage, and route/sidebar
entry.

Also updates the shared `FeatureStageBadge` beta variant with
dedicated beta styling, sizing, and label casing for the Secrets
page.

Stacked on #25370.

_This PR was generated by Coder Agents._
2026-05-26 11:51:00 -07:00
Zach ddc0e99c69 chore: remove coder_secret Terraform integration (#25512)
Removes the coder_secret Terraform integration: the data.coder_secret
consumption path through provisionerdserver → provisioner.proto →
provisioner/terraform, the dynamic-parameter secret-requirement
validation, and the workspace-update / resolve-autostart surfaces that
depended on it. This is being done due to a product/feature direction
change (see PLAT-243). User-secret CRUD (DB, REST, CLI, UI, telemetry, audit)
and the agent-manifest secret-injection path are untouched.

The provisionerd API is bumped from v1.17 to v1.18 rather than rolled
back: v1.17 shipped in v2.33.x, so user_secrets field numbers are
reserved and the changelog documents both versions.

Generated with assistance from Coder Agents.
2026-05-21 09:19:29 -06:00
Jaayden Halko 2c18e07e39 feat: add theme mode dropdown (#25183)
## Summary

- Wire the Appearance settings page to the new theme mode dropdown and
sync or single theme selectors.
- Update Appearance page tests and stories for theme mode behavior.
- Update the user settings e2e test to exercise single theme selection.

## Dependencies

- Depends on #25076, #25180, #25181, and #25182.
- This PR targets helper branch `pr25077/05-theme-mode-dropdown-base`,
which contains dependency commits only, so this PR diff stays focused on
final dropdown wiring. Rebase and retarget after the dependency PRs
merge.

## Validation

- `pnpm -C site exec vitest run --project=unit
src/pages/UserSettingsPage/AppearancePage/AppearancePage.test.tsx
src/theme/themeMode.test.ts src/api/queries/users.test.ts`
- `pnpm -C site lint:types`
- `pnpm -C site storybook:ci`
- `pnpm -C site build`
- `pnpm -C site playwright:test -- e2e/tests/users/userSettings.spec.ts`
- Pre-commit hook passed on the branch commit.
2026-05-15 16:15:06 +01:00
35C4n0r 2871a02352 fix: use actual ai_task instances for HasAITasks (#25197)
Previously, `hasAITaskResources()` scanned the Terraform graph for
`coder_ai_task` node labels. The graph includes resource definitions
regardless of `count`, so templates with `count = 0` were incorrectly
marked as `HasAITasks = true`, causing them to appear on the `/tasks`
page when no AI task resources would be created.

Replace the graph-based check with `len(aiTasks) > 0`. The `aiTasks`
slice is populated from state modules where Terraform has already
evaluated `count`, so it correctly reflects actual resource instances.

ref:
https://linear.app/codercom/issue/ECO-39/make-coder-tasks-respect-count

> Generated with [Coder Agents](https://coder.com/agents)

---------

Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
Signed-off-by: Jay Kumar <jay.kumar@coder.com>
2026-05-14 21:49:05 +05:30
Cian Johnston caabb3c4ab fix(site): show Organizations in admin dropdown for single-org OSS deployments (#25175)
Fixes https://linear.app/codercom/issue/CODAGT-350

On OSS or no-license single-org deployments, the Organizations admin
link was hidden because `canViewOrganizationSettings` was gated on
`showOrganizations`, which requires either a multi-org entitlement or >1
org. The page was still reachable via direct URL, but the members view
displayed a raw "Template RBAC is a Premium feature. Contact sales!"
error from the groups API.

Two fixes:

1. Always render the Organizations link inside the `DeploymentDropdown`.
The dropdown itself is only shown to users with admin-level permissions,
so Organizations is effectively gated on having admin access.

2. Remove `groupsByUserIdQuery.error` from the error chain on the
members page. The groups endpoint is gated behind
`templateRBACEnabledMW` on enterprise, returning a 403 on OSS. The
groups data is already optional, so the page renders fine without it.

> Generated by Coder Agents
2026-05-12 12:28:43 +01:00
Michael Suchacz 85792d08bc feat: add harness engineering layer for agent workflows (#24791)
This PR adds an opinionated harness-engineering layer for agent-driven
workflows: a small set of agent-readable docs, mechanical structure
checks, structured CI failure summaries, an architecture-lint umbrella,
and per-worktree dev-server isolation. The goal is to make local dev,
tests, and CI mechanically inspectable by agents without changing app
runtime behavior.

## What landed

**Agent docs and navigation**
- `.claude/docs/OBSERVABILITY.md`, `.claude/docs/DEV_ISOLATION.md`,
`.claude/docs/AGENT_FAILURES.md`: task-oriented guides for logs,
tracing, Prometheus, dev-server isolation, and a seeded failure catalog.
- `AGENTS.md`: added an `Agent navigation` block, then trimmed the file
from 375 to 229 lines by migrating duplicated detail into
`WORKFLOWS.md`, `GO.md`, `TESTING.md`, and `DATABASE.md`. The
user-managed custom-instructions block is preserved.
- `.agents/docs`: symlink mirror of `.claude/docs` for agent runtimes
that look under `.agents`.

**Mechanical checks**
- `scripts/check_agents_structure.sh`: validates `@...` references in
tracked `AGENTS.md` files and warns when root grows past 600 lines.
Wired as `make lint/agents` and into `make lint`.
- `scripts/audit-agent-readiness.sh`: report-first audit of harness
readiness. Currently `10 ok, 0 warn, 0 fail`.
- `scripts/check_architecture.sh` / `make lint/architecture`: umbrella
architecture-lint target. Consolidates the existing
`check_enterprise_imports.sh` and `check_codersdk_imports.sh` so they
run exactly once via the umbrella. Slot is open for new high-confidence
rules.

**Structured CI failure summaries**
- `scripts/playwright-failure-summary.sh`: parses
`site/test-results/results.json` and writes Markdown to
`$GITHUB_STEP_SUMMARY` on failure. Wired into the `test-e2e` matrix job.
- `scripts/go-test-failure-summary.sh`: parses `go test -json`
line-delimited output the same way. Wired into `test-go-pg`,
`test-go-pg-17`, and `test-go-race-pg` by injecting `gotestsum
--jsonfile` in the workflow without touching `Makefile`. JSON also
uploaded as a CI artifact on failure.
- `site/e2e/playwright.config.ts`: enables `screenshot:
only-on-failure`, `trace: retain-on-failure`, JSON reporter, and HTML
reporter alongside existing reporters.
- `.github/workflows/ci.yaml`: failure artifact uploads for Playwright
now use `if: failure()` and predictable names
(`playwright-artifacts-<variant>-<sha>`).

**Per-worktree dev-server isolation** (`scripts/develop/main.go`)
- Deterministic FNV-64a hash of the worktree path produces a port offset
in `[0, 1000)` (50 buckets, step 20 to avoid API/proxy overlap across
adjacent buckets).
- Offset is applied only to defaults; both env vars (`CODER_DEV_PORT`,
`CODER_DEV_WEB_PORT`, `CODER_DEV_PROXY_PORT`,
`CODER_DEV_PROMETHEUS_PORT`) and CLI flags retain priority.
- Hardcoded ports `9090` (embedded Prometheus UI) and `12345` (Delve)
are unchanged by design.
- Startup banner shows each port's source: `default`, `offset`, or
`explicit`.
- Unit tests in `scripts/develop/main_test.go` cover determinism,
bounds, no-overlap across the four ports, and explicit-skip behavior.
- State (`.coderv2/`) was already worktree-isolated via `os.Getwd()`, so
no state-dir changes were needed.

## Validation

`make lint/agents`, `make lint/architecture`, `make lint/emdash`, `bash
scripts/audit-agent-readiness.sh` (10 ok, 0 warn, 0 fail), `shellcheck`
on all 5 new scripts, `go test ./scripts/develop/...`, and `js-yaml`
parse of `ci.yaml` all pass. Synthetic fixtures verify both
failure-summary scripts handle empty/missing input (silent exit 0),
ANSI-stripped output, and parent/subtest formatting.

## Known follow-ups (deferred)

- Frontend Storybook/Vitest failure summary: lowest-leverage slice of
the failure-summary work. Skipping until observed pain.
- Architecture lint currently only delegates to existing import checks;
new rules (`InTx` outer-store detection, swagger-annotation lint) plug
in as needed.
- 50 port-offset buckets means two worktree paths can occasionally
collide. The DEV_ISOLATION doc tells users to set the relevant env var
when this happens.

> Mux opened this PR on Mike's behalf.
2026-05-11 17:27:29 +02:00
Kayla はな 162acaf8bf feat: update UsersPage role editing to match new designs (#24857) 2026-05-04 11:19:21 -06:00
Jaayden Halko 6149fc3619 feat(site): add colorblind-friendly themes for protan/deuter and tritan (#24672)
This is part 1 to lay the foundation for the theme changes.

Part 2 which adds the UI implementation is in this PR,
https://github.com/coder/coder/pull/24680

-----------------

Adds four sitewide colorblind-friendly theme palettes alongside the
existing
light and dark themes. The palettes retune the red/green and blue/yellow
semantic axes so success/error, warning, and diff additions/deletions
remain distinguishable under the most common color vision deficiencies.

| Preference ID | Purpose |
|---|---|
| `dark-protan-deuter` / `light-protan-deuter` | Protanopia &
deuteranopia (red/green). Success and additions shift to sky-blue;
destructive and deletions shift to vermilion/orange; warning shifts to
fuchsia so warning and destructive states do not collapse onto the same
hue. |
| `dark-tritan` / `light-tritan` | Tritanopia (blue/yellow). Warning
shifts from amber to fuchsia; red/green semantic pair is preserved. |

The diff panel and every semantic role (`success`, `error`, `warning`,
`notice`, `danger`) pick up the new palette automatically because they
consume the sitewide CSS variables in `site/src/index.css`. No backend
or database change is required: `theme_preference` is already a
free-form `text` column.

The existing `"dark"`, `"light"`, and `"auto"` preferences are
unchanged.

This PR ships the palettes and the resolution machinery
(`CONCRETE_THEMES`, `resolveThemeName`, `isConcreteThemeName`, and the
ThemeProvider/AgentEmbedPage plumbing). It intentionally does **not**
add UI to select the new themes; the follow-up PR #24680 adds a Theme
mode dropdown (Sync with system / Single theme) that exposes every
concrete theme, including the four added here.

Produced with Coder Agents assistance.

<details>
<summary>Implementation plan and decision log</summary>

Full plan (investigation, file layout, TDD phases, open risks) is
attached to the chat that produced this PR.

Key decisions:

- **Sitewide, not agent-scoped.** The diff panel already consumes the
sitewide theme via CSS variables. Keeping the change at the user
appearance layer also fixes red/green accents in alerts, badges, and
build states in one change, and matches how comparable products (e.g.
GitHub) ship this feature.
- **No backend change.** `codersdk/users.go` already accepts any
`theme_preference` string, and `ThemeProvider` now has a shared resolver
(`resolveThemeName`) that maps any persisted value to a concrete theme,
tolerating unknowns and the legacy `"auto"` value.
- **Palette provenance.** The protan/deuter palette is inspired by
CVD-safe blue/orange palettes and tuned within the existing Tailwind
color scales; the tritan palette keeps red/green semantics intact and
shifts warning to fuchsia. This PR does not claim exact Okabe-Ito or
WCAG AA derivation without recorded contrast data.
- **UI deferred.** Selecting the new themes is gated on the follow-up PR
#24680 which replaces the flat theme grid with a Theme mode dropdown.

</details>
2026-05-04 14:02:58 +01:00
Zach 79735f2d45 feat: plumb user secrets through provisioner chain to terraform (#24542)
This change passes user secrets from coderd to the Terraform process at
workspace build time so the `data.coder_secret` data source in
terraform-provider-coder can resolve values at plan time.

Secrets traverse two proto hops: `provisionerdserver` fetches them
via`ListUserSecretsWithValues`, attaches them to
`AcquiredJob.WorkspaceBuild.user_secrets` on `provisionerd.proto`;
`runner.go` forwards into `PlanRequest.user_secrets` on
`provisioner.proto`; the Terraform provisioner encodes each as
`CODER_SECRET_ENV_<name>` or `CODER_SECRET_FILE_<hex(path)>` before
invoking `terraform plan`. Only plan requests carry secrets; apply runs
with `nil` because values are baked into plan state.

Fetch is gated on a workspace transitioning to start. stop and delete
transitions never carry secrets, so revoking or deleting a stored secret
cannot make a workspace unstoppable. DB errors on the fetch fail the job
outright rather than silently continuing with an empty secret set.

Note that user secrets will be stored in the workspace_builds table in
provisioner_state with other Terraform state (including other sensitive data).
2026-04-27 08:26:07 -06:00
Seth ShelnuttandJakub Domeracki 66abd8a271 fix(site): require confirmation before executing terminal command from URL (#24650)
The terminal page auto-executed commands from the `?command=` query
parameter
on page load without user confirmation. Because session auth uses
`SameSite=Lax`
cookies, an attacker could craft a link (phishing email, Slack DM,
external page)
that executes arbitrary commands in a victim's workspace when clicked.

Adds a `ConfirmDialog` that shows the exact command and requires
explicit user
approval before it is passed to the terminal WebSocket. Canceling
removes the
`command` parameter from the URL and opens a plain terminal.

<details>
<summary>Implementation details</summary>

### Data flow (before)

`TerminalPage.tsx` reads `searchParams.get("command")` and passes it
directly
as `initialCommand` to `WorkspaceTerminal`, which embeds it in the
WebSocket
URL. `proxy.go` forwards it to the agent, which runs `bash -c
"<command>"`
immediately.

### Fix

- Added `commandConfirmed` state and `commandPendingConfirmation` flag
in
  `TerminalPage.tsx`.
- The `loading` prop passed to `WorkspaceTerminal` includes
`commandPendingConfirmation`, keeping the terminal in loading state
until
  the user confirms or cancels.
- The command is only passed as `initialCommand` after the user clicks
  "Run command" in the confirmation dialog.
- Trusted `?app=` commands (resolved from agent apps) bypass the dialog.
- Cancel removes the `?command=` parameter from the URL entirely.
- No backend changes needed; the frontend gates the command before it
  reaches the WebSocket.

### Terminal focus after dialog

`WorkspaceTerminal`'s autoFocus effect previously depended on
`[terminal, isVisible, autoFocus]` but not `loading`. It fired while the
Radix dialog's focus trap was active, so `terminal.focus()` was
intercepted. When `loading` became false after confirming the dialog,
the
effect did not re-fire. Fixed by adding `loading` to the effect deps and
skipping focus while `loading` is true.

### Files changed

| File | Change |
|------|--------|
| `site/src/pages/TerminalPage/TerminalPage.tsx` | Confirmation dialog,
`commandPendingConfirmation` in loading prop |
| `site/src/pages/TerminalPage/TerminalCommandConsentDialog.tsx` | New
dialog component |
| `site/src/pages/TerminalPage/TerminalCommandConsentDialog.stories.tsx`
| Storybook story for dialog |
| `site/src/pages/TerminalPage/TerminalPage.stories.tsx` |
`CommandConfirmation` story |
| `site/src/pages/TerminalPage/TerminalPage.test.tsx` | 4 new dialog
tests, `renderTerminalRaw` helper for non-blocking render |
| `site/src/modules/terminal/WorkspaceTerminal.tsx` | Add `loading` to
autoFocus effect deps |
| `site/e2e/helpers.ts` | Dismiss dialog in `openTerminalWindow` helper
|
| `site/e2e/tests/webTerminal.spec.ts` | Wait for
`data-status="connected"` + click terminal for focus |

</details>

> 🤖 Generated by Coder Agents

---------

Co-authored-by: Jakub Domeracki <jakub@coder.com>
2026-04-27 15:11:45 +02:00
Jake Howell 2b8a2c9c5d fix(site/e2e): wait for file upload before submitting create template form (#24548)
> 🤖 This PR was written by Coder Agent on behalf of Jake Howell

Fixes https://github.com/coder/internal/issues/1419

The `createTemplate` e2e helper clicks Save immediately after
`setInputFiles`, but the file upload API call is fire-and-forget through
the React component chain (`FileUpload.onChange` →
`CreateTemplateForm.onUpload` → `UploadTemplateView.onUpload` — none of
which `await` the returned promise up the chain).

If the upload mutation has not settled when the form submits,
`uploadedFile` (`uploadFileMutation.data`) is still `undefined`, and the
`uploadedFile!.hash` non-null assertion throws a `TypeError`. Formik
silently swallows the rejected promise, so the page stays on
`/templates/new` and the URL assertion times out.

The fix waits for the "Remove file" button to appear after
`setInputFiles`. That button only renders when `!isPending && variables`
— i.e. the upload mutation has succeeded and the hash is available.
2026-04-21 23:34:10 +10:00
Jeremy Ruppel 4b755e514a fix(site): stabilize app.spec.ts e2e test (#24400)
The `app` e2e test flakes in CI with `waitForEvent("page")` or
`isVisible()` timeouts when opening a workspace app in a new tab.

The test had several timing hazards: the HTTP server address was read
before the socket finished binding, the popup event listener was
registered after the click, and content visibility used a point-in-time
check with the default 5s timeout.

Rewritten to wait for the server to listen, register the popup promise
before clicking, use `getByRole("link")` for a precise locator, and
replace `isVisible()` with a retrying `toBeVisible({ timeout: 30_000 })`
assertion that tolerates the intermediate `about:blank` page and
app-proxy startup delay. The test body is wrapped in `try/finally` to
ensure the HTTP server is always closed.

Fixes coder/internal#577

> Generated by Coder Agents

---

someone wise once said

> and I really don't know what we could do to make it more reliable
other than maybe trying to just rewrite it from scratch.
2026-04-20 10:21:36 -04:00
Danielle Maywood aede045549 chore: bump @biomejs/biome from 2.2 to 2.4.10 (#24074) 2026-04-07 12:22:18 +01:00
Ben Potter 3a4a0b7270 fix: rename "Add member" to "Add" on template permissions page (#23943)
The "Add member" button on the template permissions page is used to add
both **users and groups**, so the label is misleading when adding a
group.

<img width="1238" height="672" alt="image"
src="https://github.com/user-attachments/assets/dbdfc79e-9e2e-4f26-9258-418f2038511e"
/>
2026-04-01 14:46:18 -06:00
Asher 308053b0e4 fix: stop workspace before starting with new parameters (#23541)
This is required to prevent the agent from becoming unhealthy.

Since we are stopping the workspace now, also add a confirmation dialog.

Also add stories to test the new behavior and make a tweak to the
permissions query in support of that.
2026-04-01 10:00:03 -08:00
Kayla はな 4fab372bdc chore: modernize utils/ imports (#23698) 2026-03-26 16:20:25 -06:00
Ethan 5112ab7da9 fix(site/e2e): fix flaky updateTemplate test expecting transient URL (#23655)
_PR generated by Mux but reviewed by a human_

## Problem

The e2e test `template update with new name redirects on successful
submit` is flaky.

After saving template settings, the app navigates to
`/templates/<name>`, which immediately redirects to
`/templates/<name>/docs` via the router's index route (`<Navigate
to="docs" replace />`). The assertion used `expect.poll()` with
`toHavePathNameEndingWith(`/${name}`)`, which matches only the
**transient intermediate URL** — it only exists while `TemplateLayout`'s
async data fetch is pending. Once the fetch resolves and the `<Outlet
/>` renders, the index route fires the `/docs` redirect and the URL no
longer matches.

## Why it's flaky (not deterministic)

The flakiness depends on whether the template query cache is warm:

- **Cache miss → PASSES**: The mutation's `onSuccess` handler
invalidates the query cache. If `TemplateLayout` needs to re-fetch, it
shows a `<Loader />`, which delays rendering the `<Outlet />` that
contains the `<Navigate to="docs">`. This gives `expect.poll()` time to
see the transient `/new-name` URL → **pass**.
- **Cache hit → FAILS**: If the template data is still in the query
client, `TemplateLayout` renders immediately and the `<Navigate
to="docs" replace />` fires nearly instantly. By the time the first poll
runs, the URL is already `/new-name/docs` → **fail**.

## Fix

Assert the **final stable URL** (`/${name}/docs`) instead of the
transient one.

This is safe because `expect.poll()` is retry-based: it keeps sampling
until a match is found (or timeout). Seeing the transient `/new-name`
URL just causes harmless retries — once the redirect completes and the
URL settles on `/new-name/docs`, the poll matches and the test passes.

| Poll | URL | Ends with `/new-name/docs`? | Action |
|---|---|---|---|
| 1st | `/templates/new-name` | No | Retry |
| 2nd | `/templates/new-name` | No | Retry |
| 3rd | `/templates/new-name/docs` | Yes | **Pass** ✅ |

Closes https://github.com/coder/internal/issues/1403
2026-03-26 04:32:44 +00:00
Kayla はな 0e69e0eaca chore: modernize typescript api client/types imports (#23637) 2026-03-25 15:21:19 -06:00
Jeremy RuppelandCopilot Autofix powered by AI 13703fb5aa fix: use auto-retrying assertion for bool parameter verification (#23315)
## Problem

Flaky e2e test `create workspace and overwrite default parameters` — the
boolean parameter verification reads `"true"` when it should be
`"false"`.

`verifyParameters` in `site/e2e/helpers.ts` used a one-shot
`isChecked()` for boolean parameters (line 214), while the
`string`/`number` path used Playwright's auto-retrying `toHaveValue()`
with a 15-second timeout. When the settings/parameters page hydrates
with React Query data, the Switch can briefly render the default value
(`true`) before settling on the override (`false`). The one-shot check
captures the stale state.

## Fix

Replace the one-shot `isChecked()` + `expect().toEqual()` with
Playwright's auto-retrying `toBeChecked()` / `not.toBeChecked()`
assertions using a 15-second timeout, matching the pattern already used
for string/number parameters.

Fixes coder/internal#1414

Authored by coder agent 🤖

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-20 15:21:26 -04:00
Kacper Sawicki 1e07ec49a6 feat: add merge_strategy support for coder_env resources (#23107)
## Description

Implements the server-side merge logic for the `merge_strategy`
attribute added to `coder_env` in [terraform-provider-coder
v2.15.0](https://github.com/coder/terraform-provider-coder/pull/489).
This allows template authors to control how duplicate environment
variable names are combined across multiple `coder_env` resources.

Relates to https://github.com/coder/coder/issues/21885

## Supported strategies

| Strategy | Behavior |
|----------|----------|
| `replace` (default) | Last value wins — backward compatible |
| `append` | Joins values with `:` separator (e.g. PATH additions) |
| `prepend` | Prepends value with `:` separator |
| `error` | Fails the build if the variable is already defined |

## Example

```hcl
resource "coder_env" "path_tools" {
  agent_id       = coder_agent.dev.id
  name           = "PATH"
  value          = "/home/coder/tools/bin"
  merge_strategy = "append"
}
```

## Changes

- **Proto**: Added `merge_strategy` field to `Env` message in
`provisioner.proto`
- **State reader**: Updated `agentEnvAttributes` struct and proto
construction in `resources.go`
- **Merge logic**: Added `mergeExtraEnvs()` function in
`provisionerdserver.go` with strategy-aware merging for both agent envs
and devcontainer subagent envs
- **Tests**: 15 unit tests covering all strategies, edge cases (empty
values, mixed strategies, multiple appends)
- **Dependency**: Bumped `terraform-provider-coder` v2.14.0 → v2.15.0
- **Fixtures**: Updated `duplicate-env-keys` test fixtures and golden
files

## Ordering

When multiple resources `append` or `prepend` to the same key, they are
processed in alphabetical order by Terraform resource address (per the
determinism fix in #22706).
2026-03-18 15:43:28 +01:00
Kayla はな 49e5547c22 feat: add support for creating service accounts (#23140) 2026-03-17 15:36:20 -06:00
Mathias Fredriksson f46692531f fix(site/e2e): increase webServer timeout to 120s (#22731)
The Playwright e2e `webServer` starts the Coder server via
`go run -tags embed`, which must compile before serving. The default 60s
timeout leaves no margin when the CI runner is slow.

Failed run:
https://github.com/coder/coder/actions/runs/22782592241/job/66091950715

Successful run:
https://github.com/coder/coder/actions/runs/22782107623/job/66090828826

The server started and printed its banner, but with only ~4s left on the
clock the health check (`/api/v2/deployment/config`) could not complete
before the timeout fired. The same ~2x slowdown shows in the
`make site/e2e/bin/coder` step (45s vs 67s), confirming this is runner
performance variability.

Increase timeout to 120s.

Refs #22727
2026-03-09 19:06:45 +00:00
Jake Howell dba688662c feat: add <ErrorAlert /> debug details (#22462)
Closes #22140

Short simple and sweet PR to add a bunch of details to our `<Alert />`
stacks. This means we aren't simply asking the user to read the
developer console and surface things easier.

- Implement `Response data` and `Stack trace` `<details />`
- Fix overflow in `ErrorAlert` debug accordions so long `Response data`
and `Stack Trace` content stays inside the alert.
- Add horizontal scroll wrappers around both `<pre>` blocks used in
debug details.
- Update `Alert` layout with `min-w-0` on flex containers so nested
content can shrink correctly and internal scrolling works as intended.

<img width="739" height="550" alt="preview-validation"
src="https://github.com/user-attachments/assets/a6f890d3-8f1f-4fd6-b9d0-882838db04a4"
/>
2026-03-06 03:48:01 +11:00
Jeremy Ruppel f07fdce20a flake: add page event logging to e2e tests (#22569)
I'm having a hard time reproducing [this
Heisenbug](https://github.com/coder/internal/issues/1154) in PR CI, but
it seems to happen pretty often on main, so I would like to add some
logging for a few more page events to the ones we already have.
2026-03-04 07:39:20 -05:00
Jeremy Ruppel b73f21662b flake: verify parameters in parallel in e2e tests (#22557)
This is an attempt to address coder/internal#1154

Tests appear to fail often on `verifyParameters`, which asserts input
visibility and value in series for all expected parameters. This change
makes the same assertions in parallel, hopefully completing before
timeout.
2026-03-03 14:56:41 -05:00
Jaayden Halko 3daac86efe refactor(site): add tabbed layout for single group page (#22486)
## Summary

Replaces the standalone **Settings** button on the single-group page
with a tabbed layout containing **Group members** and **Group settings**
tabs.

This uses the new figma designs here:
https://www.figma.com/design/klGTlHSPQwI4KBvAMdebrx/Customer-Usage-Controls-for-AI-Governance-Add-On?node-id=51-4907&m=dev

<img width="797" height="371" alt="Screenshot 2026-03-02 at 22 53 28"
src="https://github.com/user-attachments/assets/88d2ca8e-928f-404d-8569-ec4aba6c2ce4"
/>



### What changed

| File | Change |
|------|--------|
| `site/src/router.tsx` | Nested `settings` route under `:groupName`
layout route; added `GroupMembersPage` lazy import |
| `site/src/pages/GroupsPage/GroupPage.tsx` | Converted to shared
layout: header + tabs (`Tabs`/`TabsList`/`TabLink`) + `<Outlet />` with
context. Removed settings button and member-management code |
| `site/src/pages/GroupsPage/GroupMembersPage.tsx` | **New file** —
extracted member-management UI (add/remove members, member table) from
GroupPage; consumes data via `useOutletContext` |
| `site/src/pages/GroupsPage/GroupSettingsPage.tsx` | Switched from
independent group query to outlet context; removed duplicate
loading/error/title handling |
| `site/src/pages/GroupsPage/GroupSettingsPageView.tsx` | Removed
duplicate `ResourcePageHeader`; renders only the settings form |
| `site/e2e/tests/organizationGroups.spec.ts` | Updated selectors from
`"Settings"` link to `"Group settings"` link |

### How it works

- `:groupName` route now renders `GroupPage` as a **layout route** with
header, tabs, and `<Outlet />`.
- Index child route renders `GroupMembersPage` (member table +
add/remove).
- `settings` child route renders `GroupSettingsPage` (group settings
form).
- Shared group data + permissions are passed via React Router outlet
context, eliminating duplicate queries.
- URL structure is unchanged: `/organizations/:org/groups/:groupName`
(members) and `.../settings` (settings).

### Verification

- `pnpm exec tsc --noEmit` — passes
- `pnpm exec biome check --error-on-warnings` on all touched files —
passes
2026-03-02 18:12:40 +00:00
blinkagent[bot]andblink-so[bot] 8bb80b060e fix(e2e): fix flaky verifyParameters assertion in updateWorkspace test (#22413)
## Problem

The `update workspace, new required, mutable parameter added` e2e test
has been flaking consistently
([internal#1328](https://github.com/coder/internal/issues/1328)). The
error:

```
Error: Timed out 5000ms waiting for expect(locator).toHaveValue(expected)
Locator: getByTestId('parameter-field-Sixth parameter').locator('input')
Expected string: "99"
Received string: ""
```

## Root Cause

A race between page navigation and data hydration in `verifyParameters`:

1. The page navigates with `waitUntil: "domcontentloaded"` which does
not wait for API responses to settle
2. React Query may serve stale cached workspace data initially (from
before the update), causing the form to render with empty/old parameter
values
3. The `toHaveValue` assertion uses the default `actionTimeout` of
5000ms which isn't enough time for fresh data to arrive and the form to
re-render

## Fix

- Switch `verifyParameters` navigation to `waitUntil: "networkidle"` to
ensure API responses (workspace data, build parameters) are settled
before the form renders
- Increase the `toHaveValue` timeout to 15s to handle cases where
dynamic parameters hydrate slowly after initial render

Fixes coder/internal#1328

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2026-02-27 16:32:16 -05:00
edee917d88 feat: add experimental agents support (#22290)
feat: add AI chat system with agent tools and chat UI

Introduce the chatd subsystem and Agents UI for AI-powered chat
within Coder workspaces.

- Add chatd package with chat loop, message compaction, prompt
  management, and LLM provider integration (OpenAI, Anthropic)
- Add agent tools: create workspace, list/read templates, read/write/
  edit files, execute commands
- Add chat API endpoints with streaming, message editing, and
  durable reconnection
- Add database schema and migrations for chats, chat messages, chat
  providers, and chat model configs
- Add RBAC policies and dbauthz enforcement for chat resources
- Add Agents UI pages with conversation timeline, queued messages
  list, diff viewer, and model configuration panel
- Add comprehensive test coverage including coderd integration tests,
  chatd unit tests, and Storybook stories
- Gate feature behind experiments flag

---------

Co-authored-by: Cian Johnston <cian@coder.com>
Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
Co-authored-by: Jeremy Ruppel <jeremy@coder.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:50:56 +00:00
Jake HowellandKayla はな 15a2bab1cd feat: migrate from <GlobalSnackbar /> to sonner (#22258)
Replaces our custom `<GlobalSnackbar />` (MUI Snackbar + event emitter)
with [`sonner`](https://github.com/emilkowalski/sonner). Deletes
`GlobalSnackbar/`, the custom event emitter infra, and migrates ~80
source files to `toast.success()` / `toast.error()` from `sonner`.

- ~47 error toasts now surface API error detail via
`getErrorDetail(error)` in the toast description, not just a generic
message. Coincides with #22229.
- Toast messages follow an `{Action} "{entity}" {result}.` format (e.g.
`User "alice" suspended successfully.`) since toasts persist across
navigation now.
- 17 uses of `toast.promise()` for loading → success → error lifecycle.
- Some toasts include action buttons for quick navigation (e.g. "View
task", "View template").
- Multiple toasts can stack and display simultaneously.

---------

Co-authored-by: Kayla はな <mckayla@hey.com>
2026-02-26 02:42:34 +11:00
Kacper SawickiandJake Howell 60e3ab7632 feat(site)!: add consent prompt for auto-creation with prefilled parameters (#22011)
### Summary

Workspace created via mode=auto links now require explicit user
confirmation before provisioning. A warning dialog shows all prefilled
param.* values from the URL and blocks creation until the user clicks
`Confirm and Create`. Clicking `Cancel` falls back to the standard form
view.

<img width="820" height="475" alt="auto-create-consent-dialog"
src="https://github.com/user-attachments/assets/8339e3bd-434f-4a04-9385-436bf95f49d7"
/>

### Breaking behavior change

Links using `mode=auto` (e.g., "Open in Coder" buttons) will no longer
silently create workspaces. Users will now see a consent dialog and must
explicitly confirm before the workspace is provisioned. Any existing
integrations or automation relying on `mode=auto` for seamless workspace
creation will now require manual user interaction.

---------

Co-authored-by: Jake Howell <jacob@coder.com>
2026-02-12 15:39:02 +01:00
ケイラ 52af6eac68 fix: extend app test timeout (#21934) 2026-02-09 15:02:41 -07:00
Danielle Maywood 1a94aa67a3 feat(provisioner): associate resources with coder_devcontainer (#21602)
Closes https://github.com/coder/internal/issues/1239

Allow associating `coder_env`, `coder_script` and `coder_app` with
`coder_devcontainer` resource. To do this we make use of the newly added
`subagent_id` field in the `coder_devcontainer` resource added in
https://github.com/coder/terraform-provider-coder/pull/474
2026-01-29 00:01:30 +00:00
Steven Masley d2044c2ee9 chore: update protobuf to reuse file request (#21447)
**This is just the protobuf changes for the PR https://github.com/coder/coder/pull/21398**

Moved `UploadFileRequest` from `provisionerd.proto` -> `provisioner.proto`.

Renamed to `FileUpload` because it is now bi-directional.

This **is backwards compatible**. I tested it to confirm the payloads are identical.  Types were just renamed and moved around.

```golang
func TestTypeUpgrade(t *testing.T) {
	t.Parallel()

	x := &proto2.UploadFileRequest{
		Type: &proto2.UploadFileRequest_ChunkPiece{
			ChunkPiece: &proto.ChunkPiece{
				Data:         []byte("Hello World!"),
				FullDataHash: []byte("Foobar"),
				PieceIndex:   42,
			},
		},
	}

	data, err := protobuf.Marshal(x)
	require.NoError(t, err)

	// Exactly the same output
	// EhgKDEhlbGxvIFdvcmxkIRIGRm9vYmFyGCo= on `main`
	// EhgKDEhlbGxvIFdvcmxkIRIGRm9vYmFyGCo= on this branch
	fmt.Println(base64.StdEncoding.EncodeToString(data))
}
```


# What this does

This allows provisioner daemons to download files from `coderd`'s `files` table. This is used to send over cached module files and prevent the need of downloading these modules on each workspace build.
2026-01-09 11:23:32 -06:00
Steven Masley 89f4d60e7b chore: remove experiment "terraform-directory-reuse" (#21397)
Experiment is no longer required, the new method will be released without an experiment and without a toggle

Main PR is: https://github.com/coder/coder/pull/21398
2026-01-09 11:13:16 -06:00
Steven Masley 61d7d2983f fix: remove state information from apply (#21373)
Delete builds were not deleting resources as the tf state being sent in the apply request was empty.
State removed from apply request and read from the session instead.
2025-12-22 16:18:53 +00:00
Steven Masley 3194bcfc9e chore: distinct operations for provisioner's 'parse', 'init', 'plan', 'apply', 'graph' (#21064)
Provisioner steps broken into smaller granular actions.
Changes:
- `ExtractArchive` moved to `init` request (was in `configure`)
- Writing `tfstate` moved to `plan` (was in `configure`)
- Moved most plan/apply outputs to `GraphComplete`
2025-12-15 11:26:41 -06:00
Ethan 782a1052c8 test: use toHaveValue() to avoid flaky parameter verification (#20990)
## Problem

The test `create workspace with default and required parameters` was
flaky because `verifyParameters` in `site/e2e/helpers.ts` didn't wait
for input values to be populated before asserting.

After PR #20710 removed classic parameters, the form now uses dynamic
parameters loaded asynchronously via WebSocket. The input field can be
visible before its value is populated.

Closes https://github.com/coder/internal/issues/1154

## Fix

Replace immediate read + assertion:
```typescript
const value = await parameterField.inputValue();
expect(value).toEqual(buildParameter.value);
```

With Playwright's auto-retrying assertion:
```typescript
await expect(parameterField).toHaveValue(buildParameter.value);
```

From [Playwright docs for
`inputValue()`](https://playwright.dev/docs/api/class-locator#locator-input-value):

> **NOTE** If you need to assert input value, prefer
`expect(locator).toHaveValue(value[, options])` to avoid flakiness. See
assertions guide for more details.

---

This PR was fully generated by [mux](https://github.com/coder/mux), and
reviewed by a human.
2025-12-01 13:46:19 +11:00