Compare commits

..

29 Commits

Author SHA1 Message Date
dependabot[bot]
9cfd8f7041 Bump proxy-agent from 6.5.0 to 8.0.2
Bumps [proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/proxy-agent) from 6.5.0 to 8.0.2.
- [Release notes](https://github.com/TooTallNate/proxy-agents/releases)
- [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/proxy-agent/CHANGELOG.md)
- [Commits](https://github.com/TooTallNate/proxy-agents/commits/proxy-agent@8.0.2/packages/proxy-agent)

---
updated-dependencies:
- dependency-name: proxy-agent
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-16 19:55:02 +00:00
dependabot[bot]
8009d3c360 Bump typescript-eslint from 8.56.1 to 8.61.1 (#7857) 2026-06-16 11:53:49 -08:00
dependabot[bot]
4ae1fc202e Bump doctoc from 2.3.0 to 2.5.0 (#7858) 2026-06-16 11:52:36 -08:00
dependabot[bot]
a59c3ab990 Bump github/codeql-action from 4.35.4 to 4.36.2 (#7860) 2026-06-16 11:51:47 -08:00
dependabot[bot]
a7f457046f Bump actions/checkout from 6.0.2 to 6.0.3 (#7852) 2026-06-16 11:51:19 -08:00
Asher
25e847dc94 Update Playwright 2026-06-16 11:33:33 -08:00
Asher
e1c839fb1d Slightly improve heart tests
- Get rid of the global isActive mock; in particular the way it shadows
  local ones seemed sketchy.
- No need for requireActual from my testing.
- Reword the comment for why we need setImmediate.
- Add the setImmediate to another test that seemed to only pass because
  of an await on the timer call which is not actually a promise but had
  the side effect of yielding.
- Always set fake/real timers in the before/after handlers and never in
  individual tests.
2026-06-16 11:27:53 -08:00
YONGJAE LEE (이용재)
7dfd68589a fix: re-enable skipped Heart tests and make non-asserting checks assert (#7845) 2026-06-16 09:58:42 -08:00
Asher
72086edbdb Remove Windows npm installation recommendation
We need to properly support Windows before we can recommend any method
of installation, including npm.
2026-06-16 09:45:30 -08:00
Asher
7617ab2b92 Update AUR commit message to match code-server's 2026-06-16 09:45:30 -08:00
Asher
d31d0347cf Remove Dependabot commit prefix
The changelog is manually curated so the prefixes are noise.
2026-06-16 09:45:30 -08:00
dependabot[bot]
5dca609c2a chore: bump i18next from 25.8.13 to 26.3.1 (#7828) 2026-06-16 09:38:09 -08:00
dependabot[bot]
87ac56bf8c chore: bump softprops/action-gh-release from 1 to 3 (#7774) 2026-06-16 09:37:41 -08:00
dependabot[bot]
a056406eff chore: bump form-data in /test (#7848) 2026-06-16 17:35:17 +00:00
dependabot[bot]
24811a6269 chore: bump docker/setup-buildx-action from 4.0.0 to 4.1.0 (#7829) 2026-06-16 17:16:41 +00:00
dependabot[bot]
53ed1e30d2 chore: bump docker/setup-qemu-action from 4.0.0 to 4.1.0 (#7830) 2026-06-16 09:04:01 -08:00
dependabot[bot]
7393d50b09 chore: bump awalsh128/cache-apt-pkgs-action from 1.5.3 to 1.6.0 (#7832) 2026-06-16 09:03:28 -08:00
dependabot[bot]
f7663cc34c chore: bump codecov/codecov-action from 5.5.4 to 6.0.1 (#7833) 2026-06-16 09:02:59 -08:00
dependabot[bot]
71ba0191f2 chore: bump globals from 16.5.0 to 17.6.0 (#7831) 2026-06-16 08:59:54 -08:00
cdrci
19be49ebe4 Update Helm chart and changelog with 4.124.2 (#7851)
Co-authored-by: cdrci <opensource@coder.com>
2026-06-16 07:57:28 -08:00
cdrci
9fe7eb79d5 Update Code to 1.124.2 (#7846) 2026-06-15 22:28:49 -08:00
Asher
1ccd4f04d2 Update brace-expansion, js-yaml, and ws 2026-06-15 16:42:17 -08:00
Asher
364cf99338 Strip token from cookies before proxying
Since this functionality requires information placed onto the request by
code-server (req.args) and Express (req.cookies), move the standalone
tests into the integration tests as the proxy can no longer run
correctly on its own without that context.

We could strip the header elsewhere or refactor in some way (pass in a
callback function for the stripping or something) but this seems like
the simplest and safest place at the moment to ensure we catch all uses
of the proxy.

In any case, I think it does lend more confidence to know we are testing
the proxy the way it will be used in practice.  The downside is some
additional complexity when setting up tests, but at the moment I do not
think that exchange is overly burdensome.
2026-06-15 16:42:17 -08:00
ka-ishimoto
92a7dce46f Fix false positive CVE alerts by setting package name to code-oss-dev (#7839)
The VS Code build process sets the bundled lib/vscode/package.json name to "code-server" (from product.json nameShort), causing vulnerability scanners to misidentify it and flag non-applicable CVEs. Override the name to "code-oss-dev" in build-release.sh after merging package.json.
Fixes #7071

Signed-off-by: ka-ishimoto <ka-ishimoto@kddi.com>
2026-06-10 11:59:45 -08:00
cdrci
d0d53d924e Update Helm chart and changelog with 4.123.0 (#7838) 2026-06-04 10:50:04 -08:00
Asher
77d880d0c3 Drop armhf builds 2026-06-03 12:06:07 -08:00
Asher
559d73a636 Update Code to 1.123.0 (#7837) 2026-06-03 11:33:49 -08:00
Asher
6fd40c04c7 Fix Helm chart version bump script
Instead of relying on semver which is not installed, just parse it with
bash.
2026-06-02 11:29:19 -08:00
cdrci
63c071959f Update Helm chart and changelog with 4.122.1 (#7835) 2026-06-02 10:42:18 -08:00
36 changed files with 615 additions and 538 deletions

View File

@@ -16,8 +16,6 @@ updates:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
labels: []
ignore:
# Ignore patch updates for all dependencies

View File

@@ -25,7 +25,7 @@ jobs:
docs: ${{ steps.filter.outputs.docs }}
helm: ${{ steps.filter.outputs.helm }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
@@ -55,7 +55,7 @@ jobs:
name: Run prettier check
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -72,7 +72,7 @@ jobs:
needs: changes
if: needs.changes.outputs.docs == 'true'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -89,7 +89,7 @@ jobs:
needs: changes
if: needs.changes.outputs.helm == 'true'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -103,7 +103,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -121,7 +121,7 @@ jobs:
if: needs.changes.outputs.ci == 'true'
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Check workflow files
run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.9
@@ -134,7 +134,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -144,7 +144,7 @@ jobs:
test/package-lock.json
- run: SKIP_SUBMODULE_DEPS=1 npm ci
- run: npm run test:unit
- uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5
- uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
if: success()
with:
token: ${{ secrets.CODECOV_TOKEN }}
@@ -163,12 +163,12 @@ jobs:
steps:
- run: sudo apt update && sudo apt install -y libkrb5-dev
- uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # latest
- uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # latest
with:
packages: quilt
version: 1.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
submodules: true
- run: quilt push -a
@@ -219,7 +219,7 @@ jobs:
if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' || needs.changes.outputs.ci == 'true'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -269,7 +269,7 @@ jobs:
mkdir -p ~/.cache/caddy
tar -xzf caddy_2.5.2_linux_amd64.tar.gz --directory ~/.cache/caddy
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version

View File

@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Install code-server
run: ./install.sh
@@ -44,7 +44,7 @@ jobs:
container: "alpine:3.17"
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Install curl
run: apk add curl
@@ -67,7 +67,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Install code-server
run: ./install.sh

View File

@@ -33,7 +33,7 @@ jobs:
run: |
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: .node-version
@@ -64,7 +64,7 @@ jobs:
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Checkout code-server-aur repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: "cdrci/code-server-aur"
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
@@ -93,7 +93,7 @@ jobs:
run: |
git checkout -b update-version-${{ env.VERSION }}
git add .
git commit -m "chore: updating version to ${{ env.VERSION }}"
git commit -m "Update to ${{ env.VERSION }}"
git push -u origin $(git branch --show)
gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ env.VERSION }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR
@@ -108,9 +108,9 @@ jobs:
run: |
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
@@ -149,7 +149,7 @@ jobs:
run: |
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- run: ./ci/build/update-repo.sh

View File

@@ -39,9 +39,6 @@ jobs:
- npm_arch: arm64
vscode_arch: arm64
package_arch: arm64
- npm_arch: arm
vscode_arch: armhf
package_arch: armv7l
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -62,7 +59,7 @@ jobs:
steps:
- run: sudo apt update && sudo apt install -y libkrb5-dev
- uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # latest
- uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # latest
with:
packages: quilt
version: 1.0
@@ -79,7 +76,7 @@ jobs:
version=4${version:1}
echo "VERSION=$version" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
submodules: true
- run: quilt push -a
@@ -113,7 +110,7 @@ jobs:
- run: |
sed "/^## Unreleased/,/^## / ! d" CHANGELOG.md | head -n -2 | tail -n +3 > .cache/release-notes
if: ${{ matrix.vscode_arch == 'x64' }}
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
- uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
if: ${{ matrix.vscode_arch == 'x64' }}
with:
draft: true
@@ -126,7 +123,7 @@ jobs:
# Platform-specific release.
- run: KEEP_MODULES=1 npm run release
- run: npm run package
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
- uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
draft: true
discussion_category_name: "📣 Announcements"
@@ -173,7 +170,7 @@ jobs:
version=4${version:1}
echo "VERSION=$version" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
submodules: true
- run: quilt push -a
@@ -192,7 +189,7 @@ jobs:
- run: npm run test:native
- run: npm run package
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
- uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
draft: true
discussion_category_name: "📣 Announcements"

View File

@@ -41,7 +41,7 @@ jobs:
container: "alpine:3.17"
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Install test utilities
run: apk add bats checkbashisms
@@ -58,7 +58,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Install lint utilities
run: sudo apt install shellcheck

View File

@@ -25,7 +25,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
@@ -46,7 +46,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
@@ -62,7 +62,7 @@ jobs:
severity: "HIGH,CRITICAL"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
sarif_file: "trivy-repo-results.sarif"
@@ -76,17 +76,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
config-file: ./.github/codeql-config.yml
languages: javascript
- name: Autobuild
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4

View File

@@ -46,7 +46,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Run Trivy vulnerability scanner in image mode
uses: aquasecurity/trivy-action@314ff8b43182423b84c50b1670b0e10f858f2d98 # latest
@@ -58,6 +58,6 @@ jobs:
severity: "HIGH,CRITICAL"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
sarif_file: "trivy-image-results.sarif"

View File

@@ -28,7 +28,7 @@ jobs:
run: |
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
submodules: true
@@ -47,7 +47,7 @@ jobs:
echo done=false >> $GITHUB_OUTPUT
fi
- uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # latest
- uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # latest
if: steps.check.outputs.done == 'false'
with:
packages: quilt

View File

@@ -1 +1 @@
22.22.1
24.15.0

View File

@@ -22,6 +22,34 @@ Code v99.99.999
## Unreleased
## [4.124.2](https://github.com/coder/code-server/releases/tag/v4.124.2) - 2026-06-16
Code v1.124.2
### Security
- Strip code-server's session token from the cookie before proxying to a local
port. Previously, when you used built-in password authentication, the cookie
would be sent to the local proxied port, which meant if the service was
malicious and not already running as your code-server user it could use the
cookie to log into code-server and execute commands as your code-server user.
### Changed
- Update to Code 1.124.2
## [4.123.0](https://github.com/coder/code-server/releases/tag/v4.123.0) - 2026-06-03
Code v1.123.0
### Changed
- Update to Code 1.123.0
- Microsoft dropped support for armhf remotes so there will no longer be any
builds for armhf.
## [4.122.1](https://github.com/coder/code-server/releases/tag/v4.122.1) - 2026-06-02
Code v1.122.1
### Changed

View File

@@ -128,7 +128,9 @@ bundle_vscode() {
# Merge the package.json for the web/remote server so we can include
# dependencies, since we want to ship this via NPM.
jq --slurp '.[0] * .[1]' \
# Also override the name to prevent vulnerability scanners from
# misidentifying this package as VS Code (see #7071).
jq --slurp '.[0] * .[1] | .name = "code-oss-dev"' \
"$VSCODE_SRC_PATH/remote/package.json" \
"$VSCODE_OUT_PATH/package.json" > "$VSCODE_OUT_PATH/package.json.merged"
mv "$VSCODE_OUT_PATH/package.json.merged" "$VSCODE_OUT_PATH/package.json"

View File

@@ -2,16 +2,65 @@
set -Eeuo pipefail
function update_helm() {
local current
current=$(yq .version ci/helm-chart/Chart.yaml)
local next
next=$(semver "$current" -i minor)
echo "Bumping version from $current to $next..."
sed -i.bak "s/^version: $current\$/version: $next/" ci/helm-chart/Chart.yaml
# Given versions $1 and $2 figure out the first component that is different
# (major, minor, patch).
function find_version_diff() {
# shellcheck disable=SC2206
local a=( ${1//./ } )
# shellcheck disable=SC2206
local b=( ${2//./ } )
echo "Setting app version and image to $version..."
if [[ ${a[0]} != "${b[0]}" ]] ; then
echo major
elif [[ ${a[1]} != "${b[1]}" ]] ; then
echo minor
else
echo patch
fi
}
# Bump $1 by the bump type (major, minor, patch) in $2.
function bump_version() {
# shellcheck disable=SC2206
local a=( ${1//./ } )
case $2 in
major)
((a[0]++))
a[1]=0
a[2]=0
;;
minor)
((a[1]++))
a[2]=0
;;
*)
((a[2]++))
;;
esac
echo "${a[0]}.${a[1]}.${a[2]}"
}
function update_helm() {
local chart_version
chart_version=$(yq .version ci/helm-chart/Chart.yaml)
local app_version
app_version=$(yq .appVersion ci/helm-chart/Chart.yaml)
local image_version
image_version=$(yq .image.tag ci/helm-chart/values.yaml)
local bump_type
bump_type=$(find_version_diff "$app_version" "$version")
local chart_version_bump
chart_version_bump=$(bump_version "$chart_version" "$bump_type")
# Use sed to replace because yq will reformat.
echo "Bumping version from $chart_version to $chart_version_bump..."
sed -i.bak "s/^version: $chart_version\$/version: $chart_version_bump/" ci/helm-chart/Chart.yaml
echo "Bumping app version from $app_version to $version..."
sed -i.bak "s/^appVersion: .\+\$/appVersion: $version/" ci/helm-chart/Chart.yaml
echo "Bumping image version from $image_version to $version..."
sed -i.bak "s/^ tag: .\+\$/ tag: '$version'/" ci/helm-chart/values.yaml
}

View File

@@ -15,9 +15,9 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 3.37.0
version: 3.39.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 4.122.0
appVersion: 4.124.2

View File

@@ -6,7 +6,7 @@ replicaCount: 1
image:
repository: codercom/code-server
tag: '4.122.0'
tag: '4.124.2'
pullPolicy: Always
# Specifies one or more secrets to be used when pulling images from a

View File

@@ -101,9 +101,8 @@ _exact_ same commands presented in the rest of this document.
We recommend installing with `npm` when:
1. You aren't using a machine with `amd64` or `arm64`.
2. You are installing code-server on Windows.
3. You're on Linux with `glibc` < v2.28 or `glibcxx` < v3.4.21.
4. You're running Alpine Linux or are using a non-glibc libc. See
2. You're on Linux with `glibc` < v2.28 or `glibcxx` < v3.4.21.
3. You're running Alpine Linux or are using a non-glibc libc. See
[#1430](https://github.com/coder/code-server/issues/1430#issuecomment-629883198)
for more information.
@@ -296,8 +295,7 @@ You can install code-server using the [Helm package manager](https://coder.com/d
## Windows
We currently [do not publish Windows
releases](https://github.com/coder/code-server/issues/1397). We recommend
installing code-server onto Windows with [`npm`](#npm).
releases](https://github.com/coder/code-server/issues/1397).
## Raspberry Pi

585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -59,7 +59,7 @@
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-prettier": "^5.0.0",
"globals": "^16.1.0",
"globals": "^17.6.0",
"prettier": "3.8.3",
"prettier-plugin-sh": "^0.18.0",
"ts-node": "^10.9.1",
@@ -70,16 +70,17 @@
"@coder/logger": "^3.0.1",
"argon2": "^0.44.0",
"compression": "^1.7.4",
"cookie": "^1.1.1",
"cookie-parser": "^1.4.6",
"env-paths": "^2.2.1",
"express": "^5.0.1",
"http-proxy": "^1.18.1",
"httpolyglot": "^0.1.2",
"i18next": "^25.8.3",
"i18next": "^26.3.1",
"js-yaml": "^4.1.0",
"limiter": "^2.1.0",
"pem": "^1.14.8",
"proxy-agent": "^6.3.1",
"proxy-agent": "^8.0.2",
"qs": "^6.15.0",
"rotating-file-stream": "^3.1.1",
"safe-compare": "^1.1.4",

View File

@@ -263,7 +263,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
}
private startListening(): void {
@@ -584,17 +585,6 @@ class WorkspaceProvider implements IWork
@@ -590,17 +591,6 @@ class WorkspaceProvider implements IWork
}
}
@@ -281,7 +281,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
(function () {
// Find config by checking for DOM
@@ -604,8 +594,8 @@ function readCookie(name: string): strin
@@ -610,8 +600,8 @@ function readCookie(name: string): strin
if (!configElement || !configElementAttribute) {
throw new Error('Missing web configuration element');
}

View File

@@ -7,7 +7,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
@@ -342,6 +342,10 @@ export class Extension implements IExten
@@ -345,6 +345,10 @@ export class Extension implements IExten
if (this.type === ExtensionType.System && this.productService.quality === 'stable' && !this.productService.builtInExtensionsEnabledWithAutoUpdates?.some(id => id.toLowerCase() === this.identifier.id.toLowerCase())) {
return false;
}

View File

@@ -104,7 +104,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js';
import { create } from '../../../workbench/workbench.web.main.internal.js';
@@ -606,6 +607,39 @@ class WorkspaceProvider implements IWork
@@ -612,6 +613,39 @@ class WorkspaceProvider implements IWork
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),

View File

@@ -6,7 +6,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.ts
===================================================================
--- code-server.orig/lib/vscode/build/gulpfile.reh.ts
+++ code-server/lib/vscode/build/gulpfile.reh.ts
@@ -255,10 +255,15 @@ function packageTask(type: string, platf
@@ -296,10 +296,15 @@ function packageTask(type: string, platf
const destination = path.join(BUILD_ROOT, destinationFolderName);
return () => {

View File

@@ -70,8 +70,8 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy"
- content="default-src 'none'; script-src 'sha256-q+WTr+fBXpLLE3++yWNaxT6BTWQtsKscoeIlynBRk4E=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-m1DlJtsIJd46QuWYNcsaYIG1xI+9FyjKQu+cfp+zq5Q=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
- content="default-src 'none'; script-src 'sha256-nXjtuhBilO++r8hfxl5VjEScSmdm07wDAk6jw228DgM=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-A6/szVNdTzyi4hDa+9OLbzS8tSd2iUV4CqimLNWex2Y=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming -->
<meta name="viewport"

View File

@@ -54,7 +54,6 @@ init({
lowerCaseLng: true,
debug: process.env.NODE_ENV === "development",
resources: defaultResources,
showSupportNotice: false,
})
export default i18next

View File

@@ -1,5 +1,7 @@
import * as cookie from "cookie"
import type { Request } from "express"
import proxyServer from "http-proxy"
import { HttpCode } from "../common/http"
import { getCookieSessionName, HttpCode } from "../common/http"
export const proxy = proxyServer.createProxyServer({})
@@ -18,6 +20,19 @@ proxy.on("error", (error, _, res) => {
}
})
// Strip the code-server cookie if it exists to avoid transmitting the cookie
// to potentially malicious local ports.
proxy.on("proxyReq", (preq, req) => {
const cookieSessionName = getCookieSessionName((req as Request).args["cookie-suffix"])
preq.setHeader(
"Cookie",
cookie.stringifyCookie({
...(req as Request).cookies,
[cookieSessionName]: undefined,
}),
)
})
// Intercept the response to rewrite absolute redirects against the base path.
// Is disabled when the request has no base path which means /absproxy is in use.
proxy.on("proxyRes", (res, req) => {

View File

@@ -32,7 +32,7 @@ describe("code-server", ["--disable-workspace-trust"], {}, () => {
test("should show the Integrated Terminal", async ({ codeServerPage }) => {
await codeServerPage.focusTerminal()
expect(await codeServerPage.page.isVisible("#terminal")).toBe(true)
await expect(codeServerPage.page.locator("#terminal")).toBeVisible()
})
test("should open a file", async ({ codeServerPage }) => {

View File

@@ -18,7 +18,7 @@ describe("Downloads (enabled)", ["--disable-workspace-trust"], {}, async () => {
// Action
await codeServerPage.openContextMenu("text=unique-file.txt")
expect(await codeServerPage.page.isVisible("text=Download...")).toBe(true)
await expect(codeServerPage.page.locator("text=Download...")).toBeVisible()
})
test("should see the 'Show Local' button on Save As", async ({ codeServerPage }) => {
@@ -37,7 +37,7 @@ describe("Downloads (enabled)", ["--disable-workspace-trust"], {}, async () => {
await codeServerPage.page.keyboard.type("Making some edits.")
await codeServerPage.navigateMenus(["File", "Save As..."])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true)
await expect(codeServerPage.page.locator("text=Show Local")).toBeVisible()
})
test("should see the 'Show Local' button on Save File", async ({ codeServerPage }) => {
@@ -46,14 +46,14 @@ describe("Downloads (enabled)", ["--disable-workspace-trust"], {}, async () => {
await codeServerPage.waitForTab("Untitled-1")
await codeServerPage.navigateMenus(["File", "Save"])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true)
await expect(codeServerPage.page.locator("text=Show Local")).toBeVisible()
})
test("should see the 'Show Local' button on Save Workspace As", async ({ codeServerPage }) => {
// Action
await codeServerPage.navigateMenus(["File", "Save Workspace As..."])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true)
await expect(codeServerPage.page.locator("text=Show Local")).toBeVisible()
})
})
@@ -72,7 +72,7 @@ describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-d
// Action
await codeServerPage.openContextMenu("text=unique-file.txt")
expect(await codeServerPage.page.isVisible("text=Download...")).toBe(false)
await expect(codeServerPage.page.locator("text=Download...")).not.toBeVisible()
})
test("should not see the 'Show Local' button on Save as", async ({ codeServerPage }) => {
@@ -87,7 +87,7 @@ describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-d
await codeServerPage.openFile(fileName)
await codeServerPage.page.click(".tab")
await codeServerPage.navigateMenus(["File", "Save As..."])
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false)
await expect(codeServerPage.page.locator("text=Show Local")).not.toBeVisible()
})
test("should not see the 'Show Local' button on Save File", async ({ codeServerPage }) => {
@@ -96,13 +96,13 @@ describe("Downloads (disabled)", ["--disable-workspace-trust", "--disable-file-d
await codeServerPage.waitForTab("Untitled-1")
await codeServerPage.navigateMenus(["File", "Save"])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false)
await expect(codeServerPage.page.locator("text=Show Local")).not.toBeVisible()
})
test("should not see the 'Show Local' button on Save Workspace As", async ({ codeServerPage }) => {
// Action
await codeServerPage.navigateMenus(["File", "Save Workspace As..."])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false)
await expect(codeServerPage.page.locator("text=Show Local")).not.toBeVisible()
})
})

View File

@@ -13,7 +13,7 @@ function runTestExtensionTests() {
// Remove end slash in address.
const normalizedAddress = address.replace(/\/+$/, "")
await codeServerPage.page.getByText(`Info: proxyUri: ${normalizedAddress}/proxy/{{port}}/`)
await expect(codeServerPage.page.getByText(`Info: proxyUri: ${normalizedAddress}/proxy/{{port}}/`)).toBeVisible()
})
}

View File

@@ -12,7 +12,7 @@ if (process.env.GITHUB_TOKEN) {
await codeServerPage.page.click("text=Allow")
// It should ask to select an account, one of which will be the one we
// pre-injected.
expect(await codeServerPage.page.isVisible("text=Select an account")).toBe(false)
await expect(codeServerPage.page.locator("text=Select an account")).not.toBeVisible()
})
})
@@ -26,7 +26,7 @@ if (process.env.GITHUB_TOKEN) {
await codeServerPage.page.click("text=Allow")
// Since there is no account it will ask directly for the token (because
// we are on localhost; otherwise it would initiate the oauth flow).
expect(await codeServerPage.page.isVisible("text=GitHub Personal Access Token")).toBe(false)
await expect(codeServerPage.page.locator("text=GitHub Personal Access Token")).not.toBeVisible()
})
})
} else {

View File

@@ -24,8 +24,8 @@ describe("login", ["--disable-workspace-trust", "--auth", "password"], {}, () =>
// Skip entering password
// Click the submit button and login
await codeServerPage.page.click(".submit")
await codeServerPage.page.waitForLoadState("networkidle")
expect(await codeServerPage.page.isVisible("text=Missing password"))
// The required input blocks empty submits, so the server-side error can't render.
await expect(codeServerPage.page.locator("input.password:invalid")).toBeVisible()
})
test("should see an error message for incorrect password", async ({ codeServerPage }) => {
@@ -34,28 +34,29 @@ describe("login", ["--disable-workspace-trust", "--auth", "password"], {}, () =>
// Click the submit button and login
await codeServerPage.page.click(".submit")
await codeServerPage.page.waitForLoadState("networkidle")
expect(await codeServerPage.page.isVisible("text=Incorrect password"))
await expect(codeServerPage.page.locator("text=Incorrect password")).toBeVisible()
})
test("should hit the rate limiter for too many unsuccessful logins", async ({ codeServerPage }) => {
// Type in password
await codeServerPage.page.fill(".password", "password123")
test.slow()
// Click the submit button and login
// The current RateLimiter allows 2 logins per minute plus
// 12 logins per hour for a total of 14
// See: src/node/routes/login.ts
for (let i = 1; i <= 14; i++) {
await codeServerPage.page.fill(".password", "password123")
await codeServerPage.page.click(".submit")
await codeServerPage.page.waitForLoadState("networkidle")
// We double-check that the correct error message shows
// which should be for incorrect password
expect(await codeServerPage.page.isVisible("text=Incorrect password"))
await expect(codeServerPage.page.locator("text=Incorrect password")).toBeVisible()
}
// The 15th should fail for a different reason:
// login rate
await codeServerPage.page.fill(".password", "password123")
await codeServerPage.page.click(".submit")
await codeServerPage.page.waitForLoadState("networkidle")
expect(await codeServerPage.page.isVisible("text=Login rate limited!"))
await expect(codeServerPage.page.locator("text=Login rate limited!")).toBeVisible()
})
})

View File

@@ -18,14 +18,14 @@ describe("Uploads (enabled)", ["--disable-workspace-trust"], {}, () => {
// Action
await codeServerPage.openContextMenu('span:has-text("test-directory")')
expect(await codeServerPage.page.isVisible("text=Upload...")).toBe(true)
await expect(codeServerPage.page.locator("text=Upload...")).toBeVisible()
})
test("should see the 'Show Local' button on Open File", async ({ codeServerPage }) => {
// Action
await codeServerPage.navigateMenus(["File", "Open File..."])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(true)
await expect(codeServerPage.page.locator("text=Show Local")).toBeVisible()
})
})
@@ -44,13 +44,13 @@ describe("Uploads (disabled)", ["--disable-workspace-trust", "--disable-file-upl
// Action
await codeServerPage.openContextMenu('span:has-text("test-directory")')
expect(await codeServerPage.page.isVisible("text=Upload...")).toBe(false)
await expect(codeServerPage.page.locator("text=Upload...")).not.toBeVisible()
})
test("should not see the 'Show Local' button on Open File", async ({ codeServerPage }) => {
// Action
await codeServerPage.navigateMenus(["File", "Open File..."])
await codeServerPage.page.waitForSelector(".quick-input-widget")
expect(await codeServerPage.page.isVisible("text=Show Local")).toBe(false)
await expect(codeServerPage.page.locator("text=Show Local")).not.toBeVisible()
})
})

97
test/package-lock.json generated
View File

@@ -7,7 +7,7 @@
"license": "MIT",
"devDependencies": {
"@jest-mock/express": "^1.4.5",
"@playwright/test": "^1.56.1",
"@playwright/test": "^1.61.0",
"@types/jest": "^27.0.2",
"@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8",
@@ -18,7 +18,7 @@
"jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0",
"node-fetch": "^2.6.7",
"playwright": "^1.59.1",
"playwright": "^1.61.0",
"ts-jest": "^27.0.7",
"wtfnode": "^0.9.1"
}
@@ -998,13 +998,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
"integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==",
"version": "1.61.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz",
"integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.56.1"
"playwright": "1.61.0"
},
"bin": {
"playwright": "cli.js"
@@ -1013,53 +1013,6 @@
"node": ">=18"
}
},
"node_modules/@playwright/test/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/@playwright/test/node_modules/playwright": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
"integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.56.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/@playwright/test/node_modules/playwright-core": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sinonjs/commons": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
@@ -2336,17 +2289,17 @@
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
"hasown": "^2.0.4",
"mime-types": "^2.1.35"
},
"engines": {
"node": ">= 6"
@@ -2561,9 +2514,9 @@
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3602,16 +3555,16 @@
}
},
"node_modules/jsdom/node_modules/form-data": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz",
"integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.5.tgz",
"integrity": "sha512-j23EibVLnp4zNXGW7LjryXYa2X6U/M96yoOX+ybZxwkYajdxRNEqYY3zhh7y0i6kfISKS2jr+EJq1YTUDEv5+w==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"hasown": "^2.0.4",
"mime-types": "^2.1.35"
},
"engines": {
@@ -4114,13 +4067,13 @@
}
},
"node_modules/playwright": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"version": "1.61.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
"integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.59.1"
"playwright-core": "1.61.0"
},
"bin": {
"playwright": "cli.js"
@@ -4133,9 +4086,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"version": "1.61.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz",
"integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -3,7 +3,7 @@
"#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.",
"devDependencies": {
"@jest-mock/express": "^1.4.5",
"@playwright/test": "^1.56.1",
"@playwright/test": "^1.61.0",
"@types/jest": "^27.0.2",
"@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8",
@@ -14,7 +14,7 @@
"jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0",
"node-fetch": "^2.6.7",
"playwright": "^1.59.1",
"playwright": "^1.61.0",
"ts-jest": "^27.0.7",
"wtfnode": "^0.9.1"
},

View File

@@ -1,10 +1,9 @@
import { logger } from "@coder/logger"
import { readFile, writeFile, stat, utimes } from "fs/promises"
import { setImmediate } from "timers"
import { Heart } from "../../../src/node/heart"
import { clean, mockLogger, tmpdir } from "../../utils/helpers"
const mockIsActive = (resolveTo: boolean) => jest.fn().mockResolvedValue(resolveTo)
describe("Heart", () => {
const testName = "heartTests"
let testDir = ""
@@ -15,12 +14,15 @@ describe("Heart", () => {
await clean(testName)
testDir = await tmpdir(testName)
})
beforeEach(() => {
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true))
})
afterAll(() => {
jest.restoreAllMocks()
})
beforeEach(() => {
heart = new Heart(`${testDir}/shutdown.txt`, jest.fn().mockResolvedValue(true))
})
afterEach(() => {
jest.resetAllMocks()
jest.useRealTimers()
@@ -28,6 +30,7 @@ describe("Heart", () => {
heart.dispose()
}
})
it("should write to a file when given a valid file path", async () => {
// Set up heartbeat file with contents
const text = "test"
@@ -42,7 +45,7 @@ describe("Heart", () => {
expect(fileContents).toBe(text)
heart = new Heart(pathToFile, mockIsActive(true))
heart = new Heart(pathToFile, jest.fn().mockResolvedValue(true))
await heart.beat()
// Check that the heart wrote to the heartbeatFilePath and overwrote our text
const fileContentsAfterBeat = await readFile(pathToFile, { encoding: "utf8" })
@@ -51,31 +54,30 @@ describe("Heart", () => {
const fileStatusAfterEdit = await stat(pathToFile)
expect(fileStatusAfterEdit.mtimeMs).toBeGreaterThan(0)
})
it("should log a warning when given an invalid file path", async () => {
heart = new Heart(`fakeDir/fake.txt`, mockIsActive(false))
heart = new Heart(`fakeDir/fake.txt`, jest.fn().mockResolvedValue(false))
await heart.beat()
expect(logger.warn).toHaveBeenCalled()
})
it("should be active after calling beat", async () => {
await heart.beat()
const isAlive = heart.alive()
expect(isAlive).toBe(true)
})
it("should not be active after dispose is called", () => {
heart.dispose()
const isAlive = heart.alive()
expect(isAlive).toBe(false)
})
it("should beat twice without warnings", async () => {
// Use fake timers so we can speed up setTimeout
jest.useFakeTimers()
heart = new Heart(`${testDir}/hello.txt`, mockIsActive(true))
heart = new Heart(`${testDir}/hello.txt`, jest.fn().mockResolvedValue(true))
await heart.beat()
// we need to speed up clocks, timeouts
// call heartbeat again (and it won't be alive I think)
// then assert no warnings were called
jest.runAllTimers()
expect(logger.warn).not.toHaveBeenCalled()
})
@@ -84,40 +86,48 @@ describe("Heart", () => {
describe("heartbeatTimer", () => {
const testName = "heartbeatTimer"
let testDir = ""
beforeAll(async () => {
await clean(testName)
testDir = await tmpdir(testName)
mockLogger()
})
afterAll(() => {
jest.restoreAllMocks()
})
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.resetAllMocks()
jest.clearAllTimers()
jest.useRealTimers()
})
it("should call isActive when timeout expires", async () => {
const isActive = true
const mockIsActive = jest.fn().mockResolvedValue(isActive)
const mockIsActive = jest.fn().mockResolvedValue(true)
const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
await heart.beat()
jest.advanceTimersByTime(60 * 1000)
expect(mockIsActive).toHaveBeenCalled()
})
it("should log a warning when isActive rejects", async () => {
const errorMsg = "oh no"
const error = new Error(errorMsg)
const error = new Error("oh no")
const mockIsActive = jest.fn().mockRejectedValue(error)
const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
await heart.beat()
jest.advanceTimersByTime(60 * 1000)
expect(mockIsActive).toHaveBeenCalled()
expect(logger.warn).toHaveBeenCalledWith(errorMsg)
// The timer callback waits on mockIsActive, so we need to yield to let the
// callback finish.
await new Promise((resolve) => setImmediate(resolve))
expect(logger.warn).toHaveBeenCalledWith(error.message)
})
})
@@ -125,38 +135,54 @@ describe("stateChange", () => {
const testName = "stateChange"
let testDir = ""
let heart: Heart
beforeAll(async () => {
await clean(testName)
testDir = await tmpdir(testName)
mockLogger()
})
afterAll(() => {
jest.restoreAllMocks()
})
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.resetAllMocks()
jest.useRealTimers()
if (heart) {
heart.dispose()
}
})
it("should change to alive after a beat", async () => {
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true))
const mockIsActive = jest.fn().mockResolvedValue(true)
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
const mockOnChange = jest.fn()
heart.onChange(mockOnChange)
await heart.beat()
expect(mockOnChange.mock.calls[0][0]).toBe("alive")
})
it.only("should change to expired when not active", async () => {
jest.useFakeTimers()
heart = new Heart(`${testDir}/shutdown.txt`, () => new Promise((resolve) => resolve(false)))
it("should change to expired when not active", async () => {
const mockIsActive = jest.fn().mockResolvedValue(false)
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
const mockOnChange = jest.fn()
heart.onChange(mockOnChange)
await heart.beat()
await jest.advanceTimersByTime(60 * 1000)
await heart.beat()
jest.advanceTimersByTime(60 * 1000)
expect(mockIsActive).toHaveBeenCalled()
// The timer callback waits on the isActive promise, so we need to yield to
// let the callback finish.
await new Promise((resolve) => setImmediate(resolve))
expect(mockOnChange.mock.calls[1][0]).toBe("expired")
jest.clearAllTimers()
jest.useRealTimers()
})
})

View File

@@ -1,15 +1,12 @@
import * as express from "express"
import * as http from "http"
import nodeFetch from "node-fetch"
import { HttpCode } from "../../../src/common/http"
import { proxy } from "../../../src/node/proxy"
import { wss, Router as WsRouter } from "../../../src/node/wsRouter"
import { getAvailablePort, mockLogger } from "../../utils/helpers"
import { mockLogger } from "../../utils/helpers"
import * as httpserver from "../../utils/httpserver"
import * as integration from "../../utils/integration"
describe("proxy", () => {
const nhooyrDevServer = new httpserver.HttpServer()
const proxyTarget = new httpserver.HttpServer()
const wsApp = express.default()
const wsRouter = WsRouter()
let codeServer: httpserver.HttpServer | undefined
@@ -19,21 +16,22 @@ describe("proxy", () => {
beforeAll(async () => {
wsApp.use("/", wsRouter.router)
await nhooyrDevServer.listen((req, res) => {
await proxyTarget.listen((req, res) => {
e(req, res)
})
nhooyrDevServer.listenUpgrade(wsApp)
proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup`
proxyTarget.listenUpgrade(wsApp)
proxyPath = `/proxy/${proxyTarget.port()}/wsup`
absProxyPath = proxyPath.replace("/proxy/", "/absproxy/")
})
afterAll(async () => {
await nhooyrDevServer.dispose()
await proxyTarget.dispose()
})
beforeEach(() => {
e = express.default()
mockLogger()
delete process.env.PASSWORD
})
afterEach(async () => {
@@ -283,65 +281,42 @@ describe("proxy", () => {
const resp = await codeServer.fetch(proxyPath, { method: "OPTIONS" })
expect(resp.status).toBe(200)
})
})
// NOTE@jsjoeio
// Both this test suite and the one above it are very similar
// The main difference is this one uses http and node-fetch
// and specifically tests the proxy in isolation vs. using
// the httpserver abstraction we've built.
//
// Leaving this as a separate test suite for now because
// we may consider refactoring the httpserver abstraction
// in the future.
//
// If you're writing a test specifically for code in
// src/node/proxy.ts, you should probably add it to
// this test suite.
describe("proxy (standalone)", () => {
let URL = ""
let PROXY_URL = ""
let testServer: http.Server
let proxyTarget: http.Server
it("should return a 500 when no target is running ", async () => {
const target = new httpserver.HttpServer()
await target.listen(() => {})
const port = target.port()
target.dispose()
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch(`/proxy/${port}/wsup`)
expect(resp.status).toBe(HttpCode.ServerError)
expect(resp.statusText).toBe("Internal Server Error")
})
beforeEach(async () => {
const PORT = await getAvailablePort()
const PROXY_PORT = await getAvailablePort()
URL = `http://localhost:${PORT}`
PROXY_URL = `http://localhost:${PROXY_PORT}`
// Define server and a proxy server
testServer = http.createServer((req, res) => {
proxy.web(req, res, {
target: PROXY_URL,
})
it("should strip token cookie", async () => {
const token = "my-super-secure-token"
process.env.HASHED_PASSWORD = token
codeServer = await integration.setup(["--auth=password"])
// Set up a listener that just prints the cookies it got.
e.get("/wsup/cookies", (req, res) => {
res.writeHead(HttpCode.Ok, { "Content-Type": "text/plain" })
res.end(req.headers.cookie)
})
proxyTarget = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" })
res.end()
// Send the token along with other cookies which should be preserved.
// Encode one to make sure they are being re-encoded properly.
const value = "hello=there"
const encodedValue = encodeURIComponent(value)
const resp = await codeServer.fetch(proxyPath + "/cookies", {
headers: {
cookie: `cookie1=${encodedValue}; code-server-session=${token}; cookie2=hello;`,
},
})
// Start both servers
proxyTarget.listen(PROXY_PORT)
testServer.listen(PORT)
})
afterEach(async () => {
testServer.close()
proxyTarget.close()
})
it("should return a 500 when proxy target errors ", async () => {
// Close the proxy target so that proxy errors
proxyTarget.close()
const errorResp = await nodeFetch(`${URL}/error`)
expect(errorResp.status).toBe(HttpCode.ServerError)
expect(errorResp.statusText).toBe("Internal Server Error")
})
it("should proxy correctly", async () => {
const resp = await nodeFetch(`${URL}/route`)
// The proxied listener should not have printed the code-server token.
expect(resp.status).toBe(200)
expect(resp.statusText).toBe("OK")
const text = await resp.text()
expect(text).toBe(`cookie1=${encodedValue}; cookie2=hello`)
})
})