diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0a243e388740..beb2a43a31c0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: helm: ${{ steps.filter.outputs.helm }} steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Check changed files uses: dorny/paths-filter@v3 id: filter @@ -64,7 +64,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version-file: .node-version @@ -82,7 +82,7 @@ jobs: needs: changes if: needs.changes.outputs.docs == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version-file: .node-version @@ -100,7 +100,7 @@ jobs: needs: changes if: needs.changes.outputs.helm == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: azure/setup-helm@v4 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -114,7 +114,7 @@ jobs: needs: changes if: needs.changes.outputs.code == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version-file: .node-version @@ -132,7 +132,7 @@ jobs: if: needs.changes.outputs.ci == 'true' steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Check workflow files run: | bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.1 @@ -146,7 +146,7 @@ jobs: needs: changes if: needs.changes.outputs.code == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version-file: .node-version @@ -169,7 +169,7 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} DISABLE_V8_COMPILE_CACHE: 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true - run: sudo apt update && sudo apt install -y libkrb5-dev @@ -231,7 +231,7 @@ jobs: needs: [changes, build] if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: sudo apt update && sudo apt install -y libkrb5-dev - uses: actions/setup-node@v4 with: @@ -241,7 +241,7 @@ jobs: package-lock.json test/package-lock.json - run: SKIP_SUBMODULE_DEPS=1 npm ci - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: npm-package - run: tar -xzf package.tar.gz @@ -265,7 +265,7 @@ jobs: needs: [changes, build] if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: sudo apt update && sudo apt install -y libkrb5-dev - uses: actions/setup-node@v4 with: @@ -275,7 +275,7 @@ jobs: package-lock.json test/package-lock.json - run: SKIP_SUBMODULE_DEPS=1 npm ci - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: npm-package - run: tar -xzf package.tar.gz diff --git a/.github/workflows/installer.yaml b/.github/workflows/installer.yaml index e8b04453dcfc..c33ee0070763 100644 --- a/.github/workflows/installer.yaml +++ b/.github/workflows/installer.yaml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install code-server run: ./install.sh @@ -44,7 +44,7 @@ jobs: container: "alpine:3.17" steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install curl run: apk add curl @@ -67,7 +67,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install code-server run: ./install.sh diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 91e320087175..374c35876d41 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Node.js uses: actions/setup-node@v4 @@ -65,7 +65,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Configure git run: | @@ -94,13 +94,13 @@ jobs: steps: # We need to checkout code-server so we can get the version - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 path: "./code-server" - name: Checkout code-server-aur repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: "cdrci/code-server-aur" token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }} @@ -148,7 +148,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code-server - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a16fc6b90ac7..2536983d2b45 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -60,7 +60,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Node.js uses: actions/setup-node@v4 @@ -94,7 +94,7 @@ jobs: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -134,7 +134,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Node.js uses: actions/setup-node@v4 @@ -160,7 +160,7 @@ jobs: - run: brew install python-setuptools - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -195,7 +195,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Node.js uses: actions/setup-node@v4 @@ -221,7 +221,7 @@ jobs: - run: brew install python-setuptools - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package @@ -253,7 +253,7 @@ jobs: needs: npm-version steps: - name: Download npm package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: npm-release-package diff --git a/.github/workflows/scripts.yaml b/.github/workflows/scripts.yaml index d3ca65cbc548..a39d90ad4c96 100644 --- a/.github/workflows/scripts.yaml +++ b/.github/workflows/scripts.yaml @@ -41,7 +41,7 @@ jobs: container: "alpine:3.17" steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install test utilities run: apk add bats checkbashisms @@ -58,7 +58,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install lint utilities run: sudo apt install shellcheck diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml index a14094d92e82..d8afcfce34d7 100644 --- a/.github/workflows/security.yaml +++ b/.github/workflows/security.yaml @@ -25,7 +25,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 @@ -46,12 +46,12 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 with: scan-type: "fs" scan-ref: "." @@ -76,7 +76,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/trivy-docker.yaml b/.github/workflows/trivy-docker.yaml index 06ca72ae6429..97769603da9e 100644 --- a/.github/workflows/trivy-docker.yaml +++ b/.github/workflows/trivy-docker.yaml @@ -48,10 +48,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Run Trivy vulnerability scanner in image mode - uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 + uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 with: image-ref: "docker.io/codercom/code-server:latest" ignore-unfixed: true diff --git a/.node-version b/.node-version index 91d5f6ff8e3f..e2228113dd09 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.18.0 +22.19.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c177c17efda..3ba0b42cb005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,22 @@ Code v99.99.999 ## Unreleased +## [4.104.2](https://github.com/coder/code-server/releases/tag/v4.104.2) - 2025-09-26 + +Code v1.104.2 + +### Changed + +- Update to Code 1.104.2. + +## [4.104.1](https://github.com/coder/code-server/releases/tag/v4.104.1) - 2025-09-19 + +Code v1.104.1 + +### Changed + +- Update to Code 1.104.1. + ## [4.104.0](https://github.com/coder/code-server/releases/tag/v4.104.0) - 2025-09-15 Code v1.104.0 diff --git a/ci/helm-chart/Chart.yaml b/ci/helm-chart/Chart.yaml index 7d62f273000c..00e7221ee109 100644 --- a/ci/helm-chart/Chart.yaml +++ b/ci/helm-chart/Chart.yaml @@ -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.31.0 +version: 3.31.2 # 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.104.0 +appVersion: 4.104.2 diff --git a/ci/helm-chart/values.yaml b/ci/helm-chart/values.yaml index 7a9a2676144a..a414de0da0ec 100644 --- a/ci/helm-chart/values.yaml +++ b/ci/helm-chart/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: codercom/code-server - tag: '4.104.0' + tag: '4.104.2' pullPolicy: Always # Specifies one or more secrets to be used when pulling images from a diff --git a/docs/FAQ.md b/docs/FAQ.md index 2e01306cb2f2..a695cc64ac6a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -322,12 +322,8 @@ As long as there is an active browser connection, code-server touches `~/.local/share/code-server/heartbeat` once a minute. If you want to shutdown code-server if there hasn't been an active connection -after a predetermined amount of time, you can do so by checking continuously for -the last modified time on the heartbeat file. If it is older than X minutes (or -whatever amount of time you'd like), you can kill code-server. - -Eventually, [#1636](https://github.com/coder/code-server/issues/1636) will make -this process better. +after a predetermined amount of time, you can use the --idle-timeout-seconds flag +or set an `CODE_SERVER_IDLE_TIMEOUT_SECONDS` environment variable. ## How do I change the password? diff --git a/docs/android.md b/docs/android.md index 2659c44000b8..0b1dc24abf6e 100644 --- a/docs/android.md +++ b/docs/android.md @@ -14,8 +14,8 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 7. Install and use Node.js 22: ```shell -nvm install 18 -nvm use 18 +nvm install 22 +nvm use 22 ``` 8. Install code-server globally on device with: `npm install --global code-server` diff --git a/lib/vscode b/lib/vscode index 0f0d87fa9e96..7d842fb85a02 160000 --- a/lib/vscode +++ b/lib/vscode @@ -1 +1 @@ -Subproject commit 0f0d87fa9e96c856c5212fc86db137ac0d783365 +Subproject commit 7d842fb85a0275a4a8e4d7e040d2625abbf7f084 diff --git a/package-lock.json b/package-lock.json index 2c98099e0bb5..8bd50af3431d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -128,9 +128,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -190,9 +190,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -200,9 +200,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -250,9 +250,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, "license": "MIT", "engines": { @@ -273,13 +273,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -610,15 +610,14 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", - "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/qs": "*", "@types/serve-static": "*" } }, @@ -1640,18 +1639,18 @@ } }, "node_modules/body-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz", - "integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", - "iconv-lite": "^0.5.2", + "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", - "qs": "6.14.0", + "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" }, @@ -2150,16 +2149,6 @@ "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -2490,20 +2479,20 @@ } }, "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2903,55 +2892,45 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", - "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.0.1", + "body-parser": "^2.2.0", "content-disposition": "^1.0.0", - "content-type": "~1.0.4", - "cookie": "0.7.1", + "content-type": "^1.0.5", + "cookie": "^0.7.1", "cookie-signature": "^1.2.1", - "debug": "4.3.6", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "^2.0.0", - "fresh": "2.0.0", - "http-errors": "2.0.0", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", - "methods": "~1.1.2", "mime-types": "^3.0.0", - "on-finished": "2.4.1", - "once": "1.4.0", - "parseurl": "~1.3.3", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "router": "^2.0.0", - "safe-buffer": "5.2.1", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", "send": "^1.1.0", - "serve-static": "^2.1.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "^2.0.0", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { "node": ">= 18" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/cookie-signature": { @@ -2963,44 +2942,6 @@ "node": ">=6.6.0" } }, - "node_modules/express/node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3416,9 +3357,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { @@ -3669,12 +3610,12 @@ } }, "node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -4480,15 +4421,6 @@ "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromark": { "version": "2.11.4", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", @@ -4630,20 +4562,21 @@ } }, "node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz", - "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "license": "MIT", "dependencies": { - "mime-db": "^1.53.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" @@ -5120,12 +5053,13 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pem": { @@ -5334,30 +5268,34 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.6.3", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/readable-stream": { @@ -5535,11 +5473,13 @@ } }, "node_modules/router": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", - "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" @@ -5653,19 +5593,18 @@ } }, "node_modules/send": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz", - "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", "dependencies": { "debug": "^4.3.5", - "destroy": "^1.2.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", - "fresh": "^0.5.2", + "fresh": "^2.0.0", "http-errors": "^2.0.0", - "mime-types": "^2.1.35", + "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", @@ -5675,46 +5614,16 @@ "node": ">= 18" } }, - "node_modules/send/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/serve-static": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz", - "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", - "send": "^1.0.0" + "send": "^1.2.0" }, "engines": { "node": ">= 18" @@ -6307,9 +6216,9 @@ } }, "node_modules/type-is": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz", - "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -6621,15 +6530,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/patches/base-path.diff b/patches/base-path.diff index 31db2231a939..5c6e5b8b1e31 100644 --- a/patches/base-path.diff +++ b/patches/base-path.diff @@ -105,8 +105,8 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor const webSocketSchema = (/^https:/.test(mainWindow.location.href) ? 'wss' : 'ws'); + path = (mainWindow.location.pathname + "/" + path).replace(/\/\/+/g, "/") const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel); - const errorListener = socket.onError(reject); - socket.onOpen(() => { + const disposables = new DisposableStore(); + disposables.add(socket.onError(reject)); Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts diff --git a/patches/cli-window-open.diff b/patches/cli-window-open.diff index 8da64b2a5d70..3bc21fa3b4bc 100644 --- a/patches/cli-window-open.diff +++ b/patches/cli-window-open.diff @@ -17,7 +17,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts -@@ -106,10 +106,14 @@ class RemoteTerminalBackend extends Base +@@ -107,10 +107,14 @@ class RemoteTerminalBackend extends Base } const reqId = e.reqId; const commandId = e.commandId; diff --git a/patches/clipboard.diff b/patches/clipboard.diff index 02c7d54dfd93..ecf6a09e30f3 100644 --- a/patches/clipboard.diff +++ b/patches/clipboard.diff @@ -65,7 +65,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts -@@ -97,7 +97,7 @@ class RemoteTerminalBackend extends Base +@@ -98,7 +98,7 @@ class RemoteTerminalBackend extends Base } }); @@ -78,7 +78,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/argv.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/environment/common/argv.ts +++ code-server/lib/vscode/src/vs/platform/environment/common/argv.ts -@@ -135,6 +135,7 @@ export interface NativeParsedArgs { +@@ -136,6 +136,7 @@ export interface NativeParsedArgs { 'disable-chromium-sandbox'?: boolean; sandbox?: boolean; 'enable-coi'?: boolean; @@ -90,7 +90,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/node/argv.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/platform/environment/node/argv.ts +++ code-server/lib/vscode/src/vs/platform/environment/node/argv.ts -@@ -104,6 +104,7 @@ export const OPTIONS: OptionDescriptions +@@ -105,6 +105,7 @@ export const OPTIONS: OptionDescriptions 'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") }, 'profile': { type: 'string', 'cat': 'o', args: 'profileName', description: localize('profileName', "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created.") }, 'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") }, diff --git a/patches/external-file-actions.diff b/patches/external-file-actions.diff index 8a6830d00eca..ef92e1c46361 100644 --- a/patches/external-file-actions.diff +++ b/patches/external-file-actions.diff @@ -125,13 +125,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts -@@ -7,11 +7,11 @@ import { Event } from '../../base/common - import { Disposable, DisposableStore, MutableDisposable } from '../../base/common/lifecycle.js'; +@@ -6,10 +6,10 @@ + import { Disposable } from '../../base/common/lifecycle.js'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js'; - import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext, IsSimulationContext } from '../../platform/contextkey/common/contextkeys.js'; + import { IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; -import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext } from '../common/contextkeys.js'; +import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; - import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; -import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js'; @@ -139,7 +138,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from '../../platform/workspace/common/workspace.js'; import { IWorkbenchLayoutService, Parts, positionToString } from '../services/layout/browser/layoutService.js'; import { getRemoteName } from '../../platform/remote/common/remoteHosts.js'; -@@ -72,7 +72,7 @@ export class WorkbenchContextKeysHandler +@@ -69,7 +69,7 @@ export class WorkbenchContextKeysHandler @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -148,7 +147,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts @IProductService private readonly productService: IProductService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IEditorService private readonly editorService: IEditorService, -@@ -208,6 +208,10 @@ export class WorkbenchContextKeysHandler +@@ -199,6 +199,10 @@ export class WorkbenchContextKeysHandler this.auxiliaryBarMaximizedContext = AuxiliaryBarMaximizedContext.bindTo(this.contextKeyService); this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized()); diff --git a/patches/getting-started.diff b/patches/getting-started.diff index 14ab7f4d57e1..c5fd5d6ccbac 100644 --- a/patches/getting-started.diff +++ b/patches/getting-started.diff @@ -213,16 +213,16 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts -@@ -7,7 +7,7 @@ import { Event } from '../../base/common - import { Disposable, DisposableStore, MutableDisposable } from '../../base/common/lifecycle.js'; +@@ -6,7 +6,7 @@ + import { Disposable } from '../../base/common/lifecycle.js'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js'; - import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext, IsSimulationContext } from '../../platform/contextkey/common/contextkeys.js'; + import { IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js'; -import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads } from '../common/contextkeys.js'; +import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsEnabledFileDownloads, IsEnabledFileUploads, IsEnabledCoderGettingStarted, } from '../common/contextkeys.js'; - import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; -@@ -211,6 +211,7 @@ export class WorkbenchContextKeysHandler + import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService.js'; +@@ -202,6 +202,7 @@ export class WorkbenchContextKeysHandler // code-server IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) IsEnabledFileUploads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileUploads ?? true) diff --git a/src/node/cli.ts b/src/node/cli.ts index 70ede42a0591..0fce9cfbf25f 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -94,6 +94,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs { "welcome-text"?: string "abs-proxy-base-path"?: string i18n?: string + "idle-timeout-seconds"?: number /* Positional arguments. */ _?: string[] } @@ -303,6 +304,10 @@ export const options: Options> = { path: true, description: "Path to JSON file with custom translations. Merges with default strings and supports all i18n keys.", }, + "idle-timeout-seconds": { + type: "number", + description: "Timeout in seconds to wait before shutting down when idle.", + }, } export const optionDescriptions = (opts: Partial>> = options): string[] => { @@ -396,6 +401,10 @@ export const parse = ( throw new Error("--github-auth can only be set in the config file or passed in via $GITHUB_TOKEN") } + if (key === "idle-timeout-seconds" && Number(value) <= 60) { + throw new Error("--idle-timeout-seconds must be greater than 60 seconds.") + } + const option = options[key] if (option.type === "boolean") { ;(args[key] as boolean) = true @@ -611,6 +620,16 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config args["github-auth"] = process.env.GITHUB_TOKEN } + if (process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) { + if (isNaN(Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS))) { + logger.info("CODE_SERVER_IDLE_TIMEOUT_SECONDS must be a number") + } + if (Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) <= 60) { + throw new Error("--idle-timeout-seconds must be greater than 60 seconds.") + } + args["idle-timeout-seconds"] = Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) + } + // Ensure they're not readable by child processes. delete process.env.PASSWORD delete process.env.HASHED_PASSWORD diff --git a/src/node/heart.ts b/src/node/heart.ts index aac917257f23..b78f4edb80d2 100644 --- a/src/node/heart.ts +++ b/src/node/heart.ts @@ -1,5 +1,6 @@ import { logger } from "@coder/logger" import { promises as fs } from "fs" +import { Emitter } from "../common/emitter" /** * Provides a heartbeat using a local file to indicate activity. @@ -8,6 +9,9 @@ export class Heart { private heartbeatTimer?: NodeJS.Timeout private heartbeatInterval = 60000 public lastHeartbeat = 0 + private readonly _onChange = new Emitter<"alive" | "expired" | "unknown">() + readonly onChange = this._onChange.event + private state: "alive" | "expired" | "unknown" = "expired" public constructor( private readonly heartbeatPath: string, @@ -17,6 +21,13 @@ export class Heart { this.alive = this.alive.bind(this) } + private setState(state: typeof this.state) { + if (this.state !== state) { + this.state = state + this._onChange.emit(this.state) + } + } + public alive(): boolean { const now = Date.now() return now - this.lastHeartbeat < this.heartbeatInterval @@ -28,6 +39,7 @@ export class Heart { */ public async beat(): Promise { if (this.alive()) { + this.setState("alive") return } @@ -36,7 +48,22 @@ export class Heart { if (typeof this.heartbeatTimer !== "undefined") { clearTimeout(this.heartbeatTimer) } - this.heartbeatTimer = setTimeout(() => heartbeatTimer(this.isActive, this.beat), this.heartbeatInterval) + + this.heartbeatTimer = setTimeout(async () => { + try { + if (await this.isActive()) { + this.beat() + } else { + this.setState("expired") + } + } catch (error: unknown) { + logger.warn((error as Error).message) + this.setState("unknown") + } + }, this.heartbeatInterval) + + this.setState("alive") + try { return await fs.writeFile(this.heartbeatPath, "") } catch (error: any) { @@ -53,20 +80,3 @@ export class Heart { } } } - -/** - * Helper function for the heartbeatTimer. - * - * If heartbeat is active, call beat. Otherwise do nothing. - * - * Extracted to make it easier to test. - */ -export async function heartbeatTimer(isActive: Heart["isActive"], beat: Heart["beat"]) { - try { - if (await isActive()) { - beat() - } - } catch (error: unknown) { - logger.warn((error as Error).message) - } -} diff --git a/src/node/main.ts b/src/node/main.ts index 6f8e28dbdea7..c2d3bd57852b 100644 --- a/src/node/main.ts +++ b/src/node/main.ts @@ -11,6 +11,7 @@ import { loadCustomStrings } from "./i18n" import { register } from "./routes" import { VSCodeModule } from "./routes/vscode" import { isDirectory, open } from "./util" +import { wrapper } from "./wrapper" /** * Return true if the user passed an extension-related VS Code flag. @@ -141,7 +142,7 @@ export const runCodeServer = async ( const app = await createApp(args) const protocol = args.cert ? "https" : "http" const serverAddress = ensureAddress(app.server, protocol) - const disposeRoutes = await register(app, args) + const { disposeRoutes, heart } = await register(app, args) logger.info(`Using config file ${args.config}`) logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`) @@ -166,6 +167,27 @@ export const runCodeServer = async ( logger.info(" - Not serving HTTPS") } + if (args["idle-timeout-seconds"]) { + logger.info(` - Idle timeout set to ${args["idle-timeout-seconds"]} seconds`) + + let idleShutdownTimer: NodeJS.Timeout | undefined + const startIdleShutdownTimer = () => { + idleShutdownTimer = setTimeout(() => { + logger.warn(`Idle timeout of ${args["idle-timeout-seconds"]} seconds exceeded`) + wrapper.exit(0) + }, args["idle-timeout-seconds"]! * 1000) + } + + startIdleShutdownTimer() + + heart.onChange((state) => { + clearTimeout(idleShutdownTimer) + if (state === "expired") { + startIdleShutdownTimer() + } + }) + } + if (args["disable-proxy"]) { logger.info(" - Proxy disabled") } else if (args["proxy-domain"].length > 0) { diff --git a/src/node/routes/index.ts b/src/node/routes/index.ts index 2841b5a01113..28bfc58d3ee7 100644 --- a/src/node/routes/index.ts +++ b/src/node/routes/index.ts @@ -28,7 +28,10 @@ import * as vscode from "./vscode" /** * Register all routes and middleware. */ -export const register = async (app: App, args: DefaultedArgs): Promise => { +export const register = async ( + app: App, + args: DefaultedArgs, +): Promise<{ disposeRoutes: Disposable["dispose"]; heart: Heart }> => { const heart = new Heart(path.join(paths.data, "heartbeat"), async () => { return new Promise((resolve, reject) => { // getConnections appears to not call the callback when there are no more @@ -173,8 +176,11 @@ export const register = async (app: App, args: DefaultedArgs): Promise { - heart.dispose() - vscode.dispose() + return { + disposeRoutes: () => { + heart.dispose() + vscode.dispose() + }, + heart, } } diff --git a/test/package-lock.json b/test/package-lock.json index f7ee4863c278..4e28618543cd 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -7,7 +7,7 @@ "license": "MIT", "devDependencies": { "@jest-mock/express": "^1.4.5", - "@playwright/test": "^1.46.0", + "@playwright/test": "^1.56.1", "@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.46.0", + "playwright": "^1.56.1", "ts-jest": "^27.0.7", "wtfnode": "^0.9.1" } @@ -1012,13 +1012,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", - "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.47.2" + "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -4305,13 +4305,13 @@ } }, "node_modules/playwright": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", - "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "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.47.2" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -4324,9 +4324,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "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": { diff --git a/test/package.json b/test/package.json index 6d2e92edd2ee..c8211337a79c 100644 --- a/test/package.json +++ b/test/package.json @@ -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.46.0", + "@playwright/test": "^1.56.1", "@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.46.0", + "playwright": "^1.56.1", "ts-jest": "^27.0.7", "wtfnode": "^0.9.1" }, diff --git a/test/unit/node/heart.test.ts b/test/unit/node/heart.test.ts index 7aa6f08dc2bf..7ad0d21752f2 100644 --- a/test/unit/node/heart.test.ts +++ b/test/unit/node/heart.test.ts @@ -1,6 +1,6 @@ import { logger } from "@coder/logger" import { readFile, writeFile, stat, utimes } from "fs/promises" -import { Heart, heartbeatTimer } from "../../../src/node/heart" +import { Heart } from "../../../src/node/heart" import { clean, mockLogger, tmpdir } from "../../utils/helpers" const mockIsActive = (resolveTo: boolean) => jest.fn().mockResolvedValue(resolveTo) @@ -82,31 +82,81 @@ describe("Heart", () => { }) describe("heartbeatTimer", () => { - beforeAll(() => { + 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 beat when isActive resolves to true", async () => { + it("should call isActive when timeout expires", async () => { const isActive = true const mockIsActive = jest.fn().mockResolvedValue(isActive) - const mockBeatFn = jest.fn() - await heartbeatTimer(mockIsActive, mockBeatFn) + const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive) + await heart.beat() + jest.advanceTimersByTime(60 * 1000) expect(mockIsActive).toHaveBeenCalled() - expect(mockBeatFn).toHaveBeenCalled() }) it("should log a warning when isActive rejects", async () => { const errorMsg = "oh no" const error = new Error(errorMsg) const mockIsActive = jest.fn().mockRejectedValue(error) - const mockBeatFn = jest.fn() - await heartbeatTimer(mockIsActive, mockBeatFn) + const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive) + await heart.beat() + jest.advanceTimersByTime(60 * 1000) + expect(mockIsActive).toHaveBeenCalled() - expect(mockBeatFn).not.toHaveBeenCalled() expect(logger.warn).toHaveBeenCalledWith(errorMsg) }) }) + +describe("stateChange", () => { + const testName = "stateChange" + let testDir = "" + let heart: Heart + beforeAll(async () => { + await clean(testName) + testDir = await tmpdir(testName) + mockLogger() + }) + afterAll(() => { + jest.restoreAllMocks() + }) + afterEach(() => { + jest.resetAllMocks() + if (heart) { + heart.dispose() + } + }) + it("should change to alive after a beat", async () => { + heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true)) + 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))) + const mockOnChange = jest.fn() + heart.onChange(mockOnChange) + await heart.beat() + + await jest.advanceTimersByTime(60 * 1000) + expect(mockOnChange.mock.calls[1][0]).toBe("expired") + jest.clearAllTimers() + jest.useRealTimers() + }) +}) diff --git a/test/unit/node/main.test.ts b/test/unit/node/main.test.ts index 09ee6b512ef9..39ba2ca8bf9b 100644 --- a/test/unit/node/main.test.ts +++ b/test/unit/node/main.test.ts @@ -16,6 +16,7 @@ jest.mock("@coder/logger", () => ({ debug: jest.fn(), warn: jest.fn(), error: jest.fn(), + named: jest.fn(), level: 0, }, field: jest.fn(), @@ -94,7 +95,7 @@ describe("main", () => { // Mock routes module jest.doMock("../../../src/node/routes", () => ({ - register: jest.fn().mockResolvedValue(jest.fn()), + register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }), })) // Mock loadCustomStrings to succeed @@ -131,7 +132,7 @@ describe("main", () => { // Mock routes module jest.doMock("../../../src/node/routes", () => ({ - register: jest.fn().mockResolvedValue(jest.fn()), + register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }), })) // Import runCodeServer after mocking