Compare commits

..

3 Commits

Author SHA1 Message Date
Joe Previte
072b5a8f92 fixup!: update chart version 2022-11-07 11:18:16 -07:00
Joe Previte
a2e464d06d chore: update CHANGELOG 2022-11-07 11:12:15 -07:00
Joe Previte
4ad763ad59 chore: update version to 4.8.3 2022-11-07 11:10:59 -07:00
101 changed files with 1395 additions and 2005 deletions

2
.github/CODEOWNERS vendored
View File

@@ -3,5 +3,3 @@
ci/helm-chart/ @Matthew-Beckett @alexgorbatchev
docs/install.md @GNUxeava
src/node/i18n/locales/zh-cn.json @zhaozhiming

View File

@@ -1,31 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
labels: []
commit-message:
prefix: "chore"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
labels: []
ignore:
# Ignore patch updates for all dependencies
- dependency-name: "*"
update-types:
- version-update:semver-patch
# Ignore major updates to Node.js types, because they need to
# correspond to the Node.js engine version
- dependency-name: "@types/node"
update-types:
- version-update:semver-major

View File

@@ -24,7 +24,7 @@ concurrency:
jobs:
prettier:
name: Format with Prettier
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -37,7 +37,7 @@ jobs:
doctoc:
name: Doctoc markdown files
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -45,7 +45,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
uses: tj-actions/changed-files@v26.1
with:
files: |
docs/**
@@ -66,7 +66,7 @@ jobs:
lint-helm:
name: Lint Helm chart
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -76,14 +76,14 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
uses: tj-actions/changed-files@v26.1
with:
files: |
ci/helm-chart/**
- name: Install helm
if: steps.changed-files.outputs.any_changed == 'true'
uses: azure/setup-helm@v3.5
uses: azure/setup-helm@v3.4
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -97,7 +97,7 @@ jobs:
lint-ts:
name: Lint TypeScript files
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repo
@@ -107,7 +107,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
uses: tj-actions/changed-files@v26.1
with:
files: |
**/*.ts
@@ -139,70 +139,9 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn lint:ts
lint-actions:
name: Lint GitHub Actions
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Check workflow files
run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/7fdc9630cc360ea1a469eed64ac6d78caeda1234/scripts/download-actionlint.bash)
./actionlint -color -shellcheck= -ignore "set-output"
shell: bash
test-unit:
name: Run unit tests
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
with:
files: |
**/*.ts
files_ignore: |
lib/vscode/**
- name: Install Node.js v16
if: steps.changed-files.outputs.any_changed == 'true'
uses: actions/setup-node@v3
with:
node-version: "16"
- name: Fetch dependencies from cache
if: steps.changed-files.outputs.any_changed == 'true'
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true' && steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Run unit tests
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn test:unit
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
if: success()
build:
name: Build code-server
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -231,24 +170,26 @@ jobs:
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-code-server-${{ hashFiles('**/yarn.lock') }}
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-code-server-
yarn-build-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Build code-server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn build
# Get Code's git hash. When this changes it means the content is
# different and we need to rebuild.
- name: Get latest lib/vscode rev
id: vscode-rev
run: echo "rev=$(git rev-parse HEAD:./lib/vscode)" >> $GITHUB_OUTPUT
run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)"
- name: Get version
id: version
run: echo "::set-output name=version::$(jq -r .version package.json)"
# We need to rebuild when we have a new version of Code, when any of
# the patches changed, or when the code-server version changes (since
@@ -259,14 +200,25 @@ jobs:
uses: actions/cache@v3
with:
path: lib/vscode-reh-web-*
key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ hashFiles('patches/*.diff', 'ci/build/build-vscode.sh') }}
key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ steps.version.outputs.version }}-${{ hashFiles('patches/*.diff', 'ci/build/build-vscode.sh') }}
- name: Build vscode
env:
VERSION: "0.0.0"
if: steps.cache-vscode.outputs.cache-hit != 'true'
run: yarn build:vscode
# Our code imports code from VS Code's `out` directory meaning VS Code
# must be built before running these tests.
# TODO: Move to its own step?
- name: Run code-server unit tests
run: yarn test:unit
if: success()
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
if: success()
# The release package does not contain any native modules
# and is neutral to architecture/os/libc version.
- name: Create release package
@@ -283,11 +235,64 @@ jobs:
name: npm-package
path: ./package.tar.gz
npm:
name: Publish npm package
# the npm-package gets uploaded as an artifact in Build
# so we need that to complete before this runs
needs: build
# This environment "npm" requires someone from
# coder/code-server-reviewers to approve the PR before this job runs.
environment: npm
# Only run if PR comes from base repo or event is not a PR
# Reason: forks cannot access secrets and this will always fail
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Download artifact
uses: actions/download-artifact@v3
id: download
with:
name: "npm-package"
path: release-npm-package
- name: Run ./ci/steps/publish-npm.sh
run: yarn publish:npm
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# NOTE@jsjoeio
# NPM_ENVIRONMENT intentionally not set here.
# Instead, itis determined in publish-npm.sh script
# using GITHUB environment variables
- name: Comment npm information
uses: marocchino/sticky-pull-request-comment@v2
with:
GITHUB_TOKEN: ${{ github.token }}
header: npm-dev-build
message: |
✨ code-server dev build published to npm for PR #${{ github.event.number }}!
* _Last publish status_: success
* _Commit_: ${{ github.event.pull_request.head.sha }}
To install in a local project, run:
```shell-session
npm install @coder/code-server-pr@${{ github.event.number }}
```
To install globally, run:
```shell-session
npm install -g @coder/code-server-pr@${{ github.event.number }}
```
test-e2e:
name: Run e2e tests
needs: build
runs-on: ubuntu-20.04
timeout-minutes: 25
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repo
uses: actions/checkout@v3
@@ -315,7 +320,7 @@ jobs:
run: tar -xzf package.tar.gz
- name: Install release package dependencies
run: cd release && npm install --unsafe-perm --omit=dev
run: cd release && yarn install
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
@@ -327,7 +332,7 @@ jobs:
./test/node_modules/.bin/playwright install
- name: Run end-to-end tests
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e --global-timeout 840000
- name: Upload test artifacts
if: always()
@@ -342,7 +347,7 @@ jobs:
test-e2e-proxy:
name: Run e2e tests behind proxy
needs: build
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout repo
@@ -371,7 +376,7 @@ jobs:
run: tar -xzf package.tar.gz
- name: Install release package dependencies
run: cd release && npm install --unsafe-perm --omit=dev
run: cd release && yarn install
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
@@ -383,7 +388,7 @@ jobs:
./test/node_modules/.bin/playwright install
- name: Cache Caddy
uses: actions/cache@v3
uses: actions/cache@v2
id: caddy-cache
with:
path: |
@@ -396,7 +401,7 @@ jobs:
if: steps.caddy-cache.outputs.cache-hit != 'true'
run: |
gh release download v2.5.2 --repo caddyserver/caddy --pattern "caddy_2.5.2_linux_amd64.tar.gz"
mkdir -p ~/.cache/caddy
mkdir -p ~/.cache/caddy
tar -xzf caddy_2.5.2_linux_amd64.tar.gz --directory ~/.cache/caddy
- name: Start Caddy

46
.github/workflows/docs-preview.yaml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Docs preview
on:
pull_request:
branches:
- main
paths:
- "docs/**"
permissions:
actions: none
checks: none
contents: read
deployments: none
issues: none
packages: none
pull-requests: write
repository-projects: none
security-events: none
statuses: none
jobs:
preview:
name: Docs preview
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Set outputs
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Comment Credentials
uses: marocchino/sticky-pull-request-comment@v2
# Only run if PR comes from base repo
# Reason: forks cannot access secrets and this will always fail
if: github.event.pull_request.head.repo.full_name == github.repository
with:
GITHUB_TOKEN: ${{ github.token }}
header: codercom-preview-docs
message: |
✨ code-server docs for PR #${{ github.event.number }} is ready! It will be updated on every commit.
* _Host_: https://coder.com/docs/code-server/${{ steps.vars.outputs.sha_short }}
* _Last deploy status_: success
* _Commit_: ${{ github.event.pull_request.head.sha }}
* _Workflow status_: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

View File

@@ -41,7 +41,7 @@ jobs:
alpine:
name: Test installer on Alpine
runs-on: ubuntu-latest
container: "alpine:3.17"
container: "alpine:3.16"
steps:
- name: Checkout repo
uses: actions/checkout@v3

View File

@@ -4,11 +4,6 @@ on:
# Shows the manual trigger in GitHub UI
# helpful as a back-up in case the GitHub Actions Workflow fails
workflow_dispatch:
inputs:
version:
description: The version to publish (include "v", i.e. "v4.9.1").
type: string
required: true
release:
types: [released]
@@ -29,31 +24,21 @@ jobs:
- name: Checkout code-server
uses: actions/checkout@v3
- name: Install Node.js v16
uses: actions/setup-node@v3
with:
node-version: "16"
cache: "yarn"
- name: Get version
id: version
run: echo "::set-output name=version::$(jq -r .version package.json)"
- name: Download npm package from release artifacts
uses: robinraju/release-downloader@v1.7
uses: robinraju/release-downloader@v1.5
with:
repository: "coder/code-server"
tag: ${{ github.event.inputs.version || github.ref_name }}
tag: v${{ steps.version.outputs.version }}
fileName: "package.tar.gz"
out-file-path: "release-npm-package"
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ github.event.inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Publish npm package and tag with "latest"
run: yarn publish:npm
env:
VERSION: ${{ env.VERSION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_ENVIRONMENT: "production"
@@ -77,18 +62,9 @@ jobs:
git config --global user.name cdrci
git config --global user.email opensource@coder.com
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ github.event.inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Bump code-server homebrew version
env:
VERSION: ${{ env.VERSION }}
HOMEBREW_GITHUB_API_TOKEN: ${{secrets.HOMEBREW_GITHUB_API_TOKEN}}
run: ./ci/steps/brew-bump.sh
aur:
@@ -97,7 +73,6 @@ jobs:
timeout-minutes: 10
env:
GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
steps:
# We need to checkout code-server so we can get the version
- name: Checkout code-server
@@ -106,6 +81,13 @@ jobs:
fetch-depth: 0
path: "./code-server"
- name: Get code-server version
id: version
run: |
pushd code-server
echo "::set-output name=version::$(jq -r .version package.json)"
popd
- name: Checkout code-server-aur repo
uses: actions/checkout@v3
with:
@@ -124,33 +106,22 @@ jobs:
git config --global user.name cdrci
git config --global user.email opensource@coder.com
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ github.event.inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Validate package
uses: hapakaien/archlinux-package-action@v2
env:
VERSION: ${{ env.VERSION }}
with:
pkgver: ${{ env.VERSION }}
pkgver: ${{ steps.version.outputs.version }}
updpkgsums: true
srcinfo: true
- name: Open PR
# We need to git push -u otherwise gh will prompt
# asking where to push the branch.
env:
VERSION: ${{ env.VERSION }}
run: |
git checkout -b update-version-${{ env.VERSION }}
git add .
git commit -m "chore: updating version to ${{ env.VERSION }}"
git checkout -b update-version-${{ steps.version.outputs.version }}
git add .
git commit -m "chore: updating version to ${{ steps.version.outputs.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
gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ steps.version.outputs.version }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR
docker:
runs-on: ubuntu-20.04
steps:
@@ -176,23 +147,19 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ github.event.inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Get version
id: version
run: echo "::set-output name=version::$(jq -r .version package.json)"
- name: Download release artifacts
uses: robinraju/release-downloader@v1.7
uses: robinraju/release-downloader@v1.5
with:
repository: "coder/code-server"
tag: v${{ env.VERSION }}
tag: v${{ steps.version.outputs.version }}
fileName: "*.deb"
out-file-path: "release-packages"
- name: Publish to Docker
run: ./ci/steps/docker-buildx-push.sh
run: yarn publish:docker
env:
VERSION: ${{ env.VERSION }}
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -2,11 +2,6 @@ name: Draft release
on:
workflow_dispatch:
inputs:
version:
description: The version to publish (include "v", i.e. "v4.9.1").
type: string
required: true
permissions:
contents: write # For creating releases.
@@ -26,7 +21,6 @@ jobs:
name: x86-64 Linux build
runs-on: ubuntu-latest
timeout-minutes: 15
needs: npm-version
container: "centos:7"
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -44,13 +38,11 @@ jobs:
run: |
yum install -y epel-release centos-release-scl make
yum install -y devtoolset-9-{make,gcc,gcc-c++} jq rsync python3
# for keytar
yum install -y libsecret-devel
- name: Install nfpm and envsubst
run: |
mkdir -p ~/.local/bin
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.22.2/nfpm_2.22.2_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
curl -sSfL https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst
chmod +x envsubst
mv envsubst ~/.local/bin
@@ -59,10 +51,15 @@ jobs:
- name: Install yarn
run: npm install -g yarn
- name: Download npm package
uses: actions/download-artifact@v3
- name: Download artifacts
uses: dawidd6/action-download-artifact@v2
id: download
with:
name: npm-release-package
branch: ${{ github.ref }}
workflow: build.yaml
workflow_conclusion: completed
check_artifacts: true
name: npm-package
- name: Decompress npm package
run: tar -xzf package.tar.gz
@@ -70,9 +67,19 @@ jobs:
# NOTE: && here is deliberate - GitHub puts each line in its own `.sh`
# file when running inside a docker container.
- name: Build standalone release
run: source scl_source enable devtoolset-9 && npm run release:standalone
run: source scl_source enable devtoolset-9 && yarn release:standalone
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Install test dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
- name: Run integration tests on standalone release
@@ -84,16 +91,7 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
if: success()
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Build packages with nfpm
env:
VERSION: ${{ env.VERSION }}
run: yarn package
- uses: softprops/action-gh-release@v1
@@ -119,13 +117,12 @@ jobs:
# but this means we don't need to maintain a self-hosted runner!
# NOTE@jsjoeio:
# We used to use 18.04 until GitHub browned it out on December 15, 2022
# See here: https://github.com/actions/runner-images/issues/6002
# We used to use 16.04 until GitHub deprecated it on September 20, 2021
# See here: https://github.com/actions/virtual-environments/pull/3862/files
package-linux-cross:
name: Linux cross-compile builds
runs-on: ubuntu-20.04
runs-on: ubuntu-18.04
timeout-minutes: 15
needs: npm-version
strategy:
matrix:
include:
@@ -162,16 +159,19 @@ jobs:
env:
PACKAGE: ${{ format('g++-{0}', matrix.prefix) }}
- name: Download npm package
uses: actions/download-artifact@v3
- name: Download artifacts
uses: dawidd6/action-download-artifact@v2
id: download
with:
name: npm-release-package
branch: ${{ github.ref }}
workflow: build.yaml
workflow_conclusion: completed
check_artifacts: true
name: npm-package
- name: Decompress npm package
run: tar -xzf package.tar.gz
# NOTE@jsjoeio - npm fails here
# so use yarn
- name: Build standalone release
run: yarn release:standalone
@@ -181,16 +181,7 @@ jobs:
tar -xf node-${NODE_VERSION}-linux-${NPM_CONFIG_ARCH}.tar.xz node-${NODE_VERSION}-linux-${NPM_CONFIG_ARCH}/bin/node --strip-components=2
mv ./node ./release-standalone/lib/node
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Build packages with nfpm
env:
VERSION: ${{ env.VERSION }}
run: yarn package ${NPM_CONFIG_ARCH}
- uses: softprops/action-gh-release@v1
@@ -203,7 +194,6 @@ jobs:
name: x86-64 macOS build
runs-on: macos-latest
timeout-minutes: 15
needs: npm-version
steps:
- name: Checkout repo
uses: actions/checkout@v3
@@ -219,33 +209,39 @@ jobs:
curl -sSfL https://github.com/goreleaser/nfpm/releases/download/v2.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Download npm package
uses: actions/download-artifact@v3
- name: Download artifacts
uses: dawidd6/action-download-artifact@v2
id: download
with:
name: npm-release-package
branch: ${{ github.ref }}
workflow: build.yaml
workflow_conclusion: completed
check_artifacts: true
name: npm-package
- name: Decompress npm package
run: tar -xzf package.tar.gz
- name: Build standalone release
run: npm run release:standalone
run: yarn release:standalone
- name: Fetch dependencies from cache
id: cache-node-modules
uses: actions/cache@v3
with:
path: "**/node_modules"
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-build-
- name: Install test dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: SKIP_SUBMODULE_DEPS=1 yarn install
- name: Run native module tests on standalone release
run: yarn test:native
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Build packages with nfpm
env:
VERSION: ${{ env.VERSION }}
run: yarn package
- uses: softprops/action-gh-release@v1
@@ -258,23 +254,6 @@ jobs:
name: Upload npm package
runs-on: ubuntu-latest
timeout-minutes: 15
needs: npm-version
steps:
- name: Download npm package
uses: actions/download-artifact@v3
with:
name: npm-release-package
- uses: softprops/action-gh-release@v1
with:
draft: true
discussion_category_name: "📣 Announcements"
files: ./package.tar.gz
npm-version:
name: Modify package.json version
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Download artifacts
uses: dawidd6/action-download-artifact@v2
@@ -283,38 +262,11 @@ jobs:
branch: ${{ github.ref }}
workflow: build.yaml
workflow_conclusion: completed
check_artifacts: true
name: npm-package
check_artifacts: false
if_no_artifact_found: fail
- name: Decompress npm package
run: tar -xzf package.tar.gz
# NOTE@jsjoeio - we do this so we can strip out the v
# i.e. v4.9.1 -> 4.9.1
- name: Get and set VERSION
run: |
TAG="${{ inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Modify version
env:
VERSION: ${{ env.VERSION }}
run: |
echo "Updating version in root package.json"
npm version --prefix release "$VERSION"
echo "Updating version in lib/vscode/product.json"
tmp=$(mktemp)
jq ".codeServerVersion = \"$VERSION\"" release/lib/vscode/product.json > "$tmp" && mv "$tmp" release/lib/vscode/product.json
# Ensure it has the same permissions as before
chmod 644 release/lib/vscode/product.json
- name: Compress release package
run: tar -czf package.tar.gz release
- name: Upload npm package artifact
uses: actions/upload-artifact@v3
- uses: softprops/action-gh-release@v1
with:
name: npm-release-package
path: ./package.tar.gz
draft: true
discussion_category_name: "📣 Announcements"
files: ./package.tar.gz

View File

@@ -38,7 +38,7 @@ jobs:
name: Run script unit tests
runs-on: ubuntu-latest
# This runs on Alpine to make sure we're testing with actual sh.
container: "alpine:3.17"
container: "alpine:3.16"
steps:
- name: Checkout repo
uses: actions/checkout@v3

View File

@@ -12,8 +12,9 @@ on:
# Runs every Monday morning PST
- cron: "17 15 * * 1"
# Cancel in-progress runs for pull requests when developers push additional
# changes, and serialize builds in branches.
# Cancel in-progress runs for pull requests when developers push
# additional changes, and serialize builds in branches.
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
@@ -64,7 +65,7 @@ jobs:
fetch-depth: 0
- name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee
uses: aquasecurity/trivy-action@e55de85beea5fcec743de6bb6bc56943a0af3c33
with:
scan-type: "fs"
scan-ref: "."

View File

@@ -51,7 +51,7 @@ jobs:
uses: actions/checkout@v3
- name: Run Trivy vulnerability scanner in image mode
uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee
uses: aquasecurity/trivy-action@e55de85beea5fcec743de6bb6bc56943a0af3c33
with:
image-ref: "docker.io/codercom/code-server:latest"
ignore-unfixed: true

View File

@@ -1,7 +1,6 @@
lib/vscode
lib/vscode-reh-web-linux-x64
release-standalone
release-packages
release
helm-chart
test/scripts

View File

@@ -20,84 +20,10 @@ Code v99.99.999
-->
## [4.11.0](https://github.com/coder/code-server/releases/tag/v4.11.0) - 2023-03-16
Code v1.76.1
### Changed
- Updated to Code 1.76.1
## [4.10.1](https://github.com/coder/code-server/releases/tag/v4.10.1) - 2023-03-04
Code v1.75.1
### Security
Added an origin check to web sockets to prevent cross-site hijacking attacks on
users using older or niche browser that do not support SameSite cookies and
attacks across sub-domains that share the same root domain.
The check requires the host header to be set so if you use a reverse proxy
ensure it forwards that information otherwise web sockets will be blocked.
## [4.10.0](https://github.com/coder/code-server/releases/tag/v4.10.0) - 2023-02-15
Code v1.75.1
### Changed
- Updated to Code 1.75.1
### Removed
- Removed `--link` (was deprecated over thirteen months ago in 4.0.1).
## [4.9.1](https://github.com/coder/code-server/releases/tag/v4.9.1) - 2022-12-15
Code v1.73.1
### Changed
- Updated a couple steps in the build and release process to ensure we're using
`npm` and `yarn` consistently depending on the step.
### Fixed
- Fixed an issue with code-server version not displaying in the Help > About window.
- Fixed terminal not loading on macOS clients.
## [4.9.0](https://github.com/coder/code-server/releases/tag/v4.9.0) - 2022-12-06
Code v1.73.1
### Changed
- Upgraded to Code 1.73.1
### Added
- `/security.txt` added as a route with info on our security policy information thanks to @ghuntley
### Fixed
- Installing on majaro images should now work thanks to @MrPeacockNLB for
adding the `--noconfirm` flag in `install.sh`
### Known Issues
- `--cert` on Ubuntu 22.04: OpenSSL v3 is used which breaks `pem` meaning the
`--cert` feature will not work. [Reference](https://github.com/adobe/fetch/pull/318#issuecomment-1306070259)
## [4.8.3](https://github.com/coder/code-server/releases/tag/v4.8.3) - 2022-11-07
Code v1.72.1
### Added
- install script now supports arch-like (i.e. manjaro, endeavourous, etc.)
architectures
### Changed
- Updated text in the Getting Started page.

View File

@@ -14,6 +14,22 @@ main() {
sed -i.bak "1s;^;#!/usr/bin/env node\n;" out/node/entry.js && rm out/node/entry.js.bak
chmod +x out/node/entry.js
fi
# for arch; we do not use OS from lib.sh and get our own.
# lib.sh normalizes macos to darwin - but cloud-agent's binaries do not
source ./ci/lib.sh
OS="$(uname | tr '[:upper:]' '[:lower:]')"
mkdir -p ./lib
if ! [ -f ./lib/coder-cloud-agent ]; then
echo "Downloading the cloud agent..."
set +e
curl -fsSL "https://github.com/coder/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent
chmod +x ./lib/coder-cloud-agent
set -e
fi
}
main "$@"

View File

@@ -63,6 +63,8 @@ EOF
if [ "$KEEP_MODULES" = 1 ]; then
rsync node_modules/ "$RELEASE_PATH/node_modules"
mkdir -p "$RELEASE_PATH/lib"
rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib"
fi
}

View File

@@ -42,12 +42,6 @@ main() {
pushd lib/vscode
if [[ ! ${VERSION-} ]]; then
echo "VERSION not set. Please set before running this script:"
echo "VERSION='0.0.0' yarn build:vscode"
exit 1
fi
# Set the commit Code will embed into the product.json. We need to do this
# since Code tries to get the commit from the `.git` directory which will fail
# as it is a submodule.

View File

@@ -4,7 +4,7 @@ platform: "linux"
version: "v${VERSION}"
section: "devel"
priority: "optional"
maintainer: "Joe Previte <joe@coder.com>"
maintainer: "Anmol Sethi <hi@nhooyr.io>"
description: |
Run VS Code in the browser.
vendor: "Coder"
@@ -22,4 +22,4 @@ contents:
dst: /usr/lib/systemd/user/code-server.service
- src: ./release-standalone/*
dst: /usr/lib/code-server
dst: /usr/lib/code-server/

View File

@@ -1,8 +1,18 @@
#!/usr/bin/env sh
set -eu
# Copied from ../lib.sh except we do not rename Darwin and we do not need to
# detect Alpine.
# Copied from ../lib.sh.
arch() {
cpu="$(uname -m)"
case "$cpu" in
aarch64) cpu=arm64 ;;
x86_64) cpu=amd64 ;;
esac
echo "$cpu"
}
# Copied from ../lib.sh except we do not rename Darwin since the cloud agent
# uses "darwin" in the release names and we do not need to detect Alpine.
os() {
osname=$(uname | tr '[:upper:]' '[:lower:]')
case $osname in
@@ -51,6 +61,7 @@ symlink_bin_script() {
cd "$oldpwd"
}
ARCH="${NPM_CONFIG_ARCH:-$(arch)}"
OS="$(os)"
# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2
@@ -91,6 +102,14 @@ main() {
;;
esac
mkdir -p ./lib
if curl -fsSL "https://github.com/coder/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent; then
chmod +x ./lib/coder-cloud-agent
else
echo "Failed to download cloud agent; --link will not work"
fi
if ! vscode_install; then
echo "You may not have the required dependencies to build the native modules."
echo "Please see https://github.com/coder/code-server/blob/main/docs/npm.md"
@@ -105,18 +124,27 @@ main() {
}
install_with_yarn_or_npm() {
echo "User agent: ${npm_config_user_agent-none}"
# NOTE@edvincent: We want to keep using the package manager that the end-user was using to install the package.
# This also ensures that when *we* run `yarn` in the development process, the yarn.lock file is used.
case "${npm_config_user_agent-}" in
npm*)
# HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases.
# The legacy behavior doesn't complain about pre-releases being used, falling back to that for now.
# See https://github.com//pull/5071
npm install --unsafe-perm --legacy-peer-deps --omit=dev
;;
yarn*)
yarn --production --frozen-lockfile --no-default-rc
if [ -f "yarn.lock" ]; then
yarn --production --frozen-lockfile --no-default-rc
else
echo "yarn.lock file not present, not running in development mode. use npm to install code-server!"
exit 1
fi
;;
npm*)
if [ -f "yarn.lock" ]; then
echo "yarn.lock file present, running in development mode. use yarn to install code-server!"
exit 1
else
# HACK: NPM's use of semver doesn't like resolving some peerDependencies that vscode (upstream) brings in the form of pre-releases.
# The legacy behavior doesn't complain about pre-releases being used, falling back to that for now.
# See https://github.com//pull/5071
npm install --unsafe-perm --legacy-peer-deps --omit=dev
fi
;;
*)
echo "Could not determine which package manager is being used to install code-server"

103
ci/build/release-prep.sh Executable file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Description: This is a script to make the release process easier
# Run it with `yarn release:prep` and it will do the following:
# 1. Check that you have gh installed and that you're signed in
# 2. Update the version of code-server (package.json, docs, etc.)
# 3. Update the code coverage badge in the README
# 4. Open a draft PR using the release_template.md and view in browser
# If you want to perform a dry run of this script run DRY_RUN=1 yarn release:prep
set -euo pipefail
CHECKMARK="\xE2\x9C\x94"
DASH="-"
main() {
if [ "${DRY_RUN-}" = 1 ]; then
echo "Performing a dry run..."
CMD="echo"
else
CMD=''
fi
cd "$(dirname "$0")/../.."
# Check that gh is installed
if ! command -v gh &> /dev/null; then
echo "gh could not be found."
echo "We use this with the release-github-draft.sh and release-github-assets.sh scripts."
echo -e "See docs here: https://github.com/cli/cli#installation"
exit
fi
# Check that they have jq installed
if ! command -v jq &> /dev/null; then
echo "jq could not be found."
echo "We use this to parse the package.json and grab the current version of code-server."
echo -e "See docs here: https://stedolan.github.io/jq/download/"
exit
fi
# Check that they have rg installed
if ! command -v rg &> /dev/null; then
echo "rg could not be found."
echo "We use this when updating files across the codebase."
echo -e "See docs here: https://github.com/BurntSushi/ripgrep#installation"
exit
fi
# Check that they have node installed
if ! command -v node &> /dev/null; then
echo "node could not be found."
echo "That's surprising..."
echo "We use it in this script for getting the package.json version"
echo -e "See docs here: https://nodejs.org/en/download/"
exit
fi
# Check that gh is authenticated
if ! gh auth status -h github.com &> /dev/null; then
echo "gh isn't authenticated to github.com."
echo "This is needed for our scripts that use gh."
echo -e "See docs regarding authentication: https://cli.github.com/manual/gh_auth_login"
exit
fi
# Note: we need to set upstream as well or the gh pr create step will fail
# See: https://github.com/cli/cli/issues/575
CURRENT_BRANCH=$(git branch | grep '\*' | cut -d' ' -f2-)
if [[ -z $(git config "branch.${CURRENT_BRANCH}.remote") ]]; then
echo "Doesn't look like you've pushed this branch to remote"
# Note: we need to set upstream as well or the gh pr create step will fail
# See: https://github.com/cli/cli/issues/575
echo "Please set the upstream and then run the script"
exit 1
fi
# credit to jakwuh for this solution
# https://gist.github.com/DarrenN/8c6a5b969481725a4413#gistcomment-1971123
CODE_SERVER_CURRENT_VERSION=$(node -pe "require('./package.json').version")
# Ask which version we should update to
# In the future, we'll automate this and determine the latest version automatically
echo -e "$DASH Current version: ${CODE_SERVER_CURRENT_VERSION}"
# The $'\n' adds a line break. See: https://stackoverflow.com/a/39581815/3015595
CODE_SERVER_VERSION_TO_UPDATE=$(git rev-parse --abbrev-ref HEAD | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/')
echo -e "$CHECKMARK Version in branch name"
echo -e "$CHECKMARK Updating to: $CODE_SERVER_VERSION_TO_UPDATE"
$CMD rg -g '!yarn.lock' -g '!*.svg' -g '!CHANGELOG.md' -g '!lib/vscode/**' --files-with-matches --fixed-strings "${CODE_SERVER_CURRENT_VERSION}" | $CMD xargs sd "$CODE_SERVER_CURRENT_VERSION" "$CODE_SERVER_VERSION_TO_UPDATE"
$CMD git commit --no-verify -am "chore(release): bump version to $CODE_SERVER_VERSION_TO_UPDATE"
# This runs from the root so that's why we use this path vs. ../../
RELEASE_TEMPLATE_STRING=$(cat ./.github/PULL_REQUEST_TEMPLATE/release_template.md)
echo -e "\nOpening a draft PR on GitHub"
# To read about these flags, visit the docs: https://cli.github.com/manual/gh_pr_create
$CMD gh pr create --base main --title "release: $CODE_SERVER_VERSION_TO_UPDATE" --body "$RELEASE_TEMPLATE_STRING" --reviewer @coder/code-server --repo coder/code-server --draft --assignee "@me"
# Open PR in browser
$CMD gh pr view --web
}
main "$@"

View File

@@ -11,6 +11,22 @@ main() {
make -s out/index.js
popd
# Our code imports from `out` in order to work during development but if you
# have only built for production you will have not have this directory. In
# that case symlink `out` to a production build directory.
if [[ ! -e lib/vscode/out ]]; then
pushd lib
local out=(vscode-reh-web-*)
if [[ -d "${out[0]}" ]]; then
ln -s "../${out[0]}/out" ./vscode/out
else
echo "Could not find lib/vscode/out or lib/vscode-reh-web-*"
echo "Code must be built before running unit tests"
exit 1
fi
popd
fi
# We must keep jest in a sub-directory. See ../../test/package.json for more
# information. We must also run it from the root otherwise coverage will not
# include our source files.

View File

@@ -1,4 +1,4 @@
import { spawn, ChildProcess } from "child_process"
import { spawn, fork, ChildProcess } from "child_process"
import * as path from "path"
import { onLine, OnLineCallback } from "../../src/node/util"
@@ -30,13 +30,12 @@ class Watcher {
// Pass CLI args, save for `node` and the initial script name.
const args = process.argv.slice(2)
this.webServer = spawn("node", [path.join(this.rootPath, "out/node/entry.js"), ...args])
onLine(this.webServer, (line) => console.log("[code-server]", line))
this.webServer = fork(path.join(this.rootPath, "out/node/entry.js"), args)
const { pid } = this.webServer
this.webServer.on("exit", () => console.log("[code-server]", `Web process ${pid} exited`))
this.webServer.on("exit", () => console.log("[Code Server]", `Web process ${pid} exited`))
console.log("\n[code-server]", `Spawned web server process ${pid}`)
console.log("\n[Code Server]", `Spawned web server process ${pid}`)
}
//#endregion
@@ -83,10 +82,10 @@ class Watcher {
private parseVSCodeLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return
console.log("[Code OSS]", originalLine)
console.log("[VS Code]", originalLine)
if (strippedLine.includes("Finished compilation with")) {
console.log("[Code OSS] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)")
console.log("[VS Code] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)")
this.reloadWebServer()
}
}
@@ -94,10 +93,10 @@ class Watcher {
private parseCodeServerLine: OnLineCallback = (strippedLine, originalLine) => {
if (!strippedLine.length) return
console.log("[Compiler][code-server]", originalLine)
console.log("[Compiler][Code Server]", originalLine)
if (strippedLine.includes("Watching for file changes")) {
console.log("[Compiler][code-server]", "Finished compiling!", "(Refresh your web browser ♻️)")
console.log("[Compiler][Code Server]", "Finished compiling!", "(Refresh your web browser ♻️)")
this.reloadWebServer()
}
}

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.7.0
version: 3.3.3
# 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.11.0
appVersion: 4.8.3

View File

@@ -102,7 +102,6 @@ spec:
{{- range .Values.extraSecretMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- range .Values.extraVolumeMounts }}
@@ -115,11 +114,6 @@ spec:
- name: http
containerPort: 8080
protocol: TCP
{{- range .Values.extraPorts }}
- name: {{ .name }}
containerPort: {{ .port }}
protocol: {{ .protocol }}
{{- end }}
livenessProbe:
httpGet:
path: /

View File

@@ -14,12 +14,6 @@ spec:
targetPort: http
protocol: TCP
name: http
{{- range .Values.extraPorts }}
- port: {{ .port }}
targetPort: {{ .port }}
protocol: {{ .protocol }}
name: {{ .name }}
{{- end }}
selector:
app.kubernetes.io/name: {{ include "code-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -6,7 +6,7 @@ replicaCount: 1
image:
repository: codercom/code-server
tag: '4.11.0'
tag: '4.8.3'
pullPolicy: Always
# Specifies one or more secrets to be used when pulling images from a
@@ -179,7 +179,6 @@ extraInitContainers: |
extraSecretMounts: []
# - name: secret-files
# mountPath: /etc/secrets
# subPath: private.key # (optional)
# secretName: code-server-secret-files
# readOnly: true
@@ -197,8 +196,3 @@ extraConfigmapMounts: []
# subPath: certificates.crt # (optional)
# configMap: certs-configmap
# readOnly: true
extraPorts: []
# - name: minecraft
# port: 25565
# protocol: tcp

View File

@@ -9,6 +9,10 @@ popd() {
builtin popd > /dev/null
}
pkg_json_version() {
jq -r .version package.json
}
vscode_version() {
jq -r .version lib/vscode/package.json
}
@@ -44,6 +48,8 @@ rsync() {
command rsync -a --del "$@"
}
VERSION="$(pkg_json_version)"
export VERSION
ARCH="$(arch)"
export ARCH
OS=$(os)

View File

@@ -3,8 +3,9 @@ set -euo pipefail
main() {
cd "$(dirname "$0")/../.."
# NOTE@jsjoeio - this script assumes VERSION exists as an
# environment variable.
# ci/lib.sh sets VERSION so it's available to ci/release-image/docker-bake.hcl
# to push the VERSION tag.
source ./ci/lib.sh
# NOTE@jsjoeio - this script assumes that you've downloaded
# the release-packages artifact to ./release-packages before

View File

@@ -19,6 +19,12 @@ main() {
# This is because npm won't publish your package unless it's a new version.
# i.e. for development, we bump the version to <current version>-<pr number>-<commit sha>
# example: "version": "4.0.1-4769-ad7b23cfe6ffd72914e34781ef7721b129a23040"
# We need the current package.json VERSION
if ! is_env_var_set "VERSION"; then
echo "VERSION is not set. Cannot publish to npm without VERSION."
exit 1
fi
# We use this to grab the PR_NUMBER
if ! is_env_var_set "GITHUB_REF"; then
echo "GITHUB_REF is not set. Are you running this locally? We rely on values provided by GitHub."
@@ -96,7 +102,6 @@ main() {
# This means the npm version will be tagged with "beta"
# and installed when a user runs `yarn install code-server@beta`
NPM_TAG="beta"
PACKAGE_NAME="@coder/code-server-pr"
fi
if [[ "$NPM_ENVIRONMENT" == "development" ]]; then

View File

@@ -11,10 +11,9 @@
- [Version updates to Code](#version-updates-to-code)
- [Patching Code](#patching-code)
- [Build](#build)
- [Creating a Standalone Release](#creating-a-standalone-release)
- [Troubleshooting](#troubleshooting)
- [I see "Forbidden access" when I load code-server in the browser](#i-see-forbidden-access-when-i-load-code-server-in-the-browser)
- ["Can only have one anonymous define call per script"](#can-only-have-one-anonymous-define-call-per-script)
- ["Can only have one anonymous define call per script"](#can-only-have-one-anonymous-define-call-per-script)
- [Help](#help)
- [Test](#test)
- [Unit tests](#unit-tests)
@@ -171,22 +170,6 @@ yarn package
> If you need your builds to support older distros, run the build commands
> inside a Docker container with all the build requirements installed.
#### Creating a Standalone Release
Part of the build process involves creating standalone releases. At the time of
writing, we do this for the following platforms/architectures:
- Linux amd64 (.tar.gz, .deb, and .rpm)
- Linux arm64 (.tar.gz, .deb, and .rpm)
- Linux arm7l (.tar.gz)
- Linux armhf.deb
- Linux armhf.rpm
- macOS amd64 (Intel-based)
Currently, these are compiled in CI using the `yarn release-standalone` command
in the `release.yaml` workflow. We then upload them to the draft release and
distribute via GitHub Releases.
### Troubleshooting
#### I see "Forbidden access" when I load code-server in the browser
@@ -195,7 +178,7 @@ This means your patches didn't apply correctly. We have a patch to remove the au
Try popping off the patches with `quilt pop -a` and reapplying with `quilt push -a`.
#### "Can only have one anonymous define call per script"
### "Can only have one anonymous define call per script"
Code might be trying to use a dev or prod HTML in the wrong context. You can try re-running code-server and setting `VSCODE_DEV=1`.

View File

@@ -26,7 +26,6 @@
- [Is multi-tenancy possible?](#is-multi-tenancy-possible)
- [Can I use Docker in a code-server container?](#can-i-use-docker-in-a-code-server-container)
- [How do I disable telemetry?](#how-do-i-disable-telemetry)
- [What's the difference between code-server and Coder?](#whats-the-difference-between-code-server-and-coder)
- [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia)
- [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server)
- [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces)
@@ -34,7 +33,6 @@
- [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server)
- [How do I change the port?](#how-do-i-change-the-port)
- [How do I hide the coder/coder promotion in Help: Getting Started?](#how-do-i-hide-the-codercoder-promotion-in-help-getting-started)
- [How do I disable file download?](#how-do-i-disable-file-download)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- prettier-ignore-end -->
@@ -88,12 +86,6 @@ app (PWA):
1. Start the editor
2. Click the **plus** icon in the URL toolbar to install the PWA
If you use Firefox, you can use the appropriate extension to install PWA.
1. Go to the installation [website](https://addons.mozilla.org/en-US/firefox/addon/pwas-for-firefox/) of the add-on
2. Add the add-on to Firefox
3. Follow the os-specific instructions on how to install the runtime counterpart
For other browsers, you'll have to remap keybindings for shortcuts to work.
## Why can't code-server use Microsoft's extension marketplace?
@@ -370,15 +362,6 @@ Use the `--disable-telemetry` flag to disable telemetry.
> We use the data collected only to improve code-server.
## What's the difference between code-server and Coder?
code-server and Coder are both applications that can be installed on any
machine. The main difference is who they serve. Out of the box, code-server is
simply VS Code in the browser while Coder is a tool for provisioning remote
development environments via Terraform.
code-server was built for individuals while Coder was built for teams. In Coder, you create Workspaces which can have applications like code-server. If you're looking for a team solution, you should reach for [Coder](https://github.com/coder/coder).
## What's the difference between code-server and Theia?
At a high level, code-server is a patched fork of VS Code that runs in the
@@ -396,13 +379,19 @@ Theia doesn't allow you to reuse your existing VS Code config.
## What's the difference between code-server and OpenVSCode-Server?
code-server and OpenVSCode-Server both allow you to access VS Code via a
browser. OpenVSCode-Server is a direct fork of VS Code with changes comitted
directly while code-server pulls VS Code in via a submodule and makes changes
via patch files.
browser. The two projects also use their own [forks of VS Code](https://github.com/coder/vscode) to
leverage modern VS Code APIs and stay up to date with the upsteam version.
However, OpenVSCode-Server is scoped at only making VS Code available as-is in
the web browser. code-server contains additional changes to make the self-hosted
experience better (see the next section for details).
However, OpenVSCode-Server is scoped at only making VS Code available in the web browser.
code-server includes some other features:
- password auth
- proxy web ports
- certificate support
- plugin API
- settings sync (coming soon)
For more details, see [this discussion post](https://github.com/coder/code-server/discussions/4267#discussioncomment-1411583).
## What's the difference between code-server and GitHub Codespaces?
@@ -410,24 +399,8 @@ Both code-server and GitHub Codespaces allow you to access VS Code via a
browser. GitHub Codespaces, however, is a closed-source, paid service offered by
GitHub and Microsoft.
On the other hand, code-server is self-hosted, free, open-source, and can be run
on any machine with few limitations.
Specific changes include:
- Password authentication
- The ability to host at sub-paths
- Self-contained web views that do not call out to Microsoft's servers
- The ability to use your own marketplace and collect your own telemetry
- Built-in proxy for accessing ports on the remote machine integrated into
VS Code's ports panel
- Wrapper process that spawns VS Code on-demand and has a separate CLI
- Notification when updates are available
- [Some other things](https://github.com/coder/code-server/tree/main/patches)
Some of these changes appear very unlikely to ever be adopted by Microsoft.
Some may make their way upstream, further closing the gap, but at the moment it
looks like there will always be some subtle differences.
On the other hand, code-server is self-hosted, free, open-source, and
can be run on any machine with few limitations.
## Does code-server have any security login validation?
@@ -452,7 +425,3 @@ There are two ways to change the port on which code-server runs:
You can pass the flag `--disable-getting-started-override` to `code-server` or
you can set the environment variable `CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or
`CS_DISABLE_GETTING_STARTED_OVERRIDE=true`.
## How do I disable file download?
You can pass the flag `--disable-file-downloads` to `code-server`

View File

@@ -142,28 +142,24 @@ changelog](https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS).
### Publishing a release
1. Go to GitHub Actions > Draft release > Run workflow on the commit you want to
release. Make sure CI has finished the build workflow on that commit or this
will fail.
2. CI will automatically grab the build artifact on that commit, inject the
version into the `package.json`, put together platform-specific packages, and
upload those packages to a draft release.
3. Summarize the major changes in the `CHANGELOG.md`.
4. Copy the relevant changelog section to the release then publish it.
5. CI will automatically publish the NPM package, Docker image, and update
Homebrew using the published release assets.
6. Bump the chart version in `Chart.yaml` and merge in the changelog updates.
1. Create a new branch called `release/v0.0.0` (replace 0s with actual version aka v4.5.0)
1. Run `yarn release:prep`
1. Bump chart version in `Chart.yaml`.
1. Summarize the major changes in the `CHANGELOG.md`
1. Download CI artifacts and make sure code-server works locally.
1. Merge PR and wait for CI build on `main` to finish.
1. Go to GitHub Actions > Draft release > Run workflow off `main`. CI will automatically upload the artifacts to the release.
1. Add the release notes from the `CHANGELOG.md` and publish release. CI will automatically grab the
artifacts, publish the NPM package from `npm-package`, and publish the Docker
Hub image from `release-images`.
#### Release Candidates
We prefer to do release candidates so the community can test things before a
full-blown release. To do this follow the same steps as above but:
We prefer to do release candidates so the community can test things before a full-blown release. To do this follow the same steps as above but:
1. Add a `-rc.<number>` suffix to the version.
2. When you publish the release select "pre-release". CI will not automatically
publish pre-releases.
3. Do not update the chart version or merge in the changelog until the final
release.
1. Only bump version in `package.json`
1. use `0.0.0-rc.0`
1. When you publish the release, select "pre-release"
#### AUR

View File

@@ -16,7 +16,7 @@ access it in the browser.
## Requirements
See [requirements](https://coder.com/docs/code-server/latest/requirements) for minimum specs, as well as instructions
See [requirements](requirements.md) for minimum specs, as well as instructions
on how to set up a Google VM on which you can install code-server.
**TL;DR:** Linux machine with WebSockets enabled, 1 GB RAM, and 2 vCPUs

View File

@@ -1,37 +0,0 @@
# Coder
To install and run code-server in a Coder workspace, we suggest using the `install.sh`
script in your template like so:
```terraform
resource "coder_agent" "dev" {
arch = "amd64"
os = "linux"
startup_script = <<EOF
#!/bin/sh
set -x
# install and start code-server
curl -fsSL https://code-server.dev/install.sh | sh -s -- --version 4.8.3
code-server --auth none --port 13337 &
EOF
}
resource "coder_app" "code-server" {
agent_id = coder_agent.dev.id
slug = "code-server"
display_name = "code-server"
url = "http://localhost:13337/"
icon = "/icon/code.svg"
subdomain = false
share = "owner"
healthcheck {
url = "http://localhost:13337/healthz"
interval = 3
threshold = 10
}
}
```
If you run into issues, ask for help on the `coder/coder` [Discussions
here](https://github.com/coder/coder/discussions).

View File

@@ -14,7 +14,6 @@
- [Accessing web services](#accessing-web-services)
- [Using a subdomain](#using-a-subdomain)
- [Using a subpath](#using-a-subpath)
- [Using your own proxy](#using-your-own-proxy)
- [Stripping `/proxy/<port>` from the request path](#stripping-proxyport-from-the-request-path)
- [Proxying to create a React app](#proxying-to-create-a-react-app)
- [Proxying to a Vue app](#proxying-to-a-vue-app)
@@ -317,32 +316,12 @@ To set your domain, start code-server with the `--proxy-domain` flag:
code-server --proxy-domain <domain>
```
For instance, if you have code-server exposed on `domain.tld` and a Python
server running on port 8080 of the same machine code-server is running on, you
could run code-server with `--proxy-domain domain.tld` and access the Python
server via `8080.domain.tld`.
Note that this uses the host header, so ensure your reverse proxy (if you're
using one) forwards that information.
Now you can browse to `<port>.<domain>`. Note that this uses the host header, so
ensure your reverse proxy (if you're using one) forwards that information.
### Using a subpath
Simply browse to `/proxy/<port>/`. For instance, if you have code-server
exposed on `domain.tld` and a Python server running on port 8080 of the same
machine code-server is running on, you could access the Python server via
`domain.tld/proxy/8000`.
### Using your own proxy
You can make extensions and the ports panel use your own proxy by setting
`VSCODE_PROXY_URI`. For example if you set
`VSCODE_PROXY_URI=https://{{port}}.kyle.dev` when an application is detected
running on port 3000 of the same machine code-server is running on the ports
panel will create a link to https://3000.kyle.dev instead of pointing to the
built-in subpath-based proxy.
Note: relative paths are also supported i.e.
`VSCODE_PROXY_URI=./proxy/{{port}}`
Simply browse to `/proxy/<port>/`.
### Stripping `/proxy/<port>` from the request path

View File

@@ -59,7 +59,6 @@ following flags:
- `--prefix=/usr/local`: install a standalone release archive system-wide.
- `--version=X.X.X`: install version `X.X.X` instead of latest version.
- `--help`: see usage docs.
- `--edge`: install the latest edge version (i.e. pre-release)
When done, the install script prints out instructions for running and starting
code-server.
@@ -297,9 +296,9 @@ 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).
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).
> Note: You will also need to [build coder/cloud-agent manually](https://github.com/coder/cloud-agent/issues/17) if you would like to use `code-server --link` on Windows.
## Raspberry Pi

11
docs/link.md Normal file
View File

@@ -0,0 +1,11 @@
# code-server --link
> Note: This feature is no longer recommended due to instability. Stay tuned for a revised version.
Run code-server with the flag `--link` and you'll get TLS, authentication, and a dedicated URL
for accessing your IDE out of the box.
```console
$ code-server --link
Proxying code-server, you can access your IDE at https://example.coder.co
```

View File

@@ -38,9 +38,9 @@
"path": "./guide.md",
"children": [
{
"title": "Coder",
"description": "How to run code-server in Coder",
"path": "./coder.md"
"title": "--link",
"description": "How to run code-server --link",
"path": "./link.md"
},
{
"title": "iPad",

View File

@@ -97,7 +97,7 @@ code-server
# Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
```
A `postinstall.sh` script will attempt to run. Select your terminal (e.g., Git bash) as the default shell for npm run-scripts. If an additional dialog does not appear, run the install command again.
A `postinstall.sh` script will attempt to run. Select your terminal (e.g., Git bash) as the default application for `.sh` files. If an additional dialog does not appear, run the install command again.
If the `code-server` command is not found, you'll need to [add a directory to your PATH](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/). To find the directory, use the following command:

View File

@@ -12,6 +12,7 @@
- [Create a new user](#create-a-new-user)
- [Install Go](#install-go)
- [Install Python](#install-python)
- [Working with PRoot](#working-with-proot)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- prettier-ignore-end -->
@@ -19,9 +20,53 @@
## Install
1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**.
2. Run `pkg install tur-repo`
3. Run `pkg install code-server`
4. You can now start code server by simply running `code-server`.
2. Install Debian by running the following:
- Run `termux-setup-storage` to allow storage access, or else code-server won't be able to read from `/sdcard`.\
> The following command is from [proot-distro](https://github.com/termux/proot-distro), but you can also use [Andronix](https://andronix.app/).
> After Debian is installed the `~ $` will change to `root@localhost`.
```bash
pkg update -y && pkg install proot-distro -y && proot-distro install debian && proot-distro login debian
```
3. Run the following commands to setup Debian:
```bash
apt update && apt upgrade -y && apt-get install sudo vim git -y
```
4. Install [NVM](https://github.com/nvm-sh/nvm#install--update-script) by following the install guide in the README, just a curl/wget command.
5. Set up NVM for multi-user. After installing NVM it automatically adds the necessary commands for it to work, but it will only work if you are logged in as root:
- Copy the lines NVM asks you to run after running the install script.
- Run `nano /root/.bashrc` and comment out those lines by adding a `#` at the start.
- Run `nano /etc/profile` and paste those lines at the end of the file. Make sure to replace `$HOME` with `/root` on the first line.
- Now run `exit`
- Start Debian again `proot-distro login debian`
6. After following the instructions and setting up NVM you can now install the [required node version](https://coder.com/docs/code-server/latest/npm#nodejs-version) by running:
```bash
nvm install v<major_version_here>
```
7. To install `code-server` run the following:
> To check the install process (Will not actually install code-server)
> If it all looks good, you can install code-server by running the second command
```bash
curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run
```
```bash
curl -fsSL https://code-server.dev/install.sh | sh
```
8. You can now start code server by simply running `code-server`.
> Consider using a new user instead of root, read [here](https://www.howtogeek.com/124950/htg-explains-why-you-shouldnt-log-into-your-linux-system-as-root/) why using root is not recommended.\
> Learn how to add a user [here](#create-a-new-user).
## NPM Installation
@@ -157,3 +202,35 @@ eval "$(pyenv virtualenv-init -)"
7. Run `touch /root/.pyenv/version && echo "your_version_here" > /root/.pyenv/version`
8. (You may have to start Debian again) Run `python3 -V` to verify if PATH works or not.
> If `python3` doesn't work but pyenv says that the install was successful in step 6 then try running `$PYENV_ROOT/versions/your_version/bin/python3`.
### Working with PRoot
Debian PRoot Distro Dev Environment
- Since Node and code-server are installed in the Debian PRoot distro, your `~/.ssh/` configuration, `~/.bashrc`, git, npm packages, etc. should be setup in PRoot as well.
- The terminal accessible in code-server will bring up the filesystem and `~/.bashrc` in the Debian PRoot distro.
Accessing files in the Debian PRoot Distro
- The `/data/data/com.termux/files/home` directory in PRoot accesses the termux home directory (`~`)
- The `/sdcard` directory in PRoot accesses the Android storage directory, though there are [known issues with git and files in the `/sdcard` path](#git-wont-work-in-sdcard)
Accessing the Debian PRoot distro/Starting code-server
- Run the following command to access the Debian PRoot distro, from the termux shell:
```bash
proot-distro login debian
```
- Run the following command to start code-server directly in the Debian PRoot distro, from the termux shell:
```bash
proot-distro login debian -- code-server
```
- If you [created a new user](#create-a-new-user), you'll need to insert the `--user <username>` option between `login` and `debian` in the commands above to run as the user instead of root in PRoot.
Additional information on PRoot and Termux
- Additional information on using your Debian PRoot Distro can be [found here](https://github.com/termux/proot-distro#functionality-overview).

View File

@@ -46,7 +46,7 @@ Usage:
Sets the prefix used by standalone release archives. Defaults to ~/.local
The release is unarchived into ~/.local/lib/code-server-X.X.X
and the binary symlinked into ~/.local/bin/code-server
To install system wide pass --prefix=/usr/local
To install system wide pass ---prefix=/usr/local
--rsh <bin>
Specifies the remote shell for remote installation. Defaults to ssh.
@@ -387,7 +387,7 @@ install_aur() {
if [ ! "${DRY_RUN-}" ]; then
cd "$CACHE_DIR/code-server-aur"
fi
sh_c makepkg -si --noconfirm
sh_c makepkg -si
echo_systemd_postinstall AUR
}

View File

@@ -1,7 +1,7 @@
{
"name": "code-server",
"license": "MIT",
"version": "0.0.0",
"version": "4.8.3",
"description": "Run VS Code on a remote server.",
"homepage": "https://github.com/coder/code-server",
"bugs": {
@@ -61,7 +61,7 @@
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "2.8.0",
"prettier": "2.7.1",
"prettier-plugin-sh": "^0.12.8",
"ts-node": "^10.0.0",
"typescript": "^4.6.2"
@@ -83,21 +83,19 @@
"nanoid": "^3.1.31",
"minimist": "npm:minimist-lite@2.2.1",
"glob-parent": "^6.0.1",
"@types/node": "^16.0.0",
"qs": "^6.7.3"
"@types/node": "^16.0.0"
},
"dependencies": {
"@coder/logger": "^3.0.0",
"argon2": "0.30.3",
"argon2": "0.29.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"env-paths": "^2.2.0",
"express": "5.0.0-alpha.8",
"http-proxy": "^1.18.0",
"httpolyglot": "^0.1.2",
"i18next": "^22.4.6",
"js-yaml": "^4.0.0",
"limiter": "^2.1.0",
"limiter": "^1.1.5",
"pem": "^1.14.2",
"proxy-agent": "^5.0.0",
"qs": "6.11.0",
@@ -118,8 +116,7 @@
"ide",
"coder",
"vscode-remote",
"browser-ide",
"remote-development"
"browser-ide"
],
"engines": {
"node": "16"

View File

@@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/base/common/network.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/network.ts
+++ code-server/lib/vscode/src/vs/base/common/network.ts
@@ -166,7 +166,9 @@ class RemoteAuthoritiesImpl {
@@ -162,7 +162,9 @@ class RemoteAuthoritiesImpl {
return URI.from({
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
authority: `${host}:${port}`,
@@ -119,7 +119,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
@@ -267,12 +267,11 @@ export class WebClientServer {
return void res.end();
return res.end();
}
- const getFirstHeader = (headerName: string) => {
@@ -174,9 +174,9 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
+ `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=';`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
'child-src \'self\';',
`frame-src 'self' https://*.vscode-cdn.net data:;`,
'worker-src \'self\' data: blob:;',
'worker-src \'self\' data:;',
@@ -417,3 +421,70 @@ export class WebClientServer {
return void res.end(data);
return res.end(data);
}
}
+
@@ -250,7 +250,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -56,6 +56,7 @@ export type ExtensionVirtualWorkspaceSup
@@ -32,6 +32,7 @@ export type ExtensionVirtualWorkspaceSup
export interface IProductConfiguration {
readonly codeServerVersion?: string
@@ -279,10 +279,10 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
// Create workbench
create(document.body, {
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
+++ code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
@@ -16,7 +16,6 @@ import { getServiceMachineId } from 'vs/
import { IStorageService } from 'vs/platform/storage/common/storage';
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
@@ -290,7 +290,7 @@ Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/ext
-import { RemoteAuthorities } from 'vs/base/common/network';
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -75,7 +74,7 @@ export abstract class AbstractExtensionR
public getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string }, path?: string): URI | undefined {
if (this._extensionGalleryResourceUrlTemplate) {

View File

@@ -8,7 +8,7 @@ To test:
2. Open code-server
3. Open terminal
4. Open another code-server window
5. Run node ./out/node/entry.js with a file or directory argument
5. Run code-server with a file or directory argument
The file or directory should only open from the instance attached to that
terminal.

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
@@ -243,6 +243,10 @@ export class Extension implements IExten
@@ -237,6 +237,10 @@ export class Extension implements IExten
if (this.type === ExtensionType.System && this.productService.quality === 'stable') {
return false;
}
@@ -18,3 +18,14 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
if (!this.local.preRelease && this.gallery.properties.isPreReleaseVersion) {
return false;
}
@@ -1234,6 +1238,10 @@ export class ExtensionsWorkbenchService
// Skip if check updates only for builtin extensions and current extension is not builtin.
continue;
}
+ if (installed.type !== ExtensionType.User) {
+ // Never update builtin extensions.
+ continue;
+ }
if (installed.isBuiltin && (!installed.local?.identifier.uuid || (!isWeb && this.productService.quality === 'stable'))) {
// Skip checking updates for a builtin extension if it does not has Marketplace identifier or the current product is VS Code Desktop stable.
continue;

View File

@@ -12,7 +12,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -260,6 +260,11 @@ export interface IWorkbenchConstructionO
@@ -271,6 +271,11 @@ export interface IWorkbenchConstructionO
*/
readonly userDataPath?: string
@@ -23,12 +23,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -32,6 +32,11 @@ export interface IBrowserWorkbenchEnviro
@@ -31,6 +31,11 @@ export interface IBrowserWorkbenchEnviro
* Options used to configure the workbench.
*/
readonly options?: IWorkbenchConstructionOptions;
@@ -40,7 +40,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
}
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -101,6 +106,13 @@ export class BrowserWorkbenchEnvironment
@@ -62,6 +67,13 @@ export class BrowserWorkbenchEnvironment
return this.options.userDataPath;
}
@@ -92,10 +92,10 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@@ -7,12 +7,11 @@ import { Event } from 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -121,7 +121,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
@IProductService private readonly productService: IProductService,
@IEditorService private readonly editorService: IEditorService,
@IEditorResolverService private readonly editorResolverService: IEditorResolverService,
@@ -205,6 +205,9 @@ export class WorkbenchContextKeysHandler
@@ -202,6 +202,9 @@ export class WorkbenchContextKeysHandler
this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
@@ -135,16 +135,16 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts
@@ -20,7 +20,7 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID,
@@ -22,7 +22,7 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID,
import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService';
import { Schemas } from 'vs/base/common/network';
-import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/contextkeys';
+import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
-import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey } from 'vs/workbench/common/contextkeys';
+import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ThemeIcon } from 'vs/base/common/themables';
@@ -485,13 +485,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
@@ -477,13 +477,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
id: DOWNLOAD_COMMAND_ID,
title: DOWNLOAD_LABEL
},
@@ -172,9 +172,9 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
@@ -35,6 +35,8 @@ export const HasWebFileSystemAccess = ne
@@ -32,6 +32,8 @@ export const IsFullscreenContext = new R
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
+export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true);
+

View File

@@ -14,14 +14,12 @@ We can remove this once upstream supports all language packs.
one but is worse because it does not handle non-existent or empty files.
7. Replace some caching and Node requires because code-server does not restart
when changing the language unlike native Code.
8. Make language extensions installable like normal rather than using the
special set/clear language actions.
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
@@ -233,6 +233,9 @@ export async function setupServerService
@@ -209,6 +209,9 @@ export async function setupServerService
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel);
@@ -44,7 +42,7 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
export const LANGUAGE_DEFAULT = 'en';
let _isWindows = false;
@@ -90,17 +88,21 @@ if (typeof navigator === 'object' && !is
@@ -83,17 +81,19 @@ if (typeof navigator === 'object' && !is
_isMobile = _userAgent?.indexOf('Mobi') >= 0;
_isWeb = true;
@@ -58,18 +56,16 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
-
- _locale = configuredLocale || LANGUAGE_DEFAULT;
+ _locale = LANGUAGE_DEFAULT;
_language = _locale;
_platformLocale = navigator.language;
+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
+ const rawNlsConfig = el && el.getAttribute('data-settings');
+ if (rawNlsConfig) {
+ try {
+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
+ const resolved = nlsConfig.availableLanguages['*'];
+ _locale = nlsConfig.locale;
+ _platformLocale = nlsConfig.osLocale;
+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
+ _translationsConfigFile = nlsConfig._translationsConfigFile;
+ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT;
+ } catch (error) { /* Oh well. */ }
+ }
}
@@ -129,7 +125,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentServ
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts
+++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
@@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvi
@@ -105,7 +105,7 @@ export abstract class AbstractNativeEnvi
return URI.file(join(vscodePortable, 'argv.json'));
}
@@ -142,7 +138,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
+++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
@@ -32,6 +32,12 @@ export function getNLSConfiguration(lang
@@ -30,6 +30,12 @@ export function getNLSConfiguration(lang
if (InternalNLSConfiguration.is(value)) {
value._languagePackSupport = true;
}
@@ -155,7 +151,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
return value;
});
_cache.set(key, result);
@@ -46,3 +52,43 @@ export namespace InternalNLSConfiguratio
@@ -44,3 +50,43 @@ export namespace InternalNLSConfiguratio
return candidate && typeof candidate._languagePackId === 'string';
}
}
@@ -252,16 +248,7 @@ Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
@@ -52,7 +52,7 @@ import 'vs/workbench/services/dialogs/br
import 'vs/workbench/services/host/browser/browserHostService';
import 'vs/workbench/services/lifecycle/browser/lifecycleService';
import 'vs/workbench/services/clipboard/browser/clipboardService';
-import 'vs/workbench/services/localization/browser/localeService';
+import 'vs/workbench/services/localization/electron-sandbox/localeService';
import 'vs/workbench/services/path/browser/pathService';
import 'vs/workbench/services/themes/browser/browserHostColorSchemeService';
import 'vs/workbench/services/encryption/browser/encryptionService';
@@ -119,8 +119,9 @@ import 'vs/workbench/contrib/logs/browse
@@ -123,8 +123,9 @@ import 'vs/workbench/contrib/logs/browse
// Explorer
import 'vs/workbench/contrib/files/browser/files.web.contribution';
@@ -277,45 +264,37 @@ Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePack
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
+++ code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
@@ -5,18 +5,24 @@
@@ -4,10 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { URI } from 'vs/base/common/uri';
import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader';
-import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
+import { ILanguagePackItem, ILanguagePackService, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
import { ILogService } from 'vs/platform/log/common/log';
+import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
export class WebLanguagePacksService extends LanguagePackBaseService {
- // Web doesn't have a concept of language packs, so we just return an empty array
+ private readonly languagePackService: ILanguagePackService;
+
constructor(
+ constructor(
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
@IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@ILogService private readonly logService: ILogService
) {
super(extensionGalleryService);
+ this.languagePackService = ProxyChannel.toService<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks'))
}
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
@@ -72,6 +78,6 @@ export class WebLanguagePacksService ext
// Web doesn't have a concept of language packs, so we just return an empty array
+ @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService
+ ) {
+ super(extensionGalleryService)
+ this.languagePackService = ProxyChannel.toService<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks'));
+ }
+
getInstalledLanguages(): Promise<ILanguagePackItem[]> {
- return Promise.resolve([]);
+ return this.languagePackService.getInstalledLanguages()
}
}
Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
@@ -51,7 +51,8 @@ class NativeLocaleService implements ILo
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts
@@ -41,7 +41,8 @@ export class NativeLocaleService impleme
@IProductService private readonly productService: IProductService
) { }
@@ -325,7 +304,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sa
try {
const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' });
@@ -78,9 +79,6 @@ class NativeLocaleService implements ILo
@@ -68,9 +69,6 @@ export class NativeLocaleService impleme
}
private async writeLocaleValue(locale: string | undefined): Promise<boolean> {
@@ -335,70 +314,3 @@ Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sa
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
return true;
}
Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -335,9 +335,6 @@ export abstract class AbstractInstallAct
if (this.extension.isBuiltin) {
return;
}
- if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) {
this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion;
this.updateLabel();
@@ -715,7 +712,7 @@ export abstract class InstallInOtherServ
}
if (isLanguagePackExtension(this.extension.local.manifest)) {
- return true;
+ return false;
}
// Prefers to run on UI
@@ -1803,17 +1800,6 @@ export class SetLanguageAction extends E
update(): void {
this.enabled = false;
this.class = SetLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language === getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = SetLanguageAction.EnabledClass;
}
override async run(): Promise<any> {
@@ -1830,7 +1816,6 @@ export class ClearLanguageAction extends
private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`;
constructor(
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILocaleService private readonly localeService: ILocaleService,
) {
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
@@ -1840,17 +1825,6 @@ export class ClearLanguageAction extends
update(): void {
this.enabled = false;
this.class = ClearLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = ClearLanguageAction.EnabledClass;
}
override async run(): Promise<any> {

24
patches/exec-argv.diff Normal file
View File

@@ -0,0 +1,24 @@
Preserve process.execArgv
This ensures flags like `--prof` are passed down to the code-server process so
we can profile everything.
To test this:
1. run `./lib/node --prof .`
2. in another terminal, run `ps -ejww`
You should see `--prof` next to every code-server process.
Index: code-server/lib/vscode/src/vs/server/node/extensionHostConnection.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/extensionHostConnection.ts
+++ code-server/lib/vscode/src/vs/server/node/extensionHostConnection.ts
@@ -228,7 +228,7 @@ export class ExtensionHostConnection {
public async start(startParams: IRemoteExtensionHostStartParams): Promise<void> {
try {
- let execArgv: string[] = [];
+ let execArgv: string[] = process.execArgv ? process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)) : [];
if (startParams.port && !(<any>process).pkg) {
execArgv = [`--inspect${startParams.break ? '-brk' : ''}=${startParams.port}`];
}

View File

@@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts
@@ -60,7 +60,7 @@ import { GettingStartedIndexList } from
@@ -62,7 +62,7 @@ import { GettingStartedIndexList } from
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils';
@@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions';
import { Toggle } from 'vs/base/browser/ui/toggle/toggle';
@@ -796,6 +796,72 @@ export class GettingStartedPage extends
@@ -758,6 +758,72 @@ export class GettingStartedPage extends
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
);
@@ -55,7 +55,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ 'font-size: 13px',
+ 'color: #dcdee2',
+ ].join(';'),
+ }, 'Provision software development environments on your infrastructure with Coder.'),
+ }, 'Provision remote development environments on your infrastructure with Coder.'),
+ $('p', {
+ style: [
+ 'margin-top: 8px',
@@ -72,7 +72,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ 'align-items: center',
+ ].join(';'),
+ }, 'Get started ', $('span', {
+ class: ThemeIcon.asClassName(Codicon.arrowRight),
+ class: Codicon.arrowRight.classNames,
+ style: [
+ 'color: white',
+ 'margin-left: 8px',
@@ -87,56 +87,16 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ ),
+ ),
+ );
+ }
+ }
+
const leftColumn = $('.categories-column.categories-column-left', {},);
const rightColumn = $('.categories-column.categories-column-right', {},);
@@ -814,13 +880,23 @@ export class GettingStartedPage extends
@@ -775,13 +841,23 @@ export class GettingStartedPage extends
const layoutLists = () => {
if (gettingStartedList.itemCount) {
this.container.classList.remove('noWalkthroughs');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ }
}
else {
this.container.classList.add('noWalkthroughs');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, featuredExtensionList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement());
+ }
}
setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50);
};
@@ -828,13 +904,23 @@ export class GettingStartedPage extends
const layoutFeaturedExtension = () => {
if (featuredExtensionList.itemCount) {
this.container.classList.remove('noExtensions');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ reset(rightColumn, gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, featuredExtensionList.getDomElement(), gettingStartedList.getDomElement());
+ }
}
else {
this.container.classList.add('noExtensions');
- reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
- reset(rightColumn, gettingStartedList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement(), gettingStartedList.getDomElement());
@@ -145,9 +105,20 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
+ reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
+ reset(rightColumn, gettingStartedList.getDomElement());
+ }
+
recentList.setLimit(5);
}
else {
this.container.classList.add('noWalkthroughs');
- reset(leftColumn, startList.getDomElement());
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
+ reset(leftColumn, startList.getDomElement(), gettingStartedCoder);
+ } else {
+ reset(leftColumn, startList.getDomElement());
+ }
reset(rightColumn, recentList.getDomElement());
recentList.setLimit(10);
}
setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50);
};
Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css
@@ -172,7 +143,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -265,6 +265,11 @@ export interface IWorkbenchConstructionO
@@ -276,6 +276,11 @@ export interface IWorkbenchConstructionO
*/
readonly isEnabledFileDownloads?: boolean
@@ -183,12 +154,12 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -37,6 +37,11 @@ export interface IBrowserWorkbenchEnviro
@@ -36,6 +36,11 @@ export interface IBrowserWorkbenchEnviro
* Enable downloading files via menu actions.
*/
readonly isEnabledFileDownloads?: boolean;
@@ -200,7 +171,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
}
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
@@ -113,6 +118,13 @@ export class BrowserWorkbenchEnvironment
@@ -74,6 +79,13 @@ export class BrowserWorkbenchEnvironment
return this.options.isEnabledFileDownloads;
}
@@ -230,7 +201,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
'auth'?: string
'disable-file-downloads'?: boolean;
'locale'?: string
+ 'disable-getting-started-override'?: boolean,
+ 'disable-getting-started-override'?: boolean;
/* ----- server setup ----- */
@@ -252,14 +223,14 @@ Index: code-server/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 'vs/base/common/ev
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted } from 'vs/workbench/common/contextkeys';
-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys';
+import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted } from 'vs/workbench/common/contextkeys';
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -207,6 +207,7 @@ export class WorkbenchContextKeysHandler
@@ -204,6 +204,7 @@ export class WorkbenchContextKeysHandler
// code-server
IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true)
@@ -271,8 +242,8 @@ Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts
+++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts
@@ -36,6 +36,7 @@ export const HasWebFileSystemAccess = ne
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));
@@ -33,6 +33,7 @@ export const IsFullscreenContext = new R
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true);
+export const IsEnabledCoderGettingStarted = new RawContextKey<boolean>('isEnabledCoderGettingStarted', true, true);

View File

@@ -38,7 +38,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.main.ts
-const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History');
-const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
-args['user-data-dir'] = USER_DATA_PATH;
-const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath);
-const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath);
-const BUILTIN_EXTENSIONS_FOLDER_PATH = join(APP_ROOT, 'extensions');
-args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
-args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
@@ -58,7 +58,7 @@ Index: code-server/lib/vscode/src/vs/server/node/server.main.ts
+ const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History');
+ const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine');
+ args['user-data-dir'] = USER_DATA_PATH;
+ const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath);
+ const APP_ROOT = dirname(FileAccess.asFileUri('', require).fsPath);
+ const BUILTIN_EXTENSIONS_FOLDER_PATH = args['builtin-extensions-dir'] || join(APP_ROOT, 'extensions');
+ args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH;
+ args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions');
@@ -95,7 +95,7 @@ Index: code-server/lib/vscode/src/vs/base/common/processes.ts
--- code-server.orig/lib/vscode/src/vs/base/common/processes.ts
+++ code-server/lib/vscode/src/vs/base/common/processes.ts
@@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme
/^VSCODE_(?!(PORTABLE|SHELL_LOGIN)).+$/,
/^VSCODE_(?!SHELL_LOGIN).+$/,
/^SNAP(|_.*)$/,
/^GDK_PIXBUF_.+$/,
+ /^CODE_SERVER_.+$/,
@@ -107,7 +107,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandl
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts
@@ -76,8 +76,11 @@ export class BrowserDialogHandler extend
@@ -143,8 +143,11 @@ export class BrowserDialogHandler implem
async about(): Promise<void> {
const detailString = (useAgo: boolean): string => {
@@ -184,7 +184,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel';
@@ -119,6 +120,9 @@ export class BrowserMain extends Disposa
@@ -116,6 +117,9 @@ export class BrowserMain extends Disposa
// Startup
const instantiationService = workbench.startup();
@@ -198,7 +198,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -55,6 +55,8 @@ export type ExtensionVirtualWorkspaceSup
@@ -31,6 +31,8 @@ export type ExtensionVirtualWorkspaceSup
};
export interface IProductConfiguration {

View File

@@ -32,7 +32,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -255,6 +255,11 @@ export interface IWorkbenchConstructionO
@@ -266,6 +266,11 @@ export interface IWorkbenchConstructionO
*/
readonly configurationDefaults?: Record<string, any>;
@@ -43,13 +43,13 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+
//#endregion
//#region Profile options
Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -92,7 +92,14 @@ export class BrowserWorkbenchEnvironment
get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }
@@ -53,7 +53,14 @@ export class BrowserWorkbenchEnvironment
get logFile(): URI { return joinPath(this.logsHome, 'window.log'); }
@memoize
- get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); }

View File

@@ -8,7 +8,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -58,6 +58,7 @@ export interface IProductConfiguration {
@@ -34,6 +34,7 @@ export interface IProductConfiguration {
readonly codeServerVersion?: string
readonly rootEndpoint?: string
readonly updateEndpoint?: string
@@ -21,7 +21,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -13,6 +13,7 @@ import { IEnvironmentService, INativeEnv
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
/* ----- code-server ----- */
'disable-update-check': { type: 'boolean' },
+ 'auth': { type: 'string' },

View File

@@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts
+++ code-server/lib/vscode/src/vs/platform/product/common/product.ts
@@ -47,6 +47,16 @@ else if (globalThis._VSCODE_PRODUCT_JSON
@@ -53,6 +53,16 @@ else if (typeof require?.__$__nodeRequir
version: pkg.version
});
}
@@ -65,17 +65,17 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
},
callbackRoute: this._callbackRoute
};
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
+++ code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
@@ -16,7 +16,6 @@ import { getServiceMachineId } from 'vs/
import { IStorageService } from 'vs/platform/storage/common/storage';
import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry';
import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
-import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
@@ -60,7 +59,7 @@ export abstract class AbstractExtensionR
private readonly _environmentService: IEnvironmentService,

View File

@@ -1,16 +1,28 @@
Unconditionally enable the proposed API
To test run an extension that uses the proposed API (i.e.
https://github.com/microsoft/vscode-extension-samples/tree/ddae6c0c9ff203b4ed6f6b43bfacdd0834215f83/proposed-api-sample)
To test run an extension that uses the proposed API.
We also override isProposedApiEnabled in case an extension does not declare the
APIs it needs correctly (the Jupyter extension had this issue).
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
@@ -1462,7 +1462,7 @@ class ProposedApiController {
this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id)));
- this._envEnablesProposedApiForAll =
+ this._envEnablesProposedApiForAll = true ||
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensions.ts
@@ -260,10 +260,7 @@ function extensionDescriptionArrayToMap(
@@ -359,10 +359,7 @@ function extensionDescriptionArrayToMap(
}
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
@@ -22,16 +34,3 @@ Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extens
}
export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {
Index: code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
+++ code-server/lib/vscode/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts
@@ -24,7 +24,7 @@ export class ExtensionsProposedApi {
this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id)));
- this._envEnablesProposedApiForAll =
+ this._envEnablesProposedApiForAll = true ||
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID

View File

@@ -30,7 +30,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -59,6 +59,7 @@ export interface IProductConfiguration {
@@ -35,6 +35,7 @@ export interface IProductConfiguration {
readonly rootEndpoint?: string
readonly updateEndpoint?: string
readonly logoutEndpoint?: string
@@ -42,31 +42,31 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityReso
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
+++ code-server/lib/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts
@@ -11,7 +11,7 @@ import { StopWatch } from 'vs/base/commo
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/comm
import { RemoteAuthorities } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver';
-import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
+import { IRemoteAuthorityResolverService, IRemoteConnectionData, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts';
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
@@ -29,7 +29,7 @@ export class RemoteAuthorityResolverServ
constructor(
connectionToken: Promise<string> | string | undefined,
resourceUriProvider: ((uri: URI) => URI) | undefined,
- @IProductService productService: IProductService,
+ @IProductService private readonly productService: IProductService,
@ILogService private readonly _logService: ILogService,
) {
@@ -23,7 +23,7 @@ export class RemoteAuthorityResolverServ
private readonly _connectionToken: Promise<string> | string | undefined;
private readonly _connectionTokens: Map<string, string>;
- constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined) {
+ constructor(@IProductService productService: IProductService, connectionToken: Promise<string> | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, private readonly proxyEndpointTemplate?: string) {
super();
@@ -75,9 +75,14 @@ export class RemoteAuthorityResolverServ
this._connectionToken = connectionToken;
this._connectionTokens = new Map<string, string>();
@@ -61,9 +61,14 @@ export class RemoteAuthorityResolverServ
private async _doResolveAuthority(authority: string): Promise<ResolverResult> {
const connectionToken = await Promise.resolve(this._connectionTokens.get(authority) || this._connectionToken);
performance.mark(`code/didResolveConnectionToken/${authorityPrefix}`);
this._logService.info(`Resolved connection token (${authorityPrefix}) after ${sw.elapsed()} ms`);
+ let options: ResolvedOptions | undefined;
+ if (this.productService.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.productService.proxyEndpointTemplate, window.location.href);
+ if (this.proxyEndpointTemplate) {
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href);
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
+ }
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
@@ -88,11 +88,24 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
embedderIdentifier: 'server-distro',
extensionsGallery: this._productService.extensionsGallery,
},
Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts
@@ -247,7 +247,7 @@ export class BrowserMain extends Disposa
// Remote
const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName);
- const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider);
+ const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider, this.configuration.productConfiguration?.proxyEndpointTemplate);
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
// Signing
Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
@@ -383,7 +383,7 @@ export async function createTerminalEnvi
@@ -392,7 +392,7 @@ export async function createTerminalEnvi
// Sanitize the environment, removing any undesirable VS Code and Electron environment
// variables
@@ -157,7 +170,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExpl
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts
@@ -73,7 +73,7 @@ export class ForwardedPortsView extends
@@ -73,7 +73,7 @@ export class ForwardedPortsView extends
this.contextKeyListener = undefined;
}

View File

@@ -18,4 +18,5 @@ disable-downloads.diff
telemetry.diff
display-language.diff
cli-window-open.diff
exec-argv.diff
getting-started.diff

View File

@@ -6,7 +6,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -60,6 +60,10 @@ export interface IProductConfiguration {
@@ -36,6 +36,10 @@ export interface IProductConfiguration {
readonly updateEndpoint?: string
readonly logoutEndpoint?: string
readonly proxyEndpointTemplate?: string

View File

@@ -32,7 +32,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
let version = packageJson.version;
const quality = product.quality;
@@ -388,7 +387,7 @@ function tweakProductForServerWeb(produc
@@ -387,7 +386,7 @@ function tweakProductForServerWeb(produc
const minifyTask = task.define(`minify-vscode-${type}`, task.series(
optimizeTask,
util.rimraf(`out-vscode-${type}-min`),

View File

@@ -1,10 +1,9 @@
Add support for telemetry endpoint
To test:
1. Create a mock API using [RequestBin](https://requestbin.io/) or [Beeceptor](https://beeceptor.com/)
1. Create a RequestBin - https://requestbin.io/
2. Run code-server with `CS_TELEMETRY_URL` set:
i.e. `CS_TELEMETRY_URL="https://requestbin.io/1ebub9z1" ./code-server-<version>-macos-amd64/bin/code-server`
NOTE: it has to be a production build.
3. Load code-server in browser an do things (i.e. open a file)
4. Refresh RequestBin and you should see logs
@@ -12,16 +11,16 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
@@ -69,6 +69,7 @@ import { IExtensionsScannerService } fro
@@ -71,6 +71,7 @@ import { IExtensionsScannerService } fro
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
+import { TelemetryClient } from "vs/server/node/telemetryClient";
import { NullPolicyService } from 'vs/platform/policy/common/policy';
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
import { LoggerService } from 'vs/platform/log/node/loggerService';
@@ -150,10 +151,13 @@ export async function setupServerService
let oneDsAppender: ITelemetryAppender = NullAppender;
@@ -133,10 +134,13 @@ export async function setupServerService
const machineId = await getMachineId();
const isInternal = isInternalTelemetry(productService, configurationService);
if (supportsTelemetry(productService, environmentService)) {
- if (productService.aiConfig && productService.aiConfig.ariaKey) {

View File

@@ -93,7 +93,7 @@ Index: code-server/lib/vscode/src/vs/base/common/product.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
+++ code-server/lib/vscode/src/vs/base/common/product.ts
@@ -57,6 +57,7 @@ export type ExtensionVirtualWorkspaceSup
@@ -33,6 +33,7 @@ export type ExtensionVirtualWorkspaceSup
export interface IProductConfiguration {
readonly codeServerVersion?: string
readonly rootEndpoint?: string
@@ -120,7 +120,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -11,6 +11,8 @@ import { refineServiceDecorator } from '
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
+ /* ----- code-server ----- */
+ 'disable-update-check': { type: 'boolean' },

View File

@@ -41,7 +41,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
+++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -215,7 +215,7 @@ export class BrowserWorkbenchEnvironment
@@ -177,7 +177,7 @@ export class BrowserWorkbenchEnvironment
@memoize
get webviewExternalEndpoint(): string {
@@ -70,12 +70,12 @@ 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-4A7BC3A+gc8e1t5I0rfDNtBiGlA0/GTojKdfdDHAWYw=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-8I6aYsGyWES1C043+SeEOyeZlC0Y0HN5WevEDh97c1Y=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
- content="default-src 'none'; script-src 'sha256-wwaDxsm1+SKIUb5YJXiZlYMyV7QPB8+zd6HPcTjigZs=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
+ content="default-src 'none'; script-src 'sha256-IZkGO4jZeUn7pzM6pBZCZc9bUYm8oVNV3z8zEa8gxlk=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
<!-- Disable pinch zooming -->
<meta name="viewport"
@@ -323,6 +323,12 @@
@@ -331,6 +331,12 @@
const hostname = location.hostname;
@@ -92,7 +92,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index
===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
+++ code-server/lib/vscode/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
@@ -322,6 +322,12 @@
@@ -330,6 +330,12 @@
const hostname = location.hostname;

View File

@@ -6,16 +6,10 @@
"matchUpdateTypes": ["minor", "patch", "digest"],
"automerge": true,
"groupName": "Minor dependency updates"
},
{
"matchDepTypes": ["peerDependencies"],
"matchUpdateTypes": ["minor", "patch", "digest"],
"automerge": true,
"groupName": "Peer dependency updates"
}
],
"vulnerabilityAlerts": {
"enabled": "true"
},
"ignoreDeps": ["express", "ansi-regex", "env-paths", "limiter", "node", "prettier"]
"ignoreDeps": ["express"]
}

View File

@@ -10,7 +10,7 @@
http-equiv="Content-Security-Policy"
content="style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/>
<title>{{I18N_LOGIN_TITLE}}</title>
<title>{{APP_NAME}} login</title>
<link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" />
<link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" />
<link rel="manifest" href="{{BASE}}/manifest.json" crossorigin="use-credentials" />
@@ -25,7 +25,7 @@
<div class="card-box">
<div class="header">
<h1 class="main">{{WELCOME_TEXT}}</h1>
<div class="sub">{{I18N_LOGIN_BELOW}} {{PASSWORD_MSG}}</div>
<div class="sub">Please log in below. {{PASSWORD_MSG}}</div>
</div>
<div class="content">
<form class="login-form" method="post">
@@ -38,11 +38,11 @@
autofocus
class="password"
type="password"
placeholder="{{I18N_PASSWORD_PLACEHOLDER}}"
placeholder="PASSWORD"
name="password"
autocomplete="current-password"
/>
<input class="submit -button" value="{{I18N_SUBMIT}}" type="submit" />
<input class="submit -button" value="SUBMIT" type="submit" />
</div>
{{ERROR}}
</form>

View File

@@ -1,6 +0,0 @@
Contact: mailto:security@coder.com
Acknowledgments: https://coder.com/security/thanks
Preferred-Languages: en-US
Canonical: https://coder.com/.well-known/security.txt
Policy: https://coder.com/security/policy
Hiring: https://coder.com/careers

View File

@@ -4,7 +4,6 @@ export enum HttpCode {
NotFound = 404,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
LargePayload = 413,
ServerError = 500,
}

View File

@@ -3,15 +3,7 @@ import { promises as fs } from "fs"
import { load } from "js-yaml"
import * as os from "os"
import * as path from "path"
import {
canConnect,
generateCertificate,
generatePassword,
humanPath,
paths,
isNodeJSErrnoException,
splitOnFirstEquals,
} from "./util"
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util"
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
@@ -92,6 +84,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"reuse-window"?: boolean
"new-window"?: boolean
"ignore-last-opened"?: boolean
link?: OptionalString
verbose?: boolean
"app-name"?: string
"welcome-text"?: string
@@ -187,14 +180,7 @@ export const options: Options<Required<UserProvidedArgs>> = {
enable: { type: "string[]" },
help: { type: "boolean", short: "h", description: "Show this output." },
json: { type: "boolean" },
locale: {
// The preferred way to set the locale is via the UI.
type: "string",
description: `
Set vscode display language and language to show on the login page, more info see
https://en.wikipedia.org/wiki/IETF_language_tag
`,
},
locale: { type: "string" }, // The preferred way to set the locale is via the UI.
open: { type: "boolean", description: "Open in browser on startup. Does not work remotely." },
"bind-addr": {
@@ -269,6 +255,15 @@ export const options: Options<Required<UserProvidedArgs>> = {
short: "w",
description: "Text to show on login page",
},
link: {
type: OptionalString,
description: `
Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.
`,
deprecated: true,
},
}
export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => {
@@ -300,6 +295,19 @@ export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedAr
})
}
export function splitOnFirstEquals(str: string): string[] {
// we use regex instead of "=" to ensure we split at the first
// "=" and return the following substring with it
// important for the hashed-password which looks like this
// $argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY
// 2 means return two items
// Source: https://stackoverflow.com/a/4607799/3015595
// We use the ? to say the the substr after the = is optional
const split = str.split(/=(.+)?/, 2)
return split
}
/**
* Parse arguments into UserProvidedArgs. This should not go beyond checking
* that arguments are valid types and have values when required.
@@ -532,6 +540,17 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args.host = addr.host
args.port = addr.port
// If we're being exposed to the cloud, we listen on a random address and
// disable auth.
if (args.link) {
args.host = "localhost"
args.port = 0
args.socket = undefined
args["socket-mode"] = undefined
args.cert = undefined
args.auth = AuthType.None
}
if (args.cert && !args.cert.value) {
const { cert, certKey } = await generateCertificate(args["cert-host"] || "localhost")
args.cert = {
@@ -571,9 +590,6 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
// Filter duplicate proxy domains and remove any leading `*.`.
const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, "")))
args["proxy-domain"] = Array.from(proxyDomains)
if (args["proxy-domain"].length > 0 && !process.env.VSCODE_PROXY_URI) {
process.env.VSCODE_PROXY_URI = `{{port}}.${args["proxy-domain"][0]}`
}
if (typeof args._ === "undefined") {
args._ = []
@@ -798,7 +814,6 @@ export interface CodeArgs extends UserProvidedCodeArgs {
"without-connection-token"?: boolean
"without-browser-env-var"?: boolean
compatibility: string
log: string[] | undefined
}
/**
@@ -820,6 +835,5 @@ export const toCodeArgs = async (args: DefaultedArgs): Promise<CodeArgs> => {
help: !!args.help,
version: !!args.version,
port: args.port?.toString(),
log: args.log ? [args.log] : undefined,
}
}

43
src/node/coder_cloud.ts Normal file
View File

@@ -0,0 +1,43 @@
import { logger } from "@coder/logger"
import { spawn } from "child_process"
import path from "path"
import split2 from "split2"
// https://github.com/coder/coder-cloud
const coderCloudAgent = path.resolve(__dirname, "../../lib/coder-cloud-agent")
function runAgent(...args: string[]): Promise<void> {
logger.debug(`running agent with ${args}`)
const agent = spawn(coderCloudAgent, args, {
stdio: ["inherit", "inherit", "pipe"],
})
agent.stderr.pipe(split2()).on("data", (line) => {
line = line.replace(/^[0-9-]+ [0-9:]+ [^ ]+\t/, "")
logger.info(line)
})
return new Promise((res, rej) => {
agent.on("error", rej)
agent.on("close", (code) => {
if (code !== 0) {
rej({
message: `--link agent exited with ${code}`,
})
return
}
res()
})
})
}
export function coderCloudBind(address: URL | string, serverName = ""): Promise<void> {
if (typeof address === "string") {
throw new Error("Cannot link socket paths")
}
// Address needs to be in hostname:port format without the protocol.
return runAgent("bind", `--code-server-addr=${address.host}`, serverName)
}

View File

@@ -12,15 +12,7 @@ import { version as codeServerVersion } from "./constants"
import { Heart } from "./heart"
import { CoderSettings, SettingsProvider } from "./settings"
import { UpdateProvider } from "./update"
import {
getPasswordMethod,
IsCookieValidArgs,
isCookieValid,
sanitizeString,
escapeHtml,
escapeJSON,
splitOnFirstEquals,
} from "./util"
import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString, escapeHtml, escapeJSON } from "./util"
/**
* Base options included on every page.
@@ -316,82 +308,3 @@ export const getCookieOptions = (req: express.Request): express.CookieOptions =>
export const self = (req: express.Request): string => {
return normalize(`${req.baseUrl}${req.originalUrl.endsWith("/") ? "/" : ""}`, true)
}
function getFirstHeader(req: http.IncomingMessage, headerName: string): string | undefined {
const val = req.headers[headerName]
return Array.isArray(val) ? val[0] : val
}
/**
* Throw a forbidden error if origin checks fail. Call `next` if provided.
*/
export function ensureOrigin(req: express.Request, _?: express.Response, next?: express.NextFunction): void {
try {
authenticateOrigin(req)
if (next) {
next()
}
} catch (error) {
logger.debug(`${error instanceof Error ? error.message : error}; blocking request to ${req.originalUrl}`)
throw new HttpError("Forbidden", HttpCode.Forbidden)
}
}
/**
* Authenticate the request origin against the host. Throw if invalid.
*/
export function authenticateOrigin(req: express.Request): void {
// A missing origin probably means the source is non-browser. Not sure we
// have a use case for this but let it through.
const originRaw = getFirstHeader(req, "origin")
if (!originRaw) {
return
}
let origin: string
try {
origin = new URL(originRaw).host.trim().toLowerCase()
} catch (error) {
throw new Error(`unable to parse malformed origin "${originRaw}"`)
}
const host = getHost(req)
if (typeof host === "undefined") {
// A missing host likely means the reverse proxy has not been configured to
// forward the host which means we cannot perform the check. Emit an error
// so an admin can fix the issue.
logger.error("No host headers found")
logger.error("Are you behind a reverse proxy that does not forward the host?")
throw new Error("no host headers found")
}
if (host !== origin) {
throw new Error(`host "${host}" does not match origin "${origin}"`)
}
}
/**
* Get the host from headers. It will be trimmed and lowercased.
*/
function getHost(req: express.Request): string | undefined {
// Honor Forwarded if present.
const forwardedRaw = getFirstHeader(req, "forwarded")
if (forwardedRaw) {
const parts = forwardedRaw.split(/[;,]/)
for (let i = 0; i < parts.length; ++i) {
const [key, value] = splitOnFirstEquals(parts[i])
if (key.trim().toLowerCase() === "host" && value) {
return value.trim().toLowerCase()
}
}
}
// Honor X-Forwarded-Host if present.
const xHost = getFirstHeader(req, "x-forwarded-host")
if (xHost) {
return xHost.trim().toLowerCase()
}
const host = getFirstHeader(req, "host")
return host ? host.trim().toLowerCase() : undefined
}

View File

@@ -1,24 +0,0 @@
import i18next, { init } from "i18next"
import * as en from "./locales/en.json"
import * as zhCn from "./locales/zh-cn.json"
import * as th from "./locales/th.json"
init({
lng: "en",
fallbackLng: "en", // language to use if translations in user language are not available.
returnNull: false,
lowerCaseLng: true,
debug: process.env.NODE_ENV === "development",
resources: {
en: {
translation: en,
},
"zh-cn": {
translation: zhCn,
},
th: {
translation: th,
},
},
})
export default i18next

View File

@@ -1,13 +0,0 @@
{
"LOGIN_TITLE": "{{app}} login",
"LOGIN_BELOW": "Please log in below.",
"WELCOME": "Welcome to {{app}}",
"LOGIN_PASSWORD": "Check the config file at {{configFile}} for the password.",
"LOGIN_USING_ENV_PASSWORD": "Password was set from $PASSWORD.",
"LOGIN_USING_HASHED_PASSWORD": "Password was set from $HASHED_PASSWORD.",
"SUBMIT": "SUBMIT",
"PASSWORD_PLACEHOLDER": "PASSWORD",
"LOGIN_RATE_LIMIT": "Login rate limited!",
"MISS_PASSWORD": "Missing password",
"INCORRECT_PASSWORD": "Incorrect password"
}

View File

@@ -1,13 +0,0 @@
{
"LOGIN_TITLE": "เข้าสู่ระบบ {{app}}",
"LOGIN_BELOW": "กรุณาเข้าสู่ระบบด้านล่าง",
"WELCOME": "ยินดีต้อนรับสู่ {{app}}",
"LOGIN_PASSWORD": "ตรวจสอบไฟล์กำหนดค่าที่ {{configFile}} เพื่อดูรหัสผ่าน",
"LOGIN_USING_ENV_PASSWORD": "รหัสผ่านถูกกำหนดเป็น $PASSWORD",
"LOGIN_USING_HASHED_PASSWORD": "รรหัสผ่านถูกกำหนดเป็น $HASHED_PASSWORD",
"SUBMIT": "ส่ง",
"PASSWORD_PLACEHOLDER": "รหัสผ่าน",
"LOGIN_RATE_LIMIT": "ถึงขีดจำกัดอัตราการเข้าสู่ระบบ!",
"MISS_PASSWORD": "รหัสผ่านหายไป",
"INCORRECT_PASSWORD": "รหัสผ่านไม่ถูกต้อง"
}

View File

@@ -1,13 +0,0 @@
{
"LOGIN_TITLE": "{{app}} 登录",
"LOGIN_BELOW": "请在下面登录。",
"WELCOME": "欢迎来到 {{app}}",
"LOGIN_PASSWORD": "查看配置文件 {{configFile}} 中的密码。",
"LOGIN_USING_ENV_PASSWORD": "密码在 $PASSWORD 中设置。",
"LOGIN_USING_HASHED_PASSWORD": "密码在 $HASHED_PASSWORD 中设置。",
"SUBMIT": "提交",
"PASSWORD_PLACEHOLDER": "密码",
"LOGIN_RATE_LIMIT": "登录速率限制!",
"MISS_PASSWORD": "缺少密码",
"INCORRECT_PASSWORD": "密码不正确"
}

View File

@@ -6,6 +6,7 @@ import { Disposable } from "../common/emitter"
import { plural } from "../common/util"
import { createApp, ensureAddress } from "./app"
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
import { coderCloudBind } from "./coder_cloud"
import { commit, version } from "./constants"
import { register } from "./routes"
import { humanPath, isDirectory, loadAMDModule, open } from "./util"
@@ -126,7 +127,11 @@ export const runCodeServer = async (
const disposeRoutes = await register(app, args)
logger.info(`Using config file ${humanPath(os.homedir(), args.config)}`)
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
logger.info(
`${protocol.toUpperCase()} server listening on ${serverAddress.toString()} ${
args.link ? "(randomized by --link)" : ""
}`,
)
if (args.auth === AuthType.Password) {
logger.info(" - Authentication is enabled")
@@ -138,13 +143,13 @@ export const runCodeServer = async (
logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`)
}
} else {
logger.info(" - Authentication is disabled")
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`)
}
if (args.cert) {
logger.info(` - Using certificate for HTTPS: ${humanPath(os.homedir(), args.cert.value)}`)
} else {
logger.info(" - Not serving HTTPS")
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`)
}
if (args["proxy-domain"].length > 0) {
@@ -152,6 +157,11 @@ export const runCodeServer = async (
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
}
if (args.link) {
await coderCloudBind(serverAddress, args.link.value)
logger.info(" - Connected to cloud agent")
}
if (args.enable && args.enable.length > 0) {
logger.info("Enabling the following experimental features:")
args.enable.forEach((feature) => {

View File

@@ -3,19 +3,9 @@ import { HttpCode } from "../common/http"
export const proxy = proxyServer.createProxyServer({})
// The error handler catches when the proxy fails to connect (for example when
// there is nothing running on the target port).
proxy.on("error", (error, _, res) => {
// This could be for either a web socket or a regular request. Despite what
// the types say, writeHead() will not exist on web socket requests (nor will
// status() from Express). But writing out the code manually does not work
// for regular requests thus the branching behavior.
if (typeof res.writeHead !== "undefined") {
res.writeHead(HttpCode.ServerError)
res.end(error.message)
} else {
res.end(`HTTP/1.1 ${HttpCode.ServerError} ${error.message}\r\n\r\n`)
}
res.writeHead(HttpCode.ServerError)
res.end(error.message)
})
// Intercept the response to rewrite absolute redirects against the base path.

View File

@@ -1,6 +1,6 @@
import { Request, Router } from "express"
import { HttpCode, HttpError } from "../../common/http"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
import { proxy } from "../proxy"
import { Router as WsRouter } from "../wsRouter"
@@ -78,8 +78,10 @@ wsRouter.ws("*", async (req, _, next) => {
if (!port) {
return next()
}
ensureOrigin(req)
// Must be authenticated to use the proxy.
await ensureAuthenticated(req)
proxy.ws(req, req.ws, req.head, {
ignorePath: true,
target: `http://0.0.0.0:${port}${req.originalUrl}`,

View File

@@ -63,11 +63,5 @@ export const errorHandler: express.ErrorRequestHandler = async (err, req, res, n
export const wsErrorHandler: express.ErrorRequestHandler = async (err, req, res, next) => {
logger.error(`${err.message} ${err.stack}`)
let statusCode = 500
if (errorHasStatusCode(err)) {
statusCode = err.statusCode
} else if (errorHasCode(err) && notFoundCodes.includes(err.code)) {
statusCode = HttpCode.NotFound
}
;(req as WebsocketRequest).ws.end(`HTTP/1.1 ${statusCode} ${err.message}\r\n\r\n`)
;(req as WebsocketRequest).ws.end()
}

View File

@@ -33,15 +33,7 @@ import { CodeServerRouteWrapper } from "./vscode"
export const register = async (app: App, args: DefaultedArgs): Promise<Disposable["dispose"]> => {
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
// connections. Feels like it must be a bug? For now add a timer to make
// sure we eventually resolve.
const timer = setTimeout(() => {
logger.debug("Node failed to respond with connections; assuming zero")
resolve(false)
}, 5000)
app.server.getConnections((error, count) => {
clearTimeout(timer)
if (error) {
return reject(error)
}
@@ -89,13 +81,6 @@ export const register = async (app: App, args: DefaultedArgs): Promise<Disposabl
return res.redirect(`https://${req.headers.host}${req.originalUrl}`)
}
// Return security.txt.
if (req.originalUrl === "/security.txt" || req.originalUrl === "/.well-known/security.txt") {
const resourcePath = path.resolve(rootPath, "src/browser/security.txt")
res.set("Content-Type", getMediaMime(resourcePath))
return res.send(await fs.readFile(resourcePath))
}
// Return robots.txt.
if (req.originalUrl === "/robots.txt") {
const resourcePath = path.resolve(rootPath, "src/browser/robots.txt")

View File

@@ -7,13 +7,12 @@ import { CookieKeys } from "../../common/http"
import { rootPath } from "../constants"
import { authenticated, getCookieOptions, redirect, replaceTemplates } from "../http"
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
import i18n from "../i18n"
// RateLimiter wraps around the limiter library for logins.
// It allows 2 logins every minute plus 12 logins every hour.
export class RateLimiter {
private readonly minuteLimiter = new Limiter({ tokensPerInterval: 2, interval: "minute" })
private readonly hourLimiter = new Limiter({ tokensPerInterval: 12, interval: "hour" })
private readonly minuteLimiter = new Limiter(2, "minute")
private readonly hourLimiter = new Limiter(12, "hour")
public canTry(): boolean {
// Note: we must check using >= 1 because technically when there are no tokens left
@@ -29,26 +28,21 @@ export class RateLimiter {
const getRoot = async (req: Request, error?: Error): Promise<string> => {
const content = await fs.readFile(path.join(rootPath, "src/browser/pages/login.html"), "utf8")
const locale = req.args["locale"] || "en"
i18n.changeLanguage(locale)
const appName = req.args["app-name"] || "code-server"
const welcomeText = req.args["welcome-text"] || (i18n.t("WELCOME", { app: appName }) as string)
let passwordMsg = i18n.t("LOGIN_PASSWORD", { configFile: humanPath(os.homedir(), req.args.config) })
const welcomeText = req.args["welcome-text"] || `Welcome to ${appName}`
let passwordMsg = `Check the config file at ${humanPath(os.homedir(), req.args.config)} for the password.`
if (req.args.usingEnvPassword) {
passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD")
passwordMsg = "Password was set from $PASSWORD."
} else if (req.args.usingEnvHashedPassword) {
passwordMsg = i18n.t("LOGIN_USING_HASHED_PASSWORD")
passwordMsg = "Password was set from $HASHED_PASSWORD."
}
return replaceTemplates(
req,
content
.replace(/{{I18N_LOGIN_TITLE}}/g, i18n.t("LOGIN_TITLE", { app: appName }))
.replace(/{{APP_NAME}}/g, appName)
.replace(/{{WELCOME_TEXT}}/g, welcomeText)
.replace(/{{PASSWORD_MSG}}/g, passwordMsg)
.replace(/{{I18N_LOGIN_BELOW}}/g, i18n.t("LOGIN_BELOW"))
.replace(/{{I18N_PASSWORD_PLACEHOLDER}}/g, i18n.t("PASSWORD_PLACEHOLDER"))
.replace(/{{I18N_SUBMIT}}/g, i18n.t("SUBMIT"))
.replace(/{{ERROR}}/, error ? `<div class="error">${escapeHtml(error.message)}</div>` : ""),
)
}
@@ -76,11 +70,11 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
try {
// Check to see if they exceeded their login attempts
if (!limiter.canTry()) {
throw new Error(i18n.t("LOGIN_RATE_LIMIT") as string)
throw new Error("Login rate limited!")
}
if (!password) {
throw new Error(i18n.t("MISS_PASSWORD") as string)
throw new Error("Missing password")
}
const passwordMethod = getPasswordMethod(hashedPasswordFromArgs)
@@ -114,7 +108,7 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
}),
)
throw new Error(i18n.t("INCORRECT_PASSWORD") as string)
throw new Error("Incorrect password")
} catch (error: any) {
const renderedHtml = await getRoot(req, error)
res.send(renderedHtml)

View File

@@ -3,7 +3,7 @@ import * as path from "path"
import * as qs from "qs"
import * as pluginapi from "../../../typings/pluginapi"
import { HttpCode, HttpError } from "../../common/http"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
import { proxy as _proxy } from "../proxy"
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
@@ -50,7 +50,6 @@ export async function wsProxy(
passthroughPath?: boolean
},
): Promise<void> {
ensureOrigin(req)
await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, {
ignorePath: true,

View File

@@ -7,7 +7,7 @@ import { WebsocketRequest } from "../../../typings/pluginapi"
import { logError } from "../../common/util"
import { CodeArgs, toCodeArgs } from "../cli"
import { isDevMode } from "../constants"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
import { authenticated, ensureAuthenticated, redirect, replaceTemplates, self } from "../http"
import { SocketProxyProvider } from "../socket"
import { isFile, loadAMDModule } from "../util"
import { Router as WsRouter } from "../wsRouter"
@@ -40,7 +40,6 @@ export class CodeServerRouteWrapper {
//#region Route Handlers
private manifest: express.Handler = async (req, res, next) => {
const appName = req.args["app-name"] || "code-server"
res.writeHead(200, { "Content-Type": "application/manifest+json" })
return res.end(
@@ -48,8 +47,8 @@ export class CodeServerRouteWrapper {
req,
JSON.stringify(
{
name: appName,
short_name: appName,
name: "code-server",
short_name: "code-server",
start_url: ".",
display: "fullscreen",
description: "Run Code on a remote server.",
@@ -174,7 +173,7 @@ export class CodeServerRouteWrapper {
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
this.router.get("/manifest.json", this.manifest)
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
this._wsRouterWrapper.ws("*", ensureOrigin, ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
this._wsRouterWrapper.ws("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
}
dispose() {

View File

@@ -541,13 +541,3 @@ export const loadAMDModule = async <T>(amdPath: string, exportName: string): Pro
return module[exportName] as T
}
/**
* Split a string on the first equals. The result will always be an array with
* two items regardless of how many equals there are. The second item will be
* undefined if empty or missing.
*/
export function splitOnFirstEquals(str: string): [string, string | undefined] {
const split = str.split(/=(.+)?/, 2)
return [split[0], split[1]]
}

View File

@@ -147,7 +147,7 @@ abstract class Process {
* Child process that will clean up after itself if the parent goes away and can
* perform a handshake with the parent and ask it to relaunch.
*/
export class ChildProcess extends Process {
class ChildProcess extends Process {
public logger = logger.named(`child:${process.pid}`)
public constructor(private readonly parentPid: number) {

View File

@@ -32,9 +32,6 @@ export class WebsocketRouter {
/**
* Handle a websocket at this route. Note that websockets are immediately
* paused when they come in.
*
* If the origin header exists it must match the host or the connection will
* be prevented.
*/
public ws(route: expressCore.PathParams, ...handlers: pluginapi.WebSocketHandler[]): void {
this.router.get(

View File

@@ -128,8 +128,6 @@ export class CodeServer {
path.join(dir, "extensions"),
"--auth",
"none",
// The workspace to open.
...(this.args.includes("--ignore-last-opened") ? [] : [dir]),
...this.args,
// Using port zero will spawn on a random port.
"--bind-addr",
@@ -141,6 +139,8 @@ export class CodeServer {
path.join(dir, "config.yaml"),
"--user-data-dir",
dir,
// The last argument is the workspace to open.
dir,
]
this.logger.debug("spawning `node " + args.join(" ") + "`")
const proc = cp.spawn("node", args, {

View File

@@ -1,111 +0,0 @@
import { describe, test, expect } from "./baseFixture"
import { clean, getMaybeProxiedPathname } from "../utils/helpers"
const routes = ["/", "/vscode", "/vscode/"]
describe("VS Code Routes", ["--disable-workspace-trust"], {}, async () => {
const testName = "vscode-routes-default"
test.beforeAll(async () => {
await clean(testName)
})
test("should load all route variations", async ({ codeServerPage }) => {
for (const route of routes) {
await codeServerPage.navigate(route)
// Check there were no redirections
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe(route)
// TODO@jsjoeio
// now that we are in a proper browser instead of scraping the HTML we
// could possibly intercept requests to make sure assets are loading from
// the right spot.
//
// Check that page loaded from correct route
const html = await codeServerPage.page.innerHTML("html")
switch (route) {
case "/":
case "/vscode/":
expect(html).toMatch(/src="\.\/[a-z]+-[0-9a-z]+\/static\//)
break
case "/vscode":
expect(html).toMatch(/src="\.\/vscode\/[a-z]+-[0-9a-z]+\/static\//)
break
}
}
})
})
const CODE_WORKSPACE_DIR = process.env.CODE_WORKSPACE_DIR || ""
describe("VS Code Routes with code-workspace", ["--disable-workspace-trust", CODE_WORKSPACE_DIR], {}, async () => {
test("should redirect to the passed in workspace using human-readable query", async ({ codeServerPage }) => {
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe(`?workspace=${CODE_WORKSPACE_DIR}`)
})
})
const CODE_FOLDER_DIR = process.env.CODE_FOLDER_DIR || ""
describe("VS Code Routes with code-workspace", ["--disable-workspace-trust", CODE_FOLDER_DIR], {}, async () => {
test("should redirect to the passed in folder using human-readable query", async ({ codeServerPage }) => {
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe(`?folder=${CODE_FOLDER_DIR}`)
})
})
describe(
"VS Code Routes with ignore-last-opened",
["--disable-workspace-trust", "--ignore-last-opened"],
{},
async () => {
test("should not redirect", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
await codeServerPage.navigate(`/?folder=${folder}`)
await codeServerPage.navigate(`/`)
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe("")
})
},
)
describe("VS Code Routes with no workspace or folder", ["--disable-workspace-trust"], {}, async () => {
test("should redirect to last query folder/workspace", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
const workspace = process.env.CODE_WORKSPACE_DIR
await codeServerPage.navigate(`/?folder=${folder}&workspace=${workspace}`)
// If you visit again without query parameters it will re-attach them by
// redirecting. It should always redirect to the same route.
for (const route of routes) {
await codeServerPage.navigate(route)
const url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe(route)
expect(url.search).toBe(`?folder=${folder}&workspace=${workspace}`)
}
})
})
describe("VS Code Routes with no workspace or folder", ["--disable-workspace-trust"], {}, async () => {
test("should not redirect if ew passed in", async ({ codeServerPage }) => {
const folder = process.env.CODE_FOLDER_DIR
const workspace = process.env.CODE_WORKSPACE_DIR
await codeServerPage.navigate(`/?folder=${folder}&workspace=${workspace}`)
// Closing the folder should stop the redirecting.
await codeServerPage.navigate("/?ew=true")
let url = new URL(codeServerPage.page.url())
const pathname = getMaybeProxiedPathname(url)
expect(pathname).toBe("/")
expect(url.search).toBe("?ew=true")
})
})

13
test/e2e/trust.test.ts Normal file
View File

@@ -0,0 +1,13 @@
import { describe, test, expect } from "./baseFixture"
describe("Workspace trust (enabled)", [], {}, async () => {
test("should see the 'I Trust...' option", async ({ codeServerPage }) => {
expect(await codeServerPage.page.isVisible("text=Yes, I trust")).toBe(true)
})
})
describe("Workspace trust (disabled)", ["--disable-workspace-trust"], {}, async () => {
test("should not see the 'I Trust...' option", async ({ codeServerPage }) => {
expect(await codeServerPage.page.isVisible("text=Yes, I trust")).toBe(false)
})
})

View File

@@ -14,7 +14,7 @@
"jest": "^27.3.1",
"jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0",
"node-fetch": "^2.6.7",
"node-fetch": "^2.6.1",
"playwright": "^1.16.3",
"supertest": "^6.1.6",
"ts-jest": "^27.0.7",

View File

@@ -1,6 +1,5 @@
import { promises as fs } from "fs"
import { clean, getAvailablePort, getMaybeProxiedPathname, tmpdir, useEnv } from "../../test/utils/helpers"
import { REVERSE_PROXY_BASE_PATH } from "../utils/constants"
import { clean, getAvailablePort, tmpdir, useEnv } from "../../test/utils/helpers"
/**
* This file is for testing test helpers (not core code).
@@ -57,22 +56,3 @@ describe("getAvailablePort", () => {
expect(portOne).not.toEqual(portTwo)
})
})
describe("getMaybeProxiedPathname", () => {
it("should return the route", () => {
const route = "/vscode"
const url = new URL(`http://localhost:3000${route}`)
const actual = getMaybeProxiedPathname(url)
expect(actual).toBe(route)
})
it("should strip proxy if env var set", () => {
const envKey = "USE_PROXY"
const [setValue, resetValue] = useEnv(envKey)
setValue("1")
const route = "/vscode"
const url = new URL(`http://localhost:3000/8000/${REVERSE_PROXY_BASE_PATH}${route}`)
const actual = getMaybeProxiedPathname(url)
expect(actual).toBe(route)
resetValue()
})
})

View File

@@ -11,6 +11,7 @@ import {
readSocketPath,
setDefaults,
shouldOpenInExistingInstance,
splitOnFirstEquals,
toCodeArgs,
optionDescriptions,
options,
@@ -43,7 +44,6 @@ describe("parser", () => {
delete process.env.PASSWORD
delete process.env.CS_DISABLE_FILE_DOWNLOADS
delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
delete process.env.VSCODE_PROXY_URI
console.log = jest.fn()
})
@@ -297,6 +297,26 @@ describe("parser", () => {
})
})
it("should override with --link", async () => {
const args = parse(
"--cert test --cert-key test --socket test --socket-mode 777 --host 0.0.0.0 --port 8888 --link test".split(" "),
)
const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
auth: "none",
host: "localhost",
link: {
value: "test",
},
port: 0,
cert: undefined,
"cert-key": path.resolve("test"),
socket: undefined,
"socket-mode": undefined,
})
})
it("should use env var password", async () => {
process.env.PASSWORD = "test"
const args = parse([])
@@ -458,31 +478,6 @@ describe("parser", () => {
port: 8082,
})
})
it("should not set proxy uri", async () => {
await setDefaults(parse([]))
expect(process.env.VSCODE_PROXY_URI).toBeUndefined()
})
it("should set proxy uri", async () => {
await setDefaults(parse(["--proxy-domain", "coder.org"]))
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.org")
})
it("should set proxy uri to first domain", async () => {
await setDefaults(
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
)
expect(process.env.VSCODE_PROXY_URI).toEqual("{{port}}.coder.com")
})
it("should not override existing proxy uri", async () => {
process.env.VSCODE_PROXY_URI = "foo"
await setDefaults(
parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "coder.com", "--proxy-domain", "coder.org"]),
)
expect(process.env.VSCODE_PROXY_URI).toEqual("foo")
})
})
describe("cli", () => {
@@ -560,6 +555,31 @@ describe("cli", () => {
})
})
describe("splitOnFirstEquals", () => {
it("should split on the first equals", () => {
const testStr = "enabled-proposed-api=test=value"
const actual = splitOnFirstEquals(testStr)
const expected = ["enabled-proposed-api", "test=value"]
expect(actual).toEqual(expect.arrayContaining(expected))
})
it("should split on first equals regardless of multiple equals signs", () => {
const testStr =
"hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY"
const actual = splitOnFirstEquals(testStr)
const expected = [
"hashed-password",
"$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY",
]
expect(actual).toEqual(expect.arrayContaining(expected))
})
it("should always return the first element before an equals", () => {
const testStr = "auth="
const actual = splitOnFirstEquals(testStr)
const expected = ["auth"]
expect(actual).toEqual(expect.arrayContaining(expected))
})
})
describe("shouldSpawnCliProcess", () => {
it("should return false if no 'extension' related args passed in", async () => {
const args = {}
@@ -775,7 +795,6 @@ describe("toCodeArgs", () => {
help: false,
port: "8080",
version: false,
log: undefined,
}
const testName = "vscode-args"
@@ -861,13 +880,21 @@ describe("optionDescriptions", () => {
it("should show if an option is deprecated", () => {
const opts: Partial<Options<Required<UserProvidedArgs>>> = {
cert: {
link: {
type: OptionalString,
description: "foo",
description: `
Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.
`,
deprecated: true,
},
}
expect(optionDescriptions(opts)).toStrictEqual([" --cert (deprecated) foo"])
expect(optionDescriptions(opts)).toStrictEqual([
` --link (deprecated) Securely bind code-server via our cloud service with the passed name. You'll get a URL like
https://hostname-username.coder.co at which you can easily access your code-server instance.
Authorization is done via GitHub.`,
])
})
it("should show newlines in description", () => {

View File

@@ -1,125 +1,55 @@
import { getMockReq } from "@jest-mock/express"
import * as http from "../../../src/node/http"
import { mockLogger } from "../../utils/helpers"
import { constructRedirectPath, relativeRoot } from "../../../src/node/http"
describe("http", () => {
beforeEach(() => {
mockLogger()
})
afterEach(() => {
jest.clearAllMocks()
})
it("should construct a relative path to the root", () => {
expect(http.relativeRoot("/")).toStrictEqual(".")
expect(http.relativeRoot("/foo")).toStrictEqual(".")
expect(http.relativeRoot("/foo/")).toStrictEqual("./..")
expect(http.relativeRoot("/foo/bar ")).toStrictEqual("./..")
expect(http.relativeRoot("/foo/bar/")).toStrictEqual("./../..")
})
describe("origin", () => {
;[
{
origin: "",
host: "",
},
{
origin: "http://localhost:8080",
host: "",
expected: "no host headers",
},
{
origin: "http://localhost:8080",
host: " ",
expected: "does not match",
},
{
origin: "http://localhost:8080",
host: "localhost:8080",
},
{
origin: "http://localhost:8080",
host: "localhost:8081",
expected: "does not match",
},
{
origin: "localhost:8080",
host: "localhost:8080",
expected: "does not match", // Gets parsed as host: localhost and path: 8080.
},
{
origin: "test.org",
host: "localhost:8080",
expected: "malformed", // Parsing fails completely.
},
].forEach((test) => {
;[
["host", test.host],
["x-forwarded-host", test.host],
["forwarded", `for=127.0.0.1, host=${test.host}, proto=http`],
["forwarded", `for=127.0.0.1;proto=http;host=${test.host}`],
["forwarded", `proto=http;host=${test.host}, for=127.0.0.1`],
].forEach(([key, value]) => {
it(`${test.origin} -> [${key}: ${value}]`, () => {
const req = getMockReq({
originalUrl: "localhost:8080",
headers: {
origin: test.origin,
[key]: value,
},
})
if (typeof test.expected === "string") {
expect(() => http.authenticateOrigin(req)).toThrow(test.expected)
} else {
expect(() => http.authenticateOrigin(req)).not.toThrow()
}
})
})
})
})
describe("constructRedirectPath", () => {
it("should preserve slashes in queryString so they are human-readable", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = ""
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
})
it("should use an empty string if no query params", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = ""
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./"
expect(actual).toBe(expected)
})
it("should append the 'to' path relative to the originalUrl", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = "vscode"
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode"
expect(actual).toBe(expected)
})
it("should append append queryParams after 'to' path", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = "vscode"
const actual = http.constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
})
expect(relativeRoot("/")).toStrictEqual(".")
expect(relativeRoot("/foo")).toStrictEqual(".")
expect(relativeRoot("/foo/")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar ")).toStrictEqual("./..")
expect(relativeRoot("/foo/bar/")).toStrictEqual("./../..")
})
})
describe("constructRedirectPath", () => {
it("should preserve slashes in queryString so they are human-readable", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = ""
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
})
it("should use an empty string if no query params", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = ""
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./"
expect(actual).toBe(expected)
})
it("should append the 'to' path relative to the originalUrl", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = {}
const mockTo = "vscode"
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode"
expect(actual).toBe(expected)
})
it("should append append queryParams after 'to' path", () => {
const mockReq = getMockReq({
originalUrl: "localhost:8080",
})
const mockQueryParams = { folder: "/Users/jp/dev/coder" }
const mockTo = "vscode"
const actual = constructRedirectPath(mockReq, mockQueryParams, mockTo)
const expected = "./vscode?folder=/Users/jp/dev/coder"
expect(actual).toBe(expected)
})
})

View File

@@ -4,26 +4,21 @@ 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 { getAvailablePort } from "../../utils/helpers"
import * as httpserver from "../../utils/httpserver"
import * as integration from "../../utils/integration"
describe("proxy", () => {
const nhooyrDevServer = new httpserver.HttpServer()
const wsApp = express.default()
const wsRouter = WsRouter()
let codeServer: httpserver.HttpServer | undefined
let proxyPath: string
let absProxyPath: string
let e: express.Express
beforeAll(async () => {
wsApp.use("/", wsRouter.router)
await nhooyrDevServer.listen((req, res) => {
e(req, res)
})
nhooyrDevServer.listenUpgrade(wsApp)
proxyPath = `/proxy/${nhooyrDevServer.port()}/wsup`
absProxyPath = proxyPath.replace("/proxy/", "/absproxy/")
})
@@ -34,7 +29,6 @@ describe("proxy", () => {
beforeEach(() => {
e = express.default()
mockLogger()
})
afterEach(async () => {
@@ -42,7 +36,6 @@ describe("proxy", () => {
await codeServer.dispose()
codeServer = undefined
}
jest.clearAllMocks()
})
it("should rewrite the base path", async () => {
@@ -158,35 +151,6 @@ describe("proxy", () => {
expect(resp.status).toBe(500)
expect(resp.statusText).toMatch("Internal Server Error")
})
it("should pass origin check", async () => {
wsRouter.ws("/wsup", async (req) => {
wss.handleUpgrade(req, req.ws, req.head, (ws) => {
ws.send("hello")
req.ws.resume()
})
})
codeServer = await integration.setup(["--auth=none"], "")
const ws = await codeServer.wsWait(proxyPath, {
headers: {
host: "localhost:8080",
origin: "https://localhost:8080",
},
})
ws.terminate()
})
it("should fail origin check", async () => {
await expect(async () => {
codeServer = await integration.setup(["--auth=none"], "")
await codeServer.wsWait(proxyPath, {
headers: {
host: "localhost:8080",
origin: "https://evil.org",
},
})
}).rejects.toThrow()
})
})
// NOTE@jsjoeio
@@ -226,18 +190,18 @@ describe("proxy (standalone)", () => {
})
// Start both servers
proxyTarget.listen(PROXY_PORT)
testServer.listen(PORT)
await proxyTarget.listen(PROXY_PORT)
await testServer.listen(PORT)
})
afterEach(async () => {
testServer.close()
proxyTarget.close()
await testServer.close()
await proxyTarget.close()
})
it("should return a 500 when proxy target errors ", async () => {
// Close the proxy target so that proxy errors
proxyTarget.close()
await proxyTarget.close()
const errorResp = await nodeFetch(`${URL}/error`)
expect(errorResp.status).toBe(HttpCode.ServerError)
expect(errorResp.statusText).toBe("Internal Server Error")

View File

@@ -23,9 +23,7 @@ describe("health", () => {
codeServer = await integration.setup(["--auth=none"], "")
const ws = codeServer.ws("/healthz")
const message = await new Promise((resolve, reject) => {
ws.on("error", (err) => {
console.error("[healthz]", err)
})
ws.on("error", console.error)
ws.on("message", (message) => {
try {
const j = JSON.parse(message.toString())

View File

@@ -138,16 +138,5 @@ describe("login", () => {
expect(resp.status).toBe(200)
expect(htmlContent).toContain(`Welcome to ${appName}`)
})
it("should return correct welcome text when locale is set to non-English", async () => {
process.env.PASSWORD = previousEnvPassword
const locale = "zh-cn"
const codeServer = await integration.setup([`--locale=${locale}`], "")
const resp = await codeServer.fetch("/login", { method: "GET" })
const htmlContent = await resp.text()
expect(resp.status).toBe(200)
expect(htmlContent).toContain(`欢迎来到 code-server`)
})
})
})

View File

@@ -1,11 +1,17 @@
import { promises as fs } from "fs"
import * as path from "path"
import { clean, tmpdir } from "../../../utils/helpers"
import * as httpserver from "../../../utils/httpserver"
import * as integration from "../../../utils/integration"
import { mockLogger } from "../../../utils/helpers"
// TODO@jsjoeio - move these to integration tests since they rely on Code
// to be built
describe("vscode", () => {
let codeServer: httpserver.HttpServer | undefined
beforeEach(() => {
mockLogger()
const testName = "vscode"
beforeAll(async () => {
await clean(testName)
})
afterEach(async () => {
@@ -13,18 +19,119 @@ describe("vscode", () => {
await codeServer.dispose()
codeServer = undefined
}
jest.clearAllMocks()
})
it("should fail origin check", async () => {
await expect(async () => {
codeServer = await integration.setup(["--auth=none"], "")
await codeServer.wsWait("/vscode", {
headers: {
host: "localhost:8080",
origin: "https://evil.org",
},
})
}).rejects.toThrow()
const routes = ["/", "/vscode", "/vscode/"]
it("should load all route variations", async () => {
codeServer = await integration.setup(["--auth=none"], "")
for (const route of routes) {
const resp = await codeServer.fetch(route)
expect(resp.status).toBe(200)
const html = await resp.text()
const url = new URL(resp.url) // Check there were no redirections.
expect(url.pathname + url.search).toBe(route)
switch (route) {
case "/":
case "/vscode/":
expect(html).toMatch(/src="\.\/[a-z]+-[0-9a-z]+\/static\//)
break
case "/vscode":
expect(html).toMatch(/src="\.\/vscode\/[a-z]+-[0-9a-z]+\/static\//)
break
}
}
})
it("should redirect to the passed in workspace using human-readable query", async () => {
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
codeServer = await integration.setup(["--auth=none", workspace], "")
const resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe(`?workspace=${workspace}`)
})
it("should redirect to the passed in folder using human-readable query", async () => {
const folder = await tmpdir(testName)
codeServer = await integration.setup(["--auth=none", folder], "")
const resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe(`?folder=${folder}`)
})
it("should redirect to last query folder/workspace", async () => {
codeServer = await integration.setup(["--auth=none"], "")
const folder = await tmpdir(testName)
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
let resp = await codeServer.fetch("/", undefined, {
folder,
workspace,
})
expect(resp.status).toBe(200)
await resp.text()
// If you visit again without query parameters it will re-attach them by
// redirecting. It should always redirect to the same route.
for (const route of routes) {
resp = await codeServer.fetch(route)
const url = new URL(resp.url)
expect(url.pathname).toBe(route)
expect(url.search).toBe(`?folder=${folder}&workspace=${workspace}`)
await resp.text()
}
// Closing the folder should stop the redirecting.
resp = await codeServer.fetch("/", undefined, { ew: "true" })
let url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("?ew=true")
await resp.text()
resp = await codeServer.fetch("/")
url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("")
await resp.text()
})
it("should do nothing when nothing is passed in", async () => {
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch("/", undefined)
expect(resp.status).toBe(200)
const url = new URL(resp.url)
expect(url.search).toBe("")
await resp.text()
})
it("should not redirect when last opened is ignored", async () => {
codeServer = await integration.setup(["--auth=none", "--ignore-last-opened"], "")
const folder = await tmpdir(testName)
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
await fs.writeFile(workspace, "")
let resp = await codeServer.fetch("/", undefined, {
folder,
workspace,
})
expect(resp.status).toBe(200)
await resp.text()
// No redirections.
resp = await codeServer.fetch("/")
const url = new URL(resp.url)
expect(url.pathname).toBe("/")
expect(url.search).toBe("")
await resp.text()
})
})

View File

@@ -601,41 +601,3 @@ describe("constructOpenOptions", () => {
expect(urlSearch).toBe("?q=^&test")
})
})
describe("splitOnFirstEquals", () => {
const tests = [
{
name: "empty",
key: "",
value: "",
},
{
name: "split on first equals",
key: "foo",
value: "bar",
},
{
name: "split on first equals even with multiple equals",
key: "foo",
value: "bar=baz",
},
{
name: "split with empty value",
key: "foo",
value: "",
},
{
name: "split with no value",
key: "foo",
value: undefined,
},
]
tests.forEach((test) => {
it("should ${test.name}", () => {
const input = test.key && typeof test.value !== "undefined" ? `${test.key}=${test.value}` : test.key
const [key, value] = util.splitOnFirstEquals(input)
expect(key).toStrictEqual(test.key)
expect(value).toStrictEqual(test.value || undefined)
})
})
})

View File

@@ -1,14 +0,0 @@
import { ChildProcess, ParentProcess, isChild } from "../../../src/node/wrapper"
describe("wrapper", () => {
describe("isChild", () => {
it("should return false for parent process", () => {
const p = new ParentProcess("1")
expect(isChild(p)).toBe(false)
})
})
it("should return false for parent process", () => {
const childProc = new ChildProcess(1)
expect(isChild(childProc)).toBe(true)
})
})

View File

@@ -1,8 +1,6 @@
import { workspaceDir } from "./constants"
import { clean, tmpdir } from "./helpers"
import { clean } from "./helpers"
import * as wtfnode from "./wtfnode"
import * as path from "path"
import { promises as fs } from "fs"
/**
* Perform workspace cleanup and authenticate. This should be ran before e2e
@@ -19,14 +17,5 @@ export default async function () {
wtfnode.setup()
}
// Create dummy code-workspace for routes.test.ts
const codeWorkspace = path.join(await tmpdir(workspaceDir), "test.code-workspace")
await fs.writeFile(codeWorkspace, "")
process.env.CODE_WORKSPACE_DIR = codeWorkspace
// Create dummy folder for routes.test.ts
const folder = await tmpdir(workspaceDir)
process.env.CODE_FOLDER_DIR = folder
console.log("✅ Global Setup for Playwright End-to-End Tests is now complete.")
}

View File

@@ -136,17 +136,3 @@ export async function getMaybeProxiedCodeServer(codeServer: CodeServerPage | Cod
return address
}
/**
* Stripes proxy base from url.pathname
* i.e. /<port>/ide + route returns just route
*/
export function getMaybeProxiedPathname(url: URL): string {
if (process.env.USE_PROXY === "1") {
// Behind proxy, path will be /<port>/ide + route
const pathWithoutProxy = url.pathname.split(`/${REVERSE_PROXY_BASE_PATH}`)[1]
return pathWithoutProxy
}
return url.pathname
}

View File

@@ -7,6 +7,7 @@ import { Disposable } from "../../src/common/emitter"
import * as util from "../../src/common/util"
import { ensureAddress } from "../../src/node/app"
import { disposer } from "../../src/node/http"
import { handleUpgrade } from "../../src/node/wsRouter"
// Perhaps an abstraction similar to this should be used in app.ts as well.
@@ -75,25 +76,14 @@ export class HttpServer {
/**
* Open a websocket against the request path.
*/
public ws(requestPath: string, options?: Websocket.ClientOptions): Websocket {
public ws(requestPath: string): Websocket {
const address = ensureAddress(this.hs, "ws")
if (typeof address === "string") {
throw new Error("Cannot open websocket to socket path")
}
address.pathname = requestPath
return new Websocket(address.toString(), options)
}
/**
* Open a websocket and wait for it to fully open.
*/
public wsWait(requestPath: string, options?: Websocket.ClientOptions): Promise<Websocket> {
const ws = this.ws(requestPath, options)
return new Promise<Websocket>((resolve, reject) => {
ws.on("error", (err) => reject(err))
ws.on("open", () => resolve(ws))
})
return new Websocket(address.toString())
}
public port(): number {

View File

@@ -1285,16 +1285,16 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
safe-buffer "~5.1.1"
cookiejar@^2.1.3:
version "2.1.4"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
version "2.1.3"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
cross-fetch@^3.0.4:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
dependencies:
node-fetch "2.6.7"
node-fetch "2.6.1"
cross-spawn@^7.0.3:
version "7.0.3"
@@ -2334,9 +2334,9 @@ jest@^27.3.1:
jest-cli "^27.4.7"
jpeg-js@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
version "0.4.3"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
js-tokens@^4.0.0:
version "4.0.0"
@@ -2395,9 +2395,11 @@ json-schema@^0.4.0:
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
json5@2.x, json5@^2.1.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
version "2.2.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
dependencies:
minimist "^1.2.5"
kleur@^3.0.3:
version "3.0.3"
@@ -2501,12 +2503,17 @@ mimic-fn@^2.1.0:
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@^3.0.3, minimatch@^3.0.4:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^3.0.0:
version "3.1.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
@@ -2547,7 +2554,19 @@ node-addon-api@^4.3.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-fetch@2.6.7, node-fetch@^2.6.5, node-fetch@^2.6.7:
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^2.6.1:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.5:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==

View File

@@ -22,8 +22,7 @@
"./test/node_modules/@types",
"./lib/vscode/src/vs/server/@types"
],
"downlevelIteration": true,
"resolveJsonModule": true
"downlevelIteration": true
},
"include": ["./src/**/*"],
"exclude": ["/test", "/lib", "/ci", "/doc"]

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