Compare commits

...

319 Commits

Author SHA1 Message Date
Danny Kopping 41eed1dd0d chore: backport aibridge fix (#20731)
Backports https://github.com/coder/coder/pull/20730 to 2.27

2.27 is based on coder/aibridge@v0.1.3, but the fix is now in v0.1.7. We
don't want to bring in all those changes now, so I've created a v0.1.3.1
tag which just has the change cherry-picked:
https://github.com/coder/aibridge/compare/v0.1.3..v0.1.3.1

The pseudo-version `v0.1.4-0.20251112094427-5899d515872f` was set by `go
get github.com/coder/aibridge@v0.1.3.1` because this new commit does not
exist in main.

Signed-off-by: Danny Kopping <danny@coder.com>
2025-11-12 09:31:36 -06:00
Spike Curtis 2cbb86200c fix: use correct slog arguments (#20721) (#20722)
Fixes a bad slog.Error() command that didn't wrap the error in
`slog.Error`

(cherry picked from dc21699)
2025-11-12 00:20:17 +04:00
Cian Johnston 483f4d5efb chore: update Go to 1.24.10 (#20684) (#20689)
(cherry picked from commit 81c3375670)

Signed-off-by: Danny Kopping <danny@coder.com>
Co-authored-by: Danny Kopping <danny@coder.com>
2025-11-10 10:55:12 -06:00
david-fraley 800dd9cc66 fix: fix incorrect rendering of RBAC in Helm chart when workspacePerm… (#20596)
## Description

Cherry Pick commit of https://github.com/coder/coder/pull/20569 for
@rowansmithau

Co-authored-by: Rowan Smith <rowan@coder.com>
2025-10-30 14:32:36 -05:00
Dean Sheather 035ad33faf fix: initialize pseudo console with default size for SSH sessions [2.27] (#20490)
> Resolved an invalid parameter error (-2147024809) during PTY creation
on Windows 11 22H2 (but not only) when connecting via JetBrains Toolbox
which spawns the native SSH client with `-tt` forcing PTY allocation
even though there is no "terminal" on the client side to query its size.
>
> CreatePseudoConsole doesn't accept a 0x0 (zero width and zero height)
console size and unfortunately, there is NO explicit documentation in
the official Microsoft documentation that states the minimum valid
values or explicitly prohibits 0x0.
>
> Looking at real-world implementations in the search results, all
examples use reasonable non-zero values.
>
> I tested this with a local Windows VM registered to dev.coder.com i.e.
externally managed workspace.

Relates to #20468

Co-authored-by: Faur Ioan-Aurel <fioan89@gmail.com>
2025-10-28 22:47:10 +11:00
Danielle Maywood 230b55bfa0 chore: upgrade coder/clistat to v1.1.1 (#20322) (#20325)
coder/clistat has received a handful of bug fixes so we're back-porting
these bug fixes to 2.27

---

Cherry-picked from
https://github.com/coder/coder/commit/9bef5de30d8921bdeeec5ff02f14fdd7d1d932d7
2025-10-16 15:29:09 +01:00
Cian Johnston b2d6a18861 fix(coderd): truncate task prompt to 160 characters in notifications (#20147) (#20153)
Truncates the task prompt used in notifications to a maximum of 160
characters. The length of 160 characters was chosen arbitrarily.

(cherry picked from commit ffcb7a1693)
2025-10-09 07:38:34 +01:00
Dean Sheather c0cd32c2c4 chore: fix missing variable in deploy workflow (#20135) 2025-10-02 14:15:52 +10:00
Dean Sheather c2414d5287 chore: backport release freeze workflow to 2.27 (#20132)
Relates to https://github.com/coder/dogfood/pull/189
Relates to https://github.com/coder/internal/issues/1021

- Adds new script `scripts/should_deploy.sh` which implements the
algorithm in the linked issue
- Changes the `ci.yaml` workflow to run on release branches
- Moves the deployment steps out of `ci.yaml` into a new workflow
`deploy.yaml` for concurrency limiting purposes
- Changes the behavior of image tag pushing slightly:
    - Versioned tags will no longer have a `main-` prefix
    - `main` branch will still push the `main` and `latest` tags
    - `release/x.y` branches will now push `release-x.y` tags
- The deploy job will exit early if `should_deploy.sh` returns false
- The deploy job will now retag whatever image it's about to deploy as
`dogfood`

(cherry picked from commit e5c8c9bdaf)
2025-10-02 12:45:22 +10:00
Dean Sheather ff69ed69df chore: backport various downgrades from main (#20133)
JS downgrades + make `changes` a required job

---------

Co-authored-by: Bruno Quaresma <bruno@coder.com>
Co-authored-by: ケイラ <mckayla@hey.com>
Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com>
2025-10-02 12:44:43 +10:00
dependabot[bot] f4a6894bf4 chore: bump @radix-ui/react-dialog from 1.1.4 to 1.1.15 in /site (#20068)
Bumps [@radix-ui/react-dialog](https://github.com/radix-ui/primitives)
from 1.1.4 to 1.1.15.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@radix-ui/react-dialog&package-manager=npm_and_yarn&previous-version=1.1.4&new-version=1.1.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:58:26 +00:00
dependabot[bot] 77b95983aa chore: bump @radix-ui/react-select from 2.1.4 to 2.2.6 in /site (#20079)
Bumps [@radix-ui/react-select](https://github.com/radix-ui/primitives)
from 2.1.4 to 2.2.6.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@radix-ui/react-select&package-manager=npm_and_yarn&previous-version=2.1.4&new-version=2.2.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:52:18 +00:00
dependabot[bot] fc6c5a49d6 chore: bump msw from 2.4.8 to 2.11.3 in /site (#20074)
Bumps [msw](https://github.com/mswjs/msw) from 2.4.8 to 2.11.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mswjs/msw/releases">msw's
releases</a>.</em></p>
<blockquote>
<h2>v2.11.3 (2025-09-20)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>migrate to <code>until-async</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2590">#2590</a>)
(7087b1e29eb7ca0a414eff36ed3c98d03147044b) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.11.2 (2025-09-10)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>setupWorker:</strong> handle in-flight requests after
calling <code>worker.stop()</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2578">#2578</a>)
(97cf4c744d9b1a17f42ca65ac8ef93b2632b935b) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.11.1 (2025-08-31)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>use <code>tough-cookie@6</code> directly (<a
href="https://redirect.github.com/mswjs/msw/issues/2453">#2453</a>)
(6ce3b9511821e997a8e21f1d7578bbf824a3deb6) <a
href="https://github.com/sampsonjoliver"><code>@​sampsonjoliver</code></a>
<a href="https://github.com/SamMousa"><code>@​SamMousa</code></a> <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
<li><strong>HttpResponse:</strong> infer provided body type for
<code>HttpResponse.arrayBuffer</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2575">#2575</a>)
(ad3d7c1c130a38bf9f5e5886782b725d2aa53745) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.11.0 (2025-08-30)</h2>
<h3>Features</h3>
<ul>
<li>custom request predicate function (<a
href="https://redirect.github.com/mswjs/msw/issues/2541">#2541</a>)
(a05405bd5ba7c82ad3e80990c176cbf6cb94dee7) <a
href="https://github.com/ytoshiki"><code>@​ytoshiki</code></a> <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.10.5 (2025-08-13)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>export the <code>GraphQLResponseBody</code> type (<a
href="https://redirect.github.com/mswjs/msw/issues/2565">#2565</a>)
(642523d3258bd1996df986ea0c9270be150bec3d) <a
href="https://github.com/breadadams"><code>@​breadadams</code></a></li>
</ul>
<h2>v2.10.4 (2025-07-12)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>HttpHandler:</strong> use correct query parameters docs link
(<a href="https://redirect.github.com/mswjs/msw/issues/2547">#2547</a>)
(6cdce81de5576e5049899a729ab3a1424550c003) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.10.3 (2025-07-04)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>ws:</strong> support <code>resolutionContext</code> on
<code>parse</code> and <code>run</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2544">#2544</a>)
(024568571990b6068601a0ba9f03e143ccbbfffb) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
<li><strong>getResponse:</strong> support <code>resolutionContext</code>
argument (<a
href="https://redirect.github.com/mswjs/msw/issues/2543">#2543</a>)
(ce3ab1fdd3b353d6a1d8db3c69532bde44483a8a) <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<h2>v2.10.2 (2025-06-09)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>TypeScript:</strong> support <code>Response.error()</code>
and <code>HttpResponse.error()</code> as mocked responses (<a
href="https://redirect.github.com/mswjs/msw/issues/2132">#2132</a>)
(72cc8ddac8f030f747b674148b03e5a025e412d2) <a
href="https://github.com/jacquesg"><code>@​jacquesg</code></a> <a
href="https://github.com/kettanaito"><code>@​kettanaito</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mswjs/msw/commit/3f7641a52abf94bae71a100fe7293a7812ab0952"><code>3f7641a</code></a>
chore(release): v2.11.3</li>
<li><a
href="https://github.com/mswjs/msw/commit/9e6712e7c3b36366e23264608beb19e3760df251"><code>9e6712e</code></a>
test: fix <code>body-stream</code> test rough performance equal</li>
<li><a
href="https://github.com/mswjs/msw/commit/7087b1e29eb7ca0a414eff36ed3c98d03147044b"><code>7087b1e</code></a>
fix: migrate to <code>until-async</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2590">#2590</a>)</li>
<li><a
href="https://github.com/mswjs/msw/commit/df869f4b00b26605d949925e567d32f80133ff1a"><code>df869f4</code></a>
chore(release): v2.11.2</li>
<li><a
href="https://github.com/mswjs/msw/commit/97cf4c744d9b1a17f42ca65ac8ef93b2632b935b"><code>97cf4c7</code></a>
fix(setupWorker): handle in-flight requests after calling
<code>worker.stop()</code> (<a
href="https://redirect.github.com/mswjs/msw/issues/2">#2</a>...</li>
<li><a
href="https://github.com/mswjs/msw/commit/44e15210230fee2685ad63be31ff97161d6734e2"><code>44e1521</code></a>
chore: migrate eslint to flat config (<a
href="https://redirect.github.com/mswjs/msw/issues/2212">#2212</a>)</li>
<li><a
href="https://github.com/mswjs/msw/commit/25d2c8f54f6ca72569799e0d7a1bde62fb9fd931"><code>25d2c8f</code></a>
test(graphql): add <code>TypedDocumentNode</code> inference tests (<a
href="https://redirect.github.com/mswjs/msw/issues/2018">#2018</a>)</li>
<li><a
href="https://github.com/mswjs/msw/commit/5c676b8a22e32c7da15e07d1bcc752190a941240"><code>5c676b8</code></a>
chore(release): v2.11.1</li>
<li><a
href="https://github.com/mswjs/msw/commit/6ce3b9511821e997a8e21f1d7578bbf824a3deb6"><code>6ce3b95</code></a>
fix: use <code>tough-cookie@6</code> directly (<a
href="https://redirect.github.com/mswjs/msw/issues/2453">#2453</a>)</li>
<li><a
href="https://github.com/mswjs/msw/commit/3173e667512061955b8607ed890ef2a45558db77"><code>3173e66</code></a>
refactor: remove unused import &amp; imported multiple times (<a
href="https://redirect.github.com/mswjs/msw/issues/2576">#2576</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/mswjs/msw/compare/v2.4.8...v2.11.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=msw&package-manager=npm_and_yarn&previous-version=2.4.8&new-version=2.11.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:52:14 +00:00
dependabot[bot] 7c4c4abc7b chore: bump the vite group across 1 directory with 3 updates (#20057)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps the vite group with 3 updates in the /site directory:
[@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react),
[vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and
[vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker).

Updates `@vitejs/plugin-react` from 5.0.2 to 5.0.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/releases"><code>@​vitejs/plugin-react</code>'s
releases</a>.</em></p>
<blockquote>
<h2>plugin-react@5.0.4</h2>
<h3>Perf: use native refresh wrapper plugin in rolldown-vite (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/881">#881</a>)</h3>
<h2>plugin-react@5.0.3</h2>
<h3>HMR did not work for components imported with queries with
rolldown-vite (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/872">#872</a>)</h3>
<h3>Perf: simplify refresh wrapper generation (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/835">#835</a>)</h3>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md"><code>@​vitejs/plugin-react</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>5.0.4 (2025-09-27)</h2>
<h3>Perf: use native refresh wrapper plugin in rolldown-vite (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/881">#881</a>)</h3>
<h2>5.0.3 (2025-09-17)</h2>
<h3>HMR did not work for components imported with queries with
rolldown-vite (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/872">#872</a>)</h3>
<h3>Perf: simplify refresh wrapper generation (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/835">#835</a>)</h3>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/450d7df80a52c45f3da7a9612d96a4913f565ed7"><code>450d7df</code></a>
release: plugin-react@5.0.4</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/86388455a0964b0563ab35b3b7410f245d598d54"><code>8638845</code></a>
feat(react): full bundle mode compat (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/882">#882</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/bd1487d9a1396f8e9b92b95cd80c9edb8c736885"><code>bd1487d</code></a>
perf(react): use native refresh wrapper plugin in rolldown-vite (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/881">#881</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/80df894c78169d63d9f1f49d35dd548b6aa53601"><code>80df894</code></a>
docs: remove <code>disableOxcRecommendation</code> from plugin-react
readme (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/880">#880</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/608f266c8d53f41a6b1541de35b218fe2640ec05"><code>608f266</code></a>
chore(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/879">#879</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/3c2ebf89de7f5e40ed0ef932993f7d0b7695719b"><code>3c2ebf8</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/851">#851</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/8293cb38945e56729b5b045b09858da6b78ba3a3"><code>8293cb3</code></a>
release: plugin-react@5.0.3</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/89ec16d68054a767f624d3da5b91cedf2fba1759"><code>89ec16d</code></a>
fix(react): hmr did not work for components imported with queries with
rolldo...</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/3a8b4a9070399bc7518cfcceb64d1518fda1568a"><code>3a8b4a9</code></a>
perf: avoid sourcemap for refresh wrapper injection (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/835">#835</a>)</li>
<li><a
href="https://github.com/vitejs/vite-plugin-react/commit/afa28f1675e8169f6494413b2bb69577b9cbf6f5"><code>afa28f1</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/823">#823</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite-plugin-react/commits/plugin-react@5.0.4/packages/plugin-react">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for <code>@​vitejs/plugin-react</code> since
your current version.</p>
</details>
<br />

Updates `vite` from 7.1.4 to 7.1.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>v7.1.7</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v7.1.7/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v7.1.6</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v7.1.6/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v7.1.5</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v7.1.5/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v7.1.6...v7.1.7">7.1.7</a>
(2025-09-22)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>build:</strong> fix ssr environment <code>emitAssets:
true</code> when <code>sharedConfigBuild: true</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/20787">#20787</a>)
(<a
href="https://github.com/vitejs/vite/commit/4c4583ce7a13306e0853901570c5d95517fe81da">4c4583c</a>)</li>
<li><strong>client:</strong> use CSP nonce when rendering error overlay
(<a
href="https://redirect.github.com/vitejs/vite/issues/20791">#20791</a>)
(<a
href="https://github.com/vitejs/vite/commit/9bc9d1258f550e9d8f5e530cd27aecb1bee32bdb">9bc9d12</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/20811">#20811</a>)
(<a
href="https://github.com/vitejs/vite/commit/9f2247c066cac75746356c9391845235445a154b">9f2247c</a>)</li>
<li><strong>glob:</strong> handle glob imports from folders starting
with dot (<a
href="https://redirect.github.com/vitejs/vite/issues/20800">#20800</a>)
(<a
href="https://github.com/vitejs/vite/commit/105abe87c412cf0f83859ba41fed869221cbb3e0">105abe8</a>)</li>
<li><strong>hmr:</strong> trigger prune event when import is removed
from non hmr module (<a
href="https://redirect.github.com/vitejs/vite/issues/20768">#20768</a>)
(<a
href="https://github.com/vitejs/vite/commit/9f32b1dc710991c53a9f665c8d0d6945f342bf92">9f32b1d</a>)</li>
<li><strong>hmr:</strong> wait for <code>import.meta.hot.prune</code>
callbacks to complete before running other HMRs (<a
href="https://redirect.github.com/vitejs/vite/issues/20698">#20698</a>)
(<a
href="https://github.com/vitejs/vite/commit/98a3484733443ee529870477a6ab6a03572e3cbc">98a3484</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v7.1.5...v7.1.6">7.1.6</a>
(2025-09-18)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/20773">#20773</a>)
(<a
href="https://github.com/vitejs/vite/commit/88af2ae7df77160e7d11a9fa147a4967c8499f13">88af2ae</a>)</li>
<li><strong>esbuild:</strong> inject esbuild helper functions with
minified <code>$</code> variables correctly (<a
href="https://redirect.github.com/vitejs/vite/issues/20761">#20761</a>)
(<a
href="https://github.com/vitejs/vite/commit/7e8e0043d60379e11da481d9cc3c3556c9756ac0">7e8e004</a>)</li>
<li>fallback terser to main thread when nameCache is provided (<a
href="https://redirect.github.com/vitejs/vite/issues/20750">#20750</a>)
(<a
href="https://github.com/vitejs/vite/commit/a679a643404c95556dda2670643e14eca9c585bd">a679a64</a>)</li>
<li><strong>types:</strong> strict env typings fail when
<code>skipLibCheck</code> is <code>false</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/20755">#20755</a>)
(<a
href="https://github.com/vitejs/vite/commit/cc54e294746d3eac868de96f85d98dd0fa0cda11">cc54e29</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/20675">#20675</a>)
(<a
href="https://github.com/vitejs/vite/commit/a67bb5fbec5f3e42151dc7e3166858d0d33533de">a67bb5f</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/20772">#20772</a>)
(<a
href="https://github.com/vitejs/vite/commit/d785e72f2ead705e8b2416c0a5097878fced3435">d785e72</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v7.1.4...v7.1.5">7.1.5</a>
(2025-09-08)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li>apply <code>fs.strict</code> check to HTML files (<a
href="https://redirect.github.com/vitejs/vite/issues/20736">#20736</a>)
(<a
href="https://github.com/vitejs/vite/commit/14015d794f69accba68798bd0e15135bc51c9c1e">14015d7</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/20732">#20732</a>)
(<a
href="https://github.com/vitejs/vite/commit/122bfbabeb1f095ce7cabd30893e5531e9a007c4">122bfba</a>)</li>
<li>upgrade sirv to 3.0.2 (<a
href="https://redirect.github.com/vitejs/vite/issues/20735">#20735</a>)
(<a
href="https://github.com/vitejs/vite/commit/09f2b52e8d5907f26602653caf41b3a56692600d">09f2b52</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vitejs/vite/commit/693d25510aba3322e8e689764df8c3b18db04a98"><code>693d255</code></a>
release: v7.1.7</li>
<li><a
href="https://github.com/vitejs/vite/commit/98a3484733443ee529870477a6ab6a03572e3cbc"><code>98a3484</code></a>
fix(hmr): wait for <code>import.meta.hot.prune</code> callbacks to
complete before runni...</li>
<li><a
href="https://github.com/vitejs/vite/commit/9f32b1dc710991c53a9f665c8d0d6945f342bf92"><code>9f32b1d</code></a>
fix(hmr): trigger prune event when import is removed from non hmr module
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20">#20</a>...</li>
<li><a
href="https://github.com/vitejs/vite/commit/9f2247c066cac75746356c9391845235445a154b"><code>9f2247c</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20811">#20811</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/105abe87c412cf0f83859ba41fed869221cbb3e0"><code>105abe8</code></a>
fix(glob): handle glob imports from folders starting with dot (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20800">#20800</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/4c4583ce7a13306e0853901570c5d95517fe81da"><code>4c4583c</code></a>
fix(build): fix ssr environment <code>emitAssets: true</code> when
`sharedConfigBuild: t...</li>
<li><a
href="https://github.com/vitejs/vite/commit/9bc9d1258f550e9d8f5e530cd27aecb1bee32bdb"><code>9bc9d12</code></a>
fix(client): use CSP nonce when rendering error overlay (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20791">#20791</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/54377f70ad72d56a2c5b4f36a812b5991573d5b1"><code>54377f7</code></a>
release: v7.1.6</li>
<li><a
href="https://github.com/vitejs/vite/commit/88af2ae7df77160e7d11a9fa147a4967c8499f13"><code>88af2ae</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20773">#20773</a>)</li>
<li><a
href="https://github.com/vitejs/vite/commit/d785e72f2ead705e8b2416c0a5097878fced3435"><code>d785e72</code></a>
chore(deps): update rolldown-related dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20772">#20772</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v7.1.7/packages/vite">compare
view</a></li>
</ul>
</details>
<br />

Updates `vite-plugin-checker` from 0.10.3 to 0.11.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fi3ework/vite-plugin-checker/releases">vite-plugin-checker's
releases</a>.</em></p>
<blockquote>
<h2>vite-plugin-checker@0.11.0</h2>
<h3>   🚨 Breaking Changes</h3>
<ul>
<li>Use node built-in instead of <code>strip-ansi</code>  -  by <a
href="https://github.com/danielroe"><code>@​danielroe</code></a> in <a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/588">fi3ework/vite-plugin-checker#588</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/66a10dc"><!--
raw HTML omitted -->(66a10)<!-- raw HTML omitted --></a></li>
</ul>
<h3>   🚀 Features</h3>
<ul>
<li><strong>oxlint</strong>: Add support for oxlint  -  by <a
href="https://github.com/raidorev"><code>@​raidorev</code></a> in <a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/578">fi3ework/vite-plugin-checker#578</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/61408e7"><!--
raw HTML omitted -->(61408)<!-- raw HTML omitted --></a></li>
</ul>
<h3>   🐞 Bug Fixes</h3>
<ul>
<li>Address merge conflict with strip-ansi  -  by <a
href="https://github.com/danielroe"><code>@​danielroe</code></a> <a
href="https://github.com/fi3ework/vite-plugin-checker/commit/bac91cf"><!--
raw HTML omitted -->(bac91)<!-- raw HTML omitted --></a></li>
<li><strong>deps</strong>: Update dependency strip-ansi to ^7.1.2  -  in
<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/587">fi3ework/vite-plugin-checker#587</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/5ddc18c"><!--
raw HTML omitted -->(5ddc1)<!-- raw HTML omitted --></a></li>
</ul>
<h5>    <a
href="https://github.com/fi3ework/vite-plugin-checker/compare/vite-plugin-checker@0.10.3...vite-plugin-checker@0.11.0">View
changes on GitHub</a></h5>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/44a86f717078355c40ea4ee77a78fb3200ab557e"><code>44a86f7</code></a>
v0.11.0</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/d22d54a935e0214569d676f243d4b4f860fbac90"><code>d22d54a</code></a>
chore: remove in-source changelogs</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/baf7152a75711b4bf6edecd949d7a3f0b3cc8589"><code>baf7152</code></a>
ci: use trusted publishing</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/bac91cf1fc99cfd86bde979906d3e1c1cd3afeb5"><code>bac91cf</code></a>
fix: address merge conflict with strip-ansi</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/66a10dca11420d39f39879dbde3716c314e15375"><code>66a10dc</code></a>
perf!: use node built-in instead of <code>strip-ansi</code> (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/588">#588</a>)</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/61408e75f78491a3e5a32203387a529796a5a764"><code>61408e7</code></a>
feat(oxlint): add support for oxlint (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/578">#578</a>)</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/5ddc18c639ee7f4fac8d42762cc02d206d78f182"><code>5ddc18c</code></a>
fix(deps): update dependency strip-ansi to ^7.1.2 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/587">#587</a>)</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/bfc6118391f971b91ee6c8145565b17e3df51258"><code>bfc6118</code></a>
chore(deps): update dependency <code>@​tsconfig/strictest</code> to
^2.0.6 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/586">#586</a>)</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/fd3443a5d955466386dfcb07099ba7225a386369"><code>fd3443a</code></a>
chore(deps): update dependency vite to v5 [security] (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/585">#585</a>)</li>
<li><a
href="https://github.com/fi3ework/vite-plugin-checker/commit/9158f8f1a6d4388d0b2908b41de3c4e2fcaa1b04"><code>9158f8f</code></a>
chore(deps): update dependency publint to ^0.3.13 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/584">#584</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/fi3ework/vite-plugin-checker/compare/vite-plugin-checker@0.10.3...vite-plugin-checker@0.11.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for vite-plugin-checker since your current
version.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:46:00 +00:00
dependabot[bot] cdd9ddd659 chore: bump ssh2 and @types/ssh2 in /site (#20077)
Bumps [ssh2](https://github.com/mscdex/ssh2) and
[@types/ssh2](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ssh2).
These dependencies needed to be updated together.
Updates `ssh2` from 1.16.0 to 1.17.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mscdex/ssh2/commit/844f1edfc41589737671f96a4f4e76afdf46abd4"><code>844f1ed</code></a>
package: bump version to v1.17.0</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/f7cbb0cff0a3ae7e4447ca706feaf1cf184344c4"><code>f7cbb0c</code></a>
package: bump nan to v2.23.0</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/83ec32070aca0d7dc679a833aa477520365ca74a"><code>83ec320</code></a>
ci: add node 24.x</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/b33c065d8630c5942d23748ae4ca7427bc0e7d18"><code>b33c065</code></a>
ci: bump windows image version</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/c19a821454a9034824425a7d6cc74991021d377e"><code>c19a821</code></a>
client: add support for environment variables when starting SFTP</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/dd5510c0888476e9eec205273732473fcce5adc6"><code>dd5510c</code></a>
lib: fix workers thread-safety issues</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/0fe2643cb29f0a29df9388af7f1ca8da15b09cd4"><code>0fe2643</code></a>
ci: unset OPENSSL_CONF</li>
<li><a
href="https://github.com/mscdex/ssh2/commit/42491a6053e222db4ff1b6c343b57040fb490647"><code>42491a6</code></a>
ci: fix issues related to old node-gyp + new Python</li>
<li>See full diff in <a
href="https://github.com/mscdex/ssh2/compare/v1.16.0...v1.17.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/ssh2` from 1.15.1 to 1.15.5
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ssh2">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:44:53 +00:00
Susana Ferreira 516af762b4 chore(dogfood): update claude_system_prompt to not report system prompt status change (#20053)
## Description

This PR updates the dogfood "Write Coder on Coder" template by modifying
the Claude system prompt so that it does not report task status changes
related to the system prompt itself.

Currently, when a user adds the initial prompt, the Claude code
workspace app (configured via Terraform) reports four status changes:
`Working → Idle → Working → Idle`
The first pair (`Working → Idle`) is caused by the status reporting of
the system prompt, which is unnecessary noise.
These redundant transitions also triggered notification reports, leading
to extra, misleading updates.

With this update, the system prompt will no longer trigger status
transitions or notifications, ensuring that only user-driven prompts
cause meaningful status and notification updates.
2025-10-01 12:39:44 +01:00
dependabot[bot] 36279ee002 chore: bump the emotion group across 2 directories with 1 update (#20056)
Bumps the emotion group with 1 update in the /offlinedocs directory:
[@emotion/styled](https://github.com/emotion-js/emotion).
Bumps the emotion group with 1 update in the /site directory:
[@emotion/styled](https://github.com/emotion-js/emotion).

Updates `@emotion/styled` from 11.14.0 to 11.14.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emotion-js/emotion/releases"><code>@​emotion/styled</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​emotion/styled</code><a
href="https://github.com/11"><code>@​11</code></a>.14.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/emotion-js/emotion/pull/3334">#3334</a>
<a
href="https://github.com/emotion-js/emotion/commit/0facbe47bd9099ae4ed22dc201822d910ac3dec5"><code>0facbe4</code></a>
Thanks <a
href="https://github.com/ZachRiegel"><code>@​ZachRiegel</code></a>! -
Renamed default-exported variable in <code>@emotion/styled</code> to aid
inferred import names in auto-import completions in IDEs</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/emotion-js/emotion/commit/49229553967b6050c92d9602eb577bdc48167e91"><code>4922955</code></a>
Version Packages (<a
href="https://redirect.github.com/emotion-js/emotion/issues/3335">#3335</a>)</li>
<li><a
href="https://github.com/emotion-js/emotion/commit/0facbe47bd9099ae4ed22dc201822d910ac3dec5"><code>0facbe4</code></a>
Renamed default-exported variable in <code>@emotion/styled</code> to aid
inferred import...</li>
<li><a
href="https://github.com/emotion-js/emotion/commit/cce67ec6b2fc94261028b4f4778aae8c3d6c5fd6"><code>cce67ec</code></a>
Bump parcel (<a
href="https://redirect.github.com/emotion-js/emotion/issues/3258">#3258</a>)</li>
<li>See full diff in <a
href="https://github.com/emotion-js/emotion/compare/@emotion/styled@11.14.0...@emotion/styled@11.14.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@emotion/styled` from 11.14.0 to 11.14.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/emotion-js/emotion/releases"><code>@​emotion/styled</code>'s
releases</a>.</em></p>
<blockquote>
<h2><code>@​emotion/styled</code><a
href="https://github.com/11"><code>@​11</code></a>.14.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/emotion-js/emotion/pull/3334">#3334</a>
<a
href="https://github.com/emotion-js/emotion/commit/0facbe47bd9099ae4ed22dc201822d910ac3dec5"><code>0facbe4</code></a>
Thanks <a
href="https://github.com/ZachRiegel"><code>@​ZachRiegel</code></a>! -
Renamed default-exported variable in <code>@emotion/styled</code> to aid
inferred import names in auto-import completions in IDEs</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/emotion-js/emotion/commit/49229553967b6050c92d9602eb577bdc48167e91"><code>4922955</code></a>
Version Packages (<a
href="https://redirect.github.com/emotion-js/emotion/issues/3335">#3335</a>)</li>
<li><a
href="https://github.com/emotion-js/emotion/commit/0facbe47bd9099ae4ed22dc201822d910ac3dec5"><code>0facbe4</code></a>
Renamed default-exported variable in <code>@emotion/styled</code> to aid
inferred import...</li>
<li><a
href="https://github.com/emotion-js/emotion/commit/cce67ec6b2fc94261028b4f4778aae8c3d6c5fd6"><code>cce67ec</code></a>
Bump parcel (<a
href="https://redirect.github.com/emotion-js/emotion/issues/3258">#3258</a>)</li>
<li>See full diff in <a
href="https://github.com/emotion-js/emotion/compare/@emotion/styled@11.14.0...@emotion/styled@11.14.1">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:39:33 +00:00
dependabot[bot] 19e23af7a7 chore: bump undici from 6.21.2 to 6.21.3 in /site (#20075)
Bumps [undici](https://github.com/nodejs/undici) from 6.21.2 to 6.21.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nodejs/undici/releases">undici's
releases</a>.</em></p>
<blockquote>
<h2>v6.21.3</h2>
<h2>What's Changed</h2>
<ul>
<li>[Backport v6.x] append crlf to formdata body by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>
in <a
href="https://redirect.github.com/nodejs/undici/pull/4210">nodejs/undici#4210</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nodejs/undici/compare/v6.21.2...v6.21.3">https://github.com/nodejs/undici/compare/v6.21.2...v6.21.3</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nodejs/undici/commit/da0e823ac0e89390256d61c429df0cf236afb79e"><code>da0e823</code></a>
Bumped v6.21.4</li>
<li><a
href="https://github.com/nodejs/undici/commit/dbbe0a2d5004cd7b6016e52736f59ce37bdb1556"><code>dbbe0a2</code></a>
append crlf to formdata body (<a
href="https://redirect.github.com/nodejs/undici/issues/3625">#3625</a>)
(<a
href="https://redirect.github.com/nodejs/undici/issues/4210">#4210</a>)</li>
<li>See full diff in <a
href="https://github.com/nodejs/undici/compare/v6.21.2...v6.21.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=undici&package-manager=npm_and_yarn&previous-version=6.21.2&new-version=6.21.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:39:22 +00:00
dependabot[bot] ddd5352003 chore: bump react-markdown from 9.0.3 to 9.1.0 in /site (#20070)
Bumps [react-markdown](https://github.com/remarkjs/react-markdown) from
9.0.3 to 9.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/remarkjs/react-markdown/releases">react-markdown's
releases</a>.</em></p>
<blockquote>
<h2>9.1.0</h2>
<ul>
<li>6ce120e Add support for async plugins
by <a href="https://github.com/wooorm"><code>@​wooorm</code></a> in <a
href="https://redirect.github.com/remarkjs/react-markdown/pull/890">remarkjs/react-markdown#890</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0">https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/remarkjs/react-markdown/blob/main/changelog.md">react-markdown's
changelog</a>.</em></p>
<blockquote>
<h2>9.1.0 - 2025-02-20</h2>
<ul>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/6ce120e"><code>6ce120e</code></a>
Add support for async plugins</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/747e505c9a465ee77addba626f288dfbda8bcad4"><code>747e505</code></a>
9.1.0</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/6ce120e70630a062b42e225ce917cf71339fa024"><code>6ce120e</code></a>
Add support for async plugins</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/78d08de906536b6913695883f215807992fc034d"><code>78d08de</code></a>
Refactor to remove warning in tests</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/bcdc5b3b4f45be2b662ae11c4b42e74727e3ba2f"><code>bcdc5b3</code></a>
Refactor <code>package.json</code></li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/c44e246bbb0cbad1110b22b6fb239db2669de0d0"><code>c44e246</code></a>
Update dev-dependencies</li>
<li>See full diff in <a
href="https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-markdown&package-manager=npm_and_yarn&previous-version=9.0.3&new-version=9.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:38:56 +00:00
dependabot[bot] a7e65d4a76 chore: bump react-markdown from 9.0.3 to 9.1.0 in /offlinedocs (#20065)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [react-markdown](https://github.com/remarkjs/react-markdown) from
9.0.3 to 9.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/remarkjs/react-markdown/releases">react-markdown's
releases</a>.</em></p>
<blockquote>
<h2>9.1.0</h2>
<ul>
<li>6ce120e Add support for async plugins
by <a href="https://github.com/wooorm"><code>@​wooorm</code></a> in <a
href="https://redirect.github.com/remarkjs/react-markdown/pull/890">remarkjs/react-markdown#890</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0">https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/remarkjs/react-markdown/blob/main/changelog.md">react-markdown's
changelog</a>.</em></p>
<blockquote>
<h2>9.1.0 - 2025-02-20</h2>
<ul>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/6ce120e"><code>6ce120e</code></a>
Add support for async plugins</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/747e505c9a465ee77addba626f288dfbda8bcad4"><code>747e505</code></a>
9.1.0</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/6ce120e70630a062b42e225ce917cf71339fa024"><code>6ce120e</code></a>
Add support for async plugins</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/78d08de906536b6913695883f215807992fc034d"><code>78d08de</code></a>
Refactor to remove warning in tests</li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/bcdc5b3b4f45be2b662ae11c4b42e74727e3ba2f"><code>bcdc5b3</code></a>
Refactor <code>package.json</code></li>
<li><a
href="https://github.com/remarkjs/react-markdown/commit/c44e246bbb0cbad1110b22b6fb239db2669de0d0"><code>c44e246</code></a>
Update dev-dependencies</li>
<li>See full diff in <a
href="https://github.com/remarkjs/react-markdown/compare/9.0.3...9.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-markdown&package-manager=npm_and_yarn&previous-version=9.0.3&new-version=9.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:35:31 +00:00
dependabot[bot] a650685a87 chore: bump semver and @types/semver in /site (#20076)
Bumps [semver](https://github.com/npm/node-semver) and
[@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver).
These dependencies needed to be updated together.
Updates `semver` from 7.6.2 to 7.7.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/npm/node-semver/releases">semver's
releases</a>.</em></p>
<blockquote>
<h2>v7.7.2</h2>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.7.1...v7.7.2">7.7.2</a>
(2025-05-12)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/fcafb61ed566ff8ccf24818dd94b76738f037aa4"><code>fcafb61</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/780">#780</a>
add missing <code>'use strict'</code> directives (<a
href="https://redirect.github.com/npm/node-semver/issues/780">#780</a>)
(<a href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/c99f336fa3bdff465652f9041eab2127d2f52eb2"><code>c99f336</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/781">#781</a>
prerelease identifier starting with digits (<a
href="https://redirect.github.com/npm/node-semver/issues/781">#781</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/c760403b935d3ad35f83e9bbe5ebe1badef2fc71"><code>c760403</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/784">#784</a>
template-oss-apply for workflow permissions (<a
href="https://redirect.github.com/npm/node-semver/issues/784">#784</a>)
(<a
href="https://github.com/wraithgar"><code>@​wraithgar</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/2677f2a88334b0e728dbfe9ad9f5f57458437c87"><code>2677f2a</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/778">#778</a>
bump <code>@​npmcli/template-oss</code> from 4.23.6 to 4.24.3 (<a
href="https://redirect.github.com/npm/node-semver/issues/778">#778</a>)
(<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2>v7.7.1</h2>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.7.0...v7.7.1">7.7.1</a>
(2025-02-03)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/af761c05bd53eef83b5e20f8b09360b0e70557dc"><code>af761c0</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/764">#764</a>
inc: fully capture prerelease identifier (<a
href="https://redirect.github.com/npm/node-semver/issues/764">#764</a>)
(<a
href="https://github.com/wraithgar"><code>@​wraithgar</code></a>)</li>
</ul>
<h2>v7.7.0</h2>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.6.3...v7.7.0">7.7.0</a>
(2025-01-29)</h2>
<h3>Features</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/0864b3ce7932667013e0c7c5ec764777d4682883"><code>0864b3c</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/753">#753</a>
add &quot;release&quot; inc type (<a
href="https://redirect.github.com/npm/node-semver/issues/753">#753</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/d588e3782864b1cab2fe9f2452b848e8c7f609d1"><code>d588e37</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/755">#755</a>
diff: fix prerelease to stable version diff logic (<a
href="https://redirect.github.com/npm/node-semver/issues/755">#755</a>)
(<a
href="https://github.com/eminberkayd"><code>@​eminberkayd</code></a>,
berkay.daglar)</li>
<li><a
href="https://github.com/npm/node-semver/commit/8a34bdecc783407f4e1a8a1ee1f67906b84a4b78"><code>8a34bde</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/754">#754</a>
add identifier validation to <code>inc()</code> (<a
href="https://redirect.github.com/npm/node-semver/issues/754">#754</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/67e54785a0f871361230f84323cbb631b9b6d834"><code>67e5478</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/756">#756</a>
readme: added missing period for consistency (<a
href="https://redirect.github.com/npm/node-semver/issues/756">#756</a>)
(<a
href="https://github.com/shaymolcho"><code>@​shaymolcho</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/868d4bbe3d318c52544f38d5f9977a1103e924c2"><code>868d4bb</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/749">#749</a>
clarify comment about obsolete prefixes (<a
href="https://redirect.github.com/npm/node-semver/issues/749">#749</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>, <a
href="https://github.com/ljharb"><code>@​ljharb</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/145c554b8c7b7ecfcb451153ad18bdb2f24ad10d"><code>145c554</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/741">#741</a>
bump <code>@​npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/node-semver/commit/753e02b9d0cb3ac23e085dc33efcab3e08d61f2b"><code>753e02b</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/747">#747</a>
bump <code>@​npmcli/template-oss</code> from 4.23.3 to 4.23.4 (<a
href="https://redirect.github.com/npm/node-semver/issues/747">#747</a>)
(<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/0b812d5fb5fbb208e89dc1250e2efafeaa549437"><code>0b812d5</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/744">#744</a>
postinstall for dependabot template-oss PR (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
</ul>
<h2>v7.6.3</h2>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.6.2...v7.6.3">7.6.3</a>
(2024-07-16)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/73a3d79c4ec32d5dd62c9d5f64e5af7fbdad9ec0"><code>73a3d79</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/726">#726</a>
optimize Range parsing and formatting (<a
href="https://redirect.github.com/npm/node-semver/issues/726">#726</a>)
(<a href="https://github.com/jviide"><code>@​jviide</code></a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/2975ece120e17660c9f1ef517de45c09ff821064"><code>2975ece</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/719">#719</a>
fix extra backtick typo (<a
href="https://redirect.github.com/npm/node-semver/issues/719">#719</a>)
(<a href="https://github.com/stdavis"><code>@​stdavis</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/npm/node-semver/blob/main/CHANGELOG.md">semver's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.7.1...v7.7.2">7.7.2</a>
(2025-05-12)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/fcafb61ed566ff8ccf24818dd94b76738f037aa4"><code>fcafb61</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/780">#780</a>
add missing <code>'use strict'</code> directives (<a
href="https://redirect.github.com/npm/node-semver/issues/780">#780</a>)
(<a href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/c99f336fa3bdff465652f9041eab2127d2f52eb2"><code>c99f336</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/781">#781</a>
prerelease identifier starting with digits (<a
href="https://redirect.github.com/npm/node-semver/issues/781">#781</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/c760403b935d3ad35f83e9bbe5ebe1badef2fc71"><code>c760403</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/784">#784</a>
template-oss-apply for workflow permissions (<a
href="https://redirect.github.com/npm/node-semver/issues/784">#784</a>)
(<a
href="https://github.com/wraithgar"><code>@​wraithgar</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/2677f2a88334b0e728dbfe9ad9f5f57458437c87"><code>2677f2a</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/778">#778</a>
bump <code>@​npmcli/template-oss</code> from 4.23.6 to 4.24.3 (<a
href="https://redirect.github.com/npm/node-semver/issues/778">#778</a>)
(<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
</ul>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.7.0...v7.7.1">7.7.1</a>
(2025-02-03)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/af761c05bd53eef83b5e20f8b09360b0e70557dc"><code>af761c0</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/764">#764</a>
inc: fully capture prerelease identifier (<a
href="https://redirect.github.com/npm/node-semver/issues/764">#764</a>)
(<a
href="https://github.com/wraithgar"><code>@​wraithgar</code></a>)</li>
</ul>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.6.3...v7.7.0">7.7.0</a>
(2025-01-29)</h2>
<h3>Features</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/0864b3ce7932667013e0c7c5ec764777d4682883"><code>0864b3c</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/753">#753</a>
add &quot;release&quot; inc type (<a
href="https://redirect.github.com/npm/node-semver/issues/753">#753</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/d588e3782864b1cab2fe9f2452b848e8c7f609d1"><code>d588e37</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/755">#755</a>
diff: fix prerelease to stable version diff logic (<a
href="https://redirect.github.com/npm/node-semver/issues/755">#755</a>)
(<a
href="https://github.com/eminberkayd"><code>@​eminberkayd</code></a>,
berkay.daglar)</li>
<li><a
href="https://github.com/npm/node-semver/commit/8a34bdecc783407f4e1a8a1ee1f67906b84a4b78"><code>8a34bde</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/754">#754</a>
add identifier validation to <code>inc()</code> (<a
href="https://redirect.github.com/npm/node-semver/issues/754">#754</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/67e54785a0f871361230f84323cbb631b9b6d834"><code>67e5478</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/756">#756</a>
readme: added missing period for consistency (<a
href="https://redirect.github.com/npm/node-semver/issues/756">#756</a>)
(<a
href="https://github.com/shaymolcho"><code>@​shaymolcho</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/868d4bbe3d318c52544f38d5f9977a1103e924c2"><code>868d4bb</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/749">#749</a>
clarify comment about obsolete prefixes (<a
href="https://redirect.github.com/npm/node-semver/issues/749">#749</a>)
(<a href="https://github.com/mbtools"><code>@​mbtools</code></a>, <a
href="https://github.com/ljharb"><code>@​ljharb</code></a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/145c554b8c7b7ecfcb451153ad18bdb2f24ad10d"><code>145c554</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/741">#741</a>
bump <code>@​npmcli/eslint-config</code> from 4.0.5 to 5.0.0 (<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot])</li>
<li><a
href="https://github.com/npm/node-semver/commit/753e02b9d0cb3ac23e085dc33efcab3e08d61f2b"><code>753e02b</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/747">#747</a>
bump <code>@​npmcli/template-oss</code> from 4.23.3 to 4.23.4 (<a
href="https://redirect.github.com/npm/node-semver/issues/747">#747</a>)
(<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a
href="https://github.com/npm-cli-bot"><code>@​npm-cli-bot</code></a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/0b812d5fb5fbb208e89dc1250e2efafeaa549437"><code>0b812d5</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/744">#744</a>
postinstall for dependabot template-oss PR (<a
href="https://github.com/hashtagchris"><code>@​hashtagchris</code></a>)</li>
</ul>
<h2><a
href="https://github.com/npm/node-semver/compare/v7.6.2...v7.6.3">7.6.3</a>
(2024-07-16)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/73a3d79c4ec32d5dd62c9d5f64e5af7fbdad9ec0"><code>73a3d79</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/726">#726</a>
optimize Range parsing and formatting (<a
href="https://redirect.github.com/npm/node-semver/issues/726">#726</a>)
(<a href="https://github.com/jviide"><code>@​jviide</code></a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/2975ece120e17660c9f1ef517de45c09ff821064"><code>2975ece</code></a>
<a href="https://redirect.github.com/npm/node-semver/pull/719">#719</a>
fix extra backtick typo (<a
href="https://redirect.github.com/npm/node-semver/issues/719">#719</a>)
(<a href="https://github.com/stdavis"><code>@​stdavis</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/npm/node-semver/commit/281055e7716ef0415a8826972471331989ede58c"><code>281055e</code></a>
chore: release 7.7.2 (<a
href="https://redirect.github.com/npm/node-semver/issues/783">#783</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/fcafb61ed566ff8ccf24818dd94b76738f037aa4"><code>fcafb61</code></a>
fix: add missing <code>'use strict'</code> directives (<a
href="https://redirect.github.com/npm/node-semver/issues/780">#780</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/c760403b935d3ad35f83e9bbe5ebe1badef2fc71"><code>c760403</code></a>
chore: template-oss-apply for workflow permissions (<a
href="https://redirect.github.com/npm/node-semver/issues/784">#784</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/c99f336fa3bdff465652f9041eab2127d2f52eb2"><code>c99f336</code></a>
fix: prerelease identifier starting with digits (<a
href="https://redirect.github.com/npm/node-semver/issues/781">#781</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/2677f2a88334b0e728dbfe9ad9f5f57458437c87"><code>2677f2a</code></a>
chore: bump <code>@​npmcli/template-oss</code> from 4.23.6 to 4.24.3 (<a
href="https://redirect.github.com/npm/node-semver/issues/778">#778</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/0b98655dbc1a9eb79370ed80f557be98d1c06587"><code>0b98655</code></a>
chore: bump <code>@​npmcli/template-oss</code> from 4.23.4 to 4.23.6 (<a
href="https://redirect.github.com/npm/node-semver/issues/760">#760</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/30c438bb46c74f319aa8783f96d233ebf5f4a90d"><code>30c438b</code></a>
chore: release 7.7.1 (<a
href="https://redirect.github.com/npm/node-semver/issues/765">#765</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/af761c05bd53eef83b5e20f8b09360b0e70557dc"><code>af761c0</code></a>
fix(inc): fully capture prerelease identifier (<a
href="https://redirect.github.com/npm/node-semver/issues/764">#764</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/2cfcbb5021059d0b6642a77400efb4b51133bd75"><code>2cfcbb5</code></a>
chore: release 7.7.0 (<a
href="https://redirect.github.com/npm/node-semver/issues/750">#750</a>)</li>
<li><a
href="https://github.com/npm/node-semver/commit/d588e3782864b1cab2fe9f2452b848e8c7f609d1"><code>d588e37</code></a>
fix(diff): fix prerelease to stable version diff logic (<a
href="https://redirect.github.com/npm/node-semver/issues/755">#755</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/npm/node-semver/compare/v7.6.2...v7.7.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/semver` from 7.5.8 to 7.7.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:34:03 +00:00
dependabot[bot] 65c9822c65 chore: bump prettier from 3.4.1 to 3.6.2 in /offlinedocs (#20061)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [prettier](https://github.com/prettier/prettier) from 3.4.1 to
3.6.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.6.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Add missing blank line around code block by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/17675">prettier/prettier#17675</a></li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#362">Changelog</a></p>
<h2>3.6.1</h2>
<ul>
<li>Fix &quot;Warning: File descriptor 39 closed but not opened in
unmanaged mode&quot; error when running
<code>--experimental-cli</code></li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#361">Changelog</a></p>
<h2>3.6.0</h2>
<p><a
href="https://github.com/prettier/prettier/compare/3.5.3...3.6.0">diff</a></p>
<p>🔗 <a href="https://prettier.io/blog/2025/06/23/3.6.0">Release note
&quot;Prettier 3.6: Experimental fast CLI and new OXC and Hermes
plugins!&quot;</a></p>
<h2>3.5.3</h2>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#353">Changelog</a></p>
<h2>3.5.2</h2>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#352">Changelog</a></p>
<h2>3.5.1</h2>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#351">Changelog</a></p>
<h2>3.5.0</h2>
<p><a
href="https://github.com/prettier/prettier/compare/3.4.2...3.5.0">diff</a></p>
<p>🔗 <a href="https://prettier.io/blog/2025/02/09/3.5.0">Release
note</a></p>
<h2>3.4.2</h2>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#342">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.6.2</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.6.1...3.6.2">diff</a></p>
<h4>Markdown: Add missing blank line around code block (<a
href="https://redirect.github.com/prettier/prettier/pull/17675">#17675</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="md"><code>&lt;!-- Input --&gt;
1. Some text, and code block below, with newline after code block
<pre lang="yaml"><code>---
foo: bar
</code></pre>
<ol>
<li>Another</li>
<li>List</li>
</ol>
<p>&lt;!-- Prettier 3.6.1 --&gt;</p>
<ol>
<li>
<p>Some text, and code block below, with newline after code block</p>
<pre lang="yaml"><code>---
foo: bar
</code></pre>
<ol>
<li>Another</li>
<li>List</li>
</ol>
</li>
</ol>
<p>&lt;!-- Prettier 3.6.2 --&gt;</p>
<ol>
<li>
<p>Some text, and code block below, with newline after code block</p>
<pre lang="yaml"><code>---
foo: bar
</code></pre>
<ol>
<li>Another</li>
<li>List<br />
</code></pre></li>
</ol>
</li>
</ol>
<h1>3.6.1</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.6.0...3.6.1">diff</a></p>
<h4>TypeScript: Allow const without initializer (<a
href="https://redirect.github.com/prettier/prettier/pull/17650">#17650</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/17654">#17654</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="jsx"><code>// Input
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/7a8b05f41574633fd3af5298f3eeaf33567ad3d3"><code>7a8b05f</code></a>
Release 3.6.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/46526b49b6315914b9229be412c1557ce59f8dbf"><code>46526b4</code></a>
Add missing blank line around code block (<a
href="https://redirect.github.com/prettier/prettier/issues/17675">#17675</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/a04ec1196f9c3efe2312b10c2f0d02903c9de5e7"><code>a04ec11</code></a>
chore(deps): update babel to v7.27.7 (<a
href="https://redirect.github.com/prettier/prettier/issues/17684">#17684</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/32be5b6b44314579f3dcc838f26b03ce47938acb"><code>32be5b6</code></a>
chore(deps): update dependency flow-parser to v0.274.1 (<a
href="https://redirect.github.com/prettier/prettier/issues/17676">#17676</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/b55e777924538b69c564abea51a42e33597910b9"><code>b55e777</code></a>
Update docs about &quot;TypeScript Configuration Files&quot; (<a
href="https://redirect.github.com/prettier/prettier/issues/17677">#17677</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/b197c99224b2e068736020bdaa8b2f8a686d4b1e"><code>b197c99</code></a>
chore(deps): update dependency <code>@​vitejs/plugin-react</code> to
v4.6.0 (<a
href="https://redirect.github.com/prettier/prettier/issues/17674">#17674</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/1185f8370a7a4c1b038b994e7be32a2413fae12d"><code>1185f83</code></a>
chore(deps): update dependency <code>@​angular/compiler</code> to
v20.0.5 (<a
href="https://redirect.github.com/prettier/prettier/issues/17680">#17680</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/aa1316fa603e25d853e76f69cdc029c19b8d24b9"><code>aa1316f</code></a>
chore(deps): update dependency browserslist to v4.25.1 (<a
href="https://redirect.github.com/prettier/prettier/issues/17671">#17671</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/c468d33f16c665363da86f9275be4b4d9f799dcd"><code>c468d33</code></a>
chore(deps): update dependency oxc-parser to v0.75.0 (<a
href="https://redirect.github.com/prettier/prettier/issues/17672">#17672</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/3f46d91bdb7b2a650f376215fdf884babfc765b7"><code>3f46d91</code></a>
chore(deps): update dependency vite to v7 (<a
href="https://redirect.github.com/prettier/prettier/issues/17673">#17673</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.4.1...3.6.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prettier&package-manager=npm_and_yarn&previous-version=3.4.1&new-version=3.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:33:36 +00:00
dependabot[bot] 58f2574791 chore: bump next from 15.4.7 to 15.5.4 in /offlinedocs (#20063)
Bumps [next](https://github.com/vercel/next.js) from 15.4.7 to 15.5.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/next.js/releases">next's
releases</a>.</em></p>
<blockquote>
<h2>v15.5.4</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>fix: ensure onRequestError is invoked when otel enabled (<a
href="https://redirect.github.com/vercel/next.js/issues/83343">#83343</a>)</li>
<li>fix: devtools initial position should be from next config (<a
href="https://redirect.github.com/vercel/next.js/issues/83571">#83571</a>)</li>
<li>[devtool] fix overlay styles are missing (<a
href="https://redirect.github.com/vercel/next.js/issues/83721">#83721</a>)</li>
<li>Turbopack: don't match dynamic pattern for node_modules packages (<a
href="https://redirect.github.com/vercel/next.js/issues/83176">#83176</a>)</li>
<li>Turbopack: don't treat metadata routes as RSC (<a
href="https://redirect.github.com/vercel/next.js/issues/82911">#82911</a>)</li>
<li>[turbopack] Improve handling of symlink resolution errors in
track_glob and read_glob (<a
href="https://redirect.github.com/vercel/next.js/issues/83357">#83357</a>)</li>
<li>Turbopack: throw large static metadata error earlier (<a
href="https://redirect.github.com/vercel/next.js/issues/82939">#82939</a>)</li>
<li>fix: error overlay not closing when backdrop clicked (<a
href="https://redirect.github.com/vercel/next.js/issues/83981">#83981</a>)</li>
<li>Turbopack: flush Node.js worker IPC on error (<a
href="https://redirect.github.com/vercel/next.js/issues/84077">#84077</a>)</li>
</ul>
<h3>Misc Changes</h3>
<ul>
<li>[CNA] use linter preference (<a
href="https://redirect.github.com/vercel/next.js/issues/83194">#83194</a>)</li>
<li>CI: use KV for test timing data (<a
href="https://redirect.github.com/vercel/next.js/issues/83745">#83745</a>)</li>
<li>docs: september improvements and fixes (<a
href="https://redirect.github.com/vercel/next.js/issues/83997">#83997</a>)</li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/yiminghe"><code>@​yiminghe</code></a>, <a
href="https://github.com/huozhi"><code>@​huozhi</code></a>, <a
href="https://github.com/devjiwonchoi"><code>@​devjiwonchoi</code></a>,
<a href="https://github.com/mischnic"><code>@​mischnic</code></a>, <a
href="https://github.com/lukesandberg"><code>@​lukesandberg</code></a>,
<a href="https://github.com/ztanner"><code>@​ztanner</code></a>, <a
href="https://github.com/icyJoseph"><code>@​icyJoseph</code></a>, <a
href="https://github.com/leerob"><code>@​leerob</code></a>, <a
href="https://github.com/fufuShih"><code>@​fufuShih</code></a>, <a
href="https://github.com/dwrth"><code>@​dwrth</code></a>, <a
href="https://github.com/aymericzip"><code>@​aymericzip</code></a>, <a
href="https://github.com/obendev"><code>@​obendev</code></a>, <a
href="https://github.com/molebox"><code>@​molebox</code></a>, <a
href="https://github.com/OoMNoO"><code>@​OoMNoO</code></a>, <a
href="https://github.com/pontasan"><code>@​pontasan</code></a>, <a
href="https://github.com/styfle"><code>@​styfle</code></a>, <a
href="https://github.com/HondaYt"><code>@​HondaYt</code></a>, <a
href="https://github.com/ryuapp"><code>@​ryuapp</code></a>, <a
href="https://github.com/lpalmes"><code>@​lpalmes</code></a>, and <a
href="https://github.com/ijjk"><code>@​ijjk</code></a> for helping!</p>
<h2>v15.5.3</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>fix: validation return types of pages API routes (<a
href="https://redirect.github.com/vercel/next.js/issues/83069">#83069</a>)</li>
<li>fix: relative paths in dev in validator.ts (<a
href="https://redirect.github.com/vercel/next.js/issues/83073">#83073</a>)</li>
<li>fix: remove satisfies keyword from type validation to preserve old
TS compatibility (<a
href="https://redirect.github.com/vercel/next.js/issues/83071">#83071</a>)</li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/bgub"><code>@​bgub</code></a> for helping!</p>
<h2>v15.5.2</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>fix: disable unknownatrules lint rule entirely (<a
href="https://redirect.github.com/vercel/next.js/issues/83059">#83059</a>)</li>
<li>revert: add ?dpl to fonts in /_next/static/media (<a
href="https://redirect.github.com/vercel/next.js/issues/83062">#83062</a>)</li>
</ul>
<h3>Credits</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/next.js/commit/40f1d7814d8f1ab3d9e169b389015b8d6f258fb3"><code>40f1d78</code></a>
v15.5.4</li>
<li><a
href="https://github.com/vercel/next.js/commit/cb30f0a1760b640b42cc15f34c55531499d3949c"><code>cb30f0a</code></a>
[backport] docs: september improvements and fixes (<a
href="https://redirect.github.com/vercel/next.js/issues/83997">#83997</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/b6a32bb57956f0bd4175d7b04e83c3bbad5249a8"><code>b6a32bb</code></a>
[backport] [CNA] use linter preference (<a
href="https://redirect.github.com/vercel/next.js/issues/83194">#83194</a>)
(<a
href="https://redirect.github.com/vercel/next.js/issues/84087">#84087</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/26d61f1e9a27a5f6c3dea5ac36c2c20c33cc0127"><code>26d61f1</code></a>
[backport] Turbopack: flush Node.js worker IPC on error (<a
href="https://redirect.github.com/vercel/next.js/issues/84079">#84079</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/e11e87a54734bb8136e2e5ac5c0c2218b1e57a48"><code>e11e87a</code></a>
[backport] fix: error overlay not closing when backdrop clicked (<a
href="https://redirect.github.com/vercel/next.js/issues/83981">#83981</a>)
(<a
href="https://redirect.github.com/vercel/next.js/issues/83">#83</a>...</li>
<li><a
href="https://github.com/vercel/next.js/commit/0a29888575d9f95e1cdf26d62bcab05a5a53bf95"><code>0a29888</code></a>
[backport] fix: devtools initial position should be from next config (<a
href="https://redirect.github.com/vercel/next.js/issues/83571">#83571</a>)...</li>
<li><a
href="https://github.com/vercel/next.js/commit/7a53950c136242fa77af5c02307a86966379e5a8"><code>7a53950</code></a>
[backport] Turbopack: don't treat metadata routes as RSC (<a
href="https://redirect.github.com/vercel/next.js/issues/83804">#83804</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/050bdf1ae70f71cac9a4634b1059ce386b15825a"><code>050bdf1</code></a>
[backport] Turbopack: throw large static metadata error earlier (<a
href="https://redirect.github.com/vercel/next.js/issues/83816">#83816</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/1f6ea09f8586ec26978c79d0a3d90b4b6b62f1a4"><code>1f6ea09</code></a>
[backport] Turbopack: Improve handling of symlink resolution errors (<a
href="https://redirect.github.com/vercel/next.js/issues/83805">#83805</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/c7d1855499e507df5591c3697a365ae1a063ebb0"><code>c7d1855</code></a>
[backport] CI: use KV for test timing data (<a
href="https://redirect.github.com/vercel/next.js/issues/83860">#83860</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/next.js/compare/v15.4.7...v15.5.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=15.4.7&new-version=15.5.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:33:08 +00:00
dependabot[bot] c93795e271 chore: bump remark-gfm from 4.0.0 to 4.0.1 in /offlinedocs (#20066)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [remark-gfm](https://github.com/remarkjs/remark-gfm) from 4.0.0 to
4.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/remarkjs/remark-gfm/releases">remark-gfm's
releases</a>.</em></p>
<blockquote>
<h2>4.0.1</h2>
<h4>Types</h4>
<ul>
<li>4af823a Refactor to use <code>interface</code> for exposed
types</li>
<li>3a57a5b Add declaration maps</li>
<li>76559f9 Refactor to use <code>@import</code>s</li>
</ul>
<h4>Docs</h4>
<ul>
<li>173394d Add docs on footnote option</li>
<li>21cae6a Fix typo
by <a href="https://github.com/leafac"><code>@​leafac</code></a> in <a
href="https://redirect.github.com/remarkjs/remark-gfm/pull/73">remarkjs/remark-gfm#73</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/remarkjs/remark-gfm/compare/4.0.0...4.0.1">https://github.com/remarkjs/remark-gfm/compare/4.0.0...4.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/109972e8a773bf5dac1d6d2da0776557f36971aa"><code>109972e</code></a>
4.0.1</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/173394d37359ac3b193a39832fdf0babadd39d40"><code>173394d</code></a>
Add docs on footnote option</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/030dd8d53490ffef21ead3420d3e75028aff511f"><code>030dd8d</code></a>
Update dev-dependencies</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/21cae6ac8b33d98ad8a944023443a1f99f6727f0"><code>21cae6a</code></a>
Fix typo</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/4af823a56b4aab629a2558b347030c9648c0968e"><code>4af823a</code></a>
Refactor to use <code>interface</code> for exposed types</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/3a57a5bc3cb6a7f9c1f8ac17257f598c20d60397"><code>3a57a5b</code></a>
Add declaration maps</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/76559f9e0509a53589879622baeb0d31ea1f5369"><code>76559f9</code></a>
Refactor to use <code>@import</code>s</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/da382350ccbf4902572ea46dd6a6ebd839e50433"><code>da38235</code></a>
Refactor <code>package.json</code></li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/a5e8993994a569ab8508daddbc5b982399804392"><code>a5e8993</code></a>
Remove license year</li>
<li><a
href="https://github.com/remarkjs/remark-gfm/commit/4e1d55f3202a9bda07e02e271860719c813bd4f5"><code>4e1d55f</code></a>
Refactor <code>.editorconfig</code></li>
<li>Additional commits viewable in <a
href="https://github.com/remarkjs/remark-gfm/compare/4.0.0...4.0.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=remark-gfm&package-manager=npm_and_yarn&previous-version=4.0.0&new-version=4.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:27:35 +00:00
dependabot[bot] 8ee6aaa188 chore: bump knip from 5.51.0 to 5.64.1 in /site (#20073)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip)
from 5.51.0 to 5.64.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webpro-nl/knip/releases">knip's
releases</a>.</em></p>
<blockquote>
<h2>Release 5.64.1</h2>
<ul>
<li>Edit docs (634b59d07353bc09db762ee1b672df06da66da59)</li>
<li>Edit docs (d3433f00840736e11cc5c845babffe415ecad1fe)</li>
<li>Add &quot;How to keep package.json under control&quot; article
(570f40b15007c075d7f1e4d77bd2970034eec8ec)</li>
<li>We're incompatible with typescript v7
(f4f9166a0fca265e6f0dc939528836ce7003938f)</li>
<li>Migrate from bun → pnpm
(f18428c53d5bff7bad5259bf053e1d8c2b78881f)</li>
<li>Migrate from biome v1 → v2
(7ae5d72dadba0d8084842765ba3c045ac9199aa5)</li>
<li>Update dependencies (3174456e0c0f3086cc52d41cbea647b5a50cc057)</li>
<li>Re-gen plugins list (2da7ba49e8930c68b3cf8d1421a88cf975af9800)</li>
<li>Migrate from zod v3 → v4
(a71c1030be3ee6b7312fdb952504a3f1dbe5a694)</li>
</ul>
<h2>Release 5.64.0</h2>
<ul>
<li>Fix formatting (900068149a5612cb6d084d46a3ff31b94c49b284)</li>
<li>Add <code>env-cmd</code> Support (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1254">#1254</a>)
(21d6b5183ad10b3296cdb9c8f21a8f2d01bb36e9) - thanks <a
href="https://github.com/joealden"><code>@​joealden</code></a>!</li>
<li>Re-gen sponsorships chart
(185c6389226216548c5691acbac38dda3bf07dfd)</li>
<li>fix: handle only string modules in dependency resolution (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1263">#1263</a>)
(a54021b6e5904fe6a6e87614728b841fe6931858) - thanks <a
href="https://github.com/wattanx"><code>@​wattanx</code></a>!</li>
<li>fix: bun ci (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1267">#1267</a>)
(3d1c3c5b91f440ade9f1069dd41f402e50645c6c) - thanks <a
href="https://github.com/Zamiell"><code>@​Zamiell</code></a>!</li>
<li>Filter out invalid binaries (resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1264">#1264</a>)
(6f306111e4571418546da2aaf40d9b533940dd28)</li>
<li>Work types for good ol' ts 5.0.4
(9913ee755014285036a12ceed65371eb47a321eb)</li>
<li>Add <a
href="https://github.com/Datadog-OSS"><code>@​Datadog-OSS</code></a>
sponsor (a61d9fef9b3ea9c163c6408b1b38495417aeb1da)</li>
<li>feat: GitHub actions reporter (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1231">#1231</a>)
(0a234504fd626f9f0a59aa377301fa46639539c1) - thanks <a
href="https://github.com/cylewaitforit"><code>@​cylewaitforit</code></a>!</li>
<li>Add pos to unlisted deps issue type
(5b54dae614d1b0719046405241d80e390ab9f4ba)</li>
<li>Improve import specifier sanitizer (resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1257">#1257</a>)
(087a98e028994d3e19bfcfd88b7b6231855de781)</li>
<li>Cover more cases for symbol refs finder (resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1273">#1273</a>)
(3d76e51e59eceeb528ef6e20ca5e3a1bfff2a841)</li>
<li>Fix package name of rslib enabler (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1272">#1272</a>)
(432bdccb9aa3c8c7d8c0114f6614a651d959e56d) - thanks <a
href="https://github.com/nyarthan"><code>@​nyarthan</code></a>!</li>
<li>Pass parsed CLI args to config-as-a-function
(b0814c9d454ccd060aeda693398d1707ef678fc4)</li>
<li>Ignore !-suffixed deps/bins only in production mode (resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1253">#1253</a>)
(06d4df84e7fe2735fce8bc1b1b12e78016e38ebb)</li>
<li>Update docs (0d8fd135b46855f6d606783e6c256cceeccf9acf)</li>
<li>Auto-format (f54a7bd2cd74354f51fb46ae978b3e5db8759fd7)</li>
<li>Find accessed identifiers for dynamic imports (resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1155">#1155</a>,
resolves <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1230">#1230</a>)
(ec0be7e3b222da5b6ddb34baad9d2591f0f479cb)</li>
<li>Update oxc-resolver and a few more (dev) deps
(96c822a40855c21152f81a1599458850b4f6c2dc)</li>
<li>Optimize <code>getAccessedIdentifiers</code>
(8fb95019030533909c970bdb204b4779c19eaf5d)</li>
<li>fix: enable pnpm plugin on root config &amp; lockfile (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1275">#1275</a>)
(6e339cadc079b0e144c2036134154c696e8b31c0) - thanks <a
href="https://github.com/nyarthan"><code>@​nyarthan</code></a>!</li>
<li>Remove ancient past sponsors
(e9e6e911d87cf550df87647f5a9b949d32faa27f)</li>
<li>Remove default <code>binaries</code> values in plugins
(aac28c491ead836231e7487a8ebea056d0cf16cc)</li>
<li>Remove default <code>containingFilePath</code> value in angular
plugin (92089275df7752a830c171d8d1d4cb39b0a83565)</li>
<li>Add <code>isRootOnly</code> to pnpm plugin
(fe99f594ac79c2bb3590091651300062b690a12b)</li>
<li>Move/extend docs to write plugin
(497bddb5479a53d59baaa078225343a621eaa317)</li>
<li>feat: add <code>time</code> &amp; <code>unzip</code> to ignored
binaries (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1276">#1276</a>)
(4f8d9df599ffaba171e2535ffe61153f4ce1089a) - thanks <a
href="https://github.com/nyarthan"><code>@​nyarthan</code></a>!</li>
<li>Add <code>Rstest</code> Plugin (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1277">#1277</a>)
(5b7d92f101153294708a3d1afe8d2c4d61595116) - thanks <a
href="https://github.com/nyarthan"><code>@​nyarthan</code></a>!</li>
<li>Edit docs (847ccf168776f2cb6c9b5108a208ea8eae12799f)</li>
<li>feat: add plugin for <code>bumpp</code> (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1278">#1278</a>)
(136a14bc4ff4138389a831afc62f5406f66223a5) - thanks <a
href="https://github.com/nyarthan"><code>@​nyarthan</code></a>!</li>
<li>Support input resolver from args in plugins (resolve <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1274">#1274</a>)
(19dd367764fa078fb9c93bb0a715492ff0581098)</li>
<li>Edit docs (77d683e2b75b44c7fd5fc47b59621398b86028ea)</li>
</ul>
<h2>Release 5.63.1</h2>
<ul>
<li>Fix <code>rsbuild</code> Plugin (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1227">#1227</a>)
(e91eea3382059ad4067ace6079e856b2268d9f94) - thanks <a
href="https://github.com/joealden"><code>@​joealden</code></a>!</li>
<li>Binaries don't contain colons (closes <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1234">#1234</a>)
(1d060ac1043ccf211380682962c4c668758740ed)</li>
<li>Refactor options all over the place
(982d3272e46609f06ca858605d802a75726500d1)</li>
<li>feat: detect nuxt modules as dependencies (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1241">#1241</a>)
(f2072e6aecd81a2082dc60f440d1e48ab583e480) - thanks <a
href="https://github.com/danielamaia"><code>@​danielamaia</code></a>!</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/webpro-nl/knip/commit/e0ff0b5d74ae42686b86cff962a357bce9619a16"><code>e0ff0b5</code></a>
Release 5.64.1</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/a71c1030be3ee6b7312fdb952504a3f1dbe5a694"><code>a71c103</code></a>
Migrate from zod v3 → v4</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/3174456e0c0f3086cc52d41cbea647b5a50cc057"><code>3174456</code></a>
Update dependencies</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/7ae5d72dadba0d8084842765ba3c045ac9199aa5"><code>7ae5d72</code></a>
Migrate from biome v1 → v2</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/f18428c53d5bff7bad5259bf053e1d8c2b78881f"><code>f18428c</code></a>
Migrate from bun → pnpm</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/f4f9166a0fca265e6f0dc939528836ce7003938f"><code>f4f9166</code></a>
We're incompatible with typescript v7</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/f0aeb5bc66536573035fcabee283d1b28d5f39c1"><code>f0aeb5b</code></a>
Release 5.64.0</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/77d683e2b75b44c7fd5fc47b59621398b86028ea"><code>77d683e</code></a>
Edit docs</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/19dd367764fa078fb9c93bb0a715492ff0581098"><code>19dd367</code></a>
Support input resolver from args in plugins (resolve <a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1274">#1274</a>)</li>
<li><a
href="https://github.com/webpro-nl/knip/commit/136a14bc4ff4138389a831afc62f5406f66223a5"><code>136a14b</code></a>
feat: add plugin for <code>bumpp</code> (<a
href="https://github.com/webpro-nl/knip/tree/HEAD/packages/knip/issues/1278">#1278</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/webpro-nl/knip/commits/5.64.1/packages/knip">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=knip&package-manager=npm_and_yarn&previous-version=5.51.0&new-version=5.64.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:27:05 +00:00
dependabot[bot] 3dc857c75d chore: bump @radix-ui/react-slot from 1.1.1 to 1.2.3 in /site (#20072)
Bumps [@radix-ui/react-slot](https://github.com/radix-ui/primitives)
from 1.1.1 to 1.2.3.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@radix-ui/react-slot&package-manager=npm_and_yarn&previous-version=1.1.1&new-version=1.2.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:26:46 +00:00
dependabot[bot] 1c93846737 chore: bump @types/node from 20.17.16 to 20.19.19 in /offlinedocs (#20064)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
from 20.17.16 to 20.19.19.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/node&package-manager=npm_and_yarn&previous-version=20.17.16&new-version=20.19.19)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:26:11 +00:00
dependabot[bot] 057b0eb07e chore: bump @playwright/test from 1.50.1 to 1.55.1 in /site (#20071)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from
1.50.1 to 1.55.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/microsoft/playwright/releases"><code>@​playwright/test</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v1.55.1</h2>
<h3>Highlights</h3>
<p><a
href="https://redirect.github.com/microsoft/playwright/issues/37479">microsoft/playwright#37479</a>
- [Bug]: Upgrade Chromium to 140.0.7339.186.
<a
href="https://redirect.github.com/microsoft/playwright/issues/37147">microsoft/playwright#37147</a>
- [Regression]: Internal error: step id not found.
<a
href="https://redirect.github.com/microsoft/playwright/issues/37146">microsoft/playwright#37146</a>
- [Regression]: HTML reporter displays a broken chip link when there are
no projects.
<a
href="https://redirect.github.com/microsoft/playwright/pull/37137">microsoft/playwright#37137</a>
- Revert &quot;fix(a11y): track inert elements as hidden&quot;.</p>
<h2>Browser Versions</h2>
<ul>
<li>Chromium 140.0.7339.186</li>
<li>Mozilla Firefox 141.0</li>
<li>WebKit 26.0</li>
</ul>
<p>This version was also tested against the following stable
channels:</p>
<ul>
<li>Google Chrome 139</li>
<li>Microsoft Edge 139</li>
</ul>
<h2>v1.55.0</h2>
<h2>New APIs</h2>
<ul>
<li>New Property <a
href="https://playwright.dev/docs/api/class-teststepinfo#test-step-info-title-path">testStepInfo.titlePath</a>
Returns the full title path starting from the test file, including test
and step titles.</li>
</ul>
<h2>Codegen</h2>
<ul>
<li>Automatic <code>toBeVisible()</code> assertions: Codegen can now
generate automatic <code>toBeVisible()</code> assertions for common UI
interactions. This feature can be enabled in the Codegen settings
UI.</li>
</ul>
<h2>Breaking Changes</h2>
<ul>
<li>⚠️ Dropped support for Chromium extension manifest v2.</li>
</ul>
<h2>Miscellaneous</h2>
<ul>
<li>Added support for Debian 13 &quot;Trixie&quot;.</li>
</ul>
<h2>Browser Versions</h2>
<ul>
<li>Chromium 140.0.7339.16</li>
<li>Mozilla Firefox 141.0</li>
<li>WebKit 26.0</li>
</ul>
<p>This version was also tested against the following stable
channels:</p>
<ul>
<li>Google Chrome 139</li>
<li>Microsoft Edge 139</li>
</ul>
<h2>v1.54.2</h2>
<h3>Highlights</h3>
<p><a
href="https://redirect.github.com/microsoft/playwright/issues/36714">microsoft/playwright#36714</a>
- [Regression]: Codegen is not able to launch in Administrator Terminal
on Windows (ProtocolError: Protocol error)
<a
href="https://redirect.github.com/microsoft/playwright/issues/36828">microsoft/playwright#36828</a>
- [Regression]: Playwright Codegen keeps spamming with selected option
<a
href="https://redirect.github.com/microsoft/playwright/issues/36810">microsoft/playwright#36810</a>
- [Regression]: Starting Codegen with target language doesn't work
anymore</p>
<h2>Browser Versions</h2>
<ul>
<li>Chromium 139.0.7258.5</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/microsoft/playwright/commit/ae51df7a35888f663553ab4e9c45d6cb6335397f"><code>ae51df7</code></a>
chore: mark v1.55.1 (<a
href="https://redirect.github.com/microsoft/playwright/issues/37530">#37530</a>)</li>
<li><a
href="https://github.com/microsoft/playwright/commit/86dde294ce7fc9dd62d2f7ce5afd0c79cff50140"><code>86dde29</code></a>
feat(chromium): roll to r1193 (<a
href="https://redirect.github.com/microsoft/playwright/issues/37529">#37529</a>)</li>
<li><a
href="https://github.com/microsoft/playwright/commit/86328bc9f43da2fe2b9aba2822c79f8b4c0c7f72"><code>86328bc</code></a>
chore: do not use -k option (<a
href="https://redirect.github.com/microsoft/playwright/issues/37532">#37532</a>)</li>
<li><a
href="https://github.com/microsoft/playwright/commit/63799ba68340fde11445d4f48c12515c28dcc92a"><code>63799ba</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37214">#37214</a>):
docs: fix method names in release notes</li>
<li><a
href="https://github.com/microsoft/playwright/commit/21e29a42ab090d7848a3c3255f05335ba0db424d"><code>21e29a4</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37153">#37153</a>):
fix(html): don't display a chip with empty content with ...</li>
<li><a
href="https://github.com/microsoft/playwright/commit/ba62e6ab0d25f7415ffdf121e4e186aadb213be5"><code>ba62e6a</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37149">#37149</a>):
fix(test): attaching in boxed fixture</li>
<li><a
href="https://github.com/microsoft/playwright/commit/25bb073f2697c98995871d40828604b0836c29b7"><code>25bb073</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37137">#37137</a>):
Revert &quot;fix(a11y): track inert elements as hidden (<a
href="https://redirect.github.com/microsoft/playwright/issues/36947">#36947</a>)&quot;</li>
<li><a
href="https://github.com/microsoft/playwright/commit/f992162f04ae0b0b5a0f4b6114b894215be98995"><code>f992162</code></a>
chore: mark v1.55.0 (<a
href="https://redirect.github.com/microsoft/playwright/issues/37121">#37121</a>)</li>
<li><a
href="https://github.com/microsoft/playwright/commit/4a92ea00253106c8bd4234b8d5be2e205d049012"><code>4a92ea0</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37113">#37113</a>):
docs: add release-notes for v1.55</li>
<li><a
href="https://github.com/microsoft/playwright/commit/aa05507bbabc170abf7775bf0d3ddd2438dc384a"><code>aa05507</code></a>
cherry-pick(<a
href="https://redirect.github.com/microsoft/playwright/issues/37114">#37114</a>):
test: move browser._launchServer in child process</li>
<li>Additional commits viewable in <a
href="https://github.com/microsoft/playwright/compare/v1.50.1...v1.55.1">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~playwright-bot">playwright-bot</a>, a new
releaser for <code>@​playwright/test</code> since your current
version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@playwright/test&package-manager=npm_and_yarn&previous-version=1.50.1&new-version=1.55.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:26:07 +00:00
dependabot[bot] 48d19c5546 chore: bump @fontsource/fira-code from 5.2.5 to 5.2.7 in /site (#20069)
Bumps
[@fontsource/fira-code](https://github.com/fontsource/font-files/tree/HEAD/fonts/google/fira-code)
from 5.2.5 to 5.2.7.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/fontsource/font-files/commits/HEAD/fonts/google/fira-code">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@fontsource/fira-code&package-manager=npm_and_yarn&previous-version=5.2.5&new-version=5.2.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:24:56 +00:00
dependabot[bot] 9d8579bbe3 chore: bump chromatic from 11.25.2 to 11.29.0 in /site (#20067)
Bumps [chromatic](https://github.com/chromaui/chromatic-cli) from
11.25.2 to 11.29.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/chromaui/chromatic-cli/releases">chromatic's
releases</a>.</em></p>
<blockquote>
<h2>v11.29.0</h2>
<h4>🚀 Enhancement</h4>
<ul>
<li>Export <code>createLogger</code> and make all arguments optional <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1182">#1182</a>
(<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Gert Hengeveld (<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h2>v11.28.4</h2>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Revert &quot;Add git command logging and pass
<code>--no-relative</code> to <code>git diff</code>.&quot; <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1183">#1183</a>
(<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Gert Hengeveld (<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h2>v11.28.3</h2>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Add git command logging and pass <code>--no-relative</code> to
<code>git diff</code>. <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1181">#1181</a>
(<a href="https://github.com/tmeasday"><code>@​tmeasday</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Tom Coleman (<a
href="https://github.com/tmeasday"><code>@​tmeasday</code></a>)</li>
</ul>
<h2>v11.28.2</h2>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Use pagination to get all tests in the build <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1175">#1175</a>
(<a href="https://github.com/codykaup"><code>@​codykaup</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Cody Kaup (<a
href="https://github.com/codykaup"><code>@​codykaup</code></a>)</li>
</ul>
<h2>v11.28.1</h2>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Return additional build info on action rerun <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1174">#1174</a>
(<a
href="https://github.com/justin-thurman"><code>@​justin-thurman</code></a>)</li>
<li>✏️ Update help text to reflect actual state of
exitOnceUploaded. <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1169">#1169</a>
(<a href="https://github.com/jwir3"><code>@​jwir3</code></a>)</li>
</ul>
<h4>Authors: 2</h4>
<ul>
<li>Justin Thurman (<a
href="https://github.com/justin-thurman"><code>@​justin-thurman</code></a>)</li>
<li>Scott Johnson (<a
href="https://github.com/jwir3"><code>@​jwir3</code></a>)</li>
</ul>
<h2>v11.28.0</h2>
<h4>🚀 Enhancement</h4>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/chromaui/chromatic-cli/blob/main/CHANGELOG.md">chromatic's
changelog</a>.</em></p>
<blockquote>
<h1>v11.29.0 (Fri May 23 2025)</h1>
<h4>🚀 Enhancement</h4>
<ul>
<li>Export <code>createLogger</code> and make all arguments optional <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1182">#1182</a>
(<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Gert Hengeveld (<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<hr />
<h1>v11.28.4 (Fri May 23 2025)</h1>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Revert &quot;Add git command logging and pass
<code>--no-relative</code> to <code>git diff</code>.&quot; <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1183">#1183</a>
(<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Gert Hengeveld (<a
href="https://github.com/ghengeveld"><code>@​ghengeveld</code></a>)</li>
</ul>
<hr />
<h1>v11.28.3 (Thu May 22 2025)</h1>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Add git command logging and pass <code>--no-relative</code> to
<code>git diff</code>. <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1181">#1181</a>
(<a href="https://github.com/tmeasday"><code>@​tmeasday</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Tom Coleman (<a
href="https://github.com/tmeasday"><code>@​tmeasday</code></a>)</li>
</ul>
<hr />
<h1>v11.28.2 (Thu Apr 17 2025)</h1>
<h4>🐛 Bug Fix</h4>
<ul>
<li>Use pagination to get all tests in the build <a
href="https://redirect.github.com/chromaui/chromatic-cli/pull/1175">#1175</a>
(<a href="https://github.com/codykaup"><code>@​codykaup</code></a>)</li>
</ul>
<h4>Authors: 1</h4>
<ul>
<li>Cody Kaup (<a
href="https://github.com/codykaup"><code>@​codykaup</code></a>)</li>
</ul>
<hr />
<h1>v11.28.1 (Thu Apr 17 2025)</h1>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/6004745462f2b0e925f5ffd02427e1d93b5a4457"><code>6004745</code></a>
Bump version to: 11.29.0 [skip ci]</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/15caa2ae276e61cf5a1d4158a0a3b0291a526a68"><code>15caa2a</code></a>
Update CHANGELOG.md [skip ci]</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/092db515ebca02d057c1b7ed91712fd6c7720d1b"><code>092db51</code></a>
Merge pull request <a
href="https://redirect.github.com/chromaui/chromatic-cli/issues/1182">#1182</a>
from chromaui/export-createLogger</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/71ad899f7f824278ab05e0ac854ee88a78da3509"><code>71ad899</code></a>
Merge branch 'main' into export-createLogger</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/e6127ede8887d61107ec725f057a410f8c2898b9"><code>e6127ed</code></a>
Bump version to: 11.28.4 [skip ci]</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/88ca20e2f8eb6f78fc289872c043935ad044474e"><code>88ca20e</code></a>
Update CHANGELOG.md [skip ci]</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/ac3689eae4ccbabccc63298fd935dc20d352d3ad"><code>ac3689e</code></a>
Merge pull request <a
href="https://redirect.github.com/chromaui/chromatic-cli/issues/1183">#1183</a>
from chromaui/revert-1181-tom/cap-2840-turbosnap-det...</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/46ef6376e1e7dd1442e5986828bdd251d5cb4d8c"><code>46ef637</code></a>
Revert &quot;Add git command logging and pass <code>--no-relative</code>
to <code>git diff</code>.&quot;</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/fd1bbfe989c2c0e56ef6d7a40949b1a692bc4d32"><code>fd1bbfe</code></a>
Export createLogger and make all arguments optional</li>
<li><a
href="https://github.com/chromaui/chromatic-cli/commit/24e63156d595d9e2b144247661946125e3f9110c"><code>24e6315</code></a>
Bump version to: 11.28.3 [skip ci]</li>
<li>Additional commits viewable in <a
href="https://github.com/chromaui/chromatic-cli/compare/v11.25.2...v11.29.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=chromatic&package-manager=npm_and_yarn&previous-version=11.25.2&new-version=11.29.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:23:09 +00:00
dependabot[bot] 4553614bff chore: bump the mui group across 1 directory with 5 updates (#20054)
Bumps the mui group with 5 updates in the /site directory:

| Package | From | To |
| --- | --- | --- |
|
[@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material)
| `5.16.14` | `5.18.0` |
|
[@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material)
| `5.16.14` | `5.18.0` |
|
[@mui/system](https://github.com/mui/material-ui/tree/HEAD/packages/mui-system)
| `5.16.14` | `5.18.0` |
|
[@mui/utils](https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils)
| `5.16.14` | `5.17.1` |
|
[@mui/x-tree-view](https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view)
| `7.25.0` | `7.29.10` |


Updates `@mui/icons-material` from 5.16.14 to 5.18.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/releases"><code>@​mui/icons-material</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v5.18.0</h2>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/blob/v5.18.0/CHANGELOG.md"><code>@​mui/icons-material</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>5.18.0</h2>
<!-- raw HTML omitted -->
<p><em>Jun 26, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mui/material-ui/commit/6b50143dcfc2aad56a37c9f2e1f14256e9a65c4e"><code>6b50143</code></a>
5.18.0 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/46432">#46432</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/0d509f8a4a48031a1e25974a1604b803892ce94e"><code>0d509f8</code></a>
[release] v5.17.1 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45614">#45614</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/b0dc8270c5a2896293ff8b61052120a13819f0a1"><code>b0dc827</code></a>
v5.17.0 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material/issues/45605">#45605</a>)</li>
<li>See full diff in <a
href="https://github.com/mui/material-ui/commits/v5.18.0/packages/mui-icons-material">compare
view</a></li>
</ul>
</details>
<br />

Updates `@mui/material` from 5.16.14 to 5.18.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/releases"><code>@​mui/material</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v5.18.0</h2>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/blob/v5.18.0/CHANGELOG.md"><code>@​mui/material</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>5.18.0</h2>
<!-- raw HTML omitted -->
<p><em>Jun 26, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mui/material-ui/commit/6b50143dcfc2aad56a37c9f2e1f14256e9a65c4e"><code>6b50143</code></a>
5.18.0 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46432">#46432</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/f233c5b08681c5ea72c4be6e696194e9a5d57e1c"><code>f233c5b</code></a>
[material-nextjs][system] Backport CSS layers to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/46320">#46320</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/0d509f8a4a48031a1e25974a1604b803892ce94e"><code>0d509f8</code></a>
[release] v5.17.1 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45614">#45614</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/2baafb387125a74262ad6f3a76252d379de32678"><code>2baafb3</code></a>
[v5.x][core] Fix types version (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45612">#45612</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/b0dc8270c5a2896293ff8b61052120a13819f0a1"><code>b0dc827</code></a>
v5.17.0 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45605">#45605</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/371a1fe00fd826596cbba279f997595f20d18010"><code>371a1fe</code></a>
[material-ui] Support nested theme when upper theme is CSS vars theme
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-material/issues/45604">#45604</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/c4392057185ed6ab62961f1cb777d5d86919d744"><code>c439205</code></a>
[material-ui][TextareaAutosize] Temporarily disconnect ResizeObserver to
avoi...</li>
<li>See full diff in <a
href="https://github.com/mui/material-ui/commits/v5.18.0/packages/mui-material">compare
view</a></li>
</ul>
</details>
<br />

Updates `@mui/system` from 5.16.14 to 5.18.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/releases"><code>@​mui/system</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v5.18.0</h2>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/blob/v5.18.0/CHANGELOG.md"><code>@​mui/system</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>5.18.0</h2>
<!-- raw HTML omitted -->
<p><em>Jun 26, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<p>CSS layers make it easier to override styles by splitting a single
style sheet into multiple layers.
To learn more, check out the <a
href="https://v5.mui.com/material-ui/customization/css-layers/">CSS
layers documentation</a>.</p>
<h3><code>@mui/system@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3><code>@mui/material-nextjs@5.18.0</code></h3>
<ul>
<li>Backport CSS layers from v7 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46320">#46320</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<h3>Docs</h3>
<ul>
<li>Ease migration to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45991">#45991</a>)
<a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a></li>
<li>Add AccordionSummary to the breaking change migration (<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a>) (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45972">#45972</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/oliviertassinari"><code>@​oliviertassinari</code></a>,
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mui/material-ui/commit/6b50143dcfc2aad56a37c9f2e1f14256e9a65c4e"><code>6b50143</code></a>
5.18.0 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46432">#46432</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/f233c5b08681c5ea72c4be6e696194e9a5d57e1c"><code>f233c5b</code></a>
[material-nextjs][system] Backport CSS layers to v5 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/46320">#46320</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/0d509f8a4a48031a1e25974a1604b803892ce94e"><code>0d509f8</code></a>
[release] v5.17.1 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45614">#45614</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/2baafb387125a74262ad6f3a76252d379de32678"><code>2baafb3</code></a>
[v5.x][core] Fix types version (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-system/issues/45612">#45612</a>)</li>
<li>See full diff in <a
href="https://github.com/mui/material-ui/commits/v5.18.0/packages/mui-system">compare
view</a></li>
</ul>
</details>
<br />

Updates `@mui/utils` from 5.16.14 to 5.17.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/releases"><code>@​mui/utils</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mui/material-ui/blob/v5.17.1/CHANGELOG.md"><code>@​mui/utils</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>v5.17.1</h2>
<!-- raw HTML omitted -->
<p><em>Mar 18, 2025</em></p>
<p>This release fixes the <code>@mui/types</code> dependencies in all
packages to fix a package layout bug (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45612">#45612</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></p>
<h2>v5.17.0</h2>
<p><em>Mar 18, 2025</em></p>
<p>A big thanks to the 2 contributors who made this release
possible.</p>
<h3><code>@mui/material@5.17.0</code></h3>
<ul>
<li>[TextareaAutosize] Temporarily disconnect ResizeObserver to avoid
loop error (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/44540">#44540</a>)
(<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45238">#45238</a>)
<a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a></li>
<li>Support nested theme when upper theme is CSS vars theme (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45604">#45604</a>)
<a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></li>
</ul>
<p>All contributors of this release in alphabetical order: <a
href="https://github.com/DiegoAndai"><code>@​DiegoAndai</code></a>, <a
href="https://github.com/siriwatknp"><code>@​siriwatknp</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mui/material-ui/commit/0d509f8a4a48031a1e25974a1604b803892ce94e"><code>0d509f8</code></a>
[release] v5.17.1 (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45614">#45614</a>)</li>
<li><a
href="https://github.com/mui/material-ui/commit/2baafb387125a74262ad6f3a76252d379de32678"><code>2baafb3</code></a>
[v5.x][core] Fix types version (<a
href="https://github.com/mui/material-ui/tree/HEAD/packages/mui-utils/issues/45612">#45612</a>)</li>
<li>See full diff in <a
href="https://github.com/mui/material-ui/commits/v5.17.1/packages/mui-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@mui/x-tree-view` from 7.25.0 to 7.29.10
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/mui-x/releases"><code>@​mui/x-tree-view</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v7.29.10</h2>
<p>We'd like to extend a big thank you to the 1 contributor who made
this release possible. Here are some highlights :</p>
<ul>
<li>🐞 Bugfixes</li>
</ul>
<p>The following is the team member who have contributed to this
release:
<a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></p>
<h3>Tree View</h3>
<h4><code>@mui/x-tree-view@7.29.10</code></h4>
<ul>
<li>[tree view] Fix the expansion toggle on icon container with old DOM
structure (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/19546">#19546</a>)
<a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></li>
</ul>
<h4><code>@mui/x-tree-view-pro@7.29.10</code> <a
href="https://mui.com/r/x-pro-svg-link" title="Pro plan"><img
src="https://mui.com/r/x-pro-svg" alt="pro" /></a></h4>
<p>Same changes as in <code>@mui/x-tree-view@7.29.10</code>.</p>
<h2>v7.29.9</h2>
<p>We'd like to extend a big thank you to the 5 contributors who made
this release possible. Here are some highlights :</p>
<ul>
<li>🐞 Bugfixes</li>
</ul>
<p>Special thanks go out to the community members for their valuable
contributions:
<a href="https://github.com/nusr"><code>@​nusr</code></a></p>
<p>The following are all team members who have contributed to this
release:
<a
href="https://github.com/cherniavskii"><code>@​cherniavskii</code></a>,
<a
href="https://github.com/mapache-salvaje"><code>@​mapache-salvaje</code></a>,
<a href="https://github.com/MBilalShafi"><code>@​MBilalShafi</code></a>,
<a
href="https://github.com/rita-codes"><code>@​rita-codes</code></a></p>
<!-- raw HTML omitted -->
<h3>Data Grid</h3>
<h4><code>@mui/x-data-grid@7.29.9</code></h4>
<ul>
<li>[DataGrid] Do not call <code>preProcessEditCellProps()</code> if
cell is not editable based on <code>isCellEditable()</code> (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/19082">#19082</a>)
<a href="https://github.com/nusr"><code>@​nusr</code></a></li>
</ul>
<h4><code>@mui/x-data-grid-pro@7.29.9</code> <a
href="https://mui.com/r/x-pro-svg-link" title="Pro plan"><img
src="https://mui.com/r/x-pro-svg" alt="pro" /></a></h4>
<p>Same changes as in <code>@mui/x-data-grid@7.29.9</code>, plus:</p>
<ul>
<li>[DataGridPro] Fix row ordering not auto-scrolling when moving beyond
viewport (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/18718">#18718</a>)
<a
href="https://github.com/MBilalShafi"><code>@​MBilalShafi</code></a></li>
</ul>
<h4><code>@mui/x-data-grid-premium@7.29.9</code> <a
href="https://mui.com/r/x-premium-svg-link" title="Premium plan"><img
src="https://mui.com/r/x-premium-svg" alt="premium" /></a></h4>
<p>Same changes as in <code>@mui/x-data-grid-pro@7.29.9</code>.</p>
<h3>Docs</h3>
<ul>
<li>[docs] Audit and revise the Data Grid Pro row docs (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/18629">#18629</a>)
<a
href="https://github.com/mapache-salvaje"><code>@​mapache-salvaje</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mui/mui-x/blob/v7.29.10/CHANGELOG.md"><code>@​mui/x-tree-view</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>v7.29.10</h2>
<p><em>Sep 25, 2025</em></p>
<p>We'd like to extend a big thank you to the 1 contributor who made
this release possible. Here are some highlights :</p>
<ul>
<li>🐞 Bugfixes</li>
</ul>
<p>The following is the team member who have contributed to this
release:
<a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></p>
<h3>Tree View</h3>
<h4><code>@mui/x-tree-view@7.29.10</code></h4>
<ul>
<li>[tree view] Fix the expansion toggle on icon container with old DOM
structure (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/19546">#19546</a>)
<a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></li>
</ul>
<h4><code>@mui/x-tree-view-pro@7.29.10</code> <a
href="https://mui.com/r/x-pro-svg-link" title="Pro plan"><img
src="https://mui.com/r/x-pro-svg" alt="pro" /></a></h4>
<p>Same changes as in <code>@mui/x-tree-view@7.29.10</code>.</p>
<!-- raw HTML omitted -->
<h2>7.29.9</h2>
<p><em>Aug 7, 2025</em></p>
<p>We'd like to extend a big thank you to the 5 contributors who made
this release possible. Here are some highlights :</p>
<ul>
<li>🐞 Bugfixes</li>
</ul>
<p>Special thanks go out to the community members for their valuable
contributions:
<a href="https://github.com/nusr"><code>@​nusr</code></a></p>
<p>The following are all team members who have contributed to this
release:
<a
href="https://github.com/cherniavskii"><code>@​cherniavskii</code></a>,
<a
href="https://github.com/mapache-salvaje"><code>@​mapache-salvaje</code></a>,
<a href="https://github.com/MBilalShafi"><code>@​MBilalShafi</code></a>,
<a
href="https://github.com/rita-codes"><code>@​rita-codes</code></a></p>
<!-- raw HTML omitted -->
<h3>Data Grid</h3>
<h4><code>@mui/x-data-grid@7.29.9</code></h4>
<ul>
<li>[DataGrid] Do not call <code>preProcessEditCellProps()</code> if
cell is not editable based on <code>isCellEditable()</code> (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/19082">#19082</a>)
<a href="https://github.com/nusr"><code>@​nusr</code></a></li>
</ul>
<h4><code>@mui/x-data-grid-pro@7.29.9</code> <a
href="https://mui.com/r/x-pro-svg-link" title="Pro plan"><img
src="https://mui.com/r/x-pro-svg" alt="pro" /></a></h4>
<p>Same changes as in <code>@mui/x-data-grid@7.29.9</code>, plus:</p>
<ul>
<li>[DataGridPro] Fix row ordering not auto-scrolling when moving beyond
viewport (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/18718">#18718</a>)
<a
href="https://github.com/MBilalShafi"><code>@​MBilalShafi</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mui/mui-x/commit/a1d26c9ea9ca8d2f1556fd3cad9cbb6753fd6fed"><code>a1d26c9</code></a>
[release] v7.29.10 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/19705">#19705</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/c54bb3d55e772f0581e753c9eddc6cb79a94c82d"><code>c54bb3d</code></a>
[tree view] Fix the expansion toggle on icon container with old DOM
structure...</li>
<li><a
href="https://github.com/mui/mui-x/commit/a80b51b5268ec21a5680677dfbc53b6da2366a32"><code>a80b51b</code></a>
V.7.29.1 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17520">#17520</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/1cead40be63a401f263cbe5a3f2bbb370d4fef2c"><code>1cead40</code></a>
[TreeView] Fix drag and drop issue with label editing (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17415">#17415</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/80a4c73f9e215f7ea2e1e5cdbbe8e80ec5a8ed80"><code>80a4c73</code></a>
v7.29.0 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17383">#17383</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/3fe4c2a957202a86836e28ed5cc2d0c69a49de24"><code>3fe4c2a</code></a>
[core] Cleanup <code>@mui</code> dependency versions (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17390">#17390</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/714c27a35669ddb6607b8304f7dde004cebc12fa"><code>714c27a</code></a>
v7.28.1 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17073">#17073</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/77e84ecc93ccf68211b1bd0cc9d74b1d59b5f741"><code>77e84ec</code></a>
v7.28.0 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/17002">#17002</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/d83ee5c49d3b881a816385f5a59ccff8b2e503d4"><code>d83ee5c</code></a>
[core] Allow MUI Core v7 in dependencies (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/16951">#16951</a>)</li>
<li><a
href="https://github.com/mui/mui-x/commit/3b9664368b060d57fc67f9961104d63c47299b54"><code>3b96643</code></a>
v7.26.0 (<a
href="https://github.com/mui/mui-x/tree/HEAD/packages/x-tree-view/issues/16498">#16498</a>)</li>
<li>See full diff in <a
href="https://github.com/mui/mui-x/commits/v7.29.10/packages/x-tree-view">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~rita-codes">rita-codes</a>, a new releaser
for <code>@​mui/x-tree-view</code> since your current version.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:19:02 +00:00
dependabot[bot] f4c3469a9c chore: bump sanitize-html and @types/sanitize-html in /offlinedocs (#20062)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html)
and
[@types/sanitize-html](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sanitize-html).
These dependencies needed to be updated together.
Updates `sanitize-html` from 2.14.0 to 2.17.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md">sanitize-html's
changelog</a>.</em></p>
<blockquote>
<h2>2.17.0 (2025-05-14)</h2>
<ul>
<li>Add <code>preserveEscapedAttributes</code>, allowing attributes on
escaped disallowed tags to be retained. Thanks to <a
href="https://github.com/benelliott">Ben Elliot</a> for this new
option.</li>
</ul>
<h2>2.16.0 (2025-04-16)</h2>
<ul>
<li>Add <code>onOpenTag</code> and <code>onCloseTag</code> events to
enable advanced filtering to hook into the parser. Thanks to <a
href="https://github.com/naktinis">Rimvydas Naktinis</a>.</li>
</ul>
<h2>2.15.0 (2025-03-19)</h2>
<ul>
<li>Allow keeping tag content when discarding with exclusive filter by
returning <code>&quot;excludeTag&quot;</code>. Thanks to <a
href="https://github.com/rChaoz">rChaoz</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/86efc067a63515e08ecfc47f94d8bca0e3715030"><code>86efc06</code></a>
Merge pull request <a
href="https://redirect.github.com/apostrophecms/sanitize-html/issues/705">#705</a>
from apostrophecms/release-2.17.0</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/c487e7796a1dea5b1dd7a083b4efc47f7988cc51"><code>c487e77</code></a>
release 2.17.0</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/da16903beda114b6d65f2de72b2f8054af010163"><code>da16903</code></a>
Merge pull request <a
href="https://redirect.github.com/apostrophecms/sanitize-html/issues/704">#704</a>
from apostrophecms/add-thanks-to-changelog</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/0e5d881f48c4aa40536d070337159e92edc646b1"><code>0e5d881</code></a>
Update CHANGELOG</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/614e7dfc2ce156baff560254cdf5be77f0560e64"><code>614e7df</code></a>
Merge pull request <a
href="https://redirect.github.com/apostrophecms/sanitize-html/issues/668">#668</a>
from benelliott/feature/preserve-escaped-attributes-2</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/8628ceaa17fa1672037761985c4cb454537a10bd"><code>8628cea</code></a>
Update README.md</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/f07ce9dff8e047655f2f42db1f427b10eb8f982d"><code>f07ce9d</code></a>
README.md: Add warning on usage of
<code>preserveEscapedAttributes</code></li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/27de3a879ad36d01906e5297f779a5393c700c17"><code>27de3a8</code></a>
Add test demonstrating that preserveEscapedAttributes doesn't affect
behaviou...</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/3d2893e01336427c7999c31ea554ebe868780fff"><code>3d2893e</code></a>
Add documentation for <code>preserveEscapedAttributes</code> option</li>
<li><a
href="https://github.com/apostrophecms/sanitize-html/commit/ae1dc35af6b48ab9b25513342c16183c6c66a118"><code>ae1dc35</code></a>
Add <code>preserveEscapedAttributes</code> option to allow attributes on
escaped disallo...</li>
<li>Additional commits viewable in <a
href="https://github.com/apostrophecms/sanitize-html/compare/2.14.0...2.17.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/sanitize-html` from 2.13.0 to 2.16.0
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sanitize-html">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:18:43 +00:00
dependabot[bot] 6680026ebc chore: bump @chakra-ui/react from 2.10.5 to 2.10.9 in /offlinedocs (#20060)
Bumps
[@chakra-ui/react](https://github.com/chakra-ui/chakra-ui/tree/HEAD/packages/react)
from 2.10.5 to 2.10.9.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/chakra-ui/chakra-ui/commits/@chakra-ui/react@2.10.9/packages/react">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@chakra-ui/react&package-manager=npm_and_yarn&previous-version=2.10.5&new-version=2.10.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:18:02 +00:00
dependabot[bot] c9329084b7 chore: bump eslint-config-next from 14.2.23 to 14.2.33 in /offlinedocs (#20059)
Bumps
[eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next)
from 14.2.23 to 14.2.33.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/next.js/releases">eslint-config-next's
releases</a>.</em></p>
<blockquote>
<h2>v14.2.33</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>omit searchParam data from FlightRouterState before transport (<a
href="https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next/issues/80734">#80734</a>)</li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/ztanner"><code>@​ztanner</code></a> for
helping!</p>
<h2>v14.2.32</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>fix router handling when setting a location response header <a
href="https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next/issues/82588">#82588</a></li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/ztanner"><code>@​ztanner</code></a> for
helping!</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/next.js/commit/5a97b408c2d8668bed1642d382fc1d78ed3731cc"><code>5a97b40</code></a>
v14.2.33</li>
<li><a
href="https://github.com/vercel/next.js/commit/89ee5615520d593e328be994b30cd445ef5d5c17"><code>89ee561</code></a>
v14.2.32</li>
<li><a
href="https://github.com/vercel/next.js/commit/55f76620ffb715ca3255bae96fa58f4a1a0848b1"><code>55f7662</code></a>
v14.2.31</li>
<li><a
href="https://github.com/vercel/next.js/commit/243072b7a8b7fb3be74a8d9256847669b131ea7e"><code>243072b</code></a>
v14.2.30</li>
<li><a
href="https://github.com/vercel/next.js/commit/ca9211576c9a21c15980dcc6f022c2cd21542561"><code>ca92115</code></a>
v14.2.29</li>
<li><a
href="https://github.com/vercel/next.js/commit/e65628a237ea76d77d911aedb12d5137fddd90fb"><code>e65628a</code></a>
v14.2.28</li>
<li><a
href="https://github.com/vercel/next.js/commit/43f10b8ead008ba36ddf0a955353949e200c409a"><code>43f10b8</code></a>
v14.2.27</li>
<li><a
href="https://github.com/vercel/next.js/commit/10a042cdca294fd1c6852b320954bc6ccc6064e7"><code>10a042c</code></a>
v14.2.26</li>
<li><a
href="https://github.com/vercel/next.js/commit/d36a1f3c3547b0cb807f30c14aa6250a932349c8"><code>d36a1f3</code></a>
v14.2.25</li>
<li><a
href="https://github.com/vercel/next.js/commit/756be15c4cfecb6fae1c69cae7cfaf336423b6cf"><code>756be15</code></a>
v14.2.24</li>
<li>See full diff in <a
href="https://github.com/vercel/next.js/commits/v14.2.33/packages/eslint-config-next">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=eslint-config-next&package-manager=npm_and_yarn&previous-version=14.2.23&new-version=14.2.33)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:17:35 +00:00
dependabot[bot] 8b6165c438 chore: bump @types/lodash from 4.17.15 to 4.17.20 in /offlinedocs (#20058)
Bumps
[@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash)
from 4.17.15 to 4.17.20.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/lodash&package-manager=npm_and_yarn&previous-version=4.17.15&new-version=4.17.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:17:09 +00:00
Cian Johnston ff930ad4f3 feat(coderd): add ability to search org members by user_id, is_system, github_user_id (#20048)
Adds the ability to search org members by query.
Supported fields: `user_id`, `is_system`, `github_user_id`.
2025-09-30 23:54:21 +01:00
Jaayden Halko 4db5158aed fix: fix flaky test in WorkspacePage.test.tsx (#20033)
re-enabled the skipped test and tested locally. Passed 100 out of 100
times locally.
2025-09-30 23:47:17 +01:00
Bruno Quaresma b8370c25ff fix: add tasks link to sidebar logo (#20038) 2025-09-30 19:09:05 -03:00
david-fraley bf2cfddacb docs: update Claude Code version in docs (#20049) 2025-09-30 21:22:11 +00:00
Bruno Quaresma f009ebd662 feat: minor prompt redesign (#20045)
When I navigate to tasks, the prompt doesn’t feel prominent enough—even
though it should be the primary element on the screen. To give it more
focus, I made some small design changes. Here are both versions:

**Before:**
<img width="1624" height="970" alt="Screenshot 2025-09-30 at 12 10 58"
src="https://github.com/user-attachments/assets/b5451ad5-4d32-4716-8e0f-e4596e7c92c8"
/>

**After:**
<img width="1624" height="970" alt="Screenshot 2025-09-30 at 12 10 46"
src="https://github.com/user-attachments/assets/f478e327-12c0-4f49-829f-8b094a6b613e"
/>
2025-09-30 18:20:45 -03:00
david-fraley fe189b9cc8 fix(docs): add missing div to fix formatting (#20047) 2025-09-30 11:51:15 -07:00
Susana Ferreira 7bddd80d60 feat(site): add task notifications to user settings (#20006)
## Description

Add UI support for the new `Task Events` notification templates (`Task
Working` and `Task Idle`) added in backend PR
https://github.com/coder/coder/pull/19965. The
`/api/v2/notifications/templates/system` endpoint now returns these
templates, therefore this PR keeps the UI in sync.

## Changes

* Added `Task Working` and `Task Idle` to notification template mocks so
Storybook reflects backend data.
* Deployment Settings already lists all templates from the API, so no
code change was needed (stories updated automatically).
* Updated the User Settings filter to include `Task Events` so they
appear for end users.

<img width="2930" height="1666" alt="Screenshot 2025-09-29 at 17 06 22"
src="https://github.com/user-attachments/assets/045d5dd7-13a4-4378-9bf3-9fd86a86e64b"
/>

Depends on: https://github.com/coder/coder/pull/19965
2025-09-30 18:37:25 +01:00
Benjamin Peinhardt ada20d2691 feat: implement coder exp boundary subcommand (#19771) 2025-09-30 12:26:41 -05:00
Cian Johnston 6e4d903a8e fix(coderd): increase task notification dedupe bypass timestamp to 1 minute (#20043) 2025-09-30 16:05:34 +00:00
david-fraley 7481ada293 docs: orient install page towards enterprise and community members (#20041) 2025-09-30 15:58:46 +00:00
david-fraley 95aa16df1b docs: add Tasks Core Principles Page (#19878)
---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Ben Potter <ben@coder.com>
Co-authored-by: Atif Ali <atif@coder.com>
2025-09-30 10:49:39 -05:00
Danny Kopping 4e7b518b65 chore: bump coder/aibridge to v0.1.3 (#20039) 2025-09-30 15:13:39 +00:00
Bruno Quaresma e96d69ba95 feat: delete task from sidebar (#20023)
This adds a way to delete a task from the sidebar. Once this PR is
merged, I’ll also add the option to the table list.

https://github.com/user-attachments/assets/75d714ef-afe1-4d9c-b907-1eab3a59f26b

Right now, after deleting a task, the data still shows—that’s a separate
issue related to how task data is loaded in the FE. A proper tasks
endpoint is now available, and that should be addressed as part of
[https://github.com/coder/internal/issues/904](https://github.com/coder/internal/issues/904).

Related to https://github.com/coder/coder/issues/19525
2025-09-30 10:02:02 -03:00
Cian Johnston bf021e627e ci: add option to skip cleanup in ai triage workflow (#20031)
Now that we are creating the workspaces as the triggering user, it's
more ergonomic to allow the workspace owner to review the task on
completion.
2025-09-30 12:07:44 +01:00
dependabot[bot] 8cfa2c4b52 chore: bump axios from 1.8.2 to 1.12.0 in /site (#20030)
Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/releases">axios's
releases</a>.</em></p>
<blockquote>
<h2>Release v1.12.0</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li>adding build artifacts (<a
href="https://github.com/axios/axios/commit/9ec86de257bfa33856571036279169f385ed92bd">9ec86de</a>)</li>
<li>dont add dist on release (<a
href="https://github.com/axios/axios/commit/a2edc3606a4f775d868a67bb3461ff18ce7ecd11">a2edc36</a>)</li>
<li><strong>fetch-adapter:</strong> set correct Content-Type for Node
FormData (<a
href="https://redirect.github.com/axios/axios/issues/6998">#6998</a>)
(<a
href="https://github.com/axios/axios/commit/a9f47afbf3224d2ca987dbd8188789c7ea853c5d">a9f47af</a>)</li>
<li><strong>node:</strong> enforce maxContentLength for data: URLs (<a
href="https://redirect.github.com/axios/axios/issues/7011">#7011</a>)
(<a
href="https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593">945435f</a>)</li>
<li>package exports (<a
href="https://redirect.github.com/axios/axios/issues/5627">#5627</a>)
(<a
href="https://github.com/axios/axios/commit/aa78ac23fc9036163308c0f6bd2bb885e7af3f36">aa78ac2</a>)</li>
<li><strong>params:</strong> removing '[' and ']' from URL encode
exclude characters (<a
href="https://redirect.github.com/axios/axios/issues/3316">#3316</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/5715">#5715</a>)
(<a
href="https://github.com/axios/axios/commit/6d84189349c43b1dcdd977b522610660cc4c7042">6d84189</a>)</li>
<li>release pr run (<a
href="https://github.com/axios/axios/commit/fd7f404488b2c4f238c2fbe635b58026a634bfd2">fd7f404</a>)</li>
<li><strong>types:</strong> change the type guard on isCancel (<a
href="https://redirect.github.com/axios/axios/issues/5595">#5595</a>)
(<a
href="https://github.com/axios/axios/commit/0dbb7fd4f61dc568498cd13a681fa7f907d6ec7e">0dbb7fd</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li><strong>adapter:</strong> surface low‑level network error details;
attach original error via cause (<a
href="https://redirect.github.com/axios/axios/issues/6982">#6982</a>)
(<a
href="https://github.com/axios/axios/commit/78b290c57c978ed2ab420b90d97350231c9e5d74">78b290c</a>)</li>
<li><strong>fetch:</strong> add fetch, Request, Response env config
variables for the adapter; (<a
href="https://redirect.github.com/axios/axios/issues/7003">#7003</a>)
(<a
href="https://github.com/axios/axios/commit/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b">c959ff2</a>)</li>
<li>support reviver on JSON.parse (<a
href="https://redirect.github.com/axios/axios/issues/5926">#5926</a>)
(<a
href="https://github.com/axios/axios/commit/2a9763426e43d996fd60d01afe63fa6e1f5b4fca">2a97634</a>),
closes <a
href="https://redirect.github.com/axios/axios/issues/5924">#5924</a></li>
<li><strong>types:</strong> extend AxiosResponse interface to include
custom headers type (<a
href="https://redirect.github.com/axios/axios/issues/6782">#6782</a>)
(<a
href="https://github.com/axios/axios/commit/7960d34eded2de66ffd30b4687f8da0e46c4903e">7960d34</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a
href="https://github.com/WillianAgostini" title="+132/-16760
([#7002](https://github.com/axios/axios/issues/7002)
[#5926](https://github.com/axios/axios/issues/5926)
[#6782](https://github.com/axios/axios/issues/6782) )">Willian
Agostini</a></li>
<li><!-- raw HTML omitted --> <a
href="https://github.com/DigitalBrainJS" title="+4263/-293
([#7006](https://github.com/axios/axios/issues/7006)
[#7003](https://github.com/axios/axios/issues/7003) )">Dmitriy
Mozgovoy</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/mkhani01"
title="+111/-15 ([#6982](https://github.com/axios/axios/issues/6982)
)">khani</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/AmeerAssadi"
title="+123/-0 ([#7011](https://github.com/axios/axios/issues/7011)
)">Ameer Assadi</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/emiedonmokumo"
title="+55/-35 ([#6998](https://github.com/axios/axios/issues/6998)
)">Emiedonmokumo Dick-Boro</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/opsysdebug"
title="+8/-8 ([#6980](https://github.com/axios/axios/issues/6980)
)">Zeroday BYTE</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/jasonsaayman"
title="+7/-7 ([#6985](https://github.com/axios/axios/issues/6985)
[#6985](https://github.com/axios/axios/issues/6985) )">Jason
Saayman</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/HealGaren"
title="+5/-7 ([#5715](https://github.com/axios/axios/issues/5715)
)">최예찬</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/gligorkot"
title="+3/-1 ([#5627](https://github.com/axios/axios/issues/5627)
)">Gligor Kotushevski</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/adimit"
title="+2/-1 ([#5595](https://github.com/axios/axios/issues/5595)
)">Aleksandar Dimitrov</a></li>
</ul>
<h2>Release v1.11.0</h2>
<h2>Release notes:</h2>
<h3>Bug Fixes</h3>
<ul>
<li>form-data npm pakcage (<a
href="https://redirect.github.com/axios/axios/issues/6970">#6970</a>)
(<a
href="https://github.com/axios/axios/commit/e72c193722530db538b19e5ddaaa4544d226b253">e72c193</a>)</li>
<li>prevent RangeError when using large Buffers (<a
href="https://redirect.github.com/axios/axios/issues/6961">#6961</a>)
(<a
href="https://github.com/axios/axios/commit/a2214ca1bc60540baf2c80573cea3a0ff91ba9d1">a2214ca</a>)</li>
<li><strong>types:</strong> resolve type discrepancies between ESM and
CJS TypeScript declaration files (<a
href="https://redirect.github.com/axios/axios/issues/6956">#6956</a>)
(<a
href="https://github.com/axios/axios/commit/8517aa16f8d082fc1d5309c642220fa736159110">8517aa1</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/izzygld"
title="+186/-93 ([#6970](https://github.com/axios/axios/issues/6970)
)">izzy goldman</a></li>
<li><!-- raw HTML omitted --> <a
href="https://github.com/manishsahanidev" title="+70/-0
([#6961](https://github.com/axios/axios/issues/6961) )">Manish
Sahani</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/noritaka1166"
title="+12/-10 ([#6938](https://github.com/axios/axios/issues/6938)
[#6939](https://github.com/axios/axios/issues/6939) )">Noritaka
Kobayashi</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/jrnail23"
title="+13/-2 ([#6956](https://github.com/axios/axios/issues/6956)
)">James Nail</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/Tejaswi1305"
title="+1/-1 ([#6894](https://github.com/axios/axios/issues/6894)
)">Tejaswi1305</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/blob/v1.x/CHANGELOG.md">axios's
changelog</a>.</em></p>
<blockquote>
<h1><a
href="https://github.com/axios/axios/compare/v1.11.0...v1.12.0">1.12.0</a>
(2025-09-11)</h1>
<h3>Bug Fixes</h3>
<ul>
<li>adding build artifacts (<a
href="https://github.com/axios/axios/commit/9ec86de257bfa33856571036279169f385ed92bd">9ec86de</a>)</li>
<li>dont add dist on release (<a
href="https://github.com/axios/axios/commit/a2edc3606a4f775d868a67bb3461ff18ce7ecd11">a2edc36</a>)</li>
<li><strong>fetch-adapter:</strong> set correct Content-Type for Node
FormData (<a
href="https://redirect.github.com/axios/axios/issues/6998">#6998</a>)
(<a
href="https://github.com/axios/axios/commit/a9f47afbf3224d2ca987dbd8188789c7ea853c5d">a9f47af</a>)</li>
<li><strong>node:</strong> enforce maxContentLength for data: URLs (<a
href="https://redirect.github.com/axios/axios/issues/7011">#7011</a>)
(<a
href="https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593">945435f</a>)</li>
<li>package exports (<a
href="https://redirect.github.com/axios/axios/issues/5627">#5627</a>)
(<a
href="https://github.com/axios/axios/commit/aa78ac23fc9036163308c0f6bd2bb885e7af3f36">aa78ac2</a>)</li>
<li><strong>params:</strong> removing '[' and ']' from URL encode
exclude characters (<a
href="https://redirect.github.com/axios/axios/issues/3316">#3316</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/5715">#5715</a>)
(<a
href="https://github.com/axios/axios/commit/6d84189349c43b1dcdd977b522610660cc4c7042">6d84189</a>)</li>
<li>release pr run (<a
href="https://github.com/axios/axios/commit/fd7f404488b2c4f238c2fbe635b58026a634bfd2">fd7f404</a>)</li>
<li><strong>types:</strong> change the type guard on isCancel (<a
href="https://redirect.github.com/axios/axios/issues/5595">#5595</a>)
(<a
href="https://github.com/axios/axios/commit/0dbb7fd4f61dc568498cd13a681fa7f907d6ec7e">0dbb7fd</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li><strong>adapter:</strong> surface low‑level network error details;
attach original error via cause (<a
href="https://redirect.github.com/axios/axios/issues/6982">#6982</a>)
(<a
href="https://github.com/axios/axios/commit/78b290c57c978ed2ab420b90d97350231c9e5d74">78b290c</a>)</li>
<li><strong>fetch:</strong> add fetch, Request, Response env config
variables for the adapter; (<a
href="https://redirect.github.com/axios/axios/issues/7003">#7003</a>)
(<a
href="https://github.com/axios/axios/commit/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b">c959ff2</a>)</li>
<li>support reviver on JSON.parse (<a
href="https://redirect.github.com/axios/axios/issues/5926">#5926</a>)
(<a
href="https://github.com/axios/axios/commit/2a9763426e43d996fd60d01afe63fa6e1f5b4fca">2a97634</a>),
closes <a
href="https://redirect.github.com/axios/axios/issues/5924">#5924</a></li>
<li><strong>types:</strong> extend AxiosResponse interface to include
custom headers type (<a
href="https://redirect.github.com/axios/axios/issues/6782">#6782</a>)
(<a
href="https://github.com/axios/axios/commit/7960d34eded2de66ffd30b4687f8da0e46c4903e">7960d34</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a
href="https://github.com/WillianAgostini" title="+132/-16760
([#7002](https://github.com/axios/axios/issues/7002)
[#5926](https://github.com/axios/axios/issues/5926)
[#6782](https://github.com/axios/axios/issues/6782) )">Willian
Agostini</a></li>
<li><!-- raw HTML omitted --> <a
href="https://github.com/DigitalBrainJS" title="+4263/-293
([#7006](https://github.com/axios/axios/issues/7006)
[#7003](https://github.com/axios/axios/issues/7003) )">Dmitriy
Mozgovoy</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/mkhani01"
title="+111/-15 ([#6982](https://github.com/axios/axios/issues/6982)
)">khani</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/AmeerAssadi"
title="+123/-0 ([#7011](https://github.com/axios/axios/issues/7011)
)">Ameer Assadi</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/emiedonmokumo"
title="+55/-35 ([#6998](https://github.com/axios/axios/issues/6998)
)">Emiedonmokumo Dick-Boro</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/opsysdebug"
title="+8/-8 ([#6980](https://github.com/axios/axios/issues/6980)
)">Zeroday BYTE</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/jasonsaayman"
title="+7/-7 ([#6985](https://github.com/axios/axios/issues/6985)
[#6985](https://github.com/axios/axios/issues/6985) )">Jason
Saayman</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/HealGaren"
title="+5/-7 ([#5715](https://github.com/axios/axios/issues/5715)
)">최예찬</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/gligorkot"
title="+3/-1 ([#5627](https://github.com/axios/axios/issues/5627)
)">Gligor Kotushevski</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/adimit"
title="+2/-1 ([#5595](https://github.com/axios/axios/issues/5595)
)">Aleksandar Dimitrov</a></li>
</ul>
<h1><a
href="https://github.com/axios/axios/compare/v1.10.0...v1.11.0">1.11.0</a>
(2025-07-22)</h1>
<h3>Bug Fixes</h3>
<ul>
<li>form-data npm pakcage (<a
href="https://redirect.github.com/axios/axios/issues/6970">#6970</a>)
(<a
href="https://github.com/axios/axios/commit/e72c193722530db538b19e5ddaaa4544d226b253">e72c193</a>)</li>
<li>prevent RangeError when using large Buffers (<a
href="https://redirect.github.com/axios/axios/issues/6961">#6961</a>)
(<a
href="https://github.com/axios/axios/commit/a2214ca1bc60540baf2c80573cea3a0ff91ba9d1">a2214ca</a>)</li>
<li><strong>types:</strong> resolve type discrepancies between ESM and
CJS TypeScript declaration files (<a
href="https://redirect.github.com/axios/axios/issues/6956">#6956</a>)
(<a
href="https://github.com/axios/axios/commit/8517aa16f8d082fc1d5309c642220fa736159110">8517aa1</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/izzygld"
title="+186/-93 ([#6970](https://github.com/axios/axios/issues/6970)
)">izzy goldman</a></li>
<li><!-- raw HTML omitted --> <a
href="https://github.com/manishsahanidev" title="+70/-0
([#6961](https://github.com/axios/axios/issues/6961) )">Manish
Sahani</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/noritaka1166"
title="+12/-10 ([#6938](https://github.com/axios/axios/issues/6938)
[#6939](https://github.com/axios/axios/issues/6939) )">Noritaka
Kobayashi</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/jrnail23"
title="+13/-2 ([#6956](https://github.com/axios/axios/issues/6956)
)">James Nail</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/axios/axios/commit/0d8ad6e1de0f5339e02bc262d6f0df4936974120"><code>0d8ad6e</code></a>
chore(release): v1.12.0 (<a
href="https://redirect.github.com/axios/axios/issues/7013">#7013</a>)</li>
<li><a
href="https://github.com/axios/axios/commit/fd7f404488b2c4f238c2fbe635b58026a634bfd2"><code>fd7f404</code></a>
fix: release pr run</li>
<li><a
href="https://github.com/axios/axios/commit/a2edc3606a4f775d868a67bb3461ff18ce7ecd11"><code>a2edc36</code></a>
fix: dont add dist on release</li>
<li><a
href="https://github.com/axios/axios/commit/9ec86de257bfa33856571036279169f385ed92bd"><code>9ec86de</code></a>
fix: adding build artifacts</li>
<li><a
href="https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593"><code>945435f</code></a>
fix(node): enforce maxContentLength for data: URLs (<a
href="https://redirect.github.com/axios/axios/issues/7011">#7011</a>)</li>
<li><a
href="https://github.com/axios/axios/commit/28e5e3016d6ed0b3ec489427e4ec00133f45ddc2"><code>28e5e30</code></a>
chore(sponsor): update sponsor block (<a
href="https://redirect.github.com/axios/axios/issues/7005">#7005</a>)</li>
<li><a
href="https://github.com/axios/axios/commit/d03f245a40ec016b190748a865cce9fe3815c903"><code>d03f245</code></a>
chore(CI): fixed release info script to use npm registry instead of git
as fi...</li>
<li><a
href="https://github.com/axios/axios/commit/a0bc91137950f36a1f6b0a2a60d11fd7f245ff0e"><code>a0bc911</code></a>
chore: removing dist files from src (<a
href="https://redirect.github.com/axios/axios/issues/7002">#7002</a>)</li>
<li><a
href="https://github.com/axios/axios/commit/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b"><code>c959ff2</code></a>
feat(fetch): add fetch, Request, Response env config variables for the
adapte...</li>
<li><a
href="https://github.com/axios/axios/commit/a9f47afbf3224d2ca987dbd8188789c7ea853c5d"><code>a9f47af</code></a>
fix(fetch-adapter): set correct Content-Type for Node FormData (<a
href="https://redirect.github.com/axios/axios/issues/6998">#6998</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/axios/axios/compare/v1.8.2...v1.12.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=1.8.2&new-version=1.12.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/coder/coder/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 09:00:45 +00:00
Cian Johnston edd9746443 ci: assign tasks to triggering username in ai triage workflow (#20022)
Note: this requires owner-level perms on the token. This can be removed once it becomes possible to search org members by GitHub ID without owner-level perms.

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2025-09-30 09:51:28 +01:00
Ethan ec417ded24 refactor(scaletest): make workspacebuild return the built workspace (#19997)
Similar idea as in #19811, this runner doesn't need to conform to `Runnable`, so we have it return the workspace from the `RunReturningWorkspace` function, instead of the more fragile `Run`, followed by a `.WorkspaceID()`.
2025-09-30 18:19:31 +10:00
Ethan 0be922170b feat(cli): add workspace-updates scaletest command (#19905)
Closes https://github.com/coder/internal/issues/889

```
$ coder exp scaletest workspace-updates --workspace-count=4 --power-user-workspaces=2 --template="scratch"
Distribution plan:
  Total workspaces: 4
  Power users: 1 (each owning 2 workspaces = 2 total)
  Regular users: 2 (each owning 1 workspace = 2 total)
Planning workspace...
=== ✔ Queued [0ms]
==> ⧗ Running
==> ⧗ Running
=== ✔ Running [5ms]
==> ⧗ Setting up
=== ✔ Setting up [54ms]
==> ⧗ Detecting persistent resources
=== ✔ Detecting persistent resources [2909ms]
==> ⧗ Cleaning Up
=== ✔ Cleaning Up [5ms]
┌───────────────────────────────────────────┐
│ Workspace Preview                         │
├───────────────────────────────────────────┤
│ RESOURCE                 ACCESS           │
├───────────────────────────────────────────┤
│ null_resource.workspace                   │
│ └─ main (linux, amd64)    coder ssh       │
└───────────────────────────────────────────┘
Creating users...
Running workspace updates scaletest...


Test results:
        Pass:  3
        Fail:  0
        Total: 3

        Total duration: 5.378685938s
        Avg. duration:  3.701307642s

Cleaning up...

Uploading traces...
Waiting 15s for prometheus metrics to be scraped
```
2025-09-30 18:07:05 +10:00
Ethan 65ac6cb42a feat(scaletest): add runner for coder connect load gen (#19904)
Relates to https://github.com/coder/internal/issues/889.

This PR adds a scaletest runner that simulates a single Coder Connect client receiving workspace updates.

An instance of a workspace updates runner does the following:
- Creates a user, if a session token is not supplied.
- Attempts to repeatedly dial the Coder Connect endpoint, with a configurable (two minutes by default) timeout. 
- Once dialed successfully, waits for any other concurrently executing runners to also dial successfully, or timeout (using the barrier).
- Starts a configurable number of workspace builds.
- Waits for that many workspaces to be seen over the workspace updates stream (with a configurable timeout).

Exposes two prometheus metrics:
- `workspace_updates_latency_seconds` - `HistogramVec`. Labels = `{username, num_owned_workspaces, workspace_name}`
  - This is the time between starting a workspace build, and receiving both the corresponding workspace update.
- `workspace_updates_errors_total` - `NewCounterVec`. Labels = `{username, num_owned_workspaces, action}`
  - The number of times a specific action of the runner has failed, per user/client.
2025-09-30 17:28:58 +10:00
Spike Curtis 2bd11dfb15 test: precompile the db cleaner on make test (#20000)
We're seeing some timeouts from starting the db cleaner, e.g. https://github.com/coder/internal/issues/1026

My suspicion is that in CI the go build cache might not be warm, and so it can take a while to compile and run the dbcleaner subprocess.

This fix builds the cleaner once prior to starting a test run via `make test` to ensure we have a warm cache.
2025-09-30 10:42:06 +04:00
Susana Ferreira fdb0267e5d feat: add notification for task status (#19965)
## Description

Send a notification to the workspace owner when an AI task’s app state
becomes `Working` or `Idle`.
An AI task is identified by a workspace build with `HasAITask = true`
and `AITaskSidebarAppID` matching the agent app’s ID.

## Changes

* Add `TemplateTaskWorking` notification template.
* Add `TemplateTaskIdle` notification template.
* Add `GetLatestWorkspaceAppStatusesByAppID` SQL query to get the
workspace app statuses ordered by latest first.
* Update `PATCH /workspaceagents/me/app-status` to enqueue:
  * `TemplateTaskWorking` when state transitions to `working`
  * `TemplateTaskIdle` when state transitions to `idle`
* Notification labels include:
  * `task`: task initial prompt
  * `workspace`: workspace name
* Notification dedupe: include a minute-bucketed timestamp (UTC
truncated to the minute) in the enqueue data to allow identical content
to resend within the same day (but not more than once per minute).

Closes: https://github.com/coder/coder/issues/19776
2025-09-29 16:44:53 +01:00
Cian Johnston abdea7213a feat(cli): add ability to create tasks for other users (#20012) 2025-09-29 15:42:03 +01:00
Bruno Quaresma f2f4ed12e9 chore: revert redesign tasks page to match AI tools (#20013)
Reverts coder/coder#19962
2025-09-29 11:30:05 -03:00
dependabot[bot] 98b9adc3e7 chore: bump google.golang.org/api from 0.249.0 to 0.250.0 (#20015)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.249.0 to 0.250.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.250.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.249.0...v0.250.0">0.250.0</a>
(2025-09-25)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3306">#3306</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/04e934090bdb2c6defe389051d4a71346fca2f5b">04e9340</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3308">#3308</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/b85955acd18b5172dfadd48edb7450304b563423">b85955a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3309">#3309</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6d11ef46bc0bd6b3b6d35308abdba321c2949cf7">6d11ef4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3310">#3310</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/5ed74f3762c4d5b9feb311eef05b93ad7601cb7b">5ed74f3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3311">#3311</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ffae06168e8e52e49cb76884ca74f3ddc3f5a7c9">ffae061</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3312">#3312</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/7f0983cef96e29797343025ca9cbfecc98ef89d3">7f0983c</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3314">#3314</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/0e5a06df6b629c4c96210b01412c5ff2a082f027">0e5a06d</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3315">#3315</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/a6d27cff843c2e65feafd252cf87a871a4448ee8">a6d27cf</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3316">#3316</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e4eefd950ed18cb8fd1ec642c0fea6f0cb3b8322">e4eefd9</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3318">#3318</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2242712e4569edd123fdac8f3f12beb536942a4b">2242712</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.249.0...v0.250.0">0.250.0</a>
(2025-09-25)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3306">#3306</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/04e934090bdb2c6defe389051d4a71346fca2f5b">04e9340</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3308">#3308</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/b85955acd18b5172dfadd48edb7450304b563423">b85955a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3309">#3309</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6d11ef46bc0bd6b3b6d35308abdba321c2949cf7">6d11ef4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3310">#3310</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/5ed74f3762c4d5b9feb311eef05b93ad7601cb7b">5ed74f3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3311">#3311</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ffae06168e8e52e49cb76884ca74f3ddc3f5a7c9">ffae061</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3312">#3312</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/7f0983cef96e29797343025ca9cbfecc98ef89d3">7f0983c</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3314">#3314</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/0e5a06df6b629c4c96210b01412c5ff2a082f027">0e5a06d</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3315">#3315</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/a6d27cff843c2e65feafd252cf87a871a4448ee8">a6d27cf</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3316">#3316</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e4eefd950ed18cb8fd1ec642c0fea6f0cb3b8322">e4eefd9</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3318">#3318</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2242712e4569edd123fdac8f3f12beb536942a4b">2242712</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/307f588476f09cd1e0186384ce53936e7b2ae78d"><code>307f588</code></a>
chore(main): release 0.250.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3307">#3307</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/2242712e4569edd123fdac8f3f12beb536942a4b"><code>2242712</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3318">#3318</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/d41bfb6490575a0f8b237748a81766bea0b29f3c"><code>d41bfb6</code></a>
chore(google-api-go-generator): add aiplatform:v1beta1 to
skipAPIGeneration (...</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/34bbc4bbbc501aca6a533eacb6f7bbf17c1fd3b9"><code>34bbc4b</code></a>
chore(all): update all (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3313">#3313</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/e4eefd950ed18cb8fd1ec642c0fea6f0cb3b8322"><code>e4eefd9</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3316">#3316</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/a6d27cff843c2e65feafd252cf87a871a4448ee8"><code>a6d27cf</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3315">#3315</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/0e5a06df6b629c4c96210b01412c5ff2a082f027"><code>0e5a06d</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3314">#3314</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/7f0983cef96e29797343025ca9cbfecc98ef89d3"><code>7f0983c</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3312">#3312</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/ffae06168e8e52e49cb76884ca74f3ddc3f5a7c9"><code>ffae061</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3311">#3311</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/5ed74f3762c4d5b9feb311eef05b93ad7601cb7b"><code>5ed74f3</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3310">#3310</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.249.0...v0.250.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.249.0&new-version=0.250.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-29 14:25:05 +00:00
Danny Kopping 61fba2db47 chore: implement automatic aibridge usage in claude code in dogfood template (#20011) 2025-09-29 13:14:25 +00:00
Spike Curtis 988ee39b02 test: stop running Test_sshConfigProxyCommandEscape in parallel (#20009)
Fixes https://github.com/coder/internal/issues/1035

Or, at least, closes a remaining race that seems pretty likely.

The tests in question write a file, close the file, then execute the file. Sometimes Linux errors saying "text file busy" which means the file is still open for writing.

What I think is going on is:

1. Test_sshConfigProxyCommandEscape goroutine opens the file and begins writing.
2. Some other, unrelated test execs a command, which causes a `fork()` syscall. The child process now has a copy of the file descriptor to our open file.
3. Test_sshConfigProxyCommandEscape goroutine executes the file and gets "text file busy".
4. The child process calls the `exec` syscall, which closes the file (due to `CLOEXEC` being set).

The race is very tight because 3 has to happen before 4 (and, 3 involves it's own fork/exec), but it's not impossible on a busy system.

c.f. #14233 which was an earlier attempt to fix this. It only prevented the subtests from running in parallel. When the subtests were all running in parallel, the flake was fairly likely because you've got all this fork() activity happening at the same time. But, since the main test was in parallel there is still a chance a totally different test is `fork`'ing at in inopportune time.
2025-09-29 17:01:54 +04:00
Cian Johnston e878281b64 ci: integrate new tasks cli features in ai triage workflow (#20007)
Integrates new experimental tasks CLI commands into traiage workflow

Sample run:
https://github.com/coder/coder/actions/runs/18095432003/job/51485262065
2025-09-29 13:48:37 +01:00
Thomas Kosiewski 79126ab6c7 feat: implement composite API key scopes for workspaces and templates (#19945)
# Add Composite API Key Scopes

This PR adds high-level composite API key scopes to simplify token creation with common permission sets:

- `coder:workspaces.create` - Create and update workspaces
- `coder:workspaces.operate` - Read and update workspaces
- `coder:workspaces.delete` - Read and delete workspaces
- `coder:workspaces.access` - Read, SSH, and connect to workspace applications
- `coder:templates.build` - Read templates and create/read files
- `coder:templates.author` - Full template management with insights
- `coder:apikeys.manage_self` - Manage your own API keys

These composite scopes are persisted in the database and expanded during authorization, providing a more intuitive way to grant permissions compared to the granular resource:action scopes.
2025-09-29 13:17:08 +02:00
dependabot[bot] 860bcd4d91 chore: bump next from 15.2.4 to 15.4.7 in /offlinedocs (#20005)
Bumps [next](https://github.com/vercel/next.js) from 15.2.4 to 15.4.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/next.js/releases">next's
releases</a>.</em></p>
<blockquote>
<h2>v15.4.7</h2>
<blockquote>
<p>[!NOTE]<br />
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>fix router handling when setting a location response header <a
href="https://redirect.github.com/vercel/next.js/issues/82588">#82588</a></li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/ztanner"><code>@​ztanner</code></a> for
helping!</p>
<h2>v15.4.2-canary.56</h2>
<h3>Misc Changes</h3>
<ul>
<li>fix: remove a few old references to 'next lint': <a
href="https://redirect.github.com/vercel/next.js/issues/82800">#82800</a></li>
<li>docs: fix TS error in Node.js runtime local assets example: <a
href="https://redirect.github.com/vercel/next.js/issues/82672">#82672</a></li>
<li>docs: Route props helpers, typegen and next lint deprecation: <a
href="https://redirect.github.com/vercel/next.js/issues/82784">#82784</a></li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/bgub"><code>@​bgub</code></a>, <a
href="https://github.com/EAzZY-1wnL"><code>@​EAzZY-1wnL</code></a>, and
<a href="https://github.com/icyJoseph"><code>@​icyJoseph</code></a> for
helping!</p>
<h2>v15.4.2-canary.55</h2>
<h3>Core Changes</h3>
<ul>
<li>[Cache Components] Fix HMR for nested pages: <a
href="https://redirect.github.com/vercel/next.js/issues/82776">#82776</a></li>
<li>Upgrade React from <code>a96a0f39-20250815</code> to
<code>f508edc8-20250818</code>: <a
href="https://redirect.github.com/vercel/next.js/issues/82747">#82747</a></li>
<li>Avoid duplicate WebSocket connection for global error pages: <a
href="https://redirect.github.com/vercel/next.js/issues/82788">#82788</a></li>
<li>Update linter options: <a
href="https://redirect.github.com/vercel/next.js/issues/82266">#82266</a></li>
<li>React from <code>f508edc8-20250818</code> to
<code>0bdb9206-20250818</code>: <a
href="https://redirect.github.com/vercel/next.js/issues/82792">#82792</a></li>
<li>feat: stabilize experimental.typedRoutes flag: <a
href="https://redirect.github.com/vercel/next.js/issues/82762">#82762</a></li>
<li>[Cache Components] Faster partial hydration in PPR resumes: <a
href="https://redirect.github.com/vercel/next.js/issues/82742">#82742</a></li>
<li>Turbopack: Define built-in webpack conditions using an enum and
typescript union: <a
href="https://redirect.github.com/vercel/next.js/issues/82765">#82765</a></li>
<li>Turbopack: Remove beta warning: <a
href="https://redirect.github.com/vercel/next.js/issues/82797">#82797</a></li>
<li>feat: add a codemod to migrate from the deprecated &quot;next
lint&quot; command: <a
href="https://redirect.github.com/vercel/next.js/issues/82685">#82685</a></li>
<li>fix: don't fail with amp config in JS: <a
href="https://redirect.github.com/vercel/next.js/issues/82798">#82798</a></li>
<li>fix: possible null access: <a
href="https://redirect.github.com/vercel/next.js/issues/82799">#82799</a></li>
</ul>
<h3>Misc Changes</h3>
<ul>
<li>Update Rspack production test manifest: <a
href="https://redirect.github.com/vercel/next.js/issues/82771">#82771</a></li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/unstubbable"><code>@​unstubbable</code></a>, <a
href="https://github.com/bgub"><code>@​bgub</code></a>, <a
href="https://github.com/vercel-release-bot"><code>@​vercel-release-bot</code></a>,
<a href="https://github.com/lubieowoce"><code>@​lubieowoce</code></a>,
and <a href="https://github.com/bgw"><code>@​bgw</code></a> for
helping!</p>
<h2>v15.4.2-canary.54</h2>
<h3>Core Changes</h3>
<ul>
<li>[Cache Components] Defer Request Data API resolution to another task
in dev when it would suspend when prerendering: <a
href="https://redirect.github.com/vercel/next.js/issues/82386">#82386</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/next.js/commit/f30d815859e932e09222e93bb6e8a376b918d874"><code>f30d815</code></a>
v15.4.7</li>
<li><a
href="https://github.com/vercel/next.js/commit/1a026e338d2b8c977c8949a134168952338d6d01"><code>1a026e3</code></a>
fix router handling when setting a location response header (<a
href="https://redirect.github.com/vercel/next.js/issues/82588">#82588</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/be4aafd4b744fbf6500b311b74c84243f70a3059"><code>be4aafd</code></a>
v15.4.6</li>
<li><a
href="https://github.com/vercel/next.js/commit/91e5b6b84f56c096dc2bb647eb384388e06489fd"><code>91e5b6b</code></a>
Backport &quot;fix: add <code>?dpl</code> to fonts in
<code>/_next/static/media</code> (<a
href="https://redirect.github.com/vercel/next.js/issues/82384">#82384</a>)&quot;
(<a
href="https://redirect.github.com/vercel/next.js/issues/82421">#82421</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/f1629d939597cc46ccbe44fe66bda91eac31e219"><code>f1629d9</code></a>
Backport &quot;[Pages] fix: <code>_error</code> page's
<code>req.url</code> can be overwritten t… (<a
href="https://redirect.github.com/vercel/next.js/issues/82377">#82377</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/b9aab5dbe926c256d439bfecb226693dcd1a1be4"><code>b9aab5d</code></a>
v15.4.5</li>
<li><a
href="https://github.com/vercel/next.js/commit/a8c93c49dd2d42a2ced8a17bc53b3fea11d25c96"><code>a8c93c4</code></a>
Disable test new tests jobs</li>
<li><a
href="https://github.com/vercel/next.js/commit/ed2a6c754831406f8cd724eb52a562bb124c1504"><code>ed2a6c7</code></a>
[backport]: fix(next/image): improve and simplify detect-content-type
(<a
href="https://redirect.github.com/vercel/next.js/issues/82118">#82118</a>...</li>
<li><a
href="https://github.com/vercel/next.js/commit/f00fcc9011e7d0ef027021fc8424f51b8ac97880"><code>f00fcc9</code></a>
[backport]: fix(next/image): fix image-optimizer.ts headers (<a
href="https://redirect.github.com/vercel/next.js/issues/82114">#82114</a>)
(<a
href="https://redirect.github.com/vercel/next.js/issues/82175">#82175</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/55a7568e9d12459f8c5f5ae120fe2e228af284bb"><code>55a7568</code></a>
Backport: Turbopack: update mimalloc (<a
href="https://redirect.github.com/vercel/next.js/issues/81993">#81993</a>)
(<a
href="https://redirect.github.com/vercel/next.js/issues/82166">#82166</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/next.js/compare/v15.2.4...v15.4.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=next&package-manager=npm_and_yarn&previous-version=15.2.4&new-version=15.4.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/coder/coder/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-29 11:08:18 +00:00
Danny Kopping 32b5544d22 chore: revert github.com/mark3labs/mcp-go to v0.38.0 (#20004)
v0.41.0 seems to have a bug where it passes nil for tool definitions
instead of an empty map like it used to.

Signed-off-by: Danny Kopping <danny@coder.com>
2025-09-29 10:44:28 +00:00
Dean Sheather e257d2e94b chore: upgrade aibridge dependency to v0.1.1 (#20003)
Fixes openai tool call interception
(https://github.com/coder/aibridge/pull/8)
2025-09-29 09:25:06 +00:00
Cian Johnston 3e1f6afd66 chore: work around timing issue in TestReplicas/ErrorWithoutLicense (#20002)
Closes https://github.com/coder/internal/issues/268

Wraps the assertions in a `testutil.Eventually` so that hopefully any
transient timing issues resolve themselves. If this does not resolve the
issue, we may need to plumb through some kind of `chan struct{}` into
`api.Entitlements.Update()`
2025-09-29 10:20:26 +01:00
Rowan Smith aaa50715e1 fix: fix for template dormancy hour/day toggle (#19884)
resolves #15451 

> Solution: Selecting days is always be enabled and rounds (up) the
hours to the nearest day. I.e. 25 hours -> 2 days.

tested with 26hrs saved, then toggled to days. result = value changed to
2 days

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.

blink helped

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-29 09:23:02 +10:00
Bruno Quaresma 54ce03a300 chore: replace MUI buttons (#19968)
Replace MUI buttons with `components/Button`
2025-09-26 21:38:54 -03:00
Andrew Aquino 10d41b3a72 refactor: replace other deprecated Popovers which open on hover (#19753)
closes #19397 
dependency: #19709 

#19635 covered a large chunk of deprecated `Popover`s which open on
hover, specifically the ones which instantiated `HelpTooltip`. This PR
replaces all of the non-`HelpTooltip` hover-triggered popovers, and
removes the deprecated popover component :)

---------

Co-authored-by: ケイラ <mckayla@hey.com>
2025-09-26 15:05:09 -07:00
Rafael Rodriguez d29a52462b feat: remove agent name from app URLs (#19750)
## Summary

In this pull request we're removing `agent_name` from subdomains in APP
urls when an `app` is used in the subdomain. `agent_names` will still be
used when a `port` is used in the subdomain.

Closes: https://github.com/coder/coder/issues/18485

### Changes

- Updated regex to support an optional agent name
- Added logic to support checking the app slug for a matching port
(e.g., 8080 or 8080s)

### Testing

- Updated all tests to support an optional `agent_name`
2025-09-26 12:25:58 -05:00
Bruno Quaresma 403a9e57c9 chore: migrate MUI icons to lucide icons (#19967)
This PR completes the migration from Material UI icons to Lucide icons.
2025-09-26 14:02:34 -03:00
Bruno Quaresma 89339f6e5d feat: redesign tasks page to match AI tools (#19962)
**Demo:**

<img width="3228" height="1940" alt="image"
src="https://github.com/user-attachments/assets/d29555ae-8122-4043-9ee2-2603506e5f3c"
/>
2025-09-26 13:30:44 -03:00
Dean Sheather fc58996bbf chore: add StripPrefix to aibridge server handler (#19990)
oops
2025-09-26 15:40:42 +00:00
Bruno Quaresma c2d514356f fix: handle chat app not found (#19947)
Sometimes users can misconfigure the app used for chat. When that
happens, we should make it clear to the user.

<img width="1197" height="727" alt="Screenshot 2025-09-24 at 14 15 12"
src="https://github.com/user-attachments/assets/6afe2c22-e7c3-47d4-8446-76000535a492"
/>

- Handle “chat app not found.”
- Simplify stories.
- Have `TaskAppIframe` handle all task iframes so we don’t need a
separate iframe component for chat.
2025-09-26 12:36:45 -03:00
Dean Sheather 43415f0144 chore: add enterprise feature for aibridge (#19976)
Adds enterprise feature "aibridge" and gates the aibridge CRUD and LLM API endpoints behind it.
2025-09-27 01:13:06 +10:00
Paweł Banaszewski 65f2895c0d chore: add CLI command to list aibridge interceptions (#19935)
Co-authored-by: Dean Sheather <dean@deansheather.com>
2025-09-27 00:58:12 +10:00
Paweł Banaszewski 0a6ba5d51a feat: add endpoint to list aibridge interceptions (#19929)
Co-authored-by: Dean Sheather <dean@deansheather.com>
2025-09-27 00:20:33 +10:00
Bruno Quaresma d70e26d2e3 chore: remove react-helmet (#19963)
Since React 19 supports head tags natively, we no longer need to use
`react-helmet`.

---------

Co-authored-by: ケイラ <mckayla@hey.com>
Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2025-09-26 10:52:53 -03:00
Danielle Maywood eb74732839 refactor!: rename prompt field to input for task creation (#19982)
Renames the `prompt` field to `input` for task creation. This matches
the naming used in the CLI and elsewhere.
2025-09-26 12:18:08 +01:00
Cian Johnston 0a840e48fd feat(cli): add more information to coder whoami (#19971)
Builds upon https://github.com/coder/coder/pull/19970

I got kinda carried away when I saw the extra stuff we could add in
here, so I went ahead and added it:

* User ID
* Organization IDs
* Roles

This technically duplicates functionality from `coder users show` but I
figure folks may find it useful.
2025-09-26 11:20:25 +01:00
Thomas Kosiewski 05537c1894 feat: publish RBAC scopes in OAuth2 metadata endpoints (#19942)
<!--

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.

-->

Publish supported OAuth2 scopes from RBAC external scope names

This PR updates the OAuth2 metadata endpoints to publish the supported scopes from the RBAC external scope names. Previously, the `ScopesSupported` field was empty with a TODO to implement a scope system. Now, both the authorization server metadata and protected resource metadata endpoints return the list of scopes from `rbac.ExternalScopeNames()`.

The tests have been updated to verify that the correct scopes are being returned in the metadata responses.
2025-09-26 12:15:36 +02:00
Thomas Kosiewski d0db9ec88f feat: add multi-scope support to API keys (#19917)
# Canonicalize API Key Scopes

This PR introduces canonical API key scopes with a `coder:` namespace prefix to avoid collisions with low-level resource:action names. It:

1. Renames special API key scopes in the database:
   - `all` → `coder:all`
   - `application_connect` → `coder:application_connect`

2. Adds support for a new `scopes` field in the API key creation request, allowing multiple scopes to be specified while maintaining backward compatibility with the singular `scope` field.

3. Updates the API documentation to reflect these changes, including the new endpoint for listing public API key scopes.

4. Ensures backward compatibility by mapping between legacy and canonical scope names in relevant code paths.
2025-09-26 11:56:34 +02:00
Thomas Kosiewski 4bda39585d feat: add external API key scopes (#19916)
# Add support for low-level API key scopes

This PR adds support for fine-grained API key scopes based on RBAC resource:action pairs. It includes:

1. A new endpoint `/api/v2/auth/scopes` to list all public low-level API key scopes
2. Generated constants in the SDK for all public scopes
3. Tests to verify scope validation during token creation
4. Updated API documentation to reflect the expanded scope options

The implementation allows users to create API keys with specific permissions like `workspace:read` or `template:use` instead of only the legacy `all` or `application_connect` scopes.



Fixes #19847
2025-09-26 11:43:32 +02:00
Danielle Maywood b7e0b2a73d feat(cli): add exp task logs (#19915)
Closes https://github.com/coder/internal/issues/894
2025-09-26 10:37:52 +01:00
Cian Johnston 82bebc7cc8 feat(cli): improve exp task status --watch (#19969)
* Improves logic for `exp task status --watch` so that it will also exit
on task idle status.
* Adds workspace agent health to `exp task status` output.
2025-09-26 10:33:31 +01:00
Thomas Kosiewski 47c92ad1d2 feat: add public RBAC scope catalog for user-requestable permissions (#19913)
# Add a curated catalog of public RBAC scopes

This PR introduces a curated catalog of public RBAC scopes that are exposed to users. It adds:

- A `publicLowLevel` map in `scopes_catalog.go` that defines which resource:action pairs are user-requestable
- `IsPublicLowLevel()` function to check if a scope is in the public catalog
- `PublicLowLevelScopeNames()` function that returns a sorted list of public scopes
- Tests to verify the catalog entries are valid and properly sorted
- Updated documentation in the check-scopes README to clarify that public scopes should be added to this catalog

This change helps distinguish between internal-only scopes and those that should be exposed to users in the API.
2025-09-26 11:30:28 +02:00
Cian Johnston eb55f0ab19 fix(coderd)!: only show task status for current build (#19966)
This changes the task get endpoint to omit app statuses for previous
'lifetimes' of a workspace.

It also introduces a [breaking
change](https://github.com/coder/coder/blob/release/2.26/codersdk/aitasks.go#L83)
to bring `TaskStateComplete` in line with
`WorkspaceAppStatusStateComplete`. I can alternatively revert this
change and add a conversion function between the two SDK types.
2025-09-26 10:14:37 +01:00
Cian Johnston 930bbaf035 feat(cli): add formatting options to coder whoami (#19970)
This addresses a long-standing gripe of mine: to get your logged in
username you would have to do
```bash
coder whoami | awk '{print $9}'
```

This allows you to do:

```
coder whoami -o json | jq -r '.username'
```

or

```
coder whoami -f table -c username
```
2025-09-26 09:33:27 +01:00
Spike Curtis f2904726a5 test: wait for completion before asserting in TestAgentConnectionMonitor_BuildOutdated (#19959)
<!--

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.

-->

follow on to #19836 
fixes https://github.com/coder/internal/issues/970

Same issue, different (adjacent) test.
2025-09-26 09:24:11 +04:00
Spike Curtis 904a308cd4 test: ignore bad connection errors for TestServer cases that don't use the db (#19957)
<!--

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.

-->
2025-09-26 08:48:24 +04:00
Ethan 659f237737 docs: add next steps to coder desktop guide (#19975)
I ended up on the Coder Desktop docs page and thought half of it had disappeared, though it was actually tucked away behind a second page. This PR adds a next steps section to the first page that links there, as it seems very easy to miss right now.
2025-09-26 14:06:59 +10:00
Brett Kolodny 647101b421 feat: add --shared-with-me flag to coder list command (#19948)
Closes
[coder/internal#1013](https://github.com/coder/internal/issues/1013)
2025-09-25 16:54:44 -04:00
Atif Ali a78790c632 chore: update claude code module in dogfood template (#19961)
Update the Claude Code module to version 3.0.0 
Tested in
https://dev.coder.com/templates/coder/coder/versions/quizzical_yalow1
2025-09-25 20:40:51 +05:00
Mathias Fredriksson 0bac5a4d5c feat(coderd): add experimental tasks logs endpoint (#19958)
Fixes coder/internal#901
2025-09-25 15:12:49 +00:00
Cian Johnston 063e8634c7 ci: create tasks instead of workspaces in ai triage workflow (#19940)
Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
2025-09-25 16:00:54 +01:00
Danielle Maywood 252f430eb9 feat(cli): add exp task send (#19922)
Closes https://github.com/coder/internal/issues/895

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2025-09-25 15:40:02 +01:00
Danny Kopping 0a79817050 feat: initialize aibridged & mount API handler (#19798)
Addresses https://github.com/coder/internal/issues/987
2025-09-25 16:37:28 +02:00
Danny Kopping 6971f612be feat: aibridged mcp handling (#19911)
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.
2025-09-25 16:01:56 +02:00
Mathias Fredriksson 653101e5f6 fix(coderd): fix parsing of response format in tasks send (#19960) 2025-09-25 13:50:42 +00:00
Danny Kopping fc9bff7107 feat: add aibridged package (#19797)
Addresses https://github.com/coder/internal/issues/987
2025-09-25 15:40:25 +02:00
Spike Curtis 289f0217c7 feat: add scaletest Runner for dynamicparameters load gen (#19890)
relates to https://github.com/coder/internal/issues/912

Adds a new scaletest Runner to generate dynamic parameters load.

A later PR will add the CLI command, including creating the template & version.
2025-09-25 16:18:37 +04:00
Mathias Fredriksson 5317d309d0 feat(coderd): add experimental tasks send endpoint (#19941)
Fixes coder/internal#902
2025-09-25 12:12:00 +00:00
Danny Kopping 615585d5d1 feat: add aibridgedserver pkg (#19902) 2025-09-25 13:32:16 +02:00
Kacper Sawicki c8742badbb fix(cli): enhance error handling for multiple agents in SSH command (#19943)
Closes #19812

## Problem
> When I try to SSH into my workspace with multiple agents. It does not
provide an intuitive way to do that successfully and instead misguides
by printing wrong instructions.

This PR enhances the error handling to provide suggestions with SSH
commands that users can copy and paste directly.

Before:
```
Encountered an error running "coder ssh", see "coder ssh --help" for more information
error: multiple agents found, please specify the agent name, available agents: [coder dev]
```

After:
```
Encountered an error running "coder ssh", see "coder ssh --help" for more information
error: multiple agents found, please specify the agent name, available agents: [coder dev]
Try running:
         $ ssh coder.dogfood.me.coder
         $ ssh dev.dogfood.me.coder
```
2025-09-25 12:03:51 +02:00
Susana Ferreira da467bad2a feat(site): add custom notification settings (#19938)
## Description

Add Custom Notification settings to `/deployment/notifications` page and
`/settings/notifications` user's page.

<img width="2936" height="3008" alt="Screenshot 2025-09-24 at 12 53 52"
src="https://github.com/user-attachments/assets/c11ccaba-7bdd-4b0d-98b3-faa48a9796ba"
/>

<img width="2920" height="2976" alt="Screenshot 2025-09-24 at 12 54 06"
src="https://github.com/user-attachments/assets/21aa3057-a14e-4ba6-8138-98a4dc3952dc"
/>

Follow-up from: https://github.com/coder/coder/pull/19751
2025-09-25 09:48:13 +01:00
Vijay Bandari b712e975d5 chore: update okta icon (#19951) 2025-09-24 15:49:46 -06:00
Bruno Quaresma 2df9c5b6b5 feat: add sidebar to task page (#19944)
**Demo:**

https://github.com/user-attachments/assets/83363666-ed0e-488a-84b8-bb46517a2bb8

- Integrate sidebar into the task page
- Minor cosmetic changes
- Fix scrolling issues in tasks list
2025-09-24 14:52:59 -03:00
Andrew Aquino 669984c943 refactor: replace deprecated Popovers which open on click (#19709)
for #19397 
dependency: #19635 

This PR specifically covers components which open a popover that's
triggered by a click, rather than a hover.

---------

Co-authored-by: ケイラ <mckayla@hey.com>
2025-09-24 10:00:54 -07:00
ケイラ a6fc28cc6f chore: bring back x-auth-checks with a length limit (#19928) 2025-09-24 10:46:50 -06:00
Thomas Kosiewski adb7521066 feat: generate RBAC scope name constants (#19896)
# Generate RBAC scope name constants

This PR adds a new generated file `coderd/rbac/scopes_constants_gen.go` that contains typed constants for all RBAC scope names in the format `Scope<Resource><Action>`. For example, `ScopeWorkspaceRead` for the scope "workspace:read".

These constants make it easier to reference specific scopes in code without using string literals, improving type safety and making refactoring easier.

The PR:
- Adds a new template file `scripts/typegen/scopenames.gotmpl`
- Updates the typegen script to support generating scope name constants
- Updates the Makefile to include the new generated file in build targets
2025-09-24 18:40:36 +02:00
Bruno Quaresma 5ff503b8fc fix: force task to be created with latest version (#19923)
Fixes https://github.com/coder/coder/issues/19744
2025-09-24 13:23:31 -03:00
Thomas Kosiewski acc0890dce feat: add lint check for API key scope enum completeness (#19862)
Added a script/linter to ensure all `policy.RBACPermissions` entries are part of the `api_key_scope` enumerated in the `coderd/database/dump.sql` file.

Fixes #19846
2025-09-24 18:06:16 +02:00
Dean Sheather 42dd544d90 fix: use unique cookies for workspace proxies (#19930)
There is currently an issue with subdomain workspace apps on workspace
proxies, where if you have a workspace proxy wildcard nested beneath the
primary wildcard, cookies from the primary may be sent to the server
before cookies from the proxy specifically.

Currently:
1. Use a subdomain app via the primary proxy `*.coder.corp.com`
    a. Client sends no cookies
    a. Server does token smuggling flow
a. Server sets a cookie `coder_subdomain_app_session_token` on
`*.coder.corp.com`
    a. Server redirects client to reload the page
    a. Request should succeed as usual
1. Wait until the primary proxy's session token cookie has expired in
the database (or make it invalid yourself)
1. Use a subdomain app via a separate proxy `*.sydney.coder.corp.com`
a. Client sends `coder_subdomain_app_session_token` cookie from
`*.coder.corp.com`
    a. Server validates supplied cookie, it fails because it's expired
    a. Server does token smuggling flow
a. Server sets a cookie `coder_subdomain_app_session_token` on
`*.sydney.coder.corp.com`
    a. Server redirects client to reload page
    a. Client sends BOTH cookies.
a. The server will only process the first cookie it receives, so if the
expired cookie for the primary proxy is sent first the request will end
up in a permanent loop on step b.

The fix is to append `_{hash(wildcard_access_url)}` to the subdomain
cookies as we cannot control browser behavior further. This avoids the
conflict as each proxy will only read it's specific cookie.
2025-09-25 00:30:02 +10:00
Bruno Quaresma 7baaa16ac1 chore: add tasks sidebar component (#19926)
This PR is part of a series of PRs aimed at completing the tasks
sidebar.

**Demo:**

https://github.com/user-attachments/assets/0d830f22-dc94-45d7-833b-fe706226ad68

Reference:
[https://github.com/coder/coder/issues/19573](https://github.com/coder/coder/issues/19573)
Depends on:
[https://github.com/coder/coder/pull/19920](https://github.com/coder/coder/pull/19920)
Based on:
[https://github.com/coder/coder/pull/19655](https://github.com/coder/coder/pull/19655)
2025-09-24 10:40:01 -03:00
Bruno Quaresma 50a92147e3 chore: add UserCombobox component for tasks sidebar (#19920)
This PR is part of a series of PRs aimed at completing the tasks
sidebar.

Reference:
[https://github.com/coder/coder/issues/19573](https://github.com/coder/coder/issues/19573)
2025-09-24 10:27:53 -03:00
Cian Johnston 78b1ec9698 feat(cli): add optional --name arg to 'exp task create' (#19939)
Allows specifying task name in `exp task create`
2025-09-24 14:05:59 +01:00
Bruno Quaresma 6d0943a53a feat: don't redirect to task page when it is created (#19919)
**Demo:**

https://github.com/user-attachments/assets/3ffaa13e-553f-43d2-9fad-d4d3ea7a4ede

Closes https://github.com/coder/coder/issues/19675
2025-09-24 10:05:38 -03:00
Cian Johnston 8f3e03ad40 ci: add auto-generated summary and GitHub issue comment workflows to triage.yaml (#19925) 2025-09-24 11:54:44 +01:00
Thomas Kosiewski 77d19c99cd ci: fix shellcheck lint error (#19933) 2025-09-24 10:47:50 +02:00
Danny Kopping 0ece153a0e chore: define aibridged protobuf specification (#19795) 2025-09-24 17:05:04 +10:00
Spike Curtis 5f1ddb6d57 test: skip ProbeOK test until fixed to not flake (#19931)
temp skip of flaky test: https://github.com/coder/internal/issues/957
2025-09-24 10:31:51 +04:00
Cian Johnston a68122ca30 chore: fix Test_Runner/CleanupPendingBuild with testutil.Eventually (#19924)
Fixes https://github.com/coder/internal/issues/968
2025-09-23 16:53:53 +01:00
Rafael Rodriguez f80cc15d90 chore(dogfood): add tooltip to jetbrains module (#19781)
## Summary

In this pull request we're adding support for a `tooltip` to the
JetBrains module in the dogfood template. Tooltip support was added to
the JetBrains module in https://github.com/coder/registry/pull/421

### Testing

Added `tooltip` to the JetBrains app in the example `docker` template
and verified that the tooltip appeared in my workspace

<img width="1039" height="560" alt="Screenshot 2025-09-23 at 10 01 52"
src="https://github.com/user-attachments/assets/9277f343-f73e-4fb8-a5cc-145f1aad9725"
/>
2025-09-23 10:44:03 -05:00
Cian Johnston 4a56a4094b chore: apptest: update test helpers to context-aware testutil.Eventually (#19918)
Relates to https://github.com/coder/internal/issues/960
2025-09-23 09:16:33 +01:00
Danielle Maywood b71d6713d1 feat(site): allow starting task workspace from task page (#19790)
Closes https://github.com/coder/coder/issues/19622

When viewing a task with a stopped workspace, instead show a 'Start
workspace' button instead of a 'View workspace'. The user can still view
the workspace by clicking the workspace button at the top right of the
page.


https://github.com/user-attachments/assets/4424c251-5f20-4e82-9ee0-c87a0b30a193
2025-09-23 09:05:49 +01:00
Thomas Kosiewski fb0ce389a6 feat: implement API key scopes database migration (#19861)
Added database migration for API key scopes.

Fixes #19845
2025-09-22 19:26:51 +02:00
Spike Curtis a30c30724b chore: refactor cli and coderd to use ClientOptions (#19763)
Refactors CLI and coderd to use the ClientBuilder pattern rather than directly instantiating the Client.
2025-09-22 21:02:56 +04:00
Bruno Quaresma e7d648fb28 fix: make app tabs scrollable (#19881)
The solution follows the way VSCode manages its tabs. Since many other
editors use a similar approach, this feels familiar and intuitive for
our users.

**Demo:**


https://github.com/user-attachments/assets/b4cfb307-268b-4c8b-ac7f-d01dff4ce60b

Fix https://github.com/coder/coder/issues/19438
2025-09-22 13:46:54 -03:00
Thomas Kosiewski 6fb4cc6b82 fix: add retry logic to OAuth2 metadata tests to avoid race conditions (#19813)
This PR adds a readiness wait to OAuth2 metadata endpoint tests to avoid rare races with server startup. Instead of immediately making HTTP requests, the tests now use `testutil.Eventually` to retry the requests until they succeed, with a short interval between attempts. This helps prevent flaky tests that might fail due to timing issues during server initialization.  
  
Fixes: https://github.com/coder/internal/issues/996
2025-09-22 18:30:36 +02:00
ケイラ 98213d7fc4 chore: improve playwright logging (#19899) 2025-09-22 10:17:03 -06:00
ケイラ e189088c03 chore: wrap pr ai guidelines notice in a comment (#19900) 2025-09-22 09:45:43 -06:00
Cian Johnston 6d1c425421 ci: fix AI triage (a.k.a. 'traiage') workflow (#19912)
- Updates default template name
- Set `GITHUB_TOKEN` env
- Implement simple `traiage.sh resume` script to download output
artifact
2025-09-22 16:42:42 +01:00
Atif Ali d2da8357cf docs: clarify offline CLI installation instructions (#19914) 2025-09-22 19:26:06 +05:00
Spike Curtis 1734dfd291 chore: cleanup unused Client in server command (#19762)
As part of converting production code to use the new ClientBuilder, I noticed some dead code that creates a client with a URL for the only purpose of later accessing the URL. This PR removes the cruft.
2025-09-22 17:38:34 +04:00
Spike Curtis 1d0e79f37e chore: refactor InitClient() to use ClientOptions (#19761)
Refactors `InitClient` and friends to use the ClientBuilder rather than modifying a Client in place.
2025-09-22 17:26:36 +04:00
Spike Curtis 606ae897b7 chore: refactor to directly create Client in Command Handlers (#19760)
Refactors the CLI to create the `*codersdk.Client` in the handlers. This is groundwork for changing the `rootCmd.InitClient()` to use the new `ClientOption`​s.

It also improves variable locality, scoping the Client to the handler. This makes misuse less likely and reduces the memory allocations to just the command being executed, rather than allocating a Client for every command regardless of whether it is executed.
2025-09-22 17:14:07 +04:00
Cian Johnston 12496830d6 ci: add workflow for agentic issue triage (#19839)
Adds a GH workflow to start a workspace with a pre-determined template,
perform a first pass over a given GitHub issue, and persist the changes
in a GCS bucket for later refining. Tested locally with `nektos/act`.

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2025-09-22 13:29:18 +01:00
Spike Curtis 5c2b9a5b82 chore: refactor codersdk.Client creation with functional args (#19759)
Adds ClientBuilder to build a codersdk.Client. This is a safer pattern than the current usage which modifies properties of the Client after creating it, opening us up to race conditions.

Refactors agentsdk to use the builder.
2025-09-22 15:59:41 +04:00
dependabot[bot] 562ef68cd9 chore: bump github.com/hashicorp/terraform-json from 0.25.0 to 0.27.2 (#19908)
Bumps
[github.com/hashicorp/terraform-json](https://github.com/hashicorp/terraform-json)
from 0.25.0 to 0.27.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/hashicorp/terraform-json/releases">github.com/hashicorp/terraform-json's
releases</a>.</em></p>
<blockquote>
<h2>v0.27.2</h2>
<p>NOTES / BUG FIXES</p>
<ul>
<li><code>UnmarshalLogMessage</code> to use <code>json.Number</code>
(instead of default <code>float64</code>) (<a
href="https://redirect.github.com/hashicorp/terraform-json/pull/178">#178</a>)
<ul>
<li>This avoids precision related problems when decoding numbers in JSON
data. This would impact only <code>0.27.1</code> consumers.</li>
</ul>
</li>
</ul>
<h2>v0.27.1</h2>
<h2>What's Changed</h2>
<h3>ENHANCEMENTS</h3>
<ul>
<li>Replace <code>json.RawMessage</code> with <code>any</code> for list
log messages by <a
href="https://github.com/dbanck"><code>@​dbanck</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/177">hashicorp/terraform-json#177</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/hashicorp/terraform-json/compare/v0.27.0...v0.27.1">https://github.com/hashicorp/terraform-json/compare/v0.27.0...v0.27.1</a></p>
<h2>v0.27.0</h2>
<h2>What's Changed</h2>
<h3>ENHANCEMENTS</h3>
<ul>
<li>logging: Introduce types for structured UI messages +
<code>UnmarshalLogMessage()</code> by <a
href="https://github.com/radeksimko"><code>@​radeksimko</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/167">hashicorp/terraform-json#167</a></li>
<li>logging: Add query related message types by <a
href="https://github.com/radeksimko"><code>@​radeksimko</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/169">hashicorp/terraform-json#169</a></li>
<li>Add support for action schemas (unlinked) by <a
href="https://github.com/austinvalle"><code>@​austinvalle</code></a> in
<a
href="https://redirect.github.com/hashicorp/terraform-json/pull/166">hashicorp/terraform-json#166</a></li>
<li>Add support for action invocation within Plan JSON by <a
href="https://github.com/mutahhir"><code>@​mutahhir</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/172">hashicorp/terraform-json#172</a></li>
<li>tf query: add identity version for the list_resource_found message
by <a href="https://github.com/dsa0x"><code>@​dsa0x</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/176">hashicorp/terraform-json#176</a></li>
</ul>
<h3>INTERNAL</h3>
<ul>
<li>go: Bump to 1.25 by <a
href="https://github.com/radeksimko"><code>@​radeksimko</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/168">hashicorp/terraform-json#168</a></li>
<li>Bump actions/checkout from 4.2.2 to 5.0.0 in the
github-actions-breaking group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/170">hashicorp/terraform-json#170</a></li>
<li>Bump github.com/zclconf/go-cty from 1.16.3 to 1.16.4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/171">hashicorp/terraform-json#171</a></li>
<li>build(deps): bump actions/setup-go from 5.5.0 to 6.0.0 in the
github-actions-breaking group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/174">hashicorp/terraform-json#174</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mutahhir"><code>@​mutahhir</code></a>
made their first contribution in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/172">hashicorp/terraform-json#172</a></li>
<li><a href="https://github.com/dsa0x"><code>@​dsa0x</code></a> made
their first contribution in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/176">hashicorp/terraform-json#176</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/hashicorp/terraform-json/compare/v0.26.0...v0.27.0">https://github.com/hashicorp/terraform-json/compare/v0.26.0...v0.27.0</a></p>
<h2>v0.26.0</h2>
<h2>ENHANCEMENTS:</h2>
<ul>
<li>Added: support for list resource by <a
href="https://github.com/anubhav-goel"><code>@​anubhav-goel</code></a>
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/165">hashicorp/terraform-json#165</a></li>
</ul>
<h2>INTERNAL:</h2>
<ul>
<li>Bump actions/setup-go from 5.4.0 to 5.5.0 in the
github-actions-backward-compatible group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/161">hashicorp/terraform-json#161</a></li>
<li>github: Add PR template with required fields for PCI compliance by
<a href="https://github.com/xiehan"><code>@​xiehan</code></a> in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/162">hashicorp/terraform-json#162</a></li>
<li>Bump github.com/zclconf/go-cty from 1.16.2 to 1.16.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/163">hashicorp/terraform-json#163</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/anubhav-goel"><code>@​anubhav-goel</code></a>
made their first contribution in <a
href="https://redirect.github.com/hashicorp/terraform-json/pull/165">hashicorp/terraform-json#165</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/ef5e0b8eca1ba1231002598662be4ea6b8537173"><code>ef5e0b8</code></a>
UnmarshalLogMessage to use json.Number (<a
href="https://redirect.github.com/hashicorp/terraform-json/issues/178">#178</a>)</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/3fe6fb24c20ef18182e01e2ef195288fbb45be90"><code>3fe6fb2</code></a>
Replace <code>json.RawMessage</code> for list log messages (<a
href="https://redirect.github.com/hashicorp/terraform-json/issues/177">#177</a>)</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/739a5926eb30c50488beb49887a8ed6d648a888b"><code>739a592</code></a>
actions: remove action types from action schema (<a
href="https://redirect.github.com/hashicorp/terraform-json/issues/175">#175</a>)</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/2eaedbecab678d315fab0b4ec89f650a913641fb"><code>2eaedbe</code></a>
tf query: add identity version for the list_resource_found message (<a
href="https://redirect.github.com/hashicorp/terraform-json/issues/176">#176</a>)</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/2fd292250e8f5085966cbec4786a0961bb5eb071"><code>2fd2922</code></a>
Merge pull request <a
href="https://redirect.github.com/hashicorp/terraform-json/issues/172">#172</a>
from hashicorp/mutahhir/add-action-invocation</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/b539674b30344f937e07516e82b1c700d73f4c57"><code>b539674</code></a>
Add ConfigUnknown field</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/806b1a620df6e027c5a18afd0c079dfd31fe5338"><code>806b1a6</code></a>
Fix tests after type change</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/23522f30fbc6b53352baccd22e0bd8de3b6d6ec6"><code>23522f3</code></a>
Change ConfigValues and ConfigSensitive to interfaces for redaction</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/dd0e457bc673a4627c3334ebdd1b784f9eed32f6"><code>dd0e457</code></a>
Remove Action Invocation as a Resource Mode</li>
<li><a
href="https://github.com/hashicorp/terraform-json/commit/4643c0fb325f73f4d2d3286e4530c15f328e429d"><code>4643c0f</code></a>
format plan.json for test</li>
<li>Additional commits viewable in <a
href="https://github.com/hashicorp/terraform-json/compare/v0.25.0...v0.27.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/hashicorp/terraform-json&package-manager=go_modules&previous-version=0.25.0&new-version=0.27.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-22 11:30:30 +00:00
dependabot[bot] 8242fd2679 chore: bump github.com/brianvoe/gofakeit/v7 from 7.6.0 to 7.7.1 (#19907)
Bumps
[github.com/brianvoe/gofakeit/v7](https://github.com/brianvoe/gofakeit)
from 7.6.0 to 7.7.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/6fcd2f7ef84a956ff8835abe97767c45df26a693"><code>6fcd2f7</code></a>
address - alias and keyword updates to better target address</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/12b7447d083a8a4ab261935f6ff95c7c1ce5fa40"><code>12b7447</code></a>
payment - remove redundent check</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/de7423068163722b23f2b2376c31a406921f7ff5"><code>de74230</code></a>
person - added ein number</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/69b8730a1a46703039afa73c686c37316dabb944"><code>69b8730</code></a>
payment - added alias</li>
<li>See full diff in <a
href="https://github.com/brianvoe/gofakeit/compare/v7.6.0...v7.7.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/brianvoe/gofakeit/v7&package-manager=go_modules&previous-version=7.6.0&new-version=7.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-22 11:30:05 +00:00
dependabot[bot] 8486795d51 ci: bump the github-actions group with 2 updates (#19909)
Bumps the github-actions group with 2 updates:
[chromaui/action](https://github.com/chromaui/action) and
[tj-actions/changed-files](https://github.com/tj-actions/changed-files).

Updates `chromaui/action` from 13.1.4 to 13.2.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/chromaui/action/commit/20c7e42e1b2f6becd5d188df9acb02f3e2f51519"><code>20c7e42</code></a>
v13.2.0</li>
<li><a
href="https://github.com/chromaui/action/commit/8c981a1b5ed6804b29058a5a68ea818a4c954d16"><code>8c981a1</code></a>
v13.1.5</li>
<li>See full diff in <a
href="https://github.com/chromaui/action/compare/d0795df816d05c4a89c80295303970fddd247cce...20c7e42e1b2f6becd5d188df9acb02f3e2f51519">compare
view</a></li>
</ul>
</details>
<br />

Updates `tj-actions/changed-files` from
1ac60965030588f3b867cdd4a3900f37ec99970c to
4563c729c555b4141fac99c80f699f571219b836
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tj-actions/changed-files/blob/main/HISTORY.md">tj-actions/changed-files's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v46.0.5...v47.0.0">47.0.0</a>
- (2025-09-13)</h1>
<h2><!-- raw HTML omitted -->🚀 Features</h2>
<ul>
<li>Add any_added to outputs (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2567">#2567</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/c260d49a827b5eb266673bed7871c5d3ee9b5aef">c260d49</a>)
- (Jellyfrog)</li>
</ul>
<h2><!-- raw HTML omitted --> Remove</h2>
<ul>
<li>Commit and push step from build job (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2538">#2538</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/be393a90381e27c9fec2c8c2e02b00f005710145">be393a9</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->🔄 Update</h2>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2592">#2592</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/3dbc1e181273d808ccff822a6e00cf18b6628ef0">3dbc1e1</a>)
- (github-actions[bot])</p>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2591">#2591</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/b1ccff8c0892ad141d7d2de6f31e526a9dad931f">b1ccff8</a>)
- (github-actions[bot])</p>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2574">#2574</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/050a3d3360d29711ee9d8210fc639d902d23ad07">050a3d3</a>)
- (github-actions[bot])</p>
<h2><!-- raw HTML omitted -->📚 Documentation</h2>
<ul>
<li>Update link to glob patterns (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2590">#2590</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/a892f50f7a7187bc288633c09230b09ce7ad8fd0">a892f50</a>)
- (Tonye Jack)</li>
<li>Add Jellyfrog as a contributor for code, and doc (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2573">#2573</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f000a9b97f254f9590ff26f651cccde827ad36da">f000a9b</a>)
- (allcontributors[bot])</li>
</ul>
<h2><!-- raw HTML omitted -->🧪 Testing</h2>
<ul>
<li>Manual triggered workflows (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2637">#2637</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/c2ca2493190021783138cb8aac49bcee14b4bb89">c2ca249</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->⚙️ Miscellaneous Tasks</h2>
<ul>
<li><strong>deps-dev:</strong> Bump jest from 30.0.5 to 30.1.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2655">#2655</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/9a6755550a331fdcc8ec45443738933f8fa22eea">9a67555</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/git-cliff from 2.1.0 to 2.2.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2660">#2660</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/b67e30df88f43e244f4e83775e5ad8335114fb95">b67e30d</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.30.2 to
3.30.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2661">#2661</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/62aef422ffa195474d80d73387535cf4622b2824">62aef42</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.11 to
3.30.2 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2659">#2659</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e874f3cddd0f54ae776e6995ae6dae4cf40fd3d3">e874f3c</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/setup-node from 4.4.0 to 5.0.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2656">#2656</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/8c14441336bb3d84fd6b7fa83b6d7201c740baf5">8c14441</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.3.0 to 24.3.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2657">#2657</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e995ac4be5be2bcb6e29556edc51fb63aca6b49b">e995ac4</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.2.1 to 24.3.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2649">#2649</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/3b04099b21072562f07469c10deb182b24236ca9">3b04099</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.9 to
3.29.11 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2651">#2651</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e7b6c977e51984988e3cc1d6b18abe2a3ba8daaa">e7b6c97</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/git-cliff from 2.0.2 to 2.1.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2648">#2648</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/765d62bc041415a5b494ef13d02d566128b25973">765d62b</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.8 to
3.29.9 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2647">#2647</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/2036da178f85576f1940fedb74bb93a36cd89ab7">2036da1</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.7 to
3.29.8 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2644">#2644</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/239aef84a5502c79a1cea96e495d17588c66c659">239aef8</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.2.0 to 24.2.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2645">#2645</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/a7d5f5f4919b6dbc6d3a3689887964361e8dd88f">a7d5f5f</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/checkout from 4.2.2 to 5.0.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2646">#2646</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/5107f3abcc0c3737db51e2949f181e2c197d4d5b">5107f3a</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.1.0 to 24.2.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2640">#2640</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f963b3f3562b00b6d2dd25efc390eb04e51ef6c6">f963b3f</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/download-artifact from 4.3.0 to
5.0.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2641">#2641</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f956744105e18d78bba3844a1199ce43d6503017">f956744</a>)
- (dependabot[bot])</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tj-actions/changed-files/commit/4563c729c555b4141fac99c80f699f571219b836"><code>4563c72</code></a>
chore(deps-dev): bump <code>@​types/node</code> from 24.4.0 to 24.5.2
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2669">#2669</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/257f3acedc7e3cf12510ef211c6d89d140bd64aa"><code>257f3ac</code></a>
chore(deps-dev): bump <code>@​types/uuid</code> from 10.0.0 to 11.0.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2668">#2668</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/cac596ed08f1624de93e8c83fa36f0a0aff72311"><code>cac596e</code></a>
chore(deps-dev): bump <code>@​vercel/ncc</code> from 0.38.3 to 0.38.4
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2670">#2670</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/4fb6914af96ff7e05cc41c37b85b528daf830762"><code>4fb6914</code></a>
chore(deps-dev): bump ts-jest from 29.4.1 to 29.4.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2671">#2671</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/2371aa7cd265d2e8a0213dc9e2a70dfcfb8fa114"><code>2371aa7</code></a>
chore(deps-dev): bump <code>@​types/node</code> from 24.3.1 to 24.4.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2664">#2664</a>)</li>
<li>See full diff in <a
href="https://github.com/tj-actions/changed-files/compare/1ac60965030588f3b867cdd4a3900f37ec99970c...4563c729c555b4141fac99c80f699f571219b836">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-22 11:28:29 +00:00
Spike Curtis e2f5401fb2 test: add test database cleaner in subprocess (#19844)
fixes https://github.com/coder/internal/issues/927

Adds a small subprocess that outlives the testing process to clean up any leaked test databases.
2025-09-22 15:27:06 +04:00
Spike Curtis 596fdcba81 test: refactor dbtestutil to record database creation (#19843)
relates to https://github.com/coder/internal/issues/927

Refactors dbtestutil to use a `Broker` struct to create test databases. Additionally uses a `coder_testing` database to record test databases that are created and when they are dropped.

This is in preparation for the PR above this in the stack which adds a "cleaner" subprocess that cleans out any databases that were left when the test process ends.
2025-09-22 15:13:26 +04:00
Ethan e13fcaf865 refactor(scaletest): support exposing arbitrary metrics on scaletest runs (#19886)
Relates to https://github.com/coder/internal/issues/889

The existing implementation for exposing read and written bytes was a little awkward - we're going to be adding a bunch of scaletest runners / load generators that *don't* transfer any bytes. This PR has the scaletest reports expose a map of arbitrary string-keyed metrics instead.

FWIW, the latest iteration of the scaletesting infrastructure doesn't parse these reports right now - they're just logged to stdout, so we're good to break the json schema here.
2025-09-22 17:45:45 +10:00
Ethan d02ff5f9b2 refactor(scaletest): add runner for creating users (#19811)
Closes https://github.com/coder/internal/issues/985

Simple refactor of the user creation logic into it's own test runner. This lets us create users independently of workspaces, for use in a bunch of load generators, including the Coder Connect load generator.

This PR creates the new runner, and has the existing `createworkspaces` runner use it.
2025-09-22 17:35:36 +10:00
Ethan 6d9e29beb1 refactor(scaletest): generate user and workspace names if omitted (#19885)
Relates to https://github.com/coder/internal/issues/985.

Some scaletest runners would autogenerate names if they weren't supplied on the config, while others required a name be supplied, and a name was autogenerated in the CLI command handler. This PR unifies the runners to make names and emails optional on each config, and generate them in the scaletest runner if omitted. 

The create user runner in the PR above in the stack will do this too.
2025-09-22 17:25:47 +10:00
Asher 7f56212779 feat: add coder_workspace_port_forward MCP tool (#19863)
Closes https://github.com/coder/internal/issues/784
2025-09-21 15:12:57 -08:00
Bruno Quaresma d464360103 fix(site): resolve circular dependency between WorkspacesPage components (#19895)
Move `ACTIVE_BUILD_STATUSES` constant from `WorkspacesPage.tsx` to a
module to break the circular dependency between `WorkspacesPage.tsx` and
`BatchUpdateModalForm.tsx`. This resolves the circular dependency lint
error and ensures proper code organization.

**Error:**
```
• Circular Dependencies
  1) src/pages/WorkspacesPage/WorkspacesPage.tsx -> src/pages/WorkspacesPage/BatchUpdateModalForm.tsx
```

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-19 18:50:11 -03:00
Benraouane Soufiane f39cf3091c chore(site): add rustdesk icon (#19888) 2025-09-19 14:46:51 -06:00
Brett Kolodny 38ca98745b feat: add shared_with_group: and shared_with_user: filters to /workspaces endpoint (#19875)
Adds shared_with_user and shared_with_group filters to the /workspaces
endpoint.

- `shared_with_user`: filters workspaces shared with a specific user.
Accepts a user UUID or username.
- `shared_with_group`: filters workspaces shared with a specific group.
Accepts:
  - a group UUID, or
  - `<organization name>/<group name>`, or
  - `<group name>` (resolved in the default organization).


Closes
[coder/internal#1004](https://github.com/coder/internal/issues/1004)
2025-09-19 16:05:27 -04:00
david-fraley 40ffb79057 fix: update bitnami image (#19892) 2025-09-19 22:50:28 +05:00
david-fraley 738dbc6d47 fix: update bitnami to use legacy image (#19891) 2025-09-19 15:02:04 +00:00
Paweł Banaszewski 439b041780 feat: add best effort attempt to revoke oauth access token in external auth provider (#19775)
Solves #15575
Adds OAuth access token revocation when unlinking external auth
provider. Due to revocation not being consistently implemented by
providers this is only best effort attempt. Unsuccessful revocation
won't influence link removal.
2025-09-19 16:27:02 +02:00
Kacper Sawicki 0601cc8fa6 feat(site): display warnings in tasks page when wildcard is not configured (#19780)
Closes #19739 

This PR adds warnings to Task page to alert users when wildcard is not
configured.

Admin:
<img width="1871" height="1209" alt="image"
src="https://github.com/user-attachments/assets/01beb885-5493-41ed-a266-58609bee5b8b"
/>

Member:
<img width="1713" height="1204" alt="image"
src="https://github.com/user-attachments/assets/53c2bae5-5e0f-458c-b761-633399bdf2f0"
/>
2025-09-19 15:49:34 +02:00
Spike Curtis eb74d902bf test: fix TestServer cases that cancel before PostgreSQL work is done (#19872)
fixes https://github.com/coder/internal/issues/946

Some tests tear down the server before we are done with PostgreSQL work, and the default `clitest` infrastructure fails the test if any errors like that are thrown. This PR modifies the tests like that to ignore postgreSQL errors like this.
2025-09-19 15:14:48 +04:00
Thomas Kosiewski 4d8dc221bf chore: upgrade Node.js from 20.19.4 to 22.19.0 and update dependencies (#19870)
# Update Node.js from 20.19.4 to 22.19.0

This PR updates Node.js from v20.19.4 to v22.19.0 across the codebase. The change includes:

- Updated Node.js version in GitHub Actions setup-node workflow
- Updated Node.js version in the dogfood Dockerfile
- Changed from `pkgs.nodejs_20` to `unstablePkgs.nodejs_22` in the Nix flake
- Updated the Node.js engine version constraints in package.json files to allow Node.js 22
- Updated Playwright from v1.47.0 to v1.50.1
- Updated tzdata dependency from v1.0.44 to v1.0.46
- Updated the flake.lock file with latest nixpkgs references

The PR also improves the error message for Playwright version mismatches by showing the actual versions in the error.
2025-09-18 23:08:55 +02:00
Rowan Smith 623893708b feat: add helm var to support RBAC for deploying workspaces in extra namespaces (#19517)
This is a feature to create Role & RoleBinding entries on a per
namespace basis to support deploying workspaces in separate namespace to
where Coder is deployed. The idea behind this is to avoid the creation
of custom RBAC entries or the use of ClusterRoles (in order to maintain
priciple of least privilege).

> 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.

This is a blink assisted PR.

Example `helm template` without
`coder.serviceAccount.workspaceNamespaces` enabled (existing behaviour
as of current release) is below. Outcome = 1 x SA, 1 x Role, 1 x
RoleBinding, all in the coder (`.Release.Namespace`) namespace.
```
➜  coder git:(feat/helm_namespace_rbac_improvements) ✗ helm template -n coder coder . --set coder.image.tag=v2.25.1
---
...
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: coder-workspace-perms
  namespace: coder
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups:
    - apps
    resources:
    - deployments
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: "coder"
  namespace: coder
subjects:
  - kind: ServiceAccount
    name: "coder"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: coder-workspace-perms
---
```

Example `helm template` *with*
`coder.serviceAccount.workspaceNamespaces` enabled is below. Outcome = 1
x SA, 1 x Role, 1 x RoleBinding, all in the coder (`.Release.Namespace`)
namespace PLUS a Role and RoleBinding in the `dev-ws` namespace with
each of the RoleBindings referencing the coder SA in the coder
(`.Release.Namespace`) namespace:

```
➜  coder git:(feat/helm_namespace_rbac_improvements) ✗ helm template -n coder coder . --set coder.image.tag=v2.25.1 --set-json 'coder.serviceAccount.workspaceNamespaces=[{"name":"dev-ws","workspacePerms":true,"enableDeployments":true,"extraRules":[]}]' 
---
...
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: coder-workspace-perms
  namespace: coder
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups:
    - apps
    resources:
    - deployments
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: coder-workspace-perms
  namespace: dev-ws
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
  - apiGroups:
    - apps
    resources:
    - deployments
    verbs:
    - create
    - delete
    - deletecollection
    - get
    - list
    - patch
    - update
    - watch
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: "coder"
  namespace: coder
subjects:
  - kind: ServiceAccount
    name: "coder"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: coder-workspace-perms
---
# Source: coder/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: "coder"
  namespace: dev-ws
subjects:
  - kind: ServiceAccount
    name: "coder"
    namespace: coder
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: coder-workspace-perms
---
```
2025-09-18 14:27:04 +10:00
Michael Smith 8a6852f095 fix(site): revamp UI for batch-updating workspaces (#18895)
Closes https://github.com/coder/coder/issues/18879
Builds on https://github.com/coder/coder/pull/18895

## Changes made
- Deleted `BatchUpdateConfirmation` component, replacing it with
`BatchUpdateModalForm`
- Added stories for the new component, trying to capture every variant I
could think of

## Screenshots
<img width="840" height="1059" alt="image"
src="https://github.com/user-attachments/assets/0fdac28e-19e8-4a14-a20a-fc1fd3758c81"
/>


## Notes
- There's too many problems to list, but look at the issue to see all
the problems we had with the old implementation
- It's definitely helpful to look at the stories to see all the things
the component is meant to cover
2025-09-17 20:46:38 -04:00
Michael Smith 759746cbf1 fix(site): update useAgentLogs to make it more testable and add more tests (#19126)
Take 2
Closes https://github.com/coder/internal/issues/644

## Changes made
- Updated how `useAgentLogs` was defined to make it easier to inject
specific data dependencies (basically making the hook more
unit-testable)
- Simplified the hook API to limit the amount of scope of data it needs
to work
- Added more test cases, and re-enabled the one test case we had
previously disabled
- Extracted our mock websocket code into a separate file, and added more
methods to it
- Updated all runtime code to accommodate new changes
2025-09-17 19:58:02 -04:00
Michael Smith d3bf5065a0 chore(site): convert more components from Emotion to TailwindCSS (#19719)
## Changes made
- Patched React `CSSProperties` type to add support for custom CSS
properties
- Updated several of the components in the `components` directory to
Tailwind
- Updated most of the `WorkspacePageBuildView` component to Tailwind to
account for CSS specificity changes
- Updated `Search` to address accessibility violation and removed all
MUI logic
- Updated `Search` stories (added new story, decoupled all stories from
single decorator)
- Updated `autoFocus` behavior in `SearchField`
- Updated the styling for `WorkspacePageBuildView` to make sure the tabs
had enough padding
- Fixed layout effect in `WorkspacePageBuildView` to fire correctly
2025-09-17 18:55:25 -04:00
Steven Masley 679179f404 feat: scope allow_list to include resource_type (#19748)
This feature allows the `allow_list` in the scopes to specify the `type`
2025-09-17 08:32:14 -05:00
Steven Masley 3df9d8e902 test: set test flags from within an init to limit maximum test parallelism (#19575) 2025-09-17 08:24:19 -05:00
Ethan 356604eca6 chore(coderd/notifications): avoid generating warning logs for trivial enqueue failures (#19840)
I noticed during a scaletest that many warning logs were being generated when enqueuing notifications. The error was:
```
failed to notify of workspace creation: notification is not enabled
```
I don't think we should be warning if automated notifications fail to send to users because they have them disabled.

To fix, we'll stop returning these errors.
2025-09-17 18:17:18 +10:00
Marcin Tojek 8781499c3d chore: add nextflow icon (#19838)
Related: https://github.com/coder/registry/pull/416
2025-09-17 08:46:57 +02:00
ケイラ f5fac293dc fix(site): show available logs consistently on template creation page (#19832) 2025-09-16 14:33:57 -06:00
david-fraley ea718084ff docs: update Tasks Template Code (#19770) 2025-09-16 20:27:57 +00:00
ケイラ 8db82d25b3 chore: upgrade to react 19 (#19829) 2025-09-16 13:37:30 -06:00
Danny Kopping 422bba44d9 chore: add aibridge database resources & define RBAC policies (#19796)
Closes https://github.com/coder/internal/issues/986
2025-09-16 21:31:17 +02:00
Danny Kopping 348a2e0285 feat: add configs for external auth MCP usage + tool allow/denylist (#19794)
Closes https://github.com/coder/internal/issues/988

The logic for allowing/denying tools can be found in https://github.com/coder/aibridge/pull/4/files#diff-330a6371a583dd8cadeed79b95499e3a87960ad8ea4d6a94061e8f88a44834c3 (`ProxyBase.filterAllowedTools`).
2025-09-16 20:31:29 +02:00
Spike Curtis 655a36c392 test: fix TestAgentConnectionMonitor_PingTimeout race with mock assertions (#19836)
Fixes https://github.com/coder/internal/issues/970

The test doesn't wait for `monitor()` to complete, and the mock database call that we assert takes place in a `defer` within `monitor()`. This allows the mock assertions to race with the defer and flake the test.

Solution is to explicitly wait for `monitor()` to complete before the end of the test, so that mock assertions (which happen in a `t.Cleanup()`) don't race.
2025-09-16 21:54:50 +04:00
Spike Curtis 4fc0093388 fix: fix TestCloserStack_Timeout to wait for all asyncClosers (#19837)
fixes https://github.com/coder/internal/issues/966


TestCloserStack_Timeout creates `asyncCloser`s which allow control over the exact timing and order of their close method returning. They also, as a final backstop will throw an error if the test context ends before they are unblocked.

TestCloserStack_Timeout unblocks all `asyncCloser`s in a defer and then ends the test. This defer _unblocks_ the running close goroutines, but does not wait for them to finish. Since the test context is canceled as soon as the test completes, this creates a race condition where the close goroutines can trigger the context cancelled  arm of the `select` statement.

The fix is to both unblock and wait for all close goroutines to complete before ending the test and cancelling the context.
2025-09-16 21:53:50 +04:00
Brett Kolodny e6b04d1918 feat: add shared filter to workspaces query (#19807)
Adds a `shared:<boolean>` search query to the `/workspaces [get]`
endpoint


https://github.com/user-attachments/assets/ccf84bd9-c1fd-4085-825b-2e3176a2d488

Closes
[coder/internal#972](https://github.com/coder/internal/issues/972)
2025-09-16 12:37:39 -04:00
Danny Kopping 8487216548 chore: add aibridge configs & experiment (#19793) 2025-09-16 11:45:23 +02:00
Ethan 2695bb418a ci: reduce size of ci failure slack notification (#19835)
The slack message will now look something like:

>  CI Failure in main
>
> View failure: Click here
>
> @.Blink Investigate this CI failure. Before starting, use your
workspace to read
https://gist.githubusercontent.com/ethanndickson/8dea9f1db3957ac1baf30ae8ce6f1a42/raw/da95920805456580499462a14b9a906d377414c5/blink-flake-instructions.md

(The prompt section is stored in a GHA variable so it can be modified
without committing.)
2025-09-16 08:14:39 +00:00
Stephen Kirby 2f35deeb39 chore(docs): update release calendar for 2.26 mainline release (#19680)
Co-authored-by: Atif Ali <atif@coder.com>
2025-09-16 06:17:49 +00:00
Ethan 995b330250 test: avoid sharing deployment values between subtests (#19833)
Blink didn't figure out a CI failure on main was caused by a data race; fixing it.


I've also updated the [blink prompt](https://gist.githubusercontent.com/ethanndickson/8dea9f1db3957ac1baf30ae8ce6f1a42/raw/060aea7fabb82bef0029a17dad9a5daee7940760/blink-flake-instructions.md).

https://github.com/coder/coder/actions/runs/17737809615
2025-09-16 13:51:26 +10:00
david-fraley cda859363e docs: fix formatting issues (#19831) 2025-09-15 17:02:56 -05:00
Rowan Smith f40ae708d6 chore: fix grammar issue (#19810)
fix a grammar issue.

Added an extra whitespace to the end / expanded the width of the table
to maintain space as adding the one char would have pushed `helps` hard
up against the table edge.

> 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.

no AI

Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-15 21:33:56 +00:00
Asher c31768dfe7 fix: use filepath to construct mcp test write path (#19808)
Hopefully fixes https://github.com/coder/internal/issues/993

```
  file path must be absolute: "/test/some/path"
```

Not sure if this is the right fix though, since I am not sure how this
only flakes rather than always failing. It looks like starting with `/`
should not be considered absolute in Windows, but then how did it ever
pass at all?
2025-09-15 09:00:30 -08:00
Danny Kopping 18b0acab4d fix: correct MCP tools' input schemas (#19825)
The input schemas of `coder_workspace_edit_file` and
`coder_workspace_edit_files` were violating the JSON Schemas of both
Anthropic and OpenAI.

Follow-up: we should add a test to ensure future compatibility with
these specs.

Signed-off-by: Danny Kopping <danny@coder.com>
2025-09-15 17:56:44 +02:00
ケイラ 8ff4ba0c70 fix: scroll item list into view when opening MultiSelectCombobox (#19806) 2025-09-15 09:29:25 -06:00
blink-so[bot] c9a877a386 docs: remove beta references from dynamic parameters (#19714)
Dynamic parameters are now GA as of v2.25.0, so this PR updates the
troubleshooting section to remove outdated beta references and clarify
current support.

**Changes:**
- Updated "Template variables not showing up" section to remove beta
references
- Clarified that template variables are fully supported in GA version
- Simplified troubleshooting guidance

Fixes the outdated documentation mentioned in the Slack thread.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: bpmct <22407953+bpmct@users.noreply.github.com>
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-15 15:11:22 +00:00
dependabot[bot] 4dc5d5c733 chore: bump github.com/gohugoio/hugo from 0.149.1 to 0.150.0 (#19826)
Bumps [github.com/gohugoio/hugo](https://github.com/gohugoio/hugo) from
0.149.1 to 0.150.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gohugoio/hugo/releases">github.com/gohugoio/hugo's
releases</a>.</em></p>
<blockquote>
<h2>v0.150.0</h2>
<p>The big new feature in this relase is the new <code>version</code>
config option on <a
href="https://gohugo.io/configuration/module/#version">Module
imports</a>, which allows you to set the requested module <a
href="https://go.dev/ref/mod#version-queries">version query</a> directly
in your Hugo configuration (e.g. <code>hugo.toml</code>). This is a
feature that have been requested by many, and I (<a
href="https://github.com/bep"><code>@​bep</code></a>) was reminded about
it by <a
href="https://discourse.gohugo.io/t/mounting-different-branches-tags-from-the-same-repository/55849/5">this
recent thread</a>, which also outlines a common use case for this:
Mounting multiple old versions/branches of API documentation into the
project.</p>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump golang.org/x/mod from 0.27.0 to 0.28.0 d1f6a1dc5
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]</li>
<li>modules: Add support for direct version module imports in hugo.toml
747cf4ad6 <a href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13964">#13964</a></li>
<li>resources/page: Fix truncated summary logic d8774d7fc <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13967">#13967</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/13968">#13968</a></li>
<li>config/security: Add PROGRAMDATA to the osenv allowlist 3b8947d82 <a
href="https://github.com/jmooring"><code>@​jmooring</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/gohugoio/hugo/commit/3f5473b7d4e7377e807290c3acc89feeef1aaa71"><code>3f5473b</code></a>
releaser: Bump versions for release of 0.150.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/d1f6a1dc595b3f456044d803c506fbbf36c5d45f"><code>d1f6a1d</code></a>
build(deps): bump golang.org/x/mod from 0.27.0 to 0.28.0</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/747cf4ad657b6eaeffcd0c7b0a10a38980c7b54c"><code>747cf4a</code></a>
modules: Add support for direct version module imports in hugo.toml</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/d8774d7fc34b12eb39dc85d0f7df797b13ab8fcc"><code>d8774d7</code></a>
resources/page: Fix truncated summary logic</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/3b8947d8217621dbe42198263a27e04a2490ab81"><code>3b8947d</code></a>
config/security: Add PROGRAMDATA to the osenv allowlist</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/321a66ef19ac25411bbbec926b7892be5ae55416"><code>321a66e</code></a>
releaser: Prepare repository for 0.150.0-DEV</li>
<li>See full diff in <a
href="https://github.com/gohugoio/hugo/compare/v0.149.1...v0.150.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/gohugoio/hugo&package-manager=go_modules&previous-version=0.149.1&new-version=0.150.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 14:15:22 +00:00
dependabot[bot] 32b72bf571 ci: bump the github-actions group with 3 updates (#19824)
Bumps the github-actions group with 3 updates:
[step-security/harden-runner](https://github.com/step-security/harden-runner),
[tj-actions/changed-files](https://github.com/tj-actions/changed-files)
and [github/codeql-action](https://github.com/github/codeql-action).

Updates `step-security/harden-runner` from 2.13.0 to 2.13.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/step-security/harden-runner/releases">step-security/harden-runner's
releases</a>.</em></p>
<blockquote>
<h2>v2.13.1</h2>
<h2>What's Changed</h2>
<ul>
<li>
<p>Graceful handling of HTTP errors: Improved error handling when
fetching Harden Runner policies from the StepSecurity Policy Store API,
ensuring more reliable execution even in case of temporary network/API
issues.</p>
</li>
<li>
<p>Security updates for npm dependencies: Updated vulnerable npm package
dependencies to the latest secure versions.</p>
</li>
<li>
<p>Faster enterprise agent downloads: The enterprise agent is now
downloaded from GitHub Releases instead of packages.stepsecurity.io,
improving download speed and reliability.</p>
</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/step-security/harden-runner/compare/v2.13.0...v2.13.1">https://github.com/step-security/harden-runner/compare/v2.13.0...v2.13.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/step-security/harden-runner/commit/f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a"><code>f4a75cf</code></a>
Merge pull request <a
href="https://redirect.github.com/step-security/harden-runner/issues/588">#588</a>
from step-security/rc-26</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/95503d076c107b91d03775aee93f4cb86a134dda"><code>95503d0</code></a>
ci: remove code-review workflow</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/4b250a07397b4cabc038948e3d832967764db6b4"><code>4b250a0</code></a>
ci: add job to confirm dist is as expected</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/5b0ab6abcf4c643b4ddf49d19fdb13c5821d124b"><code>5b0ab6a</code></a>
update dependencies</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/d11f2c1d65a99d8b0f20925c425d4d932e6d3366"><code>d11f2c1</code></a>
fix bug where status code was not being preserved</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/b3fc98e4dfca39273624a95b9c5fd70d759997e5"><code>b3fc98e</code></a>
improve error handling for policy store sceanrio</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/92fc5d4bf78f09f5494523f3d8f2f85786fe757f"><code>92fc5d4</code></a>
update error message</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/b61b0a4938ef1a80f368fbb9a0abcf78846e15a4"><code>b61b0a4</code></a>
policy store improvements</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/e3d3f2baeacadcbf3b2ad500171dd444855d4577"><code>e3d3f2b</code></a>
use GitHub release instead of packages</li>
<li><a
href="https://github.com/step-security/harden-runner/commit/646ac01e72c16075733090f55857fc2f2d9a7e7a"><code>646ac01</code></a>
update agent</li>
<li>Additional commits viewable in <a
href="https://github.com/step-security/harden-runner/compare/ec9f2d5744a09debf3a187a3f4f675c53b671911...f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a">compare
view</a></li>
</ul>
</details>
<br />

Updates `tj-actions/changed-files` from
8c14441336bb3d84fd6b7fa83b6d7201c740baf5 to
1ac60965030588f3b867cdd4a3900f37ec99970c
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tj-actions/changed-files/blob/main/HISTORY.md">tj-actions/changed-files's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v46.0.5...v47.0.0">47.0.0</a>
- (2025-09-13)</h1>
<h2><!-- raw HTML omitted -->🚀 Features</h2>
<ul>
<li>Add any_added to outputs (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2567">#2567</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/c260d49a827b5eb266673bed7871c5d3ee9b5aef">c260d49</a>)
- (Jellyfrog)</li>
</ul>
<h2><!-- raw HTML omitted --> Remove</h2>
<ul>
<li>Commit and push step from build job (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2538">#2538</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/be393a90381e27c9fec2c8c2e02b00f005710145">be393a9</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->🔄 Update</h2>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2592">#2592</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/3dbc1e181273d808ccff822a6e00cf18b6628ef0">3dbc1e1</a>)
- (github-actions[bot])</p>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2591">#2591</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/b1ccff8c0892ad141d7d2de6f31e526a9dad931f">b1ccff8</a>)
- (github-actions[bot])</p>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2574">#2574</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/050a3d3360d29711ee9d8210fc639d902d23ad07">050a3d3</a>)
- (github-actions[bot])</p>
<h2><!-- raw HTML omitted -->📚 Documentation</h2>
<ul>
<li>Update link to glob patterns (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2590">#2590</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/a892f50f7a7187bc288633c09230b09ce7ad8fd0">a892f50</a>)
- (Tonye Jack)</li>
<li>Add Jellyfrog as a contributor for code, and doc (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2573">#2573</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f000a9b97f254f9590ff26f651cccde827ad36da">f000a9b</a>)
- (allcontributors[bot])</li>
</ul>
<h2><!-- raw HTML omitted -->🧪 Testing</h2>
<ul>
<li>Manual triggered workflows (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2637">#2637</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/c2ca2493190021783138cb8aac49bcee14b4bb89">c2ca249</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->⚙️ Miscellaneous Tasks</h2>
<ul>
<li><strong>deps-dev:</strong> Bump jest from 30.0.5 to 30.1.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2655">#2655</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/9a6755550a331fdcc8ec45443738933f8fa22eea">9a67555</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/git-cliff from 2.1.0 to 2.2.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2660">#2660</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/b67e30df88f43e244f4e83775e5ad8335114fb95">b67e30d</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.30.2 to
3.30.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2661">#2661</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/62aef422ffa195474d80d73387535cf4622b2824">62aef42</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.11 to
3.30.2 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2659">#2659</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e874f3cddd0f54ae776e6995ae6dae4cf40fd3d3">e874f3c</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/setup-node from 4.4.0 to 5.0.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2656">#2656</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/8c14441336bb3d84fd6b7fa83b6d7201c740baf5">8c14441</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.3.0 to 24.3.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2657">#2657</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e995ac4be5be2bcb6e29556edc51fb63aca6b49b">e995ac4</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.2.1 to 24.3.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2649">#2649</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/3b04099b21072562f07469c10deb182b24236ca9">3b04099</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.9 to
3.29.11 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2651">#2651</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e7b6c977e51984988e3cc1d6b18abe2a3ba8daaa">e7b6c97</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/git-cliff from 2.0.2 to 2.1.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2648">#2648</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/765d62bc041415a5b494ef13d02d566128b25973">765d62b</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.8 to
3.29.9 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2647">#2647</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/2036da178f85576f1940fedb74bb93a36cd89ab7">2036da1</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.29.7 to
3.29.8 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2644">#2644</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/239aef84a5502c79a1cea96e495d17588c66c659">239aef8</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.2.0 to 24.2.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2645">#2645</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/a7d5f5f4919b6dbc6d3a3689887964361e8dd88f">a7d5f5f</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/checkout from 4.2.2 to 5.0.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2646">#2646</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/5107f3abcc0c3737db51e2949f181e2c197d4d5b">5107f3a</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
24.1.0 to 24.2.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2640">#2640</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f963b3f3562b00b6d2dd25efc390eb04e51ef6c6">f963b3f</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump actions/download-artifact from 4.3.0 to
5.0.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2641">#2641</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/f956744105e18d78bba3844a1199ce43d6503017">f956744</a>)
- (dependabot[bot])</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tj-actions/changed-files/commit/1ac60965030588f3b867cdd4a3900f37ec99970c"><code>1ac6096</code></a>
Upgraded to v47 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2663">#2663</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/24d32ffd492484c1d75e0c0b894501ddb9d30d62"><code>24d32ff</code></a>
upgrade: to node24 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2662">#2662</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/9a6755550a331fdcc8ec45443738933f8fa22eea"><code>9a67555</code></a>
chore(deps-dev): bump jest from 30.0.5 to 30.1.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2655">#2655</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/b67e30df88f43e244f4e83775e5ad8335114fb95"><code>b67e30d</code></a>
chore(deps): bump tj-actions/git-cliff from 2.1.0 to 2.2.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2660">#2660</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/62aef422ffa195474d80d73387535cf4622b2824"><code>62aef42</code></a>
chore(deps): bump github/codeql-action from 3.30.2 to 3.30.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2661">#2661</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/e874f3cddd0f54ae776e6995ae6dae4cf40fd3d3"><code>e874f3c</code></a>
chore(deps): bump github/codeql-action from 3.29.11 to 3.30.2 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2659">#2659</a>)</li>
<li>See full diff in <a
href="https://github.com/tj-actions/changed-files/compare/8c14441336bb3d84fd6b7fa83b6d7201c740baf5...1ac60965030588f3b867cdd4a3900f37ec99970c">compare
view</a></li>
</ul>
</details>
<br />

Updates `github/codeql-action` from 3.30.1 to 3.30.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/releases">github/codeql-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.30.3</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.30.3 - 10 Sep 2025</h2>
<p>No user facing changes.</p>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.30.3/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
<h2>v3.30.2</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>3.30.2 - 09 Sep 2025</h2>
<ul>
<li>Fixed a bug which could cause language autodetection to fail. <a
href="https://redirect.github.com/github/codeql-action/pull/3084">#3084</a></li>
<li>Experimental: The <code>quality-queries</code> input that was added
in <code>3.29.2</code> as part of an internal experiment is now
deprecated and will be removed in an upcoming version of the CodeQL
Action. It has been superseded by a new <code>analysis-kinds</code>
input, which is part of the same internal experiment. Do not use this in
production as it is subject to change at any time. <a
href="https://redirect.github.com/github/codeql-action/pull/3064">#3064</a></li>
</ul>
<p>See the full <a
href="https://github.com/github/codeql-action/blob/v3.30.2/CHANGELOG.md">CHANGELOG.md</a>
for more information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's
changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a
href="https://github.com/github/codeql-action/releases">releases
page</a> for the relevant changes to the CodeQL CLI and language
packs.</p>
<h2>[UNRELEASED]</h2>
<ul>
<li>We have improved the CodeQL Action's ability to validate that the
workflow it is used in does not use different versions of the CodeQL
Action for different workflow steps. Mixing different versions of the
CodeQL Action in the same workflow is unsupported and can lead to
unpredictable results. A warning will now be emitted from the
<code>codeql-action/init</code> step if different versions of the CodeQL
Action are detected in the workflow file. Additionally, an error will
now be thrown by the other CodeQL Action steps if they load a
configuration file that was generated by a different version of the
<code>codeql-action/init</code> step. <a
href="https://redirect.github.com/github/codeql-action/pull/3099">#3099</a>
and <a
href="https://redirect.github.com/github/codeql-action/pull/3100">#3100</a></li>
<li>We added support for reducing the size of dependency caches for Java
analyses, which will reduce cache usage and speed up workflows. This
will be enabled automatically at a later time. <a
href="https://redirect.github.com/github/codeql-action/pull/3107">#3107</a></li>
</ul>
<h2>3.30.3 - 10 Sep 2025</h2>
<p>No user facing changes.</p>
<h2>3.30.2 - 09 Sep 2025</h2>
<ul>
<li>Fixed a bug which could cause language autodetection to fail. <a
href="https://redirect.github.com/github/codeql-action/pull/3084">#3084</a></li>
<li>Experimental: The <code>quality-queries</code> input that was added
in <code>3.29.2</code> as part of an internal experiment is now
deprecated and will be removed in an upcoming version of the CodeQL
Action. It has been superseded by a new <code>analysis-kinds</code>
input, which is part of the same internal experiment. Do not use this in
production as it is subject to change at any time. <a
href="https://redirect.github.com/github/codeql-action/pull/3064">#3064</a></li>
</ul>
<h2>3.30.1 - 05 Sep 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.23.0. <a
href="https://redirect.github.com/github/codeql-action/pull/3077">#3077</a></li>
</ul>
<h2>3.30.0 - 01 Sep 2025</h2>
<ul>
<li>Reduce the size of the CodeQL Action, speeding up workflows by
approximately 4 seconds. <a
href="https://redirect.github.com/github/codeql-action/pull/3054">#3054</a></li>
</ul>
<h2>3.29.11 - 21 Aug 2025</h2>
<ul>
<li>Update default CodeQL bundle version to 2.22.4. <a
href="https://redirect.github.com/github/codeql-action/pull/3044">#3044</a></li>
</ul>
<h2>3.29.10 - 18 Aug 2025</h2>
<p>No user facing changes.</p>
<h2>3.29.9 - 12 Aug 2025</h2>
<p>No user facing changes.</p>
<h2>3.29.8 - 08 Aug 2025</h2>
<ul>
<li>Fix an issue where the Action would autodetect unsupported languages
such as HTML. <a
href="https://redirect.github.com/github/codeql-action/pull/3015">#3015</a></li>
</ul>
<h2>3.29.7 - 07 Aug 2025</h2>
<p>This release rolls back 3.29.6 to address issues with language
autodetection. It is identical to 3.29.5.</p>
<h2>3.29.6 - 07 Aug 2025</h2>
<ul>
<li>The <code>cleanup-level</code> input to the <code>analyze</code>
Action is now deprecated. The CodeQL Action has written a limited amount
of intermediate results to the database since version 2.2.5, and now
automatically manages cleanup. <a
href="https://redirect.github.com/github/codeql-action/pull/2999">#2999</a></li>
<li>Update default CodeQL bundle version to 2.22.3. <a
href="https://redirect.github.com/github/codeql-action/pull/3000">#3000</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/github/codeql-action/commit/192325c86100d080feab897ff886c34abd4c83a3"><code>192325c</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3104">#3104</a>
from github/update-v3.30.3-b660efdcf</li>
<li><a
href="https://github.com/github/codeql-action/commit/e68956d90b7fe2260904652cd8de5d73563e4944"><code>e68956d</code></a>
Update changelog for v3.30.3</li>
<li><a
href="https://github.com/github/codeql-action/commit/b660efdcfdfa893d74568cd884067ed18e8d6f88"><code>b660efd</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3103">#3103</a>
from github/mbg/fix/category-check</li>
<li><a
href="https://github.com/github/codeql-action/commit/e49458befe579c5a1088aacda9f2ae384da104ff"><code>e49458b</code></a>
Fix <code>runInterpretResultsFor</code> using the wrong
<code>AnalysisConfig</code> for <code>category</code> fix</li>
<li><a
href="https://github.com/github/codeql-action/commit/f374a62c8bedef779582aeb425a68f7798f2078c"><code>f374a62</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3098">#3098</a>
from github/kaspersv/increase-overlay-base-size-limit</li>
<li><a
href="https://github.com/github/codeql-action/commit/5efa438e92992578d794ae4ceed960bf81011677"><code>5efa438</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3101">#3101</a>
from github/mbg/public-repo-notice-in-pr-template</li>
<li><a
href="https://github.com/github/codeql-action/commit/8a84a62542ea24fd569eb5afdfb2507c25328ab9"><code>8a84a62</code></a>
Overlay: Increase size limit for cached overlay base database</li>
<li><a
href="https://github.com/github/codeql-action/commit/eb50a881d87eb8488328fefe024ae2f6add8384f"><code>eb50a88</code></a>
Merge pull request <a
href="https://redirect.github.com/github/codeql-action/issues/3097">#3097</a>
from github/redsun82/only-dump-sarif</li>
<li><a
href="https://github.com/github/codeql-action/commit/4c534612bf77788909753a5602e96710156f5758"><code>4c53461</code></a>
Tweak sarif dump log</li>
<li><a
href="https://github.com/github/codeql-action/commit/dae3742b0a3b9e08acc580e15ef74bdc454d650a"><code>dae3742</code></a>
Dump soon to be uploaded SARIF on request</li>
<li>Additional commits viewable in <a
href="https://github.com/github/codeql-action/compare/f1f6e5f6af878fb37288ce1c627459e94dbf7d01...192325c86100d080feab897ff886c34abd4c83a3">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 12:40:44 +00:00
dependabot[bot] 4fde5f8588 chore: bump github.com/aws/aws-sdk-go-v2 from 1.38.1 to 1.39.0 (#19821)
Bumps
[github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2)
from 1.38.1 to 1.39.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/648027edb8aeba036195538174a63cbccaca8c16"><code>648027e</code></a>
Release 2025-09-08</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/a3b9b7b1303ebf9fc1837f70253fe086d0ee72a2"><code>a3b9b7b</code></a>
Regenerated Clients</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/67dad834dcc6ab1c14a2b9935bd92bc1ff91ddd9"><code>67dad83</code></a>
Update endpoints model</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/2bfe86a2ca763380b5fb498feab0886d41b96dcc"><code>2bfe86a</code></a>
Update API model</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/59e7410f279040b4c186e149402eebf13c5e06d9"><code>59e7410</code></a>
add businessmetrics feature ID for env-based bearer token (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/3182">#3182</a>)</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/1cdc15880e2f5087470b6f813eb0a5cd1451c95f"><code>1cdc158</code></a>
Patching override s3expire shape (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/3180">#3180</a>)</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/1745ede8f69372b0e08c4a8bcc83d43e76caeb26"><code>1745ede</code></a>
Release 2025-09-05</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/f84de5375a6e97e530819caba27fa0c7bc773cd6"><code>f84de53</code></a>
Regenerated Clients</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/7cdaa318493e10d7ef9dd87eb1130b9806c48c98"><code>7cdaa31</code></a>
Update API model</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/498b5c42d5b3fc143fc3449665faa3635f468d09"><code>498b5c4</code></a>
remove service/sms (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/3177">#3177</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/v1.38.1...v1.39.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2&package-manager=go_modules&previous-version=1.38.1&new-version=1.39.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 12:23:43 +00:00
dependabot[bot] 05f8ffd943 chore: bump github.com/valyala/fasthttp from 1.65.0 to 1.66.0 (#19822)
Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp)
from 1.65.0 to 1.66.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/valyala/fasthttp/releases">github.com/valyala/fasthttp's
releases</a>.</em></p>
<blockquote>
<h2>v1.66.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps): bump securego/gosec from 2.22.7 to 2.22.8 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2056">valyala/fasthttp#2056</a></li>
<li>docs: add fasthttp-auth to related projects section by <a
href="https://github.com/FAUST-BENCHOU"><code>@​FAUST-BENCHOU</code></a>
in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2057">valyala/fasthttp#2057</a></li>
<li>server: refactor to use atomic type by <a
href="https://github.com/cuiweixie"><code>@​cuiweixie</code></a> in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2058">valyala/fasthttp#2058</a></li>
<li>chore(deps): bump actions/setup-go from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2060">valyala/fasthttp#2060</a></li>
<li>Fix extra whitespace parsing in HTTP request lines to prevent cache
poisoning by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2061">valyala/fasthttp#2061</a></li>
<li>Drop Go 1.23 support by <a
href="https://github.com/erikdubbelboer"><code>@​erikdubbelboer</code></a>
in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2065">valyala/fasthttp#2065</a></li>
<li>Add flushing support to fasthttpadaptor by <a
href="https://github.com/grivera64"><code>@​grivera64</code></a> in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2054">valyala/fasthttp#2054</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/FAUST-BENCHOU"><code>@​FAUST-BENCHOU</code></a>
made their first contribution in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2057">valyala/fasthttp#2057</a></li>
<li><a href="https://github.com/cuiweixie"><code>@​cuiweixie</code></a>
made their first contribution in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2058">valyala/fasthttp#2058</a></li>
<li><a href="https://github.com/Copilot"><code>@​Copilot</code></a> made
their first contribution in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2061">valyala/fasthttp#2061</a>
😄</li>
<li><a href="https://github.com/grivera64"><code>@​grivera64</code></a>
made their first contribution in <a
href="https://redirect.github.com/valyala/fasthttp/pull/2054">valyala/fasthttp#2054</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/valyala/fasthttp/compare/v1.65.0...v1.66.0">https://github.com/valyala/fasthttp/compare/v1.65.0...v1.66.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/valyala/fasthttp/commit/e04490f830dd9671b53e2359cddc79a23d7877f3"><code>e04490f</code></a>
Add flushing support to fasthttpadaptor (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2054">#2054</a>)</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/e9640b4d39215ac87fa85c171b26d745a40a8e99"><code>e9640b4</code></a>
Update benchmarks in readme</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/4d25421ae55c9f5d28dc3731d9a51106a71b7254"><code>4d25421</code></a>
Drop Go 1.23 support (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2065">#2065</a>)</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/72dccd04fb1b6ec82f33b95fd8e069861a9872e7"><code>72dccd0</code></a>
Fix extra whitespace parsing in HTTP request lines to prevent cache
poisoning...</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/8c7d2bc051dfc5d5d3d0c847a293622aad42641b"><code>8c7d2bc</code></a>
chore(deps): bump actions/setup-go from 5 to 6 (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2060">#2060</a>)</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/28b7880b32812a37f8272b2153d07b2df16e821e"><code>28b7880</code></a>
server: refactor to use atomic type (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2058">#2058</a>)</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/68d21ed0086a8a7794274a58c5284ab9cac45cdd"><code>68d21ed</code></a>
docs: add fasthttp-auth to related projects section (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2057">#2057</a>)</li>
<li><a
href="https://github.com/valyala/fasthttp/commit/7ad0219d24bb082dca5e70e2bd221fe3dc5e4fb5"><code>7ad0219</code></a>
chore(deps): bump securego/gosec from 2.22.7 to 2.22.8 (<a
href="https://redirect.github.com/valyala/fasthttp/issues/2056">#2056</a>)</li>
<li>See full diff in <a
href="https://github.com/valyala/fasthttp/compare/v1.65.0...v1.66.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/valyala/fasthttp&package-manager=go_modules&previous-version=1.65.0&new-version=1.66.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 12:22:56 +00:00
dependabot[bot] 3c059c7220 chore: bump the x group with 3 updates (#19820)
Bumps the x group with 3 updates:
[golang.org/x/crypto](https://github.com/golang/crypto),
[golang.org/x/net](https://github.com/golang/net) and
[golang.org/x/tools](https://github.com/golang/tools).

Updates `golang.org/x/crypto` from 0.41.0 to 0.42.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/crypto/commit/5307a0ce6db8057c8d7c4378dc4bd715b4985ba1"><code>5307a0c</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/crypto/commit/9d779377cff7ff1f58520cc044fb90b10ddfc561"><code>9d77937</code></a>
acme: include order problem in OrderError</li>
<li><a
href="https://github.com/golang/crypto/commit/8f580defa01dec23898d3cd27f6369cdcc62f71f"><code>8f580de</code></a>
ssh: remove Go 1.24 build tag for ML-KEM kex</li>
<li><a
href="https://github.com/golang/crypto/commit/a4d1237429d6056ef197b0b911b8b9d7dca8ecf6"><code>a4d1237</code></a>
ssh/knownhosts: improve IPv6 support in Normalize</li>
<li><a
href="https://github.com/golang/crypto/commit/b8d8dae13d7dda8706ca2ab98934ad404aacae22"><code>b8d8dae</code></a>
curve25519: include potential fips140=only error in panic message</li>
<li><a
href="https://github.com/golang/crypto/commit/f5a2eabcab987dc84f30d5479ed5c5605b5de634"><code>f5a2eab</code></a>
ssh: use curve25519.X25519 instead of curve25519.ScalarMult</li>
<li><a
href="https://github.com/golang/crypto/commit/44ecf3af9978b32529ce689a6964bd557c79aa1c"><code>44ecf3a</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/crypto/compare/v0.41.0...v0.42.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/net` from 0.43.0 to 0.44.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/net/commit/3b23d576ea72235a3fef8f157eb5ab76e65854a8"><code>3b23d57</code></a>
http2: fix race condition when disabling goroutine debugging for one
test</li>
<li><a
href="https://github.com/golang/net/commit/87410502ff26318c9bd1be1c9a09e0a681774dcf"><code>8741050</code></a>
http2: simplify TestServer_Push_RejectAfterGoAway</li>
<li><a
href="https://github.com/golang/net/commit/96e405cac15ef9a113f2b30d3f3f8a216234f09c"><code>96e405c</code></a>
http2: modernize TestTransportRoundtripCloseOnWriteError</li>
<li><a
href="https://github.com/golang/net/commit/6dc6880bcd7edc82184626a363d26d65bc55c195"><code>6dc6880</code></a>
http2: simplify ClientConn Close and Shutdown tests</li>
<li><a
href="https://github.com/golang/net/commit/4e2915b652483bfc9831c080e40c679e42616f03"><code>4e2915b</code></a>
http2: modernize TestTransportAllocationsAfterResponseBodyClose</li>
<li><a
href="https://github.com/golang/net/commit/30b0e78859f03d66b2e275870d5d37455d2f0fb0"><code>30b0e78</code></a>
http2: modernize TestRoundTripDoesntConsumeRequestBodyEarly</li>
<li><a
href="https://github.com/golang/net/commit/b9acd777f1852925992bdeaa623de8cc86efa4eb"><code>b9acd77</code></a>
http2: speed up TestTransportFlowControl</li>
<li><a
href="https://github.com/golang/net/commit/5153f4db3143a3be6ce383b512ba7a4c0f935211"><code>5153f4d</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/net/commit/9338bdd9eeddfeab331342288acfef2293474490"><code>9338bdd</code></a>
http2: speed up TestTransportHandlerBodyClose</li>
<li><a
href="https://github.com/golang/net/commit/6b200364a6614a5cf8d360d854e998c1dc739f24"><code>6b20036</code></a>
http2: add synchronous handler support to serverTester</li>
<li>Additional commits viewable in <a
href="https://github.com/golang/net/compare/v0.43.0...v0.44.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/tools` from 0.36.0 to 0.37.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/tools/commit/d49da96b802368c7325ac3b662a238c09a0615b9"><code>d49da96</code></a>
go/analysis/internal/checker: fix panic due to nil Pass</li>
<li><a
href="https://github.com/golang/tools/commit/9da4f7b44800964cb753824472b25a0487f06b15"><code>9da4f7b</code></a>
gopls/internal/test/integration/completion: make test go1.26-robust</li>
<li><a
href="https://github.com/golang/tools/commit/4409ea71cc4b4233548ec108ff8b9b4b59644511"><code>4409ea7</code></a>
gopls/internal/server: add telemetry for Zed, Helix clientInfo</li>
<li><a
href="https://github.com/golang/tools/commit/75023ae58653333dcc3fc240af01833d83a72b7f"><code>75023ae</code></a>
go/ast/inspector: update outdated documentation</li>
<li><a
href="https://github.com/golang/tools/commit/785cd66cb76e91f9c504768594c5f4e422dd8e7b"><code>785cd66</code></a>
gopls/internal/test/integration/completion: skip TestFuzzFunc
pre-go1.25</li>
<li><a
href="https://github.com/golang/tools/commit/ac2e4a594c7ddc3eeddeddddd3cbef3c22e2b6b2"><code>ac2e4a5</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/tools/commit/bc9374dde117ebc9276c545225c3cc1a116a8418"><code>bc9374d</code></a>
go/callgraph/vta: remove unnecessary use of unsafe</li>
<li><a
href="https://github.com/golang/tools/commit/12d7e152453c767e46faa9b7ab3283600b627bf4"><code>12d7e15</code></a>
gopls/internal/analysis/modernize: add test of no gopls imports</li>
<li><a
href="https://github.com/golang/tools/commit/263a7694059cc37e3789d5ce4a4379adc588f46c"><code>263a769</code></a>
gopls/internal/mcp: use official go mcp sdk</li>
<li><a
href="https://github.com/golang/tools/commit/e548c709d4e162c0c771250e46eb9ea13473c1ef"><code>e548c70</code></a>
gopls/internal/analysis/modernize: fix bad edit in var ( ... ) decl</li>
<li>Additional commits viewable in <a
href="https://github.com/golang/tools/compare/v0.36.0...v0.37.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 12:20:03 +00:00
dependabot[bot] 0cf1fed600 chore: bump google.golang.org/api from 0.248.0 to 0.249.0 (#19818)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.248.0 to 0.249.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.249.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.248.0...v0.249.0">0.249.0</a>
(2025-09-08)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3281">#3281</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/c03d56bd807f14ef4612df738ba9d3f1f6d31d1d">c03d56b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3283">#3283</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/4d1d33689712d397ab582ee046fc51c94c080e04">4d1d336</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3284">#3284</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/d2b41b6ebb0b352d79d2ea64f89579ebbc9f96c7">d2b41b6</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3285">#3285</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/bcde9430e3e68f4ec7bebbbf54742774b9f7cfa7">bcde943</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3286">#3286</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/27530ddc5176ffbbd8a418f532878b495cb4fdf5">27530dd</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3288">#3288</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/240e54a47415519f419f9c35a7699205fac62dd1">240e54a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3289">#3289</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2bd4a72fae45cc4cb10b77ad1b1c2d5d8d99a650">2bd4a72</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3291">#3291</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/d09c7d319a1191f231043622df5be2f4dba1f629">d09c7d3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3292">#3292</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/7b2235b3826bd01cc4c0f47ab9828692875029c6">7b2235b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3293">#3293</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2e60cd5bcc4bafa45fa699a7753db273568af68a">2e60cd5</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3294">#3294</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/162aba409d969077157d3ecec3f7578af15a62b4">162aba4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3295">#3295</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e297a428606910b17750a7d09aaec92c2513b415">e297a42</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3296">#3296</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/f98f8357e2480c0e96acfa004f30c731988e4cbe">f98f835</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3297">#3297</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ababe603c1ec78c9becb3daf8cde2b4fbd108fde">ababe60</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3299">#3299</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ad70b6eee9be539868c5b8004a7be4501fdd7404">ad70b6e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3300">#3300</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/4fcd5bbd71a8e09cc05c17775fffc365d00dbcb5">4fcd5bb</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3301">#3301</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6db056121da007d4c80b1eacdff5cf23e60fa6a2">6db0561</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3302">#3302</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/79b251ae0fcbe4f34730ace80b89f43b529c192b">79b251a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3303">#3303</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e93c8a870b9a43ac439c438d52df964da732df02">e93c8a8</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3305">#3305</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/1ca0330e52f234f3f9e9d65c431e0f9e7a2e93a0">1ca0330</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.248.0...v0.249.0">0.249.0</a>
(2025-09-08)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3281">#3281</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/c03d56bd807f14ef4612df738ba9d3f1f6d31d1d">c03d56b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3283">#3283</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/4d1d33689712d397ab582ee046fc51c94c080e04">4d1d336</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3284">#3284</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/d2b41b6ebb0b352d79d2ea64f89579ebbc9f96c7">d2b41b6</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3285">#3285</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/bcde9430e3e68f4ec7bebbbf54742774b9f7cfa7">bcde943</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3286">#3286</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/27530ddc5176ffbbd8a418f532878b495cb4fdf5">27530dd</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3288">#3288</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/240e54a47415519f419f9c35a7699205fac62dd1">240e54a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3289">#3289</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2bd4a72fae45cc4cb10b77ad1b1c2d5d8d99a650">2bd4a72</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3291">#3291</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/d09c7d319a1191f231043622df5be2f4dba1f629">d09c7d3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3292">#3292</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/7b2235b3826bd01cc4c0f47ab9828692875029c6">7b2235b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3293">#3293</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/2e60cd5bcc4bafa45fa699a7753db273568af68a">2e60cd5</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3294">#3294</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/162aba409d969077157d3ecec3f7578af15a62b4">162aba4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3295">#3295</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e297a428606910b17750a7d09aaec92c2513b415">e297a42</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3296">#3296</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/f98f8357e2480c0e96acfa004f30c731988e4cbe">f98f835</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3297">#3297</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ababe603c1ec78c9becb3daf8cde2b4fbd108fde">ababe60</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3299">#3299</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/ad70b6eee9be539868c5b8004a7be4501fdd7404">ad70b6e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3300">#3300</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/4fcd5bbd71a8e09cc05c17775fffc365d00dbcb5">4fcd5bb</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3301">#3301</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/6db056121da007d4c80b1eacdff5cf23e60fa6a2">6db0561</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3302">#3302</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/79b251ae0fcbe4f34730ace80b89f43b529c192b">79b251a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3303">#3303</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/e93c8a870b9a43ac439c438d52df964da732df02">e93c8a8</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3305">#3305</a>)
(<a
href="https://github.com/googleapis/google-api-go-client/commit/1ca0330e52f234f3f9e9d65c431e0f9e7a2e93a0">1ca0330</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/d63ede448babcddf0d4b854b3beeef47d2246144"><code>d63ede4</code></a>
chore(main): release 0.249.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3282">#3282</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/1ca0330e52f234f3f9e9d65c431e0f9e7a2e93a0"><code>1ca0330</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3305">#3305</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/e93c8a870b9a43ac439c438d52df964da732df02"><code>e93c8a8</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3303">#3303</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/79b251ae0fcbe4f34730ace80b89f43b529c192b"><code>79b251a</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3302">#3302</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/6db056121da007d4c80b1eacdff5cf23e60fa6a2"><code>6db0561</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3301">#3301</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/4fcd5bbd71a8e09cc05c17775fffc365d00dbcb5"><code>4fcd5bb</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3300">#3300</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/ad70b6eee9be539868c5b8004a7be4501fdd7404"><code>ad70b6e</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3299">#3299</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/ababe603c1ec78c9becb3daf8cde2b4fbd108fde"><code>ababe60</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3297">#3297</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/f98f8357e2480c0e96acfa004f30c731988e4cbe"><code>f98f835</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3296">#3296</a>)</li>
<li><a
href="https://github.com/googleapis/google-api-go-client/commit/e297a428606910b17750a7d09aaec92c2513b415"><code>e297a42</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/3295">#3295</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.248.0...v0.249.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.248.0&new-version=0.249.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 11:56:32 +00:00
dependabot[bot] 38787ba9aa chore: bump github.com/brianvoe/gofakeit/v7 from 7.5.1 to 7.6.0 (#19819)
Bumps
[github.com/brianvoe/gofakeit/v7](https://github.com/brianvoe/gofakeit)
from 7.5.1 to 7.6.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/2122822d4d9aeab87b73b6eb01d4e0ade92372ae"><code>2122822</code></a>
datetime - added time and time range</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/f98f33cefbed28045ab9752c6a96effc11809de0"><code>f98f33c</code></a>
category - change time to datetime</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/7e975bcbce25b0d1b5a62543fba823d8e68ae91e"><code>7e975bc</code></a>
misc - len and nil check cleanup</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/73dfff01158d417a79e94419af885e349aa629f6"><code>73dfff0</code></a>
datetime - file rename</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/3ee1f294eb627dda54a30824049de484034331a7"><code>3ee1f29</code></a>
string - fix randomstring lookup output</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/fdde0cc092b9a004ba08beb763f0c541d7a71c85"><code>fdde0cc</code></a>
payment - cvv alias/keyword fix</li>
<li>See full diff in <a
href="https://github.com/brianvoe/gofakeit/compare/v7.5.1...v7.6.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/brianvoe/gofakeit/v7&package-manager=go_modules&previous-version=7.5.1&new-version=7.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 11:56:05 +00:00
dependabot[bot] 36f2d10bdc chore: bump github.com/coder/terraform-provider-coder/v2 from 2.10.0 to 2.11.0 (#19817)
Bumps
[github.com/coder/terraform-provider-coder/v2](https://github.com/coder/terraform-provider-coder)
from 2.10.0 to 2.11.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/coder/terraform-provider-coder/releases">github.com/coder/terraform-provider-coder/v2's
releases</a>.</em></p>
<blockquote>
<h2>v2.11.0</h2>
<h2>What's Changed</h2>
<ul>
<li>ci: fix go version to that defined in go.mod by <a
href="https://github.com/johnstcn"><code>@​johnstcn</code></a> in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/438">coder/terraform-provider-coder#438</a></li>
<li>build(deps): Bump actions/setup-go from 5 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/437">coder/terraform-provider-coder#437</a></li>
<li>build(deps): Bump golang.org/x/mod from 0.27.0 to 0.28.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/440">coder/terraform-provider-coder#440</a></li>
<li>feat: add tooltip field to workspace app by <a
href="https://github.com/rafrdz"><code>@​rafrdz</code></a> in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/435">coder/terraform-provider-coder#435</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/rafrdz"><code>@​rafrdz</code></a> made
their first contribution in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/435">coder/terraform-provider-coder#435</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/coder/terraform-provider-coder/compare/v2.10.1...v2.11.0">https://github.com/coder/terraform-provider-coder/compare/v2.10.1...v2.11.0</a></p>
<h2>v2.10.1</h2>
<h2>What's Changed</h2>
<ul>
<li>docs: add Premium license warning for external_agent resource by <a
href="https://github.com/blink-so"><code>@​blink-so</code></a> in <a
href="https://redirect.github.com/coder/terraform-provider-coder/pull/434">coder/terraform-provider-coder#434</a></li>
</ul>
<p>This patch release adds a warning to the external_agent resource
documentation to inform users that external agents require a Premium
Coder license.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/coder/terraform-provider-coder/compare/v2.10.0...v2.10.1">https://github.com/coder/terraform-provider-coder/compare/v2.10.0...v2.10.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/bb2167ecc44506469c77380367ad1c3bc08f475e"><code>bb2167e</code></a>
feat: add tooltip field to workspace app (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/435">#435</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/c8bc37a5caa446d80c8d3ea966a702d90771d71e"><code>c8bc37a</code></a>
build(deps): Bump golang.org/x/mod from 0.27.0 to 0.28.0 (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/440">#440</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/e26aeb89f14bf748ddc5cc78620c63521a8d14ef"><code>e26aeb8</code></a>
build(deps): Bump actions/setup-go from 5 to 6 (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/437">#437</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/ac2b839013500e4b3329a8bb8eab0413a182c3d5"><code>ac2b839</code></a>
ci: fix go version to that defined in go.mod (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/438">#438</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/8c9d011c4c695401b0f6e7edccb3d1bc66859d38"><code>8c9d011</code></a>
docs: add Premium license warning for external_agent resource (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/434">#434</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/42c19bc17d1298e7749fd9a429eb5ed013f53bf3"><code>42c19bc</code></a>
build(deps): Bump golang.org/x/mod from 0.25.0 to 0.27.0 (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/425">#425</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/06cddcefa93d4644a79136c3ad2d1065d55a0850"><code>06cddce</code></a>
build(deps): Bump github.com/stretchr/testify from 1.10.0 to 1.11.0 (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/429">#429</a>)</li>
<li><a
href="https://github.com/coder/terraform-provider-coder/commit/b358b645ad48f3a2f51f93ed9aad3e660942caa8"><code>b358b64</code></a>
build(deps): Bump actions/checkout from 4 to 5 (<a
href="https://redirect.github.com/coder/terraform-provider-coder/issues/426">#426</a>)</li>
<li>See full diff in <a
href="https://github.com/coder/terraform-provider-coder/compare/v2.10.0...v2.11.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/coder/terraform-provider-coder/v2&package-manager=go_modules&previous-version=2.10.0&new-version=2.11.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 11:54:58 +00:00
Kacper Sawicki 7c8f8f449b feat(site): display warning messages when wildcard is not configured (#19660)
Closes https://github.com/coder/coder/issues/19606

This PR adds warnings to alert uses when workspace applications with
subdomain access won't work due to missing wildcard access url
configuration.

### Workspace Page warning

This warning is shown when wildcard hostname is not configured and agent
has subdomain applications.

User:
<img width="1230" height="488" alt="image"
src="https://github.com/user-attachments/assets/66ead7b1-690c-4195-a560-2c5e9ecdf521"
/>

Administrator:

<img width="1231" height="492" alt="image"
src="https://github.com/user-attachments/assets/96f7d6fc-a993-4bbb-bd97-80a750caf3fa"
/>


### Template Editor Warning:

This warning is shown in the output panel (after the user clicks build)
when wildcard hostname is not configured and template contains coder_app
resource and subdomain is set to true.

<img width="1446" height="186" alt="image"
src="https://github.com/user-attachments/assets/ba8e0c8e-1b7f-4722-8cee-24be21b8fc69"
/>
2025-09-15 10:26:48 +02:00
Thomas Kosiewski d238480c7a fix: trim whitespace from API tokens (#19814) 2025-09-15 10:02:10 +02:00
Ethan 6a9b896f5b fix!: use client ip when creating connection logs for workspace proxied app accesses (#19788)
Breaking API Change: 
> The presence of the `ip` field on `codersdk.ConnectionLog` cannot be
guaranteed, and so the field has been made optional. It may be omitted
on API responses.

When running a scaletest, I noticed logs of the form:
```
2025-09-12 06:34:10.924 [erro]  coderd.workspaceapps: upsert connection log failed  trace=0xa17580  span=0xa17620  workspace_id=81b937d7-5777-4df5-b5cb-80241f30326f  agent_id=78b2ff6d-b4a6-4a4e-88a7-283e05455a88  app_id=00000000-0000-0000-0000-000000000000  user_id=00000000-0000-0000-0000-000000000000  user_agent=""  app_slug_or_port=terminal  status_code=404  request_id=67f03cf8-9523-444a-97bc-90de080a54c8 ...
    error= 1 error occurred:
           	* pq: null value in column "ip" of relation "connection_logs" violates not-null constraint
```

to ensure logs are never omitted from the connection log due to a
missing IP again (i.e. I'm not sure if we can always rely on a valid,
parseable, IP from `(http.Request).RemoteAddr`), I've removed the `NOT
NULL` constraint on `ip` on `connection_logs`, and made `ip` on the API
response optional.


The specific cause for these null IPs was the
`/workspaceproxies/me/issue-signed-app-token [post]` endpoint
constructing it's own `http.Request` without a `RemoteAddr` set, and
then passing that to the token issuer.

To solve this, we'll have workspace proxies send the real IP of the
client when calling `/workspaceproxies/me/issue-signed-app-token [post]`
via the header `Coder-Workspace-Proxy-Real-IP`.
2025-09-15 12:30:17 +10:00
Thomas Kosiewski 088d14933c feat: ensure OAuth2 refresh tokens outlive access tokens (#19769) 2025-09-13 08:57:26 +02:00
Asher be7aa58075 feat: add coder_workspace_ls MCP tool (#19652) 2025-09-12 15:57:15 -08:00
Asher 30330abaea feat: add coder_workspace_edit_file MCP tool (#19629) 2025-09-12 15:36:14 -08:00
david-fraley 1e2b66fb20 docs: update Get Started Page to Include Tasks (#19752) 2025-09-12 22:22:28 +00:00
Kacper Sawicki f9f0ebb472 feat(docs): add wildcard access url documentation page (#19713)
Closes #19607 

This pull request adds a new guide about wildcard access URLs.

---------

Co-authored-by: Dean Sheather <dean@deansheather.com>
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-12 13:34:38 -05:00
Brett Kolodny 854f3c0187 feat: add workspaces/acl [delete] endpoint (#19772)
Closes
[coder/internal#971](https://github.com/coder/internal/issues/971)
2025-09-12 12:21:01 -04:00
ケイラ 8e79dbb16f fix: prevent unruly stacking contexts from breaking scrolling (#19785) 2025-09-12 10:11:39 -06:00
Michael Suchacz 336e62bc37 fix: deflake BackedWriter tests (#19802) 2025-09-12 14:00:08 +00:00
Asher 6d39077087 chore: log error when checking if codersdk.Err (#19784) 2025-09-11 13:17:09 -08:00
Brett Kolodny 8d5c566a98 feat: add sharing remove command to the CLI (#19767)
Closes
[coder/internal#861](https://github.com/coder/internal/issues/861)
2025-09-11 16:22:25 -04:00
Asher d5a02d570f feat: add coder_workspace_write_file MCP tool (#19591) 2025-09-11 12:17:15 -08:00
Susana Ferreira eec6c8c120 feat: support custom notifications (#19751)
## Description

Adds support for sending an ad‑hoc custom notification to the
authenticated user via API and CLI. This is useful for surfacing the
result of scripts or long‑running tasks. Notifications are delivered
through the configured method and the dashboard Inbox, respecting
existing preferences and delivery settings.

## Changes

* New notification template: “Custom Notification” with a label for a
custom title and a custom message.
* New API endpoint: `POST /api/v2/notifications/custom` to send a custom
notification to the requesting user.
* New API endpoint: `GET /notifications/templates/custom` to get custom
notification template.
* New CLI subcommand: `coder notifications custom <title> <message>` to
send a custom notification to the requesting user.
* Documentation updates: Add a “Custom notifications” section under
Administration > Monitoring > Notifications, including instructions on
sending custom notifications and examples of when to use them.

Closes: https://github.com/coder/coder/issues/19611
2025-09-11 15:08:57 +02:00
Michael Suchacz 4c98decfb7 chore: add backed reader, writer and pipe implementation (#19147)
Relates to: https://github.com/coder/coder/issues/18101

This PR introduces a new `backedpipe` package that provides reliable
bidirectional byte streams over unreliable network connections. The
implementation includes:

- `BackedPipe`: Orchestrates a reader and writer to provide transparent
reconnection and data replay
- `BackedReader`: Handles reading with automatic reconnection, blocking
reads when disconnected
- `BackedWriter`: Maintains a ring buffer of recent writes for replay
during reconnection
- `RingBuffer`: Efficient circular buffer implementation for storing
data

The package enables resilient connections by tracking sequence numbers
and replaying missed data after reconnection. It handles connection
failures gracefully, automatically reconnecting and resuming data
transfer from the appropriate point.
2025-09-11 14:05:14 +02:00
Rafael Rodriguez e53bc247e9 feat: add tooltip field to workspace app that renders as markdown (#19651)
In this pull request we're adding an optional `tooltip` field. The
`tooltip` field is a string field (with markdown support) that will be
used to display tooltips on hover over app buttons in a workspace
dashboard.

Tooltip screenshot

<img width="816" height="275" alt="Screenshot 2025-08-29 at 4 11 56 PM"
src="https://github.com/user-attachments/assets/52c736a1-f632-465b-89a0-35ca99bd367b"
/>

Tooltip video


https://github.com/user-attachments/assets/21806337-accc-4acf-b8c6-450c031d98f1

Issue: https://github.com/coder/coder/issues/18431
Related provider PR:
https://github.com/coder/terraform-provider-coder/pull/435

### Changes

- Added migration to add `tooltip` column to `workspace_apps` table
- Updated queries to get/set the new `tooltip` column
- Updated frontend to render tooltip as markdown (primary tool tip takes
precedence over template tooltip)

### Testing

- Added storybook test for `Applink` markdown rendering
2025-09-10 11:01:54 -05:00
Danielle Maywood 4cd0ada0bb chore(coderd/database): add tasks data model (#19749)
Part of https://github.com/coder/internal/issues/948

Adds the initial part of the tasks data model as per the RFC.
2025-09-10 10:01:50 +01:00
Asher 4bf63b4068 feat: add coder_workspace_read_file MCP tool (#19562)
Follows similarly to the bash tool (and some code to connect to an agent
was extracted from it).

There are two main parts: a new agent endpoint, and then a new MCP tool
that consumes that endpoint.
2025-09-09 15:12:24 -08:00
david-fraley f402ec99eb docs: fix typo in Coder Desktop Guide (#19742)
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.

Fix a quick typo that was introduced in
https://github.com/coder/coder/commit/8f72538ab7cbcdf23680f4916121ceca0f325e97

---------

Co-authored-by: Prebuilds Owner <prebuilds@system>
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com>
2025-09-09 15:05:09 +00:00
Kacper Sawicki 3074547322 perf(enterprise): remove expensive GetWorkspaces query from entitlements (#19747)
Closes: https://github.com/coder/internal/issues/964

This PR addresses the significant database load issue where the
`GetWorkspaces` query was causing performance problems in the license
entitlements code.
2025-09-09 15:46:11 +02:00
Steven Masley d527f91f47 chore: update rego policy to respect user and organisation scopes (#19741)
Prior to this change, user and org scopes were always rejected
2025-09-09 05:50:08 -05:00
Danielle Maywood f3b152bb06 feat(coderd): allow specifying a name for a task (#19745)
Relates to https://github.com/coder/internal/issues/955

Add the ability to the tasks endpoint to give a task a custom name.
2025-09-09 11:08:28 +01:00
Kacper Sawicki b4e4173347 test: improve workspace build job completion logging (#19740)
Closes https://github.com/coder/internal/issues/935

This PR enhances the AwaitWorkspaceBuildJobCompleted func in coderdtest
pkg to provide better visibility into test failures and debugging
information.
2025-09-09 08:38:32 +02:00
Rafael Rodriguez 1677a30a1d fix: add support for spaces in search & enable searching by display name in templates (#19552)
## Summary

In this pull request we're updating search to support queries with
spaces in addition to the `field:value` pattern that is currently
supported.

Additionally templates search now defaults to `display_name` (since
`display_name` is optional the search will fallback to `name`) when
searching without the `field:value` pattern

Closes: https://github.com/coder/coder/issues/14384

### Downsides with searching on `name` and `display_name`

Because the `name` field cannot include spaces, we end up in a situation
where including a space in the query will result in no results since the
query searches on both `name` AND `display_name`. In the following
example, we can see the results of searching by both `name` and
`display_name` on these templates:

| Name | Display Name |
| ------ | ------------- |
| docker | Docker Template |
| faketemplate | A Fake Template |
| azure | Fake Azure Template |
| anotherfake | Another Fake Template |
| azurefake | Another Fake Fake Azure Template |



https://github.com/user-attachments/assets/b0e0793e-e77d-46bc-9a42-d7cf4f8bd910

### Proposal: Search on `display_name` by default and allow for `name`
using the `field:value` pattern

If we remove `name` from the default template search, we're now able to
search with spaces on template `display_names`. Since `display_names`
are what users see in the templates list they might expect the search to
work this way.

Below is an example of `name` being removed from the default template
search.


https://github.com/user-attachments/assets/9aba5911-4960-4384-befb-08ea1acaa3ab

With this approach users would still be able to search on template names
by specifying `exact_name:foo`.

### Testing

Added additional test cases to ensure spaces were handled as expected in
combination with `field:value` patterns.
2025-09-08 17:13:27 -05:00
Andrew Aquino ff18499cb0 refactor: replace Popover with Tooltip in HelpTooltip (#19635)
for #19397 

Currently there are 24 files that import bindings from the deprecated
`Popover` component. One of those is `HelpTooltip`, which is
instantiated in 24 other files. After this PR, the remaining files that
import the deprecated `Popover` should be able to be migrated in just
1-2 more PRs. 🤞🏽

I opted for `Tooltip` as a replacement because it's triggered on hover,
unlike our new `Popover` which is triggered on click.
2025-09-08 10:59:09 -07:00
blink-so[bot] d7d69d1ce9 fix: add xmlns attribute to amazon-q.svg for proper rendering (#19738)
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2025-09-08 21:01:10 +05:00
Kacper Sawicki 776231d025 fix(coderd): add blocking GetProvisionerJobByIDWithLock for workspace build cancellation (#19737)
Closes https://github.com/coder/internal/issues/885

Adds a new database method GetProvisionerJobByIDWithLock that uses FOR
UPDATE without SKIP LOCKED to fix workspace build cancellation returning
500 errors when jobs are locked.
2025-09-08 15:40:14 +02:00
Brett Kolodny 065c7c3d5d feat: add sharing show command to the CLI (#19707)
Closes https://github.com/coder/internal/issues/860
2025-09-08 09:30:08 -04:00
Danielle Maywood 9f66395931 chore(cli): reduce clutter from exp tasks list command (#19727)
Fixes https://github.com/coder/coder/issues/19623

- Stop showing the long ID of each task by default.
- Add new `--quiet` flag to only show IDs.
2025-09-08 13:23:18 +01:00
Thomas Kosiewski 2701d5588e fix: support path parameters in OAuth2 metadata endpoints (#19729)
Update OAuth2 metadata endpoint routes to support path suffixes

This PR updates the OAuth2 metadata endpoint routes to include a wildcard character (*) at the end of the paths. This change allows the endpoints to match requests with path suffixes, making our OAuth2 discovery implementation more flexible and compliant with the relevant RFCs.

The updated routes are:
- `/.well-known/oauth-authorization-server*` for RFC 8414 discovery
- `/.well-known/oauth-protected-resource*` for RFC 9728 discovery
2025-09-08 14:21:57 +02:00
dependabot[bot] 6e33c38777 chore: bump github.com/gohugoio/hugo from 0.148.1 to 0.149.1 (#19734)
Bumps [github.com/gohugoio/hugo](https://github.com/gohugoio/hugo) from
0.148.1 to 0.149.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gohugoio/hugo/releases">github.com/gohugoio/hugo's
releases</a>.</em></p>
<blockquote>
<h2>v0.149.1</h2>
<p>The main motivation behind this release is the <a
href="https://groups.google.com/g/golang-announce/c/PtW9VW21NPs/m/DJhMQ-m5AQAJ">Go
1.25.1</a> upgrade, which comes with a <a
href="https://redirect.github.com/golang/go/issues/75054">security
fix</a>. Hugo does not use the feature in question, but we understand
that many Hugo users like to have a clean security report.</p>
<h2>Note</h2>
<p>Note that CSS minification now targets CSS3, removing certain
optimizations that were specific to CSS2.</p>
<h2>What's Changed</h2>
<ul>
<li>Remove noindex meta tag from alias.html 25c0f2408 <a
href="https://github.com/lzap"><code>@​lzap</code></a></li>
<li>Fix nilpointer on ToC heading 4f2d2b2cc <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/11843">#11843</a></li>
<li>tpl/collections: Require collections.D args to be ints b8eb45c9d <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13952">#13952</a></li>
<li>Upgrade to Go 1.25.1 1d90afff1 <a
href="https://github.com/bep"><code>@​bep</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13960">#13960</a></li>
<li>Fix config env handling for some slice options e751afa9b <a
href="https://github.com/PikachuTW"><code>@​PikachuTW</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/13950">#13950</a></li>
<li>minifiers: Update deprecation handling a09b8a60e <a
href="https://github.com/jmooring"><code>@​jmooring</code></a> <a
href="https://redirect.github.com/gohugoio/hugo/issues/11893">#11893</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/13947">#13947</a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/13948">#13948</a></li>
</ul>
<h2>v0.149.0</h2>
<blockquote>
<p>[!NOTE]<br />
If running on Netlify, make sure you have configured your build with
their latest build image, see <a
href="https://redirect.github.com/gohugoio/hugo/issues/13942#issuecomment-3228959652">this
issue</a>.</p>
</blockquote>
<p>Hugo <code>v0.149.0</code> comes with bug fixes and a set of new
features/improvements, notably:</p>
<ul>
<li>We now build with the recently released <a
href="https://tip.golang.org/doc/go1.25">Go 1.25</a></li>
<li>A new <a
href="https://gohugo.io/functions/collections/d/">collections.D</a>
template function that generates random sequences of integers using J.
S. Vitter’s Method D, by some called <a
href="https://getkerf.wordpress.com/2016/03/30/the-best-algorithm-no-one-knows-about/">The
Best Algorithm No One Knows About</a>.</li>
<li>Two new <a
href="https://gohugo.io/configuration/permalinks/#tokens">permalinks
tokens</a>, <code>:sectionslug</code> and <code>:sectionslugs</code>,
especially useful in multilingual Hugo projects.</li>
<li>A new <code>--omitClassComments</code> flag on <code>hugo gen
chromastyles</code></li>
<li>Several improvements to how ToC from Markdown gets rendered, see <a
href="https://redirect.github.com/gohugoio/hugo/issues/13401">#13401</a>
and <a
href="https://redirect.github.com/gohugoio/hugo/issues/12605">#12605</a>.</li>
<li>A new <code>format</code> option in <a
href="https://gohugo.io/functions/transform/unmarshal/">transform.Unmarshal</a></li>
</ul>
<h2>Note</h2>
<ul>
<li>Remove test with deprecated path usage 80e973ea5 <a
href="https://github.com/bep"><code>@​bep</code></a></li>
</ul>
<h2>Bug fixes</h2>
<ul>
<li>create: Fix new content command with future dates bb4e66cd7 <a
href="https://github.com/justuswilhelm"><code>@​justuswilhelm</code></a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/12599">#12599</a></li>
<li>Fix server rebuild when adding a new leaf bundle with resources in
one go 13b43e611 <a href="https://github.com/bep"><code>@​bep</code></a>
<a
href="https://redirect.github.com/gohugoio/hugo/issues/13925">#13925</a></li>
<li>Fix rebuild when deleting a content adapter file 87e100e61 <a
href="https://github.com/bep"><code>@​bep</code></a></li>
</ul>
<h2>Improvements</h2>
<ul>
<li>tpl/collections: Add an integration test for collections.D 84b512391
<a href="https://github.com/bep"><code>@​bep</code></a></li>
<li>tpl/collections: Add collections.D using Vitter's Method D for
sequential random sampling 1ba80874e <a
href="https://github.com/bep"><code>@​bep</code></a></li>
<li>commands: Deprecate --omitEmpty on chromastyles command 61ec7a20a <a
href="https://github.com/bep"><code>@​bep</code></a></li>
<li>commands: Add --omitClassComments to the chromastyles command
c289fcaaa <a href="https://github.com/bep"><code>@​bep</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/gohugoio/hugo/commit/57a784e027a264790ddf36c89b62200525a4561b"><code>57a784e</code></a>
releaser: Bump versions for release of 0.149.1</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/25c0f2408ae777b64f373c1ca30b11c81e499900"><code>25c0f24</code></a>
Remove noindex meta tag from alias.html</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/4f2d2b2cc48d23bef9cc19e8c88a454560b2306a"><code>4f2d2b2</code></a>
Fix nilpointer on ToC heading</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/b8eb45c9dfe6572b80adcdb7bd0dcf55786dcf3d"><code>b8eb45c</code></a>
tpl/collections: Require collections.D args to be ints</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/1d90afff1be34c3cbdd7d9da41635a3f054af982"><code>1d90aff</code></a>
Upgrade to Go 1.25.1</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/e751afa9bd1207a9428cc8e3580f20d5af70b68b"><code>e751afa</code></a>
Fix config env handling for some slice options</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/a09b8a60eb9b9767b3987e9fba895bfe4ad7db58"><code>a09b8a6</code></a>
minifiers: Update deprecation handling</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/0071b47b8be7fed03786182f797ca1cef5185f54"><code>0071b47</code></a>
Update README.md</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/70d62993eef4124873d339ded8fe612cc69f0238"><code>70d6299</code></a>
releaser: Prepare repository for 0.150.0-DEV</li>
<li><a
href="https://github.com/gohugoio/hugo/commit/66240338f1b908ca3b163384c8229943e74eb290"><code>6624033</code></a>
releaser: Bump versions for release of 0.149.0</li>
<li>Additional commits viewable in <a
href="https://github.com/gohugoio/hugo/compare/v0.148.1...v0.149.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/gohugoio/hugo&package-manager=go_modules&previous-version=0.148.1&new-version=0.149.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 12:05:29 +00:00
Marcin Tojek 5c1a708c73 feat: add default workspace name to Template Embed form (#19688)
Fixes: https://github.com/coder/coder/issues/15798
2025-09-08 13:58:24 +02:00
dependabot[bot] 0b460b8d95 ci: bump the github-actions group across 1 directory with 16 updates (#19736)
Bumps the github-actions group with 16 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `4.3.0` |
`5.0.0` |
| [crate-ci/typos](https://github.com/crate-ci/typos) | `1.35.3` |
`1.36.2` |
| [azure/setup-helm](https://github.com/azure/setup-helm) | `4.3.0` |
`4.3.1` |
| [chromaui/action](https://github.com/chromaui/action) | `13.1.3` |
`13.1.4` |
| [actions/setup-java](https://github.com/actions/setup-java) | `4.7.1`
| `5.0.0` |
|
[google-github-actions/auth](https://github.com/google-github-actions/auth)
| `2.1.12` | `3.0.0` |
|
[google-github-actions/setup-gcloud](https://github.com/google-github-actions/setup-gcloud)
| `2.2.0` | `3.0.1` |
| [actions/attest](https://github.com/actions/attest) | `2.4.0` |
`3.0.0` |
|
[google-github-actions/get-gke-credentials](https://github.com/google-github-actions/get-gke-credentials)
| `2.3.4` | `3.0.0` |
| [actions/github-script](https://github.com/actions/github-script) |
`7.0.1` | `8.0.0` |
| [depot/build-push-action](https://github.com/depot/build-push-action)
| `1.15.0` | `1.16.2` |
|
[tj-actions/changed-files](https://github.com/tj-actions/changed-files)
| `f963b3f3562b00b6d2dd25efc390eb04e51ef6c6` |
`8c14441336bb3d84fd6b7fa83b6d7201c740baf5` |
|
[nixbuild/nix-quick-install-action](https://github.com/nixbuild/nix-quick-install-action)
| `32` | `33` |
| [github/codeql-action](https://github.com/github/codeql-action) |
`3.29.8` | `3.30.1` |
|
[aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action)
| `0.32.0` | `0.33.1` |
| [actions/stale](https://github.com/actions/stale) | `9.1.0` | `10.0.0`
|


Updates `actions/checkout` from 4.3.0 to 5.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
<li>Prepare v5.0.0 release by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2238">actions/checkout#2238</a></li>
</ul>
<h2>⚠️ Minimum Compatible Runner Version</h2>
<p><strong>v2.327.1</strong><br />
<a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<p>Make sure your runner is updated to this version or newer to use this
release.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v4...v5.0.0">https://github.com/actions/checkout/compare/v4...v5.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
<li>README: Suggest <code>user.email</code> to be
<code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkout#1707</a></li>
</ul>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling
<code>sparse-checkout</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkout#1692</a></li>
<li>Add dependabot config by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkout#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkout#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkout#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/checkout/commit/08c6903cd8c0fde910a37f88322edcfb5dd907a8"><code>08c6903</code></a>
Prepare v5.0.0 release (<a
href="https://redirect.github.com/actions/checkout/issues/2238">#2238</a>)</li>
<li><a
href="https://github.com/actions/checkout/commit/9f265659d3bb64ab1440b03b12f4d47a24320917"><code>9f26565</code></a>
Update actions checkout to use node 24 (<a
href="https://redirect.github.com/actions/checkout/issues/2226">#2226</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/08eba0b27e820071cde6df949e0beb9ba4906955...08c6903cd8c0fde910a37f88322edcfb5dd907a8">compare
view</a></li>
</ul>
</details>
<br />

Updates `crate-ci/typos` from 1.35.3 to 1.36.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.36.2</h2>
<h2>[1.36.2] - 2025-09-04</h2>
<h3>Fixes</h3>
<ul>
<li>Fix regression from 1.36.1 when rendering an error for a line with
invalid UTF-8</li>
</ul>
<h2>v1.36.1</h2>
<h2>[1.36.1] - 2025-09-03</h2>
<h3>Fixes</h3>
<ul>
<li>Replaced the error rendering for various quality of life
improvements</li>
</ul>
<h2>v1.36.0</h2>
<h2>[1.36.0] - 2025-09-02</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1345">August
2025</a> changes</li>
</ul>
<h2>v1.35.8</h2>
<h2>[1.35.8] - 2025-09-02</h2>
<h2>v1.35.7</h2>
<h2>[1.35.7] - 2025-08-29</h2>
<h3>Documentation</h3>
<ul>
<li>Expand PyPI metadata</li>
</ul>
<h2>v1.35.6</h2>
<h2>[1.35.6] - 2025-08-28</h2>
<h3>Fixes</h3>
<ul>
<li>Track <code>go.mod</code> as a golang file (regression from
1.13.21)</li>
</ul>
<h2>v1.35.5</h2>
<h2>[1.35.5] - 2025-08-18</h2>
<h3>Fixes</h3>
<ul>
<li>Fix typo in correction to <code>accidently</code></li>
<li>Fix typo in correction to <code>dynamincally</code></li>
<li>Fix typo in correction to <code>interruptability</code></li>
<li>Fix typo in correction to <code>interruptability</code></li>
<li>Fix typo in correction to <code>messager</code></li>
<li>Fix typo in correction to <code>preferables</code></li>
<li>Fix typo in correction to <code>producibles</code></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h1>Change Log</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<p>The format is based on <a href="http://keepachangelog.com/">Keep a
Changelog</a>
and this project adheres to <a href="http://semver.org/">Semantic
Versioning</a>.</p>
<!-- raw HTML omitted -->
<h2>[Unreleased] - ReleaseDate</h2>
<h2>[1.36.2] - 2025-09-04</h2>
<h3>Fixes</h3>
<ul>
<li>Fix regression from 1.36.1 when rendering an error for a line with
invalid UTF-8</li>
</ul>
<h2>[1.36.1] - 2025-09-03</h2>
<h3>Fixes</h3>
<ul>
<li>Replaced the error rendering for various quality of life
improvements</li>
</ul>
<h2>[1.36.0] - 2025-09-02</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1345">August
2025</a> changes</li>
</ul>
<h2>[1.35.8] - 2025-09-02</h2>
<h2>[1.35.7] - 2025-08-29</h2>
<h3>Documentation</h3>
<ul>
<li>Expand PyPI metadata</li>
</ul>
<h2>[1.35.6] - 2025-08-28</h2>
<h3>Fixes</h3>
<ul>
<li>Track <code>go.mod</code> as a golang file (regression from
1.13.21)</li>
</ul>
<h2>[1.35.5] - 2025-08-18</h2>
<h3>Fixes</h3>
<ul>
<li>Fix typo in correction to <code>accidently</code></li>
<li>Fix typo in correction to <code>dynamincally</code></li>
<li>Fix typo in correction to <code>interruptability</code></li>
<li>Fix typo in correction to <code>interruptability</code></li>
<li>Fix typo in correction to <code>messager</code></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/crate-ci/typos/commit/85f62a8a84f939ae994ab3763f01a0296d61a7ee"><code>85f62a8</code></a>
chore: Release</li>
<li><a
href="https://github.com/crate-ci/typos/commit/6f26306a9140281f8f46294118abc2b5cf88a04d"><code>6f26306</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/crate-ci/typos/commit/6bd8b39b57af405b681ed2d7d563f5cdf788388f"><code>6bd8b39</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1374">#1374</a>
from epage/invalid</li>
<li><a
href="https://github.com/crate-ci/typos/commit/f5e19d303814c9192cf68dba44e25b470ef29461"><code>f5e19d3</code></a>
fix(cli): Don't panic with invalid utf-8</li>
<li><a
href="https://github.com/crate-ci/typos/commit/5062775d92cab861376d135b425089eba7bb719d"><code>5062775</code></a>
test(cli): Generalize utf16 tests</li>
<li><a
href="https://github.com/crate-ci/typos/commit/b6297a6a5072df106aa9d94197f5d0533a9730bc"><code>b6297a6</code></a>
chore: Release</li>
<li><a
href="https://github.com/crate-ci/typos/commit/e6d718928a2978481771e814abdc731cb904c980"><code>e6d7189</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/crate-ci/typos/commit/1bf1ed2584d38a3d8f47e0715013e092bdda3cec"><code>1bf1ed2</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1372">#1372</a>
from epage/render</li>
<li><a
href="https://github.com/crate-ci/typos/commit/9e79b8d2c636476c043d040ddaa0413ad065f28b"><code>9e79b8d</code></a>
refactor(cli): Give control over the whole group</li>
<li><a
href="https://github.com/crate-ci/typos/commit/a5fa6034532da62881f747a74e2bbc1f58886265"><code>a5fa603</code></a>
refactor(cli): Extract snippet creation</li>
<li>Additional commits viewable in <a
href="https://github.com/crate-ci/typos/compare/52bd719c2c91f9d676e2aa359fc8e0db8925e6d8...85f62a8a84f939ae994ab3763f01a0296d61a7ee">compare
view</a></li>
</ul>
</details>
<br />

Updates `azure/setup-helm` from 4.3.0 to 4.3.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/azure/setup-helm/releases">azure/setup-helm's
releases</a>.</em></p>
<blockquote>
<h2>v4.3.1</h2>
<h3>Changed</h3>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/167">#167</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/167">Pinning
Action Dependencies for Security and Reliability</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/181">#181</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/181">Fix
types, and update node version.</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/191">#191</a>
<a
href="https://redirect.github.com/Azure/setup-helm/pull/191">chore(tests):
Mock arch to make tests pass on arm host</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/192">#192</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/192">chore:
remove unnecessary prebuild script</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/203">#203</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/203">Update
helm version retrieval to use JSON output for latest version</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/207">#207</a>
<a
href="https://redirect.github.com/Azure/setup-helm/pull/207">ci(workflows):
update helm version to v3.18.4 and add matrix for tests</a></li>
</ul>
<h3>Added</h3>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/197">#197</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/197">Add
pre-commit hook</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Azure/setup-helm/blob/main/CHANGELOG.md">azure/setup-helm's
changelog</a>.</em></p>
<blockquote>
<h1>Change Log</h1>
<h2>[4.3.1] - 2025-08-12</h2>
<h3>Changed</h3>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/167">#167</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/167">Pinning
Action Dependencies for Security and Reliability</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/181">#181</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/181">Fix
types, and update node version.</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/191">#191</a>
<a
href="https://redirect.github.com/Azure/setup-helm/pull/191">chore(tests):
Mock arch to make tests pass on arm host</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/192">#192</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/192">chore:
remove unnecessary prebuild script</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/203">#203</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/203">Update
helm version retrieval to use JSON output for latest version</a></li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/207">#207</a>
<a
href="https://redirect.github.com/Azure/setup-helm/pull/207">ci(workflows):
update helm version to v3.18.4 and add matrix for tests</a></li>
</ul>
<h3>Added</h3>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/197">#197</a>
<a href="https://redirect.github.com/Azure/setup-helm/pull/197">Add
pre-commit hook</a></li>
</ul>
<h2>[4.3.0] - 2025-02-15</h2>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/152">#152</a>
feat: log when restoring from cache</li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/157">#157</a>
Dependencies Update</li>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/137">#137</a>
Add dependabot</li>
</ul>
<h2>[4.2.0] - 2024-04-15</h2>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/124">#124</a>
Fix OS detection and download OS-native archive extension</li>
</ul>
<h2>[4.1.0] - 2024-03-01</h2>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/130">#130</a>
switches to use Helm published file to read latest version instead of
using GitHub releases</li>
</ul>
<h2>[4.0.0] - 2024-02-12</h2>
<ul>
<li><a
href="https://redirect.github.com/azure/setup-helm/issues/121">#121</a>
update to node20 as node16 is deprecated</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Azure/setup-helm/commit/1a275c3b69536ee54be43f2070a358922e12c8d4"><code>1a275c3</code></a>
build</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/9e7f762d6f65c7a11db6cd4eadcf3c602b273f47"><code>9e7f762</code></a>
chore(release): v4.3.1 (<a
href="https://redirect.github.com/azure/setup-helm/issues/208">#208</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/c096176d63fc5e293d360dd405e4b9ab2ddadccd"><code>c096176</code></a>
Bump <code>@​types/node</code> from 24.1.0 to 24.2.1 in the actions
group (<a
href="https://redirect.github.com/azure/setup-helm/issues/206">#206</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/5e7287287e0a62377e90be8d80c4e9520ad5676b"><code>5e72872</code></a>
ci(workflows): update helm version to v3.18.4 and add matrix for tests
(<a
href="https://redirect.github.com/azure/setup-helm/issues/207">#207</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/fb8fa4070642059a5ed2b5d02e2992422ebb5f09"><code>fb8fa40</code></a>
Update default helm version to 3.18.3 (<a
href="https://redirect.github.com/azure/setup-helm/issues/194">#194</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/0d097290a8b8242b9d9c3aae2628122e860fea32"><code>0d09729</code></a>
chore: remove unnecessary prebuild script (<a
href="https://redirect.github.com/azure/setup-helm/issues/192">#192</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/32bc12022d790ee9f7033b2b57cd5c7c309e5333"><code>32bc120</code></a>
chore(tests): Mock arch to make tests pass on arm host (<a
href="https://redirect.github.com/azure/setup-helm/issues/191">#191</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/51463d68e891cc6fcd306c1f61fce342582ac53b"><code>51463d6</code></a>
Bump the actions group with 2 updates (<a
href="https://redirect.github.com/azure/setup-helm/issues/205">#205</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/aff10941b250308c1eea3c6dd4a2e8602246ba0f"><code>aff1094</code></a>
Bump the actions group across 1 directory with 2 updates (<a
href="https://redirect.github.com/azure/setup-helm/issues/204">#204</a>)</li>
<li><a
href="https://github.com/Azure/setup-helm/commit/a10a5247d8e840d5e622e4e00cfc8b6103a9cafa"><code>a10a524</code></a>
Update helm version retrieval to use JSON output for latest version (<a
href="https://redirect.github.com/azure/setup-helm/issues/203">#203</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/azure/setup-helm/compare/b9e51907a09c216f16ebe8536097933489208112...1a275c3b69536ee54be43f2070a358922e12c8d4">compare
view</a></li>
</ul>
</details>
<br />

Updates `chromaui/action` from 13.1.3 to 13.1.4
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/chromaui/action/commit/d0795df816d05c4a89c80295303970fddd247cce"><code>d0795df</code></a>
v13.1.4</li>
<li>See full diff in <a
href="https://github.com/chromaui/action/compare/58d9ffb36c90c97a02d061544ecc849cc4a242a9...d0795df816d05c4a89c80295303970fddd247cce">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/setup-java` from 4.7.1 to 5.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/setup-java/releases">actions/setup-java's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<h3>Breaking Changes</h3>
<ul>
<li>Upgrade to node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/setup-java/pull/888">actions/setup-java#888</a></li>
</ul>
<p>Make sure your runner is updated to this version or newer to use this
release. v2.327.1 <a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<h3>Dependency Upgrades</h3>
<ul>
<li>Upgrade Publish Immutable Action by <a
href="https://github.com/HarithaVattikuti"><code>@​HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-java/pull/798">actions/setup-java#798</a></li>
<li>Upgrade eslint-plugin-jest from 27.9.0 to 28.11.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-java/pull/730">actions/setup-java#730</a></li>
<li>Upgrade undici from 5.28.5 to 5.29.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-java/pull/833">actions/setup-java#833</a></li>
<li>Upgrade form-data to bring in fix for critical vulnerability by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-java/pull/887">actions/setup-java#887</a></li>
<li>Upgrade actions/checkout from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-java/pull/896">actions/setup-java#896</a></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>Prevent default installation of JetBrains pre-releases by <a
href="https://github.com/priyagupta108"><code>@​priyagupta108</code></a>
in <a
href="https://redirect.github.com/actions/setup-java/pull/859">actions/setup-java#859</a></li>
<li>Improve Error Handling for Setup-Java Action to Help Debug
Intermittent Failures by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-java/pull/848">actions/setup-java#848</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-java/pull/848">actions/setup-java#848</a></li>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-java/pull/888">actions/setup-java#888</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-java/compare/v4...v5.0.0">https://github.com/actions/setup-java/compare/v4...v5.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/setup-java/commit/dded0888837ed1f317902acf8a20df0ad188d165"><code>dded088</code></a>
Bump actions/checkout from 4 to 5 (<a
href="https://redirect.github.com/actions/setup-java/issues/896">#896</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/0913e9a06eb8b69c62db76aa61f580c2b3a5b4e0"><code>0913e9a</code></a>
Upgrade to node 24 (<a
href="https://redirect.github.com/actions/setup-java/issues/888">#888</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/e9343db97e09d87a3c50e544105d99fe912c204b"><code>e9343db</code></a>
Bumps form-data (<a
href="https://redirect.github.com/actions/setup-java/issues/887">#887</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/ae2b61dbc685e60e4427b2e8ed4f0135c6ea8597"><code>ae2b61d</code></a>
Bump undici from 5.28.5 to 5.29.0 (<a
href="https://redirect.github.com/actions/setup-java/issues/833">#833</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/c190c18febcf6c040d80b10ea201a05a2c320263"><code>c190c18</code></a>
Bump eslint-plugin-jest from 27.9.0 to 29.0.1 (<a
href="https://redirect.github.com/actions/setup-java/issues/730">#730</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/67aec007b3fcabe15ca665bfccc1e255dd52e30d"><code>67aec00</code></a>
Fix: prevent default installation of JetBrains pre-releases (<a
href="https://redirect.github.com/actions/setup-java/issues/859">#859</a>)</li>
<li><a
href="https://github.com/actions/setup-java/commit/ebb356cc4e59bcf94f518203228485f5d40e4b58"><code>ebb356c</code></a>
Improve Error Handling for Setup-Java Action to Help Debug Intermittent
Failu...</li>
<li><a
href="https://github.com/actions/setup-java/commit/f4f1212c880fdec8162ea9a6493f4495191887b4"><code>f4f1212</code></a>
Update publish-immutable-actions.yml (<a
href="https://redirect.github.com/actions/setup-java/issues/798">#798</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/setup-java/compare/c5195efecf7bdfc987ee8bae7a71cb8b11521c00...dded0888837ed1f317902acf8a20df0ad188d165">compare
view</a></li>
</ul>
</details>
<br />

Updates `google-github-actions/auth` from 2.1.12 to 3.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google-github-actions/auth/releases">google-github-actions/auth's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump to Node 24 and remove old parameters by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/auth/pull/508">google-github-actions/auth#508</a></li>
<li>Remove hacky script by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/auth/pull/509">google-github-actions/auth#509</a></li>
<li>Release: v3.0.0 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/auth/pull/510">google-github-actions/auth#510</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/auth/compare/v2...v3.0.0">https://github.com/google-github-actions/auth/compare/v2...v3.0.0</a></p>
<h2>v2.1.13</h2>
<h2>What's Changed</h2>
<ul>
<li>Update deps by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/auth/pull/506">google-github-actions/auth#506</a></li>
<li>Release: v2.1.13 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/auth/pull/507">google-github-actions/auth#507</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/auth/compare/v2.1.12...v2.1.13">https://github.com/google-github-actions/auth/compare/v2.1.12...v2.1.13</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/google-github-actions/auth/commit/7c6bc770dae815cd3e89ee6cdf493a5fab2cc093"><code>7c6bc77</code></a>
Release: v3.0.0 (<a
href="https://redirect.github.com/google-github-actions/auth/issues/510">#510</a>)</li>
<li><a
href="https://github.com/google-github-actions/auth/commit/42e4997ee345eebb9d114030d0f9e9b47829ee80"><code>42e4997</code></a>
Remove hacky script (<a
href="https://redirect.github.com/google-github-actions/auth/issues/509">#509</a>)</li>
<li><a
href="https://github.com/google-github-actions/auth/commit/5ea4dc11472eebb0a541812f1063c7d318adf57e"><code>5ea4dc1</code></a>
Bump to Node 24 and remove old parameters (<a
href="https://redirect.github.com/google-github-actions/auth/issues/508">#508</a>)</li>
<li><a
href="https://github.com/google-github-actions/auth/commit/c200f3691d83b41bf9bbd8638997a462592937ed"><code>c200f36</code></a>
Release: v2.1.13 (<a
href="https://redirect.github.com/google-github-actions/auth/issues/507">#507</a>)</li>
<li><a
href="https://github.com/google-github-actions/auth/commit/3a53be7e7cedfadb446e102fa59e97734b2ad238"><code>3a53be7</code></a>
Update deps (<a
href="https://redirect.github.com/google-github-actions/auth/issues/506">#506</a>)</li>
<li>See full diff in <a
href="https://github.com/google-github-actions/auth/compare/b7593ed2efd1c1617e1b0254da33b86225adb2a5...7c6bc770dae815cd3e89ee6cdf493a5fab2cc093">compare
view</a></li>
</ul>
</details>
<br />

Updates `google-github-actions/setup-gcloud` from 2.2.0 to 3.0.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google-github-actions/setup-gcloud/releases">google-github-actions/setup-gcloud's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Release: v3.0.1 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/729">google-github-actions/setup-gcloud#729</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/setup-gcloud/compare/v3.0.0...v3.0.1">https://github.com/google-github-actions/setup-gcloud/compare/v3.0.0...v3.0.1</a></p>
<h2>v3.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li><strong>‼️ This release requires Node 24+!</strong></li>
<li><strong>‼️ The <code>skip_tool_cache</code> option has been
removed!</strong> Skipping the tool cache is now the default behavior.
To restore the previous behavior of using the tool cache (which is
unnecessary on GitHub managed runners, but may provide performance
increases on self-hosted runners), set <code>cache: true</code>.</li>
</ul>
<hr />
<ul>
<li>Bump to node24 by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/723">google-github-actions/setup-gcloud#723</a></li>
<li>Do not use the tool-cache by default by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/724">google-github-actions/setup-gcloud#724</a></li>
<li>Update to use v3 references by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/725">google-github-actions/setup-gcloud#725</a></li>
<li>Release: v3.0.0 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/726">google-github-actions/setup-gcloud#726</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/setup-gcloud/compare/v2.2.1...v3.0.0">https://github.com/google-github-actions/setup-gcloud/compare/v2.2.1...v3.0.0</a></p>
<h2>v3</h2>
<p>Floating v3 tag</p>
<h2>v2.2.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update deps by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/720">google-github-actions/setup-gcloud#720</a></li>
<li>Bump to the latest actions-utils to fix the gen-readme bug by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/721">google-github-actions/setup-gcloud#721</a></li>
<li>Release: v2.2.1 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/setup-gcloud/pull/722">google-github-actions/setup-gcloud#722</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/setup-gcloud/compare/v2...v2.2.1">https://github.com/google-github-actions/setup-gcloud/compare/v2...v2.2.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/aa5489c8933f4cc7a4f7d45035b3b1440c9c10db"><code>aa5489c</code></a>
Release: v3.0.1 (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/729">#729</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/26f734c2779b00b7dda794207734c511110a4368"><code>26f734c</code></a>
Release: v3.0.0 (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/726">#726</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/d26df95ce13fba88a7c3f942b50bab0a6e17dc95"><code>d26df95</code></a>
Update to use v3 references (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/725">#725</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/f7c29183d6310acc4d89a82a2e4a06374e259082"><code>f7c2918</code></a>
Do not use the tool-cache by default (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/724">#724</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/6387e6954450355c9cd00e752f0b5e6a07078e1a"><code>6387e69</code></a>
Bump to node24 (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/723">#723</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/e427ad8a34f8676edf47cf7d7925499adf3eb74f"><code>e427ad8</code></a>
Release: v2.2.1 (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/722">#722</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/d71efb74bd86be0c5c0035758f8b5b9069ef835f"><code>d71efb7</code></a>
Bump to the latest actions-utils to fix the gen-readme bug (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/721">#721</a>)</li>
<li><a
href="https://github.com/google-github-actions/setup-gcloud/commit/ed8ba68de5f95fc08e400796581a06a4acab88ae"><code>ed8ba68</code></a>
Update deps (<a
href="https://redirect.github.com/google-github-actions/setup-gcloud/issues/720">#720</a>)</li>
<li>See full diff in <a
href="https://github.com/google-github-actions/setup-gcloud/compare/cb1e50a9932213ecece00a606661ae9ca44f3397...aa5489c8933f4cc7a4f7d45035b3b1440c9c10db">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/attest` from 2.4.0 to 3.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/attest/releases">actions/attest's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump form-data from 4.0.0 to 4.0.4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/attest/pull/266">actions/attest#266</a></li>
<li>Bump <code>@​sigstore/oci</code> from 0.5.0 to 0.6.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/attest/pull/271">actions/attest#271</a></li>
<li>Upgrade to Node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> / <a
href="https://github.com/bdehamer"><code>@​bdehamer</code></a> in <a
href="https://redirect.github.com/actions/attest/pull/276">actions/attest#276</a></li>
<li>Improved checksum parsing by <a
href="https://github.com/bdehamer"><code>@​bdehamer</code></a> in <a
href="https://redirect.github.com/actions/attest/pull/280">actions/attest#280</a></li>
</ul>
<h2>⚠️ Minimum Compatible Runner Version</h2>
<p>v2.327.1
<a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<p>Make sure your runner is updated to this version or newer to use this
release.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/attest/compare/v2.4.0...v3.0.0">https://github.com/actions/attest/compare/v2.4.0...v3.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/attest/commit/daf44fb950173508f38bd2406030372c1d1162b1"><code>daf44fb</code></a>
improved checksum parsing (<a
href="https://redirect.github.com/actions/attest/issues/280">#280</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/eda10f897a7c40837fe73266c10a25e1046b87ff"><code>eda10f8</code></a>
Upgrade to Node 24 (<a
href="https://redirect.github.com/actions/attest/issues/276">#276</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/1e2321d2815b6c07c5479f3309d67a7698b091a1"><code>1e2321d</code></a>
remove super-linter (<a
href="https://redirect.github.com/actions/attest/issues/283">#283</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/aecfe99586992ebf9c025e2fb6ebb78f4f445055"><code>aecfe99</code></a>
Bump the npm-development group across 1 directory with 4 updates (<a
href="https://redirect.github.com/actions/attest/issues/282">#282</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/03f25d860248eee1946e81e6ad8d2420e4de03f3"><code>03f25d8</code></a>
Bump the npm-development group with 4 updates (<a
href="https://redirect.github.com/actions/attest/issues/273">#273</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/0fca5a6fa3c943b77f214acb62a366c247a8469e"><code>0fca5a6</code></a>
use absolute path in linter config (<a
href="https://redirect.github.com/actions/attest/issues/275">#275</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/238c03f77f9c851e5129d01004ff41fa0c755c35"><code>238c03f</code></a>
Bump actions/checkout from 4 to 5 (<a
href="https://redirect.github.com/actions/attest/issues/272">#272</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/9c3e2717a6a9e8eafa4f389f0874b682bcb6f052"><code>9c3e271</code></a>
Bump <code>@​sigstore/oci</code> from 0.5.0 to 0.6.0 (<a
href="https://redirect.github.com/actions/attest/issues/271">#271</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/b40d9fa17a9e7bc51fb727ac9b6dae3ea1da6fa3"><code>b40d9fa</code></a>
Bump the npm-development group with 8 updates (<a
href="https://redirect.github.com/actions/attest/issues/270">#270</a>)</li>
<li><a
href="https://github.com/actions/attest/commit/e831e0e28dfaff51bc0c846aef5bb4195609318f"><code>e831e0e</code></a>
Bump form-data from 4.0.0 to 4.0.4 (<a
href="https://redirect.github.com/actions/attest/issues/266">#266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/attest/compare/ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc...daf44fb950173508f38bd2406030372c1d1162b1">compare
view</a></li>
</ul>
</details>
<br />

Updates `google-github-actions/get-gke-credentials` from 2.3.4 to 3.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google-github-actions/get-gke-credentials/releases">google-github-actions/get-gke-credentials's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump to Node 24 by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/pull/339">google-github-actions/get-gke-credentials#339</a></li>
<li>Release: v3.0.0 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/pull/340">google-github-actions/get-gke-credentials#340</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/get-gke-credentials/compare/v2...v3.0.0">https://github.com/google-github-actions/get-gke-credentials/compare/v2...v3.0.0</a></p>
<h2>v2.3.5</h2>
<h2>What's Changed</h2>
<ul>
<li>Update deps by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/pull/337">google-github-actions/get-gke-credentials#337</a></li>
<li>Release: v2.3.5 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/pull/338">google-github-actions/get-gke-credentials#338</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/get-gke-credentials/compare/v2.3.4...v2.3.5">https://github.com/google-github-actions/get-gke-credentials/compare/v2.3.4...v2.3.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/google-github-actions/get-gke-credentials/commit/3da1e46a907576cefaa90c484278bb5b259dd395"><code>3da1e46</code></a>
Release: v3.0.0 (<a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/issues/340">#340</a>)</li>
<li><a
href="https://github.com/google-github-actions/get-gke-credentials/commit/a1565805d2a41ba5f23190da0a27b6c53ae74250"><code>a156580</code></a>
Bump to Node 24 (<a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/issues/339">#339</a>)</li>
<li><a
href="https://github.com/google-github-actions/get-gke-credentials/commit/64bc7249bbcf78056bb92f14d3cedc2da193946c"><code>64bc724</code></a>
Release: v2.3.5 (<a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/issues/338">#338</a>)</li>
<li><a
href="https://github.com/google-github-actions/get-gke-credentials/commit/9de2e29024e4aeff8cf37aaa326508267ea011fc"><code>9de2e29</code></a>
Update deps (<a
href="https://redirect.github.com/google-github-actions/get-gke-credentials/issues/337">#337</a>)</li>
<li>See full diff in <a
href="https://github.com/google-github-actions/get-gke-credentials/compare/8e574c49425fa7efed1e74650a449bfa6a23308a...3da1e46a907576cefaa90c484278bb5b259dd395">compare
view</a></li>
</ul>
</details>
<br />

Updates `actions/github-script` from 7.0.1 to 8.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/github-script/releases">actions/github-script's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update Node.js version support to 24.x by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/637">actions/github-script#637</a></li>
<li>README for updating actions/github-script from v7 to v8 by <a
href="https://github.com/sneha-krip"><code>@​sneha-krip</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/653">actions/github-script#653</a></li>
</ul>
<h2>⚠️ Minimum Compatible Runner Version</h2>
<p><strong>v2.327.1</strong><br />
<a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<p>Make sure your runner is updated to this version or newer to use this
release.</p>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/637">actions/github-script#637</a></li>
<li><a
href="https://github.com/sneha-krip"><code>@​sneha-krip</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/653">actions/github-script#653</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/github-script/compare/v7.1.0...v8.0.0">https://github.com/actions/github-script/compare/v7.1.0...v8.0.0</a></p>
<h2>v7.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Upgrade husky to v9 by <a
href="https://github.com/benelan"><code>@​benelan</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/482">actions/github-script#482</a></li>
<li>Add workflow file for publishing releases to immutable action
package by <a
href="https://github.com/Jcambass"><code>@​Jcambass</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/485">actions/github-script#485</a></li>
<li>Upgrade IA Publish by <a
href="https://github.com/Jcambass"><code>@​Jcambass</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/486">actions/github-script#486</a></li>
<li>Fix workflow status badges by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/497">actions/github-script#497</a></li>
<li>Update usage of <code>actions/upload-artifact</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/512">actions/github-script#512</a></li>
<li>Clear up package name confusion by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/514">actions/github-script#514</a></li>
<li>Update dependencies with <code>npm audit fix</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/515">actions/github-script#515</a></li>
<li>Specify that the used script is JavaScript by <a
href="https://github.com/timotk"><code>@​timotk</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/478">actions/github-script#478</a></li>
<li>chore: Add Dependabot for NPM and Actions by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/472">actions/github-script#472</a></li>
<li>Define <code>permissions</code> in workflows and update actions by
<a href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in
<a
href="https://redirect.github.com/actions/github-script/pull/531">actions/github-script#531</a></li>
<li>chore: Add Dependabot for .github/actions/install-dependencies by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/532">actions/github-script#532</a></li>
<li>chore: Remove .vscode settings by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/533">actions/github-script#533</a></li>
<li>ci: Use github/setup-licensed by <a
href="https://github.com/nschonni"><code>@​nschonni</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/473">actions/github-script#473</a></li>
<li>make octokit instance available as octokit on top of github, to make
it easier to seamlessly copy examples from GitHub rest api or octokit
documentations by <a
href="https://github.com/iamstarkov"><code>@​iamstarkov</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/508">actions/github-script#508</a></li>
<li>Remove <code>octokit</code> README updates for v7 by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/557">actions/github-script#557</a></li>
<li>docs: add &quot;exec&quot; usage examples by <a
href="https://github.com/neilime"><code>@​neilime</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/546">actions/github-script#546</a></li>
<li>Bump ruby/setup-ruby from 1.213.0 to 1.222.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/github-script/pull/563">actions/github-script#563</a></li>
<li>Bump ruby/setup-ruby from 1.222.0 to 1.229.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/github-script/pull/575">actions/github-script#575</a></li>
<li>Clearly document passing inputs to the <code>script</code> by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/603">actions/github-script#603</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/github-script/pull/610">actions/github-script#610</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/benelan"><code>@​benelan</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/482">actions/github-script#482</a></li>
<li><a href="https://github.com/Jcambass"><code>@​Jcambass</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/485">actions/github-script#485</a></li>
<li><a href="https://github.com/timotk"><code>@​timotk</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/478">actions/github-script#478</a></li>
<li><a
href="https://github.com/iamstarkov"><code>@​iamstarkov</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/508">actions/github-script#508</a></li>
<li><a href="https://github.com/neilime"><code>@​neilime</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/546">actions/github-script#546</a></li>
<li><a href="https://github.com/nebuk89"><code>@​nebuk89</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/github-script/pull/610">actions/github-script#610</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/github-script/compare/v7...v7.1.0">https://github.com/actions/github-script/compare/v7...v7.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/github-script/commit/ed597411d8f924073f98dfc5c65a23a2325f34cd"><code>ed59741</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/653">#653</a>
from actions/sneha-krip/readme-for-v8</li>
<li><a
href="https://github.com/actions/github-script/commit/2dc352e4baefd91bec0d06f6ae2f1045d1687ca3"><code>2dc352e</code></a>
Bold minimum Actions Runner version in README</li>
<li><a
href="https://github.com/actions/github-script/commit/01e118c8d0d22115597e46514b5794e7bc3d56f1"><code>01e118c</code></a>
Update README for Node 24 runtime requirements</li>
<li><a
href="https://github.com/actions/github-script/commit/8b222ac82eda86dcad7795c9d49b839f7bf5b18b"><code>8b222ac</code></a>
Apply suggestion from <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a></li>
<li><a
href="https://github.com/actions/github-script/commit/adc0eeac992408a7b276994ca87edde1c8ce4d25"><code>adc0eea</code></a>
README for updating actions/github-script from v7 to v8</li>
<li><a
href="https://github.com/actions/github-script/commit/20fe497b3fe0c7be8aae5c9df711ac716dc9c425"><code>20fe497</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/637">#637</a>
from actions/node24</li>
<li><a
href="https://github.com/actions/github-script/commit/e7b7f222b11a03e8b695c4c7afba89a02ea20164"><code>e7b7f22</code></a>
update licenses</li>
<li><a
href="https://github.com/actions/github-script/commit/2c81ba05f308415d095291e6eeffe983d822345b"><code>2c81ba0</code></a>
Update Node.js version support to 24.x</li>
<li><a
href="https://github.com/actions/github-script/commit/f28e40c7f34bde8b3046d885e986cb6290c5673b"><code>f28e40c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/github-script/issues/610">#610</a>
from actions/nebuk89-patch-1</li>
<li><a
href="https://github.com/actions/github-script/commit/1ae9958572fde544457e4d51aed5ea044e8936f3"><code>1ae9958</code></a>
Update README.md</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/github-script/compare/60a0d83039c74a4aee543508d2ffcb1c3799cdea...ed597411d8f924073f98dfc5c65a23a2325f34cd">compare
view</a></li>
</ul>
</details>
<br />

Updates `depot/build-push-action` from 1.15.0 to 1.16.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/depot/build-push-action/releases">depot/build-push-action's
releases</a>.</em></p>
<blockquote>
<h2>v1.16.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Use ubuntu-latest for release workflow (<a
href="https://redirect.github.com/depot/build-push-action/issues/42">#42</a>)
<a
href="https://github.com/jacobwgillespie"><code>@​jacobwgillespie</code></a></li>
</ul>
<h2>v1.16.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update <code>@​depot/actions-public-oidc-client</code> to v1.1.0 (<a
href="https://redirect.github.com/depot/build-push-action/issues/41">#41</a>)
<a
href="https://github.com/jacobwgillespie"><code>@​jacobwgillespie</code></a></li>
</ul>
<h2>v1.16.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Add support for annotations (<a
href="https://redirect.github.com/depot/build-push-action/issues/38">#38</a>)
<a href="https://github.com/zanieb"><code>@​zanieb</code></a></li>
<li>feat: add <code>save-tags</code> for multiple depot registry tags
(<a
href="https://redirect.github.com/depot/build-push-action/issues/40">#40</a>)
<a href="https://github.com/goller"><code>@​goller</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/depot/build-push-action/commit/9785b135c3c76c33db102e45be96a25ab55cd507"><code>9785b13</code></a>
Merge pull request <a
href="https://redirect.github.com/depot/build-push-action/issues/42">#42</a>
from depot/latest</li>
<li><a
href="https://github.com/depot/build-push-action/commit/7a65e80415392c68bece25b0881703a9ff3e55c5"><code>7a65e80</code></a>
Use ubuntu-latest for release workflow</li>
<li><a
href="https://github.com/depot/build-push-action/commit/0781b3393f28b17fe7982ea3fabe715825b35fad"><code>0781b33</code></a>
Merge pull request <a
href="https://redirect.github.com/depot/build-push-action/issues/41">#41</a>
from depot/updates</li>
<li><a
href="https://github.com/depot/build-push-action/commit/d5d8e086fdcca080fa6279ece582eee909994026"><code>d5d8e08</code></a>
Deduplicate dependencies</li>
<li><a
href="https://github.com/depot/build-push-action/commit/801feb102f76406603cd2a12b4146a7e5af86a34"><code>801feb1</code></a>
Update <code>@​depot/actions-public-oidc-client</code> to v1.1.0</li>
<li><a
href="https://github.com/depot/build-push-action/commit/0b7423b11016b0349604e22d7ea01b078bed6c70"><code>0b7423b</code></a>
Merge pull request <a
href="https://redirect.github.com/depot/build-push-action/issues/38">#38</a>
from zanieb/zb/annotations</li>
<li><a
href="https://github.com/depot/build-push-action/commit/57a5fb2c1acdc2d3fa72bcd36b20f1ea326890de"><code>57a5fb2</code></a>
Merge branch 'main' into zb/annotations</li>
<li><a
href="https://github.com/depot/build-push-action/commit/3ebc0d44eced926ba1bb3f8d1e29794031973ca1"><code>3ebc0d4</code></a>
Merge pull request <a
href="https://redirect.github.com/depot/build-push-action/issues/40">#40</a>
from depot/feat/save-tags</li>
<li><a
href="https://github.com/depot/build-push-action/commit/d662c5a48fd5747bf4dce16d8484eb3ebf98ad2c"><code>d662c5a</code></a>
feat: add <code>save-tags</code> for multiple depot registry tags</li>
<li><a
href="https://github.com/depot/build-push-action/commit/06fcbb73efc9fb42f4df83c43e283a2170c8f9cf"><code>06fcbb7</code></a>
Add support for annotations</li>
<li>See full diff in <a
href="https://github.com/depot/build-push-action/compare/2583627a84956d07561420dcc1d0eb1f2af3fac0...9785b135c3c76c33db102e45be96a25ab55cd507">compare
view</a></li>
</ul>
</details>
<br />

Updates `tj-actions/changed-files` from
f963b3f3562b00b6d2dd25efc390eb04e51ef6c6 to
8c14441336bb3d84fd6b7fa83b6d7201c740baf5
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tj-actions/changed-files/blob/main/HISTORY.md">tj-actions/changed-files's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v46.0.4...v46.0.5">46.0.5</a>
- (2025-04-09)</h1>
<h2><!-- raw HTML omitted -->⚙️ Miscellaneous Tasks</h2>
<ul>
<li><strong>deps:</strong> Bump yaml from 2.7.0 to 2.7.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2520">#2520</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/ed68ef82c095e0d48ec87eccea555d944a631a4c">ed68ef8</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump typescript from 5.8.2 to 5.8.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2516">#2516</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/a7bc14b808f23d3b467a4079c69a81f1a4500fd5">a7bc14b</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump <code>@​types/node</code> from
22.13.11 to 22.14.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2517">#2517</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/3d751f6b6d84071a17e1b9cf4ed79a80a27dd0ab">3d751f6</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump eslint-plugin-prettier from 5.2.3 to
5.2.6 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2519">#2519</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/e2fda4ec3cb0bc2a353843cae823430b3124db8f">e2fda4e</a>)
- (dependabot[bot])</li>
<li><strong>deps-dev:</strong> Bump ts-jest from 29.2.6 to 29.3.1 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2518">#2518</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/0bed1b1132ec4879a39a2d624cf82a00d0bcfa48">0bed1b1</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump github/codeql-action from 3.28.12 to
3.28.15 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2530">#2530</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/68024587dc36f49685c96d59d3f1081830f968bb">6802458</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/branch-names from 8.0.1 to
8.1.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2521">#2521</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/cf2e39e86bf842d1f9bc5bca56c0a6b207cca792">cf2e39e</a>)
- (dependabot[bot])</li>
<li><strong>deps:</strong> Bump tj-actions/verify-changed-files from
20.0.1 to 20.0.4 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2523">#2523</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/6abeaa506a419f85fa9e681260b443adbeebb3d4">6abeaa5</a>)
- (dependabot[bot])</li>
</ul>
<h2><!-- raw HTML omitted -->⬆️ Upgrades</h2>
<ul>
<li>Upgraded to v46.0.4 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2511">#2511</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/6f67ee9ac810f0192ea7b3d2086406f97847bcf9">6f67ee9</a>)
- (github-actions[bot])</p>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v46.0.3...v46.0.4">46.0.4</a>
- (2025-04-03)</h1>
<h2><!-- raw HTML omitted -->🐛 Bug Fixes</h2>
<ul>
<li>Bug modified_keys and changed_key outputs not set when no changes
detected (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2509">#2509</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/6cb76d07bee4c9772c6882c06c37837bf82a04d3">6cb76d0</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->📚 Documentation</h2>
<ul>
<li>Update readme (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2508">#2508</a>)
(<a
href="https://github.com/tj-actions/changed-files/commit/b74df86ccb65173a8e33ba5492ac1a2ca6b216fd">b74df86</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->⬆️ Upgrades</h2>
<ul>
<li>Upgraded to v46.0.3 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2506">#2506</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
Co-authored-by: Tonye Jack <a
href="mailto:jtonye@ymail.com">jtonye@ymail.com</a> (<a
href="https://github.com/tj-actions/changed-files/commit/27ae6b33eaed7bf87272fdeb9f1c54f9facc9d99">27ae6b3</a>)
- (github-actions[bot])</p>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v46.0.2...v46.0.3">46.0.3</a>
- (2025-03-23)</h1>
<h2><!-- raw HTML omitted -->🔄 Update</h2>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2501">#2501</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/41e0de576a0f2b64d9f06f2773f539109e55a70a">41e0de5</a>)
- (github-actions[bot])</p>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2499">#2499</a>)</li>
</ul>
<p>Co-authored-by: github-actions[bot]
&lt;41898282+github-actions[bot]<a
href="https://github.com/users"><code>@​users</code></a>.noreply.github.com&gt;
(<a
href="https://github.com/tj-actions/changed-files/commit/945787811a795cd840a1157ac590dd7827a05c8e">9457878</a>)
- (github-actions[bot])</p>
<h2><!-- raw HTML omitted -->📚 Documentation</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tj-actions/changed-files/commit/8c14441336bb3d84fd6b7fa83b6d7201c740baf5"><code>8c14441</code></a>
chore(deps): bump actions/setup-node from 4.4.0 to 5.0.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2656">#2656</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/e995ac4be5be2bcb6e29556edc51fb63aca6b49b"><code>e995ac4</code></a>
chore(deps-dev): bump <code>@​types/node</code> from 24.3.0 to 24.3.1
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2657">#2657</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/3b04099b21072562f07469c10deb182b24236ca9"><code>3b04099</code></a>
chore(deps-dev): bump <code>@​types/node</code> from 24.2.1 to 24.3.0
(<a
href="https://redirect.github.com/tj-actions/changed-files/issues/2649">#2649</a>)</li>
<li><a
href="https://github.com/tj-actions/changed-files/commit/e7b6c977e51984988e3cc1d6b18abe2a3ba8daaa"><code>e7b6c97</code></a>
chore(deps): bump github/codeql-action from 3.29.9 to 3.29.11 (<a
href="h...

_Description has been truncated_

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 11:55:07 +00:00
dependabot[bot] 2e06b6f75e chore: bump github.com/zclconf/go-cty from 1.16.3 to 1.17.0 (#19735)
Bumps [github.com/zclconf/go-cty](https://github.com/zclconf/go-cty)
from 1.16.3 to 1.17.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/zclconf/go-cty/releases">github.com/zclconf/go-cty's
releases</a>.</em></p>
<blockquote>
<h2>v1.17.0</h2>
<p><code>cty</code> now requires Go 1.23 or later.</p>
<ul>
<li>
<p><code>cty.Value.Elements</code> offers a modern
<code>iter.Seq2</code>-based equivalent of
<code>cty.Value.ElementIterator</code>.</p>
</li>
<li>
<p><code>cty.DeepValues</code> offers a modern
<code>iter.Seq2</code>-based equivalent of <code>cty.Walk</code>.</p>
</li>
<li>
<p><code>cty.Value.WrangleMarksDeep</code> allows inspecting and
modifying individual marks throughout a possibly-nested data
structure.</p>
<p>Having now got some experience using marks more extensively in some
callers, it's become clear that it's often necessary for different
subsystems to be able to collaborate using independent marks without
upsetting each other's assumptions. Today that tends to be achieved
using hand-written transforms either with <code>cty.Transform</code> or
<code>cty.Value.UnmarkDeepWithPaths</code>/<code>cty.Value.MarkWithPaths</code>,
both of which can be pretty expensive even in the common case where
there are no marks present at all.</p>
<p>This new function allows inspecting and transforming marks with far
less overhead, by creating new values only for parts of a structure that
actually need to change and by reusing (rather than recreating) the
&quot;payloads&quot; of the values being modified when we know that only
the marks have changed.</p>
</li>
<li>
<p><code>cty.ValueMarksOfType</code> and
<code>cty.ValueMarksOfTypeDeep</code> make it easier to use type-based
rather than value-based mark schemes, where different values of a common
type are used to track a specific kind of relationship with multiple
external values.</p>
</li>
<li>
<p><code>cty.Value.HasMarkDeep</code> provides a &quot;deep&quot;
version of the existing <code>cty.Value.HasMark</code>, searching
throughout a possibly-nested structure for any values that have the
given mark.</p>
</li>
<li>
<p><code>cty.Value.UnmarkDeep</code> and
<code>cty.Value.UnmarkDeepWithPaths</code> are now implemented in terms
of <code>cty.Value.WrangleMarksDeep</code>, so they benefit from its
reduced overhead. In particular they avoid reconstructing a data
structure that contains no marked values at all.</p>
</li>
<li>
<p><code>cty.Value.MarkWithPaths</code> now has a fast path when it's
given a zero-length <code>PathValueMarks</code>, in which case it just
returns the value it was given with no modifications.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/zclconf/go-cty/blob/main/CHANGELOG.md">github.com/zclconf/go-cty's
changelog</a>.</em></p>
<blockquote>
<h1>1.17.0 (September 5, 2025)</h1>
<p><code>cty</code> now requires Go 1.23 or later.</p>
<ul>
<li>
<p><code>cty.Value.Elements</code> offers a modern
<code>iter.Seq2</code>-based equivalent of
<code>cty.Value.ElementIterator</code>.</p>
</li>
<li>
<p><code>cty.DeepValues</code> offers a modern
<code>iter.Seq2</code>-based equivalent of <code>cty.Walk</code>.</p>
</li>
<li>
<p><code>cty.Value.WrangleMarksDeep</code> allows inspecting and
modifying individual marks throughout a possibly-nested data
structure.</p>
<p>Having now got some experience using marks more extensively in some
callers, it's become clear that it's often necessary for different
subsystems to be able to collaborate using independent marks without
upsetting each other's assumptions. Today that tends to be achieved
using hand-written transforms either with <code>cty.Transform</code> or
<code>cty.Value.UnmarkDeepWithPaths</code>/<code>cty.Value.MarkWithPaths</code>,
both of which can be pretty expensive even in the common case where
there are no marks present at all.</p>
<p>This new function allows inspecting and transforming marks with far
less overhead, by creating new values only for parts of a structure that
actually need to change and by reusing (rather than recreating) the
&quot;payloads&quot; of the values being modified when we know that only
the marks have changed.</p>
</li>
<li>
<p><code>cty.ValueMarksOfType</code> and
<code>cty.ValueMarksOfTypeDeep</code> make it easier to use type-based
rather than value-based mark schemes, where different values of a common
type are used to track a specific kind of relationship with multiple
external values.</p>
</li>
<li>
<p><code>cty.Value.HasMarkDeep</code> provides a &quot;deep&quot;
version of the existing <code>cty.Value.HasMark</code>, searching
throughout a possibly-nested structure for any values that have the
given mark.</p>
</li>
<li>
<p><code>cty.Value.UnmarkDeep</code> and
<code>cty.Value.UnmarkDeepWithPaths</code> are now implemented in terms
of <code>cty.Value.WrangleMarksDeep</code>, so they benefit from its
reduced overhead. In particular they avoid reconstructing a data
structure that contains no marked values at all.</p>
</li>
<li>
<p><code>cty.Value.MarkWithPaths</code> now has a fast path when it's
given a zero-length <code>PathValueMarks</code>, in which case it just
returns the value it was given with no modifications.</p>
</li>
</ul>
<h1>1.16.4 (August 20, 2025)</h1>
<ul>
<li><code>cty.UnknownAsNull</code> now accepts marked values and
preserves the given marks in its result. Previously it had no direct
support for marks and so would either panic or return incorrect results
when given marked values.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/zclconf/go-cty/commit/da4c600729aefcf628d6b042ee439e6927d1104e"><code>da4c600</code></a>
CHANGELOG: Prepare for v1.17.0 release</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/b13ddd40b1cd39b19c1acfab789049d7ff34358c"><code>b13ddd4</code></a>
cty: Use WrangleMarksDeep for UnmarkDeep and UnmarkDeepWithPaths</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/4453ac2b7f5200601370c6a3f27207ebc81d2df4"><code>4453ac2</code></a>
cty: Use DeepValues instead of Walk for deep marks inspections</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/f833b10b8efd62c42155b93ab1864c77c836dbc0"><code>f833b10</code></a>
Bulk replace interface{} -&gt; any</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/d4bb9d4fb66471b4ffd521770f5a9f4ff277ee04"><code>d4bb9d4</code></a>
cty: Various new mark-inspecting helpers</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/31572cfc28dc2446e7f163451f78116c6cf776bb"><code>31572cf</code></a>
cty+ctymarks: Deep mark wrangling helper</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/d95a68cf6be26d4d0c841a6de102483c9d382006"><code>d95a68c</code></a>
cty: Modern iter.Seq2 equivalents of Value.ElementIterator and Walk</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/e76eeea526f53bec67a8fda74d68c9593f1a500a"><code>e76eeea</code></a>
v1.16.4 release</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/700a2bccfebdc72ce9270b9715dd2d4e86bd1fa6"><code>700a2bc</code></a>
cty: UnknownAsNull accepts marked values and preserves marks</li>
<li><a
href="https://github.com/zclconf/go-cty/commit/3c2b6a0e21d8e541c2ff5b3825b27483fad0bd1b"><code>3c2b6a0</code></a>
Prepare for future v1.16.4 release</li>
<li>See full diff in <a
href="https://github.com/zclconf/go-cty/compare/v1.16.3...v1.17.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/zclconf/go-cty&package-manager=go_modules&previous-version=1.16.3&new-version=1.17.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 11:52:11 +00:00
dependabot[bot] d59a4c5e7d chore: bump github.com/moby/moby from 28.3.0+incompatible to 28.4.0+incompatible (#19733)
Bumps [github.com/moby/moby](https://github.com/moby/moby) from
28.3.0+incompatible to 28.4.0+incompatible.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/moby/moby/releases">github.com/moby/moby's
releases</a>.</em></p>
<blockquote>
<h2>v28.4.0</h2>
<h2>28.4.0</h2>
<p>For a full list of pull requests and changes in this release, refer
to the relevant GitHub milestones:</p>
<ul>
<li><a
href="https://github.com/docker/cli/issues?q=is%3Aclosed+milestone%3A28.4.0">docker/cli,
28.4.0 milestone</a></li>
<li><a
href="https://github.com/moby/moby/issues?q=is%3Aclosed+milestone%3A28.4.0">moby/moby,
28.4.0 milestone</a></li>
<li>Deprecated and removed features, see <a
href="https://github.com/docker/cli/blob/v28.4.0/docs/deprecated.md">Deprecated
Features</a>.</li>
<li>Changes to the Engine API, see <a
href="https://github.com/moby/moby/blob/v28.4.0/docs/api/version-history.md">API
version history</a>.</li>
</ul>
<h3>New</h3>
<ul>
<li>Allow Docker CLI to set the <code>GODEBUG</code> environment
variable when the key-value pair
(<code>&quot;GODEBUG&quot;:&quot;...&quot;</code>) exists inside the
docker context metadata. <a
href="https://redirect.github.com/docker/cli/pull/6399">docker/cli#6399</a></li>
</ul>
<h3>Bug fixes and enhancements</h3>
<ul>
<li>Add shell completion for <code>docker pull</code> and <code>docker
image pull</code>. <a
href="https://redirect.github.com/docker/cli/pull/6420">docker/cli#6420</a></li>
<li>Fix a regression in v28.3.3 that could cause a panic on <code>docker
push</code> if the client did not send an <code>X-Registry-Auth</code>
header. <a
href="https://redirect.github.com/moby/moby/pull/50738">moby/moby#50738</a></li>
<li>Windows: Potentially fix an issue with &quot;access denied&quot;
error when pulling images. <a
href="https://redirect.github.com/moby/moby/pull/50871">moby/moby#50871</a></li>
<li>containerd image store: Fix <code>docker history</code> failing with
<code>snapshot X does not exist</code> when calling on a non-native
image that was built locally. <a
href="https://redirect.github.com/moby/moby/pull/50875">moby/moby#50875</a></li>
<li>containerd image store: Fix <code>docker image prune</code> to emit
correct <code>untag</code> and <code>delete</code> events and list only
the deleted images root digests instead of every blob. <a
href="https://redirect.github.com/moby/moby/pull/50837">moby/moby#50837</a></li>
<li>Remove interactive login prompt from <code>docker push</code> and
<code>docker pull</code> after a failure caused by missing
authentication. <a
href="https://redirect.github.com/docker/cli/pull/6256">docker/cli#6256</a></li>
</ul>
<h3>Packaging updates</h3>
<ul>
<li>Update BuildKit to <a
href="https://github.com/moby/buildkit/releases/tag/v0.24.0">v0.24.0</a>.
<a
href="https://redirect.github.com/moby/moby/pull/50888">moby#50888</a></li>
<li>Update Go runtime to <a
href="https://go.dev/doc/devel/release#go1.24.6">1.24.7</a>. <a
href="https://redirect.github.com/moby/moby/pull/50889">moby/moby#50889</a>,
<a
href="https://redirect.github.com/docker/cli/pull/6422">docker/cli#6422</a></li>
<li>Update <code>runc</code> to <a
href="https://github.com/opencontainers/runc/releases/tag/v1.3.0">v1.3.0</a>.
<a
href="https://redirect.github.com/moby/moby/pull/50699">moby/moby#50699</a></li>
<li>Update containerd (static binaries only) to <a
href="https://github.com/containerd/containerd/releases/tag/v1.7.28">v1.7.28</a>.
<a
href="https://redirect.github.com/moby/moby/pull/50700">moby/moby#50700</a></li>
</ul>
<h3>Networking</h3>
<ul>
<li>Fix an issue that could cause slow container restart on
live-restore. <a
href="https://redirect.github.com/moby/moby/pull/50829">moby/moby#50829</a></li>
</ul>
<h3>API</h3>
<ul>
<li>Update deprecation message for <code>AuthConfig.Email</code> field.
<a
href="https://redirect.github.com/moby/moby/pull/50797">moby/moby#50797</a></li>
</ul>
<h3>Go SDK</h3>
<ul>
<li>Deprecate profiles package which got migrated to <a
href="https://github.com/moby/profiles">github.com/moby/profiles</a>. <a
href="https://redirect.github.com/moby/moby/pull/50513">moby/moby#50513</a></li>
</ul>
<h3>Deprecations</h3>
<ul>
<li>Deprecate special handling for quoted values for the
<code>--tlscacert</code>, <code>--tlscert</code>, and
<code>--tlskey</code> command-line flags. <a
href="https://redirect.github.com/docker/cli/pull/6291">docker/cli#6291</a></li>
<li>Mark legacy links env vars
(<code>DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS</code>) as
deprecated in v28.4 and set for removal in v30.0. <a
href="https://redirect.github.com/docker/cli/pull/6309">docker/cli#6309</a></li>
<li>Go-SDK: Deprecate field <code>NetworkSettingsBase.Bridge</code>,
struct <code>NetworkSettingsBase</code>, all the fields of
<code>DefaultNetworkSettings</code>, and struct
<code>DefaultNetworkSettings</code>. <a
href="https://redirect.github.com/moby/moby/pull/50839">moby/moby#50839</a></li>
<li>Go-SDK: api/types: <code>build.CacheDiskUsage</code>,
<code>container.DiskUsage</code>, <code>images.DiskUsage</code> and
<code>volumes.DiskUsage</code> are now deprecated and will be removed in
the next major release. <a
href="https://redirect.github.com/moby/moby/pull/50768">moby/moby#50768</a></li>
<li>Go-SDK: cli-plugins/manager: deprecate <code>ReexecEnvvar</code>. <a
href="https://redirect.github.com/docker/cli/pull/6411">docker/cli#6411</a></li>
<li>Go-SDK: cli-plugins/manager: deprecate annotation aliases
(<code>CommandAnnotationPlugin</code>,
<code>CommandAnnotationPluginVendor</code>,
<code>CommandAnnotationPluginVersion</code>,
<code>CommandAnnotationPluginInvalid</code>,
<code>CommandAnnotationPluginCommandPath</code>) in favor of their
equivalent in <code>cli-plugins/manager/metadata</code>. <a
href="https://redirect.github.com/docker/cli/pull/6298">docker/cli#6298</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/moby/moby/commit/249d679a6baf8a32bb6d72d6ac5cc7ab9c90b4ea"><code>249d679</code></a>
Merge pull request <a
href="https://redirect.github.com/moby/moby/issues/50890">#50890</a>
from vvoland/50889-28.x</li>
<li><a
href="https://github.com/moby/moby/commit/d664cfe1390eb623963dfc6f2d8bf02e6875ca60"><code>d664cfe</code></a>
update to go1.24.7</li>
<li><a
href="https://github.com/moby/moby/commit/b384cd2a45f543ceac77526e58761ab49494c8c5"><code>b384cd2</code></a>
Merge pull request <a
href="https://redirect.github.com/moby/moby/issues/50888">#50888</a>
from vvoland/50885-28.x</li>
<li><a
href="https://github.com/moby/moby/commit/c1ce88e7f8bba7760fddbb131f2a3e8d1f0069c4"><code>c1ce88e</code></a>
vendor: update buildkit to v0.24.0</li>
<li><a
href="https://github.com/moby/moby/commit/4a34e8e9f69367e4adc830d0562c8a36d1cb855e"><code>4a34e8e</code></a>
Merge pull request <a
href="https://redirect.github.com/moby/moby/issues/50875">#50875</a>
from vvoland/50867-28.x</li>
<li><a
href="https://github.com/moby/moby/commit/cfa70d073ec486c561dd2024050f66d5192a5a34"><code>cfa70d0</code></a>
gha/arm64: Setup qemu</li>
<li><a
href="https://github.com/moby/moby/commit/d70382e4424e19cf65d9837a6d8ed8fcb9a5f566"><code>d70382e</code></a>
integration/internal: Print Buildkit logs</li>
<li><a
href="https://github.com/moby/moby/commit/687b206c6b642608f0d61bda090a4169311dca0d"><code>687b206</code></a>
c8d/history: Fix non-native platforms</li>
<li><a
href="https://github.com/moby/moby/commit/e4224f86c085c609303cdd72fef39384237af430"><code>e4224f8</code></a>
integration/internal: Handle Buildkit in GetImageIDFromBody</li>
<li><a
href="https://github.com/moby/moby/commit/5d5332b00c7618f3ad1ab4eda4ac4102c4a9fa8c"><code>5d5332b</code></a>
Merge pull request <a
href="https://redirect.github.com/moby/moby/issues/50871">#50871</a>
from vvoland/50870-28.x</li>
<li>Additional commits viewable in <a
href="https://github.com/moby/moby/compare/v28.3.0...v28.4.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/moby/moby&package-manager=go_modules&previous-version=28.3.0+incompatible&new-version=28.4.0+incompatible)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 11:50:52 +00:00
dependabot[bot] 855dc77995 chore: bump the x group with 6 updates (#19732)
Bumps the x group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [golang.org/x/mod](https://github.com/golang/mod) | `0.27.0` |
`0.28.0` |
| [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.30.0` |
`0.31.0` |
| [golang.org/x/sync](https://github.com/golang/sync) | `0.16.0` |
`0.17.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.35.0` |
`0.36.0` |
| [golang.org/x/term](https://github.com/golang/term) | `0.34.0` |
`0.35.0` |
| [golang.org/x/text](https://github.com/golang/text) | `0.28.0` |
`0.29.0` |

Updates `golang.org/x/mod` from 0.27.0 to 0.28.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/mod/commit/1759e969dad244f2f1a18cb367aff8614384ffe4"><code>1759e96</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/mod/commit/f060e16ef6180dd29cfa2a803bd919f05ad55a88"><code>f060e16</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/mod/compare/v0.27.0...v0.28.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/oauth2` from 0.30.0 to 0.31.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/oauth2/commit/014cf778b444f29c82ececa4f3ec1f6fe3db3eaf"><code>014cf77</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li><a
href="https://github.com/golang/oauth2/commit/3c76ce5d23d0d48721316e7631625ce32afaa14b"><code>3c76ce5</code></a>
endpoints: correct Naver OAuth2 endpoint URLs</li>
<li>See full diff in <a
href="https://github.com/golang/oauth2/compare/v0.30.0...v0.31.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/sync` from 0.16.0 to 0.17.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/sync/commit/04914c200cb38d4ea960ee6a4c314a028c632991"><code>04914c2</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/sync/compare/v0.16.0...v0.17.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/sys` from 0.35.0 to 0.36.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/sys/commit/b06ce0514ea5467cf3ac72ad85e4d1845c51fbad"><code>b06ce05</code></a>
windows: add FILE_ZERO_DATA_INFORMATION</li>
<li><a
href="https://github.com/golang/sys/commit/689cc11b26fb21227ba0ff5ce9222b7bd6053c30"><code>689cc11</code></a>
unix: fix Listen on solaris</li>
<li><a
href="https://github.com/golang/sys/commit/a4712b905411aebcc0c9b999c7e61826bfe4c11d"><code>a4712b9</code></a>
plan9: drop go version tags for unsupported versions</li>
<li><a
href="https://github.com/golang/sys/commit/0293703b0ad8c5db21b6ee27bfb7addec1877064"><code>0293703</code></a>
unix: add IFAL_* consts and ifaddrlblmsg on linux</li>
<li><a
href="https://github.com/golang/sys/commit/ab85cbbe9104a583d3268f28d8a9ef36653cde64"><code>ab85cbb</code></a>
unix/linux: extend rtnetlink constants</li>
<li><a
href="https://github.com/golang/sys/commit/9bd37534d8d5917ab389c2eadec7547c0c52f20b"><code>9bd3753</code></a>
unix: switch (*CPUSet).Zero to clear builtin</li>
<li><a
href="https://github.com/golang/sys/commit/899c23279da9bc75f6c478ab908e69be3a441b4f"><code>899c232</code></a>
windows/mkwinsyscall: use syscall.SyscallN instead of
syscall.Syscall{6,9,12,15}</li>
<li><a
href="https://github.com/golang/sys/commit/543f21a0561186ad3f71483165a2a1dc32dbae4c"><code>543f21a</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/sys/compare/v0.35.0...v0.36.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/term` from 0.34.0 to 0.35.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/term/commit/1a11b45a6fdc76d25c81fa21867a34052ba8fbd1"><code>1a11b45</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/term/commit/d862cd548e11aa25b32848fd8a08ab1a178f30fa"><code>d862cd5</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/term/compare/v0.34.0...v0.35.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/text` from 0.28.0 to 0.29.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang/text/commit/e69f31bf9cf2f46bd3325bc9bad37fe9001731c2"><code>e69f31b</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="https://github.com/golang/text/commit/60c9786d9e6cc83e1900ce976fdba2e1c327d220"><code>60c9786</code></a>
all: upgrade go directive to at least 1.24.0 [generated]</li>
<li>See full diff in <a
href="https://github.com/golang/text/compare/v0.28.0...v0.29.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 11:50:16 +00:00
dependabot[bot] 8750e8cbcb chore: bump github.com/anthropics/anthropic-sdk-go from 1.4.0 to 1.11.0 (#19730)
Bumps
[github.com/anthropics/anthropic-sdk-go](https://github.com/anthropics/anthropic-sdk-go)
from 1.4.0 to 1.11.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-go/releases">github.com/anthropics/anthropic-sdk-go's
releases</a>.</em></p>
<blockquote>
<h2>v1.11.0</h2>
<h2>1.11.0 (2025-09-05)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.10.0...v1.11.0">v1.10.0...v1.11.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> adds support for Documents in tool results (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/7161c2ce9843b80374186dc83fd84a8dfebda45f">7161c2c</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> fix issue in Go with nested document
content params (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/b442cc3fd41ee53a18f8ccec868ae1057dae53a8">b442cc3</a>)</li>
<li>use release please annotations on more places (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/31a09b07991cc92d38517c80320d154246779a76">31a09b0</a>)</li>
</ul>
<h2>v1.10.0</h2>
<h2>1.10.0 (2025-09-02)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.9.1...v1.10.0">v1.9.1...v1.10.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> makes 1 hour TTL Cache Control generally
available (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c28a9a3272acb1973f2a2fb768157ab27a8f440d">c28a9a3</a>)</li>
<li><strong>client:</strong> adds support for code-execution-2025-08-26
tool (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/066a126a92a8e09f10742f13e0db36724a96c788">066a126</a>)</li>
<li>use custom decoder for []ContentBlockParamUnion (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-go/issues/464">#464</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/473159792468018c709da311d7ac27139cf851e6">4731597</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>close body before retrying (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c970e10ff45c04c38a5a2c87fe85a8c191e06f80">c970e10</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>deprecate older claude-3-5 sonnet models (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-go/issues/453">#453</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/e49d59b14be89dcfb858b565e5183ecf9c1e246b">e49d59b</a>)</li>
</ul>
<h2>v1.9.1</h2>
<h2>1.9.1 (2025-08-12)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.9.0...v1.9.1">v1.9.0...v1.9.1</a></p>
<h2>v1.9.0</h2>
<h2>1.9.0 (2025-08-12)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.8.0...v1.9.0">v1.8.0...v1.9.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>betas:</strong> add context-1m-2025-08-07 (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c086118c9acd55ec711b29a08f165b358e56332b">c086118</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-go/blob/main/CHANGELOG.md">github.com/anthropics/anthropic-sdk-go's
changelog</a>.</em></p>
<blockquote>
<h2>1.11.0 (2025-09-05)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.10.0...v1.11.0">v1.10.0...v1.11.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> adds support for Documents in tool results (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/7161c2ce9843b80374186dc83fd84a8dfebda45f">7161c2c</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> fix issue in Go with nested document
content params (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/b442cc3fd41ee53a18f8ccec868ae1057dae53a8">b442cc3</a>)</li>
<li>use release please annotations on more places (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/31a09b07991cc92d38517c80320d154246779a76">31a09b0</a>)</li>
</ul>
<h2>1.10.0 (2025-09-02)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.9.1...v1.10.0">v1.9.1...v1.10.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> makes 1 hour TTL Cache Control generally
available (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c28a9a3272acb1973f2a2fb768157ab27a8f440d">c28a9a3</a>)</li>
<li><strong>client:</strong> adds support for code-execution-2025-08-26
tool (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/066a126a92a8e09f10742f13e0db36724a96c788">066a126</a>)</li>
<li>use custom decoder for []ContentBlockParamUnion (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-go/issues/464">#464</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/473159792468018c709da311d7ac27139cf851e6">4731597</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>close body before retrying (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c970e10ff45c04c38a5a2c87fe85a8c191e06f80">c970e10</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>deprecate older claude-3-5 sonnet models (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-go/issues/453">#453</a>)
(<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/e49d59b14be89dcfb858b565e5183ecf9c1e246b">e49d59b</a>)</li>
</ul>
<h2>1.9.1 (2025-08-12)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.9.0...v1.9.1">v1.9.0...v1.9.1</a></p>
<h2>1.9.0 (2025-08-12)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.8.0...v1.9.0">v1.8.0...v1.9.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>betas:</strong> add context-1m-2025-08-07 (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c086118c9acd55ec711b29a08f165b358e56332b">c086118</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> detect breaking changes when removing
endpoints (<a
href="https://github.com/anthropics/anthropic-sdk-go/commit/91ea5197646ffd3d807610f11bab8726092e7a4b">91ea519</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/7182eab5e7ebd5d766739f86537fe25a2ab0c51c"><code>7182eab</code></a>
release: 1.11.0</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/b90d70029ae2ff67d281f86bc9d99483e8f79fa3"><code>b90d700</code></a>
fix(client): fix issue in Go with nested document content params</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/cc676e4666daccd502044c5d9249b14023f67ca8"><code>cc676e4</code></a>
feat(api): adds support for Documents in tool results</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/c93b7cd87ffe5c753d69305984ad5186d2f4e93c"><code>c93b7cd</code></a>
fix: use release please annotations on more places</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/92995ec5b5ad00e6aa720aef38a9278b48762754"><code>92995ec</code></a>
release: 1.10.0</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/0663f951e52292eb20c8c81d4585fdb01389f956"><code>0663f95</code></a>
feat(client): adds support for code-execution-2025-08-26 tool</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/90eb81bfbb98831258c8ba3030098649a06fc588"><code>90eb81b</code></a>
feat: use custom decoder for []ContentBlockParamUnion (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-go/issues/464">#464</a>)</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/2f2b92cd9882270b204ca3b0f1be9ebce9dd2572"><code>2f2b92c</code></a>
fix: close body before retrying</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/722cf6bed0ebf16a2886eca80a5f42514ddff956"><code>722cf6b</code></a>
codegen metadata</li>
<li><a
href="https://github.com/anthropics/anthropic-sdk-go/commit/1cea167dfa61355ef1268927094e0c13303e7b67"><code>1cea167</code></a>
codegen metadata</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-go/compare/v1.4.0...v1.11.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/anthropics/anthropic-sdk-go&package-manager=go_modules&previous-version=1.4.0&new-version=1.11.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 11:46:42 +00:00
Mathias Fredriksson 21402c7aaa perf(coderd/database): optimize GetAPIKeysLastUsedAfter (#19725)
This change adds a support-index for `GetAPIKeysLastUsedAfter`.

On dogfood (24h): called 4.4k times, 380ms average.

Change for tested time range: `170ms` -> `3.3ms`.
2025-09-08 13:13:12 +03:00
Mathias Fredriksson fb9753edf2 fix(coderd/database): rename duplicate migration (#19724) 2025-09-08 08:54:12 +00:00
Mathias Fredriksson 9db265d508 fix(coderd/database): optimize provisioner daemon with status query using index (#19703)
Fixes coder/internal#724

This change adds an index to optimize the
`GetProvisionerDaemonsWithStatusByOrganization` query.

Execution time dropped from `18s 838ms` to `107ms`.
2025-09-08 11:10:53 +03:00
Ethan 38028df4d5 ci: make test-go-pg-17 a required check (#19722)
We run an additional test suite with the latest major version of Postgres. Until now, it hasn't been required that this suite pass before merging, prior discussion available [here](https://github.com/coder/coder/pull/13665#discussion_r1654933195). 

Making it required also means we'll receive slack notifications when it fails on `main`.
2025-09-08 18:06:30 +10:00
Spike Curtis d25ff6c48b docs: add guidelines about PR size (#19700)
Adds guidelines about PR size to our contributing guide.
2025-09-08 09:55:48 +04:00
Ethan dae19039d7 test: fix TestCache_DeploymentStats flake (#19683)
Closes https://github.com/coder/internal/issues/961
Likely the same deal as in #19599, the body of `require.Eventually` now fires immediately, when it used to fire after 250ms (the interval). Presumably, the deployment stats become ready before the vs code session count gets incremented. This was never an issue with the 250ms delay, as this flake has only cropped up after the testify version bump.

We'll fix the issue by making it possible to wait for a full metrics cache refresh, i.e. removing `require.Eventually` in this test altogether.
2025-09-08 12:07:38 +10:00
Danielle Maywood e12b621ff0 fix(coderd): ensure agent WebSocket conn is cleaned up (#19711)
When clients disconnected from the /containers/watch endpoint, the WebSocket 
connection between coderd and the agent stayed open. This caused heartbeat 
traffic every 15s that was incorrectly counted as workspace activity, 
extending workspace lifetimes indefinitely.

Now properly cancels the agent connection context when the client disconnects.
2025-09-05 14:26:46 +01:00
Atif Ali 8f72538ab7 docs: reorganize Coder Desktop docs (#18871)
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-05 06:17:47 +00:00
ケイラ 04fa027532 chore: upgrade to vite 7 (#19352)
Nice little treat :)
2025-09-04 17:43:19 -06:00
Brett Kolodny 909acbc833 feat: add sharing add command to the CLI (#19576)
Adds a `sharing add` command for sharing Workspaces with other users and
groups.

The command allows sharing with multiple users, and groups within one
command as well as specifying the role (`use`, or `admin`) defaulting to
`use` if none is specified.

In the current implementation when the command completes we show the
user the current state of the workspace ACL.

```
$ coder sharing add apricot-catfish-86 --user=member:admin --group=contractors:use
USER    GROUP        ROLE
member  -            admin
member  contractors  use
```

If a user is a part of multiple groups, or the workspace has been
individually shared with them they will show up multiple times. Although
this is a bit confusing at first glance it's important to be able to
tell what the maximum role a user may have, and via what ACL they have
it.

---

One piece of UX to consider is that in order to be able to share a
Workspace with a user they must have a role that can read that user. In
the tests we give the user the `ScopedRoleOrgAuditor` role.

Closes
[coder/internal#859](https://github.com/coder/internal/issues/859)
2025-09-04 17:37:16 -04:00
Brett Kolodny a78d65c8b9 fix: prevent new workspace page from scrolling past the bottom of the screen (#19705)
Fixes the overflow on the page and also improves the scroll behavior of
the form

Closes #19690 


https://github.com/user-attachments/assets/00397698-ef34-4146-9589-9218c08510fe
2025-09-04 17:24:05 -04:00
Callum Styan 0ec9df390b fix: reduce impact of GetPrebuildMetrics on database (#19694)
see https://github.com/coder/internal/issues/959 but the tl; dr is:
- we call this DB query on an interval (every 15s) and it would be
called on each coderd replica as well
- the generated values update very infrequently (for our most used
internal template I saw the builds created/claimed update twice in a 1h
period)
- we have no index on the initiator ID, so this query has to scan the
entire workspace_builds table on every request

In reality this should likely just be a Prometheus metric, and
Prometheus can handle the counter reset behaviour at query time, but for
now this should at least cut the load of the query to 25% of it's
current impact.

---------

Signed-off-by: Callum Styan <callumstyan@gmail.com>
2025-09-04 13:43:50 -07:00
Hugo Dutka 20309074d1 docs: update Tailscale DERP fleet usage phrasing (#19653)
I noticed that our docs mention the possibility of using the
Tailscale-managed DERP server fleet.
https://github.com/coder/coder/pull/15901 changed the phrasing from

> However, Tailscale has graciously allowed us to use  

to  

> However, our Wireguard integration through Tailscale has graciously
allowed us to use

This change alters the original meaning of the sentence. AFAIK, the
original meant that we contacted Tailscale directly and asked if it
would be ok for our customers to use the Tailscale-managed DERP server
fleet, and Tailscale graciously agreed. The new phrasing conveys
something different. This PR reverts the phrasing to the original.

---------

Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-04 19:09:08 +02:00
Danielle Maywood f94abfc83e feat(cli): add quiet flag to task create (#19701)
Allows passing `--quiet` (or `-q`) to the task create command so that it
only prints the ID of the task.
2025-09-04 10:03:22 +01:00
Ethan f867a9d481 fix(cli/templatepush): only implicitly read from stdin if the directory flag is unset (#19681)
In trying to address confusion with the `-`  (for stdin) directory flag last year, I had `template push` read from stdin if stdin was not a TTY. However, I made the mistake of checking if the directory flag was set or not by comparing it to the default value. This meant in something like GitHub Actions, where you don't have a TTY for stdin, it was impossible to read from the current working directory. The fix is just to check if the flag was explicitly set, using pflags.

If users encounter this bug, and this fix is unavailable in their version of Coder, they can workaround it by setting `-d "$(pwd)"`
2025-09-04 14:31:56 +10:00
Ethan 50704a5014 ci: improve 'tfail in goroutine' ruleguard rule (#19682)
This PR improves the ruleguard rule for detecting `t.Fail` calls in goroutines. It picks up additional violations, of which are fixed in this PR.
See self-review for details.

The motivation for fixing this comes from a flake I fixed in https://github.com/coder/coder/pull/19599, where tests would fail from a `require` in an `Eventually`.
2025-09-04 14:28:29 +10:00
Ethan 1b4ce0909c fix: pin pg_dump version when generating schema (#19696)
The latest release of all `pg_dump` major versions, going back to 13,
started inserting `\restrict` `\unrestrict` keywords into dumps. This
currently breaks sqlc in `gen/dump` and our check migration script. Full
details of the postgres change are available here:
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=575f54d4c

To fix, we'll always use the `pg_dump` in our postgres 13.21 docker
image for schema dumps, instead of what's on the runner/local machine.

Coder doesn't restore from postgres dumps, so we're not vulnerable to
attacks that would be patched by the latest postgres version.
Regardless, we'll unpin ASAP.

Once sqlc is updated to handle these keywords, we need to start
stripping them when comparing the schema in the migration check script,
and then we can unpin the pg_dump version. This is being tracked at
https://github.com/coder/internal/issues/965
2025-09-04 14:00:21 +10:00
Atif Ali 62c74305db chore(docs): always point to latest toolsdk in MCP docs (#19689)
Updated toolsdk documentation link to the latest version.

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.
2025-09-03 13:28:33 +00:00
Spike Curtis 04dfda8a0e fix: change enqueue error to debug log level (#19686)
fixes https://github.com/coder/internal/issues/958

Logging was being done at error level, but most likely any errors are from simple races between an update triggered around the same time as a client disconnecting. Debug is fine for these.
2025-09-03 13:42:02 +04:00
Marcin Tojek d415964b9f fix: show popup on successful template build (#19674)
Fixes: https://github.com/coder/coder/issues/18364
2025-09-03 09:23:37 +02:00
Spike Curtis 18945a7949 chore: refactor CLI agent auth tests as unit tests (#19609)
Fixes https://github.com/coder/internal/issues/933

Refactors CLI tests that check the `--auth` flag parsing for various public clouds into a unit test that just creates the agent Client and asserts on the type.

Testing that the agent client actually authenticates correctly with these auth types is well covered by Coderd tests, so we don't need to retread that ground here, and the deleted tests were flaky on Windows.
2025-09-03 10:49:19 +04:00
Spike Curtis 1354d84eb4 chore: refactor instance identity to be a SessionTokenProvider (#19566)
Refactors Agent instance identity to be a SessionTokenProvider.

Refactors the CLI to create Agent clients via a centralized function, rather than add-hoc via individual command handlers and their flags.

This allows commands besides `coder agent`, but which still use the agent identity, to support instance identity authentication.

Fixes #19111 by unifying all API requests to go thru the SessionTokenProvider for auth credentials.
2025-09-03 10:38:42 +04:00
Spike Curtis ee35ad3a57 fix: remove hold WaitGroup in TestConcurrentFetch (#19617)
Fixes: https://github.com/coder/internal/issues/950

Pretty sure the intention of the `hold` wait group is to try to get the two goroutines that the test starts running at the same time. But, that should be the case for two goroutines started anyway.

The use of `hold` doesn't actually guarantee concurrent execution of `Acquire`, just that both goroutines get as far as `Done()` --- the go scheduler could run them serially without incident.

So I've chosen to just remove the use of `hold` to simplify.

But, for posterity, the data race was due to incrementing by 1 in the loop along with the goroutine that calls Done. You could increment by 1 and then back down to 0 before the second iteration of the loop starts. This then causes a data race with calling `Wait()` in the first goroutine and `Add()` in the second iteration. c.f. https://pkg.go.dev/sync#WaitGroup.Add
2025-09-03 09:25:49 +04:00
Brett Kolodny 6606d8b642 fix: add bottom padding to workspace page (#19643)
Before:
<img width="2874" height="142" alt="CleanShot 2025-08-29 at 11 58 56@2x"
src="https://github.com/user-attachments/assets/4e270397-d0d5-40b7-aac4-f00b7f842fda"
/>


After:

<img width="3104" height="228" alt="CleanShot 2025-08-29 at 11 59 14@2x"
src="https://github.com/user-attachments/assets/106ccaf1-2615-4354-8b1d-3e68fc73b218"
/>
2025-09-02 20:16:51 -04:00
Steven Masley f571730715 chore: update coder/preview to v1.0.4 (#19640) 2025-09-02 15:01:20 -04:00
Cian Johnston bd6e91eeab fix(coderd): add audit log on creating a new session key (#19672)
Fixes https://github.com/coder/coder/issues/19671
(re-?)Adds an audit log entry when an API key is created via `coder
login`.

NOTE: This does _not_ backfill audit logs.

<img width="1354" height="207" alt="Screenshot 2025-09-02 at 14 16 24"
src="https://github.com/user-attachments/assets/921e85c1-eced-4a19-9d37-8f84f4af1e73"
/>
2025-09-02 14:21:56 +01:00
Danielle Maywood 12bce12952 fix(coderd): ensure a newly created task can be fetched (#19670)
Due to how we currently label a workspace as a task, there is a delay
between when a task workspace is created and when it is labelled as a
task.

This PR introduces fallback check for when a workspace does _not_ have
`HasAITask` set. This fallback check tests to see if the special "AI
Prompt" parameter is present in the workspace's build parameters.
2025-09-02 13:25:32 +01:00
Cian Johnston 06cbb2890f fix: expire token for prebuilds user when regenerating session token (#19667)
* provisionerdserver: Expires prebuild user token for workspace, if it
exists, when regenerating session token.
* dbauthz: disallow prebuilds user from creating api keys
* dbpurge: added functionality to expire stale api keys owned by the
prebuilds user
2025-09-02 09:38:43 +01:00
Danielle Maywood a2a758d5a6 chore(cli): re-order CLI create command (#19658)
Relates to https://github.com/coder/internal/issues/893

Instead of `coder task create <template> --input <input>`, it is now
`coder task create <input> --template <template>`.

If there is only one AI task template on the deployment, the
`--template` parameter can be omitted.
2025-09-02 09:07:25 +01:00
dependabot[bot] 519812776e chore: bump github.com/stretchr/testify from 1.10.0 to 1.11.1 (#19599)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify)
from 1.10.0 to 1.11.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/stretchr/testify/releases">github.com/stretchr/testify's
releases</a>.</em></p>
<blockquote>
<h2>v1.11.1</h2>
<p>This release fixes <a
href="https://redirect.github.com/stretchr/testify/issues/1785">#1785</a>
introduced in v1.11.0 where expected argument values implementing the
stringer interface (<code>String() string</code>) with a method which
mutates their value, when passed to mock.Mock.On
(<code>m.On(&quot;Method&quot;, &lt;expected&gt;).Return()</code>) or
actual argument values passed to mock.Mock.Called may no longer match
one another where they previously did match. The behaviour prior to
v1.11.0 where the stringer is always called is restored. Future testify
releases may not call the stringer method at all in this case.</p>
<h2>What's Changed</h2>
<ul>
<li>Backport <a
href="https://redirect.github.com/stretchr/testify/issues/1786">#1786</a>
to release/1.11: mock: revert to pre-v1.11.0 argument matching behavior
for mutating stringers by <a
href="https://github.com/brackendawson"><code>@​brackendawson</code></a>
in <a
href="https://redirect.github.com/stretchr/testify/pull/1788">stretchr/testify#1788</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/stretchr/testify/compare/v1.11.0...v1.11.1">https://github.com/stretchr/testify/compare/v1.11.0...v1.11.1</a></p>
<h2>v1.11.0</h2>
<h2>What's Changed</h2>
<h3>Functional Changes</h3>
<p>v1.11.0 Includes a number of performance improvements.</p>
<ul>
<li>Call stack perf change for CallerInfo by <a
href="https://github.com/mikeauclair"><code>@​mikeauclair</code></a> in
<a
href="https://redirect.github.com/stretchr/testify/pull/1614">stretchr/testify#1614</a></li>
<li>Lazily render mock diff output on successful match by <a
href="https://github.com/mikeauclair"><code>@​mikeauclair</code></a> in
<a
href="https://redirect.github.com/stretchr/testify/pull/1615">stretchr/testify#1615</a></li>
<li>assert: check early in Eventually, EventuallyWithT, and Never by <a
href="https://github.com/cszczepaniak"><code>@​cszczepaniak</code></a>
in <a
href="https://redirect.github.com/stretchr/testify/pull/1427">stretchr/testify#1427</a></li>
<li>assert: add IsNotType by <a
href="https://github.com/bartventer"><code>@​bartventer</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1730">stretchr/testify#1730</a></li>
<li>assert.JSONEq: shortcut if same strings by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1754">stretchr/testify#1754</a></li>
<li>assert.YAMLEq: shortcut if same strings by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1755">stretchr/testify#1755</a></li>
<li>assert: faster and simpler isEmpty using reflect.Value.IsZero by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1761">stretchr/testify#1761</a></li>
<li>suite: faster methods filtering (internal refactor) by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1758">stretchr/testify#1758</a></li>
</ul>
<h3>Fixes</h3>
<ul>
<li>assert.ErrorAs: log target type by <a
href="https://github.com/craig65535"><code>@​craig65535</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1345">stretchr/testify#1345</a></li>
<li>Fix failure message formatting for Positive and Negative asserts in
<a
href="https://redirect.github.com/stretchr/testify/pull/1062">stretchr/testify#1062</a></li>
<li>Improve ErrorIs message when error is nil but an error was expected
by <a href="https://github.com/tsioftas"><code>@​tsioftas</code></a> in
<a
href="https://redirect.github.com/stretchr/testify/pull/1681">stretchr/testify#1681</a></li>
<li>fix Subset/NotSubset when calling with mixed input types by <a
href="https://github.com/siliconbrain"><code>@​siliconbrain</code></a>
in <a
href="https://redirect.github.com/stretchr/testify/pull/1729">stretchr/testify#1729</a></li>
<li>Improve ErrorAs failure message when error is nil by <a
href="https://github.com/ccoVeille"><code>@​ccoVeille</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1734">stretchr/testify#1734</a></li>
<li>mock.AssertNumberOfCalls: improve error msg by <a
href="https://github.com/3scalation"><code>@​3scalation</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1743">stretchr/testify#1743</a></li>
</ul>
<h3>Documentation, Build &amp; CI</h3>
<ul>
<li>docs: Fix typo in README by <a
href="https://github.com/alexandear"><code>@​alexandear</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1688">stretchr/testify#1688</a></li>
<li>Replace deprecated io/ioutil with io and os by <a
href="https://github.com/alexandear"><code>@​alexandear</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1684">stretchr/testify#1684</a></li>
<li>Document consequences of calling t.FailNow() by <a
href="https://github.com/greg0ire"><code>@​greg0ire</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1710">stretchr/testify#1710</a></li>
<li>chore: update docs for Unset <a
href="https://redirect.github.com/stretchr/testify/issues/1621">#1621</a>
by <a href="https://github.com/techfg"><code>@​techfg</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1709">stretchr/testify#1709</a></li>
<li>README: apply gofmt to examples by <a
href="https://github.com/alexandear"><code>@​alexandear</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1687">stretchr/testify#1687</a></li>
<li>refactor: use %q and %T to simplify fmt.Sprintf by <a
href="https://github.com/alexandear"><code>@​alexandear</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1674">stretchr/testify#1674</a></li>
<li>Propose Christophe Colombier (ccoVeille) as approver by <a
href="https://github.com/brackendawson"><code>@​brackendawson</code></a>
in <a
href="https://redirect.github.com/stretchr/testify/pull/1716">stretchr/testify#1716</a></li>
<li>Update documentation for the Error function in assert or require
package by <a
href="https://github.com/architagr"><code>@​architagr</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1675">stretchr/testify#1675</a></li>
<li>assert: remove deprecated build constraints by <a
href="https://github.com/alexandear"><code>@​alexandear</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1671">stretchr/testify#1671</a></li>
<li>assert: apply gofumpt to internal test suite by <a
href="https://github.com/ccoVeille"><code>@​ccoVeille</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1739">stretchr/testify#1739</a></li>
<li>CI: fix shebang in .ci.*.sh scripts by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1746">stretchr/testify#1746</a></li>
<li>assert,require: enable parallel testing on (almost) all top tests by
<a href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1747">stretchr/testify#1747</a></li>
<li>suite.Passed: add one more status test report by <a
href="https://github.com/Ararsa-Derese"><code>@​Ararsa-Derese</code></a>
in <a
href="https://redirect.github.com/stretchr/testify/pull/1706">stretchr/testify#1706</a></li>
<li>Add Helper() method in internal mocks and assert.CollectT by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1423">stretchr/testify#1423</a></li>
<li>assert.Same/NotSame: improve usage of Sprintf by <a
href="https://github.com/ccoVeille"><code>@​ccoVeille</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1742">stretchr/testify#1742</a></li>
<li>mock: enable parallel testing on internal testsuite by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1756">stretchr/testify#1756</a></li>
<li>suite: cleanup use of 'testing' internals at runtime by <a
href="https://github.com/dolmen"><code>@​dolmen</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1751">stretchr/testify#1751</a></li>
<li>assert: check test failure message for Empty and NotEmpty by <a
href="https://github.com/ccoVeille"><code>@​ccoVeille</code></a> in <a
href="https://redirect.github.com/stretchr/testify/pull/1745">stretchr/testify#1745</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/stretchr/testify/commit/2a57335dc9cd6833daa820bc94d9b40c26a7917d"><code>2a57335</code></a>
Merge pull request <a
href="https://redirect.github.com/stretchr/testify/issues/1788">#1788</a>
from brackendawson/1785-backport-1.11</li>
<li><a
href="https://github.com/stretchr/testify/commit/af8c91234f184009f57ef29027b39ca89cb00100"><code>af8c912</code></a>
Backport <a
href="https://redirect.github.com/stretchr/testify/issues/1786">#1786</a>
to release/1.11</li>
<li><a
href="https://github.com/stretchr/testify/commit/b7801fbf5cd58d201296d5d0e132d1849966dbd4"><code>b7801fb</code></a>
Merge pull request <a
href="https://redirect.github.com/stretchr/testify/issues/1778">#1778</a>
from stretchr/dependabot/github_actions/actions/chec...</li>
<li><a
href="https://github.com/stretchr/testify/commit/69831f3b08c40d56a09d0be93e9d5ae034f1590b"><code>69831f3</code></a>
build(deps): bump actions/checkout from 4 to 5</li>
<li><a
href="https://github.com/stretchr/testify/commit/a53be35c3b0cfcd5189cffcfd75df60ea581104c"><code>a53be35</code></a>
Improve captureTestingT helper</li>
<li><a
href="https://github.com/stretchr/testify/commit/aafb604176db7e1f2c9810bc90d644291d057687"><code>aafb604</code></a>
mock: improve formatting of error message</li>
<li><a
href="https://github.com/stretchr/testify/commit/7218e0390acd2aea3edb18574110ec2753c0aeef"><code>7218e03</code></a>
improve error msg</li>
<li><a
href="https://github.com/stretchr/testify/commit/929a2126c2702df436312656a0304580b526c6e9"><code>929a212</code></a>
Merge pull request <a
href="https://redirect.github.com/stretchr/testify/issues/1758">#1758</a>
from stretchr/dolmen/suite-faster-method-filtering</li>
<li><a
href="https://github.com/stretchr/testify/commit/bc7459ec38128532ff32f23cfab4ea0b725210f2"><code>bc7459e</code></a>
suite: faster filtering of methods (-testify.m)</li>
<li><a
href="https://github.com/stretchr/testify/commit/7d37b5c962954410bcd7a71ff3a77c79514056d1"><code>7d37b5c</code></a>
suite: refactor methodFilter</li>
<li>Additional commits viewable in <a
href="https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/stretchr/testify&package-manager=go_modules&previous-version=1.10.0&new-version=1.11.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ethan Dickson <ethan@coder.com>
2025-09-02 03:12:37 +00:00
Ethan a3b81761f1 chore: delete old changelogs from docs (#19631)
A Dependabot PR got blocked by a typo in a 2.10 changelog! I then noticed we're keeping these old changelogs (<= 2.10) around, even though we haven't been updating this directory for many months now. 

I'm putting this PR up as I assume we want to delete those, it seems they'd be more confusing to users than anything. They're not referenced on the website nor in the docs manifest.json. 
If I'm mistaken, and we do want to keep these, feel free to close this PR.
2025-09-02 11:16:57 +10:00
Ethan 98c72c3a25 ci: make blink ci failure prompt a gha variable (#19633)
Got sick of seeing blink create duplicates, so I'm updating the prompt. To make it configurable without committing I'm making it a variable, here's what I've got:

> Investigate this CI failure. Check logs, and figure out what went wrong. Search for existing issues in coder/internal. If an issue for the CI failure does not exist already, create one ONLY in coder/internal. Do NOT create duplicate issues. Use title format \"flake: TestName\" for flaky tests, and assign them to the person from git blame.
If multiple tests fail with the reason `unknown`, the test process exited unexpectedly, perhaps due to a panic.

Once blink supports per-slack-channel contexts, i'll probably just set the variable to the empty string and use that instead.
2025-09-02 11:07:24 +10:00
dependabot[bot] a8f92cb586 chore: bump github.com/golang-migrate/migrate/v4 from 4.18.1 to 4.19.0 (#19662)
Bumps
[github.com/golang-migrate/migrate/v4](https://github.com/golang-migrate/migrate)
from 4.18.1 to 4.19.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/golang-migrate/migrate/releases">github.com/golang-migrate/migrate/v4's
releases</a>.</em></p>
<blockquote>
<h2>v4.19.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fixed sqlserver not actually getting a lock if lock is already set
by <a href="https://github.com/urbim"><code>@​urbim</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1186">golang-migrate/migrate#1186</a></li>
<li>Bump golang.org/x/oauth2 from 0.18.0 to 0.27.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1299">golang-migrate/migrate#1299</a></li>
<li>Update apt-key to gpg by <a
href="https://github.com/sandhilt"><code>@​sandhilt</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1277">golang-migrate/migrate#1277</a></li>
<li>Update dktest to v0.4.6 for docker vuln fix by <a
href="https://github.com/dhui"><code>@​dhui</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1309">golang-migrate/migrate#1309</a></li>
<li>refactor: Remove go.uber.org/atomic in favor of std sync/atomic by
<a href="https://github.com/romshark"><code>@​romshark</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1303">golang-migrate/migrate#1303</a></li>
<li>Ensure bufferWriter is always closed in Migration.Buffer and
propagate close errors by <a
href="https://github.com/ckantcs"><code>@​ckantcs</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1308">golang-migrate/migrate#1308</a></li>
<li>Add support for Go 1.25 and drop support for 1.23 by <a
href="https://github.com/dhui"><code>@​dhui</code></a> in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1310">golang-migrate/migrate#1310</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/urbim"><code>@​urbim</code></a> made
their first contribution in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1186">golang-migrate/migrate#1186</a></li>
<li><a href="https://github.com/sandhilt"><code>@​sandhilt</code></a>
made their first contribution in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1277">golang-migrate/migrate#1277</a></li>
<li><a href="https://github.com/romshark"><code>@​romshark</code></a>
made their first contribution in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1303">golang-migrate/migrate#1303</a></li>
<li><a href="https://github.com/ckantcs"><code>@​ckantcs</code></a> made
their first contribution in <a
href="https://redirect.github.com/golang-migrate/migrate/pull/1308">golang-migrate/migrate#1308</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/golang-migrate/migrate/compare/v4.18.3...v4.19.0">https://github.com/golang-migrate/migrate/compare/v4.18.3...v4.19.0</a></p>
<h2>v4.18.3</h2>
<h2>Changelog</h2>
<ul>
<li>a4d0a1b Bump github.com/golang-jwt/jwt/v4 from 4.5.1 to 4.5.2</li>
<li>f37ef79 Bump golang.org/x/crypto from 0.31.0 to 0.35.0</li>
<li>5b97c92 Bump golang.org/x/net from 0.33.0 to 0.38.0</li>
<li>e6d84f6 Drop support for Go 1.22 and add support for Go 1.24</li>
<li>fccd197 Mention CLI install instructions in main README</li>
<li>34c2b4a Remove redundant build tags</li>
<li>a868033 Update FAQ.md - typo</li>
<li>7269490 Update golangci-lint version used in GitHub Actions</li>
<li>c5137c4 Update migrate -help output for the readme file</li>
<li>033835a Update to dktest v0.4.5</li>
<li>8b09191 fix: typo limited not limitted</li>
<li>60d73be refactor: replace github.com/pkg/errors with stdlib</li>
<li>36d17ba tests: fix various tests (<a
href="https://redirect.github.com/golang-migrate/migrate/issues/1209">#1209</a>)</li>
</ul>
<h2>v4.18.2</h2>
<h2>Changelog</h2>
<ul>
<li>e145cde Bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.5.1</li>
<li>e22d012 Don't output sensitive information on error</li>
<li>e5a152b Drop support for Azure SQL Edge</li>
<li>12c619e Fix CI (<a
href="https://redirect.github.com/golang-migrate/migrate/issues/1222">#1222</a>)</li>
<li>bc06922 Update dktest from v0.4.3 to v0.4.4</li>
<li>7651c8a linter fixes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/golang-migrate/migrate/commit/8b9c5f77128ef93d65a082208a2009a3911fe6d4"><code>8b9c5f7</code></a>
Merge pull request <a
href="https://redirect.github.com/golang-migrate/migrate/issues/1310">#1310</a>
from dhui/update_go</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/b4ec9bccb14cd7b0eb0510ac9d3d01d4be79324f"><code>b4ec9bc</code></a>
Add support for Go 1.25 and drop support for 1.23</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/ed4bdd4614e991ca8fba606b38a45f6409a9deb6"><code>ed4bdd4</code></a>
Ensure bufferWriter is always closed in Migration.Buffer and propagate
close ...</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/8945e853c4c84a92bfa42572ac4cfd7874e23108"><code>8945e85</code></a>
Merge pull request <a
href="https://redirect.github.com/golang-migrate/migrate/issues/1303">#1303</a>
from romshark/master</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/7108d806dd50c1510510239a161887757f240122"><code>7108d80</code></a>
Merge pull request <a
href="https://redirect.github.com/golang-migrate/migrate/issues/1309">#1309</a>
from dhui/dktest_v0.4.6</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/682016f04c2c0f8faa1d122fd22a62563876f71d"><code>682016f</code></a>
Merge pull request <a
href="https://redirect.github.com/golang-migrate/migrate/issues/1277">#1277</a>
from sandhilt/doc/change-apt-key-to-gpg</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/f3e6b5a737de1b7c79aaf81168395d6f2eb1fbb0"><code>f3e6b5a</code></a>
Replace usage of deprecated docker types</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/0a17402aa2359c8c5cd5a68a0e08a41c4460337f"><code>0a17402</code></a>
Update dktest to v0.4.6 for docker vuln fix</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/5eee0c8030ca227bfd27a425988f9c7c4948e90a"><code>5eee0c8</code></a>
Merge pull request <a
href="https://redirect.github.com/golang-migrate/migrate/issues/1299">#1299</a>
from golang-migrate/dependabot/go_modules/golang.org...</li>
<li><a
href="https://github.com/golang-migrate/migrate/commit/642a24d61bb0a870a15020adceeee3e85d5151c3"><code>642a24d</code></a>
Bump golang.org/x/oauth2 from 0.18.0 to 0.27.0</li>
<li>Additional commits viewable in <a
href="https://github.com/golang-migrate/migrate/compare/v4.18.1...v4.19.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/golang-migrate/migrate/v4&package-manager=go_modules&previous-version=4.18.1&new-version=4.19.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-01 19:08:03 +00:00
dependabot[bot] d9afbc21ef chore: bump github.com/brianvoe/gofakeit/v7 from 7.4.0 to 7.5.1 (#19661)
Bumps
[github.com/brianvoe/gofakeit/v7](https://github.com/brianvoe/gofakeit)
from 7.4.0 to 7.5.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/c0093afeceb88fc40270917132d79fcbf63079cc"><code>c0093af</code></a>
payment - alias and keyword updates</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/521ca8fadc0efa182334e3e28e9ecc01b55da84d"><code>521ca8f</code></a>
aliases and keywords - completed</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/fe7530dc54e19139d5e1f9b6824d0e7881113d7c"><code>fe7530d</code></a>
aliases and keywords - continue to refine</li>
<li><a
href="https://github.com/brianvoe/gofakeit/commit/002a6c57e4af7b1e84542e8ff15674bc25cb2dee"><code>002a6c5</code></a>
work in progress</li>
<li>See full diff in <a
href="https://github.com/brianvoe/gofakeit/compare/v7.4.0...v7.5.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/brianvoe/gofakeit/v7&package-manager=go_modules&previous-version=7.4.0&new-version=7.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-01 18:56:00 +00:00
Callum Styan 4fab14b40b fix: limit the scope of the template average build time query to the last 100 (#19648)
This PR should resolve https://github.com/coder/internal/issues/719 by
limiting the `workspace_builds` rows selected by the query to the most
recent 100 builds of a template, as opposed to all builds in the last
30d. For our own internal templates with the most builds (1700-2000 in a
30d period) this should cut the query execution time by about 80%.

Unless we have some restriction on keeping the 30d period, contract
related or otherwise, this seems like a safe change to make. In addition
to the execution speed improvements it also means the memory for the
query is bounded as well.

If we want to keep a 30d time period for the avg build time value I
think it's worth exploring a purpose built solution such as histogram
structures where the build times could be bucketized by template ID as
they're observed.

---------

Signed-off-by: Callum Styan <callumstyan@gmail.com>
2025-09-01 09:31:21 -07:00
Callum Styan 6574299fcc fix: fix TestExecutorAutostartSkipsWhenNoProvisionersAvailable flake, part 2 (#19649)
This test still flakes occasionally, see
https://github.com/coder/internal/issues/954#issuecomment-3237154735

The cause appears to be related to the assignment of `time.Now()` as the
`LastSeenAt` time when creating a provisioner which can flake with the
calculated scheduled next autostart and the code to set then
`require.Eventually` the updated provisioner LastSeenAt.

Instead we should simply calculate all time values for the stale portion
of the test based on the provisioners LastSeenAt value to avoid such
issues.

Signed-off-by: Callum Styan <callumstyan@gmail.com>
2025-09-01 08:52:34 -07:00
Mathias Fredriksson 3470632db3 feat(cli): add coder exp task delete (#19644)
Fixes coder/internal#897
2025-09-01 11:14:13 +03:00
Cian Johnston 3ac36b8a1e chore(dogfood): gitconfig: allow email change (#19650) 2025-09-01 08:46:46 +01:00
Dean Sheather 39bf3ba628 chore: replace GetManagedAgentCount query with aggregate table (#19636)
- Removes GetManagedAgentCount query
- Adds new table `usage_events_daily` which stores aggregated usage
events by the type and UTC day
- Adds trigger to update the values in this table when a new row is
inserted into `usage_events`
- Adds a migration that adds `usage_events_daily` rows for existing data
in `usage_events`
- Adds tests for the trigger
- Adds tests for the backfill query in the migration

Since the `usage_events` table is unreleased currently, this migration
will do nothing on real deployments and will only affect preview
deployments such as dogfood.

Closes https://github.com/coder/internal/issues/943
2025-08-30 03:39:37 +10:00
Danielle Maywood 433f9c4a38 refactor: modify task creation endpoint to return a task, not workspace (#19637)
Relates to https://github.com/coder/internal/issues/898

Refactor the `POST /api/experimental/tasks/{user}` endpoint to return a
codersdk.Task instead of a codersdk.Workspace
2025-08-29 16:32:19 +01:00
Atif Ali 6e55ed8d08 chore(docs): update external-workspace image (#19608) 2025-08-29 19:55:02 +05:00
Susana Ferreira 353f5dedc1 fix(coderd): fix logic for reporting prebuilt workspace duration metric (#19641)
## Description

When creating a prebuilt workspace, both `flags.IsPrebuild` and
`flags.IsFirstBuild` are true. Previously, the logic rejected cases with
multiple flags, so `coderd_workspace_creation_duration_seconds` wasn’t
updated for prebuilt creations. This is the only valid scenario where
two flags can be true.

## Changes

* Fix logic to update `coderd_workspace_creation_duration_seconds`
metric for prebuilt workspaces.
* Add prebuild helper functions to coderdenttest (other prebuild tests
can reuse this).
* Update workspace's provisionerdmetric tests to include this metric.

Follow-up: https://github.com/coder/coder/pull/19503
Related to: https://github.com/coder/coder/issues/19528
2025-08-29 15:48:48 +01:00
blink-so[bot] 02ecf32afe docs: replace offline deployments terminology to air-gapped (#19625)
This PR comprehensively updates the offline deployments documentation to
use more precise "air-gapped" terminology and improves consistency
throughout the documentation.

## Changes Made

### Terminology Updates
- **Title**: Changed from "Offline Deployments" to "Air-gapped
Deployments"
- **Summary**: Updated to prioritize "air-gapped" terminology and added
"disconnected" to cover additional deployment scenarios
- **Content**: Updated tutorial references to use "air-gapped" instead
of "offline"
- **Section headers**: 
  - Changed "Offline container images" to "Air-gapped container images"
  - Changed "Offline docs" to "Air-gapped docs"
- **Table headers**: Changed "Offline deployments" to "Air-gapped
deployments"

### Navigation & URL Structure
- **Navigation title**: Updated `docs/manifest.json` to show "Air-gapped
Deployments" in sidebar
- **Navigation description**: Updated to "Run Coder in air-gapped /
disconnected / offline environments"
- **File rename**: `docs/install/offline.md` → `docs/install/airgap.md`
for consistency
- **URL change**: `/install/offline` → `/install/airgap`
- **Subsection anchors**:
  - `/install/offline#offline-docs` → `/install/airgap#airgap-docs`
- `/install/offline#offline-container-images` →
`/install/airgap#airgap-container-images`

### Internal Links & References
Updated all internal documentation links:
- `docs/admin/integrations/index.md`
- `docs/admin/networking/index.md`
- `docs/changelogs/v0.27.0.md` (including anchor reference)
- `docs/tutorials/faqs.md`

### Backward Compatibility
- **Redirects**: Added `docs/_redirects` with 301 redirects:
  - `/install/offline` → `/install/airgap`
  - `/install/offline#offline-docs` → `/install/airgap#airgap-docs`
- `/install/offline#offline-container-images` →
`/install/airgap#airgap-container-images`
- **Content**: Maintains "offline" in the description for broader
understanding
- **Deep links**: All subsection anchors redirect properly to maintain
existing bookmarks

## Rationale

- **"Air-gapped"** is more precise and commonly used in
enterprise/security contexts
- **"Disconnected"** covers additional scenarios where networks may be
temporarily or partially isolated
- **Consistency** ensures filename, URL, navigation, content, and
subsection anchors all align with the same terminology
- **Backward compatibility** maintained through comprehensive redirects
to prevent broken links at any level

## Testing

- [x] Verified all internal links point to the new URL structure
- [x] Confirmed navigation title updates correctly
- [x] Ensured content accuracy is maintained
- [x] Added redirects for backward compatibility (main page +
subsections)
- [x] Updated all cross-references in related documentation
- [x] Verified subsection anchor redirects work properly
- [x] Confirmed no unnecessary .md file redirects

## Result

Complete terminology consistency across:
-  Page title and headers
-  Navigation and breadcrumbs  
-  File names and URL structure
-  Internal documentation links
-  Table headers and section titles
-  Subsection anchors and deep links
-  Backward compatibility via comprehensive redirects

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-08-29 09:34:44 -05:00
Mathias Fredriksson e5ac640e5e feat(coderd): add tasks delete endpoint (#19638)
This change adds a DELETE endpoint for tasks (for now, alias of
workspace build delete transition).

Fixes coder/internal#903
2025-08-29 13:54:54 +00:00
Dean Sheather 605dad8b1f fix: suppress license expiry warning if a new license covers the gap (#19601)
Previously, if you had a new license that would start before the current
one fully expired, you would get a warning. Now, the license validity
periods are merged together, and a warning is only generated based on
the end of the current contiguous period of license coverage.

Closes #19498
2025-08-29 13:53:23 +00:00
Danielle Maywood 29a731375e refactor: untangle workspace creation from http logic (#19639)
Coder Tasks requires us to create a workspace, but we want to be able to
return a `codersdk.Task` instead of a `codersdk.Workspace`. This
requires untangling `createWorkspace` from directly writing to
`http.ResponseWriter`.
2025-08-29 14:17:33 +01:00
Hugo Dutka 7365da1110 chore(coderd/database/dbauthz): migrate TestSystemFunctions to mocked DB (#19301)
Related to https://github.com/coder/internal/issues/869

---------

Signed-off-by: Danny Kopping <danny@coder.com>
Co-authored-by: Danny Kopping <danny@coder.com>
2025-08-29 11:04:11 +02:00
Spike Curtis 192c81e8f9 chore: refactor codersdk to use SessionTokenProvider (#19565)
Refactors `codersdk.Client`'s use of session tokens to use a `SessionTokenProvider`, which abstracts the obtaining and storing of the session token.

The main motiviation is to unify Agent authentication an an upstack PR, which can use cloud instance identity via token exchange, rather than a fixed session token.

However, the abstraction could also allow functionality like obtaining the session token from other external sources like the OS credential manager, or an external secret/key management system like Vault.
2025-08-29 10:41:32 +02:00
Ethan f721f3d9d7 chore: add --disable-direct to coder exp scaletest workspace-traffic --ssh (#19632)
Relates to https://github.com/coder/internal/issues/888

As part of our renewed connection scaletesting efforts, we want to
scaletest coder in a scenario where direct connections aren't available
(relatively common for our customers), and all concurrent connections
are relayed via DERP.

This PR adds a flag, `--disable-direct` that can be included on the
existing`coder exp scaletest workspace-traffic -ssh` to disable direct
connections.
2025-08-29 17:02:13 +10:00
Spike Curtis 71ea919c2c chore: upgrade our tailscale fork to address CVE (#19634)
# Update dependencies: Tailscale and xz compression library

This PR updates two dependencies:
- Bumps our fork of Tailscale from
`v1.1.1-0.20250729141742-067f1e5d9716` to
`v1.1.1-0.20250829055033-3536204c8d21`
- Updates the xz compression library from `v0.5.12` to `v0.5.15`
2025-08-29 10:39:35 +04:00
Callum Styan 321c2b8fce fix: fix flake in TestExecutorAutostartSkipsWhenNoProvisionersAvailable (#19478)
The flake here had two causes:
1. related to usage of time.Now() in MustWaitForProvisionersAvailable
and
2. the fact that UpdateProvisionerLastSeenAt can not use a time that is
further in the past than the current LastSeenAt time

Previously the test here was calling
`coderdtest.MustWaitForProvisionersAvailable` which was using `time.Now`
rather than the next tick time like the real `hasProvisionersAvailable`
function does. Additionally, when using `UpdateProvisionerLastSeenAt`
the underlying db query enforces that the time we're trying to set
`LastSeenAt` to cannot be older than the current value.

I was able to reliably reproduce the flake by executing both the
`UpdateProvisionerLastSeenAt` call and `tickCh <- next` in their own
goroutines, the former with a small sleep to reliably ensure we'd
trigger the autobuild before we set the `LastSeenAt` time. That's when I
also noticed that `coderdtest.MustWaitForProvisionersAvailable` was
using `time.Now` instead of the tick time. When I updated that function
to take in a tick time + added a 2nd call to
`UpdateProvisionerLastSeenAt` to set an original non-stale time, we
could then never get the test to pass because the later call to set the
stale time would not actually modify `LastSeenAt`. On top of that,
calling the provisioner daemons closer in the middle of the function
doesn't really do anything of value in this test.

**The fix for the flake is to keep the go routines, ensuring there would
be a flake if there was not a relevant fix, but to include the fix which
is to ensure that we explicitly wait for the provisioner to be stale
before passing the time to `tickCh`.**

---------

Signed-off-by: Callum Styan <callumstyan@gmail.com>
2025-08-28 12:07:50 -07:00
Rafael Rodriguez 95dccf3424 feat: add user filter to templates page to filter by template author (#19561)
## Summary

In this pull request we're adding a user selector dropdown to the
templates page that allows an admin to select a user. The selected user
will be used in the `author:<username>` filter to filter the templates
list by a template author.

Closes: https://github.com/coder/coder/issues/19547

### Changes

Admin View - Can view all users

<img width="1622" height="489" alt="Screenshot 2025-08-26 at 5 24 07 PM"
src="https://github.com/user-attachments/assets/f2ace51e-5834-4bed-bd4f-14c6800816f0"
/>

Admin View - Using the user filter


https://github.com/user-attachments/assets/b4570cca-6dff-45c1-89ab-844f126bdc0f

User view - Cannot view all users

<img width="1617" height="455" alt="Screenshot 2025-08-26 at 5 25 38 PM"
src="https://github.com/user-attachments/assets/f8680acb-d463-4a22-826e-053f0e7dbe21"
/>

### Testing

- Added storybook test for viewing the templates page with a user
dropdown
2025-08-28 13:59:28 -05:00
Danielle Maywood 75b38f12d8 fix(coderd): ignore sub agents when converting a task to workspace (#19624)
Addresses comment raised on previous PR
https://github.com/coder/coder/pull/19619#discussion_r2307943410

We know we can skip sub agents when searching for which agent is related
to the task, as this is not an explicitly supported feature at the
moment. When we come to properly setting up a Task -> Agent relationship
this limitation will be dropped.
2025-08-28 18:27:31 +01:00
Cian Johnston 26e8a35af0 fix(scripts): unset CODER_URL and CODER_SESSION_TOKEN for development server (#19620)
The coder-login module was recently updated to set environment variables
instead of running `coder login`.

This unfortunately broke `develop.sh`:

```
Encountered an error running "coder login", see "coder login --help" for more information
error: Trace=[create api key: ]
```

Unsetting these env vars so that they do not interfere.
2025-08-28 17:42:50 +01:00
Jon Ayers ebfc98df58 chore: move guards to satisfy CodeQL (#19600) 2025-08-28 12:33:51 -04:00
Danielle Maywood 43765864e5 chore: add fields to codersdk.Task (#19619)
Closes https://github.com/coder/internal/issues/949

Adds the following fields to `codersdk.Task`

- OwnerName
- TemplateName
- TemplateDisplayName
- TemplateIcon
- WorkspaceAgentID
- WorkspaceAgentLifecycle
- WorkspaceAgentHealth

The implementation is unfortunately not compatible with multiple agents
as we have no reliable way to tell which agent has the AI task running
in it. For now we just pick the first agent found, but in the future
this will need to be changed.
2025-08-28 17:28:47 +01:00
Brett Kolodny c095e9ca60 fix: set radio item to relative position (#19621)
Closes #19564 


https://github.com/user-attachments/assets/dc70976c-fb46-46ed-92b0-6e0430529fe8
2025-08-28 12:25:40 -04:00
Charlie Voiselle 0aa0986b29 fix: update link to CLI server experiments documentation (#19589)
This pull request makes a minor update to an external documentation link
in the `OverviewPageView` component. The change ensures that users are
directed to the correct reference section for CLI server experiments.

* Updated the `href` attribute in the documentation link to point to
`https://coder.com/docs/reference/cli/server#--experiments` instead of
the previous URL, improving the accuracy of the reference for users.
2025-08-28 12:21:09 -04:00
blink-so[bot] b61a5d7c33 feat: replace the jetbrains-gateway module with the jetbrains toolbox (#19583)
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Atif Ali <atif@coder.com>
2025-08-28 20:49:43 +05:00
Bruno Quaresma abc946c5bd fix: don't show prebuild workspaces as tasks (#19572)
Fixes https://github.com/coder/coder/issues/19570

**Before:**

<img width="2776" height="1274" alt="image"
src="https://github.com/user-attachments/assets/bd260dbf-0868-4e4a-9997-b2fd3c99f33c"
/>

**After:**

<img width="1624" height="970" alt="Screenshot 2025-08-27 at 09 11 31"
src="https://github.com/user-attachments/assets/c85489d8-031c-4cbe-8298-6fee04e30b1f"
/>

**Things to notice:**
- There is a task without a prompt at the end, it should not happen
anymore
- There is no test for this because we mock the API function and the fix
was inside of it. It is a temp solution, the API should be ready to be
used by the FE soon
2025-08-28 12:14:53 -03:00
Susana Ferreira 0ab345ca84 feat: add prebuild timing metrics to Prometheus (#19503)
## Description

This PR introduces one counter and two histograms related to workspace
creation and claiming. The goal is to provide clearer observability into
how workspaces are created (regular vs prebuild) and the time cost of
those operations.

### `coderd_workspace_creation_total`

* Metric type: Counter
* Name: `coderd_workspace_creation_total`
* Labels: `organization_name`, `template_name`, `preset_name`

This counter tracks whether a regular workspace (not created from a
prebuild pool) was created using a preset or not.
Currently, we already expose `coderd_prebuilt_workspaces_claimed_total`
for claimed prebuilt workspaces, but we lack a comparable metric for
regular workspace creations. This metric fills that gap, making it
possible to compare regular creations against claims.

Implementation notes:
* Exposed as a `coderd_` metric, consistent with other workspace-related
metrics (e.g. `coderd_api_workspace_latest_build`:
https://github.com/coder/coder/blob/main/coderd/prometheusmetrics/prometheusmetrics.go#L149).
* Every `defaultRefreshRate` (1 minute ), DB query
`GetRegularWorkspaceCreateMetrics` is executed to fetch all regular
workspaces (not created from a prebuild pool).
* The counter is updated with the total from all time (not just since
metric introduction). This differs from the histograms below, which only
accumulate from their introduction forward.

### `coderd_workspace_creation_duration_seconds` &
`coderd_prebuilt_workspace_claim_duration_seconds`

* Metric types: Histogram
* Names:
  * `coderd_workspace_creation_duration_seconds`
* Labels: `organization_name`, `template_name`, `preset_name`, `type`
(`regular`, `prebuild`)
  * `coderd_prebuilt_workspace_claim_duration_seconds`
    * Labels: `organization_name`, `template_name`, `preset_name`

We already have `coderd_provisionerd_workspace_build_timings_seconds`,
which tracks build run times for all workspace builds handled by the
provisioner daemon.
However, in the context of this issue, we are only interested in
creation and claim build times, not all transitions; additionally, this
metric does not include `preset_name`, and adding it there would
significantly increase cardinality. Therefore, separate more focused
metrics are introduced here:
* `coderd_workspace_creation_duration_seconds`: Build time to create a
workspace (either a regular workspace or the build into a prebuild pool,
for prebuild initial provisioning build).
* `coderd_prebuilt_workspace_claim_duration_seconds`: Time to claim a
prebuilt workspace from the pool.

The reason for two separate histograms is that:
* Creation (regular or prebuild): provisioning builds with similar time
magnitude, generally expected to take longer than a claim operation.
* Claim: expected to be a much faster provisioning build.

#### Native histogram usage

Provisioning times vary widely between projects. Using static buckets
risks unbalanced or poorly informative histograms.
To address this, these metrics use [Prometheus native
histograms](https://prometheus.io/docs/specs/native_histograms/):
* First introduced in Prometheus v2.40.0
* Recommended stable usage from v2.45+
* Requires Go client `prometheus/client_golang` v1.15.0+
* Experimental and must be explicitly enabled on the server
(`--enable-feature=native-histograms`)

For compatibility, we also retain a classic bucket definition (aligned
with the existing provisioner metric:
https://github.com/coder/coder/blob/main/provisionerd/provisionerd.go#L182-L189).
* If native histograms are enabled, Prometheus ingests the
high-resolution histogram.
* If not, it falls back to the predefined buckets.

Implementation notes:
* Unlike the counter, these histograms are updated in real-time at
workspace build job completion.
* They reflect data only from the point of introduction forward (no
historical backfill).

## Relates to 

Closes: https://github.com/coder/coder/issues/19528
Native histograms tested in observability stack:
https://github.com/coder/observability/pull/50
2025-08-28 15:00:26 +01:00
Kacper Sawicki 9fd33a7653 chore(docs): set external workspaces as premium feature in manifest.json (#19615) 2025-08-28 14:51:43 +02:00
Cian Johnston 8d6a322344 chore(docs): document automatic task naming (#19614)
Updates our experimental AI docs on how to automatically generate task
names.

---------

Co-authored-by: Ben Potter <ben@coder.com>
2025-08-28 12:58:36 +01:00
Danielle Maywood 347ab5b348 fix(coderd/taskname): ensure generated name is within 32 byte limit (#19612)
The previous logic verified a generated name was valid, _and then
appended a suffix to it_. This was flawed as it would allow a 32
character name, and then append an extra 5 characters to it.

Instead we now append the suffix _and then_ verify it is valid.
2025-08-28 12:58:02 +01:00
Cian Johnston 8c731a087e chore(coderd/database/dbauthz): refactor TestPing, TestNew, TestInTX to use dbmock (#19604)
Part of https://github.com/coder/internal/issues/869
2025-08-28 12:37:13 +01:00
Ethan 43fe44db50 chore: delete scaletest infrastructure (#19603)
We've successfully migrated the latest iteration of our scaletest
infrastructure (`scaletest/terraform/action`) to
https://github.com/coder/scaletest (private repo). This PR removes the
older iterations, and the scriptsfor spinning up & running the load
generators against that infrastructure (`scaletest.sh`). The tooling for
generating load against a Coder deployment remains untouched, as does
the public documentation for that tooling (i.e. `coder exp scaletest`).

If we ever need that old scaletest Terraform code, it's always in the
git history!
2025-08-28 19:07:50 +10:00
Kacper Sawicki 74fb2aaf08 fix: fix flake in TestPatchCancelWorkspaceBuild/User_is_allowed_to_cancel (#19522)
Fixes: https://github.com/coder/internal/issues/885
2025-08-28 10:24:43 +02:00
Kacper Sawicki 33509f2c20 feat(docs): add docs for external workspaces (#19437)
## Description
This PR introduces documentation for recently merged feature: external
workspaces.

https://github.com/coder/coder/pull/19285
https://github.com/coder/coder/pull/19286
https://github.com/coder/coder/pull/19287
https://github.com/coder/coder/pull/19288

---------

Co-authored-by: Atif Ali <atif@coder.com>
2025-08-28 10:12:08 +02:00
Jon Ayers be40b8ca3e chore: set more explicit guards for serving bin files (#19597) 2025-08-27 19:12:05 -07:00
Jon Ayers 0f1fc88d5a chore: pin devcontainer-cli for .devcontainer config (#19594) 2025-08-27 19:26:47 -04:00
Jon Ayers 252f7d461e chore: pin dependencies in Dockerfiles (#19587)
Fixes up some security issues related to lack of pinned dependencies
2025-08-27 18:41:28 -04:00
dependabot[bot] b729c29ab9 chore: bump coder/cursor/coder from 1.3.1 to 1.3.2 in /dogfood/coder (#19593)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/cursor/coder&package-manager=terraform&previous-version=1.3.1&new-version=1.3.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 22:33:19 +00:00
dependabot[bot] 64c50534e7 chore: bump coder/windsurf/coder from 1.1.1 to 1.2.0 in /dogfood/coder (#19592)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/windsurf/coder&package-manager=terraform&previous-version=1.1.1&new-version=1.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 22:27:04 +00:00
dependabot[bot] dbf42612e2 chore: bump coder/coder-login/coder from 1.0.31 to 1.1.0 in /dogfood/coder-envbuilder (#19590)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/coder-login/coder&package-manager=terraform&previous-version=1.0.31&new-version=1.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 22:06:48 +00:00
dependabot[bot] 58a3cfb9fd chore: bump coder/coder-login/coder from 1.0.31 to 1.1.0 in /dogfood/coder (#19586)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/coder-login/coder&package-manager=terraform&previous-version=1.0.31&new-version=1.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 21:07:48 +00:00
Bruno Quaresma 491977db90 refactor: remove activity column from workspaces table (#19555)
Fixes https://github.com/coder/coder/issues/19504
2025-08-27 15:52:27 -03:00
Andrew Aquino 6c01a772eb feat: show warning in AppLink if hostname is long enough to break port forwarding (#19506)
closes #15178 

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/26d2002a-fa2f-46eb-9c06-b29420123f0a"
/>
2025-08-27 11:51:41 -07:00
dependabot[bot] fe01ae767e chore: bump github.com/aws/aws-sdk-go-v2/config from 1.30.2 to 1.31.3 (#19582)
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.30.2 to 1.31.3.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/e1909a587c354bd1b2962eebaba94c16838669a5"><code>e1909a5</code></a>
Release 2025-08-26</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/2dead494608f76e4d3fe649f643457f224dd434d"><code>2dead49</code></a>
Regenerated Clients</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/8f87507c4d78351202d05ad1d75dcb8b40ad1882"><code>8f87507</code></a>
Update endpoints model</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/9f13166e6c118ee340f5b2e666d44d67141c7327"><code>9f13166</code></a>
Update API model</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/92833dd046ba7e5afe1aafc56d0542c6668b4faf"><code>92833dd</code></a>
drop opsworks and opsworkscm (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/3172">#3172</a>)</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/50d1314f18412311633a2a9d9faec813e3998420"><code>50d1314</code></a>
Release 2025-08-25.2</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/d163c8cb48dcb1bfa07f51c26cb3cbde0d191159"><code>d163c8c</code></a>
Deprecate opsworks/opsworkscm (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/3171">#3171</a>)</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/f0a97a78c219cb6b0ceacfddc8107b850a87aa08"><code>f0a97a7</code></a>
Release 2025-08-25</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/3b73a3be8423cd3e099e3754830ebeefb5518afe"><code>3b73a3b</code></a>
Regenerated Clients</li>
<li><a
href="https://github.com/aws/aws-sdk-go-v2/commit/9c6a548460fe2cbd8a830ea5a6ed6bf62b667d82"><code>9c6a548</code></a>
Update endpoints model</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/v1.30.2...config/v1.31.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.30.2&new-version=1.31.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 18:46:28 +00:00
Jakub Domeracki 0b6f353b99 chore: override version of DOMPurify (#19574)
The [DOMPurify](https://github.com/cure53/DOMPurify) version used by the
latest version of
[monaco-editor](https://github.com/microsoft/monaco-editor) contains [at
least one known
CVE](https://security.snyk.io/package/npm/dompurify/3.1.7)
https://github.com/coder/coder/issues/19445
https://github.com/coder/coder/pull/19446

This PR aims to override the version to resolve security issues:
https://www.npmjs.com/package/dompurify/v/3.2.6
2025-08-27 19:55:57 +02:00
Hugo Dutka cc308d1754 chore(coderd/database/dbauthz): migrate TestWorkspace to mocked DB (#19306)
Related to https://github.com/coder/internal/issues/869

---------

Co-authored-by: Cian Johnston <cian@coder.com>
2025-08-27 19:11:28 +02:00
dependabot[bot] 4c0c7de918 chore: bump coder/claude-code/coder from 2.1.0 to 2.2.0 in /dogfood/coder (#19580)
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=coder/claude-code/coder&package-manager=terraform&previous-version=2.1.0&new-version=2.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 16:49:36 +00:00
Hugo Dutka 2888055782 chore(coderd/database/dbauthz): migrate the ProvisionerJob and Organization tests to mocked DB (#19303)
Related to https://github.com/coder/internal/issues/869

---------

Co-authored-by: Steven Masley <stevenmasley@gmail.com>
2025-08-27 18:30:04 +02:00
Hugo Dutka 88c0edce24 chore(coderd/database/dbauthz): migrate the Notifications and Prebuilds tests to use mocked DB (#19302)
Related to https://github.com/coder/internal/issues/869

---------

Co-authored-by: Steven Masley <stevenmasley@gmail.com>
2025-08-27 18:27:35 +02:00
Hugo Dutka a2945b00fd fix: revert github.com/mark3labs/mcp-go to 0.32.0 (#19578)
This PR reverts github.com/mark3labs/mcp-go to 0.32.0, which was the
version used by https://github.com/coder/coder/pull/18670 that
introduced MCP HTTP support in Coder, and ensures dependabot doesn't
upgrade it automatically.

A bug has been introduced in a recent version of mcp-go that causes some
HTTP MCP requests to fail with the error message

```
[erro]  coderd.mcp: Failed to handle sampling response: no active session found for session mcp-session-e3cb7333-284f-46bd-a009-d611f1b690f6
```

The bug may be related to this issue:
https://github.com/mark3labs/mcp-go/issues/554.
2025-08-27 16:05:44 +00:00
Sas Swart 4e9ee80882 feat(enterprise/coderd): allow system users to be added to groups (#19518)
closes https://github.com/coder/coder/issues/18274

This pull request makes system users visible in various group related
queries so that they can be added to and removed from groups. This
allows system user quotas to be configured. System users are still
ignored in certain queries, such as when license seat consumption is
determined.

This pull request further ensures the existence of a
"coder_prebuilt_workspaces" group in any organization that needs
prebuilt workspaces

---------

Co-authored-by: Susana Ferreira <susana@coder.com>
2025-08-27 16:57:59 +02:00
Cian Johnston dbc6c980b9 fix(coderd): filter out non-task workspaces in api.tasksList (#19559)
Quick fix for following issue in CLI:
```
$ go run ./cmd/coder exp task list
Encountered an error running "coder exp task list", see "coder exp task list --help" for more information
error: Trace=[list tasks: ]
Internal error fetching task prompts and states.
workspace 14d548f4-aaad-40dd-833b-6ffe9c9d31bc is not an AI task workspace
exit status 1
```

This occurs in a short time window directly after creating a new task.

I took a stab at writing a test for this, but ran out of time. I'm not
entirely sure what causes non-AI-task workspaces to be returned in the
query but I suspect it's when a workspace build is pending or running.
2025-08-27 15:32:22 +01:00
Susana Ferreira 5f68807713 chore(dogfood): update workspace lifecycle ignore_changes with env and entrypoint (#19571)
Update the dogfood "Write Coder on Coder" template to ignore env and
entrypoint changes in workspace's lifecycle block according to
https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#template-configuration-best-practices

Related to internal thread:
https://codercom.slack.com/archives/C07GRNNRW03/p1756295446304449
Related to Prebuilt claim notifications
<img width="1320" height="980" alt="Screenshot 2025-08-27 at 13 55 21"
src="https://github.com/user-attachments/assets/b475d057-76c8-4e9d-8e6d-559b292aafe1"
/>
2025-08-27 14:23:44 +01:00
Ethan fcef2ec3a5 test: dial socket when testing coder ssh unix socket forwarding (#19563)
Closes https://github.com/coder/internal/issues/942

The flakey test, `RemoteForwardUnixSocket`, was using `netstat` to check if the unix socket was forwarded properly. In the flake, it looks like netstat was hanging. This PR has `RemoteForwardUnixSocket` be rewritten to match the implementation of `RemoteForwardMultipleUnixSockets`, where we send bytes over the socket in-process instead. More importantly, that test hasn't flaked (yet).

Note: The implementation has been copied directly from the other test, comments and all.
2025-08-27 21:30:54 +10:00
Danielle Maywood 5c2022e08c fix(coderd): fix devcontainer mock recreate flaky test (#19568)
Fix https://github.com/coder/internal/issues/826

I wasn't able to recreate the flake, but my underlying assumption (from
reading the logs we have) is that there is a race condition where the
test will begin cleanup before the dev container recreation goroutine
has a chance to call `devcontainer up`.

I've refactored the test slightly and made it so that the test will not
finish until either the context has timed out, or `Up` has been called.
2025-08-27 12:07:47 +01:00
Cian Johnston bd139f3a43 fix(coderd/provisionerdserver): workaround lack of coder_ai_task resource on stop transition (#19560)
This works around the issue where a task may "disappear" on stop.
Re-using the previous value of `has_ai_task` and `sidebar_app_id` on a
stop transition.

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2025-08-27 10:33:17 +01:00
978 changed files with 50746 additions and 22139 deletions
+5 -1
View File
@@ -1,7 +1,11 @@
#!/bin/sh
install_devcontainer_cli() {
npm install -g @devcontainers/cli@0.80.0 --integrity=sha512-w2EaxgjyeVGyzfA/KUEZBhyXqu/5PyWNXcnrXsZOBrt3aN2zyGiHrXoG54TF6K0b5DSCF01Rt5fnIyrCeFzFKw==
set -e
echo "🔧 Installing DevContainer CLI..."
cd "$(dirname "$0")/../tools/devcontainer-cli"
npm ci --omit=dev
ln -sf "$(pwd)/node_modules/.bin/devcontainer" "$(npm config get prefix)/bin/devcontainer"
}
install_ssh_config() {
+26
View File
@@ -0,0 +1,26 @@
{
"name": "devcontainer-cli",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "devcontainer-cli",
"version": "1.0.0",
"dependencies": {
"@devcontainers/cli": "^0.80.0"
}
},
"node_modules/@devcontainers/cli": {
"version": "0.80.0",
"resolved": "https://registry.npmjs.org/@devcontainers/cli/-/cli-0.80.0.tgz",
"integrity": "sha512-w2EaxgjyeVGyzfA/KUEZBhyXqu/5PyWNXcnrXsZOBrt3aN2zyGiHrXoG54TF6K0b5DSCF01Rt5fnIyrCeFzFKw==",
"bin": {
"devcontainer": "devcontainer.js"
},
"engines": {
"node": "^16.13.0 || >=18.0.0"
}
}
}
}
@@ -0,0 +1,8 @@
{
"name": "devcontainer-cli",
"private": true,
"version": "1.0.0",
"dependencies": {
"@devcontainers/cli": "^0.80.0"
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ description: |
inputs:
version:
description: "The Go version to use."
default: "1.24.6"
default: "1.24.10"
use-preinstalled-go:
description: "Whether to use preinstalled Go."
default: "false"
+1 -1
View File
@@ -16,7 +16,7 @@ runs:
- name: Setup Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: 20.19.4
node-version: 22.19.0
# See https://github.com/actions/setup-node#caching-global-packages-data
cache: "pnpm"
cache-dependency-path: ${{ inputs.directory }}/pnpm-lock.yaml
+5
View File
@@ -33,6 +33,7 @@ updates:
- dependency-name: "*"
update-types:
- version-update:semver-patch
- dependency-name: "github.com/mark3labs/mcp-go"
# Update our Dockerfile.
- package-ecosystem: "docker"
@@ -79,6 +80,9 @@ updates:
mui:
patterns:
- "@mui*"
radix:
patterns:
- "@radix-ui/*"
react:
patterns:
- "react"
@@ -103,6 +107,7 @@ updates:
- dependency-name: "*"
update-types:
- version-update:semver-major
- dependency-name: "@playwright/test"
open-pull-requests-limit: 15
- package-ecosystem: "terraform"
+4
View File
@@ -1 +1,5 @@
<!--
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.
-->
+87 -179
View File
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
- release/*
pull_request:
workflow_dispatch:
@@ -34,12 +35,12 @@ jobs:
tailnet-integration: ${{ steps.filter.outputs.tailnet-integration }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -123,7 +124,7 @@ jobs:
# runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
# steps:
# - name: Checkout
# uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
# uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# with:
# fetch-depth: 1
# # See: https://github.com/stefanzweifel/git-auto-commit-action?tab=readme-ov-file#commits-made-by-this-action-do-not-trigger-new-workflow-runs
@@ -156,12 +157,12 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -190,7 +191,7 @@ jobs:
# Check for any typos
- name: Check for typos
uses: crate-ci/typos@52bd719c2c91f9d676e2aa359fc8e0db8925e6d8 # v1.35.3
uses: crate-ci/typos@85f62a8a84f939ae994ab3763f01a0296d61a7ee # v1.36.2
with:
config: .github/workflows/typos.toml
@@ -203,7 +204,7 @@ jobs:
# Needed for helm chart linting
- name: Install helm
uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
with:
version: v3.9.2
@@ -234,12 +235,12 @@ jobs:
if: ${{ !cancelled() }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -290,12 +291,12 @@ jobs:
timeout-minutes: 7
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -340,7 +341,7 @@ jobs:
- windows-2022
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -366,7 +367,7 @@ jobs:
uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b # v0.1.0
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -531,9 +532,6 @@ jobs:
with:
api-key: ${{ secrets.DATADOG_API_KEY }}
# NOTE: this could instead be defined as a matrix strategy, but we want to
# only block merging if tests on postgres 13 fail. Using a matrix strategy
# here makes the check in the above `required` job rather complicated.
test-go-pg-17:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
needs:
@@ -546,12 +544,12 @@ jobs:
timeout-minutes: 25
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -595,12 +593,12 @@ jobs:
timeout-minutes: 25
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -655,12 +653,12 @@ jobs:
timeout-minutes: 20
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -682,12 +680,12 @@ jobs:
timeout-minutes: 20
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -715,12 +713,12 @@ jobs:
name: ${{ matrix.variant.name }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -787,12 +785,12 @@ jobs:
if: needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true'
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# 👇 Ensures Chromatic can read your full git history
fetch-depth: 0
@@ -808,7 +806,7 @@ jobs:
# the check to pass. This is desired in PRs, but not in mainline.
- name: Publish to Chromatic (non-mainline)
if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
uses: chromaui/action@58d9ffb36c90c97a02d061544ecc849cc4a242a9 # v13.1.3
uses: chromaui/action@20c7e42e1b2f6becd5d188df9acb02f3e2f51519 # v13.2.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
STORYBOOK: true
@@ -840,7 +838,7 @@ jobs:
# infinitely "in progress" in mainline unless we re-review each build.
- name: Publish to Chromatic (mainline)
if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
uses: chromaui/action@58d9ffb36c90c97a02d061544ecc849cc4a242a9 # v13.1.3
uses: chromaui/action@20c7e42e1b2f6becd5d188df9acb02f3e2f51519 # v13.2.0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
STORYBOOK: true
@@ -868,12 +866,12 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# 0 is required here for version.sh to work.
fetch-depth: 0
@@ -922,10 +920,12 @@ jobs:
required:
runs-on: ubuntu-latest
needs:
- changes
- fmt
- lint
- gen
- test-go-pg
- test-go-pg-17
- test-go-race-pg
- test-js
- test-e2e
@@ -937,17 +937,19 @@ jobs:
if: always()
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Ensure required checks
run: | # zizmor: ignore[template-injection] We're just reading needs.x.result here, no risk of injection
echo "Checking required checks"
echo "- changes: ${{ needs.changes.result }}"
echo "- fmt: ${{ needs.fmt.result }}"
echo "- lint: ${{ needs.lint.result }}"
echo "- gen: ${{ needs.gen.result }}"
echo "- test-go-pg: ${{ needs.test-go-pg.result }}"
echo "- test-go-pg-17: ${{ needs.test-go-pg-17.result }}"
echo "- test-go-race-pg: ${{ needs.test-go-race-pg.result }}"
echo "- test-js: ${{ needs.test-js.result }}"
echo "- test-e2e: ${{ needs.test-e2e.result }}"
@@ -968,12 +970,12 @@ jobs:
needs: changes
# We always build the dylibs on Go changes to verify we're not merging unbuildable code,
# but they need only be signed and uploaded on coder/coder main.
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }}
steps:
# Harden Runner doesn't work on macOS
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -996,7 +998,7 @@ jobs:
uses: ./.github/actions/setup-go
- name: Install rcodesign
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }}
run: |
set -euo pipefail
wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
@@ -1007,7 +1009,7 @@ jobs:
rm /tmp/rcodesign.tar.gz
- name: Setup Apple Developer certificate and API key
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }}
run: |
set -euo pipefail
touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
@@ -1028,12 +1030,12 @@ jobs:
make gen/mark-fresh
make build/coder-dylib
env:
CODER_SIGN_DARWIN: ${{ github.ref == 'refs/heads/main' && '1' || '0' }}
CODER_SIGN_DARWIN: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && '1' || '0' }}
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt
- name: Upload build artifacts
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dylibs
@@ -1043,7 +1045,7 @@ jobs:
retention-days: 7
- name: Delete Apple Developer certificate and API key
if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }}
if: ${{ github.repository_owner == 'coder' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }}
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
check-build:
@@ -1055,12 +1057,12 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -1093,7 +1095,7 @@ jobs:
needs:
- changes
- build-dylib
if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-22.04' }}
permissions:
# Necessary to push docker images to ghcr.io.
@@ -1110,12 +1112,12 @@ jobs:
IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -1156,7 +1158,7 @@ jobs:
# Necessary for signing Windows binaries.
- name: Setup Java
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
distribution: "zulu"
java-version: "11.0"
@@ -1189,14 +1191,14 @@ jobs:
# Setup GCloud for signing Windows binaries.
- name: Authenticate to Google Cloud
id: gcloud_auth
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_CODE_SIGNING_SERVICE_ACCOUNT }}
token_format: "access_token"
- name: Setup GCloud SDK
uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # v2.2.0
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Download dylibs
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -1246,40 +1248,45 @@ jobs:
id: build-docker
env:
CODER_IMAGE_BASE: ghcr.io/coder/coder-preview
CODER_IMAGE_TAG_PREFIX: main
DOCKER_CLI_EXPERIMENTAL: "enabled"
run: |
set -euxo pipefail
# build Docker images for each architecture
version="$(./scripts/version.sh)"
tag="main-${version//+/-}"
tag="${version//+/-}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
# build images for each architecture
# note: omitting the -j argument to avoid race conditions when pushing
make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag
# only push if we are on main branch
if [ "${GITHUB_REF}" == "refs/heads/main" ]; then
# only push if we are on main branch or release branch
if [[ "${GITHUB_REF}" == "refs/heads/main" || "${GITHUB_REF}" == refs/heads/release/* ]]; then
# build and push multi-arch manifest, this depends on the other images
# being pushed so will automatically push them
# note: omitting the -j argument to avoid race conditions when pushing
make push/build/coder_"$version"_linux_{amd64,arm64,armv7}.tag
# Define specific tags
tags=("$tag" "main" "latest")
tags=("$tag")
if [ "${GITHUB_REF}" == "refs/heads/main" ]; then
tags+=("main" "latest")
elif [[ "${GITHUB_REF}" == refs/heads/release/* ]]; then
tags+=("release-${GITHUB_REF#refs/heads/release/}")
fi
# Create and push a multi-arch manifest for each tag
# we are adding `latest` tag and keeping `main` for backward
# compatibality
for t in "${tags[@]}"; do
# shellcheck disable=SC2046
./scripts/build_docker_multiarch.sh \
--push \
--target "ghcr.io/coder/coder-preview:$t" \
--version "$version" \
$(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag)
echo "Pushing multi-arch manifest for tag: $t"
# shellcheck disable=SC2046
./scripts/build_docker_multiarch.sh \
--push \
--target "ghcr.io/coder/coder-preview:$t" \
--version "$version" \
$(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag)
done
fi
@@ -1323,7 +1330,7 @@ jobs:
id: attest_main
if: github.ref == 'refs/heads/main'
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: "ghcr.io/coder/coder-preview:main"
predicate-type: "https://slsa.dev/provenance/v1"
@@ -1360,7 +1367,7 @@ jobs:
id: attest_latest
if: github.ref == 'refs/heads/main'
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: "ghcr.io/coder/coder-preview:latest"
predicate-type: "https://slsa.dev/provenance/v1"
@@ -1397,7 +1404,7 @@ jobs:
id: attest_version
if: github.ref == 'refs/heads/main'
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: "ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }}"
predicate-type: "https://slsa.dev/provenance/v1"
@@ -1470,112 +1477,28 @@ jobs:
./build/*.deb
retention-days: 7
# Deploy is handled in deploy.yaml so we can apply concurrency limits.
deploy:
name: "deploy"
runs-on: ubuntu-latest
timeout-minutes: 30
needs:
- changes
- build
if: |
github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/'))
&& needs.changes.outputs.docs-only == 'false'
&& !github.event.pull_request.head.repo.fork
uses: ./.github/workflows/deploy.yaml
with:
image: ${{ needs.build.outputs.IMAGE }}
permissions:
contents: read
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
fetch-depth: 0
persist-credentials: false
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # v2.2.0
- name: Set up Flux CLI
uses: fluxcd/flux2/action@6bf37f6a560fd84982d67f853162e4b3c2235edb # v2.6.4
with:
# Keep this and the github action up to date with the version of flux installed in dogfood cluster
version: "2.5.1"
- name: Get Cluster Credentials
uses: google-github-actions/get-gke-credentials@8e574c49425fa7efed1e74650a449bfa6a23308a # v2.3.4
with:
cluster_name: dogfood-v2
location: us-central1-a
project_id: coder-dogfood-v2
- name: Reconcile Flux
run: |
set -euxo pipefail
flux --namespace flux-system reconcile source git flux-system
flux --namespace flux-system reconcile source git coder-main
flux --namespace flux-system reconcile kustomization flux-system
flux --namespace flux-system reconcile kustomization coder
flux --namespace flux-system reconcile source chart coder-coder
flux --namespace flux-system reconcile source chart coder-coder-provisioner
flux --namespace coder reconcile helmrelease coder
flux --namespace coder reconcile helmrelease coder-provisioner
# Just updating Flux is usually not enough. The Helm release may get
# redeployed, but unless something causes the Deployment to update the
# pods won't be recreated. It's important that the pods get recreated,
# since we use `imagePullPolicy: Always` to ensure we're running the
# latest image.
- name: Rollout Deployment
run: |
set -euxo pipefail
kubectl --namespace coder rollout restart deployment/coder
kubectl --namespace coder rollout status deployment/coder
kubectl --namespace coder rollout restart deployment/coder-provisioner
kubectl --namespace coder rollout status deployment/coder-provisioner
kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged
kubectl --namespace coder rollout status deployment/coder-provisioner-tagged
deploy-wsproxies:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
fetch-depth: 0
persist-credentials: false
- name: Setup flyctl
uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5
- name: Deploy workspace proxies
run: |
flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes
flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes
flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes
flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
IMAGE: ${{ needs.build.outputs.IMAGE }}
TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }}
TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }}
TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }}
TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }}
packages: write # to retag image as dogfood
secrets:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
FLY_PARIS_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }}
FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }}
FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }}
FLY_JNB_CODER_PROXY_SESSION_TOKEN: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }}
# sqlc-vet runs a postgres docker container, runs Coder migrations, and then
# runs sqlc-vet to ensure all queries are valid. This catches any mistakes
@@ -1586,12 +1509,12 @@ jobs:
if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -1614,6 +1537,7 @@ jobs:
steps:
- name: Send Slack notification
run: |
ESCAPED_PROMPT=$(printf "%s" "<@U08TJ4YNCA3> $BLINK_CI_FAILURE_PROMPT" | jq -Rsa .)
curl -X POST -H 'Content-type: application/json' \
--data '{
"blocks": [
@@ -1625,23 +1549,6 @@ jobs:
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'"
},
{
"type": "mrkdwn",
"text": "*Committer:*\n'"${GITHUB_ACTOR}"'"
},
{
"type": "mrkdwn",
"text": "*Commit:*\n'"${GITHUB_SHA}"'"
}
]
},
{
"type": "section",
"text": {
@@ -1653,7 +1560,7 @@ jobs:
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<@U08TJ4YNCA3> investigate this CI failure. Check logs, search for existing issues, use git blame to find who last modified failing tests, create issue in coder/internal (not public repo), use title format \"flake: TestName\" for flaky tests, and assign to the person from git blame."
"text": '"$ESCAPED_PROMPT"'
}
}
]
@@ -1661,3 +1568,4 @@ jobs:
env:
SLACK_WEBHOOK: ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }}
RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
BLINK_CI_FAILURE_PROMPT: ${{ vars.BLINK_CI_FAILURE_PROMPT }}
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
if: ${{ github.event_name == 'pull_request_target' && !github.event.pull_request.draft }}
steps:
- name: release-labels
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
# This script ensures PR title and labels are in sync:
#
+170
View File
@@ -0,0 +1,170 @@
name: deploy
on:
# Via workflow_call, called from ci.yaml
workflow_call:
inputs:
image:
description: "Image and tag to potentially deploy. Current branch will be validated against should-deploy check."
required: true
type: string
secrets:
FLY_API_TOKEN:
required: true
FLY_PARIS_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_JNB_CODER_PROXY_SESSION_TOKEN:
required: true
permissions:
contents: read
concurrency:
group: ${{ github.workflow }} # no per-branch concurrency
cancel-in-progress: false
jobs:
# Determines if the given branch should be deployed to dogfood.
should-deploy:
name: should-deploy
runs-on: ubuntu-latest
outputs:
verdict: ${{ steps.check.outputs.verdict }} # DEPLOY or NOOP
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Check if deploy is enabled
id: check
run: |
set -euo pipefail
verdict="$(./scripts/should_deploy.sh)"
echo "verdict=$verdict" >> "$GITHUB_OUTPUT"
deploy:
name: "deploy"
runs-on: ubuntu-latest
timeout-minutes: 30
needs: should-deploy
if: needs.should-deploy.outputs.verdict == 'DEPLOY'
permissions:
contents: read
id-token: write
packages: write # to retag image as dogfood
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: GHCR Login
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Set up Flux CLI
uses: fluxcd/flux2/action@6bf37f6a560fd84982d67f853162e4b3c2235edb # v2.6.4
with:
# Keep this and the github action up to date with the version of flux installed in dogfood cluster
version: "2.7.0"
- name: Get Cluster Credentials
uses: google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395 # v3.0.0
with:
cluster_name: dogfood-v2
location: us-central1-a
project_id: coder-dogfood-v2
# Retag image as dogfood while maintaining the multi-arch manifest
- name: Tag image as dogfood
run: docker buildx imagetools create --tag "ghcr.io/coder/coder-preview:dogfood" "$IMAGE"
env:
IMAGE: ${{ inputs.image }}
- name: Reconcile Flux
run: |
set -euxo pipefail
flux --namespace flux-system reconcile source git flux-system
flux --namespace flux-system reconcile source git coder-main
flux --namespace flux-system reconcile kustomization flux-system
flux --namespace flux-system reconcile kustomization coder
flux --namespace flux-system reconcile source chart coder-coder
flux --namespace flux-system reconcile source chart coder-coder-provisioner
flux --namespace coder reconcile helmrelease coder
flux --namespace coder reconcile helmrelease coder-provisioner
# Just updating Flux is usually not enough. The Helm release may get
# redeployed, but unless something causes the Deployment to update the
# pods won't be recreated. It's important that the pods get recreated,
# since we use `imagePullPolicy: Always` to ensure we're running the
# latest image.
- name: Rollout Deployment
run: |
set -euxo pipefail
kubectl --namespace coder rollout restart deployment/coder
kubectl --namespace coder rollout status deployment/coder
kubectl --namespace coder rollout restart deployment/coder-provisioner
kubectl --namespace coder rollout status deployment/coder-provisioner
kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged
kubectl --namespace coder rollout status deployment/coder-provisioner-tagged
deploy-wsproxies:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Setup flyctl
uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5
- name: Deploy workspace proxies
run: |
flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes
flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes
flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes
flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
IMAGE: ${{ inputs.image }}
TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }}
TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }}
TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }}
TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }}
+3 -3
View File
@@ -38,12 +38,12 @@ jobs:
if: github.repository_owner == 'coder'
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -62,7 +62,7 @@ jobs:
# This uses OIDC authentication, so no auth variables are required.
- name: Build base Docker image via depot.dev
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2
with:
project: wl5hnrrkns
context: base-build-context
+2 -2
View File
@@ -23,14 +23,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Setup Node
uses: ./.github/actions/setup-node
- uses: tj-actions/changed-files@f963b3f3562b00b6d2dd25efc390eb04e51ef6c6 # v45.0.7
- uses: tj-actions/changed-files@4563c729c555b4141fac99c80f699f571219b836 # v45.0.7
id: changed-files
with:
files: |
+7 -7
View File
@@ -26,17 +26,17 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Setup Nix
uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v32
uses: nixbuild/nix-quick-install-action@1f095fee853b33114486cfdeae62fa099cda35a9 # v33
with:
# Pinning to 2.28 here, as Nix gets a "error: [json.exception.type_error.302] type must be array, but is string"
# on version 2.29 and above.
@@ -88,7 +88,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push Non-Nix image
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2
with:
project: b4q6ltmpzh
token: ${{ secrets.DEPOT_TOKEN }}
@@ -125,12 +125,12 @@ jobs:
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -138,7 +138,7 @@ jobs:
uses: ./.github/actions/setup-tf
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
+6 -21
View File
@@ -27,7 +27,7 @@ jobs:
- windows-2022
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -53,7 +53,7 @@ jobs:
uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b # v0.1.0
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
@@ -143,7 +143,7 @@ jobs:
DB=ci gotestsum \
--format standard-quiet --packages "./..." \
-- -timeout=20m -v -p $NUM_PARALLEL_PACKAGES -parallel=$NUM_PARALLEL_TESTS $TESTCOUNT
-- -timeout=20m -v -p "$NUM_PARALLEL_PACKAGES" -parallel="$NUM_PARALLEL_TESTS" "$TESTCOUNT"
- name: Upload Embedded Postgres Cache
uses: ./.github/actions/embedded-pg-cache/upload
@@ -170,6 +170,7 @@ jobs:
steps:
- name: Send Slack notification
run: |
ESCAPED_PROMPT=$(printf "%s" "<@U08TJ4YNCA3> $BLINK_CI_FAILURE_PROMPT" | jq -Rsa .)
curl -X POST -H 'Content-type: application/json' \
--data '{
"blocks": [
@@ -181,23 +182,6 @@ jobs:
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Workflow:*\n'"${GITHUB_WORKFLOW}"'"
},
{
"type": "mrkdwn",
"text": "*Committer:*\n'"${GITHUB_ACTOR}"'"
},
{
"type": "mrkdwn",
"text": "*Commit:*\n'"${GITHUB_SHA}"'"
}
]
},
{
"type": "section",
"text": {
@@ -209,7 +193,7 @@ jobs:
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<@U08TJ4YNCA3> investigate this CI failure. Check logs, search for existing issues, use git blame to find who last modified failing tests, create issue in coder/internal (not public repo), use title format \"flake: TestName\" for flaky tests, and assign to the person from git blame."
"text": '"$ESCAPED_PROMPT"'
}
}
]
@@ -217,3 +201,4 @@ jobs:
env:
SLACK_WEBHOOK: ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }}
RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
BLINK_CI_FAILURE_PROMPT: ${{ vars.BLINK_CI_FAILURE_PROMPT }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
packages: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
+10 -9
View File
@@ -39,12 +39,12 @@ jobs:
PR_OPEN: ${{ steps.check_pr.outputs.pr_open }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -76,12 +76,12 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -184,7 +184,7 @@ jobs:
pull-requests: write # needed for commenting on PRs
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -228,12 +228,12 @@ jobs:
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -288,7 +288,7 @@ jobs:
PR_HOSTNAME: "pr${{ needs.get_info.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -337,7 +337,7 @@ jobs:
kubectl create namespace "pr${PR_NUMBER}"
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -370,6 +370,7 @@ jobs:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install coder-db bitnami/postgresql \
--namespace "pr${PR_NUMBER}" \
--set image.repository=bitnamilegacy/postgresql \
--set auth.username=coder \
--set auth.password=coder \
--set auth.database=coder \
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
+18 -18
View File
@@ -37,7 +37,7 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Allow only maintainers/admins
uses: actions/github-script@v7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -65,7 +65,7 @@ jobs:
steps:
# Harden Runner doesn't work on macOS.
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -164,12 +164,12 @@ jobs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -253,7 +253,7 @@ jobs:
# Necessary for signing Windows binaries.
- name: Setup Java
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
distribution: "zulu"
java-version: "11.0"
@@ -317,14 +317,14 @@ jobs:
# Setup GCloud for signing Windows binaries.
- name: Authenticate to Google Cloud
id: gcloud_auth
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_CODE_SIGNING_SERVICE_ACCOUNT }}
token_format: "access_token"
- name: Setup GCloud SDK
uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # v2.2.0
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Download dylibs
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -397,7 +397,7 @@ jobs:
# This uses OIDC authentication, so no auth variables are required.
- name: Build base Docker image via depot.dev
if: steps.image-base-tag.outputs.tag != ''
uses: depot/build-push-action@2583627a84956d07561420dcc1d0eb1f2af3fac0 # v1.15.0
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2
with:
project: wl5hnrrkns
context: base-build-context
@@ -454,7 +454,7 @@ jobs:
id: attest_base
if: ${{ !inputs.dry_run && steps.image-base-tag.outputs.tag != '' }}
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: ${{ steps.image-base-tag.outputs.tag }}
predicate-type: "https://slsa.dev/provenance/v1"
@@ -570,7 +570,7 @@ jobs:
id: attest_main
if: ${{ !inputs.dry_run }}
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: ${{ steps.build_docker.outputs.multiarch_image }}
predicate-type: "https://slsa.dev/provenance/v1"
@@ -614,7 +614,7 @@ jobs:
id: attest_latest
if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }}
continue-on-error: true
uses: actions/attest@ce27ba3b4a9a139d9a20a4a07d69fabb52f1e5bc # v2.4.0
uses: actions/attest@daf44fb950173508f38bd2406030372c1d1162b1 # v3.0.0
with:
subject-name: ${{ steps.latest_tag.outputs.tag }}
predicate-type: "https://slsa.dev/provenance/v1"
@@ -734,13 +734,13 @@ jobs:
CREATED_LATEST_TAG: ${{ steps.build_docker.outputs.created_latest_tag }}
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5 # v2.1.12
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Setup GCloud SDK
uses: google-github-actions/setup-gcloud@cb1e50a9932213ecece00a606661ae9ca44f3397 # 2.2.0
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # 3.0.1
- name: Publish Helm Chart
if: ${{ !inputs.dry_run }}
@@ -802,7 +802,7 @@ jobs:
# TODO: skip this if it's not a new release (i.e. a backport). This is
# fine right now because it just makes a PR that we can close.
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -878,7 +878,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
@@ -888,7 +888,7 @@ jobs:
GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -971,12 +971,12 @@ jobs:
if: ${{ !inputs.dry_run }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 1
persist-credentials: false
+3 -3
View File
@@ -20,12 +20,12 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
with:
sarif_file: results.sarif
+8 -8
View File
@@ -27,12 +27,12 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -40,7 +40,7 @@ jobs:
uses: ./.github/actions/setup-go
- name: Initialize CodeQL
uses: github/codeql-action/init@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
with:
languages: go, javascript
@@ -50,7 +50,7 @@ jobs:
rm Makefile
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
- name: Send Slack notification on failure
if: ${{ failure() }}
@@ -69,12 +69,12 @@ jobs:
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
@@ -146,7 +146,7 @@ jobs:
echo "image=$(cat "$image_job")" >> "$GITHUB_OUTPUT"
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
with:
image-ref: ${{ steps.build.outputs.image }}
format: sarif
@@ -154,7 +154,7 @@ jobs:
severity: "CRITICAL,HIGH"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
with:
sarif_file: trivy-results.sarif
category: "Trivy"
+6 -6
View File
@@ -18,12 +18,12 @@ jobs:
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: stale
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
with:
stale-issue-label: "stale"
stale-pr-label: "stale"
@@ -44,7 +44,7 @@ jobs:
# Start with the oldest issues, always.
ascending: true
- name: "Close old issues labeled likely-no"
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -96,12 +96,12 @@ jobs:
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Run delete-old-branches-action
@@ -120,7 +120,7 @@ jobs:
actions: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
+185
View File
@@ -0,0 +1,185 @@
name: AI Triage Automation
on:
workflow_dispatch:
inputs:
issue_url:
description: "GitHub Issue URL to process"
required: true
type: string
template_name:
description: "Coder template to use for workspace"
required: true
default: "traiage"
type: string
template_preset:
description: "Template preset to use"
required: true
default: "Default"
type: string
prefix:
description: "Prefix for workspace name"
required: false
default: "traiage"
type: string
cleanup:
description: "Cleanup workspace after triage."
required: false
default: false
type: boolean
jobs:
traiage:
name: Triage GitHub Issue with Claude Code
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
TEMPLATE_NAME: ${{ inputs.template_name }}
permissions:
contents: read
issues: write
actions: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Extract context key from issue
id: extract-context
env:
ISSUE_URL: ${{ inputs.issue_url }}
GITHUB_TOKEN: ${{ github.token }}
run: |
issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')"
context_key="gh-${issue_number}"
echo "context_key=${context_key}" >> "${GITHUB_OUTPUT}"
echo "CONTEXT_KEY=${context_key}" >> "${GITHUB_ENV}"
- name: Download and install Coder binary
shell: bash
env:
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
run: |
if [ "${{ runner.arch }}" == "ARM64" ]; then
ARCH="arm64"
else
ARCH="amd64"
fi
mkdir -p "${HOME}/.local/bin"
curl -fsSL --compressed "$CODER_URL/bin/coder-linux-${ARCH}" -o "${HOME}/.local/bin/coder"
chmod +x "${HOME}/.local/bin/coder"
export PATH="$HOME/.local/bin:$PATH"
coder version
coder whoami
echo "$HOME/.local/bin" >> "${GITHUB_PATH}"
- name: Get Coder username from GitHub actor
id: get-coder-username
env:
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
GITHUB_USER_ID: ${{
(github.event_name == 'workflow_dispatch' && github.actor_id)
}}
run: |
[[ -z "${GITHUB_USER_ID}" || "${GITHUB_USER_ID}" == "null" ]] && echo "No GitHub actor ID found" && exit 1
user_json=$(
coder users list --github-user-id="${GITHUB_USER_ID}" --output=json
)
coder_username=$(jq -r 'first | .username' <<< "$user_json")
[[ -z "${coder_username}" || "${coder_username}" == "null" ]] && echo "No Coder user with GitHub user ID ${GITHUB_USER_ID} found" && exit 1
echo "coder_username=${coder_username}" >> "${GITHUB_OUTPUT}"
# TODO(Cian): this is a good use-case for 'recipes'
- name: Create Coder task
id: create-task
env:
CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }}
CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }}
GITHUB_TOKEN: ${{ github.token }}
ISSUE_URL: ${{ inputs.issue_url }}
PREFIX: ${{ inputs.prefix }}
RUN_ID: ${{ github.run_id }}
TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }}
TEMPLATE_PRESET: ${{ inputs.template_preset }}
run: |
# Fetch issue description using `gh` CLI
issue_description=$(gh issue view "${ISSUE_URL}")
# Write a prompt to PROMPT_FILE
PROMPT=$(cat <<EOF
Analyze the below GitHub issue description, understand the root cause, and make appropriate changes to resolve the issue.
ISSUE URL: ${ISSUE_URL}
ISSUE DESCRIPTION BELOW:
${issue_description}
EOF
)
export PROMPT
export TASK_NAME="${PREFIX}-${CONTEXT_KEY}-${RUN_ID}"
echo "Creating task: $TASK_NAME"
./scripts/traiage.sh create
coder exp task status "${CODER_USERNAME}/$TASK_NAME" --watch
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}"
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}"
- name: Create and upload archive
id: create-archive
if: inputs.cleanup
env:
BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage"
run: |
echo "Creating archive for workspace: $TASK_NAME"
./scripts/traiage.sh archive
echo "archive_url=${BUCKET_PREFIX%%/}/$TASK_NAME.tar.gz" >> "${GITHUB_OUTPUT}"
- name: Generate a summary of the changes and post a comment on GitHub.
id: generate-summary
if: inputs.cleanup
env:
ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }}
BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage"
CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }}
GITHUB_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
ISSUE_URL: ${{ inputs.issue_url }}
TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }}
run: |
SUMMARY_FILE=$(mktemp)
trap 'rm -f "${SUMMARY_FILE}"' EXIT
AUTO_SUMMARY=$(./scripts/traiage.sh summary)
{
echo "## TrAIage Results"
echo "- **Issue URL:** ${ISSUE_URL}"
echo "- **Context Key:** ${CONTEXT_KEY}"
echo "- **Workspace:** ${TASK_NAME}"
echo "- **Archive URL:** ${ARCHIVE_URL}"
echo
echo "${AUTO_SUMMARY}"
echo
echo "To fetch the output to your own workspace:"
echo
echo '```bash'
echo "BUCKET_PREFIX=${BUCKET_PREFIX} TASK_NAME=${TASK_NAME} ./scripts/traiage.sh resume"
echo '```'
echo
} >> "${SUMMARY_FILE}"
if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then
gh issue comment "${ISSUE_URL}" --body-file "${SUMMARY_FILE}" --create-if-none --edit-last
else
echo "Skipping comment on other repo."
fi
cat "${SUMMARY_FILE}" >> "${GITHUB_STEP_SUMMARY}"
- name: Cleanup task
if: inputs.cleanup && steps.create-task.outputs.TASK_NAME != '' && steps.create-archive.outputs.archive_url != ''
run: |
echo "Cleaning up task: $TASK_NAME"
./scripts/traiage.sh delete || true
+1
View File
@@ -1,5 +1,6 @@
[default]
extend-ignore-identifiers-re = ["gho_.*"]
extend-ignore-re = ["(#|//)\\s*spellchecker:ignore-next-line\\n.*"]
[default.extend-identifiers]
alog = "alog"
+2 -2
View File
@@ -21,12 +21,12 @@ jobs:
pull-requests: write # required to post PR review comments by the action
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
+4
View File
@@ -0,0 +1,4 @@
rules:
cache-poisoning:
ignore:
- "ci.yaml:184"
+1
View File
@@ -54,6 +54,7 @@
}
},
"tailwindCSS.classFunctions": ["cva", "cn"],
"[css][html][markdown][yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
+55 -4
View File
@@ -561,7 +561,7 @@ endif
# Note: we don't run zizmor in the lint target because it takes a while. CI
# runs it explicitly.
lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/actions/actionlint
lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/actions/actionlint lint/check-scopes
.PHONY: lint
lint/site-icons:
@@ -614,6 +614,11 @@ lint/actions/zizmor:
.
.PHONY: lint/actions/zizmor
# Verify api_key_scope enum contains all RBAC <resource>:<action> values.
lint/check-scopes: coderd/database/dump.sql
go run ./scripts/check-scopes
.PHONY: lint/check-scopes
# All files generated by the database should be added here, and this can be used
# as a target for jobs that need to run after the database is generated.
DB_GEN_FILES := \
@@ -630,16 +635,23 @@ TAILNETTEST_MOCKS := \
tailnet/tailnettest/workspaceupdatesprovidermock.go \
tailnet/tailnettest/subscriptionmock.go
AIBRIDGED_MOCKS := \
enterprise/x/aibridged/aibridgedmock/clientmock.go \
enterprise/x/aibridged/aibridgedmock/poolmock.go
GEN_FILES := \
tailnet/proto/tailnet.pb.go \
agent/proto/agent.pb.go \
provisionersdk/proto/provisioner.pb.go \
provisionerd/proto/provisionerd.pb.go \
vpn/vpn.pb.go \
enterprise/x/aibridged/proto/aibridged.pb.go \
$(DB_GEN_FILES) \
$(SITE_GEN_FILES) \
coderd/rbac/object_gen.go \
codersdk/rbacresources_gen.go \
coderd/rbac/scopes_constants_gen.go \
codersdk/apikey_scopes_gen.go \
docs/admin/integrations/prometheus.md \
docs/reference/cli/index.md \
docs/admin/security/audit-logs.md \
@@ -653,7 +665,8 @@ GEN_FILES := \
agent/agentcontainers/acmock/acmock.go \
agent/agentcontainers/dcspec/dcspec_gen.go \
coderd/httpmw/loggermw/loggermock/loggermock.go \
codersdk/workspacesdk/agentconnmock/agentconnmock.go
codersdk/workspacesdk/agentconnmock/agentconnmock.go \
$(AIBRIDGED_MOCKS)
# all gen targets should be added here and to gen/mark-fresh
gen: gen/db gen/golden-files $(GEN_FILES)
@@ -683,11 +696,13 @@ gen/mark-fresh:
provisionersdk/proto/provisioner.pb.go \
provisionerd/proto/provisionerd.pb.go \
vpn/vpn.pb.go \
enterprise/x/aibridged/proto/aibridged.pb.go \
coderd/database/dump.sql \
$(DB_GEN_FILES) \
site/src/api/typesGenerated.ts \
coderd/rbac/object_gen.go \
codersdk/rbacresources_gen.go \
coderd/rbac/scopes_constants_gen.go \
site/src/api/rbacresourcesGenerated.ts \
site/src/api/countriesGenerated.ts \
docs/admin/integrations/prometheus.md \
@@ -704,6 +719,7 @@ gen/mark-fresh:
agent/agentcontainers/dcspec/dcspec_gen.go \
coderd/httpmw/loggermw/loggermock/loggermock.go \
codersdk/workspacesdk/agentconnmock/agentconnmock.go \
$(AIBRIDGED_MOCKS) \
"
for file in $$files; do
@@ -751,6 +767,10 @@ codersdk/workspacesdk/agentconnmock/agentconnmock.go: codersdk/workspacesdk/agen
go generate ./codersdk/workspacesdk/agentconnmock/
touch "$@"
$(AIBRIDGED_MOCKS): enterprise/x/aibridged/client.go enterprise/x/aibridged/pool.go
go generate ./enterprise/x/aibridged/aibridgedmock/
touch "$@"
agent/agentcontainers/dcspec/dcspec_gen.go: \
node_modules/.installed \
agent/agentcontainers/dcspec/devContainer.base.schema.json \
@@ -801,6 +821,14 @@ vpn/vpn.pb.go: vpn/vpn.proto
--go_opt=paths=source_relative \
./vpn/vpn.proto
enterprise/x/aibridged/proto/aibridged.pb.go: enterprise/x/aibridged/proto/aibridged.proto
protoc \
--go_out=. \
--go_opt=paths=source_relative \
--go-drpc_out=. \
--go-drpc_opt=paths=source_relative \
./enterprise/x/aibridged/proto/aibridged.proto
site/src/api/typesGenerated.ts: site/node_modules/.installed $(wildcard scripts/apitypings/*) $(shell find ./codersdk $(FIND_EXCLUSIONS) -type f -name '*.go')
# -C sets the directory for the go run command
go run -C ./scripts/apitypings main.go > $@
@@ -827,6 +855,15 @@ coderd/rbac/object_gen.go: scripts/typegen/rbacobject.gotmpl scripts/typegen/mai
rmdir -v "$$tempdir"
touch "$@"
coderd/rbac/scopes_constants_gen.go: scripts/typegen/scopenames.gotmpl scripts/typegen/main.go coderd/rbac/policy/policy.go
# Generate typed low-level ScopeName constants from RBACPermissions
# Write to a temp file first to avoid truncating the package during build
# since the generator imports the rbac package.
tempfile=$(shell mktemp /tmp/scopes_constants_gen.XXXXXX)
go run ./scripts/typegen/main.go rbac scopenames > "$$tempfile"
mv -v "$$tempfile" coderd/rbac/scopes_constants_gen.go
touch "$@"
codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go
# Do no overwrite codersdk/rbacresources_gen.go directly, as it would make the file empty, breaking
# the `codersdk` package and any parallel build targets.
@@ -834,6 +871,12 @@ codersdk/rbacresources_gen.go: scripts/typegen/codersdk.gotmpl scripts/typegen/m
mv /tmp/rbacresources_gen.go codersdk/rbacresources_gen.go
touch "$@"
codersdk/apikey_scopes_gen.go: scripts/apikeyscopesgen/main.go coderd/rbac/scopes_catalog.go coderd/rbac/scopes.go
# Generate SDK constants for external API key scopes.
go run ./scripts/apikeyscopesgen > /tmp/apikey_scopes_gen.go
mv /tmp/apikey_scopes_gen.go codersdk/apikey_scopes_gen.go
touch "$@"
site/src/api/rbacresourcesGenerated.ts: site/node_modules/.installed scripts/typegen/codersdk.gotmpl scripts/typegen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go
go run scripts/typegen/main.go rbac typescript > "$@"
(cd site/ && pnpm exec biome format --write src/api/rbacresourcesGenerated.ts)
@@ -977,11 +1020,19 @@ endif
TEST_PACKAGES ?= ./...
test:
warm-go-cache-db-cleaner:
# ensure Go's build cache for the cleanercmd is fresh so that tests don't have to build from scratch. This
# could take some time and counts against the test's timeout, which can lead to flakes.
# c.f. https://github.com/coder/internal/issues/1026
mkdir -p build
$(GIT_FLAGS) go build -o ./build/cleaner github.com/coder/coder/v2/coderd/database/dbtestutil/cleanercmd
.PHONY: warm-go-cache-db-cleaner
test: warm-go-cache-db-cleaner
$(GIT_FLAGS) gotestsum --format standard-quiet $(GOTESTSUM_RETRY_FLAGS) --packages="$(TEST_PACKAGES)" -- $(GOTEST_FLAGS)
.PHONY: test
test-cli:
test-cli: warm-go-cache-db-cleaner
$(MAKE) test TEST_PACKAGES="./cli..."
.PHONY: test-cli
+4 -14
View File
@@ -74,7 +74,6 @@ type Options struct {
LogDir string
TempDir string
ScriptDataDir string
ExchangeToken func(ctx context.Context) (string, error)
Client Client
ReconnectingPTYTimeout time.Duration
EnvironmentVariables map[string]string
@@ -99,6 +98,7 @@ type Client interface {
proto.DRPCAgentClient26, tailnetproto.DRPCTailnetClient26, error,
)
tailnet.DERPMapRewriter
agentsdk.RefreshableSessionTokenProvider
}
type Agent interface {
@@ -131,11 +131,6 @@ func New(options Options) Agent {
}
options.ScriptDataDir = options.TempDir
}
if options.ExchangeToken == nil {
options.ExchangeToken = func(_ context.Context) (string, error) {
return "", nil
}
}
if options.ReportMetadataInterval == 0 {
options.ReportMetadataInterval = time.Second
}
@@ -172,7 +167,6 @@ func New(options Options) Agent {
coordDisconnected: make(chan struct{}),
environmentVariables: options.EnvironmentVariables,
client: options.Client,
exchangeToken: options.ExchangeToken,
filesystem: options.Filesystem,
logDir: options.LogDir,
tempDir: options.TempDir,
@@ -203,7 +197,6 @@ func New(options Options) Agent {
// coordinator during shut down.
close(a.coordDisconnected)
a.announcementBanners.Store(new([]codersdk.BannerConfig))
a.sessionToken.Store(new(string))
a.init()
return a
}
@@ -212,7 +205,6 @@ type agent struct {
clock quartz.Clock
logger slog.Logger
client Client
exchangeToken func(ctx context.Context) (string, error)
tailnetListenPort uint16
filesystem afero.Fs
logDir string
@@ -254,7 +246,6 @@ type agent struct {
scriptRunner *agentscripts.Runner
announcementBanners atomic.Pointer[[]codersdk.BannerConfig] // announcementBanners is atomic because it is periodically updated.
announcementBannersRefreshInterval time.Duration
sessionToken atomic.Pointer[string]
sshServer *agentssh.Server
sshMaxTimeout time.Duration
blockFileTransfer bool
@@ -916,11 +907,10 @@ func (a *agent) run() (retErr error) {
// This allows the agent to refresh its token if necessary.
// For instance identity this is required, since the instance
// may not have re-provisioned, but a new agent ID was created.
sessionToken, err := a.exchangeToken(a.hardCtx)
err := a.client.RefreshToken(a.hardCtx)
if err != nil {
return xerrors.Errorf("exchange token: %w", err)
return xerrors.Errorf("refresh token: %w", err)
}
a.sessionToken.Store(&sessionToken)
// ConnectRPC returns the dRPC connection we use for the Agent and Tailnet v2+ APIs
aAPI, tAPI, err := a.client.ConnectRPC26(a.hardCtx)
@@ -1359,7 +1349,7 @@ func (a *agent) updateCommandEnv(current []string) (updated []string, err error)
"CODER_WORKSPACE_OWNER_NAME": manifest.OwnerName,
// Specific Coder subcommands require the agent token exposed!
"CODER_AGENT_TOKEN": *a.sessionToken.Load(),
"CODER_AGENT_TOKEN": a.client.GetSessionToken(),
// Git on Windows resolves with UNIX-style paths.
// If using backslashes, it's unable to find the executable.
+12 -22
View File
@@ -22,7 +22,6 @@ import (
"slices"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
@@ -2926,11 +2925,11 @@ func TestAgent_Speedtest(t *testing.T) {
func TestAgent_Reconnect(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
logger := testutil.Logger(t)
// After the agent is disconnected from a coordinator, it's supposed
// to reconnect!
coordinator := tailnet.NewCoordinator(logger)
defer coordinator.Close()
fCoordinator := tailnettest.NewFakeCoordinator()
agentID := uuid.New()
statsCh := make(chan *proto.Stats, 50)
@@ -2942,27 +2941,24 @@ func TestAgent_Reconnect(t *testing.T) {
DERPMap: derpMap,
},
statsCh,
coordinator,
fCoordinator,
)
defer client.Close()
initialized := atomic.Int32{}
closer := agent.New(agent.Options{
ExchangeToken: func(ctx context.Context) (string, error) {
initialized.Add(1)
return "", nil
},
Client: client,
Logger: logger.Named("agent"),
})
defer closer.Close()
require.Eventually(t, func() bool {
return coordinator.Node(agentID) != nil
}, testutil.WaitShort, testutil.IntervalFast)
client.LastWorkspaceAgent()
require.Eventually(t, func() bool {
return initialized.Load() == 2
}, testutil.WaitShort, testutil.IntervalFast)
call1 := testutil.RequireReceive(ctx, t, fCoordinator.CoordinateCalls)
require.Equal(t, client.GetNumRefreshTokenCalls(), 1)
close(call1.Resps) // hang up
// expect reconnect
testutil.RequireReceive(ctx, t, fCoordinator.CoordinateCalls)
// Check that the agent refreshes the token when it reconnects.
require.Equal(t, client.GetNumRefreshTokenCalls(), 2)
closer.Close()
}
func TestAgent_WriteVSCodeConfigs(t *testing.T) {
@@ -2984,9 +2980,6 @@ func TestAgent_WriteVSCodeConfigs(t *testing.T) {
defer client.Close()
filesystem := afero.NewMemMapFs()
closer := agent.New(agent.Options{
ExchangeToken: func(ctx context.Context) (string, error) {
return "", nil
},
Client: client,
Logger: logger.Named("agent"),
Filesystem: filesystem,
@@ -3015,9 +3008,6 @@ func TestAgent_DebugServer(t *testing.T) {
conn, _, _, _, agnt := setupAgent(t, agentsdk.Manifest{
DERPMap: derpMap,
}, 0, func(c *agenttest.Client, o *agent.Options) {
o.ExchangeToken = func(context.Context) (string, error) {
return "token", nil
}
o.LogDir = logDir
})
+1 -9
View File
@@ -1,7 +1,6 @@
package agenttest
import (
"context"
"net/url"
"testing"
@@ -31,18 +30,11 @@ func New(t testing.TB, coderURL *url.URL, agentToken string, opts ...func(*agent
}
if o.Client == nil {
agentClient := agentsdk.New(coderURL)
agentClient.SetSessionToken(agentToken)
agentClient := agentsdk.New(coderURL, agentsdk.WithFixedToken(agentToken))
agentClient.SDK.SetLogger(log)
o.Client = agentClient
}
if o.ExchangeToken == nil {
o.ExchangeToken = func(_ context.Context) (string, error) {
return agentToken, nil
}
}
if o.LogDir == "" {
o.LogDir = t.TempDir()
}
+30 -4
View File
@@ -3,6 +3,7 @@ package agenttest
import (
"context"
"io"
"net/http"
"slices"
"sync"
"sync/atomic"
@@ -28,6 +29,7 @@ import (
"github.com/coder/coder/v2/tailnet"
"github.com/coder/coder/v2/tailnet/proto"
"github.com/coder/coder/v2/testutil"
"github.com/coder/websocket"
)
const statsInterval = 500 * time.Millisecond
@@ -86,10 +88,34 @@ type Client struct {
fakeAgentAPI *FakeAgentAPI
LastWorkspaceAgent func()
mu sync.Mutex // Protects following.
logs []agentsdk.Log
derpMapUpdates chan *tailcfg.DERPMap
derpMapOnce sync.Once
mu sync.Mutex // Protects following.
logs []agentsdk.Log
derpMapUpdates chan *tailcfg.DERPMap
derpMapOnce sync.Once
refreshTokenCalls int
}
func (*Client) AsRequestOption() codersdk.RequestOption {
return func(_ *http.Request) {}
}
func (*Client) SetDialOption(*websocket.DialOptions) {}
func (*Client) GetSessionToken() string {
return "agenttest-token"
}
func (c *Client) RefreshToken(context.Context) error {
c.mu.Lock()
defer c.mu.Unlock()
c.refreshTokenCalls++
return nil
}
func (c *Client) GetNumRefreshTokenCalls() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.refreshTokenCalls
}
func (*Client) RewriteDERPMap(*tailcfg.DERPMap) {}
+3
View File
@@ -60,6 +60,9 @@ func (a *agent) apiHandler() http.Handler {
r.Get("/api/v0/listening-ports", lp.handler)
r.Get("/api/v0/netcheck", a.HandleNetcheck)
r.Post("/api/v0/list-directory", a.HandleLS)
r.Get("/api/v0/read-file", a.HandleReadFile)
r.Post("/api/v0/write-file", a.HandleWriteFile)
r.Post("/api/v0/edit-files", a.HandleEditFiles)
r.Get("/debug/logs", a.HandleHTTPDebugLogs)
r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock)
r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState)
+273
View File
@@ -0,0 +1,273 @@
package agent
import (
"context"
"errors"
"fmt"
"io"
"mime"
"net/http"
"os"
"path/filepath"
"strconv"
"syscall"
"github.com/icholy/replace"
"github.com/spf13/afero"
"golang.org/x/text/transform"
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
)
type HTTPResponseCode = int
func (a *agent) HandleReadFile(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
query := r.URL.Query()
parser := httpapi.NewQueryParamParser().RequiredNotEmpty("path")
path := parser.String(query, "", "path")
offset := parser.PositiveInt64(query, 0, "offset")
limit := parser.PositiveInt64(query, 0, "limit")
parser.ErrorExcessParams(query)
if len(parser.Errors) > 0 {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Query parameters have invalid values.",
Validations: parser.Errors,
})
return
}
status, err := a.streamFile(ctx, rw, path, offset, limit)
if err != nil {
httpapi.Write(ctx, rw, status, codersdk.Response{
Message: err.Error(),
})
return
}
}
func (a *agent) streamFile(ctx context.Context, rw http.ResponseWriter, path string, offset, limit int64) (HTTPResponseCode, error) {
if !filepath.IsAbs(path) {
return http.StatusBadRequest, xerrors.Errorf("file path must be absolute: %q", path)
}
f, err := a.filesystem.Open(path)
if err != nil {
status := http.StatusInternalServerError
switch {
case errors.Is(err, os.ErrNotExist):
status = http.StatusNotFound
case errors.Is(err, os.ErrPermission):
status = http.StatusForbidden
}
return status, err
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return http.StatusInternalServerError, err
}
if stat.IsDir() {
return http.StatusBadRequest, xerrors.Errorf("open %s: not a file", path)
}
size := stat.Size()
if limit == 0 {
limit = size
}
bytesRemaining := max(size-offset, 0)
bytesToRead := min(bytesRemaining, limit)
// Relying on just the file name for the mime type for now.
mimeType := mime.TypeByExtension(filepath.Ext(path))
if mimeType == "" {
mimeType = "application/octet-stream"
}
rw.Header().Set("Content-Type", mimeType)
rw.Header().Set("Content-Length", strconv.FormatInt(bytesToRead, 10))
rw.WriteHeader(http.StatusOK)
reader := io.NewSectionReader(f, offset, bytesToRead)
_, err = io.Copy(rw, reader)
if err != nil && !errors.Is(err, io.EOF) && ctx.Err() == nil {
a.logger.Error(ctx, "workspace agent read file", slog.Error(err))
}
return 0, nil
}
func (a *agent) HandleWriteFile(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
query := r.URL.Query()
parser := httpapi.NewQueryParamParser().RequiredNotEmpty("path")
path := parser.String(query, "", "path")
parser.ErrorExcessParams(query)
if len(parser.Errors) > 0 {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Query parameters have invalid values.",
Validations: parser.Errors,
})
return
}
status, err := a.writeFile(ctx, r, path)
if err != nil {
httpapi.Write(ctx, rw, status, codersdk.Response{
Message: err.Error(),
})
return
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
Message: fmt.Sprintf("Successfully wrote to %q", path),
})
}
func (a *agent) writeFile(ctx context.Context, r *http.Request, path string) (HTTPResponseCode, error) {
if !filepath.IsAbs(path) {
return http.StatusBadRequest, xerrors.Errorf("file path must be absolute: %q", path)
}
dir := filepath.Dir(path)
err := a.filesystem.MkdirAll(dir, 0o755)
if err != nil {
status := http.StatusInternalServerError
switch {
case errors.Is(err, os.ErrPermission):
status = http.StatusForbidden
case errors.Is(err, syscall.ENOTDIR):
status = http.StatusBadRequest
}
return status, err
}
f, err := a.filesystem.Create(path)
if err != nil {
status := http.StatusInternalServerError
switch {
case errors.Is(err, os.ErrPermission):
status = http.StatusForbidden
case errors.Is(err, syscall.EISDIR):
status = http.StatusBadRequest
}
return status, err
}
defer f.Close()
_, err = io.Copy(f, r.Body)
if err != nil && !errors.Is(err, io.EOF) && ctx.Err() == nil {
a.logger.Error(ctx, "workspace agent write file", slog.Error(err))
}
return 0, nil
}
func (a *agent) HandleEditFiles(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var req workspacesdk.FileEditRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
if len(req.Files) == 0 {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "must specify at least one file",
})
return
}
var combinedErr error
status := http.StatusOK
for _, edit := range req.Files {
s, err := a.editFile(r.Context(), edit.Path, edit.Edits)
// Keep the highest response status, so 500 will be preferred over 400, etc.
if s > status {
status = s
}
if err != nil {
combinedErr = errors.Join(combinedErr, err)
}
}
if combinedErr != nil {
httpapi.Write(ctx, rw, status, codersdk.Response{
Message: combinedErr.Error(),
})
return
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
Message: "Successfully edited file(s)",
})
}
func (a *agent) editFile(ctx context.Context, path string, edits []workspacesdk.FileEdit) (int, error) {
if path == "" {
return http.StatusBadRequest, xerrors.New("\"path\" is required")
}
if !filepath.IsAbs(path) {
return http.StatusBadRequest, xerrors.Errorf("file path must be absolute: %q", path)
}
if len(edits) == 0 {
return http.StatusBadRequest, xerrors.New("must specify at least one edit")
}
f, err := a.filesystem.Open(path)
if err != nil {
status := http.StatusInternalServerError
switch {
case errors.Is(err, os.ErrNotExist):
status = http.StatusNotFound
case errors.Is(err, os.ErrPermission):
status = http.StatusForbidden
}
return status, err
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return http.StatusInternalServerError, err
}
if stat.IsDir() {
return http.StatusBadRequest, xerrors.Errorf("open %s: not a file", path)
}
transforms := make([]transform.Transformer, len(edits))
for i, edit := range edits {
transforms[i] = replace.String(edit.Search, edit.Replace)
}
tmpfile, err := afero.TempFile(a.filesystem, "", filepath.Base(path))
if err != nil {
return http.StatusInternalServerError, err
}
defer tmpfile.Close()
_, err = io.Copy(tmpfile, replace.Chain(f, transforms...))
if err != nil {
if rerr := a.filesystem.Remove(tmpfile.Name()); rerr != nil {
a.logger.Warn(ctx, "unable to clean up temp file", slog.Error(rerr))
}
return http.StatusInternalServerError, xerrors.Errorf("edit %s: %w", path, err)
}
err = a.filesystem.Rename(tmpfile.Name(), path)
if err != nil {
return http.StatusInternalServerError, err
}
return 0, nil
}
+722
View File
@@ -0,0 +1,722 @@
package agent_test
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"runtime"
"syscall"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/agent"
"github.com/coder/coder/v2/agent/agenttest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/testutil"
)
type testFs struct {
afero.Fs
// intercept can return an error for testing when a call fails.
intercept func(call, file string) error
}
func newTestFs(base afero.Fs, intercept func(call, file string) error) *testFs {
return &testFs{
Fs: base,
intercept: intercept,
}
}
func (fs *testFs) Open(name string) (afero.File, error) {
if err := fs.intercept("open", name); err != nil {
return nil, err
}
return fs.Fs.Open(name)
}
func (fs *testFs) Create(name string) (afero.File, error) {
if err := fs.intercept("create", name); err != nil {
return nil, err
}
// Unlike os, afero lets you create files where directories already exist and
// lets you nest them underneath files, somehow.
stat, err := fs.Fs.Stat(name)
if err == nil && stat.IsDir() {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: syscall.EISDIR,
}
}
stat, err = fs.Fs.Stat(filepath.Dir(name))
if err == nil && !stat.IsDir() {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: syscall.ENOTDIR,
}
}
return fs.Fs.Create(name)
}
func (fs *testFs) MkdirAll(name string, mode os.FileMode) error {
if err := fs.intercept("mkdirall", name); err != nil {
return err
}
// Unlike os, afero lets you create directories where files already exist and
// lets you nest them underneath files somehow.
stat, err := fs.Fs.Stat(filepath.Dir(name))
if err == nil && !stat.IsDir() {
return &os.PathError{
Op: "mkdir",
Path: name,
Err: syscall.ENOTDIR,
}
}
stat, err = fs.Fs.Stat(name)
if err == nil && !stat.IsDir() {
return &os.PathError{
Op: "mkdir",
Path: name,
Err: syscall.ENOTDIR,
}
}
return fs.Fs.MkdirAll(name, mode)
}
func (fs *testFs) Rename(oldName, newName string) error {
if err := fs.intercept("rename", newName); err != nil {
return err
}
return fs.Fs.Rename(oldName, newName)
}
func TestReadFile(t *testing.T) {
t.Parallel()
tmpdir := os.TempDir()
noPermsFilePath := filepath.Join(tmpdir, "no-perms")
//nolint:dogsled
conn, _, _, fs, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, opts *agent.Options) {
opts.Filesystem = newTestFs(opts.Filesystem, func(call, file string) error {
if file == noPermsFilePath {
return os.ErrPermission
}
return nil
})
})
dirPath := filepath.Join(tmpdir, "a-directory")
err := fs.MkdirAll(dirPath, 0o755)
require.NoError(t, err)
filePath := filepath.Join(tmpdir, "file")
err = afero.WriteFile(fs, filePath, []byte("content"), 0o644)
require.NoError(t, err)
imagePath := filepath.Join(tmpdir, "file.png")
err = afero.WriteFile(fs, imagePath, []byte("not really an image"), 0o644)
require.NoError(t, err)
tests := []struct {
name string
path string
limit int64
offset int64
bytes []byte
mimeType string
errCode int
error string
}{
{
name: "NoPath",
path: "",
errCode: http.StatusBadRequest,
error: "\"path\" is required",
},
{
name: "RelativePathDotSlash",
path: "./relative",
errCode: http.StatusBadRequest,
error: "file path must be absolute",
},
{
name: "RelativePath",
path: "also-relative",
errCode: http.StatusBadRequest,
error: "file path must be absolute",
},
{
name: "NegativeLimit",
path: filePath,
limit: -10,
errCode: http.StatusBadRequest,
error: "value is negative",
},
{
name: "NegativeOffset",
path: filePath,
offset: -10,
errCode: http.StatusBadRequest,
error: "value is negative",
},
{
name: "NonExistent",
path: filepath.Join(tmpdir, "does-not-exist"),
errCode: http.StatusNotFound,
error: "file does not exist",
},
{
name: "IsDir",
path: dirPath,
errCode: http.StatusBadRequest,
error: "not a file",
},
{
name: "NoPermissions",
path: noPermsFilePath,
errCode: http.StatusForbidden,
error: "permission denied",
},
{
name: "Defaults",
path: filePath,
bytes: []byte("content"),
mimeType: "application/octet-stream",
},
{
name: "Limit1",
path: filePath,
limit: 1,
bytes: []byte("c"),
mimeType: "application/octet-stream",
},
{
name: "Offset1",
path: filePath,
offset: 1,
bytes: []byte("ontent"),
mimeType: "application/octet-stream",
},
{
name: "Limit1Offset2",
path: filePath,
limit: 1,
offset: 2,
bytes: []byte("n"),
mimeType: "application/octet-stream",
},
{
name: "Limit7Offset0",
path: filePath,
limit: 7,
offset: 0,
bytes: []byte("content"),
mimeType: "application/octet-stream",
},
{
name: "Limit100",
path: filePath,
limit: 100,
bytes: []byte("content"),
mimeType: "application/octet-stream",
},
{
name: "Offset7",
path: filePath,
offset: 7,
bytes: []byte{},
mimeType: "application/octet-stream",
},
{
name: "Offset100",
path: filePath,
offset: 100,
bytes: []byte{},
mimeType: "application/octet-stream",
},
{
name: "MimeTypePng",
path: imagePath,
bytes: []byte("not really an image"),
mimeType: "image/png",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
reader, mimeType, err := conn.ReadFile(ctx, tt.path, tt.offset, tt.limit)
if tt.errCode != 0 {
require.Error(t, err)
cerr := coderdtest.SDKError(t, err)
require.Contains(t, cerr.Error(), tt.error)
require.Equal(t, tt.errCode, cerr.StatusCode())
} else {
require.NoError(t, err)
defer reader.Close()
bytes, err := io.ReadAll(reader)
require.NoError(t, err)
require.Equal(t, tt.bytes, bytes)
require.Equal(t, tt.mimeType, mimeType)
}
})
}
}
func TestWriteFile(t *testing.T) {
t.Parallel()
tmpdir := os.TempDir()
noPermsFilePath := filepath.Join(tmpdir, "no-perms-file")
noPermsDirPath := filepath.Join(tmpdir, "no-perms-dir")
//nolint:dogsled
conn, _, _, fs, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, opts *agent.Options) {
opts.Filesystem = newTestFs(opts.Filesystem, func(call, file string) error {
if file == noPermsFilePath || file == noPermsDirPath {
return os.ErrPermission
}
return nil
})
})
dirPath := filepath.Join(tmpdir, "directory")
err := fs.MkdirAll(dirPath, 0o755)
require.NoError(t, err)
filePath := filepath.Join(tmpdir, "file")
err = afero.WriteFile(fs, filePath, []byte("content"), 0o644)
require.NoError(t, err)
notDirErr := "not a directory"
if runtime.GOOS == "windows" {
notDirErr = "cannot find the path"
}
tests := []struct {
name string
path string
bytes []byte
errCode int
error string
}{
{
name: "NoPath",
path: "",
errCode: http.StatusBadRequest,
error: "\"path\" is required",
},
{
name: "RelativePathDotSlash",
path: "./relative",
errCode: http.StatusBadRequest,
error: "file path must be absolute",
},
{
name: "RelativePath",
path: "also-relative",
errCode: http.StatusBadRequest,
error: "file path must be absolute",
},
{
name: "NonExistent",
path: filepath.Join(tmpdir, "/nested/does-not-exist"),
bytes: []byte("now it does exist"),
},
{
name: "IsDir",
path: dirPath,
errCode: http.StatusBadRequest,
error: "is a directory",
},
{
name: "IsNotDir",
path: filepath.Join(filePath, "file2"),
errCode: http.StatusBadRequest,
error: notDirErr,
},
{
name: "NoPermissionsFile",
path: noPermsFilePath,
errCode: http.StatusForbidden,
error: "permission denied",
},
{
name: "NoPermissionsDir",
path: filepath.Join(noPermsDirPath, "within-no-perm-dir"),
errCode: http.StatusForbidden,
error: "permission denied",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
reader := bytes.NewReader(tt.bytes)
err := conn.WriteFile(ctx, tt.path, reader)
if tt.errCode != 0 {
require.Error(t, err)
cerr := coderdtest.SDKError(t, err)
require.Contains(t, cerr.Error(), tt.error)
require.Equal(t, tt.errCode, cerr.StatusCode())
} else {
require.NoError(t, err)
b, err := afero.ReadFile(fs, tt.path)
require.NoError(t, err)
require.Equal(t, tt.bytes, b)
}
})
}
}
func TestEditFiles(t *testing.T) {
t.Parallel()
tmpdir := os.TempDir()
noPermsFilePath := filepath.Join(tmpdir, "no-perms-file")
failRenameFilePath := filepath.Join(tmpdir, "fail-rename")
//nolint:dogsled
conn, _, _, fs, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, opts *agent.Options) {
opts.Filesystem = newTestFs(opts.Filesystem, func(call, file string) error {
if file == noPermsFilePath {
return &os.PathError{
Op: call,
Path: file,
Err: os.ErrPermission,
}
} else if file == failRenameFilePath && call == "rename" {
return xerrors.New("rename failed")
}
return nil
})
})
dirPath := filepath.Join(tmpdir, "directory")
err := fs.MkdirAll(dirPath, 0o755)
require.NoError(t, err)
tests := []struct {
name string
contents map[string]string
edits []workspacesdk.FileEdits
expected map[string]string
errCode int
errors []string
}{
{
name: "NoFiles",
errCode: http.StatusBadRequest,
errors: []string{"must specify at least one file"},
},
{
name: "NoPath",
errCode: http.StatusBadRequest,
edits: []workspacesdk.FileEdits{
{
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errors: []string{"\"path\" is required"},
},
{
name: "RelativePathDotSlash",
edits: []workspacesdk.FileEdits{
{
Path: "./relative",
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusBadRequest,
errors: []string{"file path must be absolute"},
},
{
name: "RelativePath",
edits: []workspacesdk.FileEdits{
{
Path: "also-relative",
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusBadRequest,
errors: []string{"file path must be absolute"},
},
{
name: "NoEdits",
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "no-edits"),
},
},
errCode: http.StatusBadRequest,
errors: []string{"must specify at least one edit"},
},
{
name: "NonExistent",
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "does-not-exist"),
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusNotFound,
errors: []string{"file does not exist"},
},
{
name: "IsDir",
edits: []workspacesdk.FileEdits{
{
Path: dirPath,
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusBadRequest,
errors: []string{"not a file"},
},
{
name: "NoPermissions",
edits: []workspacesdk.FileEdits{
{
Path: noPermsFilePath,
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusForbidden,
errors: []string{"permission denied"},
},
{
name: "FailRename",
contents: map[string]string{failRenameFilePath: "foo bar"},
edits: []workspacesdk.FileEdits{
{
Path: failRenameFilePath,
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
errCode: http.StatusInternalServerError,
errors: []string{"rename failed"},
},
{
name: "Edit1",
contents: map[string]string{filepath.Join(tmpdir, "edit1"): "foo bar"},
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "edit1"),
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
},
},
},
expected: map[string]string{filepath.Join(tmpdir, "edit1"): "bar bar"},
},
{
name: "EditEdit", // Edits affect previous edits.
contents: map[string]string{filepath.Join(tmpdir, "edit-edit"): "foo bar"},
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "edit-edit"),
Edits: []workspacesdk.FileEdit{
{
Search: "foo",
Replace: "bar",
},
{
Search: "bar",
Replace: "qux",
},
},
},
},
expected: map[string]string{filepath.Join(tmpdir, "edit-edit"): "qux qux"},
},
{
name: "Multiline",
contents: map[string]string{filepath.Join(tmpdir, "multiline"): "foo\nbar\nbaz\nqux"},
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "multiline"),
Edits: []workspacesdk.FileEdit{
{
Search: "bar\nbaz",
Replace: "frob",
},
},
},
},
expected: map[string]string{filepath.Join(tmpdir, "multiline"): "foo\nfrob\nqux"},
},
{
name: "Multifile",
contents: map[string]string{
filepath.Join(tmpdir, "file1"): "file 1",
filepath.Join(tmpdir, "file2"): "file 2",
filepath.Join(tmpdir, "file3"): "file 3",
},
edits: []workspacesdk.FileEdits{
{
Path: filepath.Join(tmpdir, "file1"),
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited1",
},
},
},
{
Path: filepath.Join(tmpdir, "file2"),
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited2",
},
},
},
{
Path: filepath.Join(tmpdir, "file3"),
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited3",
},
},
},
},
expected: map[string]string{
filepath.Join(tmpdir, "file1"): "edited1 1",
filepath.Join(tmpdir, "file2"): "edited2 2",
filepath.Join(tmpdir, "file3"): "edited3 3",
},
},
{
name: "MultiError",
contents: map[string]string{
filepath.Join(tmpdir, "file8"): "file 8",
},
edits: []workspacesdk.FileEdits{
{
Path: noPermsFilePath,
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited7",
},
},
},
{
Path: filepath.Join(tmpdir, "file8"),
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited8",
},
},
},
{
Path: filepath.Join(tmpdir, "file9"),
Edits: []workspacesdk.FileEdit{
{
Search: "file",
Replace: "edited9",
},
},
},
},
expected: map[string]string{
filepath.Join(tmpdir, "file8"): "edited8 8",
},
// Higher status codes will override lower ones, so in this case the 404
// takes priority over the 403.
errCode: http.StatusNotFound,
errors: []string{
fmt.Sprintf("%s: permission denied", noPermsFilePath),
"file9: file does not exist",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
for path, content := range tt.contents {
err := afero.WriteFile(fs, path, []byte(content), 0o644)
require.NoError(t, err)
}
err := conn.EditFiles(ctx, workspacesdk.FileEditRequest{Files: tt.edits})
if tt.errCode != 0 {
require.Error(t, err)
cerr := coderdtest.SDKError(t, err)
for _, error := range tt.errors {
require.Contains(t, cerr.Error(), error)
}
require.Equal(t, tt.errCode, cerr.StatusCode())
} else {
require.NoError(t, err)
}
for path, expect := range tt.expected {
b, err := afero.ReadFile(fs, path)
require.NoError(t, err)
require.Equal(t, expect, string(b))
}
})
}
}
@@ -0,0 +1,350 @@
package backedpipe
import (
"context"
"io"
"sync"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/singleflight"
"golang.org/x/xerrors"
)
var (
ErrPipeClosed = xerrors.New("pipe is closed")
ErrPipeAlreadyConnected = xerrors.New("pipe is already connected")
ErrReconnectionInProgress = xerrors.New("reconnection already in progress")
ErrReconnectFailed = xerrors.New("reconnect failed")
ErrInvalidSequenceNumber = xerrors.New("remote sequence number exceeds local sequence")
ErrReconnectWriterFailed = xerrors.New("reconnect writer failed")
)
// connectionState represents the current state of the BackedPipe connection.
type connectionState int
const (
// connected indicates the pipe is connected and operational.
connected connectionState = iota
// disconnected indicates the pipe is not connected but not closed.
disconnected
// reconnecting indicates a reconnection attempt is in progress.
reconnecting
// closed indicates the pipe is permanently closed.
closed
)
// ErrorEvent represents an error from a reader or writer with connection generation info.
type ErrorEvent struct {
Err error
Component string // "reader" or "writer"
Generation uint64 // connection generation when error occurred
}
const (
// Default buffer capacity used by the writer - 64MB
DefaultBufferSize = 64 * 1024 * 1024
)
// Reconnector is an interface for establishing connections when the BackedPipe needs to reconnect.
// Implementations should:
// 1. Establish a new connection to the remote side
// 2. Exchange sequence numbers with the remote side
// 3. Return the new connection and the remote's reader sequence number
//
// The readerSeqNum parameter is the local reader's current sequence number
// (total bytes successfully read from the remote). This must be sent to the
// remote so it can replay its data to us starting from this number.
//
// The returned remoteReaderSeqNum should be the remote side's reader sequence
// number (how many bytes of our outbound data it has successfully read). This
// informs our writer where to resume (i.e., which bytes to replay to the remote).
type Reconnector interface {
Reconnect(ctx context.Context, readerSeqNum uint64) (conn io.ReadWriteCloser, remoteReaderSeqNum uint64, err error)
}
// BackedPipe provides a reliable bidirectional byte stream over unreliable network connections.
// It orchestrates a BackedReader and BackedWriter to provide transparent reconnection
// and data replay capabilities.
type BackedPipe struct {
ctx context.Context
cancel context.CancelFunc
mu sync.RWMutex
reader *BackedReader
writer *BackedWriter
reconnector Reconnector
conn io.ReadWriteCloser
// State machine
state connectionState
connGen uint64 // Increments on each successful reconnection
// Unified error handling with generation filtering
errChan chan ErrorEvent
// singleflight group to dedupe concurrent ForceReconnect calls
sf singleflight.Group
// Track first error per generation to avoid duplicate reconnections
lastErrorGen uint64
}
// NewBackedPipe creates a new BackedPipe with default options and the specified reconnector.
// The pipe starts disconnected and must be connected using Connect().
func NewBackedPipe(ctx context.Context, reconnector Reconnector) *BackedPipe {
pipeCtx, cancel := context.WithCancel(ctx)
errChan := make(chan ErrorEvent, 1)
bp := &BackedPipe{
ctx: pipeCtx,
cancel: cancel,
reconnector: reconnector,
state: disconnected,
connGen: 0, // Start with generation 0
errChan: errChan,
}
// Create reader and writer with typed error channel for generation-aware error reporting
bp.reader = NewBackedReader(errChan)
bp.writer = NewBackedWriter(DefaultBufferSize, errChan)
// Start error handler goroutine
go bp.handleErrors()
return bp
}
// Connect establishes the initial connection using the reconnect function.
func (bp *BackedPipe) Connect() error {
bp.mu.Lock()
defer bp.mu.Unlock()
if bp.state == closed {
return ErrPipeClosed
}
if bp.state == connected {
return ErrPipeAlreadyConnected
}
// Use internal context for the actual reconnect operation to ensure
// Close() reliably cancels any in-flight attempt.
return bp.reconnectLocked()
}
// Read implements io.Reader by delegating to the BackedReader.
func (bp *BackedPipe) Read(p []byte) (int, error) {
return bp.reader.Read(p)
}
// Write implements io.Writer by delegating to the BackedWriter.
func (bp *BackedPipe) Write(p []byte) (int, error) {
bp.mu.RLock()
writer := bp.writer
state := bp.state
bp.mu.RUnlock()
if state == closed {
return 0, io.EOF
}
return writer.Write(p)
}
// Close closes the pipe and all underlying connections.
func (bp *BackedPipe) Close() error {
bp.mu.Lock()
defer bp.mu.Unlock()
if bp.state == closed {
return nil
}
bp.state = closed
bp.cancel() // Cancel main context
// Close all components in parallel to avoid deadlocks
//
// IMPORTANT: The connection must be closed first to unblock any
// readers or writers that might be holding the mutex on Read/Write
var g errgroup.Group
if bp.conn != nil {
conn := bp.conn
g.Go(func() error {
return conn.Close()
})
bp.conn = nil
}
if bp.reader != nil {
reader := bp.reader
g.Go(func() error {
return reader.Close()
})
}
if bp.writer != nil {
writer := bp.writer
g.Go(func() error {
return writer.Close()
})
}
// Wait for all close operations to complete and return any error
return g.Wait()
}
// Connected returns whether the pipe is currently connected.
func (bp *BackedPipe) Connected() bool {
bp.mu.RLock()
defer bp.mu.RUnlock()
return bp.state == connected && bp.reader.Connected() && bp.writer.Connected()
}
// reconnectLocked handles the reconnection logic. Must be called with write lock held.
func (bp *BackedPipe) reconnectLocked() error {
if bp.state == reconnecting {
return ErrReconnectionInProgress
}
bp.state = reconnecting
defer func() {
// Only reset to disconnected if we're still in reconnecting state
// (successful reconnection will set state to connected)
if bp.state == reconnecting {
bp.state = disconnected
}
}()
// Close existing connection if any
if bp.conn != nil {
_ = bp.conn.Close()
bp.conn = nil
}
// Increment the generation and update both reader and writer.
// We do it now to track even the connections that fail during
// Reconnect.
bp.connGen++
bp.reader.SetGeneration(bp.connGen)
bp.writer.SetGeneration(bp.connGen)
// Reconnect reader and writer
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go bp.reader.Reconnect(seqNum, newR)
// Get the precise reader sequence number from the reader while it holds its lock
readerSeqNum, ok := <-seqNum
if !ok {
// Reader was closed during reconnection
return ErrReconnectFailed
}
// Perform reconnect using the exact sequence number we just received
conn, remoteReaderSeqNum, err := bp.reconnector.Reconnect(bp.ctx, readerSeqNum)
if err != nil {
// Unblock reader reconnect
newR <- nil
return ErrReconnectFailed
}
// Provide the new connection to the reader (reader still holds its lock)
newR <- conn
// Replay our outbound data from the remote's reader sequence number
writerReconnectErr := bp.writer.Reconnect(remoteReaderSeqNum, conn)
if writerReconnectErr != nil {
return ErrReconnectWriterFailed
}
// Success - update state
bp.conn = conn
bp.state = connected
return nil
}
// handleErrors listens for connection errors from reader/writer and triggers reconnection.
// It filters errors from old connections and ensures only the first error per generation
// triggers reconnection.
func (bp *BackedPipe) handleErrors() {
for {
select {
case <-bp.ctx.Done():
return
case errorEvt := <-bp.errChan:
bp.handleConnectionError(errorEvt)
}
}
}
// handleConnectionError handles errors from either reader or writer components.
// It filters errors from old connections and ensures only one reconnection per generation.
func (bp *BackedPipe) handleConnectionError(errorEvt ErrorEvent) {
bp.mu.Lock()
defer bp.mu.Unlock()
// Skip if already closed
if bp.state == closed {
return
}
// Filter errors from old connections (lower generation)
if errorEvt.Generation < bp.connGen {
return
}
// Skip if not connected (already disconnected or reconnecting)
if bp.state != connected {
return
}
// Skip if we've already seen an error for this generation
if bp.lastErrorGen >= errorEvt.Generation {
return
}
// This is the first error for this generation
bp.lastErrorGen = errorEvt.Generation
// Mark as disconnected
bp.state = disconnected
// Try to reconnect using internal context
reconnectErr := bp.reconnectLocked()
if reconnectErr != nil {
// Reconnection failed - log or handle as needed
// For now, we'll just continue and wait for manual reconnection
_ = errorEvt.Err // Use the original error from the component
_ = errorEvt.Component // Component info available for potential logging by higher layers
}
}
// ForceReconnect forces a reconnection attempt immediately.
// This can be used to force a reconnection if a new connection is established.
// It prevents duplicate reconnections when called concurrently.
func (bp *BackedPipe) ForceReconnect() error {
// Deduplicate concurrent ForceReconnect calls so only one reconnection
// attempt runs at a time from this API. Use the pipe's internal context
// to ensure Close() cancels any in-flight attempt.
_, err, _ := bp.sf.Do("force-reconnect", func() (interface{}, error) {
bp.mu.Lock()
defer bp.mu.Unlock()
if bp.state == closed {
return nil, io.EOF
}
// Don't force reconnect if already reconnecting
if bp.state == reconnecting {
return nil, ErrReconnectionInProgress
}
return nil, bp.reconnectLocked()
})
return err
}
@@ -0,0 +1,989 @@
package backedpipe_test
import (
"bytes"
"context"
"io"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/agent/immortalstreams/backedpipe"
"github.com/coder/coder/v2/testutil"
)
// mockConnection implements io.ReadWriteCloser for testing
type mockConnection struct {
mu sync.Mutex
readBuffer bytes.Buffer
writeBuffer bytes.Buffer
closed bool
readError error
writeError error
closeError error
readFunc func([]byte) (int, error)
writeFunc func([]byte) (int, error)
seqNum uint64
}
func newMockConnection() *mockConnection {
return &mockConnection{}
}
func (mc *mockConnection) Read(p []byte) (int, error) {
mc.mu.Lock()
defer mc.mu.Unlock()
if mc.readFunc != nil {
return mc.readFunc(p)
}
if mc.readError != nil {
return 0, mc.readError
}
return mc.readBuffer.Read(p)
}
func (mc *mockConnection) Write(p []byte) (int, error) {
mc.mu.Lock()
defer mc.mu.Unlock()
if mc.writeFunc != nil {
return mc.writeFunc(p)
}
if mc.writeError != nil {
return 0, mc.writeError
}
return mc.writeBuffer.Write(p)
}
func (mc *mockConnection) Close() error {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.closed = true
return mc.closeError
}
func (mc *mockConnection) WriteString(s string) {
mc.mu.Lock()
defer mc.mu.Unlock()
_, _ = mc.readBuffer.WriteString(s)
}
func (mc *mockConnection) ReadString() string {
mc.mu.Lock()
defer mc.mu.Unlock()
return mc.writeBuffer.String()
}
func (mc *mockConnection) SetReadError(err error) {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.readError = err
}
func (mc *mockConnection) SetWriteError(err error) {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.writeError = err
}
func (mc *mockConnection) Reset() {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.readBuffer.Reset()
mc.writeBuffer.Reset()
mc.readError = nil
mc.writeError = nil
mc.closed = false
}
// mockReconnector implements the Reconnector interface for testing
type mockReconnector struct {
mu sync.Mutex
connections []*mockConnection
connectionIndex int
callCount int
signalChan chan struct{}
}
// Reconnect implements the Reconnector interface
func (m *mockReconnector) Reconnect(ctx context.Context, readerSeqNum uint64) (io.ReadWriteCloser, uint64, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.callCount++
if m.connectionIndex >= len(m.connections) {
return nil, 0, xerrors.New("no more connections available")
}
conn := m.connections[m.connectionIndex]
m.connectionIndex++
// Signal when reconnection happens
if m.connectionIndex > 1 {
select {
case m.signalChan <- struct{}{}:
default:
}
}
// Determine remoteReaderSeqNum (how many bytes of our outbound data the remote has read)
var remoteReaderSeqNum uint64
switch {
case m.callCount == 1:
remoteReaderSeqNum = 0
case conn.seqNum != 0:
remoteReaderSeqNum = conn.seqNum
default:
// Default to 0 if unspecified
remoteReaderSeqNum = 0
}
return conn, remoteReaderSeqNum, nil
}
// GetCallCount returns the current call count in a thread-safe manner
func (m *mockReconnector) GetCallCount() int {
m.mu.Lock()
defer m.mu.Unlock()
return m.callCount
}
// mockReconnectFunc creates a unified reconnector with all behaviors enabled
func mockReconnectFunc(connections ...*mockConnection) (*mockReconnector, chan struct{}) {
signalChan := make(chan struct{}, 1)
reconnector := &mockReconnector{
connections: connections,
signalChan: signalChan,
}
return reconnector, signalChan
}
// blockingReconnector is a reconnector that blocks on a channel for deterministic testing
type blockingReconnector struct {
conn1 *mockConnection
conn2 *mockConnection
callCount int
blockChan <-chan struct{}
blockedChan chan struct{}
mu sync.Mutex
signalOnce sync.Once // Ensure we only signal once for the first actual reconnect
}
func (b *blockingReconnector) Reconnect(ctx context.Context, readerSeqNum uint64) (io.ReadWriteCloser, uint64, error) {
b.mu.Lock()
b.callCount++
currentCall := b.callCount
b.mu.Unlock()
if currentCall == 1 {
// Initial connect
return b.conn1, 0, nil
}
// Signal that we're about to block, but only once for the first reconnect attempt
// This ensures we properly test singleflight deduplication
b.signalOnce.Do(func() {
select {
case b.blockedChan <- struct{}{}:
default:
// If channel is full, don't block
}
})
// For subsequent calls, block until channel is closed
select {
case <-b.blockChan:
// Channel closed, proceed with reconnection
case <-ctx.Done():
return nil, 0, ctx.Err()
}
return b.conn2, 0, nil
}
// GetCallCount returns the current call count in a thread-safe manner
func (b *blockingReconnector) GetCallCount() int {
b.mu.Lock()
defer b.mu.Unlock()
return b.callCount
}
func mockBlockingReconnectFunc(conn1, conn2 *mockConnection, blockChan <-chan struct{}) (*blockingReconnector, chan struct{}) {
blockedChan := make(chan struct{}, 1)
reconnector := &blockingReconnector{
conn1: conn1,
conn2: conn2,
blockChan: blockChan,
blockedChan: blockedChan,
}
return reconnector, blockedChan
}
// eofTestReconnector is a custom reconnector for the EOF test case
type eofTestReconnector struct {
mu sync.Mutex
conn1 io.ReadWriteCloser
conn2 io.ReadWriteCloser
callCount int
}
func (e *eofTestReconnector) Reconnect(ctx context.Context, readerSeqNum uint64) (io.ReadWriteCloser, uint64, error) {
e.mu.Lock()
defer e.mu.Unlock()
e.callCount++
if e.callCount == 1 {
return e.conn1, 0, nil
}
if e.callCount == 2 {
// Second call is the reconnection after EOF
// Return 5 to indicate remote has read all 5 bytes of "hello"
return e.conn2, 5, nil
}
return nil, 0, xerrors.New("no more connections")
}
// GetCallCount returns the current call count in a thread-safe manner
func (e *eofTestReconnector) GetCallCount() int {
e.mu.Lock()
defer e.mu.Unlock()
return e.callCount
}
func TestBackedPipe_NewBackedPipe(t *testing.T) {
t.Parallel()
ctx := context.Background()
reconnectFn, _ := mockReconnectFunc(newMockConnection())
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
require.NotNil(t, bp)
require.False(t, bp.Connected())
}
func TestBackedPipe_Connect(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnector, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
err := bp.Connect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 1, reconnector.GetCallCount())
}
func TestBackedPipe_ConnectAlreadyConnected(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
err := bp.Connect()
require.NoError(t, err)
// Second connect should fail
err = bp.Connect()
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrPipeAlreadyConnected)
}
func TestBackedPipe_ConnectAfterClose(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
err := bp.Close()
require.NoError(t, err)
err = bp.Connect()
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrPipeClosed)
}
func TestBackedPipe_BasicReadWrite(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
err := bp.Connect()
require.NoError(t, err)
// Write data
n, err := bp.Write([]byte("hello"))
require.NoError(t, err)
require.Equal(t, 5, n)
// Simulate data coming back
conn.WriteString("world")
// Read data
buf := make([]byte, 10)
n, err = bp.Read(buf)
require.NoError(t, err)
require.Equal(t, 5, n)
require.Equal(t, "world", string(buf[:n]))
}
func TestBackedPipe_WriteBeforeConnect(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
// Write before connecting should block
writeComplete := make(chan error, 1)
go func() {
_, err := bp.Write([]byte("hello"))
writeComplete <- err
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when disconnected")
case <-time.After(100 * time.Millisecond):
// Expected - write is blocked
}
// Connect should unblock the write
err := bp.Connect()
require.NoError(t, err)
// Write should now complete
err = testutil.RequireReceive(ctx, t, writeComplete)
require.NoError(t, err)
// Check that data was replayed to connection
require.Equal(t, "hello", conn.ReadString())
}
func TestBackedPipe_ReadBlocksWhenDisconnected(t *testing.T) {
t.Parallel()
ctx := context.Background()
testCtx := testutil.Context(t, testutil.WaitShort)
reconnectFn, _ := mockReconnectFunc(newMockConnection())
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
// Start a read that should block
readDone := make(chan struct{})
readStarted := make(chan struct{}, 1)
var readErr error
go func() {
defer close(readDone)
readStarted <- struct{}{} // Signal that we're about to start the read
buf := make([]byte, 10)
_, readErr = bp.Read(buf)
}()
// Wait for the goroutine to start
testutil.TryReceive(testCtx, t, readStarted)
// Ensure the read is actually blocked by verifying it hasn't completed
require.Eventually(t, func() bool {
select {
case <-readDone:
t.Fatal("Read should be blocked when disconnected")
return false
default:
// Good, still blocked
return true
}
}, testutil.WaitShort, testutil.IntervalMedium)
// Close should unblock the read
bp.Close()
testutil.TryReceive(testCtx, t, readDone)
require.Equal(t, io.EOF, readErr)
}
func TestBackedPipe_Reconnection(t *testing.T) {
t.Parallel()
ctx := context.Background()
testCtx := testutil.Context(t, testutil.WaitShort)
conn1 := newMockConnection()
conn2 := newMockConnection()
conn2.seqNum = 17 // Remote has received 17 bytes, so replay from sequence 17
reconnectFn, signalChan := mockReconnectFunc(conn1, conn2)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
defer bp.Close()
// Initial connect
err := bp.Connect()
require.NoError(t, err)
// Write some data before failure
bp.Write([]byte("before disconnect***"))
// Simulate connection failure
conn1.SetReadError(xerrors.New("connection lost"))
conn1.SetWriteError(xerrors.New("connection lost"))
// Trigger a write to cause the pipe to notice the failure
_, _ = bp.Write([]byte("trigger failure "))
testutil.RequireReceive(testCtx, t, signalChan)
// Wait for reconnection to complete
require.Eventually(t, func() bool {
return bp.Connected()
}, testutil.WaitShort, testutil.IntervalFast, "pipe should reconnect")
replayedData := conn2.ReadString()
require.Equal(t, "***trigger failure ", replayedData, "Should replay exactly the data written after sequence 17")
// Verify that new writes work with the reconnected pipe
_, err = bp.Write([]byte("new data after reconnect"))
require.NoError(t, err)
// Read all data from the connection (replayed + new data)
allData := conn2.ReadString()
require.Equal(t, "***trigger failure new data after reconnect", allData, "Should have replayed data plus new data")
}
func TestBackedPipe_Close(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
err := bp.Connect()
require.NoError(t, err)
err = bp.Close()
require.NoError(t, err)
require.True(t, conn.closed)
// Operations after close should fail
_, err = bp.Read(make([]byte, 10))
require.Equal(t, io.EOF, err)
_, err = bp.Write([]byte("test"))
require.Equal(t, io.EOF, err)
}
func TestBackedPipe_CloseIdempotent(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
err := bp.Close()
require.NoError(t, err)
// Second close should be no-op
err = bp.Close()
require.NoError(t, err)
}
func TestBackedPipe_ReconnectFunctionFailure(t *testing.T) {
t.Parallel()
ctx := context.Background()
failingReconnector := &mockReconnector{
connections: nil, // No connections available
}
bp := backedpipe.NewBackedPipe(ctx, failingReconnector)
defer bp.Close()
err := bp.Connect()
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrReconnectFailed)
require.False(t, bp.Connected())
}
func TestBackedPipe_ForceReconnect(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn1 := newMockConnection()
conn2 := newMockConnection()
// Set conn2 sequence number to 9 to indicate remote has read all 9 bytes of "test data"
conn2.seqNum = 9
reconnector, _ := mockReconnectFunc(conn1, conn2)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Initial connect
err := bp.Connect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 1, reconnector.GetCallCount())
// Write some data to the first connection
_, err = bp.Write([]byte("test data"))
require.NoError(t, err)
require.Equal(t, "test data", conn1.ReadString())
// Force a reconnection
err = bp.ForceReconnect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 2, reconnector.GetCallCount())
// Since the mock returns the proper sequence number, no data should be replayed
// The new connection should be empty
require.Equal(t, "", conn2.ReadString())
// Verify that data can still be written and read after forced reconnection
_, err = bp.Write([]byte("new data"))
require.NoError(t, err)
require.Equal(t, "new data", conn2.ReadString())
// Verify that reads work with the new connection
conn2.WriteString("response data")
buf := make([]byte, 20)
n, err := bp.Read(buf)
require.NoError(t, err)
require.Equal(t, 13, n)
require.Equal(t, "response data", string(buf[:n]))
}
func TestBackedPipe_ForceReconnectWhenClosed(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
// Close the pipe first
err := bp.Close()
require.NoError(t, err)
// Try to force reconnect when closed
err = bp.ForceReconnect()
require.Error(t, err)
require.Equal(t, io.EOF, err)
}
func TestBackedPipe_StateTransitionsAndGenerationTracking(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn1 := newMockConnection()
conn2 := newMockConnection()
conn3 := newMockConnection()
reconnector, signalChan := mockReconnectFunc(conn1, conn2, conn3)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Initial state should be disconnected
require.False(t, bp.Connected())
// Connect should transition to connected
err := bp.Connect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 1, reconnector.GetCallCount())
// Write some data
_, err = bp.Write([]byte("test data gen 1"))
require.NoError(t, err)
// Simulate connection failure by setting errors on connection
conn1.SetReadError(xerrors.New("connection lost"))
conn1.SetWriteError(xerrors.New("connection lost"))
// Trigger a write to cause the pipe to notice the failure
_, _ = bp.Write([]byte("trigger failure"))
// Wait for reconnection signal
testutil.RequireReceive(testutil.Context(t, testutil.WaitShort), t, signalChan)
// Wait for reconnection to complete
require.Eventually(t, func() bool {
return bp.Connected()
}, testutil.WaitShort, testutil.IntervalFast, "should reconnect")
require.Equal(t, 2, reconnector.GetCallCount())
// Force another reconnection
err = bp.ForceReconnect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 3, reconnector.GetCallCount())
// Close should transition to closed state
err = bp.Close()
require.NoError(t, err)
require.False(t, bp.Connected())
// Operations on closed pipe should fail
err = bp.Connect()
require.Equal(t, backedpipe.ErrPipeClosed, err)
err = bp.ForceReconnect()
require.Equal(t, io.EOF, err)
}
func TestBackedPipe_GenerationFiltering(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn1 := newMockConnection()
conn2 := newMockConnection()
reconnector, _ := mockReconnectFunc(conn1, conn2)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Connect
err := bp.Connect()
require.NoError(t, err)
require.True(t, bp.Connected())
// Simulate multiple rapid errors from the same connection generation
// Only the first one should trigger reconnection
conn1.SetReadError(xerrors.New("error 1"))
conn1.SetWriteError(xerrors.New("error 2"))
// Trigger multiple errors quickly
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
_, _ = bp.Write([]byte("trigger error 1"))
}()
go func() {
defer wg.Done()
_, _ = bp.Write([]byte("trigger error 2"))
}()
// Wait for both writes to complete
wg.Wait()
// Wait for reconnection to complete
require.Eventually(t, func() bool {
return bp.Connected()
}, testutil.WaitShort, testutil.IntervalFast, "should reconnect once")
// Should have only reconnected once despite multiple errors
require.Equal(t, 2, reconnector.GetCallCount()) // Initial connect + 1 reconnect
}
func TestBackedPipe_DuplicateReconnectionPrevention(t *testing.T) {
t.Parallel()
ctx := context.Background()
testCtx := testutil.Context(t, testutil.WaitShort)
// Create a blocking reconnector for deterministic testing
conn1 := newMockConnection()
conn2 := newMockConnection()
blockChan := make(chan struct{})
reconnector, blockedChan := mockBlockingReconnectFunc(conn1, conn2, blockChan)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Initial connect
err := bp.Connect()
require.NoError(t, err)
require.Equal(t, 1, reconnector.GetCallCount(), "should have exactly 1 call after initial connect")
// We'll use channels to coordinate the test execution:
// 1. Start all goroutines but have them wait
// 2. Release the first one and wait for it to block
// 3. Release the others while the first is still blocked
const numConcurrent = 3
startSignals := make([]chan struct{}, numConcurrent)
startedSignals := make([]chan struct{}, numConcurrent)
for i := range startSignals {
startSignals[i] = make(chan struct{})
startedSignals[i] = make(chan struct{})
}
errors := make([]error, numConcurrent)
var wg sync.WaitGroup
// Start all goroutines
for i := 0; i < numConcurrent; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
// Wait for the signal to start
<-startSignals[idx]
// Signal that we're about to call ForceReconnect
close(startedSignals[idx])
errors[idx] = bp.ForceReconnect()
}(i)
}
// Start the first ForceReconnect and wait for it to block
close(startSignals[0])
<-startedSignals[0]
// Wait for the first reconnect to actually start and block
testutil.RequireReceive(testCtx, t, blockedChan)
// Now start all the other ForceReconnect calls
// They should all join the same singleflight operation
for i := 1; i < numConcurrent; i++ {
close(startSignals[i])
}
// Wait for all additional goroutines to have started their calls
for i := 1; i < numConcurrent; i++ {
<-startedSignals[i]
}
// At this point, one reconnect has started and is blocked,
// and all other goroutines have called ForceReconnect and should be
// waiting on the same singleflight operation.
// Due to singleflight, only one reconnect should have been attempted.
require.Equal(t, 2, reconnector.GetCallCount(), "should have exactly 2 calls: initial connect + 1 reconnect due to singleflight")
// Release the blocking reconnect function
close(blockChan)
// Wait for all ForceReconnect calls to complete
wg.Wait()
// All calls should succeed (they share the same result from singleflight)
for i, err := range errors {
require.NoError(t, err, "ForceReconnect %d should succeed", i, err)
}
// Final verification: call count should still be exactly 2
require.Equal(t, 2, reconnector.GetCallCount(), "final call count should be exactly 2: initial connect + 1 singleflight reconnect")
}
func TestBackedPipe_SingleReconnectionOnMultipleErrors(t *testing.T) {
t.Parallel()
ctx := context.Background()
testCtx := testutil.Context(t, testutil.WaitShort)
// Create connections for initial connect and reconnection
conn1 := newMockConnection()
conn2 := newMockConnection()
reconnector, signalChan := mockReconnectFunc(conn1, conn2)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Initial connect
err := bp.Connect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 1, reconnector.GetCallCount())
// Write some initial data to establish the connection
_, err = bp.Write([]byte("initial data"))
require.NoError(t, err)
// Set up both read and write errors on the connection
conn1.SetReadError(xerrors.New("read connection lost"))
conn1.SetWriteError(xerrors.New("write connection lost"))
// Trigger write error (this will trigger reconnection)
go func() {
_, _ = bp.Write([]byte("trigger write error"))
}()
// Wait for reconnection to start
testutil.RequireReceive(testCtx, t, signalChan)
// Wait for reconnection to complete
require.Eventually(t, func() bool {
return bp.Connected()
}, testutil.WaitShort, testutil.IntervalFast, "should reconnect after write error")
// Verify that only one reconnection occurred
require.Equal(t, 2, reconnector.GetCallCount(), "should have exactly 2 calls: initial connect + 1 reconnection")
require.True(t, bp.Connected(), "should be connected after reconnection")
}
func TestBackedPipe_ForceReconnectWhenDisconnected(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn := newMockConnection()
reconnector, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Don't connect initially, just force reconnect
err := bp.ForceReconnect()
require.NoError(t, err)
require.True(t, bp.Connected())
require.Equal(t, 1, reconnector.GetCallCount())
// Verify we can write and read
_, err = bp.Write([]byte("test"))
require.NoError(t, err)
require.Equal(t, "test", conn.ReadString())
conn.WriteString("response")
buf := make([]byte, 10)
n, err := bp.Read(buf)
require.NoError(t, err)
require.Equal(t, 8, n)
require.Equal(t, "response", string(buf[:n]))
}
func TestBackedPipe_EOFTriggersReconnection(t *testing.T) {
t.Parallel()
ctx := context.Background()
// Create connections where we can control when EOF occurs
conn1 := newMockConnection()
conn2 := newMockConnection()
conn2.WriteString("newdata") // Pre-populate conn2 with data
// Make conn1 return EOF after reading "world"
hasReadData := false
conn1.readFunc = func(p []byte) (int, error) {
// Don't lock here - the Read method already holds the lock
// First time: return "world"
if !hasReadData && conn1.readBuffer.Len() > 0 {
n, _ := conn1.readBuffer.Read(p)
hasReadData = true
return n, nil
}
// After that: return EOF
return 0, io.EOF
}
conn1.WriteString("world")
reconnector := &eofTestReconnector{
conn1: conn1,
conn2: conn2,
}
bp := backedpipe.NewBackedPipe(ctx, reconnector)
defer bp.Close()
// Initial connect
err := bp.Connect()
require.NoError(t, err)
require.Equal(t, 1, reconnector.GetCallCount())
// Write some data
_, err = bp.Write([]byte("hello"))
require.NoError(t, err)
buf := make([]byte, 10)
// First read should succeed
n, err := bp.Read(buf)
require.NoError(t, err)
require.Equal(t, 5, n)
require.Equal(t, "world", string(buf[:n]))
// Next read will encounter EOF and should trigger reconnection
// After reconnection, it should read from conn2
n, err = bp.Read(buf)
require.NoError(t, err)
require.Equal(t, 7, n)
require.Equal(t, "newdata", string(buf[:n]))
// Verify reconnection happened
require.Equal(t, 2, reconnector.GetCallCount())
// Verify the pipe is still connected and functional
require.True(t, bp.Connected())
// Further writes should go to the new connection
_, err = bp.Write([]byte("aftereof"))
require.NoError(t, err)
require.Equal(t, "aftereof", conn2.ReadString())
}
func BenchmarkBackedPipe_Write(b *testing.B) {
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
bp.Connect()
b.Cleanup(func() {
_ = bp.Close()
})
data := make([]byte, 1024) // 1KB writes
b.ResetTimer()
for i := 0; i < b.N; i++ {
bp.Write(data)
}
}
func BenchmarkBackedPipe_Read(b *testing.B) {
ctx := context.Background()
conn := newMockConnection()
reconnectFn, _ := mockReconnectFunc(conn)
bp := backedpipe.NewBackedPipe(ctx, reconnectFn)
bp.Connect()
b.Cleanup(func() {
_ = bp.Close()
})
buf := make([]byte, 1024)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Fill connection with fresh data for each iteration
conn.WriteString(string(buf))
bp.Read(buf)
}
}
@@ -0,0 +1,166 @@
package backedpipe
import (
"io"
"sync"
)
// BackedReader wraps an unreliable io.Reader and makes it resilient to disconnections.
// It tracks sequence numbers for all bytes read and can handle reconnection,
// blocking reads when disconnected instead of erroring.
type BackedReader struct {
mu sync.Mutex
cond *sync.Cond
reader io.Reader
sequenceNum uint64
closed bool
// Error channel for generation-aware error reporting
errorEventChan chan<- ErrorEvent
// Current connection generation for error reporting
currentGen uint64
}
// NewBackedReader creates a new BackedReader with generation-aware error reporting.
// The reader is initially disconnected and must be connected using Reconnect before
// reads will succeed. The errorEventChan will receive ErrorEvent structs containing
// error details, component info, and connection generation.
func NewBackedReader(errorEventChan chan<- ErrorEvent) *BackedReader {
if errorEventChan == nil {
panic("error event channel cannot be nil")
}
br := &BackedReader{
errorEventChan: errorEventChan,
}
br.cond = sync.NewCond(&br.mu)
return br
}
// Read implements io.Reader. It blocks when disconnected until either:
// 1. A reconnection is established
// 2. The reader is closed
//
// When connected, it reads from the underlying reader and updates sequence numbers.
// Connection failures are automatically detected and reported to the higher layer via callback.
func (br *BackedReader) Read(p []byte) (int, error) {
br.mu.Lock()
defer br.mu.Unlock()
for {
// Step 1: Wait until we have a reader or are closed
for br.reader == nil && !br.closed {
br.cond.Wait()
}
if br.closed {
return 0, io.EOF
}
// Step 2: Perform the read while holding the mutex
// This ensures proper synchronization with Reconnect and Close operations
n, err := br.reader.Read(p)
br.sequenceNum += uint64(n) // #nosec G115 -- n is always >= 0 per io.Reader contract
if err == nil {
return n, nil
}
// Mark reader as disconnected so future reads will wait for reconnection
br.reader = nil
// Notify parent of error with generation information
select {
case br.errorEventChan <- ErrorEvent{
Err: err,
Component: "reader",
Generation: br.currentGen,
}:
default:
// Channel is full, drop the error.
// This is not a problem, because we set the reader to nil
// and block until reconnected so no new errors will be sent
// until pipe processes the error and reconnects.
}
// If we got some data before the error, return it now
if n > 0 {
return n, nil
}
}
}
// Reconnect coordinates reconnection using channels for better synchronization.
// The seqNum channel is used to send the current sequence number to the caller.
// The newR channel is used to receive the new reader from the caller.
// This allows for better coordination during the reconnection process.
func (br *BackedReader) Reconnect(seqNum chan<- uint64, newR <-chan io.Reader) {
// Grab the lock
br.mu.Lock()
defer br.mu.Unlock()
if br.closed {
// Close the channel to indicate closed state
close(seqNum)
return
}
// Get the sequence number to send to the other side via seqNum channel
seqNum <- br.sequenceNum
close(seqNum)
// Wait for the reconnect to complete, via newR channel, and give us a new io.Reader
newReader := <-newR
// If reconnection fails while we are starting it, the caller sends nil on newR
if newReader == nil {
// Reconnection failed, keep current state
return
}
// Reconnection successful
br.reader = newReader
// Notify any waiting reads via the cond
br.cond.Broadcast()
}
// Close the reader and wake up any blocked reads.
// After closing, all Read calls will return io.EOF.
func (br *BackedReader) Close() error {
br.mu.Lock()
defer br.mu.Unlock()
if br.closed {
return nil
}
br.closed = true
br.reader = nil
// Wake up any blocked reads
br.cond.Broadcast()
return nil
}
// SequenceNum returns the current sequence number (total bytes read).
func (br *BackedReader) SequenceNum() uint64 {
br.mu.Lock()
defer br.mu.Unlock()
return br.sequenceNum
}
// Connected returns whether the reader is currently connected.
func (br *BackedReader) Connected() bool {
br.mu.Lock()
defer br.mu.Unlock()
return br.reader != nil
}
// SetGeneration sets the current connection generation for error reporting.
func (br *BackedReader) SetGeneration(generation uint64) {
br.mu.Lock()
defer br.mu.Unlock()
br.currentGen = generation
}
@@ -0,0 +1,603 @@
package backedpipe_test
import (
"context"
"io"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/agent/immortalstreams/backedpipe"
"github.com/coder/coder/v2/testutil"
)
// mockReader implements io.Reader with controllable behavior for testing
type mockReader struct {
mu sync.Mutex
data []byte
pos int
err error
readFunc func([]byte) (int, error)
}
func newMockReader(data string) *mockReader {
return &mockReader{data: []byte(data)}
}
func (mr *mockReader) Read(p []byte) (int, error) {
mr.mu.Lock()
defer mr.mu.Unlock()
if mr.readFunc != nil {
return mr.readFunc(p)
}
if mr.err != nil {
return 0, mr.err
}
if mr.pos >= len(mr.data) {
return 0, io.EOF
}
n := copy(p, mr.data[mr.pos:])
mr.pos += n
return n, nil
}
func (mr *mockReader) setError(err error) {
mr.mu.Lock()
defer mr.mu.Unlock()
mr.err = err
}
func TestBackedReader_NewBackedReader(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
require.NotNil(t, br)
require.Equal(t, uint64(0), br.SequenceNum())
require.False(t, br.Connected())
}
func TestBackedReader_BasicReadOperation(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader := newMockReader("hello world")
// Connect the reader
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number from reader
seq := testutil.RequireReceive(ctx, t, seqNum)
require.Equal(t, uint64(0), seq)
// Send new reader
testutil.RequireSend(ctx, t, newR, io.Reader(reader))
// Read data
buf := make([]byte, 5)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, 5, n)
require.Equal(t, "hello", string(buf))
require.Equal(t, uint64(5), br.SequenceNum())
// Read more data
n, err = br.Read(buf)
require.NoError(t, err)
require.Equal(t, 5, n)
require.Equal(t, " worl", string(buf))
require.Equal(t, uint64(10), br.SequenceNum())
}
func TestBackedReader_ReadBlocksWhenDisconnected(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
// Start a read operation that should block
readDone := make(chan struct{})
var readErr error
var readBuf []byte
var readN int
go func() {
defer close(readDone)
buf := make([]byte, 10)
readN, readErr = br.Read(buf)
readBuf = buf[:readN]
}()
// Ensure the read is actually blocked by verifying it hasn't completed
// and that the reader is not connected
select {
case <-readDone:
t.Fatal("Read should be blocked when disconnected")
default:
// Read is still blocked, which is what we want
}
require.False(t, br.Connected(), "Reader should not be connected")
// Connect and the read should unblock
reader := newMockReader("test")
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send new reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, io.Reader(reader))
// Wait for read to complete
testutil.TryReceive(ctx, t, readDone)
require.NoError(t, readErr)
require.Equal(t, "test", string(readBuf))
}
func TestBackedReader_ReconnectionAfterFailure(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader1 := newMockReader("first")
// Initial connection
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send new reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, io.Reader(reader1))
// Read some data
buf := make([]byte, 5)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, "first", string(buf[:n]))
require.Equal(t, uint64(5), br.SequenceNum())
// Simulate connection failure
reader1.setError(xerrors.New("connection lost"))
// Start a read that will block due to connection failure
readDone := make(chan error, 1)
go func() {
_, err := br.Read(buf)
readDone <- err
}()
// Wait for the error to be reported via error channel
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Error(t, receivedErrorEvent.Err)
require.Equal(t, "reader", receivedErrorEvent.Component)
require.Contains(t, receivedErrorEvent.Err.Error(), "connection lost")
// Verify read is still blocked
select {
case err := <-readDone:
t.Fatalf("Read should still be blocked, but completed with: %v", err)
default:
// Good, still blocked
}
// Verify disconnection
require.False(t, br.Connected())
// Reconnect with new reader
reader2 := newMockReader("second")
seqNum2 := make(chan uint64, 1)
newR2 := make(chan io.Reader, 1)
go br.Reconnect(seqNum2, newR2)
// Get sequence number and send new reader
seq := testutil.RequireReceive(ctx, t, seqNum2)
require.Equal(t, uint64(5), seq) // Should return current sequence number
testutil.RequireSend(ctx, t, newR2, io.Reader(reader2))
// Wait for read to unblock and succeed with new data
readErr := testutil.RequireReceive(ctx, t, readDone)
require.NoError(t, readErr) // Should succeed with new reader
require.True(t, br.Connected())
}
func TestBackedReader_Close(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader := newMockReader("test")
// Connect
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send new reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, io.Reader(reader))
// First, read all available data
buf := make([]byte, 10)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, 4, n) // "test" is 4 bytes
// Close the reader before EOF triggers reconnection
err = br.Close()
require.NoError(t, err)
// After close, reads should return EOF
n, err = br.Read(buf)
require.Equal(t, 0, n)
require.Equal(t, io.EOF, err)
// Subsequent reads should return EOF
_, err = br.Read(buf)
require.Equal(t, io.EOF, err)
}
func TestBackedReader_CloseIdempotent(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
err := br.Close()
require.NoError(t, err)
// Second close should be no-op
err = br.Close()
require.NoError(t, err)
}
func TestBackedReader_ReconnectAfterClose(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
err := br.Close()
require.NoError(t, err)
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Should get 0 sequence number for closed reader
seq := testutil.TryReceive(ctx, t, seqNum)
require.Equal(t, uint64(0), seq)
}
// Helper function to reconnect a reader using channels
func reconnectReader(ctx context.Context, t testing.TB, br *backedpipe.BackedReader, reader io.Reader) {
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send new reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, reader)
}
func TestBackedReader_SequenceNumberTracking(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader := newMockReader("0123456789")
reconnectReader(ctx, t, br, reader)
// Read in chunks and verify sequence number
buf := make([]byte, 3)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, 3, n)
require.Equal(t, uint64(3), br.SequenceNum())
n, err = br.Read(buf)
require.NoError(t, err)
require.Equal(t, 3, n)
require.Equal(t, uint64(6), br.SequenceNum())
n, err = br.Read(buf)
require.NoError(t, err)
require.Equal(t, 3, n)
require.Equal(t, uint64(9), br.SequenceNum())
}
func TestBackedReader_EOFHandling(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader := newMockReader("test")
reconnectReader(ctx, t, br, reader)
// Read all data
buf := make([]byte, 10)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, 4, n)
require.Equal(t, "test", string(buf[:n]))
// Next read should encounter EOF, which triggers disconnection
// The read should block waiting for reconnection
readDone := make(chan struct{})
var readErr error
var readN int
go func() {
defer close(readDone)
readN, readErr = br.Read(buf)
}()
// Wait for EOF to be reported via error channel
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Equal(t, io.EOF, receivedErrorEvent.Err)
require.Equal(t, "reader", receivedErrorEvent.Component)
// Reader should be disconnected after EOF
require.False(t, br.Connected())
// Read should still be blocked
select {
case <-readDone:
t.Fatal("Read should be blocked waiting for reconnection after EOF")
default:
// Good, still blocked
}
// Reconnect with new data
reader2 := newMockReader("more")
reconnectReader(ctx, t, br, reader2)
// Wait for the blocked read to complete with new data
testutil.TryReceive(ctx, t, readDone)
require.NoError(t, readErr)
require.Equal(t, 4, readN)
require.Equal(t, "more", string(buf[:readN]))
}
func BenchmarkBackedReader_Read(b *testing.B) {
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
buf := make([]byte, 1024)
// Create a reader that never returns EOF by cycling through data
reader := &mockReader{
readFunc: func(p []byte) (int, error) {
// Fill buffer with 'x' characters - never EOF
for i := range p {
p[i] = 'x'
}
return len(p), nil
},
}
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
defer cancel()
reconnectReader(ctx, b, br, reader)
b.ResetTimer()
for i := 0; i < b.N; i++ {
br.Read(buf)
}
}
func TestBackedReader_PartialReads(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
// Create a reader that returns partial reads
reader := &mockReader{
readFunc: func(p []byte) (int, error) {
// Always return just 1 byte at a time
if len(p) == 0 {
return 0, nil
}
p[0] = 'A'
return 1, nil
},
}
reconnectReader(ctx, t, br, reader)
// Read multiple times
buf := make([]byte, 10)
for i := 0; i < 5; i++ {
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, 1, n)
require.Equal(t, byte('A'), buf[0])
}
require.Equal(t, uint64(5), br.SequenceNum())
}
func TestBackedReader_CloseWhileBlockedOnUnderlyingReader(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
// Create a reader that blocks on Read calls but can be unblocked
readStarted := make(chan struct{}, 1)
readUnblocked := make(chan struct{})
blockingReader := &mockReader{
readFunc: func(p []byte) (int, error) {
select {
case readStarted <- struct{}{}:
default:
}
<-readUnblocked // Block until signaled
// After unblocking, return an error to simulate connection failure
return 0, xerrors.New("connection interrupted")
},
}
// Connect the blocking reader
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send blocking reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, io.Reader(blockingReader))
// Start a read that will block on the underlying reader
readDone := make(chan struct{})
var readErr error
var readN int
go func() {
defer close(readDone)
buf := make([]byte, 10)
readN, readErr = br.Read(buf)
}()
// Wait for the read to start and block on the underlying reader
testutil.RequireReceive(ctx, t, readStarted)
// Verify read is blocked by checking that it hasn't completed
// and ensuring we have adequate time for it to reach the blocking state
require.Eventually(t, func() bool {
select {
case <-readDone:
t.Fatal("Read should be blocked on underlying reader")
return false
default:
// Good, still blocked
return true
}
}, testutil.WaitShort, testutil.IntervalMedium)
// Start Close() in a goroutine since it will block until the underlying read completes
closeDone := make(chan error, 1)
go func() {
closeDone <- br.Close()
}()
// Verify Close() is also blocked waiting for the underlying read
select {
case <-closeDone:
t.Fatal("Close should be blocked until underlying read completes")
case <-time.After(10 * time.Millisecond):
// Good, Close is blocked
}
// Unblock the underlying reader, which will cause both the read and close to complete
close(readUnblocked)
// Wait for both the read and close to complete
testutil.TryReceive(ctx, t, readDone)
closeErr := testutil.RequireReceive(ctx, t, closeDone)
require.NoError(t, closeErr)
// The read should return EOF because Close() was called while it was blocked,
// even though the underlying reader returned an error
require.Equal(t, 0, readN)
require.Equal(t, io.EOF, readErr)
// Subsequent reads should return EOF since the reader is now closed
buf := make([]byte, 10)
n, err := br.Read(buf)
require.Equal(t, 0, n)
require.Equal(t, io.EOF, err)
}
func TestBackedReader_CloseWhileBlockedWaitingForReconnect(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
br := backedpipe.NewBackedReader(errChan)
reader1 := newMockReader("initial")
// Initial connection
seqNum := make(chan uint64, 1)
newR := make(chan io.Reader, 1)
go br.Reconnect(seqNum, newR)
// Get sequence number and send initial reader
testutil.RequireReceive(ctx, t, seqNum)
testutil.RequireSend(ctx, t, newR, io.Reader(reader1))
// Read initial data
buf := make([]byte, 10)
n, err := br.Read(buf)
require.NoError(t, err)
require.Equal(t, "initial", string(buf[:n]))
// Simulate connection failure
reader1.setError(xerrors.New("connection lost"))
// Start a read that will block waiting for reconnection
readDone := make(chan struct{})
var readErr error
var readN int
go func() {
defer close(readDone)
readN, readErr = br.Read(buf)
}()
// Wait for the error to be reported (indicating disconnection)
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Error(t, receivedErrorEvent.Err)
require.Equal(t, "reader", receivedErrorEvent.Component)
require.Contains(t, receivedErrorEvent.Err.Error(), "connection lost")
// Verify read is blocked waiting for reconnection
select {
case <-readDone:
t.Fatal("Read should be blocked waiting for reconnection")
default:
// Good, still blocked
}
// Verify reader is disconnected
require.False(t, br.Connected())
// Close the BackedReader while read is blocked waiting for reconnection
err = br.Close()
require.NoError(t, err)
// The read should unblock and return EOF
testutil.TryReceive(ctx, t, readDone)
require.Equal(t, 0, readN)
require.Equal(t, io.EOF, readErr)
}
@@ -0,0 +1,243 @@
package backedpipe
import (
"io"
"os"
"sync"
"golang.org/x/xerrors"
)
var (
ErrWriterClosed = xerrors.New("cannot reconnect closed writer")
ErrNilWriter = xerrors.New("new writer cannot be nil")
ErrFutureSequence = xerrors.New("cannot replay from future sequence")
ErrReplayDataUnavailable = xerrors.New("failed to read replay data")
ErrReplayFailed = xerrors.New("replay failed")
ErrPartialReplay = xerrors.New("partial replay")
)
// BackedWriter wraps an unreliable io.Writer and makes it resilient to disconnections.
// It maintains a ring buffer of recent writes for replay during reconnection.
type BackedWriter struct {
mu sync.Mutex
cond *sync.Cond
writer io.Writer
buffer *ringBuffer
sequenceNum uint64 // total bytes written
closed bool
// Error channel for generation-aware error reporting
errorEventChan chan<- ErrorEvent
// Current connection generation for error reporting
currentGen uint64
}
// NewBackedWriter creates a new BackedWriter with generation-aware error reporting.
// The writer is initially disconnected and will block writes until connected.
// The errorEventChan will receive ErrorEvent structs containing error details,
// component info, and connection generation. Capacity must be > 0.
func NewBackedWriter(capacity int, errorEventChan chan<- ErrorEvent) *BackedWriter {
if capacity <= 0 {
panic("backed writer capacity must be > 0")
}
if errorEventChan == nil {
panic("error event channel cannot be nil")
}
bw := &BackedWriter{
buffer: newRingBuffer(capacity),
errorEventChan: errorEventChan,
}
bw.cond = sync.NewCond(&bw.mu)
return bw
}
// blockUntilConnectedOrClosed blocks until either a writer is available or the BackedWriter is closed.
// Returns os.ErrClosed if closed while waiting, nil if connected. You must hold the mutex to call this.
func (bw *BackedWriter) blockUntilConnectedOrClosed() error {
for bw.writer == nil && !bw.closed {
bw.cond.Wait()
}
if bw.closed {
return os.ErrClosed
}
return nil
}
// Write implements io.Writer.
// When connected, it writes to both the ring buffer (to preserve data in case we need to replay it)
// and the underlying writer.
// If the underlying write fails, the writer is marked as disconnected and the write blocks
// until reconnection occurs.
func (bw *BackedWriter) Write(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
bw.mu.Lock()
defer bw.mu.Unlock()
// Block until connected
if err := bw.blockUntilConnectedOrClosed(); err != nil {
return 0, err
}
// Write to buffer
bw.buffer.Write(p)
bw.sequenceNum += uint64(len(p))
// Try to write to underlying writer
n, err := bw.writer.Write(p)
if err == nil && n != len(p) {
err = io.ErrShortWrite
}
if err != nil {
// Connection failed or partial write, mark as disconnected
bw.writer = nil
// Notify parent of error with generation information
select {
case bw.errorEventChan <- ErrorEvent{
Err: err,
Component: "writer",
Generation: bw.currentGen,
}:
default:
// Channel is full, drop the error.
// This is not a problem, because we set the writer to nil
// and block until reconnected so no new errors will be sent
// until pipe processes the error and reconnects.
}
// Block until reconnected - reconnection will replay this data
if err := bw.blockUntilConnectedOrClosed(); err != nil {
return 0, err
}
// Don't retry - reconnection replay handled it
return len(p), nil
}
// Write succeeded
return len(p), nil
}
// Reconnect replaces the current writer with a new one and replays data from the specified
// sequence number. If the requested sequence number is no longer in the buffer,
// returns an error indicating data loss.
//
// IMPORTANT: You must close the current writer, if any, before calling this method.
// Otherwise, if a Write operation is currently blocked in the underlying writer's
// Write method, this method will deadlock waiting for the mutex that Write holds.
func (bw *BackedWriter) Reconnect(replayFromSeq uint64, newWriter io.Writer) error {
bw.mu.Lock()
defer bw.mu.Unlock()
if bw.closed {
return ErrWriterClosed
}
if newWriter == nil {
return ErrNilWriter
}
// Check if we can replay from the requested sequence number
if replayFromSeq > bw.sequenceNum {
return ErrFutureSequence
}
// Calculate how many bytes we need to replay
replayBytes := bw.sequenceNum - replayFromSeq
var replayData []byte
if replayBytes > 0 {
// Get the last replayBytes from buffer
// If the buffer doesn't have enough data (some was evicted),
// ReadLast will return an error
var err error
// Safe conversion: The check above (replayFromSeq > bw.sequenceNum) ensures
// replayBytes = bw.sequenceNum - replayFromSeq is always <= bw.sequenceNum.
// Since sequence numbers are much smaller than maxInt, the uint64->int conversion is safe.
//nolint:gosec // Safe conversion: replayBytes <= sequenceNum, which is much less than maxInt
replayData, err = bw.buffer.ReadLast(int(replayBytes))
if err != nil {
return ErrReplayDataUnavailable
}
}
// Clear the current writer first in case replay fails
bw.writer = nil
// Replay data if needed. We keep the mutex held during replay to ensure
// no concurrent operations can interfere with the reconnection process.
if len(replayData) > 0 {
n, err := newWriter.Write(replayData)
if err != nil {
// Reconnect failed, writer remains nil
return ErrReplayFailed
}
if n != len(replayData) {
// Reconnect failed, writer remains nil
return ErrPartialReplay
}
}
// Set new writer only after successful replay. This ensures no concurrent
// writes can interfere with the replay operation.
bw.writer = newWriter
// Wake up any operations waiting for connection
bw.cond.Broadcast()
return nil
}
// Close closes the writer and prevents further writes.
// After closing, all Write calls will return os.ErrClosed.
// This code keeps the Close() signature consistent with io.Closer,
// but it never actually returns an error.
//
// IMPORTANT: You must close the current underlying writer, if any, before calling
// this method. Otherwise, if a Write operation is currently blocked in the
// underlying writer's Write method, this method will deadlock waiting for the
// mutex that Write holds.
func (bw *BackedWriter) Close() error {
bw.mu.Lock()
defer bw.mu.Unlock()
if bw.closed {
return nil
}
bw.closed = true
bw.writer = nil
// Wake up any blocked operations
bw.cond.Broadcast()
return nil
}
// SequenceNum returns the current sequence number (total bytes written).
func (bw *BackedWriter) SequenceNum() uint64 {
bw.mu.Lock()
defer bw.mu.Unlock()
return bw.sequenceNum
}
// Connected returns whether the writer is currently connected.
func (bw *BackedWriter) Connected() bool {
bw.mu.Lock()
defer bw.mu.Unlock()
return bw.writer != nil
}
// SetGeneration sets the current connection generation for error reporting.
func (bw *BackedWriter) SetGeneration(generation uint64) {
bw.mu.Lock()
defer bw.mu.Unlock()
bw.currentGen = generation
}
@@ -0,0 +1,992 @@
package backedpipe_test
import (
"bytes"
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/agent/immortalstreams/backedpipe"
"github.com/coder/coder/v2/testutil"
)
// mockWriter implements io.Writer with controllable behavior for testing
type mockWriter struct {
mu sync.Mutex
buffer bytes.Buffer
err error
writeFunc func([]byte) (int, error)
writeCalls int
}
func newMockWriter() *mockWriter {
return &mockWriter{}
}
// newBackedWriterForTest creates a BackedWriter with a small buffer for testing eviction behavior
func newBackedWriterForTest(bufferSize int) *backedpipe.BackedWriter {
errChan := make(chan backedpipe.ErrorEvent, 1)
return backedpipe.NewBackedWriter(bufferSize, errChan)
}
func (mw *mockWriter) Write(p []byte) (int, error) {
mw.mu.Lock()
defer mw.mu.Unlock()
mw.writeCalls++
if mw.writeFunc != nil {
return mw.writeFunc(p)
}
if mw.err != nil {
return 0, mw.err
}
return mw.buffer.Write(p)
}
func (mw *mockWriter) Len() int {
mw.mu.Lock()
defer mw.mu.Unlock()
return mw.buffer.Len()
}
func (mw *mockWriter) Reset() {
mw.mu.Lock()
defer mw.mu.Unlock()
mw.buffer.Reset()
mw.writeCalls = 0
mw.err = nil
mw.writeFunc = nil
}
func (mw *mockWriter) setError(err error) {
mw.mu.Lock()
defer mw.mu.Unlock()
mw.err = err
}
func TestBackedWriter_NewBackedWriter(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
require.NotNil(t, bw)
require.Equal(t, uint64(0), bw.SequenceNum())
require.False(t, bw.Connected())
}
func TestBackedWriter_WriteBlocksWhenDisconnected(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Write should block when disconnected
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
n, writeErr = bw.Write([]byte("hello"))
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when disconnected")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Connect and verify write completes
writer := newMockWriter()
err := bw.Reconnect(0, writer)
require.NoError(t, err)
// Write should now complete
testutil.TryReceive(ctx, t, writeComplete)
require.NoError(t, writeErr)
require.Equal(t, 5, n)
require.Equal(t, uint64(5), bw.SequenceNum())
require.Equal(t, []byte("hello"), writer.buffer.Bytes())
}
func TestBackedWriter_WriteToUnderlyingWhenConnected(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
writer := newMockWriter()
// Connect
err := bw.Reconnect(0, writer)
require.NoError(t, err)
require.True(t, bw.Connected())
// Write should go to both buffer and underlying writer
n, err := bw.Write([]byte("hello"))
require.NoError(t, err)
require.Equal(t, 5, n)
// Data should be buffered
require.Equal(t, uint64(5), bw.SequenceNum())
// Check underlying writer
require.Equal(t, []byte("hello"), writer.buffer.Bytes())
}
func TestBackedWriter_BlockOnWriteFailure(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
writer := newMockWriter()
// Connect
err := bw.Reconnect(0, writer)
require.NoError(t, err)
// Cause write to fail
writer.setError(xerrors.New("write failed"))
// Write should block when underlying writer fails, not succeed immediately
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
n, writeErr = bw.Write([]byte("hello"))
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when underlying writer fails")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Wait for error event which implies writer was marked disconnected
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Contains(t, receivedErrorEvent.Err.Error(), "write failed")
require.Equal(t, "writer", receivedErrorEvent.Component)
require.False(t, bw.Connected())
// Reconnect with working writer and verify write completes
writer2 := newMockWriter()
err = bw.Reconnect(0, writer2) // Replay from beginning
require.NoError(t, err)
// Write should now complete
testutil.TryReceive(ctx, t, writeComplete)
require.NoError(t, writeErr)
require.Equal(t, 5, n)
require.Equal(t, uint64(5), bw.SequenceNum())
require.Equal(t, []byte("hello"), writer2.buffer.Bytes())
}
func TestBackedWriter_ReplayOnReconnect(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Connect initially to write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
// Write some data while connected
_, err = bw.Write([]byte("hello"))
require.NoError(t, err)
_, err = bw.Write([]byte(" world"))
require.NoError(t, err)
require.Equal(t, uint64(11), bw.SequenceNum())
// Disconnect by causing a write failure
writer1.setError(xerrors.New("connection lost"))
// Write should block when underlying writer fails
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
n, writeErr = bw.Write([]byte("test"))
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when underlying writer fails")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Wait for error event which implies writer was marked disconnected
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Contains(t, receivedErrorEvent.Err.Error(), "connection lost")
require.Equal(t, "writer", receivedErrorEvent.Component)
require.False(t, bw.Connected())
// Reconnect with new writer and request replay from beginning
writer2 := newMockWriter()
err = bw.Reconnect(0, writer2)
require.NoError(t, err)
// Write should now complete
select {
case <-writeComplete:
// Expected - write completed
case <-time.After(100 * time.Millisecond):
t.Fatal("Write should have completed after reconnection")
}
require.NoError(t, writeErr)
require.Equal(t, 4, n)
// Should have replayed all data including the failed write that was buffered
require.Equal(t, []byte("hello worldtest"), writer2.buffer.Bytes())
// Write new data should go to both
_, err = bw.Write([]byte("!"))
require.NoError(t, err)
require.Equal(t, []byte("hello worldtest!"), writer2.buffer.Bytes())
}
func TestBackedWriter_PartialReplay(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Connect initially to write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
// Write some data
_, err = bw.Write([]byte("hello"))
require.NoError(t, err)
_, err = bw.Write([]byte(" world"))
require.NoError(t, err)
_, err = bw.Write([]byte("!"))
require.NoError(t, err)
// Reconnect with new writer and request replay from middle
writer2 := newMockWriter()
err = bw.Reconnect(5, writer2) // From " world!"
require.NoError(t, err)
// Should have replayed only the requested portion
require.Equal(t, []byte(" world!"), writer2.buffer.Bytes())
}
func TestBackedWriter_ReplayFromFutureSequence(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Connect initially to write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
_, err = bw.Write([]byte("hello"))
require.NoError(t, err)
writer2 := newMockWriter()
err = bw.Reconnect(10, writer2) // Future sequence
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrFutureSequence)
}
func TestBackedWriter_ReplayDataLoss(t *testing.T) {
t.Parallel()
bw := newBackedWriterForTest(10) // Small buffer for testing
// Connect initially to write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
// Fill buffer beyond capacity to cause eviction
_, err = bw.Write([]byte("0123456789")) // Fills buffer exactly
require.NoError(t, err)
_, err = bw.Write([]byte("abcdef")) // Should evict "012345"
require.NoError(t, err)
writer2 := newMockWriter()
err = bw.Reconnect(0, writer2) // Try to replay from evicted data
// With the new error handling, this should fail because we can't read all the data
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrReplayDataUnavailable)
}
func TestBackedWriter_BufferEviction(t *testing.T) {
t.Parallel()
bw := newBackedWriterForTest(5) // Very small buffer for testing
// Connect initially
writer := newMockWriter()
err := bw.Reconnect(0, writer)
require.NoError(t, err)
// Write data that will cause eviction
n, err := bw.Write([]byte("abcde"))
require.NoError(t, err)
require.Equal(t, 5, n)
// Write more to cause eviction
n, err = bw.Write([]byte("fg"))
require.NoError(t, err)
require.Equal(t, 2, n)
// Verify that the buffer contains only the latest data after eviction
// Total sequence number should be 7 (5 + 2)
require.Equal(t, uint64(7), bw.SequenceNum())
// Try to reconnect from the beginning - this should fail because
// the early data was evicted from the buffer
writer2 := newMockWriter()
err = bw.Reconnect(0, writer2)
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrReplayDataUnavailable)
// However, reconnecting from a sequence that's still in the buffer should work
// The buffer should contain the last 5 bytes: "cdefg"
writer3 := newMockWriter()
err = bw.Reconnect(2, writer3) // From sequence 2, should replay "cdefg"
require.NoError(t, err)
require.Equal(t, []byte("cdefg"), writer3.buffer.Bytes())
require.True(t, bw.Connected())
}
func TestBackedWriter_Close(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
writer := newMockWriter()
bw.Reconnect(0, writer)
err := bw.Close()
require.NoError(t, err)
// Writes after close should fail
_, err = bw.Write([]byte("test"))
require.Equal(t, os.ErrClosed, err)
// Reconnect after close should fail
err = bw.Reconnect(0, newMockWriter())
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrWriterClosed)
}
func TestBackedWriter_CloseIdempotent(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
err := bw.Close()
require.NoError(t, err)
// Second close should be no-op
err = bw.Close()
require.NoError(t, err)
}
func TestBackedWriter_ReconnectDuringReplay(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Connect initially to write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
_, err = bw.Write([]byte("hello world"))
require.NoError(t, err)
// Create a writer that fails during replay
writer2 := &mockWriter{
err: backedpipe.ErrReplayFailed,
}
err = bw.Reconnect(0, writer2)
require.Error(t, err)
require.ErrorIs(t, err, backedpipe.ErrReplayFailed)
require.False(t, bw.Connected())
}
func TestBackedWriter_BlockOnPartialWrite(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Create writer that does partial writes
writer := &mockWriter{
writeFunc: func(p []byte) (int, error) {
if len(p) > 3 {
return 3, nil // Only write first 3 bytes
}
return len(p), nil
},
}
bw.Reconnect(0, writer)
// Write should block due to partial write
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
n, writeErr = bw.Write([]byte("hello"))
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when underlying writer does partial write")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Wait for error event which implies writer was marked disconnected
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Contains(t, receivedErrorEvent.Err.Error(), "short write")
require.Equal(t, "writer", receivedErrorEvent.Component)
require.False(t, bw.Connected())
// Reconnect with working writer and verify write completes
writer2 := newMockWriter()
err := bw.Reconnect(0, writer2) // Replay from beginning
require.NoError(t, err)
// Write should now complete
testutil.TryReceive(ctx, t, writeComplete)
require.NoError(t, writeErr)
require.Equal(t, 5, n)
require.Equal(t, uint64(5), bw.SequenceNum())
require.Equal(t, []byte("hello"), writer2.buffer.Bytes())
}
func TestBackedWriter_WriteUnblocksOnReconnect(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Start a single write that should block
writeResult := make(chan error, 1)
go func() {
_, err := bw.Write([]byte("test"))
writeResult <- err
}()
// Verify write is blocked
select {
case <-writeResult:
t.Fatal("Write should have blocked when disconnected")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Connect and verify write completes
writer := newMockWriter()
err := bw.Reconnect(0, writer)
require.NoError(t, err)
// Write should now complete
err = testutil.RequireReceive(ctx, t, writeResult)
require.NoError(t, err)
// Write should have been written to the underlying writer
require.Equal(t, "test", writer.buffer.String())
}
func TestBackedWriter_CloseUnblocksWaitingWrites(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Start a write that should block
writeComplete := make(chan error, 1)
go func() {
_, err := bw.Write([]byte("test"))
writeComplete <- err
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked when disconnected")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Close the writer
err := bw.Close()
require.NoError(t, err)
// Write should now complete with error
err = testutil.RequireReceive(ctx, t, writeComplete)
require.Equal(t, os.ErrClosed, err)
}
func TestBackedWriter_WriteBlocksAfterDisconnection(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
writer := newMockWriter()
// Connect initially
err := bw.Reconnect(0, writer)
require.NoError(t, err)
// Write should succeed when connected
_, err = bw.Write([]byte("hello"))
require.NoError(t, err)
// Cause disconnection - the write should now block instead of returning an error
writer.setError(xerrors.New("connection lost"))
// This write should block
writeComplete := make(chan error, 1)
go func() {
_, err := bw.Write([]byte("world"))
writeComplete <- err
}()
// Verify write is blocked
select {
case <-writeComplete:
t.Fatal("Write should have blocked after disconnection")
case <-time.After(50 * time.Millisecond):
// Expected - write is blocked
}
// Wait for error event which implies writer was marked disconnected
receivedErrorEvent := testutil.RequireReceive(ctx, t, errChan)
require.Contains(t, receivedErrorEvent.Err.Error(), "connection lost")
require.Equal(t, "writer", receivedErrorEvent.Component)
require.False(t, bw.Connected())
// Reconnect and verify write completes
writer2 := newMockWriter()
err = bw.Reconnect(5, writer2) // Replay from after "hello"
require.NoError(t, err)
err = testutil.RequireReceive(ctx, t, writeComplete)
require.NoError(t, err)
// Check that only "world" was written during replay (not duplicated)
require.Equal(t, []byte("world"), writer2.buffer.Bytes()) // Only "world" since we replayed from sequence 5
}
func TestBackedWriter_ConcurrentWriteAndClose(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Don't connect initially - this will cause writes to block in blockUntilConnectedOrClosed()
writeStarted := make(chan struct{}, 1)
// Start a write operation that will block waiting for connection
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
// Signal that we're about to start the write
writeStarted <- struct{}{}
// This write will block in blockUntilConnectedOrClosed() since no writer is connected
n, writeErr = bw.Write([]byte("hello"))
}()
// Wait for write goroutine to start
ctx := testutil.Context(t, testutil.WaitShort)
testutil.RequireReceive(ctx, t, writeStarted)
// Ensure the write is actually blocked by repeatedly checking that:
// 1. The write hasn't completed yet
// 2. The writer is still not connected
// We use require.Eventually to give it a fair chance to reach the blocking state
require.Eventually(t, func() bool {
select {
case <-writeComplete:
t.Fatal("Write should be blocked when no writer is connected")
return false
default:
// Write is still blocked, which is what we want
return !bw.Connected()
}
}, testutil.WaitShort, testutil.IntervalMedium)
// Close the writer while the write is blocked waiting for connection
closeErr := bw.Close()
require.NoError(t, closeErr)
// Wait for write to complete
select {
case <-writeComplete:
// Good, write completed
case <-ctx.Done():
t.Fatal("Write did not complete in time")
}
// The write should have failed with os.ErrClosed because Close() was called
// while it was waiting for connection
require.ErrorIs(t, writeErr, os.ErrClosed)
require.Equal(t, 0, n)
// Subsequent writes should also fail
n, err := bw.Write([]byte("world"))
require.Equal(t, 0, n)
require.ErrorIs(t, err, os.ErrClosed)
}
func TestBackedWriter_ConcurrentWriteAndReconnect(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Initial connection
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
// Write some initial data
_, err = bw.Write([]byte("initial"))
require.NoError(t, err)
// Start reconnection which will block new writes
replayStarted := make(chan struct{}, 1) // Buffered to prevent race condition
replayCanComplete := make(chan struct{})
writer2 := &mockWriter{
writeFunc: func(p []byte) (int, error) {
// Signal that replay has started
select {
case replayStarted <- struct{}{}:
default:
// Signal already sent, which is fine
}
// Wait for test to allow replay to complete
<-replayCanComplete
return len(p), nil
},
}
// Start the reconnection in a goroutine so we can control timing
reconnectComplete := make(chan error, 1)
go func() {
reconnectComplete <- bw.Reconnect(0, writer2)
}()
ctx := testutil.Context(t, testutil.WaitShort)
// Wait for replay to start
testutil.RequireReceive(ctx, t, replayStarted)
// Now start a write operation that will be blocked by the ongoing reconnect
writeStarted := make(chan struct{}, 1)
writeComplete := make(chan struct{})
var writeErr error
var n int
go func() {
defer close(writeComplete)
// Signal that we're about to start the write
writeStarted <- struct{}{}
// This write should be blocked during reconnect
n, writeErr = bw.Write([]byte("blocked"))
}()
// Wait for write to start
testutil.RequireReceive(ctx, t, writeStarted)
// Use a small timeout to ensure the write goroutine has a chance to get blocked
// on the mutex before we check if it's still blocked
writeCheckTimer := time.NewTimer(testutil.IntervalFast)
defer writeCheckTimer.Stop()
select {
case <-writeComplete:
t.Fatal("Write should be blocked during reconnect")
case <-writeCheckTimer.C:
// Write is still blocked after a reasonable wait
}
// Allow replay to complete, which will allow reconnect to finish
close(replayCanComplete)
// Wait for reconnection to complete
select {
case reconnectErr := <-reconnectComplete:
require.NoError(t, reconnectErr)
case <-ctx.Done():
t.Fatal("Reconnect did not complete in time")
}
// Wait for write to complete
<-writeComplete
// Write should succeed after reconnection completes
require.NoError(t, writeErr)
require.Equal(t, 7, n) // "blocked" is 7 bytes
// Verify the writer is connected
require.True(t, bw.Connected())
}
func TestBackedWriter_ConcurrentReconnectAndClose(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Initial connection and write some data
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
_, err = bw.Write([]byte("test data"))
require.NoError(t, err)
// Start reconnection with slow replay
reconnectStarted := make(chan struct{}, 1)
replayCanComplete := make(chan struct{})
reconnectComplete := make(chan struct{})
var reconnectErr error
go func() {
defer close(reconnectComplete)
writer2 := &mockWriter{
writeFunc: func(p []byte) (int, error) {
// Signal that replay has started
select {
case reconnectStarted <- struct{}{}:
default:
}
// Wait for test to allow replay to complete
<-replayCanComplete
return len(p), nil
},
}
reconnectErr = bw.Reconnect(0, writer2)
}()
// Wait for reconnection to start
ctx := testutil.Context(t, testutil.WaitShort)
testutil.RequireReceive(ctx, t, reconnectStarted)
// Start Close() in a separate goroutine since it will block until Reconnect() completes
closeStarted := make(chan struct{}, 1)
closeComplete := make(chan error, 1)
go func() {
closeStarted <- struct{}{} // Signal that Close() is starting
closeComplete <- bw.Close()
}()
// Wait for Close() to start, then give it a moment to attempt to acquire the mutex
testutil.RequireReceive(ctx, t, closeStarted)
closeCheckTimer := time.NewTimer(testutil.IntervalFast)
defer closeCheckTimer.Stop()
select {
case <-closeComplete:
t.Fatal("Close should be blocked during reconnect")
case <-closeCheckTimer.C:
// Good, Close is still blocked after a reasonable wait
}
// Allow replay to complete so reconnection can finish
close(replayCanComplete)
// Wait for reconnect to complete
select {
case <-reconnectComplete:
// Good, reconnect completed
case <-ctx.Done():
t.Fatal("Reconnect did not complete in time")
}
// Wait for close to complete
select {
case closeErr := <-closeComplete:
require.NoError(t, closeErr)
case <-ctx.Done():
t.Fatal("Close did not complete in time")
}
// With mutex held during replay, Close() waits for Reconnect() to finish.
// So Reconnect() should succeed, then Close() runs and closes the writer.
require.NoError(t, reconnectErr)
// Verify writer is closed (Close() ran after Reconnect() completed)
require.False(t, bw.Connected())
}
func TestBackedWriter_MultipleWritesDuringReconnect(t *testing.T) {
t.Parallel()
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Initial connection
writer1 := newMockWriter()
err := bw.Reconnect(0, writer1)
require.NoError(t, err)
// Write some initial data
_, err = bw.Write([]byte("initial"))
require.NoError(t, err)
// Start multiple write operations
numWriters := 5
var wg sync.WaitGroup
writeResults := make([]error, numWriters)
writesStarted := make(chan struct{}, numWriters)
for i := 0; i < numWriters; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// Signal that this write is starting
writesStarted <- struct{}{}
data := []byte{byte('A' + id)}
_, writeResults[id] = bw.Write(data)
}(i)
}
// Wait for all writes to start
ctx := testutil.Context(t, testutil.WaitLong)
for i := 0; i < numWriters; i++ {
testutil.RequireReceive(ctx, t, writesStarted)
}
// Use a timer to ensure all write goroutines have had a chance to start executing
// and potentially get blocked on the mutex before we start the reconnection
writesReadyTimer := time.NewTimer(testutil.IntervalFast)
defer writesReadyTimer.Stop()
<-writesReadyTimer.C
// Start reconnection with controlled replay
replayStarted := make(chan struct{}, 1)
replayCanComplete := make(chan struct{})
writer2 := &mockWriter{
writeFunc: func(p []byte) (int, error) {
// Signal that replay has started
select {
case replayStarted <- struct{}{}:
default:
}
// Wait for test to allow replay to complete
<-replayCanComplete
return len(p), nil
},
}
// Start reconnection in a goroutine so we can control timing
reconnectComplete := make(chan error, 1)
go func() {
reconnectComplete <- bw.Reconnect(0, writer2)
}()
// Wait for replay to start
testutil.RequireReceive(ctx, t, replayStarted)
// Allow replay to complete
close(replayCanComplete)
// Wait for reconnection to complete
select {
case reconnectErr := <-reconnectComplete:
require.NoError(t, reconnectErr)
case <-ctx.Done():
t.Fatal("Reconnect did not complete in time")
}
// Wait for all writes to complete
wg.Wait()
// All writes should succeed
for i, err := range writeResults {
require.NoError(t, err, "Write %d should succeed", i)
}
// Verify the writer is connected
require.True(t, bw.Connected())
}
func BenchmarkBackedWriter_Write(b *testing.B) {
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan) // 64KB buffer
writer := newMockWriter()
bw.Reconnect(0, writer)
data := bytes.Repeat([]byte("x"), 1024) // 1KB writes
b.ResetTimer()
for i := 0; i < b.N; i++ {
bw.Write(data)
}
}
func BenchmarkBackedWriter_Reconnect(b *testing.B) {
errChan := make(chan backedpipe.ErrorEvent, 1)
bw := backedpipe.NewBackedWriter(backedpipe.DefaultBufferSize, errChan)
// Connect initially to fill buffer with data
initialWriter := newMockWriter()
err := bw.Reconnect(0, initialWriter)
if err != nil {
b.Fatal(err)
}
// Fill buffer with data
data := bytes.Repeat([]byte("x"), 1024)
for i := 0; i < 32; i++ {
bw.Write(data)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
writer := newMockWriter()
bw.Reconnect(0, writer)
}
}
@@ -0,0 +1,129 @@
package backedpipe
import "golang.org/x/xerrors"
// ringBuffer implements an efficient circular buffer with a fixed-size allocation.
// This implementation is not thread-safe and relies on external synchronization.
type ringBuffer struct {
buffer []byte
start int // index of first valid byte
end int // index of last valid byte (-1 when empty)
}
// newRingBuffer creates a new ring buffer with the specified capacity.
// Capacity must be > 0.
func newRingBuffer(capacity int) *ringBuffer {
if capacity <= 0 {
panic("ring buffer capacity must be > 0")
}
return &ringBuffer{
buffer: make([]byte, capacity),
end: -1, // -1 indicates empty buffer
}
}
// Size returns the current number of bytes in the buffer.
func (rb *ringBuffer) Size() int {
if rb.end == -1 {
return 0 // Buffer is empty
}
if rb.start <= rb.end {
return rb.end - rb.start + 1
}
// Buffer wraps around
return len(rb.buffer) - rb.start + rb.end + 1
}
// Write writes data to the ring buffer. If the buffer would overflow,
// it evicts the oldest data to make room for new data.
func (rb *ringBuffer) Write(data []byte) {
if len(data) == 0 {
return
}
capacity := len(rb.buffer)
// If data is larger than capacity, only keep the last capacity bytes
if len(data) > capacity {
data = data[len(data)-capacity:]
// Clear buffer and write new data
rb.start = 0
rb.end = -1 // Will be set properly below
}
// Calculate how much we need to evict to fit new data
spaceNeeded := len(data)
availableSpace := capacity - rb.Size()
if spaceNeeded > availableSpace {
bytesToEvict := spaceNeeded - availableSpace
rb.evict(bytesToEvict)
}
// Buffer has data, write after current end
writePos := (rb.end + 1) % capacity
if writePos+len(data) <= capacity {
// No wrap needed - single copy
copy(rb.buffer[writePos:], data)
rb.end = (rb.end + len(data)) % capacity
} else {
// Need to wrap around - two copies
firstChunk := capacity - writePos
copy(rb.buffer[writePos:], data[:firstChunk])
copy(rb.buffer[0:], data[firstChunk:])
rb.end = len(data) - firstChunk - 1
}
}
// evict removes the specified number of bytes from the beginning of the buffer.
func (rb *ringBuffer) evict(count int) {
if count >= rb.Size() {
// Evict everything
rb.start = 0
rb.end = -1
return
}
rb.start = (rb.start + count) % len(rb.buffer)
// Buffer remains non-empty after partial eviction
}
// ReadLast returns the last n bytes from the buffer.
// If n is greater than the available data, returns an error.
// If n is negative, returns an error.
func (rb *ringBuffer) ReadLast(n int) ([]byte, error) {
if n < 0 {
return nil, xerrors.New("cannot read negative number of bytes")
}
if n == 0 {
return nil, nil
}
size := rb.Size()
// If requested more than available, return error
if n > size {
return nil, xerrors.Errorf("requested %d bytes but only %d available", n, size)
}
result := make([]byte, n)
capacity := len(rb.buffer)
// Calculate where to start reading from (n bytes before the end)
startOffset := size - n
actualStart := (rb.start + startOffset) % capacity
// Copy the last n bytes
if actualStart+n <= capacity {
// No wrap needed
copy(result, rb.buffer[actualStart:actualStart+n])
} else {
// Need to wrap around
firstChunk := capacity - actualStart
copy(result[0:firstChunk], rb.buffer[actualStart:capacity])
copy(result[firstChunk:], rb.buffer[0:n-firstChunk])
}
return result, nil
}
@@ -0,0 +1,261 @@
package backedpipe
import (
"bytes"
"os"
"runtime"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"github.com/coder/coder/v2/testutil"
)
func TestMain(m *testing.M) {
if runtime.GOOS == "windows" {
// Don't run goleak on windows tests, they're super flaky right now.
// See: https://github.com/coder/coder/issues/8954
os.Exit(m.Run())
}
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
}
func TestRingBuffer_NewRingBuffer(t *testing.T) {
t.Parallel()
rb := newRingBuffer(100)
// Test that we can write and read from the buffer
rb.Write([]byte("test"))
data, err := rb.ReadLast(4)
require.NoError(t, err)
require.Equal(t, []byte("test"), data)
}
func TestRingBuffer_WriteAndRead(t *testing.T) {
t.Parallel()
rb := newRingBuffer(10)
// Write some data
rb.Write([]byte("hello"))
// Read last 4 bytes
data, err := rb.ReadLast(4)
require.NoError(t, err)
require.Equal(t, "ello", string(data))
// Write more data
rb.Write([]byte("world"))
// Read last 5 bytes
data, err = rb.ReadLast(5)
require.NoError(t, err)
require.Equal(t, "world", string(data))
// Read last 3 bytes
data, err = rb.ReadLast(3)
require.NoError(t, err)
require.Equal(t, "rld", string(data))
// Read more than available (should be 10 bytes total)
_, err = rb.ReadLast(15)
require.Error(t, err)
require.Contains(t, err.Error(), "requested 15 bytes but only")
}
func TestRingBuffer_OverflowEviction(t *testing.T) {
t.Parallel()
rb := newRingBuffer(5)
// Fill buffer
rb.Write([]byte("abcde"))
// Overflow should evict oldest data
rb.Write([]byte("fg"))
// Should now contain "cdefg"
data, err := rb.ReadLast(5)
require.NoError(t, err)
require.Equal(t, []byte("cdefg"), data)
}
func TestRingBuffer_LargeWrite(t *testing.T) {
t.Parallel()
rb := newRingBuffer(5)
// Write data larger than capacity
rb.Write([]byte("abcdefghij"))
// Should contain last 5 bytes
data, err := rb.ReadLast(5)
require.NoError(t, err)
require.Equal(t, []byte("fghij"), data)
}
func TestRingBuffer_WrapAround(t *testing.T) {
t.Parallel()
rb := newRingBuffer(5)
// Fill buffer
rb.Write([]byte("abcde"))
// Write more to cause wrap-around
rb.Write([]byte("fgh"))
// Should contain "defgh"
data, err := rb.ReadLast(5)
require.NoError(t, err)
require.Equal(t, []byte("defgh"), data)
// Test reading last 3 bytes after wrap
data, err = rb.ReadLast(3)
require.NoError(t, err)
require.Equal(t, []byte("fgh"), data)
}
func TestRingBuffer_ReadLastEdgeCases(t *testing.T) {
t.Parallel()
rb := newRingBuffer(3)
// Write some data (5 bytes to a 3-byte buffer, so only last 3 bytes remain)
rb.Write([]byte("hello"))
// Test reading negative count
data, err := rb.ReadLast(-1)
require.Error(t, err)
require.Contains(t, err.Error(), "cannot read negative number of bytes")
require.Nil(t, data)
// Test reading zero bytes
data, err = rb.ReadLast(0)
require.NoError(t, err)
require.Nil(t, data)
// Test reading more than available (buffer has 3 bytes, try to read 10)
_, err = rb.ReadLast(10)
require.Error(t, err)
require.Contains(t, err.Error(), "requested 10 bytes but only 3 available")
// Test reading exact amount available
data, err = rb.ReadLast(3)
require.NoError(t, err)
require.Equal(t, []byte("llo"), data)
}
func TestRingBuffer_EmptyWrite(t *testing.T) {
t.Parallel()
rb := newRingBuffer(10)
// Write empty data
rb.Write([]byte{})
// Buffer should still be empty
_, err := rb.ReadLast(5)
require.Error(t, err)
require.Contains(t, err.Error(), "requested 5 bytes but only 0 available")
}
func TestRingBuffer_MultipleWrites(t *testing.T) {
t.Parallel()
rb := newRingBuffer(10)
// Write data in chunks
rb.Write([]byte("ab"))
rb.Write([]byte("cd"))
rb.Write([]byte("ef"))
data, err := rb.ReadLast(6)
require.NoError(t, err)
require.Equal(t, []byte("abcdef"), data)
// Test partial reads
data, err = rb.ReadLast(4)
require.NoError(t, err)
require.Equal(t, []byte("cdef"), data)
data, err = rb.ReadLast(2)
require.NoError(t, err)
require.Equal(t, []byte("ef"), data)
}
func TestRingBuffer_EdgeCaseEviction(t *testing.T) {
t.Parallel()
rb := newRingBuffer(3)
// Write data that will cause eviction
rb.Write([]byte("abc"))
// Write more to cause eviction
rb.Write([]byte("d"))
// Should now contain "bcd"
data, err := rb.ReadLast(3)
require.NoError(t, err)
require.Equal(t, []byte("bcd"), data)
}
func TestRingBuffer_ComplexWrapAroundScenario(t *testing.T) {
t.Parallel()
rb := newRingBuffer(8)
// Fill buffer
rb.Write([]byte("12345678"))
// Evict some and add more to create complex wrap scenario
rb.Write([]byte("abcd"))
data, err := rb.ReadLast(8)
require.NoError(t, err)
require.Equal(t, []byte("5678abcd"), data)
// Add more
rb.Write([]byte("xyz"))
data, err = rb.ReadLast(8)
require.NoError(t, err)
require.Equal(t, []byte("8abcdxyz"), data)
// Test reading various amounts from the end
data, err = rb.ReadLast(7)
require.NoError(t, err)
require.Equal(t, []byte("abcdxyz"), data)
data, err = rb.ReadLast(4)
require.NoError(t, err)
require.Equal(t, []byte("dxyz"), data)
}
// Benchmark tests for performance validation
func BenchmarkRingBuffer_Write(b *testing.B) {
rb := newRingBuffer(64 * 1024 * 1024) // 64MB for benchmarks
data := bytes.Repeat([]byte("x"), 1024) // 1KB writes
b.ResetTimer()
for i := 0; i < b.N; i++ {
rb.Write(data)
}
}
func BenchmarkRingBuffer_ReadLast(b *testing.B) {
rb := newRingBuffer(64 * 1024 * 1024) // 64MB for benchmarks
// Fill buffer with test data
for i := 0; i < 64; i++ {
rb.Write(bytes.Repeat([]byte("x"), 1024))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := rb.ReadLast((i % 100) + 1)
if err != nil {
b.Fatal(err)
}
}
}
+66 -75
View File
@@ -11,23 +11,39 @@ import (
"strings"
"github.com/shirou/gopsutil/v4/disk"
"github.com/spf13/afero"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
)
var WindowsDriveRegex = regexp.MustCompile(`^[a-zA-Z]:\\$`)
func (*agent) HandleLS(rw http.ResponseWriter, r *http.Request) {
func (a *agent) HandleLS(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var query LSRequest
if !httpapi.Read(ctx, rw, r, &query) {
// An absolute path may be optionally provided, otherwise a path split into an
// array must be provided in the body (which can be relative).
query := r.URL.Query()
parser := httpapi.NewQueryParamParser()
path := parser.String(query, "", "path")
parser.ErrorExcessParams(query)
if len(parser.Errors) > 0 {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Query parameters have invalid values.",
Validations: parser.Errors,
})
return
}
resp, err := listFiles(query)
var req workspacesdk.LSRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
resp, err := listFiles(a.filesystem, path, req)
if err != nil {
status := http.StatusInternalServerError
switch {
@@ -46,58 +62,66 @@ func (*agent) HandleLS(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, resp)
}
func listFiles(query LSRequest) (LSResponse, error) {
var fullPath []string
switch query.Relativity {
case LSRelativityHome:
home, err := os.UserHomeDir()
if err != nil {
return LSResponse{}, xerrors.Errorf("failed to get user home directory: %w", err)
func listFiles(fs afero.Fs, path string, query workspacesdk.LSRequest) (workspacesdk.LSResponse, error) {
absolutePathString := path
if absolutePathString != "" {
if !filepath.IsAbs(path) {
return workspacesdk.LSResponse{}, xerrors.Errorf("path must be absolute: %q", path)
}
fullPath = []string{home}
case LSRelativityRoot:
if runtime.GOOS == "windows" {
if len(query.Path) == 0 {
return listDrives()
} else {
var fullPath []string
switch query.Relativity {
case workspacesdk.LSRelativityHome:
home, err := os.UserHomeDir()
if err != nil {
return workspacesdk.LSResponse{}, xerrors.Errorf("failed to get user home directory: %w", err)
}
if !WindowsDriveRegex.MatchString(query.Path[0]) {
return LSResponse{}, xerrors.Errorf("invalid drive letter %q", query.Path[0])
fullPath = []string{home}
case workspacesdk.LSRelativityRoot:
if runtime.GOOS == "windows" {
if len(query.Path) == 0 {
return listDrives()
}
if !WindowsDriveRegex.MatchString(query.Path[0]) {
return workspacesdk.LSResponse{}, xerrors.Errorf("invalid drive letter %q", query.Path[0])
}
} else {
fullPath = []string{"/"}
}
} else {
fullPath = []string{"/"}
default:
return workspacesdk.LSResponse{}, xerrors.Errorf("unsupported relativity type %q", query.Relativity)
}
default:
return LSResponse{}, xerrors.Errorf("unsupported relativity type %q", query.Relativity)
}
fullPath = append(fullPath, query.Path...)
fullPathRelative := filepath.Join(fullPath...)
absolutePathString, err := filepath.Abs(fullPathRelative)
if err != nil {
return LSResponse{}, xerrors.Errorf("failed to get absolute path of %q: %w", fullPathRelative, err)
fullPath = append(fullPath, query.Path...)
fullPathRelative := filepath.Join(fullPath...)
var err error
absolutePathString, err = filepath.Abs(fullPathRelative)
if err != nil {
return workspacesdk.LSResponse{}, xerrors.Errorf("failed to get absolute path of %q: %w", fullPathRelative, err)
}
}
// codeql[go/path-injection] - The intent is to allow the user to navigate to any directory in their workspace.
f, err := os.Open(absolutePathString)
f, err := fs.Open(absolutePathString)
if err != nil {
return LSResponse{}, xerrors.Errorf("failed to open directory %q: %w", absolutePathString, err)
return workspacesdk.LSResponse{}, xerrors.Errorf("failed to open directory %q: %w", absolutePathString, err)
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return LSResponse{}, xerrors.Errorf("failed to stat directory %q: %w", absolutePathString, err)
return workspacesdk.LSResponse{}, xerrors.Errorf("failed to stat directory %q: %w", absolutePathString, err)
}
if !stat.IsDir() {
return LSResponse{}, xerrors.Errorf("path %q is not a directory", absolutePathString)
return workspacesdk.LSResponse{}, xerrors.Errorf("path %q is not a directory", absolutePathString)
}
// `contents` may be partially populated even if the operation fails midway.
contents, _ := f.ReadDir(-1)
respContents := make([]LSFile, 0, len(contents))
contents, _ := f.Readdir(-1)
respContents := make([]workspacesdk.LSFile, 0, len(contents))
for _, file := range contents {
respContents = append(respContents, LSFile{
respContents = append(respContents, workspacesdk.LSFile{
Name: file.Name(),
AbsolutePathString: filepath.Join(absolutePathString, file.Name()),
IsDir: file.IsDir(),
@@ -105,7 +129,7 @@ func listFiles(query LSRequest) (LSResponse, error) {
}
// Sort alphabetically: directories then files
slices.SortFunc(respContents, func(a, b LSFile) int {
slices.SortFunc(respContents, func(a, b workspacesdk.LSFile) int {
if a.IsDir && !b.IsDir {
return -1
}
@@ -117,35 +141,35 @@ func listFiles(query LSRequest) (LSResponse, error) {
absolutePath := pathToArray(absolutePathString)
return LSResponse{
return workspacesdk.LSResponse{
AbsolutePath: absolutePath,
AbsolutePathString: absolutePathString,
Contents: respContents,
}, nil
}
func listDrives() (LSResponse, error) {
func listDrives() (workspacesdk.LSResponse, error) {
// disk.Partitions() will return partitions even if there was a failure to
// get one. Any errored partitions will not be returned.
partitionStats, err := disk.Partitions(true)
if err != nil && len(partitionStats) == 0 {
// Only return the error if there were no partitions returned.
return LSResponse{}, xerrors.Errorf("failed to get partitions: %w", err)
return workspacesdk.LSResponse{}, xerrors.Errorf("failed to get partitions: %w", err)
}
contents := make([]LSFile, 0, len(partitionStats))
contents := make([]workspacesdk.LSFile, 0, len(partitionStats))
for _, a := range partitionStats {
// Drive letters on Windows have a trailing separator as part of their name.
// i.e. `os.Open("C:")` does not work, but `os.Open("C:\\")` does.
name := a.Mountpoint + string(os.PathSeparator)
contents = append(contents, LSFile{
contents = append(contents, workspacesdk.LSFile{
Name: name,
AbsolutePathString: name,
IsDir: true,
})
}
return LSResponse{
return workspacesdk.LSResponse{
AbsolutePath: []string{},
AbsolutePathString: "",
Contents: contents,
@@ -163,36 +187,3 @@ func pathToArray(path string) []string {
}
return out
}
type LSRequest struct {
// e.g. [], ["repos", "coder"],
Path []string `json:"path"`
// Whether the supplied path is relative to the user's home directory,
// or the root directory.
Relativity LSRelativity `json:"relativity"`
}
type LSResponse struct {
AbsolutePath []string `json:"absolute_path"`
// Returned so clients can display the full path to the user, and
// copy it to configure file sync
// e.g. Windows: "C:\\Users\\coder"
// Linux: "/home/coder"
AbsolutePathString string `json:"absolute_path_string"`
Contents []LSFile `json:"contents"`
}
type LSFile struct {
Name string `json:"name"`
// e.g. "C:\\Users\\coder\\hello.txt"
// "/home/coder/hello.txt"
AbsolutePathString string `json:"absolute_path_string"`
IsDir bool `json:"is_dir"`
}
type LSRelativity string
const (
LSRelativityRoot LSRelativity = "root"
LSRelativityHome LSRelativity = "home"
)
+76 -38
View File
@@ -6,67 +6,103 @@ import (
"runtime"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/codersdk/workspacesdk"
)
type testFs struct {
afero.Fs
}
func newTestFs(base afero.Fs) *testFs {
return &testFs{
Fs: base,
}
}
func (*testFs) Open(name string) (afero.File, error) {
return nil, os.ErrPermission
}
func TestListFilesWithQueryParam(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
query := workspacesdk.LSRequest{}
_, err := listFiles(fs, "not-relative", query)
require.Error(t, err)
require.Contains(t, err.Error(), "must be absolute")
tmpDir := t.TempDir()
err = fs.MkdirAll(tmpDir, 0o755)
require.NoError(t, err)
res, err := listFiles(fs, tmpDir, query)
require.NoError(t, err)
require.Len(t, res.Contents, 0)
}
func TestListFilesNonExistentDirectory(t *testing.T) {
t.Parallel()
query := LSRequest{
fs := afero.NewMemMapFs()
query := workspacesdk.LSRequest{
Path: []string{"idontexist"},
Relativity: LSRelativityHome,
Relativity: workspacesdk.LSRelativityHome,
}
_, err := listFiles(query)
_, err := listFiles(fs, "", query)
require.ErrorIs(t, err, os.ErrNotExist)
}
func TestListFilesPermissionDenied(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
t.Skip("creating an unreadable-by-user directory is non-trivial on Windows")
}
fs := newTestFs(afero.NewMemMapFs())
home, err := os.UserHomeDir()
require.NoError(t, err)
tmpDir := t.TempDir()
reposDir := filepath.Join(tmpDir, "repos")
err = os.Mkdir(reposDir, 0o000)
err = fs.MkdirAll(reposDir, 0o000)
require.NoError(t, err)
rel, err := filepath.Rel(home, reposDir)
require.NoError(t, err)
query := LSRequest{
query := workspacesdk.LSRequest{
Path: pathToArray(rel),
Relativity: LSRelativityHome,
Relativity: workspacesdk.LSRelativityHome,
}
_, err = listFiles(query)
_, err = listFiles(fs, "", query)
require.ErrorIs(t, err, os.ErrPermission)
}
func TestListFilesNotADirectory(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
home, err := os.UserHomeDir()
require.NoError(t, err)
tmpDir := t.TempDir()
err = fs.MkdirAll(tmpDir, 0o755)
require.NoError(t, err)
filePath := filepath.Join(tmpDir, "file.txt")
err = os.WriteFile(filePath, []byte("content"), 0o600)
err = afero.WriteFile(fs, filePath, []byte("content"), 0o600)
require.NoError(t, err)
rel, err := filepath.Rel(home, filePath)
require.NoError(t, err)
query := LSRequest{
query := workspacesdk.LSRequest{
Path: pathToArray(rel),
Relativity: LSRelativityHome,
Relativity: workspacesdk.LSRelativityHome,
}
_, err = listFiles(query)
_, err = listFiles(fs, "", query)
require.ErrorContains(t, err, "is not a directory")
}
@@ -76,7 +112,7 @@ func TestListFilesSuccess(t *testing.T) {
tc := []struct {
name string
baseFunc func(t *testing.T) string
relativity LSRelativity
relativity workspacesdk.LSRelativity
}{
{
name: "home",
@@ -85,7 +121,7 @@ func TestListFilesSuccess(t *testing.T) {
require.NoError(t, err)
return home
},
relativity: LSRelativityHome,
relativity: workspacesdk.LSRelativityHome,
},
{
name: "root",
@@ -95,7 +131,7 @@ func TestListFilesSuccess(t *testing.T) {
}
return "/"
},
relativity: LSRelativityRoot,
relativity: workspacesdk.LSRelativityRoot,
},
}
@@ -104,19 +140,20 @@ func TestListFilesSuccess(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
base := tc.baseFunc(t)
tmpDir := t.TempDir()
reposDir := filepath.Join(tmpDir, "repos")
err := os.Mkdir(reposDir, 0o755)
err := fs.MkdirAll(reposDir, 0o755)
require.NoError(t, err)
downloadsDir := filepath.Join(tmpDir, "Downloads")
err = os.Mkdir(downloadsDir, 0o755)
err = fs.MkdirAll(downloadsDir, 0o755)
require.NoError(t, err)
textFile := filepath.Join(tmpDir, "file.txt")
err = os.WriteFile(textFile, []byte("content"), 0o600)
err = afero.WriteFile(fs, textFile, []byte("content"), 0o600)
require.NoError(t, err)
var queryComponents []string
@@ -129,16 +166,16 @@ func TestListFilesSuccess(t *testing.T) {
queryComponents = pathToArray(rel)
}
query := LSRequest{
query := workspacesdk.LSRequest{
Path: queryComponents,
Relativity: tc.relativity,
}
resp, err := listFiles(query)
resp, err := listFiles(fs, "", query)
require.NoError(t, err)
require.Equal(t, tmpDir, resp.AbsolutePathString)
// Output is sorted
require.Equal(t, []LSFile{
require.Equal(t, []workspacesdk.LSFile{
{
Name: "Downloads",
AbsolutePathString: downloadsDir,
@@ -166,43 +203,44 @@ func TestListFilesListDrives(t *testing.T) {
t.Skip("skipping test on non-Windows OS")
}
query := LSRequest{
fs := afero.NewOsFs()
query := workspacesdk.LSRequest{
Path: []string{},
Relativity: LSRelativityRoot,
Relativity: workspacesdk.LSRelativityRoot,
}
resp, err := listFiles(query)
resp, err := listFiles(fs, "", query)
require.NoError(t, err)
require.Contains(t, resp.Contents, LSFile{
require.Contains(t, resp.Contents, workspacesdk.LSFile{
Name: "C:\\",
AbsolutePathString: "C:\\",
IsDir: true,
})
query = LSRequest{
query = workspacesdk.LSRequest{
Path: []string{"C:\\"},
Relativity: LSRelativityRoot,
Relativity: workspacesdk.LSRelativityRoot,
}
resp, err = listFiles(query)
resp, err = listFiles(fs, "", query)
require.NoError(t, err)
query = LSRequest{
query = workspacesdk.LSRequest{
Path: resp.AbsolutePath,
Relativity: LSRelativityRoot,
Relativity: workspacesdk.LSRelativityRoot,
}
resp, err = listFiles(query)
resp, err = listFiles(fs, "", query)
require.NoError(t, err)
// System directory should always exist
require.Contains(t, resp.Contents, LSFile{
require.Contains(t, resp.Contents, workspacesdk.LSFile{
Name: "Windows",
AbsolutePathString: "C:\\Windows",
IsDir: true,
})
query = LSRequest{
query = workspacesdk.LSRequest{
// Network drives are not supported.
Path: []string{"\\sshfs\\work"},
Relativity: LSRelativityRoot,
Relativity: workspacesdk.LSRelativityRoot,
}
resp, err = listFiles(query)
resp, err = listFiles(fs, "", query)
require.ErrorContains(t, err, "drive")
}
+12 -91
View File
@@ -15,7 +15,6 @@ import (
"strings"
"time"
"cloud.google.com/go/compute/metadata"
"golang.org/x/xerrors"
"gopkg.in/natefinch/lumberjack.v2"
@@ -38,9 +37,8 @@ import (
"github.com/coder/coder/v2/codersdk/agentsdk"
)
func (r *RootCmd) workspaceAgent() *serpent.Command {
func workspaceAgent() *serpent.Command {
var (
auth string
logDir string
scriptDataDir string
pprofAddress string
@@ -59,6 +57,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
devcontainerProjectDiscovery bool
devcontainerDiscoveryAutostart bool
)
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "agent",
Short: `Starts the Coder workspace agent.`,
@@ -176,12 +175,14 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
version := buildinfo.Version()
logger.Info(ctx, "agent is starting now",
slog.F("url", r.agentURL),
slog.F("auth", auth),
slog.F("url", agentAuth.agentURL),
slog.F("auth", agentAuth.agentAuth),
slog.F("version", version),
)
client := agentsdk.New(r.agentURL)
client, err := agentAuth.CreateClient()
if err != nil {
return xerrors.Errorf("create agent client: %w", err)
}
client.SDK.SetLogger(logger)
// Set a reasonable timeout so requests can't hang forever!
// The timeout needs to be reasonably long, because requests
@@ -190,7 +191,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
client.SDK.HTTPClient.Timeout = 30 * time.Second
// Attach header transport so we process --agent-header and
// --agent-header-command flags
headerTransport, err := headerTransport(ctx, r.agentURL, agentHeader, agentHeaderCommand)
headerTransport, err := headerTransport(ctx, &agentAuth.agentURL, agentHeader, agentHeaderCommand)
if err != nil {
return xerrors.Errorf("configure header transport: %w", err)
}
@@ -214,68 +215,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
ignorePorts[port] = "debug"
}
// exchangeToken returns a session token.
// This is abstracted to allow for the same looping condition
// regardless of instance identity auth type.
var exchangeToken func(context.Context) (agentsdk.AuthenticateResponse, error)
switch auth {
case "token":
token, _ := inv.ParsedFlags().GetString(varAgentToken)
if token == "" {
tokenFile, _ := inv.ParsedFlags().GetString(varAgentTokenFile)
if tokenFile != "" {
tokenBytes, err := os.ReadFile(tokenFile)
if err != nil {
return xerrors.Errorf("read token file %q: %w", tokenFile, err)
}
token = strings.TrimSpace(string(tokenBytes))
}
}
if token == "" {
return xerrors.Errorf("CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set for token auth")
}
client.SetSessionToken(token)
case "google-instance-identity":
// This is *only* done for testing to mock client authentication.
// This will never be set in a production scenario.
var gcpClient *metadata.Client
gcpClientRaw := ctx.Value("gcp-client")
if gcpClientRaw != nil {
gcpClient, _ = gcpClientRaw.(*metadata.Client)
}
exchangeToken = func(ctx context.Context) (agentsdk.AuthenticateResponse, error) {
return client.AuthGoogleInstanceIdentity(ctx, "", gcpClient)
}
case "aws-instance-identity":
// This is *only* done for testing to mock client authentication.
// This will never be set in a production scenario.
var awsClient *http.Client
awsClientRaw := ctx.Value("aws-client")
if awsClientRaw != nil {
awsClient, _ = awsClientRaw.(*http.Client)
if awsClient != nil {
client.SDK.HTTPClient = awsClient
}
}
exchangeToken = func(ctx context.Context) (agentsdk.AuthenticateResponse, error) {
return client.AuthAWSInstanceIdentity(ctx)
}
case "azure-instance-identity":
// This is *only* done for testing to mock client authentication.
// This will never be set in a production scenario.
var azureClient *http.Client
azureClientRaw := ctx.Value("azure-client")
if azureClientRaw != nil {
azureClient, _ = azureClientRaw.(*http.Client)
if azureClient != nil {
client.SDK.HTTPClient = azureClient
}
}
exchangeToken = func(ctx context.Context) (agentsdk.AuthenticateResponse, error) {
return client.AuthAzureInstanceIdentity(ctx)
}
}
executablePath, err := os.Executable()
if err != nil {
return xerrors.Errorf("getting os executable: %w", err)
@@ -343,18 +282,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
LogDir: logDir,
ScriptDataDir: scriptDataDir,
// #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
TailnetListenPort: uint16(tailnetListenPort),
ExchangeToken: func(ctx context.Context) (string, error) {
if exchangeToken == nil {
return client.SDK.SessionToken(), nil
}
resp, err := exchangeToken(ctx)
if err != nil {
return "", err
}
client.SetSessionToken(resp.SessionToken)
return resp.SessionToken, nil
},
TailnetListenPort: uint16(tailnetListenPort),
EnvironmentVariables: environmentVariables,
IgnorePorts: ignorePorts,
SSHMaxTimeout: sshMaxTimeout,
@@ -365,7 +293,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
Execer: execer,
Devcontainers: devcontainers,
DevcontainerAPIOptions: []agentcontainers.Option{
agentcontainers.WithSubAgentURL(r.agentURL.String()),
agentcontainers.WithSubAgentURL(agentAuth.agentURL.String()),
agentcontainers.WithProjectDiscovery(devcontainerProjectDiscovery),
agentcontainers.WithDiscoveryAutostart(devcontainerDiscoveryAutostart),
},
@@ -400,13 +328,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
}
cmd.Options = serpent.OptionSet{
{
Flag: "auth",
Default: "token",
Description: "Specify the authentication type to use for the agent.",
Env: "CODER_AGENT_AUTH",
Value: serpent.StringOf(&auth),
},
{
Flag: "log-dir",
Default: os.TempDir(),
@@ -529,7 +450,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
Value: serpent.BoolOf(&devcontainerDiscoveryAutostart),
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
-157
View File
@@ -1,7 +1,6 @@
package cli_test
import (
"context"
"fmt"
"net/http"
"os"
@@ -11,7 +10,6 @@ import (
"sync/atomic"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -21,10 +19,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
)
@@ -64,158 +59,6 @@ func TestWorkspaceAgent(t *testing.T) {
}, testutil.WaitLong, testutil.IntervalMedium)
})
t.Run("Azure", func(t *testing.T) {
t.Parallel()
instanceID := "instanceidentifier"
certificates, metadataClient := coderdtest.NewAzureInstanceIdentity(t, instanceID)
db, ps := dbtestutil.NewDB(t,
dbtestutil.WithDumpOnFailure(),
)
client := coderdtest.New(t, &coderdtest.Options{
Database: db,
Pubsub: ps,
AzureCertificates: certificates,
})
user := coderdtest.CreateFirstUser(t, client)
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OrganizationID: user.OrganizationID,
OwnerID: user.UserID,
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
agents[0].Auth = &proto.Agent_InstanceId{InstanceId: instanceID}
return agents
}).Do()
inv, _ := clitest.New(t, "agent", "--auth", "azure-instance-identity", "--agent-url", client.URL.String())
inv = inv.WithContext(
//nolint:revive,staticcheck
context.WithValue(inv.Context(), "azure-client", metadataClient),
)
ctx := inv.Context()
clitest.Start(t, inv)
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).
MatchResources(matchAgentWithVersion).Wait()
workspace, err := client.Workspace(ctx, r.Workspace.ID)
require.NoError(t, err)
resources := workspace.LatestBuild.Resources
if assert.NotEmpty(t, workspace.LatestBuild.Resources) && assert.NotEmpty(t, resources[0].Agents) {
assert.NotEmpty(t, resources[0].Agents[0].Version)
}
dialer, err := workspacesdk.New(client).
DialAgent(ctx, resources[0].Agents[0].ID, nil)
require.NoError(t, err)
defer dialer.Close()
require.True(t, dialer.AwaitReachable(ctx))
})
t.Run("AWS", func(t *testing.T) {
t.Parallel()
instanceID := "instanceidentifier"
certificates, metadataClient := coderdtest.NewAWSInstanceIdentity(t, instanceID)
db, ps := dbtestutil.NewDB(t,
dbtestutil.WithDumpOnFailure(),
)
client := coderdtest.New(t, &coderdtest.Options{
Database: db,
Pubsub: ps,
AWSCertificates: certificates,
})
user := coderdtest.CreateFirstUser(t, client)
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OrganizationID: user.OrganizationID,
OwnerID: user.UserID,
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
agents[0].Auth = &proto.Agent_InstanceId{InstanceId: instanceID}
return agents
}).Do()
inv, _ := clitest.New(t, "agent", "--auth", "aws-instance-identity", "--agent-url", client.URL.String())
inv = inv.WithContext(
//nolint:revive,staticcheck
context.WithValue(inv.Context(), "aws-client", metadataClient),
)
clitest.Start(t, inv)
ctx := inv.Context()
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).
MatchResources(matchAgentWithVersion).
Wait()
workspace, err := client.Workspace(ctx, r.Workspace.ID)
require.NoError(t, err)
resources := workspace.LatestBuild.Resources
if assert.NotEmpty(t, resources) && assert.NotEmpty(t, resources[0].Agents) {
assert.NotEmpty(t, resources[0].Agents[0].Version)
}
dialer, err := workspacesdk.New(client).
DialAgent(ctx, resources[0].Agents[0].ID, nil)
require.NoError(t, err)
defer dialer.Close()
require.True(t, dialer.AwaitReachable(ctx))
})
t.Run("GoogleCloud", func(t *testing.T) {
t.Parallel()
instanceID := "instanceidentifier"
validator, metadataClient := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false)
db, ps := dbtestutil.NewDB(t,
dbtestutil.WithDumpOnFailure(),
)
client := coderdtest.New(t, &coderdtest.Options{
Database: db,
Pubsub: ps,
GoogleTokenValidator: validator,
})
owner := coderdtest.CreateFirstUser(t, client)
member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OrganizationID: owner.OrganizationID,
OwnerID: memberUser.ID,
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
agents[0].Auth = &proto.Agent_InstanceId{InstanceId: instanceID}
return agents
}).Do()
inv, cfg := clitest.New(t, "agent", "--auth", "google-instance-identity", "--agent-url", client.URL.String())
clitest.SetupConfig(t, member, cfg)
clitest.Start(t,
inv.WithContext(
//nolint:revive,staticcheck
context.WithValue(inv.Context(), "gcp-client", metadataClient),
),
)
ctx := inv.Context()
coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).
MatchResources(matchAgentWithVersion).
Wait()
workspace, err := client.Workspace(ctx, r.Workspace.ID)
require.NoError(t, err)
resources := workspace.LatestBuild.Resources
if assert.NotEmpty(t, resources) && assert.NotEmpty(t, resources[0].Agents) {
assert.NotEmpty(t, resources[0].Agents[0].Version)
}
dialer, err := workspacesdk.New(client).DialAgent(ctx, resources[0].Agents[0].ID, nil)
require.NoError(t, err)
defer dialer.Close()
require.True(t, dialer.AwaitReachable(ctx))
sshClient, err := dialer.SSHClient(ctx)
require.NoError(t, err)
defer sshClient.Close()
session, err := sshClient.NewSession()
require.NoError(t, err)
defer session.Close()
key := "CODER_AGENT_TOKEN"
command := "sh -c 'echo $" + key + "'"
if runtime.GOOS == "windows" {
command = "cmd.exe /c echo %" + key + "%"
}
token, err := session.CombinedOutput(command)
require.NoError(t, err)
_, err = uuid.Parse(strings.TrimSpace(string(token)))
require.NoError(t, err)
})
t.Run("PostStartup", func(t *testing.T) {
t.Parallel()
+6 -3
View File
@@ -12,18 +12,21 @@ import (
)
func (r *RootCmd) autoupdate() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "autoupdate <workspace> <always|never>",
Short: "Toggle auto-update policy for a workspace",
Middleware: serpent.Chain(
serpent.RequireNArgs(2),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
policy := strings.ToLower(inv.Args[1])
err := validateAutoUpdatePolicy(policy)
err = validateAutoUpdatePolicy(policy)
if err != nil {
return xerrors.Errorf("validate policy: %w", err)
}
+5 -3
View File
@@ -236,7 +236,6 @@ func (r *RootCmd) configSSH() *serpent.Command {
dryRun bool
coderCliPath string
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "config-ssh",
@@ -253,9 +252,13 @@ func (r *RootCmd) configSSH() *serpent.Command {
),
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
if sshConfigOpts.waitEnum != "auto" && sshConfigOpts.skipProxyCommand {
@@ -280,7 +283,6 @@ func (r *RootCmd) configSSH() *serpent.Command {
out = inv.Stderr
}
var err error
coderBinary := coderCliPath
if coderBinary == "" {
coderBinary, err = currentBinPath(out)
+6 -4
View File
@@ -135,11 +135,13 @@ func Test_sshConfigSplitOnCoderSection(t *testing.T) {
}
}
// This test tries to mimic the behavior of OpenSSH
// when executing e.g. a ProxyCommand.
// nolint:tparallel
// This test tries to mimic the behavior of OpenSSH when executing e.g. a ProxyCommand.
// nolint:paralleltest
func Test_sshConfigProxyCommandEscape(t *testing.T) {
t.Parallel()
// Don't run this test, or any of its subtests in parallel. The test works by writing a file and then immediately
// executing it. Other tests might also exec a subprocess, and if they do in parallel, there is a small race
// condition where our file is open when they fork, and remains open while we attempt to execute it, causing
// a "text file busy" error.
tests := []struct {
name string
+5 -3
View File
@@ -50,7 +50,6 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
// shares the same name across multiple organizations.
orgContext = NewOrganizationContext()
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "create [workspace]",
@@ -61,9 +60,12 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
Command: "coder create <username>/<workspace_name>",
},
),
Middleware: serpent.Chain(r.InitClient(client)),
Handler: func(inv *serpent.Invocation) error {
var err error
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspaceOwner := codersdk.Me
if len(inv.Args) >= 1 {
workspaceOwner, workspaceName, err = splitNamedWorkspace(inv.Args[0])
+5 -2
View File
@@ -16,7 +16,6 @@ func (r *RootCmd) deleteWorkspace() *serpent.Command {
orphan bool
prov buildFlags
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "delete <workspace>",
@@ -29,9 +28,13 @@ func (r *RootCmd) deleteWorkspace() *serpent.Command {
),
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return err
-23
View File
@@ -1,23 +0,0 @@
package cli
import "github.com/coder/serpent"
func (r *RootCmd) expCmd() *serpent.Command {
cmd := &serpent.Command{
Use: "exp",
Short: "Internal commands for testing and experimentation. These are prone to breaking changes with no notice.",
Handler: func(i *serpent.Invocation) error {
return i.Command.HelpHandler(i)
},
Hidden: true,
Children: []*serpent.Command{
r.scaletestCmd(),
r.errorExample(),
r.mcpCommand(),
r.promptExample(),
r.rptyCommand(),
r.tasksCommand(),
},
}
return cmd
}
+12
View File
@@ -0,0 +1,12 @@
package cli
import (
boundarycli "github.com/coder/boundary/cli"
"github.com/coder/serpent"
)
func (*RootCmd) boundary() *serpent.Command {
cmd := boundarycli.BaseCommand() // Package coder/boundary/cli exports a "base command" designed to be integrated as a subcommand.
cmd.Use += " [args...]" // The base command looks like `boundary -- command`. Serpent adds the flags piece, but we need to add the args.
return cmd
}
+33
View File
@@ -0,0 +1,33 @@
package cli_test
import (
"testing"
"github.com/stretchr/testify/assert"
boundarycli "github.com/coder/boundary/cli"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
)
// Actually testing the functionality of coder/boundary takes place in the
// coder/boundary repo, since it's a dependency of coder.
// Here we want to test basically that integrating it as a subcommand doesn't break anything.
func TestBoundarySubcommand(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
inv, _ := clitest.New(t, "exp", "boundary", "--help")
pty := ptytest.New(t).Attach(inv)
go func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
}()
// Expect the --help output to include the short description.
// We're simply confirming that `coder boundary --help` ran without a runtime error as
// a good chunk of serpents self validation logic happens at runtime.
pty.ExpectMatch(boundarycli.BaseCommand().Short)
}
+15 -9
View File
@@ -56,7 +56,7 @@ func (r *RootCmd) mcpConfigure() *serpent.Command {
},
Children: []*serpent.Command{
r.mcpConfigureClaudeDesktop(),
r.mcpConfigureClaudeCode(),
mcpConfigureClaudeCode(),
r.mcpConfigureCursor(),
},
}
@@ -117,7 +117,7 @@ func (*RootCmd) mcpConfigureClaudeDesktop() *serpent.Command {
return cmd
}
func (r *RootCmd) mcpConfigureClaudeCode() *serpent.Command {
func mcpConfigureClaudeCode() *serpent.Command {
var (
claudeAPIKey string
claudeConfigPath string
@@ -131,6 +131,7 @@ func (r *RootCmd) mcpConfigureClaudeCode() *serpent.Command {
deprecatedCoderMCPClaudeAPIKey string
)
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "claude-code <project-directory>",
Short: "Configure the Claude Code server. You will need to run this command for each project you want to use. Specify the project directory as the first argument.",
@@ -148,7 +149,7 @@ func (r *RootCmd) mcpConfigureClaudeCode() *serpent.Command {
binPath = testBinaryName
}
configureClaudeEnv := map[string]string{}
agentClient, err := r.createAgentClient()
agentClient, err := agentAuth.CreateClient()
if err != nil {
cliui.Warnf(inv.Stderr, "failed to create agent client: %s", err)
} else {
@@ -292,6 +293,7 @@ func (r *RootCmd) mcpConfigureClaudeCode() *serpent.Command {
},
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
@@ -397,15 +399,20 @@ type mcpServer struct {
func (r *RootCmd) mcpServer() *serpent.Command {
var (
client = new(codersdk.Client)
instructions string
allowedTools []string
appStatusSlug string
aiAgentAPIURL url.URL
)
return &serpent.Command{
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "server",
Handler: func(inv *serpent.Invocation) error {
client, err := r.TryInitClient(inv)
if err != nil {
return err
}
var lastReport taskReport
// Create a queue that skips duplicates and preserves summaries.
queue := cliutil.NewQueue[taskReport](512).WithPredicate(func(report taskReport) (taskReport, bool) {
@@ -494,7 +501,7 @@ func (r *RootCmd) mcpServer() *serpent.Command {
}
// Try to create an agent client for status reporting. Not validated.
agentClient, err := r.createAgentClient()
agentClient, err := agentAuth.CreateClient()
if err == nil {
cliui.Infof(inv.Stderr, "Agent URL : %s", agentClient.SDK.URL.String())
srv.agentClient = agentClient
@@ -545,9 +552,6 @@ func (r *RootCmd) mcpServer() *serpent.Command {
return srv.startServer(ctx, inv, instructions, allowedTools)
},
Short: "Start the Coder MCP server.",
Middleware: serpent.Chain(
r.TryInitClient(client),
),
Options: []serpent.Option{
{
Name: "instructions",
@@ -579,6 +583,8 @@ func (r *RootCmd) mcpServer() *serpent.Command {
},
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
func (s *mcpServer) startReporter(ctx context.Context, inv *serpent.Invocation) {
+5 -5
View File
@@ -22,16 +22,17 @@ import (
)
func (r *RootCmd) rptyCommand() *serpent.Command {
var (
client = new(codersdk.Client)
args handleRPTYArgs
)
var args handleRPTYArgs
cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error {
if r.disableDirect {
return xerrors.New("direct connections are disabled, but you can try websocat ;-)")
}
client, err := r.InitClient(inv)
if err != nil {
return err
}
args.NamedWorkspace = inv.Args[0]
args.Command = inv.Args[1:]
return handleRPTY(inv, client, args)
@@ -39,7 +40,6 @@ func (r *RootCmd) rptyCommand() *serpent.Command {
Long: "Establish an RPTY session with a workspace/agent. This uses the same mechanism as the Web Terminal.",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, -1),
r.InitClient(client),
),
Options: []serpent.Option{
{
+427 -117
View File
@@ -32,14 +32,16 @@ import (
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/scaletest/agentconn"
"github.com/coder/coder/v2/scaletest/createusers"
"github.com/coder/coder/v2/scaletest/createworkspaces"
"github.com/coder/coder/v2/scaletest/dashboard"
"github.com/coder/coder/v2/scaletest/harness"
"github.com/coder/coder/v2/scaletest/loadtestutil"
"github.com/coder/coder/v2/scaletest/reconnectingpty"
"github.com/coder/coder/v2/scaletest/workspacebuild"
"github.com/coder/coder/v2/scaletest/workspacetraffic"
"github.com/coder/coder/v2/scaletest/workspaceupdates"
"github.com/coder/serpent"
)
@@ -56,6 +58,7 @@ func (r *RootCmd) scaletestCmd() *serpent.Command {
r.scaletestCleanup(),
r.scaletestDashboard(),
r.scaletestCreateWorkspaces(),
r.scaletestWorkspaceUpdates(),
r.scaletestWorkspaceTraffic(),
},
}
@@ -131,80 +134,111 @@ func (s *scaletestTracingFlags) provider(ctx context.Context) (trace.TracerProvi
}, true, nil
}
type scaletestStrategyFlags struct {
type concurrencyFlags struct {
cleanup bool
concurrency int64
}
func (c *concurrencyFlags) attach(opts *serpent.OptionSet) {
concurrencyLong, concurrencyEnv, concurrencyDescription := "concurrency", "CODER_SCALETEST_CONCURRENCY", "Number of concurrent jobs to run. 0 means unlimited."
if c.cleanup {
concurrencyLong, concurrencyEnv, concurrencyDescription = "cleanup-"+concurrencyLong, "CODER_SCALETEST_CLEANUP_CONCURRENCY", strings.ReplaceAll(concurrencyDescription, "jobs", "cleanup jobs")
}
*opts = append(*opts, serpent.Option{
Flag: concurrencyLong,
Env: concurrencyEnv,
Description: concurrencyDescription,
Default: "1",
Value: serpent.Int64Of(&c.concurrency),
})
}
func (c *concurrencyFlags) toStrategy() harness.ExecutionStrategy {
switch c.concurrency {
case 1:
return harness.LinearExecutionStrategy{}
case 0:
return harness.ConcurrentExecutionStrategy{}
default:
return harness.ParallelExecutionStrategy{
Limit: int(c.concurrency),
}
}
}
type timeoutFlags struct {
cleanup bool
concurrency int64
timeout time.Duration
timeoutPerJob time.Duration
}
func (s *scaletestStrategyFlags) attach(opts *serpent.OptionSet) {
concurrencyLong, concurrencyEnv, concurrencyDescription := "concurrency", "CODER_SCALETEST_CONCURRENCY", "Number of concurrent jobs to run. 0 means unlimited."
func (t *timeoutFlags) attach(opts *serpent.OptionSet) {
timeoutLong, timeoutEnv, timeoutDescription := "timeout", "CODER_SCALETEST_TIMEOUT", "Timeout for the entire test run. 0 means unlimited."
jobTimeoutLong, jobTimeoutEnv, jobTimeoutDescription := "job-timeout", "CODER_SCALETEST_JOB_TIMEOUT", "Timeout per job. Jobs may take longer to complete under higher concurrency limits."
if s.cleanup {
concurrencyLong, concurrencyEnv, concurrencyDescription = "cleanup-"+concurrencyLong, "CODER_SCALETEST_CLEANUP_CONCURRENCY", strings.ReplaceAll(concurrencyDescription, "jobs", "cleanup jobs")
if t.cleanup {
timeoutLong, timeoutEnv, timeoutDescription = "cleanup-"+timeoutLong, "CODER_SCALETEST_CLEANUP_TIMEOUT", strings.ReplaceAll(timeoutDescription, "test", "cleanup")
jobTimeoutLong, jobTimeoutEnv, jobTimeoutDescription = "cleanup-"+jobTimeoutLong, "CODER_SCALETEST_CLEANUP_JOB_TIMEOUT", strings.ReplaceAll(jobTimeoutDescription, "jobs", "cleanup jobs")
}
*opts = append(
*opts,
serpent.Option{
Flag: concurrencyLong,
Env: concurrencyEnv,
Description: concurrencyDescription,
Default: "1",
Value: serpent.Int64Of(&s.concurrency),
},
serpent.Option{
Flag: timeoutLong,
Env: timeoutEnv,
Description: timeoutDescription,
Default: "30m",
Value: serpent.DurationOf(&s.timeout),
Value: serpent.DurationOf(&t.timeout),
},
serpent.Option{
Flag: jobTimeoutLong,
Env: jobTimeoutEnv,
Description: jobTimeoutDescription,
Default: "5m",
Value: serpent.DurationOf(&s.timeoutPerJob),
Value: serpent.DurationOf(&t.timeoutPerJob),
},
)
}
func (s *scaletestStrategyFlags) toStrategy() harness.ExecutionStrategy {
var strategy harness.ExecutionStrategy
switch s.concurrency {
case 1:
strategy = harness.LinearExecutionStrategy{}
case 0:
strategy = harness.ConcurrentExecutionStrategy{}
default:
strategy = harness.ParallelExecutionStrategy{
Limit: int(s.concurrency),
}
}
if s.timeoutPerJob > 0 {
strategy = harness.TimeoutExecutionStrategyWrapper{
Timeout: s.timeoutPerJob,
func (t *timeoutFlags) wrapStrategy(strategy harness.ExecutionStrategy) harness.ExecutionStrategy {
if t.timeoutPerJob > 0 {
return harness.TimeoutExecutionStrategyWrapper{
Timeout: t.timeoutPerJob,
Inner: strategy,
}
}
return strategy
}
func (s *scaletestStrategyFlags) toContext(ctx context.Context) (context.Context, context.CancelFunc) {
if s.timeout > 0 {
return context.WithTimeout(ctx, s.timeout)
func (t *timeoutFlags) toContext(ctx context.Context) (context.Context, context.CancelFunc) {
if t.timeout > 0 {
return context.WithTimeout(ctx, t.timeout)
}
return context.WithCancel(ctx)
}
type scaletestStrategyFlags struct {
concurrencyFlags
timeoutFlags
}
func newScaletestCleanupStrategy() *scaletestStrategyFlags {
return &scaletestStrategyFlags{
concurrencyFlags: concurrencyFlags{cleanup: true},
timeoutFlags: timeoutFlags{cleanup: true},
}
}
func (s *scaletestStrategyFlags) attach(opts *serpent.OptionSet) {
s.timeoutFlags.attach(opts)
s.concurrencyFlags.attach(opts)
}
func (s *scaletestStrategyFlags) toStrategy() harness.ExecutionStrategy {
return s.timeoutFlags.wrapStrategy(s.concurrencyFlags.toStrategy())
}
type scaleTestOutputFormat string
const (
@@ -395,18 +429,17 @@ func (r *userCleanupRunner) Run(ctx context.Context, _ string, _ io.Writer) erro
func (r *RootCmd) scaletestCleanup() *serpent.Command {
var template string
cleanupStrategy := &scaletestStrategyFlags{cleanup: true}
client := new(codersdk.Client)
cleanupStrategy := newScaletestCleanupStrategy()
cmd := &serpent.Command{
Use: "cleanup",
Short: "Cleanup scaletest workspaces, then cleanup scaletest users.",
Long: "The strategy flags will apply to each stage of the cleanup process.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
me, err := requireAdmin(ctx, client)
@@ -547,18 +580,20 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
tracingFlags = &scaletestTracingFlags{}
strategy = &scaletestStrategyFlags{}
cleanupStrategy = &scaletestStrategyFlags{cleanup: true}
cleanupStrategy = newScaletestCleanupStrategy()
output = &scaletestOutputFlags{}
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "create-workspaces",
Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.",
Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`,
Middleware: r.InitClient(client),
Use: "create-workspaces",
Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.",
Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`,
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
me, err := requireAdmin(ctx, client)
@@ -647,16 +682,6 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
if useHostUser {
config.User.SessionToken = client.SessionToken()
} else {
config.User.Username, config.User.Email, err = newScaleTestUser(id)
if err != nil {
return xerrors.Errorf("create scaletest username and email: %w", err)
}
}
config.Workspace.Request.Name, err = newScaleTestWorkspace(id)
if err != nil {
return xerrors.Errorf("create scaletest workspace name: %w", err)
}
if runCommand != "" {
@@ -859,21 +884,319 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
return cmd
}
func (r *RootCmd) scaletestWorkspaceUpdates() *serpent.Command {
var (
workspaceCount int64
powerUserWorkspaces int64
powerUserPercentage float64
workspaceUpdatesTimeout time.Duration
dialTimeout time.Duration
template string
noCleanup bool
parameterFlags workspaceParameterFlags
tracingFlags = &scaletestTracingFlags{}
// This test requires unlimited concurrency
timeoutStrategy = &timeoutFlags{}
cleanupStrategy = newScaletestCleanupStrategy()
output = &scaletestOutputFlags{}
prometheusFlags = &scaletestPrometheusFlags{}
)
cmd := &serpent.Command{
Use: "workspace-updates",
Short: "Simulate the load of Coder Desktop clients receiving workspace updates",
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.TryInitClient(inv)
if err != nil {
return err
}
notifyCtx, stop := signal.NotifyContext(ctx, StopSignals...) // Checked later.
defer stop()
ctx = notifyCtx
me, err := requireAdmin(ctx, client)
if err != nil {
return err
}
client.HTTPClient = &http.Client{
Transport: &codersdk.HeaderTransport{
Transport: http.DefaultTransport,
Header: map[string][]string{
codersdk.BypassRatelimitHeader: {"true"},
},
},
}
if workspaceCount <= 0 {
return xerrors.Errorf("--workspace-count must be greater than 0")
}
if powerUserWorkspaces <= 1 {
return xerrors.Errorf("--power-user-workspaces must be greater than 1")
}
if powerUserPercentage < 0 || powerUserPercentage > 100 {
return xerrors.Errorf("--power-user-proportion must be between 0 and 100")
}
powerUserWorkspaceCount := int64(float64(workspaceCount) * powerUserPercentage / 100)
remainder := powerUserWorkspaceCount % powerUserWorkspaces
// If the power user workspaces can't be evenly divided, round down
// to the nearest multiple so that we only have two groups of users.
workspaceCount -= remainder
powerUserWorkspaceCount -= remainder
powerUserCount := powerUserWorkspaceCount / powerUserWorkspaces
regularWorkspaceCount := workspaceCount - powerUserWorkspaceCount
regularUserCount := regularWorkspaceCount
regularUserWorkspaceCount := 1
_, _ = fmt.Fprintf(inv.Stderr, "Distribution plan:\n")
_, _ = fmt.Fprintf(inv.Stderr, " Total workspaces: %d\n", workspaceCount)
_, _ = fmt.Fprintf(inv.Stderr, " Power users: %d (each owning %d workspaces = %d total)\n",
powerUserCount, powerUserWorkspaces, powerUserWorkspaceCount)
_, _ = fmt.Fprintf(inv.Stderr, " Regular users: %d (each owning %d workspace = %d total)\n",
regularUserCount, regularUserWorkspaceCount, regularWorkspaceCount)
outputs, err := output.parse()
if err != nil {
return xerrors.Errorf("could not parse --output flags")
}
tpl, err := parseTemplate(ctx, client, me.OrganizationIDs, template)
if err != nil {
return xerrors.Errorf("parse template: %w", err)
}
cliRichParameters, err := asWorkspaceBuildParameters(parameterFlags.richParameters)
if err != nil {
return xerrors.Errorf("can't parse given parameter values: %w", err)
}
richParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
Action: WorkspaceCreate,
TemplateVersionID: tpl.ActiveVersionID,
RichParameterFile: parameterFlags.richParameterFile,
RichParameters: cliRichParameters,
})
if err != nil {
return xerrors.Errorf("prepare build: %w", err)
}
tracerProvider, closeTracing, tracingEnabled, err := tracingFlags.provider(ctx)
if err != nil {
return xerrors.Errorf("create tracer provider: %w", err)
}
tracer := tracerProvider.Tracer(scaletestTracerName)
reg := prometheus.NewRegistry()
metrics := workspaceupdates.NewMetrics(reg)
logger := inv.Logger
prometheusSrvClose := ServeHandler(ctx, logger, promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), prometheusFlags.Address, "prometheus")
defer prometheusSrvClose()
defer func() {
_, _ = fmt.Fprintln(inv.Stderr, "\nUploading traces...")
if err := closeTracing(ctx); err != nil {
_, _ = fmt.Fprintf(inv.Stderr, "\nError uploading traces: %+v\n", err)
}
// Wait for prometheus metrics to be scraped
_, _ = fmt.Fprintf(inv.Stderr, "Waiting %s for prometheus metrics to be scraped\n", prometheusFlags.Wait)
<-time.After(prometheusFlags.Wait)
}()
_, _ = fmt.Fprintln(inv.Stderr, "Creating users...")
dialBarrier := new(sync.WaitGroup)
dialBarrier.Add(int(powerUserCount + regularUserCount))
configs := make([]workspaceupdates.Config, 0, powerUserCount+regularUserCount)
for range powerUserCount {
config := workspaceupdates.Config{
User: createusers.Config{
OrganizationID: me.OrganizationIDs[0],
},
Workspace: workspacebuild.Config{
OrganizationID: me.OrganizationIDs[0],
Request: codersdk.CreateWorkspaceRequest{
TemplateID: tpl.ID,
RichParameterValues: richParameters,
},
NoWaitForAgents: true,
},
WorkspaceCount: powerUserWorkspaces,
WorkspaceUpdatesTimeout: workspaceUpdatesTimeout,
DialTimeout: dialTimeout,
Metrics: metrics,
DialBarrier: dialBarrier,
}
if err := config.Validate(); err != nil {
return xerrors.Errorf("validate config: %w", err)
}
configs = append(configs, config)
}
for range regularUserCount {
config := workspaceupdates.Config{
User: createusers.Config{
OrganizationID: me.OrganizationIDs[0],
},
Workspace: workspacebuild.Config{
OrganizationID: me.OrganizationIDs[0],
Request: codersdk.CreateWorkspaceRequest{
TemplateID: tpl.ID,
RichParameterValues: richParameters,
},
NoWaitForAgents: true,
},
WorkspaceCount: int64(regularUserWorkspaceCount),
WorkspaceUpdatesTimeout: workspaceUpdatesTimeout,
DialTimeout: dialTimeout,
Metrics: metrics,
DialBarrier: dialBarrier,
}
if err := config.Validate(); err != nil {
return xerrors.Errorf("validate config: %w", err)
}
configs = append(configs, config)
}
th := harness.NewTestHarness(timeoutStrategy.wrapStrategy(harness.ConcurrentExecutionStrategy{}), cleanupStrategy.toStrategy())
for i, config := range configs {
name := fmt.Sprintf("workspaceupdates-%dw", config.WorkspaceCount)
id := strconv.Itoa(i)
var runner harness.Runnable = workspaceupdates.NewRunner(client, config)
if tracingEnabled {
runner = &runnableTraceWrapper{
tracer: tracer,
spanName: fmt.Sprintf("%s/%s", name, id),
runner: runner,
}
}
th.AddRun(name, id, runner)
}
_, _ = fmt.Fprintln(inv.Stderr, "Running workspace updates scaletest...")
testCtx, testCancel := timeoutStrategy.toContext(ctx)
defer testCancel()
err = th.Run(testCtx)
if err != nil {
return xerrors.Errorf("run test harness (harness failure, not a test failure): %w", err)
}
// If the command was interrupted, skip stats.
if notifyCtx.Err() != nil {
return notifyCtx.Err()
}
res := th.Results()
for _, o := range outputs {
err = o.write(res, inv.Stdout)
if err != nil {
return xerrors.Errorf("write output %q to %q: %w", o.format, o.path, err)
}
}
if !noCleanup {
_, _ = fmt.Fprintln(inv.Stderr, "\nCleaning up...")
cleanupCtx, cleanupCancel := cleanupStrategy.toContext(ctx)
defer cleanupCancel()
err = th.Cleanup(cleanupCtx)
if err != nil {
return xerrors.Errorf("cleanup tests: %w", err)
}
}
if res.TotalFail > 0 {
return xerrors.New("load test failed, see above for more details")
}
return nil
},
}
cmd.Options = serpent.OptionSet{
{
Flag: "workspace-count",
FlagShorthand: "c",
Env: "CODER_SCALETEST_WORKSPACE_COUNT",
Description: "Required: Total number of workspaces to create.",
Value: serpent.Int64Of(&workspaceCount),
Required: true,
},
{
Flag: "power-user-workspaces",
Env: "CODER_SCALETEST_POWER_USER_WORKSPACES",
Description: "Number of workspaces each power-user owns.",
Value: serpent.Int64Of(&powerUserWorkspaces),
Required: true,
},
{
Flag: "power-user-percentage",
Env: "CODER_SCALETEST_POWER_USER_PERCENTAGE",
Default: "50.0",
Description: "Percentage of total workspaces owned by power-users (0-100).",
Value: serpent.Float64Of(&powerUserPercentage),
},
{
Flag: "workspace-updates-timeout",
Env: "CODER_SCALETEST_WORKSPACE_UPDATES_TIMEOUT",
Default: "5m",
Description: "How long to wait for all expected workspace updates.",
Value: serpent.DurationOf(&workspaceUpdatesTimeout),
},
{
Flag: "dial-timeout",
Env: "CODER_SCALETEST_DIAL_TIMEOUT",
Default: "2m",
Description: "Timeout for dialing the tailnet endpoint.",
Value: serpent.DurationOf(&dialTimeout),
},
{
Flag: "template",
FlagShorthand: "t",
Env: "CODER_SCALETEST_TEMPLATE",
Description: "Required: Name or ID of the template to use for workspaces.",
Value: serpent.StringOf(&template),
Required: true,
},
{
Flag: "no-cleanup",
Env: "CODER_SCALETEST_NO_CLEANUP",
Description: "Do not clean up resources after the test completes.",
Value: serpent.BoolOf(&noCleanup),
},
}
cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...)
tracingFlags.attach(&cmd.Options)
timeoutStrategy.attach(&cmd.Options)
cleanupStrategy.attach(&cmd.Options)
output.attach(&cmd.Options)
prometheusFlags.attach(&cmd.Options)
return cmd
}
func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
var (
tickInterval time.Duration
bytesPerTick int64
ssh bool
disableDirect bool
useHostLogin bool
app string
template string
targetWorkspaces string
workspaceProxyURL string
client = &codersdk.Client{}
tracingFlags = &scaletestTracingFlags{}
strategy = &scaletestStrategyFlags{}
cleanupStrategy = &scaletestStrategyFlags{cleanup: true}
cleanupStrategy = newScaletestCleanupStrategy()
output = &scaletestOutputFlags{}
prometheusFlags = &scaletestPrometheusFlags{}
)
@@ -881,10 +1204,12 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
cmd := &serpent.Command{
Use: "workspace-traffic",
Short: "Generate traffic to scaletest workspaces through coderd",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) (err error) {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
notifyCtx, stop := signal.NotifyContext(ctx, StopSignals...) // Checked later.
@@ -1011,9 +1336,10 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
return xerrors.Errorf("parse workspace proxy URL: %w", err)
}
webClient = codersdk.New(u)
webClient.HTTPClient = client.HTTPClient
webClient.SetSessionToken(client.SessionToken())
webClient = codersdk.New(u,
codersdk.WithHTTPClient(client.HTTPClient),
codersdk.WithSessionToken(client.SessionToken()),
)
appConfig, err = createWorkspaceAppConfig(webClient, appHost.Host, app, ws, agent)
if err != nil {
@@ -1023,15 +1349,16 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
// Setup our workspace agent connection.
config := workspacetraffic.Config{
AgentID: agent.ID,
BytesPerTick: bytesPerTick,
Duration: strategy.timeout,
TickInterval: tickInterval,
ReadMetrics: metrics.ReadMetrics(ws.OwnerName, ws.Name, agent.Name),
WriteMetrics: metrics.WriteMetrics(ws.OwnerName, ws.Name, agent.Name),
SSH: ssh,
Echo: ssh,
App: appConfig,
AgentID: agent.ID,
BytesPerTick: bytesPerTick,
Duration: strategy.timeout,
TickInterval: tickInterval,
ReadMetrics: metrics.ReadMetrics(ws.OwnerName, ws.Name, agent.Name),
WriteMetrics: metrics.WriteMetrics(ws.OwnerName, ws.Name, agent.Name),
SSH: ssh,
DisableDirect: disableDirect,
Echo: ssh,
App: appConfig,
}
if webClient != nil {
@@ -1117,6 +1444,13 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
Description: "Send traffic over SSH, cannot be used with --app.",
Value: serpent.BoolOf(&ssh),
},
{
Flag: "disable-direct",
Env: "CODER_SCALETEST_WORKSPACE_TRAFFIC_DISABLE_DIRECT_CONNECTIONS",
Default: "false",
Description: "Disable direct connections for SSH traffic to workspaces. Does nothing if `--ssh` is not also set.",
Value: serpent.BoolOf(&disableDirect),
},
{
Flag: "app",
Env: "CODER_SCALETEST_WORKSPACE_TRAFFIC_APP",
@@ -1151,16 +1485,14 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
func (r *RootCmd) scaletestDashboard() *serpent.Command {
var (
interval time.Duration
jitter time.Duration
headless bool
randSeed int64
targetUsers string
client = &codersdk.Client{}
interval time.Duration
jitter time.Duration
headless bool
randSeed int64
targetUsers string
tracingFlags = &scaletestTracingFlags{}
strategy = &scaletestStrategyFlags{}
cleanupStrategy = &scaletestStrategyFlags{cleanup: true}
cleanupStrategy = newScaletestCleanupStrategy()
output = &scaletestOutputFlags{}
prometheusFlags = &scaletestPrometheusFlags{}
)
@@ -1168,10 +1500,12 @@ func (r *RootCmd) scaletestDashboard() *serpent.Command {
cmd := &serpent.Command{
Use: "dashboard",
Short: "Generate traffic to the HTTP API to simulate use of the dashboard.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if !(interval > 0) {
return xerrors.Errorf("--interval must be greater than zero")
}
@@ -1239,8 +1573,9 @@ func (r *RootCmd) scaletestDashboard() *serpent.Command {
return xerrors.Errorf("create token for user: %w", err)
}
userClient := codersdk.New(client.URL)
userClient.SetSessionToken(userTokResp.Key)
userClient := codersdk.New(client.URL,
codersdk.WithSessionToken(userTokResp.Key),
)
config := dashboard.Config{
Interval: interval,
@@ -1399,31 +1734,6 @@ func (r *runnableTraceWrapper) Cleanup(ctx context.Context, id string, logs io.W
return c.Cleanup(ctx, id, logs)
}
// newScaleTestUser returns a random username and email address that can be used
// for scale testing. The returned username is prefixed with "scaletest-" and
// the returned email address is suffixed with "@scaletest.local".
func newScaleTestUser(id string) (username string, email string, err error) {
randStr, err := cryptorand.String(8)
return fmt.Sprintf("scaletest-%s-%s", randStr, id), fmt.Sprintf("%s-%s@scaletest.local", randStr, id), err
}
// newScaleTestWorkspace returns a random workspace name that can be used for
// scale testing. The returned workspace name is prefixed with "scaletest-" and
// suffixed with the given id.
func newScaleTestWorkspace(id string) (name string, err error) {
randStr, err := cryptorand.String(8)
return fmt.Sprintf("scaletest-%s-%s", randStr, id), err
}
func isScaleTestUser(user codersdk.User) bool {
return strings.HasSuffix(user.Email, "@scaletest.local")
}
func isScaleTestWorkspace(workspace codersdk.Workspace) bool {
return strings.HasPrefix(workspace.OwnerName, "scaletest-") ||
strings.HasPrefix(workspace.Name, "scaletest-")
}
func getScaletestWorkspaces(ctx context.Context, client *codersdk.Client, owner, template string) ([]codersdk.Workspace, int, error) {
var (
pageNumber = 0
@@ -1462,7 +1772,7 @@ func getScaletestWorkspaces(ctx context.Context, client *codersdk.Client, owner,
pageWorkspaces := make([]codersdk.Workspace, 0, len(page.Workspaces))
for _, w := range page.Workspaces {
if !isScaleTestWorkspace(w) {
if !loadtestutil.IsScaleTestWorkspace(w.Name, w.OwnerName) {
continue
}
if noOwnerAccess && w.OwnerID != me.ID {
@@ -1502,7 +1812,7 @@ func getScaletestUsers(ctx context.Context, client *codersdk.Client) ([]codersdk
pageUsers := make([]codersdk.User, 0, len(page.Users))
for _, u := range page.Users {
if isScaleTestUser(u) {
if loadtestutil.IsScaleTestUser(u.Username, u.Email) {
pageUsers = append(pageUsers, u)
}
}
+4 -1
View File
@@ -13,8 +13,11 @@ func (r *RootCmd) tasksCommand() *serpent.Command {
return i.Command.HelpHandler(i)
},
Children: []*serpent.Command{
r.taskList(),
r.taskCreate(),
r.taskDelete(),
r.taskList(),
r.taskLogs(),
r.taskSend(),
r.taskStatus(),
},
}
+215
View File
@@ -0,0 +1,215 @@
package cli
import (
"fmt"
"io"
"strings"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) taskCreate() *serpent.Command {
var (
orgContext = NewOrganizationContext()
ownerArg string
taskName string
templateName string
templateVersionName string
presetName string
stdin bool
quiet bool
)
cmd := &serpent.Command{
Use: "create [input]",
Short: "Create an experimental task",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
),
Options: serpent.OptionSet{
{
Name: "name",
Flag: "name",
Description: "Specify the name of the task. If you do not specify one, a name will be generated for you.",
Value: serpent.StringOf(&taskName),
Required: false,
Default: "",
},
{
Name: "owner",
Flag: "owner",
Description: "Specify the owner of the task. Defaults to the current user.",
Value: serpent.StringOf(&ownerArg),
Required: false,
Default: codersdk.Me,
},
{
Name: "template",
Flag: "template",
Env: "CODER_TASK_TEMPLATE_NAME",
Value: serpent.StringOf(&templateName),
},
{
Name: "template-version",
Flag: "template-version",
Env: "CODER_TASK_TEMPLATE_VERSION",
Value: serpent.StringOf(&templateVersionName),
},
{
Name: "preset",
Flag: "preset",
Env: "CODER_TASK_PRESET_NAME",
Value: serpent.StringOf(&presetName),
Default: PresetNone,
},
{
Name: "stdin",
Flag: "stdin",
Description: "Reads from stdin for the task input.",
Value: serpent.BoolOf(&stdin),
},
{
Name: "quiet",
Flag: "quiet",
FlagShorthand: "q",
Description: "Only display the created task's ID.",
Value: serpent.BoolOf(&quiet),
},
},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var (
ctx = inv.Context()
expClient = codersdk.NewExperimentalClient(client)
taskInput string
templateVersionID uuid.UUID
templateVersionPresetID uuid.UUID
)
organization, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("get current organization: %w", err)
}
if stdin {
bytes, err := io.ReadAll(inv.Stdin)
if err != nil {
return xerrors.Errorf("reading stdin: %w", err)
}
taskInput = string(bytes)
} else {
if len(inv.Args) != 1 {
return xerrors.Errorf("expected an input for task")
}
taskInput = inv.Args[0]
}
if taskInput == "" {
return xerrors.Errorf("a task cannot be started with an empty input")
}
switch {
case templateName == "":
templates, err := client.Templates(ctx, codersdk.TemplateFilter{SearchQuery: "has-ai-task:true", OrganizationID: organization.ID})
if err != nil {
return xerrors.Errorf("list templates: %w", err)
}
if len(templates) == 0 {
return xerrors.Errorf("no task templates configured")
}
// When a deployment has only 1 AI task template, we will
// allow omitting the template. Otherwise we will require
// the user to be explicit with their choice of template.
if len(templates) > 1 {
templateNames := make([]string, 0, len(templates))
for _, template := range templates {
templateNames = append(templateNames, template.Name)
}
return xerrors.Errorf("template name not provided, available templates: %s", strings.Join(templateNames, ", "))
}
if templateVersionName != "" {
templateVersion, err := client.TemplateVersionByOrganizationAndName(ctx, organization.ID, templates[0].Name, templateVersionName)
if err != nil {
return xerrors.Errorf("get template version: %w", err)
}
templateVersionID = templateVersion.ID
} else {
templateVersionID = templates[0].ActiveVersionID
}
case templateVersionName != "":
templateVersion, err := client.TemplateVersionByOrganizationAndName(ctx, organization.ID, templateName, templateVersionName)
if err != nil {
return xerrors.Errorf("get template version: %w", err)
}
templateVersionID = templateVersion.ID
default:
template, err := client.TemplateByName(ctx, organization.ID, templateName)
if err != nil {
return xerrors.Errorf("get template: %w", err)
}
templateVersionID = template.ActiveVersionID
}
if presetName != PresetNone {
templatePresets, err := client.TemplateVersionPresets(ctx, templateVersionID)
if err != nil {
return xerrors.Errorf("get template presets: %w", err)
}
preset, err := resolvePreset(templatePresets, presetName)
if err != nil {
return xerrors.Errorf("resolve preset: %w", err)
}
templateVersionPresetID = preset.ID
}
task, err := expClient.CreateTask(ctx, ownerArg, codersdk.CreateTaskRequest{
Name: taskName,
TemplateVersionID: templateVersionID,
TemplateVersionPresetID: templateVersionPresetID,
Input: taskInput,
})
if err != nil {
return xerrors.Errorf("create task: %w", err)
}
if quiet {
_, _ = fmt.Fprintln(inv.Stdout, task.ID)
} else {
_, _ = fmt.Fprintf(
inv.Stdout,
"The task %s has been created at %s!\n",
cliui.Keyword(task.Name),
cliui.Timestamp(task.CreatedAt),
)
}
return nil
},
}
orgContext.AttachOptions(cmd)
return cmd
}
@@ -5,14 +5,12 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/cli/cliui"
@@ -33,9 +31,10 @@ func TestTaskCreate(t *testing.T) {
templateID = uuid.New()
templateVersionID = uuid.New()
templateVersionPresetID = uuid.New()
taskID = uuid.New()
)
templateAndVersionFoundHandler := func(t *testing.T, ctx context.Context, orgID uuid.UUID, templateName, templateVersionName, presetName, prompt string) http.HandlerFunc {
templateAndVersionFoundHandler := func(t *testing.T, ctx context.Context, orgID uuid.UUID, templateName, templateVersionName, presetName, prompt, taskName, username string) http.HandlerFunc {
t.Helper()
return func(w http.ResponseWriter, r *http.Request) {
@@ -46,11 +45,11 @@ func TestTaskCreate(t *testing.T) {
ID: orgID,
}},
})
case fmt.Sprintf("/api/v2/organizations/%s/templates/my-template/versions/my-template-version", orgID):
case fmt.Sprintf("/api/v2/organizations/%s/templates/%s/versions/%s", orgID, templateName, templateVersionName):
httpapi.Write(ctx, w, http.StatusOK, codersdk.TemplateVersion{
ID: templateVersionID,
})
case fmt.Sprintf("/api/v2/organizations/%s/templates/my-template", orgID):
case fmt.Sprintf("/api/v2/organizations/%s/templates/%s", orgID, templateName):
httpapi.Write(ctx, w, http.StatusOK, codersdk.Template{
ID: templateID,
ActiveVersionID: templateVersionID,
@@ -62,13 +61,21 @@ func TestTaskCreate(t *testing.T) {
Name: presetName,
},
})
case "/api/experimental/tasks/me":
case "/api/v2/templates":
httpapi.Write(ctx, w, http.StatusOK, []codersdk.Template{
{
ID: templateID,
Name: templateName,
ActiveVersionID: templateVersionID,
},
})
case fmt.Sprintf("/api/experimental/tasks/%s", username):
var req codersdk.CreateTaskRequest
if !httpapi.Read(ctx, w, r, &req) {
return
}
assert.Equal(t, prompt, req.Prompt, "prompt mismatch")
assert.Equal(t, prompt, req.Input, "prompt mismatch")
assert.Equal(t, templateVersionID, req.TemplateVersionID, "template version mismatch")
if presetName == "" {
@@ -77,10 +84,17 @@ func TestTaskCreate(t *testing.T) {
assert.Equal(t, templateVersionPresetID, req.TemplateVersionPresetID, "template version preset id mismatch")
}
httpapi.Write(ctx, w, http.StatusCreated, codersdk.Workspace{
Name: "task-wild-goldfish-27",
created := codersdk.Task{
ID: taskID,
Name: taskName,
CreatedAt: taskCreatedAt,
})
}
if req.Name != "" {
assert.Equal(t, req.Name, taskName, "name mismatch")
created.Name = req.Name
}
httpapi.Write(ctx, w, http.StatusCreated, created)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
@@ -90,71 +104,101 @@ func TestTaskCreate(t *testing.T) {
tests := []struct {
args []string
env []string
stdin string
expectError string
expectOutput string
handler func(t *testing.T, ctx context.Context) http.HandlerFunc
}{
{
args: []string{"my-template@my-template-version", "--input", "my custom prompt", "--org", organizationID.String()},
args: []string{"--stdin"},
stdin: "reads prompt from stdin",
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "reads prompt from stdin", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my-template", "--input", "my custom prompt", "--org", organizationID.String()},
args: []string{"my custom prompt"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my custom prompt", "--owner", "someone-else"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", "someone-else")
},
},
{
args: []string{"--name", "abc123", "my custom prompt"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("abc123"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "abc123", codersdk.Me)
},
},
{
args: []string{"my custom prompt", "--template", "my-template", "--template-version", "my-template-version", "--org", organizationID.String()},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my custom prompt", "--template", "my-template", "--org", organizationID.String()},
env: []string{"CODER_TASK_TEMPLATE_VERSION=my-template-version"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"--input", "my custom prompt", "--org", organizationID.String()},
args: []string{"my custom prompt", "--org", organizationID.String()},
env: []string{"CODER_TASK_TEMPLATE_NAME=my-template", "CODER_TASK_TEMPLATE_VERSION=my-template-version"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
env: []string{"CODER_TASK_TEMPLATE_NAME=my-template", "CODER_TASK_TEMPLATE_VERSION=my-template-version", "CODER_TASK_INPUT=my custom prompt", "CODER_ORGANIZATION=" + organizationID.String()},
args: []string{"my custom prompt", "--template", "my-template", "--org", organizationID.String()},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my-template", "--input", "my custom prompt", "--org", organizationID.String()},
args: []string{"my custom prompt", "--template", "my-template", "--preset", "my-preset", "--org", organizationID.String()},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my-template", "--input", "my custom prompt", "--preset", "my-preset", "--org", organizationID.String()},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt")
},
},
{
args: []string{"my-template", "--input", "my custom prompt"},
args: []string{"my custom prompt", "--template", "my-template"},
env: []string{"CODER_TASK_PRESET_NAME=my-preset"},
expectOutput: fmt.Sprintf("The task %s has been created at %s!", cliui.Keyword("task-wild-goldfish-27"), cliui.Timestamp(taskCreatedAt)),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my-template", "--input", "my custom prompt", "--preset", "not-real-preset"},
args: []string{"my custom prompt", "-q"},
expectOutput: taskID.String(),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "my-template-version", "", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my custom prompt", "--template", "my-template", "--preset", "not-real-preset"},
expectError: `preset "not-real-preset" not found`,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt")
return templateAndVersionFoundHandler(t, ctx, organizationID, "my-template", "", "my-preset", "my custom prompt", "task-wild-goldfish-27", codersdk.Me)
},
},
{
args: []string{"my-template@not-real-template-version", "--input", "my custom prompt"},
args: []string{"my custom prompt", "--template", "my-template", "--template-version", "not-real-template-version"},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -165,6 +209,11 @@ func TestTaskCreate(t *testing.T) {
ID: organizationID,
}},
})
case fmt.Sprintf("/api/v2/organizations/%s/templates/my-template", organizationID):
httpapi.Write(ctx, w, http.StatusOK, codersdk.Template{
ID: templateID,
ActiveVersionID: templateVersionID,
})
case fmt.Sprintf("/api/v2/organizations/%s/templates/my-template/versions/not-real-template-version", organizationID):
httpapi.ResourceNotFound(w)
default:
@@ -174,7 +223,7 @@ func TestTaskCreate(t *testing.T) {
},
},
{
args: []string{"not-real-template", "--input", "my custom prompt", "--org", organizationID.String()},
args: []string{"my custom prompt", "--template", "not-real-template", "--org", organizationID.String()},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -194,7 +243,7 @@ func TestTaskCreate(t *testing.T) {
},
},
{
args: []string{"template-in-different-org", "--input", "my-custom-prompt", "--org", anotherOrganizationID.String()},
args: []string{"my-custom-prompt", "--template", "template-in-different-org", "--org", anotherOrganizationID.String()},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -214,7 +263,7 @@ func TestTaskCreate(t *testing.T) {
},
},
{
args: []string{"no-org", "--input", "my-custom-prompt"},
args: []string{"no-org-prompt"},
expectError: "Must select an organization with --org=<org_name>",
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -227,6 +276,49 @@ func TestTaskCreate(t *testing.T) {
}
},
},
{
args: []string{"no task templates"},
expectError: "no task templates configured",
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v2/users/me/organizations":
httpapi.Write(ctx, w, http.StatusOK, []codersdk.Organization{
{MinimalOrganization: codersdk.MinimalOrganization{
ID: organizationID,
}},
})
case "/api/v2/templates":
httpapi.Write(ctx, w, http.StatusOK, []codersdk.Template{})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{"no template name provided"},
expectError: "template name not provided, available templates: wibble, wobble",
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v2/users/me/organizations":
httpapi.Write(ctx, w, http.StatusOK, []codersdk.Organization{
{MinimalOrganization: codersdk.MinimalOrganization{
ID: organizationID,
}},
})
case "/api/v2/templates":
httpapi.Write(ctx, w, http.StatusOK, []codersdk.Template{
{Name: "wibble"},
{Name: "wobble"},
})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
}
for _, tt := range tests {
@@ -236,7 +328,7 @@ func TestTaskCreate(t *testing.T) {
var (
ctx = testutil.Context(t, testutil.WaitShort)
srv = httptest.NewServer(tt.handler(t, ctx))
client = new(codersdk.Client)
client = codersdk.New(testutil.MustURL(t, srv.URL))
args = []string{"exp", "task", "create"}
sb strings.Builder
err error
@@ -244,11 +336,9 @@ func TestTaskCreate(t *testing.T) {
t.Cleanup(srv.Close)
client.URL, err = url.Parse(srv.URL)
require.NoError(t, err)
inv, root := clitest.New(t, append(args, tt.args...)...)
inv.Environ = serpent.ParseEnviron(tt.env, "")
inv.Stdin = strings.NewReader(tt.stdin)
inv.Stdout = &sb
inv.Stderr = &sb
clitest.SetupConfig(t, client, root)
+97
View File
@@ -0,0 +1,97 @@
package cli
import (
"fmt"
"strings"
"time"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/pretty"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) taskDelete() *serpent.Command {
cmd := &serpent.Command{
Use: "delete <task> [<task> ...]",
Short: "Delete experimental tasks",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, -1),
),
Options: serpent.OptionSet{
cliui.SkipPromptOption(),
},
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
exp := codersdk.NewExperimentalClient(client)
type toDelete struct {
ID uuid.UUID
Owner string
Display string
}
var items []toDelete
for _, identifier := range inv.Args {
identifier = strings.TrimSpace(identifier)
if identifier == "" {
return xerrors.New("task identifier cannot be empty or whitespace")
}
// Check task identifier, try UUID first.
if id, err := uuid.Parse(identifier); err == nil {
task, err := exp.TaskByID(ctx, id)
if err != nil {
return xerrors.Errorf("resolve task %q: %w", identifier, err)
}
display := fmt.Sprintf("%s/%s", task.OwnerName, task.Name)
items = append(items, toDelete{ID: id, Display: display, Owner: task.OwnerName})
continue
}
// Non-UUID, treat as a workspace identifier (name or owner/name).
ws, err := namedWorkspace(ctx, client, identifier)
if err != nil {
return xerrors.Errorf("resolve task %q: %w", identifier, err)
}
display := ws.FullName()
items = append(items, toDelete{ID: ws.ID, Display: display, Owner: ws.OwnerName})
}
// Confirm deletion of the tasks.
var displayList []string
for _, it := range items {
displayList = append(displayList, it.Display)
}
_, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: fmt.Sprintf("Delete these tasks: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(displayList, ", "))),
IsConfirm: true,
Default: cliui.ConfirmNo,
})
if err != nil {
return err
}
for _, item := range items {
if err := exp.DeleteTask(ctx, item.Owner, item.ID); err != nil {
return xerrors.Errorf("delete task %q: %w", item.Display, err)
}
_, _ = fmt.Fprintln(
inv.Stdout, "Deleted task "+pretty.Sprint(cliui.DefaultStyles.Keyword, item.Display)+" at "+cliui.Timestamp(time.Now()),
)
}
return nil
},
}
return cmd
}
+224
View File
@@ -0,0 +1,224 @@
package cli_test
import (
"bytes"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
)
func TestExpTaskDelete(t *testing.T) {
t.Parallel()
type testCounters struct {
deleteCalls atomic.Int64
nameResolves atomic.Int64
}
type handlerBuilder func(c *testCounters) http.HandlerFunc
type testCase struct {
name string
args []string
promptYes bool
wantErr bool
wantDeleteCalls int64
wantNameResolves int64
wantDeletedMessage int
buildHandler handlerBuilder
}
const (
id1 = "11111111-1111-1111-1111-111111111111"
id2 = "22222222-2222-2222-2222-222222222222"
id3 = "33333333-3333-3333-3333-333333333333"
id4 = "44444444-4444-4444-4444-444444444444"
id5 = "55555555-5555-5555-5555-555555555555"
)
cases := []testCase{
{
name: "Prompted_ByName_OK",
args: []string{"exists"},
promptYes: true,
buildHandler: func(c *testCounters) http.HandlerFunc {
taskID := uuid.MustParse(id1)
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/v2/users/me/workspace/exists":
c.nameResolves.Add(1)
httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Workspace{
ID: taskID,
Name: "exists",
OwnerName: "me",
})
case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id1:
c.deleteCalls.Add(1)
w.WriteHeader(http.StatusAccepted)
default:
httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
}
}
},
wantDeleteCalls: 1,
wantNameResolves: 1,
},
{
name: "Prompted_ByUUID_OK",
args: []string{id2},
promptYes: true,
buildHandler: func(c *testCounters) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/"+id2:
httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{
ID: uuid.MustParse(id2),
OwnerName: "me",
Name: "uuid-task",
})
case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id2:
c.deleteCalls.Add(1)
w.WriteHeader(http.StatusAccepted)
default:
httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
}
}
},
wantDeleteCalls: 1,
},
{
name: "Multiple_YesFlag",
args: []string{"--yes", "first", id4},
buildHandler: func(c *testCounters) http.HandlerFunc {
firstID := uuid.MustParse(id3)
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/v2/users/me/workspace/first":
c.nameResolves.Add(1)
httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Workspace{
ID: firstID,
Name: "first",
OwnerName: "me",
})
case r.Method == http.MethodGet && r.URL.Path == "/api/experimental/tasks/me/"+id4:
httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Task{
ID: uuid.MustParse(id4),
OwnerName: "me",
Name: "uuid-task-2",
})
case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id3:
c.deleteCalls.Add(1)
w.WriteHeader(http.StatusAccepted)
case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id4:
c.deleteCalls.Add(1)
w.WriteHeader(http.StatusAccepted)
default:
httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
}
}
},
wantDeleteCalls: 2,
wantNameResolves: 1,
wantDeletedMessage: 2,
},
{
name: "ResolveNameError",
args: []string{"doesnotexist"},
wantErr: true,
buildHandler: func(_ *testCounters) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/v2/users/me/workspace/doesnotexist":
httpapi.ResourceNotFound(w)
default:
httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
}
}
},
},
{
name: "DeleteError",
args: []string{"bad"},
promptYes: true,
wantErr: true,
buildHandler: func(c *testCounters) http.HandlerFunc {
taskID := uuid.MustParse(id5)
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/v2/users/me/workspace/bad":
c.nameResolves.Add(1)
httpapi.Write(r.Context(), w, http.StatusOK, codersdk.Workspace{
ID: taskID,
Name: "bad",
OwnerName: "me",
})
case r.Method == http.MethodDelete && r.URL.Path == "/api/experimental/tasks/me/"+id5:
httpapi.InternalServerError(w, xerrors.New("boom"))
default:
httpapi.InternalServerError(w, xerrors.New("unwanted path: "+r.Method+" "+r.URL.Path))
}
}
},
wantNameResolves: 1,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitMedium)
var counters testCounters
srv := httptest.NewServer(tc.buildHandler(&counters))
t.Cleanup(srv.Close)
client := codersdk.New(testutil.MustURL(t, srv.URL))
args := append([]string{"exp", "task", "delete"}, tc.args...)
inv, root := clitest.New(t, args...)
inv = inv.WithContext(ctx)
clitest.SetupConfig(t, client, root)
var runErr error
var outBuf bytes.Buffer
if tc.promptYes {
pty := ptytest.New(t).Attach(inv)
w := clitest.StartWithWaiter(t, inv)
pty.ExpectMatch("Delete these tasks:")
pty.WriteLine("yes")
runErr = w.Wait()
outBuf.Write(pty.ReadAll())
} else {
inv.Stdout = &outBuf
inv.Stderr = &outBuf
runErr = inv.Run()
}
if tc.wantErr {
require.Error(t, runErr)
} else {
require.NoError(t, runErr)
}
require.Equal(t, tc.wantDeleteCalls, counters.deleteCalls.Load(), "wrong delete call count")
require.Equal(t, tc.wantNameResolves, counters.nameResolves.Load(), "wrong name resolve count")
if tc.wantDeletedMessage > 0 {
output := outBuf.String()
require.GreaterOrEqual(t, strings.Count(output, "Deleted task"), tc.wantDeletedMessage)
}
})
}
}
+22 -3
View File
@@ -36,13 +36,12 @@ func (r *RootCmd) taskList() *serpent.Command {
statusFilter string
all bool
user string
quiet bool
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter(
cliui.TableFormat(
[]taskListRow{},
[]string{
"id",
"name",
"status",
"state",
@@ -73,7 +72,6 @@ func (r *RootCmd) taskList() *serpent.Command {
Aliases: []string{"ls"},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Options: serpent.OptionSet{
{
@@ -98,8 +96,21 @@ func (r *RootCmd) taskList() *serpent.Command {
Default: "",
Value: serpent.StringOf(&user),
},
{
Name: "quiet",
Description: "Only display task IDs.",
Flag: "quiet",
FlagShorthand: "q",
Default: "false",
Value: serpent.BoolOf(&quiet),
},
},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
exp := codersdk.NewExperimentalClient(client)
@@ -116,6 +127,14 @@ func (r *RootCmd) taskList() *serpent.Command {
return xerrors.Errorf("list tasks: %w", err)
}
if quiet {
for _, task := range tasks {
_, _ = fmt.Fprintln(inv.Stdout, task.ID.String())
}
return nil
}
// If no rows and not JSON, show a friendly message.
if len(tasks) == 0 && formatter.FormatID() != cliui.JSONFormat().ID() {
_, _ = fmt.Fprintln(inv.Stderr, "No tasks found.")
@@ -6,6 +6,8 @@ import (
"database/sql"
"encoding/json"
"io"
"slices"
"strings"
"testing"
"github.com/google/uuid"
@@ -200,6 +202,43 @@ func TestExpTaskList(t *testing.T) {
pty.ExpectMatch(ws.Name)
})
t.Run("Quiet", func(t *testing.T) {
t.Parallel()
// Quiet logger to reduce noise.
quiet := slog.Make(sloghuman.Sink(io.Discard))
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{Logger: &quiet})
owner := coderdtest.CreateFirstUser(t, client)
memberClient, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
// Given: We have two tasks
task1 := makeAITask(t, db, owner.OrganizationID, owner.UserID, memberUser.ID, database.WorkspaceTransitionStart, "keep me running")
task2 := makeAITask(t, db, owner.OrganizationID, owner.UserID, memberUser.ID, database.WorkspaceTransitionStop, "stop me please")
// Given: We add the `--quiet` flag
inv, root := clitest.New(t, "exp", "task", "list", "--quiet")
clitest.SetupConfig(t, memberClient, root)
ctx := testutil.Context(t, testutil.WaitShort)
var stdout bytes.Buffer
inv.Stdout = &stdout
inv.Stderr = &stdout
// When: We run the command
err := inv.WithContext(ctx).Run()
require.NoError(t, err)
want := []string{task1.ID.String(), task2.ID.String()}
got := slice.Filter(strings.Split(stdout.String(), "\n"), func(s string) bool {
return len(s) != 0
})
slices.Sort(want)
slices.Sort(got)
require.Equal(t, want, got)
})
}
func TestExpTaskList_OwnerCanListOthers(t *testing.T) {
+73
View File
@@ -0,0 +1,73 @@
package cli
import (
"fmt"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) taskLogs() *serpent.Command {
formatter := cliui.NewOutputFormatter(
cliui.TableFormat(
[]codersdk.TaskLogEntry{},
[]string{
"type",
"content",
},
),
cliui.JSONFormat(),
)
cmd := &serpent.Command{
Use: "logs <task>",
Short: "Show a task's logs",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var (
ctx = inv.Context()
exp = codersdk.NewExperimentalClient(client)
task = inv.Args[0]
taskID uuid.UUID
)
if id, err := uuid.Parse(task); err == nil {
taskID = id
} else {
ws, err := namedWorkspace(ctx, client, task)
if err != nil {
return xerrors.Errorf("resolve task %q: %w", task, err)
}
taskID = ws.ID
}
logs, err := exp.TaskLogs(ctx, codersdk.Me, taskID)
if err != nil {
return xerrors.Errorf("get task logs: %w", err)
}
out, err := formatter.Format(ctx, logs.Logs)
if err != nil {
return xerrors.Errorf("format task logs: %w", err)
}
_, _ = fmt.Fprintln(inv.Stdout, out)
return nil
},
}
formatter.AttachOptions(&cmd.Options)
return cmd
}
+200
View File
@@ -0,0 +1,200 @@
package cli_test
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
func Test_TaskLogs(t *testing.T) {
t.Parallel()
var (
clock = time.Date(2025, 8, 26, 12, 34, 56, 0, time.UTC)
taskID = uuid.MustParse("11111111-1111-1111-1111-111111111111")
taskName = "task-workspace"
taskLogs = []codersdk.TaskLogEntry{
{
ID: 0,
Content: "What is 1 + 1?",
Type: codersdk.TaskLogTypeInput,
Time: clock,
},
{
ID: 1,
Content: "2",
Type: codersdk.TaskLogTypeOutput,
Time: clock.Add(1 * time.Second),
},
}
)
tests := []struct {
args []string
expectTable string
expectLogs []codersdk.TaskLogEntry
expectError string
handler func(t *testing.T, ctx context.Context) http.HandlerFunc
}{
{
args: []string{taskName, "--output", "json"},
expectLogs: taskLogs,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/v2/users/me/workspace/%s", taskName):
httpapi.Write(ctx, w, http.StatusOK, codersdk.Workspace{
ID: taskID,
})
case fmt.Sprintf("/api/experimental/tasks/me/%s/logs", taskID.String()):
httpapi.Write(ctx, w, http.StatusOK, codersdk.TaskLogsResponse{
Logs: taskLogs,
})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{taskID.String(), "--output", "json"},
expectLogs: taskLogs,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/logs", taskID.String()):
httpapi.Write(ctx, w, http.StatusOK, codersdk.TaskLogsResponse{
Logs: taskLogs,
})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{taskID.String()},
expectTable: `
TYPE CONTENT
input What is 1 + 1?
output 2`,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/logs", taskID.String()):
httpapi.Write(ctx, w, http.StatusOK, codersdk.TaskLogsResponse{
Logs: taskLogs,
})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{"doesnotexist"},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v2/users/me/workspace/doesnotexist":
httpapi.ResourceNotFound(w)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{uuid.Nil.String()}, // uuid does not exist
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/logs", uuid.Nil.String()):
httpapi.ResourceNotFound(w)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{"err-fetching-logs"},
expectError: assert.AnError.Error(),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v2/users/me/workspace/err-fetching-logs":
httpapi.Write(ctx, w, http.StatusOK, codersdk.Workspace{
ID: taskID,
})
case fmt.Sprintf("/api/experimental/tasks/me/%s/logs", taskID.String()):
httpapi.InternalServerError(w, assert.AnError)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
}
for _, tt := range tests {
t.Run(strings.Join(tt.args, ","), func(t *testing.T) {
t.Parallel()
var (
ctx = testutil.Context(t, testutil.WaitShort)
srv = httptest.NewServer(tt.handler(t, ctx))
client = codersdk.New(testutil.MustURL(t, srv.URL))
args = []string{"exp", "task", "logs"}
stdout strings.Builder
err error
)
t.Cleanup(srv.Close)
inv, root := clitest.New(t, append(args, tt.args...)...)
inv.Stdout = &stdout
inv.Stderr = &stdout
clitest.SetupConfig(t, client, root)
err = inv.WithContext(ctx).Run()
if tt.expectError == "" {
assert.NoError(t, err)
} else {
assert.ErrorContains(t, err, tt.expectError)
}
if tt.expectTable != "" {
if diff := tableDiff(tt.expectTable, stdout.String()); diff != "" {
t.Errorf("unexpected output diff (-want +got):\n%s", diff)
}
}
if tt.expectLogs != nil {
var logs []codersdk.TaskLogEntry
err = json.NewDecoder(strings.NewReader(stdout.String())).Decode(&logs)
require.NoError(t, err)
assert.Equal(t, tt.expectLogs, logs)
}
})
}
}
+78
View File
@@ -0,0 +1,78 @@
package cli
import (
"io"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) taskSend() *serpent.Command {
var stdin bool
cmd := &serpent.Command{
Use: "send <task> [<input> | --stdin]",
Short: "Send input to a task",
Middleware: serpent.RequireRangeArgs(1, 2),
Options: serpent.OptionSet{
{
Name: "stdin",
Flag: "stdin",
Description: "Reads the input from stdin.",
Value: serpent.BoolOf(&stdin),
},
},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var (
ctx = inv.Context()
exp = codersdk.NewExperimentalClient(client)
task = inv.Args[0]
taskInput string
taskID uuid.UUID
)
if stdin {
bytes, err := io.ReadAll(inv.Stdin)
if err != nil {
return xerrors.Errorf("reading stdio: %w", err)
}
taskInput = string(bytes)
} else {
if len(inv.Args) != 2 {
return xerrors.Errorf("expected an input for the task")
}
taskInput = inv.Args[1]
}
if id, err := uuid.Parse(task); err == nil {
taskID = id
} else {
ws, err := namedWorkspace(ctx, client, task)
if err != nil {
return xerrors.Errorf("resolve task: %w", err)
}
taskID = ws.ID
}
if err = exp.TaskSend(ctx, codersdk.Me, taskID, codersdk.TaskSendRequest{Input: taskInput}); err != nil {
return xerrors.Errorf("send input to task: %w", err)
}
return nil
},
}
return cmd
}
+173
View File
@@ -0,0 +1,173 @@
package cli_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
func Test_TaskSend(t *testing.T) {
t.Parallel()
var (
taskName = "task-workspace"
taskID = uuid.MustParse("11111111-1111-1111-1111-111111111111")
)
tests := []struct {
args []string
stdin string
expectError string
handler func(t *testing.T, ctx context.Context) http.HandlerFunc
}{
{
args: []string{taskName, "carry on with the task"},
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/v2/users/me/workspace/%s", taskName):
httpapi.Write(ctx, w, http.StatusOK, codersdk.Workspace{
ID: taskID,
})
case fmt.Sprintf("/api/experimental/tasks/me/%s/send", taskID.String()):
var req codersdk.TaskSendRequest
if !httpapi.Read(ctx, w, r, &req) {
return
}
assert.Equal(t, "carry on with the task", req.Input)
httpapi.Write(ctx, w, http.StatusNoContent, nil)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{taskID.String(), "carry on with the task"},
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/send", taskID.String()):
var req codersdk.TaskSendRequest
if !httpapi.Read(ctx, w, r, &req) {
return
}
assert.Equal(t, "carry on with the task", req.Input)
httpapi.Write(ctx, w, http.StatusNoContent, nil)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{taskName, "--stdin"},
stdin: "carry on with the task",
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/v2/users/me/workspace/%s", taskName):
httpapi.Write(ctx, w, http.StatusOK, codersdk.Workspace{
ID: taskID,
})
case fmt.Sprintf("/api/experimental/tasks/me/%s/send", taskID.String()):
var req codersdk.TaskSendRequest
if !httpapi.Read(ctx, w, r, &req) {
return
}
assert.Equal(t, "carry on with the task", req.Input)
httpapi.Write(ctx, w, http.StatusNoContent, nil)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{"doesnotexist", "some task input"},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v2/users/me/workspace/doesnotexist":
httpapi.ResourceNotFound(w)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{uuid.Nil.String(), "some task input"},
expectError: httpapi.ResourceNotFoundResponse.Message,
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/send", uuid.Nil.String()):
httpapi.ResourceNotFound(w)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
{
args: []string{uuid.Nil.String(), "some task input"},
expectError: assert.AnError.Error(),
handler: func(t *testing.T, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case fmt.Sprintf("/api/experimental/tasks/me/%s/send", uuid.Nil.String()):
httpapi.InternalServerError(w, assert.AnError)
default:
t.Errorf("unexpected path: %s", r.URL.Path)
}
}
},
},
}
for _, tt := range tests {
t.Run(strings.Join(tt.args, ","), func(t *testing.T) {
t.Parallel()
var (
ctx = testutil.Context(t, testutil.WaitShort)
srv = httptest.NewServer(tt.handler(t, ctx))
client = codersdk.New(testutil.MustURL(t, srv.URL))
args = []string{"exp", "task", "send"}
err error
)
t.Cleanup(srv.Close)
inv, root := clitest.New(t, append(args, tt.args...)...)
inv.Stdin = strings.NewReader(tt.stdin)
clitest.SetupConfig(t, client, root)
err = inv.WithContext(ctx).Run()
if tt.expectError == "" {
assert.NoError(t, err)
} else {
assert.ErrorContains(t, err, tt.expectError)
}
})
}
}
+53 -27
View File
@@ -15,13 +15,13 @@ import (
func (r *RootCmd) taskStatus() *serpent.Command {
var (
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter(
cliui.TableFormat(
[]taskStatusRow{},
[]string{
"state changed",
"status",
"healthy",
"state",
"message",
},
@@ -66,9 +66,13 @@ func (r *RootCmd) taskStatus() *serpent.Command {
},
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(i *serpent.Invocation) error {
client, err := r.InitClient(i)
if err != nil {
return err
}
ctx := i.Context()
ec := codersdk.NewExperimentalClient(client)
identifier := i.Args[0]
@@ -89,44 +93,46 @@ func (r *RootCmd) taskStatus() *serpent.Command {
return err
}
out, err := formatter.Format(ctx, toStatusRow(task))
tsr := toStatusRow(task)
out, err := formatter.Format(ctx, []taskStatusRow{tsr})
if err != nil {
return xerrors.Errorf("format task status: %w", err)
}
_, _ = fmt.Fprintln(i.Stdout, out)
if !watchArg {
if !watchArg || taskWatchIsEnded(task) {
return nil
}
lastStatus := task.Status
lastState := task.CurrentState
t := time.NewTicker(watchIntervalArg)
defer t.Stop()
// TODO: implement streaming updates instead of polling
lastStatusRow := tsr
for range t.C {
task, err := ec.TaskByID(ctx, taskID)
if err != nil {
return err
}
if lastStatus == task.Status && taskStatusEqual(lastState, task.CurrentState) {
continue
}
out, err := formatter.Format(ctx, toStatusRow(task))
if err != nil {
return xerrors.Errorf("format task status: %w", err)
}
// hack: skip the extra column header from formatter
if formatter.FormatID() != cliui.JSONFormat().ID() {
out = strings.SplitN(out, "\n", 2)[1]
}
_, _ = fmt.Fprintln(i.Stdout, out)
if task.Status == codersdk.WorkspaceStatusStopped {
// Only print if something changed
newStatusRow := toStatusRow(task)
if !taskStatusRowEqual(lastStatusRow, newStatusRow) {
out, err := formatter.Format(ctx, []taskStatusRow{newStatusRow})
if err != nil {
return xerrors.Errorf("format task status: %w", err)
}
// hack: skip the extra column header from formatter
if formatter.FormatID() != cliui.JSONFormat().ID() {
out = strings.SplitN(out, "\n", 2)[1]
}
_, _ = fmt.Fprintln(i.Stdout, out)
}
if taskWatchIsEnded(task) {
return nil
}
lastStatus = task.Status
lastState = task.CurrentState
lastStatusRow = newStatusRow
}
return nil
},
@@ -135,14 +141,20 @@ func (r *RootCmd) taskStatus() *serpent.Command {
return cmd
}
func taskStatusEqual(s1, s2 *codersdk.TaskStateEntry) bool {
if s1 == nil && s2 == nil {
func taskWatchIsEnded(task codersdk.Task) bool {
if task.Status == codersdk.WorkspaceStatusStopped {
return true
}
if s1 == nil || s2 == nil {
if task.WorkspaceAgentHealth == nil || !task.WorkspaceAgentHealth.Healthy {
return false
}
return s1.State == s2.State
if task.WorkspaceAgentLifecycle == nil || task.WorkspaceAgentLifecycle.Starting() || task.WorkspaceAgentLifecycle.ShuttingDown() {
return false
}
if task.CurrentState == nil || task.CurrentState.State == codersdk.TaskStateWorking {
return false
}
return true
}
type taskStatusRow struct {
@@ -150,22 +162,36 @@ type taskStatusRow struct {
ChangedAgo string `json:"-" table:"state changed,default_sort"`
Timestamp time.Time `json:"-" table:"-"`
TaskStatus string `json:"-" table:"status"`
Healthy bool `json:"-" table:"healthy"`
TaskState string `json:"-" table:"state"`
Message string `json:"-" table:"message"`
}
func toStatusRow(task codersdk.Task) []taskStatusRow {
func taskStatusRowEqual(r1, r2 taskStatusRow) bool {
return r1.TaskStatus == r2.TaskStatus &&
r1.Healthy == r2.Healthy &&
r1.TaskState == r2.TaskState &&
r1.Message == r2.Message
}
func toStatusRow(task codersdk.Task) taskStatusRow {
tsr := taskStatusRow{
Task: task,
ChangedAgo: time.Since(task.UpdatedAt).Truncate(time.Second).String() + " ago",
Timestamp: task.UpdatedAt,
TaskStatus: string(task.Status),
}
tsr.Healthy = task.WorkspaceAgentHealth != nil &&
task.WorkspaceAgentHealth.Healthy &&
task.WorkspaceAgentLifecycle != nil &&
!task.WorkspaceAgentLifecycle.Starting() &&
!task.WorkspaceAgentLifecycle.ShuttingDown()
if task.CurrentState != nil {
tsr.ChangedAgo = time.Since(task.CurrentState.Timestamp).Truncate(time.Second).String() + " ago"
tsr.Timestamp = task.CurrentState.Timestamp
tsr.TaskState = string(task.CurrentState.State)
tsr.Message = task.CurrentState.Message
}
return []taskStatusRow{tsr}
return tsr
}
+36 -35
View File
@@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
@@ -63,8 +64,8 @@ func Test_TaskStatus(t *testing.T) {
},
{
args: []string{"exists"},
expectOutput: `STATE CHANGED STATUS STATE MESSAGE
0s ago running working Thinking furiously...`,
expectOutput: `STATE CHANGED STATUS HEALTHY STATE MESSAGE
0s ago running true working Thinking furiously...`,
hf: func(ctx context.Context, now time.Time) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
@@ -83,6 +84,10 @@ func Test_TaskStatus(t *testing.T) {
Timestamp: now,
Message: "Thinking furiously...",
},
WorkspaceAgentHealth: &codersdk.WorkspaceAgentHealth{
Healthy: true,
},
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
})
default:
t.Errorf("unexpected path: %s", r.URL.Path)
@@ -93,12 +98,10 @@ func Test_TaskStatus(t *testing.T) {
{
args: []string{"exists", "--watch"},
expectOutput: `
STATE CHANGED STATUS STATE MESSAGE
4s ago running
3s ago running working Reticulating splines...
2s ago running completed Splines reticulated successfully!
2s ago stopping completed Splines reticulated successfully!
2s ago stopped completed Splines reticulated successfully!`,
STATE CHANGED STATUS HEALTHY STATE MESSAGE
4s ago running true
3s ago running true working Reticulating splines...
2s ago running true complete Splines reticulated successfully!`,
hf: func(ctx context.Context, now time.Time) func(http.ResponseWriter, *http.Request) {
var calls atomic.Int64
return func(w http.ResponseWriter, r *http.Request) {
@@ -116,13 +119,21 @@ STATE CHANGED STATUS STATE MESSAGE
Status: codersdk.WorkspaceStatusPending,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now.Add(-5 * time.Second),
WorkspaceAgentHealth: &codersdk.WorkspaceAgentHealth{
Healthy: true,
},
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
})
case 1:
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
Status: codersdk.WorkspaceStatusRunning,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now.Add(-4 * time.Second),
WorkspaceAgentHealth: &codersdk.WorkspaceAgentHealth{
Healthy: true,
},
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
UpdatedAt: now.Add(-4 * time.Second),
})
case 2:
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
@@ -130,6 +141,10 @@ STATE CHANGED STATUS STATE MESSAGE
Status: codersdk.WorkspaceStatusRunning,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now.Add(-4 * time.Second),
WorkspaceAgentHealth: &codersdk.WorkspaceAgentHealth{
Healthy: true,
},
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
CurrentState: &codersdk.TaskStateEntry{
State: codersdk.TaskStateWorking,
Timestamp: now.Add(-3 * time.Second),
@@ -142,32 +157,12 @@ STATE CHANGED STATUS STATE MESSAGE
Status: codersdk.WorkspaceStatusRunning,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now.Add(-4 * time.Second),
CurrentState: &codersdk.TaskStateEntry{
State: codersdk.TaskStateCompleted,
Timestamp: now.Add(-2 * time.Second),
Message: "Splines reticulated successfully!",
WorkspaceAgentHealth: &codersdk.WorkspaceAgentHealth{
Healthy: true,
},
})
case 4:
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
Status: codersdk.WorkspaceStatusStopping,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now.Add(-1 * time.Second),
WorkspaceAgentLifecycle: ptr.Ref(codersdk.WorkspaceAgentLifecycleReady),
CurrentState: &codersdk.TaskStateEntry{
State: codersdk.TaskStateCompleted,
Timestamp: now.Add(-2 * time.Second),
Message: "Splines reticulated successfully!",
},
})
case 5:
httpapi.Write(ctx, w, http.StatusOK, codersdk.Task{
ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"),
Status: codersdk.WorkspaceStatusStopped,
CreatedAt: now.Add(-5 * time.Second),
UpdatedAt: now,
CurrentState: &codersdk.TaskStateEntry{
State: codersdk.TaskStateCompleted,
State: codersdk.TaskStateComplete,
Timestamp: now.Add(-2 * time.Second),
Message: "Splines reticulated successfully!",
},
@@ -188,9 +183,16 @@ STATE CHANGED STATUS STATE MESSAGE
"id": "11111111-1111-1111-1111-111111111111",
"organization_id": "00000000-0000-0000-0000-000000000000",
"owner_id": "00000000-0000-0000-0000-000000000000",
"owner_name": "",
"name": "",
"template_id": "00000000-0000-0000-0000-000000000000",
"template_name": "",
"template_display_name": "",
"template_icon": "",
"workspace_id": null,
"workspace_agent_id": null,
"workspace_agent_lifecycle": null,
"workspace_agent_health": null,
"initial_prompt": "",
"status": "running",
"current_state": {
@@ -236,13 +238,12 @@ STATE CHANGED STATUS STATE MESSAGE
ctx = testutil.Context(t, testutil.WaitShort)
now = time.Now().UTC() // TODO: replace with quartz
srv = httptest.NewServer(http.HandlerFunc(tc.hf(ctx, now)))
client = new(codersdk.Client)
client = codersdk.New(testutil.MustURL(t, srv.URL))
sb = strings.Builder{}
args = []string{"exp", "task", "status", "--watch-interval", testutil.IntervalFast.String()}
)
t.Cleanup(srv.Close)
client.URL = testutil.MustURL(t, srv.URL)
args = append(args, tc.args...)
inv, root := clitest.New(t, args...)
inv.Stdout = &sb
-128
View File
@@ -1,128 +0,0 @@
package cli
import (
"fmt"
"strings"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) taskCreate() *serpent.Command {
var (
orgContext = NewOrganizationContext()
client = new(codersdk.Client)
templateName string
templateVersionName string
presetName string
taskInput string
)
cmd := &serpent.Command{
Use: "create [template]",
Short: "Create an experimental task",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
),
Options: serpent.OptionSet{
{
Flag: "input",
Env: "CODER_TASK_INPUT",
Value: serpent.StringOf(&taskInput),
Required: true,
},
{
Env: "CODER_TASK_TEMPLATE_NAME",
Value: serpent.StringOf(&templateName),
},
{
Env: "CODER_TASK_TEMPLATE_VERSION",
Value: serpent.StringOf(&templateVersionName),
},
{
Flag: "preset",
Env: "CODER_TASK_PRESET_NAME",
Value: serpent.StringOf(&presetName),
Default: PresetNone,
},
},
Handler: func(inv *serpent.Invocation) error {
var (
ctx = inv.Context()
expClient = codersdk.NewExperimentalClient(client)
templateVersionID uuid.UUID
templateVersionPresetID uuid.UUID
)
organization, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("get current organization: %w", err)
}
if len(inv.Args) > 0 {
templateName, templateVersionName, _ = strings.Cut(inv.Args[0], "@")
}
if templateName == "" {
return xerrors.Errorf("template name not provided")
}
if templateVersionName != "" {
templateVersion, err := client.TemplateVersionByOrganizationAndName(ctx, organization.ID, templateName, templateVersionName)
if err != nil {
return xerrors.Errorf("get template version: %w", err)
}
templateVersionID = templateVersion.ID
} else {
template, err := client.TemplateByName(ctx, organization.ID, templateName)
if err != nil {
return xerrors.Errorf("get template: %w", err)
}
templateVersionID = template.ActiveVersionID
}
if presetName != PresetNone {
templatePresets, err := client.TemplateVersionPresets(ctx, templateVersionID)
if err != nil {
return xerrors.Errorf("get template presets: %w", err)
}
preset, err := resolvePreset(templatePresets, presetName)
if err != nil {
return xerrors.Errorf("resolve preset: %w", err)
}
templateVersionPresetID = preset.ID
}
workspace, err := expClient.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{
TemplateVersionID: templateVersionID,
TemplateVersionPresetID: templateVersionPresetID,
Prompt: taskInput,
})
if err != nil {
return xerrors.Errorf("create task: %w", err)
}
_, _ = fmt.Fprintf(
inv.Stdout,
"The task %s has been created at %s!\n",
cliui.Keyword(workspace.Name),
cliui.Timestamp(workspace.CreatedAt),
)
return nil
},
}
orgContext.AttachOptions(cmd)
return cmd
}
+9 -14
View File
@@ -2,19 +2,16 @@ package cli
import (
"encoding/json"
"fmt"
"golang.org/x/xerrors"
"github.com/tidwall/gjson"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/pretty"
"github.com/coder/serpent"
)
func (r *RootCmd) externalAuth() *serpent.Command {
func externalAuth() *serpent.Command {
return &serpent.Command{
Use: "external-auth",
Short: "Manage external authentication",
@@ -23,14 +20,15 @@ func (r *RootCmd) externalAuth() *serpent.Command {
return i.Command.HelpHandler(i)
},
Children: []*serpent.Command{
r.externalAuthAccessToken(),
externalAuthAccessToken(),
},
}
}
func (r *RootCmd) externalAuthAccessToken() *serpent.Command {
func externalAuthAccessToken() *serpent.Command {
var extra string
return &serpent.Command{
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "access-token <provider>",
Short: "Print auth for an external provider",
Long: "Print an access-token for an external auth provider. " +
@@ -70,12 +68,7 @@ fi
ctx, stop := inv.SignalNotifyContext(ctx, StopSignals...)
defer stop()
if r.agentToken == "" {
_, _ = fmt.Fprint(inv.Stderr, pretty.Sprintf(headLineStyle(), "No agent token found, this command must be run from inside a running workspace.\n"))
return xerrors.Errorf("agent token not found")
}
client, err := r.tryCreateAgentClient()
client, err := agentAuth.CreateClient()
if err != nil {
return xerrors.Errorf("create agent client: %w", err)
}
@@ -115,4 +108,6 @@ fi
return nil
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
+10 -5
View File
@@ -5,12 +5,10 @@ import (
"golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) favorite() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Aliases: []string{"fav", "favou" + "rite"},
Annotations: workspaceCommand,
@@ -18,9 +16,13 @@ func (r *RootCmd) favorite() *serpent.Command {
Short: "Add a workspace to your favorites",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ws, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return xerrors.Errorf("get workspace: %w", err)
@@ -37,7 +39,6 @@ func (r *RootCmd) favorite() *serpent.Command {
}
func (r *RootCmd) unfavorite() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Aliases: []string{"unfav", "unfavou" + "rite"},
Annotations: workspaceCommand,
@@ -45,9 +46,13 @@ func (r *RootCmd) unfavorite() *serpent.Command {
Short: "Remove a workspace from your favorites",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ws, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return xerrors.Errorf("get workspace: %w", err)
+5 -3
View File
@@ -18,8 +18,8 @@ import (
// gitAskpass is used by the Coder agent to automatically authenticate
// with Git providers based on a hostname.
func (r *RootCmd) gitAskpass() *serpent.Command {
return &serpent.Command{
func gitAskpass(agentAuth *AgentAuth) *serpent.Command {
cmd := &serpent.Command{
Use: "gitaskpass",
Hidden: true,
Handler: func(inv *serpent.Invocation) error {
@@ -33,7 +33,7 @@ func (r *RootCmd) gitAskpass() *serpent.Command {
return xerrors.Errorf("parse host: %w", err)
}
client, err := r.tryCreateAgentClient()
client, err := agentAuth.CreateClient()
if err != nil {
return xerrors.Errorf("create agent client: %w", err)
}
@@ -90,4 +90,6 @@ func (r *RootCmd) gitAskpass() *serpent.Command {
return nil
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
+7 -1
View File
@@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
)
func TestGitAskpass(t *testing.T) {
@@ -32,6 +33,7 @@ func TestGitAskpass(t *testing.T) {
url := srv.URL
inv, _ := clitest.New(t, "--agent-url", url, "Username for 'https://github.com':")
inv.Environ.Set("GIT_PREFIX", "/")
inv.Environ.Set("CODER_AGENT_TOKEN", "fake-token")
pty := ptytest.New(t)
inv.Stdout = pty.Output()
clitest.Start(t, inv)
@@ -39,6 +41,7 @@ func TestGitAskpass(t *testing.T) {
inv, _ = clitest.New(t, "--agent-url", url, "Password for 'https://potato@github.com':")
inv.Environ.Set("GIT_PREFIX", "/")
inv.Environ.Set("CODER_AGENT_TOKEN", "fake-token")
pty = ptytest.New(t)
inv.Stdout = pty.Output()
clitest.Start(t, inv)
@@ -56,6 +59,7 @@ func TestGitAskpass(t *testing.T) {
url := srv.URL
inv, _ := clitest.New(t, "--agent-url", url, "--no-open", "Username for 'https://github.com':")
inv.Environ.Set("GIT_PREFIX", "/")
inv.Environ.Set("CODER_AGENT_TOKEN", "fake-token")
pty := ptytest.New(t)
inv.Stderr = pty.Output()
err := inv.Run()
@@ -65,6 +69,7 @@ func TestGitAskpass(t *testing.T) {
t.Run("Poll", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
resp := atomic.Pointer[agentsdk.ExternalAuthResponse]{}
resp.Store(&agentsdk.ExternalAuthResponse{
URL: "https://something.org",
@@ -86,6 +91,7 @@ func TestGitAskpass(t *testing.T) {
inv, _ := clitest.New(t, "--agent-url", url, "--no-open", "Username for 'https://github.com':")
inv.Environ.Set("GIT_PREFIX", "/")
inv.Environ.Set("CODER_AGENT_TOKEN", "fake-token")
stdout := ptytest.New(t)
inv.Stdout = stdout.Output()
stderr := ptytest.New(t)
@@ -94,7 +100,7 @@ func TestGitAskpass(t *testing.T) {
err := inv.Run()
assert.NoError(t, err)
}()
<-poll
testutil.RequireReceive(ctx, t, poll)
stderr.ExpectMatch("Open the following URL to authenticate")
resp.Store(&agentsdk.ExternalAuthResponse{
Username: "username",
+4 -3
View File
@@ -18,7 +18,8 @@ import (
"github.com/coder/serpent"
)
func (r *RootCmd) gitssh() *serpent.Command {
func gitssh() *serpent.Command {
agentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "gitssh",
Hidden: true,
@@ -38,7 +39,7 @@ func (r *RootCmd) gitssh() *serpent.Command {
return err
}
client, err := r.tryCreateAgentClient()
client, err := agentAuth.CreateClient()
if err != nil {
return xerrors.Errorf("create agent client: %w", err)
}
@@ -108,7 +109,7 @@ func (r *RootCmd) gitssh() *serpent.Command {
return nil
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
+1 -2
View File
@@ -54,8 +54,7 @@ func prepareTestGitSSH(ctx context.Context, t *testing.T) (*agentsdk.Client, str
}).WithAgent().Do()
// start workspace agent
agentClient := agentsdk.New(client.URL)
agentClient.SetSessionToken(r.AgentToken)
agentClient := agentsdk.New(client.URL, agentsdk.WithFixedToken(r.AgentToken))
_ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
o.Client = agentClient
})
+30 -3
View File
@@ -95,8 +95,8 @@ func (r *RootCmd) list() *serpent.Command {
),
cliui.JSONFormat(),
)
sharedWithMe bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "list",
@@ -104,10 +104,37 @@ func (r *RootCmd) list() *serpent.Command {
Aliases: []string{"ls"},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Options: serpent.OptionSet{
{
Name: "shared-with-me",
Description: "Show workspaces shared with you.",
Flag: "shared-with-me",
Value: serpent.BoolOf(&sharedWithMe),
Hidden: true,
},
},
Handler: func(inv *serpent.Invocation) error {
res, err := QueryConvertWorkspaces(inv.Context(), client, filter.Filter(), WorkspaceListRowFromWorkspace)
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspaceFilter := filter.Filter()
if sharedWithMe {
user, err := client.User(inv.Context(), codersdk.Me)
if err != nil {
return xerrors.Errorf("fetch current user: %w", err)
}
workspaceFilter.SharedWithUser = user.ID.String()
// Unset the default query that conflicts with the --shared-with-me flag
if workspaceFilter.FilterQuery == "owner:me" {
workspaceFilter.FilterQuery = ""
}
}
res, err := QueryConvertWorkspaces(inv.Context(), client, workspaceFilter, WorkspaceListRowFromWorkspace)
if err != nil {
return err
}
+46
View File
@@ -13,6 +13,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
@@ -100,4 +101,49 @@ func TestList(t *testing.T) {
require.Len(t, stderr.Bytes(), 0)
})
t.Run("SharedWorkspaces", func(t *testing.T) {
t.Parallel()
var (
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
}),
})
orgOwner = coderdtest.CreateFirstUser(t, client)
memberClient, member = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
Name: "wibble",
OwnerID: orgOwner.UserID,
OrganizationID: orgOwner.OrganizationID,
}).Do().Workspace
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
Name: "wobble",
OwnerID: orgOwner.UserID,
OrganizationID: orgOwner.OrganizationID,
}).Do().Workspace
)
ctx := testutil.Context(t, testutil.WaitMedium)
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
UserRoles: map[string]codersdk.WorkspaceRole{
member.ID.String(): codersdk.WorkspaceRoleUse,
},
})
inv, root := clitest.New(t, "list", "--shared-with-me", "--output=json")
clitest.SetupConfig(t, memberClient, root)
stdout := new(bytes.Buffer)
inv.Stdout = stdout
err := inv.WithContext(ctx).Run()
require.NoError(t, err)
var workspaces []codersdk.Workspace
require.NoError(t, json.Unmarshal(stdout.Bytes(), &workspaces))
require.Len(t, workspaces, 1)
require.Equal(t, sharedWorkspace.ID, workspaces[0].ID)
})
}
+5 -6
View File
@@ -8,24 +8,23 @@ import (
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) logout() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "logout",
Short: "Unauthenticate your local session",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var errors []error
config := r.createConfig()
var err error
_, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Are you sure you want to log out?",
IsConfirm: true,
+5 -6
View File
@@ -9,22 +9,21 @@ import (
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/healthcheck/derphealth"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/healthsdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/serpent"
)
func (r *RootCmd) netcheck() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "netcheck",
Short: "Print network debug information for DERP and STUN",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(inv.Context(), 30*time.Second)
defer cancel()
+53 -10
View File
@@ -16,7 +16,7 @@ func (r *RootCmd) notifications() *serpent.Command {
Short: "Manage Coder notifications",
Long: "Administrators can use these commands to change notification settings.\n" + FormatExamples(
Example{
Description: "Pause Coder notifications. Administrators can temporarily stop notifiers from dispatching messages in case of the target outage (for example: unavailable SMTP server or Webhook not responding).",
Description: "Pause Coder notifications. Administrators can temporarily stop notifiers from dispatching messages in case of the target outage (for example: unavailable SMTP server or Webhook not responding)",
Command: "coder notifications pause",
},
Example{
@@ -24,9 +24,13 @@ func (r *RootCmd) notifications() *serpent.Command {
Command: "coder notifications resume",
},
Example{
Description: "Send a test notification. Administrators can use this to verify the notification target settings.",
Description: "Send a test notification. Administrators can use this to verify the notification target settings",
Command: "coder notifications test",
},
Example{
Description: "Send a custom notification to the requesting user. Sending notifications targeting other users or groups is currently not supported",
Command: "coder notifications custom \"Custom Title\" \"Custom Message\"",
},
),
Aliases: []string{"notification"},
Handler: func(inv *serpent.Invocation) error {
@@ -36,22 +40,26 @@ func (r *RootCmd) notifications() *serpent.Command {
r.pauseNotifications(),
r.resumeNotifications(),
r.testNotifications(),
r.customNotifications(),
},
}
return cmd
}
func (r *RootCmd) pauseNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "pause",
Short: "Pause notifications",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
NotifierPaused: true,
})
if err != nil {
@@ -66,16 +74,19 @@ func (r *RootCmd) pauseNotifications() *serpent.Command {
}
func (r *RootCmd) resumeNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "resume",
Short: "Resume notifications",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
NotifierPaused: false,
})
if err != nil {
@@ -90,15 +101,18 @@ func (r *RootCmd) resumeNotifications() *serpent.Command {
}
func (r *RootCmd) testNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "test",
Short: "Send a test notification",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if err := client.PostTestNotification(inv.Context()); err != nil {
return xerrors.Errorf("unable to post test notification: %w", err)
}
@@ -109,3 +123,32 @@ func (r *RootCmd) testNotifications() *serpent.Command {
}
return cmd
}
func (r *RootCmd) customNotifications() *serpent.Command {
cmd := &serpent.Command{
Use: "custom <title> <message>",
Short: "Send a custom notification",
Middleware: serpent.Chain(
serpent.RequireNArgs(2),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PostCustomNotification(inv.Context(), codersdk.CustomNotificationRequest{
Content: &codersdk.CustomNotificationContent{
Title: inv.Args[0],
Message: inv.Args[1],
},
})
if err != nil {
return xerrors.Errorf("unable to post custom notification: %w", err)
}
_, _ = fmt.Fprintln(inv.Stderr, "A custom notification has been sent.")
return nil
},
}
return cmd
}
+101
View File
@@ -12,6 +12,8 @@ import (
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/codersdk"
@@ -166,3 +168,102 @@ func TestNotificationsTest(t *testing.T) {
require.Len(t, sent, 0)
})
}
func TestCustomNotifications(t *testing.T) {
t.Parallel()
t.Run("BadRequest", func(t *testing.T) {
t.Parallel()
notifyEnq := &notificationstest.FakeEnqueuer{}
ownerClient := coderdtest.New(t, &coderdtest.Options{
DeploymentValues: coderdtest.DeploymentValues(t),
NotificationsEnqueuer: notifyEnq,
})
// Given: A member user
ownerUser := coderdtest.CreateFirstUser(t, ownerClient)
memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, ownerUser.OrganizationID)
// When: The member user attempts to send a custom notification with empty title and message
inv, root := clitest.New(t, "notifications", "custom", "", "")
clitest.SetupConfig(t, memberClient, root)
// Then: an error is expected with no notifications sent
err := inv.Run()
var sdkError *codersdk.Error
require.Error(t, err)
require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
require.Equal(t, http.StatusBadRequest, sdkError.StatusCode())
require.Equal(t, "Invalid request body", sdkError.Message)
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
require.Len(t, sent, 0)
})
t.Run("SystemUserNotAllowed", func(t *testing.T) {
t.Parallel()
notifyEnq := &notificationstest.FakeEnqueuer{}
ownerClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
DeploymentValues: coderdtest.DeploymentValues(t),
NotificationsEnqueuer: notifyEnq,
})
// Given: A system user (prebuilds system user)
_, token := dbgen.APIKey(t, db, database.APIKey{
UserID: database.PrebuildsSystemUserID,
LoginType: database.LoginTypeNone,
})
systemUserClient := codersdk.New(ownerClient.URL)
systemUserClient.SetSessionToken(token)
// When: The system user attempts to send a custom notification
inv, root := clitest.New(t, "notifications", "custom", "Custom Title", "Custom Message")
clitest.SetupConfig(t, systemUserClient, root)
// Then: an error is expected with no notifications sent
err := inv.Run()
var sdkError *codersdk.Error
require.Error(t, err)
require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
require.Equal(t, http.StatusForbidden, sdkError.StatusCode())
require.Equal(t, "Forbidden", sdkError.Message)
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTestNotification))
require.Len(t, sent, 0)
})
t.Run("Success", func(t *testing.T) {
t.Parallel()
notifyEnq := &notificationstest.FakeEnqueuer{}
ownerClient := coderdtest.New(t, &coderdtest.Options{
DeploymentValues: coderdtest.DeploymentValues(t),
NotificationsEnqueuer: notifyEnq,
})
// Given: A member user
ownerUser := coderdtest.CreateFirstUser(t, ownerClient)
memberClient, memberUser := coderdtest.CreateAnotherUser(t, ownerClient, ownerUser.OrganizationID)
// When: The member user attempts to send a custom notification
inv, root := clitest.New(t, "notifications", "custom", "Custom Title", "Custom Message")
clitest.SetupConfig(t, memberClient, root)
// Then: we expect a custom notification to be sent to the member user
err := inv.Run()
require.NoError(t, err)
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateCustomNotification))
require.Len(t, sent, 1)
require.Equal(t, memberUser.ID, sent[0].UserID)
require.Len(t, sent[0].Labels, 2)
require.Equal(t, "Custom Title", sent[0].Labels["custom_title"])
require.Equal(t, "Custom Message", sent[0].Labels["custom_message"])
require.Equal(t, memberUser.ID.String(), sent[0].CreatedBy)
})
}
+12 -10
View File
@@ -41,24 +41,25 @@ const vscodeDesktopName = "VS Code Desktop"
func (r *RootCmd) openVSCode() *serpent.Command {
var (
generateToken bool
testOpenError bool
appearanceConfig codersdk.AppearanceConfig
generateToken bool
testOpenError bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "vscode <workspace> [<directory in workspace>]",
Short: fmt.Sprintf("Open a workspace in %s", vscodeDesktopName),
Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, 2),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context())
defer cancel()
appearanceConfig := initAppearance(ctx, client)
// Check if we're inside a workspace, and especially inside _this_
// workspace so we can perform path resolution/expansion. Generally,
@@ -299,15 +300,16 @@ func (r *RootCmd) openApp() *serpent.Command {
testOpenError bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "app <workspace> <app slug>",
Short: "Open a workspace application.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context())
defer cancel()
+5 -3
View File
@@ -37,7 +37,6 @@ func (r *RootCmd) organizations() *serpent.Command {
func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Command {
var (
stringFormat func(orgs []codersdk.Organization) (string, error)
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter(
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
typed, ok := data.([]codersdk.Organization)
@@ -77,7 +76,6 @@ func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Com
},
),
Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireRangeArgs(0, 1),
),
Options: serpent.OptionSet{
@@ -90,13 +88,17 @@ func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Com
},
},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
orgArg := "selected"
if len(inv.Args) >= 1 {
orgArg = inv.Args[0]
}
var orgs []codersdk.Organization
var err error
switch strings.ToLower(orgArg) {
case "selected":
stringFormat = func(orgs []codersdk.Organization) (string, error) {
+6 -4
View File
@@ -12,22 +12,24 @@ import (
)
func (r *RootCmd) createOrganization() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "create <organization name>",
Short: "Create a new organization.",
Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1),
),
Options: serpent.OptionSet{
cliui.SkipPromptOption(),
},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
orgName := inv.Args[0]
err := codersdk.NameValid(orgName)
err = codersdk.NameValid(orgName)
if err != nil {
return xerrors.Errorf("organization name %q is invalid: %w", orgName, err)
}
+17 -13
View File
@@ -31,16 +31,17 @@ func (r *RootCmd) organizationMembers(orgContext *OrganizationContext) *serpent.
}
func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "remove <username | user_id>",
Short: "Remove a new member to the current organization",
Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
organization, err := orgContext.Selected(inv, client)
if err != nil {
@@ -62,16 +63,17 @@ func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *ser
}
func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "add <username | user_id>",
Short: "Add a new member to the current organization",
Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
organization, err := orgContext.Selected(inv, client)
if err != nil {
@@ -93,16 +95,15 @@ func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpen
}
func (r *RootCmd) assignOrganizationRoles(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "edit-roles <username | user_id> [roles...]",
Aliases: []string{"edit-role"},
Short: "Edit organization member's roles",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
organization, err := orgContext.Selected(inv, client)
if err != nil {
@@ -141,15 +142,18 @@ func (r *RootCmd) listOrganizationMembers(orgContext *OrganizationContext) *serp
cliui.JSONFormat(),
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "list",
Short: "List all organization members",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
organization, err := orgContext.Selected(inv, client)
if err != nil {
+14 -8
View File
@@ -54,14 +54,15 @@ func (r *RootCmd) showOrganizationRoles(orgContext *OrganizationContext) *serpen
cliui.JSONFormat(),
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "show [role_names ...]",
Short: "Show role(s)",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
org, err := orgContext.Selected(inv, client)
if err != nil {
@@ -117,7 +118,6 @@ func (r *RootCmd) createOrganizationRole(orgContext *OrganizationContext) *serpe
jsonInput bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "create <role_name>",
Short: "Create a new organization custom role",
@@ -144,10 +144,13 @@ func (r *RootCmd) createOrganizationRole(orgContext *OrganizationContext) *serpe
},
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client)
if err != nil {
return err
@@ -240,7 +243,6 @@ func (r *RootCmd) updateOrganizationRole(orgContext *OrganizationContext) *serpe
jsonInput bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "update <role_name>",
Short: "Update an organization custom role",
@@ -267,9 +269,13 @@ func (r *RootCmd) updateOrganizationRole(orgContext *OrganizationContext) *serpe
},
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
org, err := orgContext.Selected(inv, client)
if err != nil {
+10 -9
View File
@@ -95,7 +95,6 @@ type organizationSetting struct {
}
func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "set",
Short: "Update specified organization setting.",
@@ -108,7 +107,6 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
Options: []serpent.Option{},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv)
@@ -124,12 +122,15 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
Options: []serpent.Option{},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
var org codersdk.Organization
var err error
if !set.DisableOrgContext {
org, err = orgContext.Selected(inv, client)
@@ -170,7 +171,6 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
}
func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "show",
Short: "Outputs specified organization setting.",
@@ -183,7 +183,6 @@ func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, sett
Options: []serpent.Option{},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv)
@@ -199,13 +198,15 @@ func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, sett
Options: []serpent.Option{},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
var org codersdk.Organization
var err error
if !set.DisableOrgContext {
org, err = orgContext.Selected(inv, client)
if err != nil {
+10 -10
View File
@@ -84,28 +84,28 @@ func (s *pingSummary) Write(w io.Writer) {
func (r *RootCmd) ping() *serpent.Command {
var (
pingNum int64
pingTimeout time.Duration
pingWait time.Duration
pingTimeLocal bool
pingTimeUTC bool
appearanceConfig codersdk.AppearanceConfig
pingNum int64
pingTimeout time.Duration
pingWait time.Duration
pingTimeLocal bool
pingTimeUTC bool
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "ping <workspace>",
Short: "Ping a workspace",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context())
defer cancel()
appearanceConfig := initAppearance(ctx, client)
notifyCtx, notifyCancel := inv.SignalNotifyContext(ctx, StopSignals...)
defer notifyCancel()
+5 -4
View File
@@ -38,9 +38,7 @@ func (r *RootCmd) portForward() *serpent.Command {
tcpForwards []string // <port>:<port>
udpForwards []string // <port>:<port>
disableAutostart bool
appearanceConfig codersdk.AppearanceConfig
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "port-forward <workspace>",
Short: `Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`,
@@ -69,12 +67,15 @@ func (r *RootCmd) portForward() *serpent.Command {
),
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context())
defer cancel()
appearanceConfig := initAppearance(ctx, client)
specs, err := parsePortForwards(tcpForwards, udpForwards)
if err != nil {
+9 -7
View File
@@ -38,7 +38,6 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
}
var (
client = new(codersdk.Client)
orgContext = NewOrganizationContext()
formatter = cliui.NewOutputFormatter(
cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "type", "template display name", "status", "queue", "tags"}),
@@ -54,10 +53,13 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
Aliases: []string{"ls"},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("current organization: %w", err)
@@ -129,19 +131,19 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
}
func (r *RootCmd) provisionerJobsCancel() *serpent.Command {
var (
client = new(codersdk.Client)
orgContext = NewOrganizationContext()
)
orgContext := NewOrganizationContext()
cmd := &serpent.Command{
Use: "cancel <job_id>",
Short: "Cancel a provisioner job",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("current organization: %w", err)
+4 -3
View File
@@ -35,7 +35,6 @@ func (r *RootCmd) provisionerList() *serpent.Command {
OrganizationName string `json:"organization_name" table:"organization"`
}
var (
client = new(codersdk.Client)
orgContext = NewOrganizationContext()
formatter = cliui.NewOutputFormatter(
cliui.TableFormat([]provisionerDaemonRow{}, []string{"created at", "last seen at", "key name", "name", "version", "status", "tags"}),
@@ -53,11 +52,13 @@ func (r *RootCmd) provisionerList() *serpent.Command {
Aliases: []string{"ls"},
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("current organization: %w", err)
+7 -5
View File
@@ -14,13 +14,15 @@ import (
func (r *RootCmd) publickey() *serpent.Command {
var reset bool
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "publickey",
Aliases: []string{"pubkey"},
Short: "Output your Coder public key used for Git operations",
Middleware: r.InitClient(client),
Use: "publickey",
Aliases: []string{"pubkey"},
Short: "Output your Coder public key used for Git operations",
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if reset {
// Confirm prompt if using --reset. We don't want to accidentally
// reset our public key.
+6 -4
View File
@@ -13,18 +13,20 @@ import (
)
func (r *RootCmd) rename() *serpent.Command {
var appearanceConfig codersdk.AppearanceConfig
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "rename <workspace> <new name>",
Short: "Rename a workspace",
Middleware: serpent.Chain(
serpent.RequireNArgs(2),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
appearanceConfig := initAppearance(inv.Context(), client)
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return xerrors.Errorf("get workspace: %w", err)
+5 -2
View File
@@ -19,17 +19,20 @@ func (r *RootCmd) restart() *serpent.Command {
bflags buildFlags
)
client := new(codersdk.Client)
cmd := &serpent.Command{
Annotations: workspaceCommand,
Use: "restart <workspace>",
Short: "Restart a workspace",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Options: serpent.OptionSet{cliui.SkipPromptOption()},
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context()
out := inv.Stdout
+244 -186
View File
@@ -59,9 +59,6 @@ var (
const (
varURL = "url"
varToken = "token"
varAgentToken = "agent-token"
varAgentTokenFile = "agent-token-file"
varAgentURL = "agent-url"
varHeader = "header"
varHeaderCommand = "header-command"
varNoOpen = "no-open"
@@ -82,6 +79,7 @@ const (
//nolint:gosec
envAgentTokenFile = "CODER_AGENT_TOKEN_FILE"
envAgentURL = "CODER_AGENT_URL"
envAgentAuth = "CODER_AGENT_AUTH"
envURL = "CODER_URL"
)
@@ -90,7 +88,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
return []*serpent.Command{
r.completion(),
r.dotfiles(),
r.externalAuth(),
externalAuth(),
r.login(),
r.logout(),
r.netcheck(),
@@ -99,6 +97,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.portForward(),
r.publickey(),
r.resetPassword(),
r.sharing(),
r.state(),
r.templates(),
r.tokens(),
@@ -129,24 +128,54 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
// Hidden
r.connectCmd(),
r.expCmd(),
r.gitssh(),
gitssh(),
r.support(),
r.vpnDaemon(),
r.vscodeSSH(),
r.workspaceAgent(),
workspaceAgent(),
}
}
// AGPLExperimental returns all AGPL experimental subcommands.
func (r *RootCmd) AGPLExperimental() []*serpent.Command {
return []*serpent.Command{
r.scaletestCmd(),
r.errorExample(),
r.mcpCommand(),
r.promptExample(),
r.rptyCommand(),
r.tasksCommand(),
r.boundary(),
}
}
// AGPL returns all AGPL commands including any non-core commands that are
// duplicated in the Enterprise CLI.
func (r *RootCmd) AGPL() []*serpent.Command {
all := append(
r.CoreSubcommands(),
r.Server( /* Do not import coderd here. */ nil),
r.Provisioners(),
ExperimentalCommand(r.AGPLExperimental()),
)
return all
}
// ExperimentalCommand creates an experimental command that is hidden and has
// the given subcommands.
func ExperimentalCommand(subcommands []*serpent.Command) *serpent.Command {
cmd := &serpent.Command{
Use: "exp",
Short: "Internal commands for testing and experimentation. These are prone to breaking changes with no notice.",
Handler: func(i *serpent.Invocation) error {
return i.Command.HelpHandler(i)
},
Hidden: true,
Children: subcommands,
}
return cmd
}
// RunWithSubcommands runs the root command with the given subcommands.
// It is abstracted to enable the Enterprise code to add commands.
func (r *RootCmd) RunWithSubcommands(subcommands []*serpent.Command) {
@@ -198,6 +227,7 @@ func (r *RootCmd) RunWithSubcommands(subcommands []*serpent.Command) {
func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, error) {
fmtLong := `Coder %s A tool for provisioning self-hosted development environments with Terraform.
`
hiddenAgentAuth := &AgentAuth{}
cmd := &serpent.Command{
Use: "coder [global-flags] <subcommand>",
Long: fmt.Sprintf(fmtLong, buildinfo.Version()) + FormatExamples(
@@ -220,7 +250,7 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err
// with a `gitaskpass` subcommand, we override the entrypoint
// to check if the command was invoked.
if gitauth.CheckCommand(i.Args, i.Environ.ToOS()) {
return r.gitAskpass().Handler(i)
return gitAskpass(hiddenAgentAuth).Handler(i)
}
return i.Command.HelpHandler(i)
},
@@ -349,9 +379,6 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err
}
})
if r.agentURL == nil {
r.agentURL = new(url.URL)
}
if r.clientURL == nil {
r.clientURL = new(url.URL)
}
@@ -381,30 +408,6 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err
Value: serpent.StringOf(&r.token),
Group: globalGroup,
},
{
Flag: varAgentToken,
Env: envAgentToken,
Description: "An agent authentication token.",
Value: serpent.StringOf(&r.agentToken),
Hidden: true,
Group: globalGroup,
},
{
Flag: varAgentTokenFile,
Env: envAgentTokenFile,
Description: "A file containing an agent authentication token.",
Value: serpent.StringOf(&r.agentTokenFile),
Hidden: true,
Group: globalGroup,
},
{
Flag: varAgentURL,
Env: envAgentURL,
Description: "URL for an agent to access your deployment.",
Value: serpent.URLOf(r.agentURL),
Hidden: true,
Group: globalGroup,
},
{
Flag: varNoVersionCheck,
Env: envNoVersionCheck,
@@ -496,136 +499,151 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err
Hidden: true,
},
}
hiddenAgentAuth.AttachOptions(cmd, true)
return cmd, nil
}
// RootCmd contains parameters and helpers useful to all commands.
type RootCmd struct {
clientURL *url.URL
token string
globalConfig string
header []string
headerCommand string
agentToken string
agentTokenFile string
agentURL *url.URL
forceTTY bool
noOpen bool
verbose bool
versionFlag bool
disableDirect bool
debugHTTP bool
clientURL *url.URL
token string
globalConfig string
header []string
headerCommand string
forceTTY bool
noOpen bool
verbose bool
versionFlag bool
disableDirect bool
debugHTTP bool
disableNetworkTelemetry bool
noVersionCheck bool
noFeatureWarning bool
}
// InitClient authenticates the client with files from disk
// and injects header middlewares for telemetry, authentication,
// InitClient creates and configures a new client with authentication, telemetry,
// and version checks.
func (r *RootCmd) InitClient(client *codersdk.Client) serpent.MiddlewareFunc {
return func(next serpent.HandlerFunc) serpent.HandlerFunc {
return func(inv *serpent.Invocation) error {
conf := r.createConfig()
var err error
// Read the client URL stored on disk.
if r.clientURL == nil || r.clientURL.String() == "" {
rawURL, err := conf.URL().Read()
// If the configuration files are absent, the user is logged out
if os.IsNotExist(err) {
binPath, err := os.Executable()
if err != nil {
binPath = "coder"
}
return xerrors.Errorf(notLoggedInMessage, binPath)
}
if err != nil {
return err
}
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return err
}
}
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return err
}
}
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
func (r *RootCmd) InitClient(inv *serpent.Invocation) (*codersdk.Client, error) {
conf := r.createConfig()
var err error
// Read the client URL stored on disk.
if r.clientURL == nil || r.clientURL.String() == "" {
rawURL, err := conf.URL().Read()
// If the configuration files are absent, the user is logged out
if os.IsNotExist(err) {
binPath, err := os.Executable()
if err != nil {
return err
binPath = "coder"
}
client.SetSessionToken(r.token)
return nil, xerrors.Errorf(notLoggedInMessage, binPath)
}
if err != nil {
return nil, err
}
if r.debugHTTP {
client.PlainLogger = os.Stderr
client.SetLogBodies(true)
}
client.DisableDirectConnections = r.disableDirect
return next(inv)
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return nil, err
}
}
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return nil, err
}
}
// Configure HTTP client with transport wrappers
httpClient, err := r.createHTTPClient(inv.Context(), r.clientURL, inv)
if err != nil {
return nil, err
}
clientOpts := []codersdk.ClientOption{
codersdk.WithSessionToken(r.token),
codersdk.WithHTTPClient(httpClient),
}
if r.disableDirect {
clientOpts = append(clientOpts, codersdk.WithDisableDirectConnections())
}
if r.debugHTTP {
clientOpts = append(clientOpts,
codersdk.WithPlainLogger(os.Stderr),
codersdk.WithLogBodies(),
)
}
return codersdk.New(r.clientURL, clientOpts...), nil
}
// TryInitClient is similar to InitClient but doesn't error when credentials are missing.
// This allows commands to run without requiring authentication, but still use auth if available.
func (r *RootCmd) TryInitClient(client *codersdk.Client) serpent.MiddlewareFunc {
return func(next serpent.HandlerFunc) serpent.HandlerFunc {
return func(inv *serpent.Invocation) error {
conf := r.createConfig()
var err error
// Read the client URL stored on disk.
if r.clientURL == nil || r.clientURL.String() == "" {
rawURL, err := conf.URL().Read()
// If the configuration files are absent, just continue without URL
if err != nil {
// Continue with a nil or empty URL
if !os.IsNotExist(err) {
return err
}
} else {
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return err
}
}
func (r *RootCmd) TryInitClient(inv *serpent.Invocation) (*codersdk.Client, error) {
conf := r.createConfig()
var err error
// Read the client URL stored on disk.
if r.clientURL == nil || r.clientURL.String() == "" {
rawURL, err := conf.URL().Read()
// If the configuration files are absent, just continue without URL
if err != nil {
// Continue with a nil or empty URL
if !os.IsNotExist(err) {
return nil, err
}
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return err
}
} else {
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return nil, err
}
// Only configure the client if we have a URL
if r.clientURL != nil && r.clientURL.String() != "" {
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
if err != nil {
return err
}
client.SetSessionToken(r.token)
if r.debugHTTP {
client.PlainLogger = os.Stderr
client.SetLogBodies(true)
}
client.DisableDirectConnections = r.disableDirect
}
return next(inv)
}
}
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return nil, err
}
}
// Only configure the client if we have a URL
if r.clientURL != nil && r.clientURL.String() != "" {
// Configure HTTP client with transport wrappers
httpClient, err := r.createHTTPClient(inv.Context(), r.clientURL, inv)
if err != nil {
return nil, err
}
clientOpts := []codersdk.ClientOption{
codersdk.WithSessionToken(r.token),
codersdk.WithHTTPClient(httpClient),
}
if r.disableDirect {
clientOpts = append(clientOpts, codersdk.WithDisableDirectConnections())
}
if r.debugHTTP {
clientOpts = append(clientOpts,
codersdk.WithPlainLogger(os.Stderr),
codersdk.WithLogBodies(),
)
}
return codersdk.New(r.clientURL, clientOpts...), nil
}
// Return a minimal client if no URL is available
return &codersdk.Client{}, nil
}
// HeaderTransport creates a new transport that executes `--header-command`
@@ -634,7 +652,7 @@ func (r *RootCmd) HeaderTransport(ctx context.Context, serverURL *url.URL) (*cod
return headerTransport(ctx, serverURL, r.header, r.headerCommand)
}
func (r *RootCmd) configureClient(ctx context.Context, client *codersdk.Client, serverURL *url.URL, inv *serpent.Invocation) error {
func (r *RootCmd) createHTTPClient(ctx context.Context, serverURL *url.URL, inv *serpent.Invocation) (*http.Client, error) {
transport := http.DefaultTransport
transport = wrapTransportWithTelemetryHeader(transport, inv)
if !r.noVersionCheck {
@@ -650,57 +668,101 @@ func (r *RootCmd) configureClient(ctx context.Context, client *codersdk.Client,
}
headerTransport, err := r.HeaderTransport(ctx, serverURL)
if err != nil {
return xerrors.Errorf("create header transport: %w", err)
return nil, xerrors.Errorf("create header transport: %w", err)
}
// The header transport has to come last.
// codersdk checks for the header transport to get headers
// to clone on the DERP client.
headerTransport.Transport = transport
client.HTTPClient = &http.Client{
return &http.Client{
Transport: headerTransport,
}
client.URL = serverURL
return nil
}, nil
}
func (r *RootCmd) createUnauthenticatedClient(ctx context.Context, serverURL *url.URL, inv *serpent.Invocation) (*codersdk.Client, error) {
var client codersdk.Client
err := r.configureClient(ctx, &client, serverURL, inv)
return &client, err
httpClient, err := r.createHTTPClient(ctx, serverURL, inv)
if err != nil {
return nil, err
}
client := codersdk.New(serverURL, codersdk.WithHTTPClient(httpClient))
return client, nil
}
// createAgentClient returns a new client from the command context. It works
type AgentAuth struct {
// Agent Client config
agentToken string
agentTokenFile string
agentURL url.URL
agentAuth string
}
func (a *AgentAuth) AttachOptions(cmd *serpent.Command, hidden bool) {
cmd.Options = append(cmd.Options, serpent.Option{
Name: "Agent Token",
Description: "An agent authentication token.",
Flag: "agent-token",
Env: envAgentToken,
Value: serpent.StringOf(&a.agentToken),
Hidden: hidden,
}, serpent.Option{
Name: "Agent Token File",
Description: "A file containing an agent authentication token.",
Flag: "agent-token-file",
Env: envAgentTokenFile,
Value: serpent.StringOf(&a.agentTokenFile),
Hidden: hidden,
}, serpent.Option{
Name: "Agent URL",
Description: "URL for an agent to access your deployment.",
Flag: "agent-url",
Env: envAgentURL,
Value: serpent.URLOf(&a.agentURL),
Hidden: hidden,
}, serpent.Option{
Name: "Agent Auth",
Description: "Specify the authentication type to use for the agent.",
Flag: "auth",
Env: envAgentAuth,
Default: "token",
Value: serpent.StringOf(&a.agentAuth),
Hidden: hidden,
})
}
// CreateClient returns a new agent client from the command context. It works
// just like InitClient, but uses the agent token and URL instead.
func (r *RootCmd) createAgentClient() (*agentsdk.Client, error) {
agentURL := r.agentURL
if agentURL == nil || agentURL.String() == "" {
func (a *AgentAuth) CreateClient() (*agentsdk.Client, error) {
agentURL := a.agentURL
if agentURL.String() == "" {
return nil, xerrors.Errorf("%s must be set", envAgentURL)
}
token := r.agentToken
if token == "" {
if r.agentTokenFile == "" {
return nil, xerrors.Errorf("Either %s or %s must be set", envAgentToken, envAgentTokenFile)
}
tokenBytes, err := os.ReadFile(r.agentTokenFile)
if err != nil {
return nil, xerrors.Errorf("read token file %q: %w", r.agentTokenFile, err)
}
token = strings.TrimSpace(string(tokenBytes))
}
client := agentsdk.New(agentURL)
client.SetSessionToken(token)
return client, nil
}
// tryCreateAgentClient returns a new client from the command context. It works
// just like tryCreateAgentClient, but does not error.
func (r *RootCmd) tryCreateAgentClient() (*agentsdk.Client, error) {
// TODO: Why does this not actually return any errors despite the function
// signature? Could we just use createAgentClient instead, or is it expected
// that we return a client in some cases even without a valid URL or token?
client := agentsdk.New(r.agentURL)
client.SetSessionToken(r.agentToken)
return client, nil
switch a.agentAuth {
case "token":
token := a.agentToken
if token == "" {
if a.agentTokenFile == "" {
return nil, xerrors.Errorf("Either %s or %s must be set", envAgentToken, envAgentTokenFile)
}
tokenBytes, err := os.ReadFile(a.agentTokenFile)
if err != nil {
return nil, xerrors.Errorf("read token file %q: %w", a.agentTokenFile, err)
}
token = strings.TrimSpace(string(tokenBytes))
}
if token == "" {
return nil, xerrors.Errorf("CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set for token auth")
}
return agentsdk.New(&a.agentURL, agentsdk.WithFixedToken(token)), nil
case "google-instance-identity":
return agentsdk.New(&a.agentURL, agentsdk.WithGoogleInstanceIdentity("", nil)), nil
case "aws-instance-identity":
return agentsdk.New(&a.agentURL, agentsdk.WithAWSInstanceIdentity()), nil
case "azure-instance-identity":
return agentsdk.New(&a.agentURL, agentsdk.WithAzureInstanceIdentity()), nil
default:
return nil, xerrors.Errorf("unknown agent auth type: %s", a.agentAuth)
}
}
type OrganizationContext struct {
@@ -799,17 +861,13 @@ func namedWorkspace(ctx context.Context, client *codersdk.Client, identifier str
return client.WorkspaceByOwnerAndName(ctx, owner, name, codersdk.WorkspaceOptions{})
}
func initAppearance(client *codersdk.Client, outConfig *codersdk.AppearanceConfig) serpent.MiddlewareFunc {
return func(next serpent.HandlerFunc) serpent.HandlerFunc {
return func(inv *serpent.Invocation) error {
cfg, _ := client.Appearance(inv.Context())
if cfg.DocsURL == "" {
cfg.DocsURL = codersdk.DefaultDocsURL()
}
*outConfig = cfg
return next(inv)
}
func initAppearance(ctx context.Context, client *codersdk.Client) codersdk.AppearanceConfig {
// best effort
cfg, _ := client.Appearance(ctx)
if cfg.DocsURL == "" {
cfg.DocsURL = codersdk.DefaultDocsURL()
}
return cfg
}
// createConfig consumes the global configuration flag to produce a config root.
+88 -8
View File
@@ -10,20 +10,20 @@ import (
"sync/atomic"
"testing"
"github.com/coder/serpent"
"github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/cli"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/pty/ptytest"
"github.com/coder/coder/v2/testutil"
"github.com/coder/serpent"
)
//nolint:tparallel,paralleltest
@@ -275,3 +275,83 @@ func TestHandlersOK(t *testing.T) {
clitest.HandlersOK(t, cmd)
}
func TestCreateAgentClient_Token(t *testing.T) {
t.Parallel()
client := createAgentWithFlags(t,
"--agent-token", "fake-token",
"--agent-url", "http://coder.fake")
require.Equal(t, "fake-token", client.GetSessionToken())
}
func TestCreateAgentClient_Google(t *testing.T) {
t.Parallel()
client := createAgentWithFlags(t,
"--auth", "google-instance-identity",
"--agent-url", "http://coder.fake")
provider, ok := client.RefreshableSessionTokenProvider.(*agentsdk.InstanceIdentitySessionTokenProvider)
require.True(t, ok)
require.NotNil(t, provider.TokenExchanger)
require.IsType(t, &agentsdk.GoogleSessionTokenExchanger{}, provider.TokenExchanger)
}
func TestCreateAgentClient_AWS(t *testing.T) {
t.Parallel()
client := createAgentWithFlags(t,
"--auth", "aws-instance-identity",
"--agent-url", "http://coder.fake")
provider, ok := client.RefreshableSessionTokenProvider.(*agentsdk.InstanceIdentitySessionTokenProvider)
require.True(t, ok)
require.NotNil(t, provider.TokenExchanger)
require.IsType(t, &agentsdk.AWSSessionTokenExchanger{}, provider.TokenExchanger)
}
func TestCreateAgentClient_Azure(t *testing.T) {
t.Parallel()
client := createAgentWithFlags(t,
"--auth", "azure-instance-identity",
"--agent-url", "http://coder.fake")
provider, ok := client.RefreshableSessionTokenProvider.(*agentsdk.InstanceIdentitySessionTokenProvider)
require.True(t, ok)
require.NotNil(t, provider.TokenExchanger)
require.IsType(t, &agentsdk.AzureSessionTokenExchanger{}, provider.TokenExchanger)
}
func createAgentWithFlags(t *testing.T, flags ...string) *agentsdk.Client {
t.Helper()
r := &cli.RootCmd{}
var client *agentsdk.Client
subCmd := agentClientCommand(&client)
cmd, err := r.Command([]*serpent.Command{subCmd})
require.NoError(t, err)
inv, _ := clitest.NewWithCommand(t, cmd,
append([]string{"agent-client"}, flags...)...)
err = inv.Run()
require.NoError(t, err)
require.NotNil(t, client)
return client
}
// agentClientCommand creates a subcommand that creates an agent client and stores it in the provided clientRef. Used to
// test the properties of the client with various root command flags.
func agentClientCommand(clientRef **agentsdk.Client) *serpent.Command {
agentAuth := &cli.AgentAuth{}
cmd := &serpent.Command{
Use: "agent-client",
Short: `Creates and agent client for testing.`,
Handler: func(inv *serpent.Invocation) error {
client, err := agentAuth.CreateClient()
if err != nil {
return xerrors.Errorf("create agent client: %w", err)
}
*clientRef = client
return nil
},
}
agentAuth.AttachOptions(cmd, false)
return cmd
}
+16 -8
View File
@@ -90,16 +90,18 @@ func (r *RootCmd) scheduleShow() *serpent.Command {
cliui.JSONFormat(),
)
)
client := new(codersdk.Client)
showCmd := &serpent.Command{
Use: "show <workspace | --search <query> | --all>",
Short: "Show workspace schedules",
Long: scheduleShowDescriptionLong,
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
// To preserve existing behavior, if an argument is passed we will
// only show the schedule for that workspace.
// This will clobber the search query if one is passed.
@@ -137,7 +139,6 @@ func (r *RootCmd) scheduleShow() *serpent.Command {
}
func (r *RootCmd) scheduleStart() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "start <workspace-name> { <start-time> [day-of-week] [location] | manual }",
Long: scheduleStartDescriptionLong + "\n" + FormatExamples(
@@ -149,9 +150,12 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
Short: "Edit workspace start schedule",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(2, 4),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return err
@@ -193,7 +197,6 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
}
func (r *RootCmd) scheduleStop() *serpent.Command {
client := new(codersdk.Client)
return &serpent.Command{
Use: "stop <workspace-name> { <duration> | manual }",
Long: scheduleStopDescriptionLong + "\n" + FormatExamples(
@@ -204,9 +207,12 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
Short: "Edit workspace stop schedule",
Middleware: serpent.Chain(
serpent.RequireNArgs(2),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil {
return err
@@ -244,7 +250,6 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
}
func (r *RootCmd) scheduleExtend() *serpent.Command {
client := new(codersdk.Client)
extendCmd := &serpent.Command{
Use: "extend <workspace-name> <duration from now>",
Aliases: []string{"override-stop"},
@@ -256,9 +261,12 @@ func (r *RootCmd) scheduleExtend() *serpent.Command {
),
Middleware: serpent.Chain(
serpent.RequireNArgs(2),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
extendDuration, err := parseDuration(inv.Args[1])
if err != nil {
return err

Some files were not shown because too many files have changed in this diff Show More