mirror of
https://github.com/coder/code-server.git
synced 2026-04-13 21:32:52 -05:00
Compare commits
141 Commits
release/v4
...
v4.14.1-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c19962930 | ||
|
|
56d10d82bf | ||
|
|
cef2aa22dc | ||
|
|
b5a9ef80e7 | ||
|
|
5d3c9edce4 | ||
|
|
9955cd91a4 | ||
|
|
fdeaba9581 | ||
|
|
40ff2e6049 | ||
|
|
73d42f7ea0 | ||
|
|
fb73742b2b | ||
|
|
ccb0d3a34f | ||
|
|
4a121edd16 | ||
|
|
f6db985712 | ||
|
|
6d00fc7f46 | ||
|
|
ee024f3f2d | ||
|
|
0703ef008c | ||
|
|
2109d1cf6a | ||
|
|
74af05dfbe | ||
|
|
2798322b03 | ||
|
|
b3b971480f | ||
|
|
6745a46034 | ||
|
|
43ef50b404 | ||
|
|
45c89856fd | ||
|
|
5f4ae75431 | ||
|
|
521ff44aed | ||
|
|
5708e6ce32 | ||
|
|
3f7db15fde | ||
|
|
8c99f41b90 | ||
|
|
e02d56dbfd | ||
|
|
aac5efa046 | ||
|
|
ff2764f7b1 | ||
|
|
bda6b631ab | ||
|
|
9bac70ea0d | ||
|
|
7722ef1437 | ||
|
|
951d8ac45e | ||
|
|
2e17735795 | ||
|
|
1da7cda39e | ||
|
|
c829d74203 | ||
|
|
39075b2cf3 | ||
|
|
aed1fc0119 | ||
|
|
6e411adb01 | ||
|
|
36ba646bcb | ||
|
|
278d0f2184 | ||
|
|
b954250018 | ||
|
|
2b72da22e6 | ||
|
|
3c838e2d02 | ||
|
|
f8d563c49b | ||
|
|
39e63af359 | ||
|
|
c995988b70 | ||
|
|
a44bd71043 | ||
|
|
19bcd043d7 | ||
|
|
c32a31d802 | ||
|
|
45ca68c362 | ||
|
|
8f1a70339d | ||
|
|
35209aa5b5 | ||
|
|
87f44599e5 | ||
|
|
71f01ec0f0 | ||
|
|
f8e2d00099 | ||
|
|
a485fec27c | ||
|
|
aa2276293a | ||
|
|
72eae01684 | ||
|
|
bca733de3d | ||
|
|
78282a1fd6 | ||
|
|
dbdd2edb62 | ||
|
|
a9d61daa91 | ||
|
|
6c08466b05 | ||
|
|
85e083580d | ||
|
|
b0431069a1 | ||
|
|
f9cc07926b | ||
|
|
be40eca5d9 | ||
|
|
9ba08907da | ||
|
|
d477972c68 | ||
|
|
a47cd81d8c | ||
|
|
c9fbcffd53 | ||
|
|
befa76d09d | ||
|
|
e0ece195c1 | ||
|
|
ac1fba8bde | ||
|
|
6d8ed77fb0 | ||
|
|
4fb87f920f | ||
|
|
36daac3031 | ||
|
|
45aef719d3 | ||
|
|
6d6c5e18d1 | ||
|
|
17bca521af | ||
|
|
82073743b1 | ||
|
|
71ff747c48 | ||
|
|
e5a2537aee | ||
|
|
bce6239801 | ||
|
|
93589edb61 | ||
|
|
bfcca5fcc0 | ||
|
|
a76c0c5742 | ||
|
|
6e1b9131e9 | ||
|
|
776d57b12b | ||
|
|
026879b78c | ||
|
|
401d423dfd | ||
|
|
96d9c5eb0f | ||
|
|
134e9b40b7 | ||
|
|
7c2aa8c417 | ||
|
|
d40a9742c0 | ||
|
|
67416b7b79 | ||
|
|
9e266db379 | ||
|
|
834d16df2a | ||
|
|
d835cb9865 | ||
|
|
9e9cbd846d | ||
|
|
077a8f6a6b | ||
|
|
4e280811f9 | ||
|
|
3eb35979f0 | ||
|
|
8377bd23df | ||
|
|
930791d249 | ||
|
|
5c21015dbe | ||
|
|
fa39d4761a | ||
|
|
87f606db2a | ||
|
|
06483bc59d | ||
|
|
9012ddfe79 | ||
|
|
1efc5f104e | ||
|
|
ef5865f506 | ||
|
|
48588ee542 | ||
|
|
ac6b5bd4e6 | ||
|
|
0ca6620ee6 | ||
|
|
907747d394 | ||
|
|
d8995ec0b8 | ||
|
|
c088e73063 | ||
|
|
83c3453f50 | ||
|
|
3182be634e | ||
|
|
1297e9ac88 | ||
|
|
1484bee621 | ||
|
|
f43082e142 | ||
|
|
b6adcf50c6 | ||
|
|
df49838739 | ||
|
|
2cbb1135e1 | ||
|
|
43e8c439bf | ||
|
|
74edfe7c43 | ||
|
|
69d469b714 | ||
|
|
e2f585f368 | ||
|
|
3367e1ad4e | ||
|
|
c885c96644 | ||
|
|
b978655c07 | ||
|
|
5a8bb2b8e8 | ||
|
|
77c3cd3aa1 | ||
|
|
ff8ea36b27 | ||
|
|
f56ce5b66d | ||
|
|
2530a0d265 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -3,3 +3,5 @@
|
||||
ci/helm-chart/ @Matthew-Beckett @alexgorbatchev
|
||||
|
||||
docs/install.md @GNUxeava
|
||||
|
||||
src/node/i18n/locales/zh-cn.json @zhaozhiming
|
||||
|
||||
31
.github/dependabot.yaml
vendored
Normal file
31
.github/dependabot.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
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
|
||||
281
.github/workflows/build.yaml
vendored
281
.github/workflows/build.yaml
vendored
@@ -24,7 +24,7 @@ concurrency:
|
||||
jobs:
|
||||
prettier:
|
||||
name: Format with Prettier
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
doctoc:
|
||||
name: Doctoc markdown files
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v26.1
|
||||
uses: tj-actions/changed-files@v36
|
||||
with:
|
||||
files: |
|
||||
docs/**
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
lint-helm:
|
||||
name: Lint Helm chart
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -76,14 +76,14 @@ jobs:
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v26.1
|
||||
uses: tj-actions/changed-files@v36
|
||||
with:
|
||||
files: |
|
||||
ci/helm-chart/**
|
||||
|
||||
- name: Install helm
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: azure/setup-helm@v3.4
|
||||
uses: azure/setup-helm@v3.5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
|
||||
lint-ts:
|
||||
name: Lint TypeScript files
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v26.1
|
||||
uses: tj-actions/changed-files@v36
|
||||
with:
|
||||
files: |
|
||||
**/*.ts
|
||||
@@ -139,9 +139,70 @@ 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@v36
|
||||
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-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -170,26 +231,24 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
|
||||
key: yarn-build-code-server-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
yarn-build-
|
||||
yarn-build-code-server-
|
||||
|
||||
- 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 "::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)"
|
||||
run: echo "rev=$(git rev-parse HEAD:./lib/vscode)" >> $GITHUB_OUTPUT
|
||||
|
||||
# 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
|
||||
@@ -200,25 +259,14 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: lib/vscode-reh-web-*
|
||||
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') }}
|
||||
key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ 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
|
||||
@@ -235,119 +283,10 @@ 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-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
|
||||
- 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: Download npm package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: npm-package
|
||||
|
||||
- name: Decompress npm package
|
||||
run: tar -xzf package.tar.gz
|
||||
|
||||
- name: Install release package dependencies
|
||||
run: cd release && yarn install
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
|
||||
|
||||
- name: Install Playwright OS dependencies
|
||||
run: |
|
||||
./test/node_modules/.bin/playwright install-deps
|
||||
./test/node_modules/.bin/playwright install
|
||||
|
||||
- name: Run end-to-end tests
|
||||
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e --global-timeout 840000
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: failed-test-videos
|
||||
path: ./test/test-results
|
||||
|
||||
- name: Remove release packages and test artifacts
|
||||
run: rm -rf ./release ./test/test-results
|
||||
|
||||
test-e2e-proxy:
|
||||
name: Run e2e tests behind proxy
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -376,7 +315,63 @@ jobs:
|
||||
run: tar -xzf package.tar.gz
|
||||
|
||||
- name: Install release package dependencies
|
||||
run: cd release && yarn install
|
||||
run: cd release && npm install --unsafe-perm --omit=dev
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
run: SKIP_SUBMODULE_DEPS=1 yarn --frozen-lockfile
|
||||
|
||||
- name: Install Playwright OS dependencies
|
||||
run: |
|
||||
./test/node_modules/.bin/playwright install-deps
|
||||
./test/node_modules/.bin/playwright install
|
||||
|
||||
- name: Run end-to-end tests
|
||||
run: CODE_SERVER_TEST_ENTRY=./release yarn test:e2e
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: failed-test-videos
|
||||
path: ./test/test-results
|
||||
|
||||
- name: Remove release packages and test artifacts
|
||||
run: rm -rf ./release ./test/test-results
|
||||
|
||||
test-e2e-proxy:
|
||||
name: Run e2e tests behind proxy
|
||||
needs: build
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
|
||||
- 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: Download npm package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: npm-package
|
||||
|
||||
- name: Decompress npm package
|
||||
run: tar -xzf package.tar.gz
|
||||
|
||||
- name: Install release package dependencies
|
||||
run: cd release && npm install --unsafe-perm --omit=dev
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
@@ -388,7 +383,7 @@ jobs:
|
||||
./test/node_modules/.bin/playwright install
|
||||
|
||||
- name: Cache Caddy
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
id: caddy-cache
|
||||
with:
|
||||
path: |
|
||||
@@ -401,7 +396,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
46
.github/workflows/docs-preview.yaml
vendored
@@ -1,46 +0,0 @@
|
||||
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 }}
|
||||
2
.github/workflows/installer.yaml
vendored
2
.github/workflows/installer.yaml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
alpine:
|
||||
name: Test installer on Alpine
|
||||
runs-on: ubuntu-latest
|
||||
container: "alpine:3.16"
|
||||
container: "alpine:3.17"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
79
.github/workflows/publish.yaml
vendored
79
.github/workflows/publish.yaml
vendored
@@ -4,6 +4,11 @@ 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]
|
||||
@@ -24,21 +29,31 @@ jobs:
|
||||
- name: Checkout code-server
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "::set-output name=version::$(jq -r .version package.json)"
|
||||
- name: Install Node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Download npm package from release artifacts
|
||||
uses: robinraju/release-downloader@v1.5
|
||||
uses: robinraju/release-downloader@v1.8
|
||||
with:
|
||||
repository: "coder/code-server"
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
tag: ${{ github.event.inputs.version || github.ref_name }}
|
||||
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"
|
||||
@@ -62,9 +77,18 @@ 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:
|
||||
@@ -73,6 +97,7 @@ 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
|
||||
@@ -81,13 +106,6 @@ 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:
|
||||
@@ -106,22 +124,33 @@ 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: ${{ steps.version.outputs.version }}
|
||||
pkgver: ${{ env.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-${{ steps.version.outputs.version }}
|
||||
git add .
|
||||
git commit -m "chore: updating version to ${{ steps.version.outputs.version }}"
|
||||
git checkout -b update-version-${{ env.VERSION }}
|
||||
git add .
|
||||
git commit -m "chore: updating version to ${{ env.VERSION }}"
|
||||
git push -u origin $(git branch --show)
|
||||
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
|
||||
gh pr create --repo coder/code-server-aur --title "chore: bump version to ${{ env.VERSION }}" --body "PR opened by @$GITHUB_ACTOR" --assignee $GITHUB_ACTOR
|
||||
docker:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
@@ -147,19 +176,23 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "::set-output name=version::$(jq -r .version package.json)"
|
||||
# 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: Download release artifacts
|
||||
uses: robinraju/release-downloader@v1.5
|
||||
uses: robinraju/release-downloader@v1.8
|
||||
with:
|
||||
repository: "coder/code-server"
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
tag: v${{ env.VERSION }}
|
||||
fileName: "*.deb"
|
||||
out-file-path: "release-packages"
|
||||
|
||||
- name: Publish to Docker
|
||||
run: yarn publish:docker
|
||||
run: ./ci/steps/docker-buildx-push.sh
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
158
.github/workflows/release.yaml
vendored
158
.github/workflows/release.yaml
vendored
@@ -2,6 +2,11 @@ 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.
|
||||
@@ -21,6 +26,7 @@ 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 }}
|
||||
@@ -38,11 +44,13 @@ 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.3.1/nfpm_2.3.1_`uname -s`_`uname -m`.tar.gz | tar -C ~/.local/bin -zxv nfpm
|
||||
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/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst
|
||||
chmod +x envsubst
|
||||
mv envsubst ~/.local/bin
|
||||
@@ -51,15 +59,10 @@ jobs:
|
||||
- name: Install yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Download artifacts
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
id: download
|
||||
- name: Download npm package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
branch: ${{ github.ref }}
|
||||
workflow: build.yaml
|
||||
workflow_conclusion: completed
|
||||
check_artifacts: true
|
||||
name: npm-package
|
||||
name: npm-release-package
|
||||
|
||||
- name: Decompress npm package
|
||||
run: tar -xzf package.tar.gz
|
||||
@@ -67,19 +70,9 @@ 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 && 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-
|
||||
run: source scl_source enable devtoolset-9 && npm run release:standalone
|
||||
|
||||
- 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
|
||||
@@ -91,7 +84,16 @@ 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
|
||||
@@ -117,12 +119,13 @@ jobs:
|
||||
# but this means we don't need to maintain a self-hosted runner!
|
||||
|
||||
# NOTE@jsjoeio:
|
||||
# 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
|
||||
# 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
|
||||
package-linux-cross:
|
||||
name: Linux cross-compile builds
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 15
|
||||
needs: npm-version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -159,19 +162,16 @@ jobs:
|
||||
env:
|
||||
PACKAGE: ${{ format('g++-{0}', matrix.prefix) }}
|
||||
|
||||
- name: Download artifacts
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
id: download
|
||||
- name: Download npm package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
branch: ${{ github.ref }}
|
||||
workflow: build.yaml
|
||||
workflow_conclusion: completed
|
||||
check_artifacts: true
|
||||
name: npm-package
|
||||
name: npm-release-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,7 +181,16 @@ 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
|
||||
@@ -194,6 +203,7 @@ jobs:
|
||||
name: x86-64 macOS build
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 15
|
||||
needs: npm-version
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -209,39 +219,33 @@ 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 artifacts
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
id: download
|
||||
- name: Download npm package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
branch: ${{ github.ref }}
|
||||
workflow: build.yaml
|
||||
workflow_conclusion: completed
|
||||
check_artifacts: true
|
||||
name: npm-package
|
||||
name: npm-release-package
|
||||
|
||||
- name: Decompress npm package
|
||||
run: tar -xzf package.tar.gz
|
||||
|
||||
- name: Build standalone release
|
||||
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-
|
||||
run: npm run release:standalone
|
||||
|
||||
- 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
|
||||
@@ -254,6 +258,23 @@ 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
|
||||
@@ -262,11 +283,38 @@ jobs:
|
||||
branch: ${{ github.ref }}
|
||||
workflow: build.yaml
|
||||
workflow_conclusion: completed
|
||||
check_artifacts: true
|
||||
name: npm-package
|
||||
check_artifacts: false
|
||||
if_no_artifact_found: fail
|
||||
|
||||
- uses: softprops/action-gh-release@v1
|
||||
- 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
|
||||
with:
|
||||
draft: true
|
||||
discussion_category_name: "📣 Announcements"
|
||||
files: ./package.tar.gz
|
||||
name: npm-release-package
|
||||
path: ./package.tar.gz
|
||||
|
||||
2
.github/workflows/scripts.yaml
vendored
2
.github/workflows/scripts.yaml
vendored
@@ -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.16"
|
||||
container: "alpine:3.17"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
7
.github/workflows/security.yaml
vendored
7
.github/workflows/security.yaml
vendored
@@ -12,9 +12,8 @@ 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.
|
||||
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run
|
||||
# Cancel in-progress runs for pull requests when developers push additional
|
||||
# changes, and serialize builds in branches.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
@@ -65,7 +64,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run Trivy vulnerability scanner in repo mode
|
||||
uses: aquasecurity/trivy-action@e55de85beea5fcec743de6bb6bc56943a0af3c33
|
||||
uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2
|
||||
with:
|
||||
scan-type: "fs"
|
||||
scan-ref: "."
|
||||
|
||||
2
.github/workflows/trivy-docker.yaml
vendored
2
.github/workflows/trivy-docker.yaml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run Trivy vulnerability scanner in image mode
|
||||
uses: aquasecurity/trivy-action@e55de85beea5fcec743de6bb6bc56943a0af3c33
|
||||
uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2
|
||||
with:
|
||||
image-ref: "docker.io/codercom/code-server:latest"
|
||||
ignore-unfixed: true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
lib/vscode
|
||||
lib/vscode-reh-web-linux-x64
|
||||
release-standalone
|
||||
release-packages
|
||||
release
|
||||
helm-chart
|
||||
test/scripts
|
||||
|
||||
153
CHANGELOG.md
153
CHANGELOG.md
@@ -20,6 +20,159 @@ Code v99.99.999
|
||||
|
||||
-->
|
||||
|
||||
## Unreleased
|
||||
|
||||
Code v1.79.2
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix being unable to launch multiple instances of code-server for different
|
||||
users.
|
||||
|
||||
### Added
|
||||
|
||||
- `--session-socket` CLI flag to configure the location of the session socket.
|
||||
By default it will be placed in `--user-data-dir`.
|
||||
|
||||
## [4.14.0](https://github.com/coder/code-server/releases/tag/v4.14.0) - 2023-06-16
|
||||
|
||||
Code v1.79.2
|
||||
|
||||
### Added
|
||||
|
||||
- `--domain-proxy` now supports `{{port}}` and `{{host}}` template variables.
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated to Code 1.79.2
|
||||
- Files opened from an external terminal will now open in the most closely
|
||||
related window rather than in the last opened window.
|
||||
|
||||
## [4.13.0](https://github.com/coder/code-server/releases/tag/v4.13.0) - 2023-05-19
|
||||
|
||||
Code v1.78.2
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated to Code 1.78.2.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Proxying files that contain non-ASCII characters.
|
||||
- Origin check when X-Forwarded-Host contains comma-separated hosts.
|
||||
|
||||
## [4.12.0](https://github.com/coder/code-server/releases/tag/v4.12.0) - 2023-04-21
|
||||
|
||||
Code v1.77.3
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated to Code 1.77.3
|
||||
- Ports panel will use domain-based proxy (instead of the default path-based
|
||||
proxy) when set via --proxy-domain.
|
||||
- Apply --app-name to the PWA title.
|
||||
|
||||
### Added
|
||||
|
||||
- Thai translation for login page.
|
||||
- Debug logs around the origin security check. If you are getting forbidden
|
||||
errors on web sockets please run code-server with `--log debug` to see why the
|
||||
requests are being blocked.
|
||||
|
||||
## [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.
|
||||
|
||||
## [4.8.2](https://github.com/coder/code-server/releases/tag/v4.8.2) - 2022-11-02
|
||||
|
||||
Code v1.72.1
|
||||
|
||||
### Added
|
||||
|
||||
- New text in the Getting Started page with info about
|
||||
`coder/coder`. This is enabled by default but can be disabled by passing the CLI
|
||||
flag `--disable-getting-started-override` or setting
|
||||
`CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or
|
||||
`CS_DISABLE_GETTING_STARTED_OVERRIDE=true`.
|
||||
|
||||
## [4.8.1](https://github.com/coder/code-server/releases/tag/v4.8.1) - 2022-10-28
|
||||
|
||||
Code v1.72.1
|
||||
|
||||
@@ -29,7 +29,7 @@ This directory contains scripts used for the development of code-server.
|
||||
- [./ci/dev/watch.ts](./dev/watch.ts) (`yarn watch`)
|
||||
- Starts a process to build and launch code-server and restart on any code changes.
|
||||
- Example usage in [./docs/CONTRIBUTING.md](../docs/CONTRIBUTING.md).
|
||||
- [./ci/dev/gen_icons.sh](./ci/dev/gen_icons.sh) (`yarn icons`)
|
||||
- [./ci/dev/gen_icons.sh](./dev/gen_icons.sh) (`yarn icons`)
|
||||
- Generates the various icons from a single `.svg` favicon in
|
||||
`src/browser/media/favicon.svg`.
|
||||
- Requires [imagemagick](https://imagemagick.org/index.php)
|
||||
@@ -75,7 +75,7 @@ You can disable minification by setting `MINIFY=`.
|
||||
|
||||
This directory contains the release docker container image.
|
||||
|
||||
- [./ci/steps/build-docker-buildx-push.sh](./ci/steps/docker-buildx-push.sh)
|
||||
- [./ci/steps/build-docker-buildx-push.sh](./steps/docker-buildx-push.sh)
|
||||
- Builds the release containers with tags `codercom/code-server-$ARCH:$VERSION` for amd64 and arm64 with `docker buildx` and pushes them.
|
||||
- Assumes debian releases are ready in `./release-packages`.
|
||||
|
||||
|
||||
@@ -14,22 +14,6 @@ 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 "$@"
|
||||
|
||||
@@ -63,8 +63,6 @@ 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
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ main() {
|
||||
rsync ./ci/build/code-server.sh "$RELEASE_PATH/bin/code-server"
|
||||
rsync "$node_path" "$RELEASE_PATH/lib/node"
|
||||
|
||||
chmod 755 "$RELEASE_PATH/lib/node"
|
||||
|
||||
pushd "$RELEASE_PATH"
|
||||
npm install --unsafe-perm --omit=dev
|
||||
popd
|
||||
|
||||
@@ -15,7 +15,7 @@ copy-bin-script() {
|
||||
local dest="lib/vscode-reh-web-linux-x64/bin/$script"
|
||||
cp "lib/vscode/resources/server/bin/$script" "$dest"
|
||||
sed -i.bak "s/@@VERSION@@/$(vscode_version)/g" "$dest"
|
||||
sed -i.bak "s/@@COMMIT@@/$VSCODE_DISTRO_COMMIT/g" "$dest"
|
||||
sed -i.bak "s/@@COMMIT@@/$BUILD_SOURCEVERSION/g" "$dest"
|
||||
sed -i.bak "s/@@APPNAME@@/code-server/g" "$dest"
|
||||
|
||||
# Fix Node path on Darwin and Linux.
|
||||
@@ -42,11 +42,17 @@ 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.
|
||||
export VSCODE_DISTRO_COMMIT
|
||||
VSCODE_DISTRO_COMMIT=$(git rev-parse HEAD)
|
||||
export BUILD_SOURCEVERSION
|
||||
BUILD_SOURCEVERSION=$(git rev-parse HEAD)
|
||||
|
||||
# Add the date, our name, links, and enable telemetry (this just makes
|
||||
# telemetry available; telemetry can still be disabled by flag or setting).
|
||||
@@ -103,6 +109,15 @@ EOF
|
||||
|
||||
popd
|
||||
|
||||
pushd lib/vscode-reh-web-linux-x64
|
||||
# Make sure Code took the version we set in the environment variable. Not
|
||||
# having a version will break display languages.
|
||||
if ! jq -e .commit product.json; then
|
||||
echo "'commit' is missing from product.json"
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
|
||||
# These provide a `code-server` command in the integrated terminal to open
|
||||
# files in the current instance.
|
||||
delete-bin-script remote-cli/code-server
|
||||
|
||||
@@ -4,7 +4,7 @@ platform: "linux"
|
||||
version: "v${VERSION}"
|
||||
section: "devel"
|
||||
priority: "optional"
|
||||
maintainer: "Anmol Sethi <hi@nhooyr.io>"
|
||||
maintainer: "Joe Previte <joe@coder.com>"
|
||||
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
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
# 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.
|
||||
# Copied from ../lib.sh except we do not rename Darwin and we do not need to
|
||||
# detect Alpine.
|
||||
os() {
|
||||
osname=$(uname | tr '[:upper:]' '[:lower:]')
|
||||
case $osname in
|
||||
@@ -61,7 +51,6 @@ 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
|
||||
@@ -102,14 +91,6 @@ 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"
|
||||
@@ -124,27 +105,18 @@ 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
|
||||
yarn*)
|
||||
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
|
||||
# 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
|
||||
;;
|
||||
*)
|
||||
echo "Could not determine which package manager is being used to install code-server"
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/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 "$@"
|
||||
@@ -11,22 +11,6 @@ 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { spawn, fork, ChildProcess } from "child_process"
|
||||
import { spawn, ChildProcess } from "child_process"
|
||||
import * as path from "path"
|
||||
import { onLine, OnLineCallback } from "../../src/node/util"
|
||||
|
||||
@@ -30,12 +30,13 @@ class Watcher {
|
||||
|
||||
// Pass CLI args, save for `node` and the initial script name.
|
||||
const args = process.argv.slice(2)
|
||||
this.webServer = fork(path.join(this.rootPath, "out/node/entry.js"), args)
|
||||
this.webServer = spawn("node", [path.join(this.rootPath, "out/node/entry.js"), ...args])
|
||||
onLine(this.webServer, (line) => console.log("[code-server]", line))
|
||||
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
|
||||
@@ -82,10 +83,10 @@ class Watcher {
|
||||
private parseVSCodeLine: OnLineCallback = (strippedLine, originalLine) => {
|
||||
if (!strippedLine.length) return
|
||||
|
||||
console.log("[VS Code]", originalLine)
|
||||
console.log("[Code OSS]", originalLine)
|
||||
|
||||
if (strippedLine.includes("Finished compilation with")) {
|
||||
console.log("[VS Code] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)")
|
||||
console.log("[Code OSS] ✨ Finished compiling! ✨", "(Refresh your web browser ♻️)")
|
||||
this.reloadWebServer()
|
||||
}
|
||||
}
|
||||
@@ -93,10 +94,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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.3.1
|
||||
version: 3.10.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
appVersion: 4.8.1
|
||||
appVersion: 4.14.0
|
||||
|
||||
@@ -102,6 +102,7 @@ spec:
|
||||
{{- range .Values.extraSecretMounts }}
|
||||
- name: {{ .name }}
|
||||
mountPath: {{ .mountPath }}
|
||||
subPath: {{ .subPath | default "" }}
|
||||
readOnly: {{ .readOnly }}
|
||||
{{- end }}
|
||||
{{- range .Values.extraVolumeMounts }}
|
||||
@@ -114,6 +115,11 @@ spec:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
{{- range .Values.extraPorts }}
|
||||
- name: {{ .name }}
|
||||
containerPort: {{ .port }}
|
||||
protocol: {{ .protocol }}
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
|
||||
@@ -14,6 +14,12 @@ 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 }}
|
||||
|
||||
@@ -6,7 +6,7 @@ replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: codercom/code-server
|
||||
tag: '4.8.1'
|
||||
tag: '4.14.0'
|
||||
pullPolicy: Always
|
||||
|
||||
# Specifies one or more secrets to be used when pulling images from a
|
||||
@@ -179,6 +179,7 @@ extraInitContainers: |
|
||||
extraSecretMounts: []
|
||||
# - name: secret-files
|
||||
# mountPath: /etc/secrets
|
||||
# subPath: private.key # (optional)
|
||||
# secretName: code-server-secret-files
|
||||
# readOnly: true
|
||||
|
||||
@@ -196,3 +197,8 @@ extraConfigmapMounts: []
|
||||
# subPath: certificates.crt # (optional)
|
||||
# configMap: certs-configmap
|
||||
# readOnly: true
|
||||
|
||||
extraPorts: []
|
||||
# - name: minecraft
|
||||
# port: 25565
|
||||
# protocol: tcp
|
||||
|
||||
@@ -9,10 +9,6 @@ popd() {
|
||||
builtin popd > /dev/null
|
||||
}
|
||||
|
||||
pkg_json_version() {
|
||||
jq -r .version package.json
|
||||
}
|
||||
|
||||
vscode_version() {
|
||||
jq -r .version lib/vscode/package.json
|
||||
}
|
||||
@@ -48,8 +44,6 @@ rsync() {
|
||||
command rsync -a --del "$@"
|
||||
}
|
||||
|
||||
VERSION="$(pkg_json_version)"
|
||||
export VERSION
|
||||
ARCH="$(arch)"
|
||||
export ARCH
|
||||
OS=$(os)
|
||||
|
||||
@@ -3,9 +3,8 @@ set -euo pipefail
|
||||
|
||||
main() {
|
||||
cd "$(dirname "$0")/../.."
|
||||
# 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 VERSION exists as an
|
||||
# environment variable.
|
||||
|
||||
# NOTE@jsjoeio - this script assumes that you've downloaded
|
||||
# the release-packages artifact to ./release-packages before
|
||||
|
||||
@@ -19,12 +19,6 @@ 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."
|
||||
@@ -102,6 +96,7 @@ 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
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
- [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)
|
||||
@@ -170,6 +171,22 @@ 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
|
||||
@@ -178,7 +195,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`.
|
||||
|
||||
|
||||
59
docs/FAQ.md
59
docs/FAQ.md
@@ -26,6 +26,7 @@
|
||||
- [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)
|
||||
@@ -33,6 +34,7 @@
|
||||
- [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 -->
|
||||
@@ -86,6 +88,12 @@ 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?
|
||||
@@ -362,6 +370,15 @@ 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
|
||||
@@ -379,19 +396,13 @@ 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. 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.
|
||||
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.
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
## What's the difference between code-server and GitHub Codespaces?
|
||||
|
||||
@@ -399,8 +410,24 @@ 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.
|
||||
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.
|
||||
|
||||
## Does code-server have any security login validation?
|
||||
|
||||
@@ -425,3 +452,7 @@ 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`
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
- [Changelog](#changelog)
|
||||
- [Releases](#releases)
|
||||
- [Publishing a release](#publishing-a-release)
|
||||
- [Release Candidates](#release-candidates)
|
||||
- [AUR](#aur)
|
||||
- [Docker](#docker)
|
||||
- [Homebrew](#homebrew)
|
||||
@@ -141,16 +142,28 @@ changelog](https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS).
|
||||
|
||||
### Publishing a release
|
||||
|
||||
1. Create a new branch called `release`
|
||||
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`.
|
||||
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.
|
||||
|
||||
#### 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:
|
||||
|
||||
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.
|
||||
|
||||
#### AUR
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ access it in the browser.
|
||||
|
||||
## Requirements
|
||||
|
||||
See [requirements](requirements.md) for minimum specs, as well as instructions
|
||||
See [requirements](https://coder.com/docs/code-server/latest/requirements) 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
|
||||
|
||||
@@ -21,3 +21,11 @@ nvm use 16
|
||||
8. Install code-server globally on device with: `npm install --global code-server --unsafe-perm`
|
||||
9. Run code-server with `code-server`
|
||||
10. Access on localhost:8080 in your browser
|
||||
|
||||
# Running code-server using Nix-on-Droid
|
||||
|
||||
1. Install Nix-on-Droid from [F-Droid](https://f-droid.org/packages/com.termux.nix/)
|
||||
2. Start app
|
||||
3. Spawn a shell with code-server by running `nix-shell -p code-server`
|
||||
4. Run code-server with `code-server`
|
||||
5. Access on localhost:8080 in your browser
|
||||
|
||||
37
docs/coder.md
Normal file
37
docs/coder.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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).
|
||||
@@ -14,6 +14,7 @@
|
||||
- [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)
|
||||
@@ -316,12 +317,32 @@ To set your domain, start code-server with the `--proxy-domain` flag:
|
||||
code-server --proxy-domain <domain>
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Using a subpath
|
||||
|
||||
Simply browse to `/proxy/<port>/`.
|
||||
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}}`
|
||||
|
||||
### Stripping `/proxy/<port>` from the request path
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ 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.
|
||||
@@ -296,9 +297,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).
|
||||
|
||||
> 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.
|
||||
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).
|
||||
|
||||
## Raspberry Pi
|
||||
|
||||
|
||||
11
docs/link.md
11
docs/link.md
@@ -1,11 +0,0 @@
|
||||
# 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
|
||||
```
|
||||
@@ -38,9 +38,9 @@
|
||||
"path": "./guide.md",
|
||||
"children": [
|
||||
{
|
||||
"title": "--link",
|
||||
"description": "How to run code-server --link",
|
||||
"path": "./link.md"
|
||||
"title": "Coder",
|
||||
"description": "How to run code-server in Coder",
|
||||
"path": "./coder.md"
|
||||
},
|
||||
{
|
||||
"title": "iPad",
|
||||
|
||||
@@ -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 application for `.sh` files. 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 shell for npm run-scripts. 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:
|
||||
|
||||
|
||||
115
docs/termux.md
115
docs/termux.md
@@ -8,11 +8,11 @@
|
||||
- [Upgrade](#upgrade)
|
||||
- [Known Issues](#known-issues)
|
||||
- [Git won't work in `/sdcard`](#git-wont-work-in-sdcard)
|
||||
- [Many extensions including language packs fail to install](#many-extensions-including-language-packs-fail-to-install)
|
||||
- [Extra](#extra)
|
||||
- [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 -->
|
||||
@@ -20,53 +20,9 @@
|
||||
## Install
|
||||
|
||||
1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**.
|
||||
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).
|
||||
2. Run `pkg install tur-repo`
|
||||
3. Run `pkg install code-server`
|
||||
4. You can now start code server by simply running `code-server`.
|
||||
|
||||
## NPM Installation
|
||||
|
||||
@@ -132,6 +88,37 @@ Potential Workaround :
|
||||
1. Create a soft-link from the debian-fs to your folder in `/sdcard`
|
||||
2. Use git from termux (preferred)
|
||||
|
||||
### Many extensions including language packs fail to install
|
||||
|
||||
Issue: Android is not seen as a Linux environment but as a separate, unsupported platform, so code-server only allows [Web Extensions](https://code.visualstudio.com/api/extension-guides/web-extensions), refusing to download extensions that run on the server.\
|
||||
Fix: None\
|
||||
Potential workarounds :
|
||||
|
||||
Either
|
||||
|
||||
- Manually download extensions as `.vsix` file and install them via `Extensions: Install from VSIX...` in the Command Palette.
|
||||
|
||||
- Use an override to pretend the platform is Linux:
|
||||
|
||||
Create a JS script that patches `process.platform`:
|
||||
|
||||
```js
|
||||
// android-as-linux.js
|
||||
Object.defineProperty(process, "platform", {
|
||||
get() {
|
||||
return "linux"
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Then use Node's `--require` option to make sure it is loaded before `code-server` starts:
|
||||
|
||||
```sh
|
||||
NODE_OPTIONS="--require /path/to/android-as-linux.js" code-server
|
||||
```
|
||||
|
||||
⚠️ Note that Android and Linux are not 100% compatible, so use these workarounds at your own risk. Extensions that have native dependencies other than Node or that directly interact with the OS might cause issues.
|
||||
|
||||
## Extra
|
||||
|
||||
### Create a new user
|
||||
@@ -202,35 +189,3 @@ 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).
|
||||
|
||||
30
flake.lock
generated
30
flake.lock
generated
@@ -1,12 +1,15 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -17,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1660639432,
|
||||
"narHash": "sha256-2WDiboOCfB0LhvnDVMXOAr8ZLDfm3WdO54CkoDPwN1A=",
|
||||
"lastModified": 1683594133,
|
||||
"narHash": "sha256-iUhLhEAgOCnexSGDsYT2ouydis09uDoNzM7UC685XGE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6c6409e965a6c883677be7b9d87a95fab6c3472e",
|
||||
"rev": "8d447c5626cfefb9b129d5b30103344377fe09bc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -34,6 +37,21 @@
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
in {
|
||||
devShells.default = pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
nodejs yarn' python pkg-config git rsync jq moreutils
|
||||
nodejs yarn' python3 pkg-config git rsync jq moreutils quilt bats
|
||||
];
|
||||
buildInputs = with pkgs; (lib.optionals (!stdenv.isDarwin) [ libsecret ]
|
||||
++ (with xorg; [ libX11 libxkbfile ])
|
||||
++ lib.optionals stdenv.isDarwin [
|
||||
AppKit Cocoa CoreServices Security cctools xcbuild
|
||||
]);
|
||||
++ lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
|
||||
AppKit Cocoa CoreServices Security xcbuild
|
||||
]));
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
16
install.sh
16
install.sh
@@ -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.
|
||||
@@ -131,6 +131,11 @@ Or, if you don't want/need a background service you can run:
|
||||
EOF
|
||||
}
|
||||
|
||||
echo_coder_postinstall() {
|
||||
echoh
|
||||
echoh "Deploy code-server for your team with Coder: https://github.com/coder/coder"
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "${TRACE-}" ]; then
|
||||
set -x
|
||||
@@ -243,6 +248,7 @@ main() {
|
||||
if [ "$METHOD" = standalone ]; then
|
||||
if has_standalone; then
|
||||
install_standalone
|
||||
echo_coder_postinstall
|
||||
exit 0
|
||||
else
|
||||
echoerr "There are no standalone releases for $ARCH"
|
||||
@@ -286,6 +292,8 @@ main() {
|
||||
npm_fallback install_standalone
|
||||
;;
|
||||
esac
|
||||
|
||||
echo_coder_postinstall
|
||||
}
|
||||
|
||||
parse_arg() {
|
||||
@@ -379,7 +387,7 @@ install_aur() {
|
||||
if [ ! "${DRY_RUN-}" ]; then
|
||||
cd "$CACHE_DIR/code-server-aur"
|
||||
fi
|
||||
sh_c makepkg -si
|
||||
sh_c makepkg -si --noconfirm
|
||||
|
||||
echo_systemd_postinstall AUR
|
||||
}
|
||||
@@ -482,7 +490,7 @@ os() {
|
||||
# - amzn, centos, rhel, fedora, ... -> fedora
|
||||
# - opensuse-{leap,tumbleweed} -> opensuse
|
||||
# - alpine -> alpine
|
||||
# - arch -> arch
|
||||
# - arch, manjaro, endeavouros, ... -> arch
|
||||
#
|
||||
# Inspired by https://github.com/docker/docker-install/blob/26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c/install.sh#L111-L120.
|
||||
distro() {
|
||||
@@ -496,7 +504,7 @@ distro() {
|
||||
. /etc/os-release
|
||||
if [ "${ID_LIKE-}" ]; then
|
||||
for id_like in $ID_LIKE; do
|
||||
case "$id_like" in debian | fedora | opensuse)
|
||||
case "$id_like" in debian | fedora | opensuse | arch)
|
||||
echo "$id_like"
|
||||
return
|
||||
;;
|
||||
|
||||
Submodule lib/vscode updated: 129500ee4c...695af097c7
21
package.json
21
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-server",
|
||||
"license": "MIT",
|
||||
"version": "4.8.1",
|
||||
"version": "0.0.0",
|
||||
"description": "Run VS Code on a remote server.",
|
||||
"homepage": "https://github.com/coder/code-server",
|
||||
"bugs": {
|
||||
@@ -49,7 +49,6 @@
|
||||
"@types/proxy-from-env": "^1.0.1",
|
||||
"@types/safe-compare": "^1.1.0",
|
||||
"@types/semver": "^7.1.0",
|
||||
"@types/split2": "^3.2.0",
|
||||
"@types/trusted-types": "^2.0.2",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.41.0",
|
||||
@@ -61,10 +60,10 @@
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "2.7.1",
|
||||
"prettier": "2.8.0",
|
||||
"prettier-plugin-sh": "^0.12.8",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^4.6.2"
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
@@ -83,27 +82,28 @@
|
||||
"nanoid": "^3.1.31",
|
||||
"minimist": "npm:minimist-lite@2.2.1",
|
||||
"glob-parent": "^6.0.1",
|
||||
"@types/node": "^16.0.0"
|
||||
"@types/node": "^16.0.0",
|
||||
"qs": "^6.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coder/logger": "^3.0.0",
|
||||
"argon2": "0.29.0",
|
||||
"argon2": "0.30.3",
|
||||
"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": "^1.1.5",
|
||||
"limiter": "^2.1.0",
|
||||
"pem": "^1.14.2",
|
||||
"proxy-agent": "^5.0.0",
|
||||
"proxy-agent": "^6.2.1",
|
||||
"qs": "6.11.0",
|
||||
"rotating-file-stream": "^3.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"safe-compare": "^1.1.4",
|
||||
"semver": "^7.1.3",
|
||||
"split2": "^4.0.0",
|
||||
"ws": "^8.0.0",
|
||||
"xdg-basedir": "^4.0.0"
|
||||
},
|
||||
@@ -116,7 +116,8 @@
|
||||
"ide",
|
||||
"coder",
|
||||
"vscode-remote",
|
||||
"browser-ide"
|
||||
"browser-ide",
|
||||
"remote-development"
|
||||
],
|
||||
"engines": {
|
||||
"node": "16"
|
||||
|
||||
@@ -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
|
||||
@@ -162,7 +162,9 @@ class RemoteAuthoritiesImpl {
|
||||
@@ -167,7 +167,9 @@ class RemoteAuthoritiesImpl {
|
||||
return URI.from({
|
||||
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
|
||||
authority: `${host}:${port}`,
|
||||
@@ -99,27 +99,20 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
|
||||
+++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
|
||||
@@ -274,6 +274,7 @@ export class BrowserSocketFactory implem
|
||||
|
||||
connect(host: string, port: number, path: string, query: string, debugLabel: string, callback: IConnectCallback): void {
|
||||
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
|
||||
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
|
||||
const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
|
||||
const errorListener = socket.onError((err) => callback(err, undefined));
|
||||
socket.onOpen(() => {
|
||||
@@ -282,6 +283,3 @@ export class BrowserSocketFactory implem
|
||||
});
|
||||
}
|
||||
}
|
||||
-
|
||||
-
|
||||
-
|
||||
@@ -280,6 +280,7 @@ export class BrowserSocketFactory implem
|
||||
connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> {
|
||||
return new Promise<ISocket>((resolve, reject) => {
|
||||
const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws');
|
||||
+ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/")
|
||||
const socket = this._webSocketFactory.create(`${webSocketSchema}://${(/:/.test(host) && !/\[/.test(host)) ? `[${host}]` : host}:${port}${path}?${query}&skipWebSocketFrames=false`, debugLabel);
|
||||
const errorListener = socket.onError(reject);
|
||||
socket.onOpen(() => {
|
||||
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 res.end();
|
||||
return void res.end();
|
||||
}
|
||||
|
||||
- const getFirstHeader = (headerName: string) => {
|
||||
@@ -136,24 +129,20 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||
if (!remoteAuthority) {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
@@ -298,6 +297,8 @@ export class WebClientServer {
|
||||
@@ -298,8 +297,12 @@ export class WebClientServer {
|
||||
scopes: [['user:email'], ['repo']]
|
||||
} : undefined;
|
||||
|
||||
+ const base = relativeRoot(getOriginalUrl(req))
|
||||
+ const vscodeBase = relativePath(getOriginalUrl(req))
|
||||
|
||||
const workbenchWebConfiguration = {
|
||||
remoteAuthority,
|
||||
@@ -309,6 +310,7 @@ export class WebClientServer {
|
||||
workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']),
|
||||
productConfiguration: <Partial<IProductConfiguration>>{
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
+ rootEndpoint: base,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
...this._productService.extensionsGallery,
|
||||
@@ -326,8 +328,10 @@ export class WebClientServer {
|
||||
+
|
||||
const productConfiguration = <Partial<IProductConfiguration>>{
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
+ rootEndpoint: base,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
...this._productService.extensionsGallery,
|
||||
@@ -334,11 +337,12 @@ export class WebClientServer {
|
||||
const values: { [key: string]: string } = {
|
||||
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
|
||||
WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
|
||||
@@ -165,8 +154,11 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||
+ VS_BASE: vscodeBase,
|
||||
};
|
||||
|
||||
|
||||
@@ -344,7 +348,7 @@ export class WebClientServer {
|
||||
-
|
||||
let data;
|
||||
try {
|
||||
const workbenchTemplate = (await fsp.readFile(filePath)).toString();
|
||||
@@ -352,7 +356,7 @@ export class WebClientServer {
|
||||
'default-src \'self\';',
|
||||
'img-src \'self\' https: data: blob:;',
|
||||
'media-src \'self\';',
|
||||
@@ -174,9 +166,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:;',
|
||||
@@ -417,3 +421,70 @@ export class WebClientServer {
|
||||
return res.end(data);
|
||||
'worker-src \'self\' data: blob:;',
|
||||
@@ -425,3 +429,70 @@ export class WebClientServer {
|
||||
return void res.end(data);
|
||||
}
|
||||
}
|
||||
+
|
||||
@@ -250,7 +242,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
|
||||
@@ -32,6 +32,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||
@@ -56,6 +56,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||
|
||||
export interface IProductConfiguration {
|
||||
readonly codeServerVersion?: string
|
||||
@@ -262,7 +254,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
@@ -489,6 +489,7 @@ function doCreateUri(path: string, query
|
||||
@@ -484,6 +484,7 @@ function doCreateUri(path: string, query
|
||||
});
|
||||
}
|
||||
|
||||
@@ -270,7 +262,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
return URI.parse(window.location.href).with({ path, query });
|
||||
}
|
||||
|
||||
@@ -500,7 +501,7 @@ function doCreateUri(path: string, query
|
||||
@@ -495,7 +496,7 @@ function doCreateUri(path: string, query
|
||||
if (!configElement || !configElementAttribute) {
|
||||
throw new Error('Missing web configuration element');
|
||||
}
|
||||
@@ -279,22 +271,22 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
|
||||
// Create workbench
|
||||
create(document.body, {
|
||||
Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||
Index: 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
|
||||
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||
+++ code-server/lib/vscode/src/vs/platform/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 { RemoteAuthorities } from 'vs/base/common/network';
|
||||
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
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) {
|
||||
const uri = URI.parse(format2(this._extensionGalleryResourceUrlTemplate, { publisher: galleryExtension.publisher, name: galleryExtension.name, version: galleryExtension.version, path: 'extension' }));
|
||||
@@ -102,7 +101,7 @@ export abstract class AbstractExtensionR
|
||||
: version,
|
||||
path: 'extension'
|
||||
}));
|
||||
- return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri;
|
||||
+ return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ To test:
|
||||
2. Open code-server
|
||||
3. Open terminal
|
||||
4. Open another code-server window
|
||||
5. Run code-server with a file or directory argument
|
||||
5. Run node ./out/node/entry.js with a file or directory argument
|
||||
|
||||
The file or directory should only open from the instance attached to that
|
||||
terminal.
|
||||
@@ -17,7 +17,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTe
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
|
||||
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts
|
||||
@@ -100,10 +100,14 @@ class RemoteTerminalBackend extends Base
|
||||
@@ -97,10 +97,14 @@ class RemoteTerminalBackend extends Base
|
||||
}
|
||||
const reqId = e.reqId;
|
||||
const commandId = e.commandId;
|
||||
|
||||
@@ -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
|
||||
@@ -237,6 +237,10 @@ export class Extension implements IExten
|
||||
@@ -243,6 +243,10 @@ export class Extension implements IExten
|
||||
if (this.type === ExtensionType.System && this.productService.quality === 'stable') {
|
||||
return false;
|
||||
}
|
||||
@@ -18,14 +18,3 @@ 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;
|
||||
|
||||
@@ -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
|
||||
@@ -271,6 +271,11 @@ export interface IWorkbenchConstructionO
|
||||
@@ -281,6 +281,11 @@ export interface IWorkbenchConstructionO
|
||||
*/
|
||||
readonly userDataPath?: string
|
||||
|
||||
@@ -23,24 +23,24 @@ 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
|
||||
@@ -31,6 +31,11 @@ export interface IBrowserWorkbenchEnviro
|
||||
* Options used to configure the workbench.
|
||||
*/
|
||||
@@ -34,6 +34,11 @@ export interface IBrowserWorkbenchEnviro
|
||||
readonly options?: IWorkbenchConstructionOptions;
|
||||
+
|
||||
+ /**
|
||||
|
||||
/**
|
||||
+ * Enable downloading files via menu actions.
|
||||
+ */
|
||||
+ readonly isEnabledFileDownloads?: boolean;
|
||||
}
|
||||
|
||||
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
|
||||
@@ -62,6 +67,13 @@ export class BrowserWorkbenchEnvironment
|
||||
+
|
||||
+ /**
|
||||
* Gets whether a resolver extension is expected for the environment.
|
||||
*/
|
||||
readonly expectsResolverExtension: boolean;
|
||||
@@ -111,6 +116,13 @@ export class BrowserWorkbenchEnvironment
|
||||
return this.options.userDataPath;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,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
|
||||
@@ -304,6 +304,7 @@ export class WebClientServer {
|
||||
@@ -325,6 +325,7 @@ export class WebClientServer {
|
||||
remoteAuthority,
|
||||
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
|
||||
userDataPath: this._environmentService.userDataPath,
|
||||
@@ -90,29 +90,22 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts
|
||||
+++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts
|
||||
@@ -7,12 +7,11 @@ import { Event } from 'vs/base/common/ev
|
||||
@@ -7,12 +7,12 @@ import { Event } from 'vs/base/common/ev
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } 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, 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 { 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, ActiveEditorCanToggleReadonlyContext } 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, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
|
||||
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, SideBySideEditor } 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';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
@@ -25,6 +24,7 @@ import { IPaneCompositePartService } fro
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
+import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
|
||||
export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private inputFocusedContext: IContextKey<boolean>;
|
||||
@@ -77,7 +77,7 @@ export class WorkbenchContextKeysHandler
|
||||
@@ -80,7 +80,7 @@ export class WorkbenchContextKeysHandler
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@@ -121,7 +114,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,
|
||||
@@ -202,6 +202,9 @@ export class WorkbenchContextKeysHandler
|
||||
@@ -210,6 +210,9 @@ export class WorkbenchContextKeysHandler
|
||||
this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);
|
||||
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
|
||||
|
||||
@@ -135,16 +128,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
|
||||
@@ -22,7 +22,7 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID,
|
||||
@@ -20,7 +20,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, 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 { 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 { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
@@ -477,13 +477,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
@@ -489,13 +489,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo
|
||||
id: DOWNLOAD_COMMAND_ID,
|
||||
title: DOWNLOAD_LABEL
|
||||
},
|
||||
@@ -172,9 +165,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
|
||||
@@ -32,6 +32,8 @@ export const IsFullscreenContext = new R
|
||||
@@ -35,6 +35,8 @@ export const HasWebFileSystemAccess = ne
|
||||
|
||||
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
|
||||
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 IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true);
|
||||
+
|
||||
|
||||
@@ -14,12 +14,14 @@ 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
|
||||
@@ -209,6 +209,9 @@ export async function setupServerService
|
||||
@@ -234,6 +234,9 @@ export async function setupServerService
|
||||
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
|
||||
socketServer.registerChannel('extensions', channel);
|
||||
|
||||
@@ -42,7 +44,7 @@ Index: code-server/lib/vscode/src/vs/base/common/platform.ts
|
||||
export const LANGUAGE_DEFAULT = 'en';
|
||||
|
||||
let _isWindows = false;
|
||||
@@ -83,17 +81,19 @@ if (typeof navigator === 'object' && !is
|
||||
@@ -90,17 +88,21 @@ if (typeof navigator === 'object' && !is
|
||||
_isMobile = _userAgent?.indexOf('Mobi') >= 0;
|
||||
_isWeb = true;
|
||||
|
||||
@@ -56,16 +58,18 @@ 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. */ }
|
||||
+ }
|
||||
}
|
||||
@@ -125,7 +129,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
|
||||
@@ -105,7 +105,7 @@ export abstract class AbstractNativeEnvi
|
||||
@@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvi
|
||||
return URI.file(join(vscodePortable, 'argv.json'));
|
||||
}
|
||||
|
||||
@@ -138,7 +142,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
|
||||
@@ -30,6 +30,12 @@ export function getNLSConfiguration(lang
|
||||
@@ -32,6 +32,12 @@ export function getNLSConfiguration(lang
|
||||
if (InternalNLSConfiguration.is(value)) {
|
||||
value._languagePackSupport = true;
|
||||
}
|
||||
@@ -151,7 +155,7 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
||||
return value;
|
||||
});
|
||||
_cache.set(key, result);
|
||||
@@ -44,3 +50,43 @@ export namespace InternalNLSConfiguratio
|
||||
@@ -46,3 +52,43 @@ export namespace InternalNLSConfiguratio
|
||||
return candidate && typeof candidate._languagePackId === 'string';
|
||||
}
|
||||
}
|
||||
@@ -207,23 +211,23 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts';
|
||||
|
||||
@@ -299,6 +300,8 @@ export class WebClientServer {
|
||||
@@ -337,6 +338,8 @@ export class WebClientServer {
|
||||
callbackRoute: this._callbackRoute
|
||||
};
|
||||
|
||||
const base = relativeRoot(getOriginalUrl(req))
|
||||
const vscodeBase = relativePath(getOriginalUrl(req))
|
||||
+ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
|
||||
+ const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
|
||||
|
||||
const workbenchWebConfiguration = {
|
||||
remoteAuthority,
|
||||
@@ -336,6 +339,7 @@ export class WebClientServer {
|
||||
const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
|
||||
const values: { [key: string]: string } = {
|
||||
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
|
||||
@@ -345,6 +348,7 @@ export class WebClientServer {
|
||||
WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
|
||||
BASE: base,
|
||||
VS_BASE: vscodeBase,
|
||||
+ NLS_CONFIGURATION: asJSON(nlsConfiguration),
|
||||
};
|
||||
|
||||
|
||||
let data;
|
||||
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||
@@ -248,9 +252,18 @@ 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
|
||||
@@ -123,8 +123,9 @@ import 'vs/workbench/contrib/logs/browse
|
||||
// Explorer
|
||||
import 'vs/workbench/contrib/files/browser/files.web.contribution';
|
||||
@@ -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';
|
||||
@@ -116,8 +116,9 @@ registerSingleton(ILanguagePackService,
|
||||
// Logs
|
||||
import 'vs/workbench/contrib/logs/browser/logs.contribution';
|
||||
|
||||
-// Localization
|
||||
-import 'vs/workbench/contrib/localization/browser/localization.contribution';
|
||||
@@ -264,37 +277,45 @@ 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
|
||||
@@ -4,10 +4,23 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
@@ -5,18 +5,24 @@
|
||||
|
||||
import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
+import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks';
|
||||
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 { 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,
|
||||
+ @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService
|
||||
+ ) {
|
||||
+ super(extensionGalleryService)
|
||||
+ this.languagePackService = ProxyChannel.toService<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks'));
|
||||
+ }
|
||||
+
|
||||
@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
|
||||
getInstalledLanguages(): Promise<ILanguagePackItem[]> {
|
||||
- return Promise.resolve([]);
|
||||
+ return this.languagePackService.getInstalledLanguages()
|
||||
}
|
||||
}
|
||||
Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts
|
||||
Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
|
||||
===================================================================
|
||||
--- 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
|
||||
--- 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
|
||||
@IProductService private readonly productService: IProductService
|
||||
) { }
|
||||
|
||||
@@ -304,7 +325,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-san
|
||||
try {
|
||||
const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' });
|
||||
|
||||
@@ -68,9 +69,6 @@ export class NativeLocaleService impleme
|
||||
@@ -78,9 +79,6 @@ class NativeLocaleService implements ILo
|
||||
}
|
||||
|
||||
private async writeLocaleValue(locale: string | undefined): Promise<boolean> {
|
||||
@@ -314,3 +335,70 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/localization/electron-san
|
||||
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> {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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}`];
|
||||
}
|
||||
@@ -10,41 +10,107 @@ 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
|
||||
@@ -62,7 +62,7 @@ import { GettingStartedIndexList } from
|
||||
@@ -10,7 +10,7 @@ import { IInstantiationService } from 'v
|
||||
import { IEditorSerializer, IEditorOpenContext } from 'vs/workbench/common/editor';
|
||||
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
-import { $, addDisposableListener, append, clearNode, Dimension, reset } from 'vs/base/browser/dom';
|
||||
+import { $, addDisposableListener, append, clearNode, Dimension, reset, prepend } from 'vs/base/browser/dom';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { hiddenEntriesConfigurationKey, IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService';
|
||||
@@ -59,7 +59,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';
|
||||
-import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
|
||||
+import { IsEnabledCoderGettingStarted, WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
|
||||
import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { OpenFolderAction, OpenFileFolderAction, OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions';
|
||||
import { Toggle } from 'vs/base/browser/ui/toggle/toggle';
|
||||
@@ -753,11 +753,24 @@ export class GettingStartedPage extends
|
||||
onShowOnStartupChanged();
|
||||
}));
|
||||
|
||||
- const header = $('.header', {},
|
||||
+ let header = $('.header', {},
|
||||
$('h1.product-name.caption', {}, this.productService.nameLong),
|
||||
@@ -770,6 +770,72 @@ export class GettingStartedPage extends
|
||||
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
|
||||
);
|
||||
|
||||
+ let gettingStartedCoder: HTMLElement = $('.header', {});
|
||||
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
|
||||
+ header = $('.header', {},
|
||||
+ $('h1.product-name.caption', {}, this.productService.nameLong),
|
||||
+ $('p.subtitle.description.coder', {},
|
||||
+ "Using code-server on a team?",
|
||||
+ ),
|
||||
+ $('p.subtitle.description.coder-coder', {},
|
||||
+ "Check out: ",
|
||||
+ $('a', { href: "https://github.com/coder/coder" }, "coder/coder")
|
||||
+ ),
|
||||
+ );
|
||||
+ }
|
||||
+ gettingStartedCoder = $('.gettingStartedCategory', {},
|
||||
+ $('h2', {
|
||||
+ style: 'margin-bottom: 12px',
|
||||
+ }, 'Next Up'),
|
||||
+ $('a', {
|
||||
+ href: 'https://cdr.co/code-server-to-coder',
|
||||
+ target: '_blank',
|
||||
+ },
|
||||
+ $('button', {
|
||||
+ style: [
|
||||
+ 'padding: 10px 16px ',
|
||||
+ 'border-radius: 4px',
|
||||
+ 'background: linear-gradient(94.04deg, #7934DA 0%, #4D52E0 101.2%)',
|
||||
+ 'color: white',
|
||||
+ 'overflow: hidden',
|
||||
+ 'margin-right: 14px',
|
||||
+ ].join(';'),
|
||||
+ },
|
||||
+ $('h3', {
|
||||
+ style: [
|
||||
+ 'margin: 0px 0px 6px',
|
||||
+ 'font-weight: 500',
|
||||
+ ].join(';'),
|
||||
+ }, 'Deploy code-server for your team'),
|
||||
+ $('p', {
|
||||
+ style: [
|
||||
+ 'margin: 0',
|
||||
+ 'font-size: 13px',
|
||||
+ 'color: #dcdee2',
|
||||
+ ].join(';'),
|
||||
+ }, 'Provision software development environments on your infrastructure with Coder.'),
|
||||
+ $('p', {
|
||||
+ style: [
|
||||
+ 'margin-top: 8px',
|
||||
+ 'font-size: 13px',
|
||||
+ 'color: #dcdee2',
|
||||
+ ].join(';'),
|
||||
+ }, 'Coder is a self-service portal which provisions via Terraform—Linux, macOS, Windows, x86, ARM, and, of course, Kubernetes based infrastructure.'),
|
||||
+ $('p', {
|
||||
+ style: [
|
||||
+ 'margin: 0',
|
||||
+ 'margin-top: 8px',
|
||||
+ 'font-size: 13px',
|
||||
+ 'display: flex',
|
||||
+ 'align-items: center',
|
||||
+ ].join(';'),
|
||||
+ }, 'Get started ', $('span', {
|
||||
+ class: ThemeIcon.asClassName(Codicon.arrowRight),
|
||||
+ style: [
|
||||
+ 'color: white',
|
||||
+ 'margin-left: 8px',
|
||||
+ ].join(';'),
|
||||
+ })),
|
||||
+ $('img', {
|
||||
+ src: './_static/src/browser/media/templates.png',
|
||||
+ style: [
|
||||
+ 'margin-bottom: -65px',
|
||||
+ ].join(';'),
|
||||
+ }),
|
||||
+ ),
|
||||
+ ),
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
|
||||
const leftColumn = $('.categories-column.categories-column-left', {},);
|
||||
const rightColumn = $('.categories-column.categories-column-right', {},);
|
||||
|
||||
@@ -819,6 +885,9 @@ export class GettingStartedPage extends
|
||||
recentList.setLimit(5);
|
||||
reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
|
||||
}
|
||||
+ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) {
|
||||
+ prepend(rightColumn, gettingStartedCoder)
|
||||
+ }
|
||||
};
|
||||
|
||||
featuredExtensionList.onDidChange(layoutFeaturedExtension);
|
||||
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
|
||||
@@ -69,7 +135,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
|
||||
@@ -276,6 +276,11 @@ export interface IWorkbenchConstructionO
|
||||
@@ -286,6 +286,11 @@ export interface IWorkbenchConstructionO
|
||||
*/
|
||||
readonly isEnabledFileDownloads?: boolean
|
||||
|
||||
@@ -80,24 +146,24 @@ 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
|
||||
@@ -36,6 +36,11 @@ export interface IBrowserWorkbenchEnviro
|
||||
* Enable downloading files via menu actions.
|
||||
*/
|
||||
@@ -39,6 +39,11 @@ export interface IBrowserWorkbenchEnviro
|
||||
readonly isEnabledFileDownloads?: boolean;
|
||||
+
|
||||
+ /**
|
||||
|
||||
/**
|
||||
+ * Enable Coder's custom getting started text.
|
||||
+ */
|
||||
+ readonly isEnabledCoderGettingStarted?: boolean;
|
||||
}
|
||||
|
||||
export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService {
|
||||
@@ -74,6 +79,13 @@ export class BrowserWorkbenchEnvironment
|
||||
+
|
||||
+ /**
|
||||
* Gets whether a resolver extension is expected for the environment.
|
||||
*/
|
||||
readonly expectsResolverExtension: boolean;
|
||||
@@ -123,6 +128,13 @@ export class BrowserWorkbenchEnvironment
|
||||
return this.options.isEnabledFileDownloads;
|
||||
}
|
||||
|
||||
@@ -127,7 +193,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 ----- */
|
||||
|
||||
@@ -135,7 +201,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
|
||||
@@ -308,6 +308,7 @@ export class WebClientServer {
|
||||
@@ -328,6 +328,7 @@ export class WebClientServer {
|
||||
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
|
||||
userDataPath: this._environmentService.userDataPath,
|
||||
isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'],
|
||||
@@ -149,14 +215,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 } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } 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, 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 { 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, ActiveEditorCanToggleReadonlyContext } 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, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys';
|
||||
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, SideBySideEditor } 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';
|
||||
@@ -204,6 +204,7 @@ export class WorkbenchContextKeysHandler
|
||||
@@ -212,6 +212,7 @@ export class WorkbenchContextKeysHandler
|
||||
|
||||
// code-server
|
||||
IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true)
|
||||
@@ -168,8 +234,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
|
||||
@@ -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)
|
||||
@@ -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'));
|
||||
|
||||
export const IsEnabledFileDownloads = new RawContextKey<boolean>('isEnabledFileDownloads', true, true);
|
||||
+export const IsEnabledCoderGettingStarted = new RawContextKey<boolean>('isEnabledCoderGettingStarted', true, true);
|
||||
|
||||
@@ -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('', require).fsPath);
|
||||
-const APP_ROOT = dirname(FileAccess.asFileUri('').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('', require).fsPath);
|
||||
+ const APP_ROOT = dirname(FileAccess.asFileUri('').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_(?!SHELL_LOGIN).+$/,
|
||||
/^VSCODE_(?!(PORTABLE|SHELL_LOGIN|ENV_REPLACE|ENV_APPEND|ENV_PREPEND)).+$/,
|
||||
/^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
|
||||
@@ -143,8 +143,11 @@ export class BrowserDialogHandler implem
|
||||
@@ -77,8 +77,11 @@ export class BrowserDialogHandler extend
|
||||
|
||||
async about(): Promise<void> {
|
||||
const detailString = (useAgo: boolean): string => {
|
||||
@@ -176,7 +176,7 @@ 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
|
||||
@@ -69,6 +69,7 @@ import { IndexedDB } from 'vs/base/brows
|
||||
@@ -67,6 +67,7 @@ import { IndexedDB } from 'vs/base/brows
|
||||
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
|
||||
import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
|
||||
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
|
||||
@@ -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';
|
||||
@@ -116,6 +117,9 @@ export class BrowserMain extends Disposa
|
||||
@@ -127,6 +128,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
|
||||
@@ -31,6 +31,8 @@ export type ExtensionVirtualWorkspaceSup
|
||||
@@ -55,6 +55,8 @@ export type ExtensionVirtualWorkspaceSup
|
||||
};
|
||||
|
||||
export interface IProductConfiguration {
|
||||
@@ -264,11 +264,11 @@ 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
|
||||
@@ -308,6 +308,7 @@ export class WebClientServer {
|
||||
folderUri: resolveWorkspaceURI(this._environmentService.args['default-folder']),
|
||||
workspaceUri: resolveWorkspaceURI(this._environmentService.args['default-workspace']),
|
||||
productConfiguration: <Partial<IProductConfiguration>>{
|
||||
+ codeServerVersion: this._productService.codeServerVersion,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
...this._productService.extensionsGallery,
|
||||
@@ -299,6 +299,7 @@ export class WebClientServer {
|
||||
} : undefined;
|
||||
|
||||
const productConfiguration = <Partial<IProductConfiguration>>{
|
||||
+ codeServerVersion: this._productService.codeServerVersion,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
...this._productService.extensionsGallery,
|
||||
|
||||
@@ -20,7 +20,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
|
||||
@@ -303,6 +303,7 @@ export class WebClientServer {
|
||||
@@ -320,6 +320,7 @@ export class WebClientServer {
|
||||
const workbenchWebConfiguration = {
|
||||
remoteAuthority,
|
||||
webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre',
|
||||
@@ -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
|
||||
@@ -266,6 +266,11 @@ export interface IWorkbenchConstructionO
|
||||
@@ -276,6 +276,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
|
||||
@@ -53,7 +53,14 @@ export class BrowserWorkbenchEnvironment
|
||||
get logFile(): URI { return joinPath(this.logsHome, 'window.log'); }
|
||||
@@ -102,7 +102,14 @@ export class BrowserWorkbenchEnvironment
|
||||
get logFile(): URI { return joinPath(this.windowLogsPath, 'window.log'); }
|
||||
|
||||
@memoize
|
||||
- get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); }
|
||||
|
||||
@@ -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
|
||||
@@ -34,6 +34,7 @@ export interface IProductConfiguration {
|
||||
@@ -58,6 +58,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<ServerParsedArgs> = {
|
||||
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
|
||||
/* ----- code-server ----- */
|
||||
'disable-update-check': { type: 'boolean' },
|
||||
+ 'auth': { type: 'string' },
|
||||
@@ -40,14 +40,14 @@ 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
|
||||
@@ -313,6 +313,7 @@ export class WebClientServer {
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
+ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
},
|
||||
@@ -304,6 +304,7 @@ export class WebClientServer {
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
+ logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/workbench/browser/client.ts
|
||||
|
||||
@@ -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
|
||||
@@ -53,6 +53,16 @@ else if (typeof require?.__$__nodeRequir
|
||||
@@ -47,6 +47,16 @@ else if (globalThis._VSCODE_PRODUCT_JSON
|
||||
version: pkg.version
|
||||
});
|
||||
}
|
||||
@@ -49,35 +49,35 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,14 +312,7 @@ export class WebClientServer {
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
embedderIdentifier: 'server-distro',
|
||||
- extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
- ...this._productService.extensionsGallery,
|
||||
- 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
|
||||
- scheme: 'http',
|
||||
- authority: remoteAuthority,
|
||||
- path: `${this._webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}`
|
||||
- }).toString(true)
|
||||
- } : undefined
|
||||
+ extensionsGallery: this._productService.extensionsGallery,
|
||||
},
|
||||
callbackRoute: this._callbackRoute
|
||||
@@ -304,14 +304,7 @@ export class WebClientServer {
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
embedderIdentifier: 'server-distro',
|
||||
- extensionsGallery: this._webExtensionResourceUrlTemplate ? {
|
||||
- ...this._productService.extensionsGallery,
|
||||
- 'resourceUrlTemplate': this._webExtensionResourceUrlTemplate.with({
|
||||
- scheme: 'http',
|
||||
- authority: remoteAuthority,
|
||||
- path: `${this._webExtensionRoute}/${this._webExtensionResourceUrlTemplate.authority}${this._webExtensionResourceUrlTemplate.path}`
|
||||
- }).toString(true)
|
||||
- } : undefined
|
||||
+ extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||
|
||||
if (!this._environmentService.isBuilt) {
|
||||
Index: 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
|
||||
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||
+++ code-server/lib/vscode/src/vs/platform/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';
|
||||
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
|
||||
|
||||
@@ -60,7 +59,7 @@ export abstract class AbstractExtensionR
|
||||
const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource';
|
||||
@@ -77,7 +76,7 @@ export abstract class AbstractExtensionR
|
||||
private readonly _environmentService: IEnvironmentService,
|
||||
private readonly _configurationService: IConfigurationService,
|
||||
) {
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
Unconditionally enable the proposed API
|
||||
|
||||
To test run an extension that uses 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)
|
||||
|
||||
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
|
||||
@@ -359,10 +359,7 @@ function extensionDescriptionArrayToMap(
|
||||
@@ -260,10 +260,7 @@ function extensionDescriptionArrayToMap(
|
||||
}
|
||||
|
||||
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
|
||||
@@ -34,3 +22,16 @@ 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
|
||||
|
||||
@@ -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
|
||||
@@ -35,6 +35,7 @@ export interface IProductConfiguration {
|
||||
@@ -59,6 +59,7 @@ export interface IProductConfiguration {
|
||||
readonly rootEndpoint?: string
|
||||
readonly updateEndpoint?: string
|
||||
readonly logoutEndpoint?: string
|
||||
@@ -42,70 +42,48 @@ 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
|
||||
@@ -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 { IProductService } from 'vs/platform/product/common/productService';
|
||||
-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 {
|
||||
@@ -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) {
|
||||
@@ -33,7 +33,7 @@ export class RemoteAuthorityResolverServ
|
||||
isWorkbenchOptionsBasedResolution: boolean,
|
||||
connectionToken: Promise<string> | string | undefined,
|
||||
resourceUriProvider: ((uri: URI) => URI) | undefined,
|
||||
- @IProductService productService: IProductService,
|
||||
+ @IProductService private readonly productService: IProductService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
this._connectionToken = connectionToken;
|
||||
this._connectionTokens = new Map<string, string>();
|
||||
@@ -61,9 +61,14 @@ export class RemoteAuthorityResolverServ
|
||||
|
||||
private async _doResolveAuthority(authority: string): Promise<ResolverResult> {
|
||||
@@ -84,9 +84,14 @@ export class RemoteAuthorityResolverServ
|
||||
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.proxyEndpointTemplate) {
|
||||
+ const proxyUrl = new URL(this.proxyEndpointTemplate, window.location.href);
|
||||
+ if (this.productService.proxyEndpointTemplate) {
|
||||
+ const proxyUrl = new URL(this.productService.proxyEndpointTemplate, window.location.href);
|
||||
+ options = { extensionHostEnv: { VSCODE_PROXY_URI: decodeURIComponent(proxyUrl.toString()) }}
|
||||
+ }
|
||||
const defaultPort = (/^https:/.test(window.location.href) ? 443 : 80);
|
||||
const { host, port } = parseAuthorityWithOptionalPort(authority, defaultPort);
|
||||
- const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken } };
|
||||
+ const result: ResolverResult = { authority: { authority, host: host, port: port, connectionToken }, options };
|
||||
RemoteAuthorities.set(authority, result.authority.host, result.authority.port);
|
||||
- const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken } };
|
||||
+ const result: ResolverResult = { authority: { authority, connectTo: new WebSocketRemoteConnection(host, port), connectionToken }, options };
|
||||
RemoteAuthorities.set(authority, host, port);
|
||||
this._cache.set(authority, result);
|
||||
this._onDidChangeConnectionData.fire();
|
||||
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
|
||||
@@ -314,6 +314,7 @@ export class WebClientServer {
|
||||
rootEndpoint: base,
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
|
||||
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
|
||||
@@ -305,6 +305,7 @@ export class WebClientServer {
|
||||
rootEndpoint: base,
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
+ proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
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
|
||||
@@ -392,7 +392,7 @@ export async function createTerminalEnvi
|
||||
@@ -384,7 +384,7 @@ export async function createTerminalEnvi
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
// variables
|
||||
@@ -126,25 +104,26 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||
|
||||
interface ICredential {
|
||||
service: string;
|
||||
@@ -511,6 +512,38 @@ function doCreateUri(path: string, query
|
||||
} : undefined,
|
||||
@@ -505,6 +506,39 @@ function doCreateUri(path: string, query
|
||||
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
|
||||
workspaceProvider: WorkspaceProvider.create(config),
|
||||
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
|
||||
- credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider() // with a remote, we don't use a local credentials provider
|
||||
+ credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider(), // with a remote, we don't use a local credentials provider
|
||||
- credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider()
|
||||
+ credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider(),
|
||||
+ resolveExternalUri: (uri: URI): Promise<URI> => {
|
||||
+ let resolvedUri = uri
|
||||
+ const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
|
||||
+
|
||||
+ if (localhostMatch && resolvedUri.authority !== location.host) {
|
||||
+ if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
|
||||
+ resolvedUri = URI.parse(new URL(config.productConfiguration.proxyEndpointTemplate.replace('{{port}}', localhostMatch.port.toString()), window.location.href).toString())
|
||||
+ const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
|
||||
+ .replace('{{port}}', localhostMatch.port.toString())
|
||||
+ .replace('{{host}}', window.location.host)
|
||||
+ resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
|
||||
+ } else {
|
||||
+ throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // If not localhost, return unmodified
|
||||
+ // If not localhost, return unmodified.
|
||||
+ return Promise.resolve(resolvedUri)
|
||||
+ },
|
||||
+ tunnelProvider: {
|
||||
@@ -170,7 +149,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,5 +18,4 @@ disable-downloads.diff
|
||||
telemetry.diff
|
||||
display-language.diff
|
||||
cli-window-open.diff
|
||||
exec-argv.diff
|
||||
getting-started.diff
|
||||
|
||||
@@ -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
|
||||
@@ -36,6 +36,10 @@ export interface IProductConfiguration {
|
||||
@@ -60,6 +60,10 @@ export interface IProductConfiguration {
|
||||
readonly updateEndpoint?: string
|
||||
readonly logoutEndpoint?: string
|
||||
readonly proxyEndpointTemplate?: string
|
||||
@@ -54,14 +54,14 @@ 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
|
||||
@@ -316,6 +316,10 @@ export class WebClientServer {
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
|
||||
+ serviceWorker: {
|
||||
+ scope: vscodeBase + '/',
|
||||
+ path: base + '/_static/out/browser/serviceWorker.js',
|
||||
+ },
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
},
|
||||
@@ -306,6 +306,10 @@ export class WebClientServer {
|
||||
updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
logoutEndpoint: this._environmentService.args['auth'] && this._environmentService.args['auth'] !== "none" ? base + '/logout' : undefined,
|
||||
proxyEndpointTemplate: process.env.VSCODE_PROXY_URI ?? base + '/proxy/{{port}}/',
|
||||
+ serviceWorker: {
|
||||
+ scope: vscodeBase + '/',
|
||||
+ path: base + '/_static/out/browser/serviceWorker.js',
|
||||
+ },
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/build/gulpfile.reh.js
|
||||
+++ code-server/lib/vscode/build/gulpfile.reh.js
|
||||
@@ -191,8 +191,7 @@ function packageTask(type, platform, arc
|
||||
@@ -199,8 +199,7 @@ function packageTask(type, platform, arc
|
||||
|
||||
const src = gulp.src(sourceFolderName + '/**', { base: '.' })
|
||||
.pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); }))
|
||||
@@ -20,7 +20,7 @@ Index: code-server/lib/vscode/build/gulpfile.reh.js
|
||||
|
||||
const workspaceExtensionPoints = ['debuggers', 'jsonValidation'];
|
||||
const isUIExtension = (manifest) => {
|
||||
@@ -231,9 +230,9 @@ function packageTask(type, platform, arc
|
||||
@@ -239,9 +238,9 @@ function packageTask(type, platform, arc
|
||||
.map(name => `.build/extensions/${name}/**`);
|
||||
|
||||
const extensions = gulp.src(extensionPaths, { base: '.build', dot: true });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Store a static reference to the IPC socket
|
||||
Store the IPC socket with workspace metadata.
|
||||
|
||||
This lets us use it to open files inside code-server from outside of
|
||||
code-server.
|
||||
@@ -9,29 +9,121 @@ To test this:
|
||||
|
||||
It should open in your existing code-server instance.
|
||||
|
||||
When the extension host is terminated, the socket is unregistered.
|
||||
|
||||
Index: code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||
+++ code-server/lib/vscode/src/vs/workbench/api/node/extHostExtensionService.ts
|
||||
@@ -2,7 +2,9 @@
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-
|
||||
+import { promises as fs } from 'fs';
|
||||
+import * as os from 'os'
|
||||
+import * as path from 'vs/base/common/path';
|
||||
+import * as _http from 'http';
|
||||
import * as performance from 'vs/base/common/performance';
|
||||
import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
|
||||
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
|
||||
@@ -72,6 +74,10 @@ export class ExtHostExtensionService ext
|
||||
if (this._initData.remote.isRemote && this._initData.remote.authority) {
|
||||
const cliServer = this._instaService.createInstance(CLIServer);
|
||||
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
|
||||
+
|
||||
+ fs.writeFile(path.join(os.tmpdir(), 'vscode-ipc'), cliServer.ipcHandlePath).catch((error) => {
|
||||
+ this._logService.error(error);
|
||||
+ });
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { ExtensionRuntime } from 'vs/wor
|
||||
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
|
||||
import { realpathSync } from 'vs/base/node/extpath';
|
||||
import { ExtHostConsoleForwarder } from 'vs/workbench/api/node/extHostConsoleForwarder';
|
||||
+import { IExtHostWorkspace } from '../common/extHostWorkspace';
|
||||
import { ExtHostDiskFileSystemProvider } from 'vs/workbench/api/node/extHostDiskFileSystemProvider';
|
||||
|
||||
// Module loading tricks
|
||||
class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
@@ -83,6 +84,52 @@ export class ExtHostExtensionService ext
|
||||
await interceptor.install();
|
||||
performance.mark('code/extHost/didInitAPI');
|
||||
|
||||
+ (async () => {
|
||||
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
|
||||
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
|
||||
+ if (!socketPath || !codeServerSocketPath) {
|
||||
+ return;
|
||||
+ }
|
||||
+ const workspace = this._instaService.invokeFunction((accessor) => {
|
||||
+ const workspaceService = accessor.get(IExtHostWorkspace);
|
||||
+ return workspaceService.workspace;
|
||||
+ });
|
||||
+ const entry = {
|
||||
+ workspace,
|
||||
+ socketPath
|
||||
+ };
|
||||
+ const message = JSON.stringify({entry});
|
||||
+ await new Promise<void>((resolve, reject) => {
|
||||
+ const opts: _http.RequestOptions = {
|
||||
+ path: '/add-session',
|
||||
+ socketPath: codeServerSocketPath,
|
||||
+ method: 'POST',
|
||||
+ headers: {
|
||||
+ 'content-type': 'application/json',
|
||||
+ }
|
||||
+ };
|
||||
+ const req = _http.request(opts, (res) => {
|
||||
+ res.on('error', reject);
|
||||
+ res.on('end', () => {
|
||||
+ try {
|
||||
+ if (res.statusCode === 200) {
|
||||
+ resolve();
|
||||
+ } else {
|
||||
+ reject(new Error('Unexpected status code: ' + res.statusCode));
|
||||
+ }
|
||||
+ } catch (e: unknown) {
|
||||
+ reject(e);
|
||||
+ }
|
||||
+ });
|
||||
+ });
|
||||
+ req.on('error', reject);
|
||||
+ req.write(message);
|
||||
+ req.end();
|
||||
+ });
|
||||
+ })().catch(error => {
|
||||
+ this._logService.error(error);
|
||||
+ });
|
||||
+
|
||||
// Do this when extension service exists, but extensions are not being activated yet.
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy, this._initData);
|
||||
Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
|
||||
+++ code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
+import * as _http from 'http';
|
||||
import * as nativeWatchdog from 'native-watchdog';
|
||||
import * as net from 'net';
|
||||
import * as minimist from 'minimist';
|
||||
@@ -400,7 +401,28 @@ async function startExtensionHostProcess
|
||||
);
|
||||
|
||||
// rewrite onTerminate-function to be a proper shutdown
|
||||
- onTerminate = (reason: string) => extensionHostMain.terminate(reason);
|
||||
+ onTerminate = (reason: string) => {
|
||||
+ extensionHostMain.terminate(reason);
|
||||
+
|
||||
+ const socketPath = process.env['VSCODE_IPC_HOOK_CLI'];
|
||||
+ const codeServerSocketPath = process.env['CODE_SERVER_SESSION_SOCKET']
|
||||
+ if (!socketPath || !codeServerSocketPath) {
|
||||
+ return;
|
||||
+ }
|
||||
+ const message = JSON.stringify({socketPath});
|
||||
+ const opts: _http.RequestOptions = {
|
||||
+ path: '/delete-session',
|
||||
+ socketPath: codeServerSocketPath,
|
||||
+ method: 'POST',
|
||||
+ headers: {
|
||||
+ 'content-type': 'application/json',
|
||||
+ 'accept': 'application/json'
|
||||
+ }
|
||||
+ };
|
||||
+ const req = _http.request(opts);
|
||||
+ req.write(message);
|
||||
+ req.end();
|
||||
+ };
|
||||
}
|
||||
|
||||
startExtensionHostProcess().catch((err) => console.log(err));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
Add support for telemetry endpoint
|
||||
|
||||
To test:
|
||||
1. Create a RequestBin - https://requestbin.io/
|
||||
1. Create a mock API using [RequestBin](https://requestbin.io/) or [Beeceptor](https://beeceptor.com/)
|
||||
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
|
||||
|
||||
@@ -11,16 +12,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
|
||||
@@ -71,6 +71,7 @@ import { IExtensionsScannerService } fro
|
||||
@@ -69,6 +69,7 @@ import { IExtensionsScannerService } fro
|
||||
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
|
||||
import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
|
||||
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
|
||||
import { IUserDataProfilesService } 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';
|
||||
|
||||
@@ -133,10 +134,13 @@ export async function setupServerService
|
||||
const machineId = await getMachineId();
|
||||
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||
@@ -151,10 +152,13 @@ export async function setupServerService
|
||||
let oneDsAppender: ITelemetryAppender = NullAppender;
|
||||
const isInternal = isInternalTelemetry(productService, configurationService);
|
||||
if (supportsTelemetry(productService, environmentService)) {
|
||||
- if (productService.aiConfig && productService.aiConfig.ariaKey) {
|
||||
@@ -93,11 +94,11 @@ 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
|
||||
@@ -321,6 +321,7 @@ export class WebClientServer {
|
||||
scope: vscodeBase + '/',
|
||||
path: base + '/_static/out/browser/serviceWorker.js',
|
||||
},
|
||||
+ enableTelemetry: this._productService.enableTelemetry,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
@@ -310,6 +310,7 @@ export class WebClientServer {
|
||||
scope: vscodeBase + '/',
|
||||
path: base + '/_static/out/browser/serviceWorker.js',
|
||||
},
|
||||
+ enableTelemetry: this._productService.enableTelemetry,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -33,6 +33,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||
@@ -57,6 +57,7 @@ export type ExtensionVirtualWorkspaceSup
|
||||
export interface IProductConfiguration {
|
||||
readonly codeServerVersion?: string
|
||||
readonly rootEndpoint?: string
|
||||
@@ -105,14 +105,14 @@ 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
|
||||
@@ -312,6 +312,7 @@ export class WebClientServer {
|
||||
productConfiguration: <Partial<IProductConfiguration>>{
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
},
|
||||
@@ -303,6 +303,7 @@ export class WebClientServer {
|
||||
const productConfiguration = <Partial<IProductConfiguration>>{
|
||||
codeServerVersion: this._productService.codeServerVersion,
|
||||
rootEndpoint: base,
|
||||
+ updateEndpoint: !this._environmentService.args['disable-update-check'] ? base + '/update/check' : undefined,
|
||||
embedderIdentifier: 'server-distro',
|
||||
extensionsGallery: this._productService.extensionsGallery,
|
||||
};
|
||||
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||
===================================================================
|
||||
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
||||
@@ -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<ServerParsedArgs> = {
|
||||
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
|
||||
+ /* ----- code-server ----- */
|
||||
+ 'disable-update-check': { type: 'boolean' },
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -177,7 +177,7 @@ export class BrowserWorkbenchEnvironment
|
||||
@@ -225,7 +225,7 @@ export class BrowserWorkbenchEnvironment
|
||||
|
||||
@memoize
|
||||
get webviewExternalEndpoint(): string {
|
||||
@@ -54,7 +54,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
|
||||
@@ -302,6 +302,7 @@ export class WebClientServer {
|
||||
@@ -316,6 +316,7 @@ export class WebClientServer {
|
||||
|
||||
const workbenchWebConfiguration = {
|
||||
remoteAuthority,
|
||||
@@ -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-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';">
|
||||
- content="default-src 'none'; script-src 'sha256-N4YFn5ze5crjPqMK/opogKs7bSGWtf3lmjV/3LfbSOs=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
|
||||
+ content="default-src 'none'; script-src 'sha256-B5FRTRmagxqZ2yiS/ip5EgFZJPHAF0G0O3NgwgN6hhg=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
|
||||
|
||||
<!-- Disable pinch zooming -->
|
||||
<meta name="viewport"
|
||||
@@ -331,6 +331,12 @@
|
||||
@@ -334,6 +334,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
|
||||
@@ -330,6 +330,12 @@
|
||||
@@ -333,6 +333,12 @@
|
||||
|
||||
const hostname = location.hostname;
|
||||
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
"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"]
|
||||
"ignoreDeps": ["express", "ansi-regex", "env-paths", "limiter", "node", "prettier"]
|
||||
}
|
||||
|
||||
BIN
src/browser/media/templates.png
Normal file
BIN
src/browser/media/templates.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -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>{{APP_NAME}} login</title>
|
||||
<title>{{I18N_LOGIN_TITLE}}</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">Please log in below. {{PASSWORD_MSG}}</div>
|
||||
<div class="sub">{{I18N_LOGIN_BELOW}} {{PASSWORD_MSG}}</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<form class="login-form" method="post">
|
||||
@@ -38,11 +38,11 @@
|
||||
autofocus
|
||||
class="password"
|
||||
type="password"
|
||||
placeholder="PASSWORD"
|
||||
placeholder="{{I18N_PASSWORD_PLACEHOLDER}}"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
<input class="submit -button" value="SUBMIT" type="submit" />
|
||||
<input class="submit -button" value="{{I18N_SUBMIT}}" type="submit" />
|
||||
</div>
|
||||
{{ERROR}}
|
||||
</form>
|
||||
|
||||
6
src/browser/security.txt
Normal file
6
src/browser/security.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
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
|
||||
@@ -4,6 +4,7 @@ export enum HttpCode {
|
||||
NotFound = 404,
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
Forbidden = 403,
|
||||
LargePayload = 413,
|
||||
ServerError = 500,
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import * as util from "../common/util"
|
||||
import { DefaultedArgs } from "./cli"
|
||||
import { disposer } from "./http"
|
||||
import { isNodeJSErrnoException } from "./util"
|
||||
import { EditorSessionManager, makeEditorSessionManagerServer } from "./vscodeSocket"
|
||||
import { handleUpgrade } from "./wsRouter"
|
||||
|
||||
type ListenOptions = Pick<DefaultedArgs, "socket-mode" | "socket" | "port" | "host">
|
||||
type SocketOptions = { socket: string; "socket-mode"?: string }
|
||||
type ListenOptions = DefaultedArgs | SocketOptions
|
||||
|
||||
export interface App extends Disposable {
|
||||
/** Handles regular HTTP requests. */
|
||||
@@ -20,12 +22,18 @@ export interface App extends Disposable {
|
||||
wsRouter: Express
|
||||
/** The underlying HTTP server. */
|
||||
server: http.Server
|
||||
/** Handles requests to the editor session management API. */
|
||||
editorSessionManagerServer: http.Server
|
||||
}
|
||||
|
||||
export const listen = async (server: http.Server, { host, port, socket, "socket-mode": mode }: ListenOptions) => {
|
||||
if (socket) {
|
||||
const isSocketOpts = (opts: ListenOptions): opts is SocketOptions => {
|
||||
return !!(opts as SocketOptions).socket || !(opts as DefaultedArgs).host
|
||||
}
|
||||
|
||||
export const listen = async (server: http.Server, opts: ListenOptions) => {
|
||||
if (isSocketOpts(opts)) {
|
||||
try {
|
||||
await fs.unlink(socket)
|
||||
await fs.unlink(opts.socket)
|
||||
} catch (error: any) {
|
||||
handleArgsSocketCatchError(error)
|
||||
}
|
||||
@@ -38,18 +46,20 @@ export const listen = async (server: http.Server, { host, port, socket, "socket-
|
||||
server.on("error", (err) => util.logError(logger, "http server error", err))
|
||||
resolve()
|
||||
}
|
||||
if (socket) {
|
||||
server.listen(socket, onListen)
|
||||
if (isSocketOpts(opts)) {
|
||||
server.listen(opts.socket, onListen)
|
||||
} else {
|
||||
// [] is the correct format when using :: but Node errors with them.
|
||||
server.listen(port, host.replace(/^\[|\]$/g, ""), onListen)
|
||||
server.listen(opts.port, opts.host.replace(/^\[|\]$/g, ""), onListen)
|
||||
}
|
||||
})
|
||||
|
||||
// NOTE@jsjoeio: we need to chmod after the server is finished
|
||||
// listening. Otherwise, the socket may not have been created yet.
|
||||
if (socket && mode) {
|
||||
await fs.chmod(socket, mode)
|
||||
if (isSocketOpts(opts)) {
|
||||
if (opts["socket-mode"]) {
|
||||
await fs.chmod(opts.socket, opts["socket-mode"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +80,22 @@ export const createApp = async (args: DefaultedArgs): Promise<App> => {
|
||||
)
|
||||
: http.createServer(router)
|
||||
|
||||
const dispose = disposer(server)
|
||||
const disposeServer = disposer(server)
|
||||
|
||||
await listen(server, args)
|
||||
|
||||
const wsRouter = express()
|
||||
handleUpgrade(wsRouter, server)
|
||||
|
||||
return { router, wsRouter, server, dispose }
|
||||
const editorSessionManager = new EditorSessionManager()
|
||||
const editorSessionManagerServer = await makeEditorSessionManagerServer(args["session-socket"], editorSessionManager)
|
||||
const disposeEditorSessionManagerServer = disposer(editorSessionManagerServer)
|
||||
|
||||
const dispose = async () => {
|
||||
await Promise.all([disposeServer(), disposeEditorSessionManagerServer()])
|
||||
}
|
||||
|
||||
return { router, wsRouter, server, dispose, editorSessionManagerServer }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
152
src/node/cli.ts
152
src/node/cli.ts
@@ -3,9 +3,8 @@ 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 } from "./util"
|
||||
|
||||
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
|
||||
import { generateCertificate, generatePassword, humanPath, paths, splitOnFirstEquals } from "./util"
|
||||
import { EditorSessionManagerClient } from "./vscodeSocket"
|
||||
|
||||
export enum Feature {
|
||||
// No current experimental features!
|
||||
@@ -52,6 +51,7 @@ export interface UserProvidedCodeArgs {
|
||||
"disable-file-downloads"?: boolean
|
||||
"disable-workspace-trust"?: boolean
|
||||
"disable-getting-started-override"?: boolean
|
||||
"session-socket"?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,6 @@ 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
|
||||
@@ -162,6 +161,9 @@ export const options: Options<Required<UserProvidedArgs>> = {
|
||||
"Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" +
|
||||
"then notifies you once every week that a new release is available.",
|
||||
},
|
||||
"session-socket": {
|
||||
type: "string",
|
||||
},
|
||||
"disable-file-downloads": {
|
||||
type: "boolean",
|
||||
description:
|
||||
@@ -180,7 +182,14 @@ export const options: Options<Required<UserProvidedArgs>> = {
|
||||
enable: { type: "string[]" },
|
||||
help: { type: "boolean", short: "h", description: "Show this output." },
|
||||
json: { type: "boolean" },
|
||||
locale: { type: "string" }, // The preferred way to set the locale is via the UI.
|
||||
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
|
||||
`,
|
||||
},
|
||||
open: { type: "boolean", description: "Open in browser on startup. Does not work remotely." },
|
||||
|
||||
"bind-addr": {
|
||||
@@ -255,15 +264,6 @@ 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[] => {
|
||||
@@ -295,19 +295,6 @@ 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.
|
||||
@@ -441,19 +428,23 @@ export const parse = (
|
||||
throw new Error("--cert-key is missing")
|
||||
}
|
||||
|
||||
logger.debug(() => [
|
||||
`parsed ${opts?.configFile ? "config" : "command line"}`,
|
||||
field("args", {
|
||||
...args,
|
||||
password: args.password ? "<redacted>" : undefined,
|
||||
"hashed-password": args["hashed-password"] ? "<redacted>" : undefined,
|
||||
"github-auth": args["github-auth"] ? "<redacted>" : undefined,
|
||||
}),
|
||||
])
|
||||
logger.debug(() => [`parsed ${opts?.configFile ? "config" : "command line"}`, field("args", redactArgs(args))])
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
/**
|
||||
* Redact sensitive information from arguments for logging.
|
||||
*/
|
||||
export const redactArgs = (args: UserProvidedArgs): UserProvidedArgs => {
|
||||
return {
|
||||
...args,
|
||||
password: args.password ? "<redacted>" : undefined,
|
||||
"hashed-password": args["hashed-password"] ? "<redacted>" : undefined,
|
||||
"github-auth": args["github-auth"] ? "<redacted>" : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User-provided arguments with defaults. The distinction between user-provided
|
||||
* args and defaulted args exists so we can tell the difference between end
|
||||
@@ -472,6 +463,7 @@ export interface DefaultedArgs extends ConfigArgs {
|
||||
usingEnvHashedPassword: boolean
|
||||
"extensions-dir": string
|
||||
"user-data-dir": string
|
||||
"session-socket": string
|
||||
/* Positional arguments. */
|
||||
_: string[]
|
||||
}
|
||||
@@ -492,6 +484,11 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
|
||||
args["extensions-dir"] = path.join(args["user-data-dir"], "extensions")
|
||||
}
|
||||
|
||||
if (!args["session-socket"]) {
|
||||
args["session-socket"] = path.join(args["user-data-dir"], "code-server-ipc.sock")
|
||||
}
|
||||
process.env.CODE_SERVER_SESSION_SOCKET = args["session-socket"]
|
||||
|
||||
// --verbose takes priority over --log and --log takes priority over the
|
||||
// environment variable.
|
||||
if (args.verbose) {
|
||||
@@ -540,17 +537,6 @@ 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 = {
|
||||
@@ -589,12 +575,25 @@ 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)
|
||||
const finalProxies = []
|
||||
|
||||
if (typeof args._ === "undefined") {
|
||||
args._ = []
|
||||
for (const proxyDomain of proxyDomains) {
|
||||
if (!proxyDomain.includes("{{port}}")) {
|
||||
finalProxies.push("{{port}}." + proxyDomain)
|
||||
} else {
|
||||
finalProxies.push(proxyDomain)
|
||||
}
|
||||
}
|
||||
|
||||
// all proxies are of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
|
||||
// e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}
|
||||
if (finalProxies.length > 0 && !process.env.VSCODE_PROXY_URI) {
|
||||
process.env.VSCODE_PROXY_URI = `//${finalProxies[0]}`
|
||||
}
|
||||
args["proxy-domain"] = finalProxies
|
||||
|
||||
args._ = getResolvedPathsFromArgs(args)
|
||||
|
||||
return {
|
||||
...args,
|
||||
usingEnvPassword,
|
||||
@@ -602,6 +601,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
|
||||
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
|
||||
}
|
||||
|
||||
export function getResolvedPathsFromArgs(args: UserProvidedArgs): string[] {
|
||||
return (args._ ?? []).map((p) => path.resolve(p))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return the default config file.
|
||||
*
|
||||
@@ -741,47 +744,38 @@ function bindAddrFromAllSources(...argsConfig: UserProvidedArgs[]): Addr {
|
||||
return addr
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the socketPath based on path passed in.
|
||||
*
|
||||
* The one usually passed in is the DEFAULT_SOCKET_PATH.
|
||||
*
|
||||
* If it can't read the path, it throws an error and returns undefined.
|
||||
*/
|
||||
export async function readSocketPath(path: string): Promise<string | undefined> {
|
||||
try {
|
||||
return await fs.readFile(path, "utf8")
|
||||
} catch (error) {
|
||||
// If it doesn't exist, we don't care.
|
||||
// But if it fails for some reason, we should throw.
|
||||
// We want to surface that to the user.
|
||||
if (!isNodeJSErrnoException(error) || error.code !== "ENOENT") {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if it looks like the user is trying to open a file or folder in an
|
||||
* existing instance. The arguments here should be the arguments the user
|
||||
* explicitly passed on the command line, *NOT DEFAULTS* or the configuration.
|
||||
*/
|
||||
export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Promise<string | undefined> => {
|
||||
export const shouldOpenInExistingInstance = async (
|
||||
args: UserProvidedArgs,
|
||||
sessionSocket: string,
|
||||
): Promise<string | undefined> => {
|
||||
// Always use the existing instance if we're running from VS Code's terminal.
|
||||
if (process.env.VSCODE_IPC_HOOK_CLI) {
|
||||
logger.debug("Found VSCODE_IPC_HOOK_CLI")
|
||||
return process.env.VSCODE_IPC_HOOK_CLI
|
||||
}
|
||||
|
||||
const paths = getResolvedPathsFromArgs(args)
|
||||
const client = new EditorSessionManagerClient(sessionSocket)
|
||||
|
||||
// If these flags are set then assume the user is trying to open in an
|
||||
// existing instance since these flags have no effect otherwise.
|
||||
// existing instance since these flags have no effect otherwise. That means
|
||||
// if there is no existing instance we should error rather than falling back
|
||||
// to spawning code-server normally.
|
||||
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
|
||||
return args[cur as keyof UserProvidedArgs] ? prev + 1 : prev
|
||||
}, 0)
|
||||
if (openInFlagCount > 0) {
|
||||
logger.debug("Found --reuse-window or --new-window")
|
||||
return readSocketPath(DEFAULT_SOCKET_PATH)
|
||||
const socketPath = await client.getConnectedSocketPath(paths[0])
|
||||
if (!socketPath) {
|
||||
throw new Error(`No opened code-server instances found to handle ${paths[0]}`)
|
||||
}
|
||||
return socketPath
|
||||
}
|
||||
|
||||
// It's possible the user is trying to spawn another instance of code-server.
|
||||
@@ -789,9 +783,13 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
|
||||
// code-server is invoked exactly like this: `code-server my-file`).
|
||||
// 2. That a file or directory was passed.
|
||||
// 3. That the socket is active.
|
||||
// 4. That an instance exists to handle the path (implied by #3).
|
||||
if (Object.keys(args).length === 1 && typeof args._ !== "undefined" && args._.length > 0) {
|
||||
const socketPath = await readSocketPath(DEFAULT_SOCKET_PATH)
|
||||
if (socketPath && (await canConnect(socketPath))) {
|
||||
if (!(await client.canConnect())) {
|
||||
return undefined
|
||||
}
|
||||
const socketPath = await client.getConnectedSocketPath(paths[0])
|
||||
if (socketPath) {
|
||||
logger.debug("Found existing code-server socket")
|
||||
return socketPath
|
||||
}
|
||||
@@ -814,6 +812,7 @@ export interface CodeArgs extends UserProvidedCodeArgs {
|
||||
"without-connection-token"?: boolean
|
||||
"without-browser-env-var"?: boolean
|
||||
compatibility: string
|
||||
log: string[] | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -835,5 +834,6 @@ export const toCodeArgs = async (args: DefaultedArgs): Promise<CodeArgs> => {
|
||||
help: !!args.help,
|
||||
version: !!args.version,
|
||||
port: args.port?.toString(),
|
||||
log: args.log ? [args.log] : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ async function entry(): Promise<void> {
|
||||
return runCodeCli(args)
|
||||
}
|
||||
|
||||
const socketPath = await shouldOpenInExistingInstance(cliArgs)
|
||||
const socketPath = await shouldOpenInExistingInstance(cliArgs, args["session-socket"])
|
||||
if (socketPath) {
|
||||
logger.debug("Trying to open in existing instance")
|
||||
return openInExistingInstance(args, socketPath)
|
||||
|
||||
@@ -12,7 +12,15 @@ 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 } from "./util"
|
||||
import {
|
||||
getPasswordMethod,
|
||||
IsCookieValidArgs,
|
||||
isCookieValid,
|
||||
sanitizeString,
|
||||
escapeHtml,
|
||||
escapeJSON,
|
||||
splitOnFirstEquals,
|
||||
} from "./util"
|
||||
|
||||
/**
|
||||
* Base options included on every page.
|
||||
@@ -308,3 +316,86 @@ 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.
|
||||
*/
|
||||
export 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. Some reverse proxies will set multiple
|
||||
// comma-separated hosts.
|
||||
const xHost = getFirstHeader(req, "x-forwarded-host")
|
||||
if (xHost) {
|
||||
const firstXHost = xHost.split(",")[0]
|
||||
if (firstXHost) {
|
||||
return firstXHost.trim().toLowerCase()
|
||||
}
|
||||
}
|
||||
|
||||
const host = getFirstHeader(req, "host")
|
||||
return host ? host.trim().toLowerCase() : undefined
|
||||
}
|
||||
|
||||
24
src/node/i18n/index.ts
Normal file
24
src/node/i18n/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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
|
||||
13
src/node/i18n/locales/en.json
Normal file
13
src/node/i18n/locales/en.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
13
src/node/i18n/locales/th.json
Normal file
13
src/node/i18n/locales/th.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"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": "รหัสผ่านไม่ถูกต้อง"
|
||||
}
|
||||
13
src/node/i18n/locales/zh-cn.json
Normal file
13
src/node/i18n/locales/zh-cn.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"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": "密码不正确"
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
import { field, logger } from "@coder/logger"
|
||||
import http from "http"
|
||||
import * as os from "os"
|
||||
import path from "path"
|
||||
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"
|
||||
@@ -71,9 +69,8 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
|
||||
forceNewWindow: args["new-window"],
|
||||
gotoLineMode: true,
|
||||
}
|
||||
const paths = args._ || []
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const fp = path.resolve(paths[i])
|
||||
for (let i = 0; i < args._.length; i++) {
|
||||
const fp = args._[i]
|
||||
if (await isDirectory(fp)) {
|
||||
pipeArgs.folderURIs.push(fp)
|
||||
} else {
|
||||
@@ -127,12 +124,7 @@ 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()} ${
|
||||
args.link ? "(randomized by --link)" : ""
|
||||
}`,
|
||||
)
|
||||
|
||||
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
|
||||
if (args.auth === AuthType.Password) {
|
||||
logger.info(" - Authentication is enabled")
|
||||
if (args.usingEnvPassword) {
|
||||
@@ -143,23 +135,26 @@ export const runCodeServer = async (
|
||||
logger.info(` - Using password from ${humanPath(os.homedir(), args.config)}`)
|
||||
}
|
||||
} else {
|
||||
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`)
|
||||
logger.info(" - Authentication is disabled")
|
||||
}
|
||||
|
||||
if (args.cert) {
|
||||
logger.info(` - Using certificate for HTTPS: ${humanPath(os.homedir(), args.cert.value)}`)
|
||||
} else {
|
||||
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`)
|
||||
logger.info(" - Not serving HTTPS")
|
||||
}
|
||||
|
||||
if (args["proxy-domain"].length > 0) {
|
||||
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
|
||||
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
|
||||
args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`))
|
||||
}
|
||||
if (process.env.VSCODE_PROXY_URI) {
|
||||
logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`)
|
||||
}
|
||||
|
||||
if (args.link) {
|
||||
await coderCloudBind(serverAddress, args.link.value)
|
||||
logger.info(" - Connected to cloud agent")
|
||||
const sessionServerAddress = app.editorSessionManagerServer.address()
|
||||
if (sessionServerAddress) {
|
||||
logger.info(`Session server listening on ${sessionServerAddress.toString()}`)
|
||||
}
|
||||
|
||||
if (args.enable && args.enable.length > 0) {
|
||||
|
||||
@@ -227,7 +227,7 @@ export class PluginAPI {
|
||||
`)
|
||||
}
|
||||
if (!semver.satisfies(version, packageJSON.engines["code-server"])) {
|
||||
throw new Error(
|
||||
this.logger.warn(
|
||||
`plugin range ${q(packageJSON.engines["code-server"])} incompatible` + ` with code-server version ${version}`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,19 @@ 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) => {
|
||||
res.writeHead(HttpCode.ServerError)
|
||||
res.end(error.message)
|
||||
// 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`)
|
||||
}
|
||||
})
|
||||
|
||||
// Intercept the response to rewrite absolute redirects against the base path.
|
||||
|
||||
@@ -1,34 +1,56 @@
|
||||
import { Request, Router } from "express"
|
||||
import { HttpCode, HttpError } from "../../common/http"
|
||||
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
|
||||
import { getHost, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
|
||||
import { proxy } from "../proxy"
|
||||
import { Router as WsRouter } from "../wsRouter"
|
||||
|
||||
export const router = Router()
|
||||
|
||||
const proxyDomainToRegex = (matchString: string): RegExp => {
|
||||
const escapedMatchString = matchString.replace(/[.*+?^$()|[\]\\]/g, "\\$&")
|
||||
|
||||
// Replace {{port}} with a regex group to capture the port
|
||||
// Replace {{host}} with .+ to allow any host match (so rely on DNS record here)
|
||||
let regexString = escapedMatchString.replace("{{port}}", "(\\d+)")
|
||||
regexString = regexString.replace("{{host}}", ".+")
|
||||
|
||||
regexString = regexString.replace(/[{}]/g, "\\$&") //replace any '{}' that might be left
|
||||
|
||||
return new RegExp("^" + regexString + "$")
|
||||
}
|
||||
|
||||
let proxyRegexes: RegExp[] = []
|
||||
const proxyDomainsToRegex = (proxyDomains: string[]): RegExp[] => {
|
||||
if (proxyDomains.length !== proxyRegexes.length) {
|
||||
proxyRegexes = proxyDomains.map(proxyDomainToRegex)
|
||||
}
|
||||
return proxyRegexes
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port if the request should be proxied. Anything that ends in a
|
||||
* proxy domain and has a *single* subdomain should be proxied. Anything else
|
||||
* should return `undefined` and will be handled as normal.
|
||||
* Return the port if the request should be proxied.
|
||||
*
|
||||
* The proxy-domain should be of format anyprefix-{{port}}-anysuffix.{{host}}, where {{host}} is optional
|
||||
* e.g. code-8080.domain.tld would match for code-{{port}}.domain.tld and code-{{port}}.{{host}}.
|
||||
*
|
||||
* For example if `coder.com` is specified `8080.coder.com` will be proxied
|
||||
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
|
||||
*/
|
||||
const maybeProxy = (req: Request): string | undefined => {
|
||||
// Split into parts.
|
||||
const host = req.headers.host || ""
|
||||
const idx = host.indexOf(":")
|
||||
const domain = idx !== -1 ? host.substring(0, idx) : host
|
||||
const parts = domain.split(".")
|
||||
|
||||
// There must be an exact match.
|
||||
const port = parts.shift()
|
||||
const proxyDomain = parts.join(".")
|
||||
if (!port || !req.args["proxy-domain"].includes(proxyDomain)) {
|
||||
const reqDomain = getHost(req)
|
||||
if (reqDomain === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return port
|
||||
const regexs = proxyDomainsToRegex(req.args["proxy-domain"])
|
||||
|
||||
for (const regex of regexs) {
|
||||
const match = reqDomain.match(regex)
|
||||
|
||||
if (match) {
|
||||
return match[1] // match[1] contains the port
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
router.all("*", async (req, res, next) => {
|
||||
@@ -78,10 +100,8 @@ wsRouter.ws("*", async (req, _, next) => {
|
||||
if (!port) {
|
||||
return next()
|
||||
}
|
||||
|
||||
// Must be authenticated to use the proxy.
|
||||
ensureOrigin(req)
|
||||
await ensureAuthenticated(req)
|
||||
|
||||
proxy.ws(req, req.ws, req.head, {
|
||||
ignorePath: true,
|
||||
target: `http://0.0.0.0:${port}${req.originalUrl}`,
|
||||
|
||||
@@ -62,6 +62,16 @@ 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}`)
|
||||
;(req as WebsocketRequest).ws.end()
|
||||
let statusCode = 500
|
||||
if (errorHasStatusCode(err)) {
|
||||
statusCode = err.statusCode
|
||||
} else if (errorHasCode(err) && notFoundCodes.includes(err.code)) {
|
||||
statusCode = HttpCode.NotFound
|
||||
}
|
||||
if (statusCode >= 500) {
|
||||
logger.error(`${err.message} ${err.stack}`)
|
||||
} else {
|
||||
logger.debug(`${err.message} ${err.stack}`)
|
||||
}
|
||||
;(req as WebsocketRequest).ws.end(`HTTP/1.1 ${statusCode} ${err.message}\r\n\r\n`)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,15 @@ 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)
|
||||
}
|
||||
@@ -81,6 +89,13 @@ 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")
|
||||
|
||||
@@ -7,12 +7,13 @@ 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(2, "minute")
|
||||
private readonly hourLimiter = new Limiter(12, "hour")
|
||||
private readonly minuteLimiter = new Limiter({ tokensPerInterval: 2, interval: "minute" })
|
||||
private readonly hourLimiter = new Limiter({ tokensPerInterval: 12, interval: "hour" })
|
||||
|
||||
public canTry(): boolean {
|
||||
// Note: we must check using >= 1 because technically when there are no tokens left
|
||||
@@ -28,21 +29,26 @@ 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"] || `Welcome to ${appName}`
|
||||
let passwordMsg = `Check the config file at ${humanPath(os.homedir(), req.args.config)} for the password.`
|
||||
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) })
|
||||
if (req.args.usingEnvPassword) {
|
||||
passwordMsg = "Password was set from $PASSWORD."
|
||||
passwordMsg = i18n.t("LOGIN_USING_ENV_PASSWORD")
|
||||
} else if (req.args.usingEnvHashedPassword) {
|
||||
passwordMsg = "Password was set from $HASHED_PASSWORD."
|
||||
passwordMsg = i18n.t("LOGIN_USING_HASHED_PASSWORD")
|
||||
}
|
||||
|
||||
return replaceTemplates(
|
||||
req,
|
||||
content
|
||||
.replace(/{{APP_NAME}}/g, appName)
|
||||
.replace(/{{I18N_LOGIN_TITLE}}/g, i18n.t("LOGIN_TITLE", { app: 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>` : ""),
|
||||
)
|
||||
}
|
||||
@@ -70,11 +76,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("Login rate limited!")
|
||||
throw new Error(i18n.t("LOGIN_RATE_LIMIT") as string)
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw new Error("Missing password")
|
||||
throw new Error(i18n.t("MISS_PASSWORD") as string)
|
||||
}
|
||||
|
||||
const passwordMethod = getPasswordMethod(hashedPasswordFromArgs)
|
||||
@@ -108,7 +114,7 @@ router.post<{}, string, { password: string; base?: string }, { to?: string }>("/
|
||||
}),
|
||||
)
|
||||
|
||||
throw new Error("Incorrect password")
|
||||
throw new Error(i18n.t("INCORRECT_PASSWORD") as string)
|
||||
} catch (error: any) {
|
||||
const renderedHtml = await getRoot(req, error)
|
||||
res.send(renderedHtml)
|
||||
|
||||
@@ -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, redirect, self } from "../http"
|
||||
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
|
||||
import { proxy as _proxy } from "../proxy"
|
||||
|
||||
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
|
||||
@@ -11,7 +11,7 @@ const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
|
||||
return `http://0.0.0.0:${req.params.port}/${req.originalUrl}`
|
||||
}
|
||||
const query = qs.stringify(req.query)
|
||||
return `http://0.0.0.0:${req.params.port}/${req.params[0] || ""}${query ? `?${query}` : ""}`
|
||||
return encodeURI(`http://0.0.0.0:${req.params.port}${req.params[0] || ""}${query ? `?${query}` : ""}`)
|
||||
}
|
||||
|
||||
export async function proxy(
|
||||
@@ -50,6 +50,7 @@ export async function wsProxy(
|
||||
passthroughPath?: boolean
|
||||
},
|
||||
): Promise<void> {
|
||||
ensureOrigin(req)
|
||||
await ensureAuthenticated(req)
|
||||
_proxy.ws(req, req.ws, req.head, {
|
||||
ignorePath: true,
|
||||
|
||||
@@ -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, redirect, replaceTemplates, self } from "../http"
|
||||
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
|
||||
import { SocketProxyProvider } from "../socket"
|
||||
import { isFile, loadAMDModule } from "../util"
|
||||
import { Router as WsRouter } from "../wsRouter"
|
||||
@@ -40,6 +40,7 @@ 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(
|
||||
@@ -47,8 +48,8 @@ export class CodeServerRouteWrapper {
|
||||
req,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "code-server",
|
||||
short_name: "code-server",
|
||||
name: appName,
|
||||
short_name: appName,
|
||||
start_url: ".",
|
||||
display: "fullscreen",
|
||||
description: "Run Code on a remote server.",
|
||||
@@ -173,7 +174,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("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
||||
this._wsRouterWrapper.ws("*", ensureOrigin, ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { field, logger } from "@coder/logger"
|
||||
import * as http from "http"
|
||||
import * as https from "https"
|
||||
import ProxyAgent from "proxy-agent"
|
||||
import { ProxyAgent } from "proxy-agent"
|
||||
import * as semver from "semver"
|
||||
import * as url from "url"
|
||||
import { httpProxyUri, version } from "./constants"
|
||||
@@ -104,7 +104,9 @@ export class UpdateProvider {
|
||||
const request = (uri: string): void => {
|
||||
logger.debug("Making request", field("uri", uri))
|
||||
const isHttps = uri.startsWith("https")
|
||||
const agent = httpProxyUri ? new ProxyAgent(httpProxyUri) : undefined
|
||||
const agent = new ProxyAgent({
|
||||
getProxyForUrl: () => httpProxyUri || "",
|
||||
})
|
||||
const httpx = isHttps ? https : http
|
||||
const client = httpx.get(uri, { headers: { "User-Agent": "code-server" }, agent }, (response) => {
|
||||
if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 400) {
|
||||
|
||||
@@ -541,3 +541,13 @@ 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]]
|
||||
}
|
||||
|
||||
206
src/node/vscodeSocket.ts
Normal file
206
src/node/vscodeSocket.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { logger } from "@coder/logger"
|
||||
import express from "express"
|
||||
import * as http from "http"
|
||||
import * as path from "path"
|
||||
import { HttpCode } from "../common/http"
|
||||
import { listen } from "./app"
|
||||
import { canConnect } from "./util"
|
||||
|
||||
export interface EditorSessionEntry {
|
||||
workspace: {
|
||||
id: string
|
||||
folders: {
|
||||
uri: {
|
||||
path: string
|
||||
}
|
||||
}[]
|
||||
}
|
||||
|
||||
socketPath: string
|
||||
}
|
||||
|
||||
interface DeleteSessionRequest {
|
||||
socketPath: string
|
||||
}
|
||||
|
||||
interface AddSessionRequest {
|
||||
entry: EditorSessionEntry
|
||||
}
|
||||
|
||||
interface GetSessionResponse {
|
||||
socketPath?: string
|
||||
}
|
||||
|
||||
export async function makeEditorSessionManagerServer(
|
||||
codeServerSocketPath: string,
|
||||
editorSessionManager: EditorSessionManager,
|
||||
): Promise<http.Server> {
|
||||
const router = express()
|
||||
|
||||
// eslint-disable-next-line import/no-named-as-default-member
|
||||
router.use(express.json())
|
||||
|
||||
router.get("/session", async (req, res) => {
|
||||
const filePath = req.query.filePath as string
|
||||
if (!filePath) {
|
||||
res.status(HttpCode.BadRequest).send("filePath is required")
|
||||
return
|
||||
}
|
||||
try {
|
||||
const socketPath = await editorSessionManager.getConnectedSocketPath(filePath)
|
||||
const response: GetSessionResponse = { socketPath }
|
||||
res.json(response)
|
||||
} catch (error: unknown) {
|
||||
res.status(HttpCode.ServerError).send(error)
|
||||
}
|
||||
})
|
||||
|
||||
router.post("/add-session", async (req, res) => {
|
||||
const request = req.body as AddSessionRequest
|
||||
if (!request.entry) {
|
||||
res.status(400).send("entry is required")
|
||||
}
|
||||
editorSessionManager.addSession(request.entry)
|
||||
res.status(200).send()
|
||||
})
|
||||
|
||||
router.post("/delete-session", async (req, res) => {
|
||||
const request = req.body as DeleteSessionRequest
|
||||
if (!request.socketPath) {
|
||||
res.status(400).send("socketPath is required")
|
||||
}
|
||||
editorSessionManager.deleteSession(request.socketPath)
|
||||
res.status(200).send()
|
||||
})
|
||||
|
||||
const server = http.createServer(router)
|
||||
try {
|
||||
await listen(server, { socket: codeServerSocketPath })
|
||||
} catch (e) {
|
||||
logger.warn(`Could not create socket at ${codeServerSocketPath}`)
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
export class EditorSessionManager {
|
||||
// Map from socket path to EditorSessionEntry.
|
||||
private entries = new Map<string, EditorSessionEntry>()
|
||||
|
||||
addSession(entry: EditorSessionEntry): void {
|
||||
logger.debug(`Adding session to session registry: ${entry.socketPath}`)
|
||||
this.entries.set(entry.socketPath, entry)
|
||||
}
|
||||
|
||||
getCandidatesForFile(filePath: string): EditorSessionEntry[] {
|
||||
const matchCheckResults = new Map<string, boolean>()
|
||||
|
||||
const checkMatch = (entry: EditorSessionEntry): boolean => {
|
||||
if (matchCheckResults.has(entry.socketPath)) {
|
||||
return matchCheckResults.get(entry.socketPath)!
|
||||
}
|
||||
const result = entry.workspace.folders.some((folder) => filePath.startsWith(folder.uri.path + path.sep))
|
||||
matchCheckResults.set(entry.socketPath, result)
|
||||
return result
|
||||
}
|
||||
|
||||
return Array.from(this.entries.values())
|
||||
.reverse() // Most recently registered first.
|
||||
.sort((a, b) => {
|
||||
// Matches first.
|
||||
const aMatch = checkMatch(a)
|
||||
const bMatch = checkMatch(b)
|
||||
if (aMatch === bMatch) {
|
||||
return 0
|
||||
}
|
||||
if (aMatch) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
}
|
||||
|
||||
deleteSession(socketPath: string): void {
|
||||
logger.debug(`Deleting session from session registry: ${socketPath}`)
|
||||
this.entries.delete(socketPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best socket path that we can connect to.
|
||||
* We also delete any sockets that we can't connect to.
|
||||
*/
|
||||
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
|
||||
const candidates = this.getCandidatesForFile(filePath)
|
||||
let match: EditorSessionEntry | undefined = undefined
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (await canConnect(candidate.socketPath)) {
|
||||
match = candidate
|
||||
break
|
||||
}
|
||||
this.deleteSession(candidate.socketPath)
|
||||
}
|
||||
|
||||
return match?.socketPath
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorSessionManagerClient {
|
||||
constructor(private codeServerSocketPath: string) {}
|
||||
|
||||
async canConnect() {
|
||||
return canConnect(this.codeServerSocketPath)
|
||||
}
|
||||
|
||||
async getConnectedSocketPath(filePath: string): Promise<string | undefined> {
|
||||
const response = await new Promise<GetSessionResponse>((resolve, reject) => {
|
||||
const opts = {
|
||||
path: "/session?filePath=" + encodeURIComponent(filePath),
|
||||
socketPath: this.codeServerSocketPath,
|
||||
method: "GET",
|
||||
}
|
||||
const req = http.request(opts, (res) => {
|
||||
let rawData = ""
|
||||
res.setEncoding("utf8")
|
||||
res.on("data", (chunk) => {
|
||||
rawData += chunk
|
||||
})
|
||||
res.on("end", () => {
|
||||
try {
|
||||
const obj = JSON.parse(rawData)
|
||||
if (res.statusCode === 200) {
|
||||
resolve(obj)
|
||||
} else {
|
||||
reject(new Error("Unexpected status code: " + res.statusCode))
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
req.on("error", reject)
|
||||
req.end()
|
||||
})
|
||||
return response.socketPath
|
||||
}
|
||||
|
||||
// Currently only used for tests.
|
||||
async addSession(request: AddSessionRequest): Promise<void> {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const opts = {
|
||||
path: "/add-session",
|
||||
socketPath: this.codeServerSocketPath,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
accept: "application/json",
|
||||
},
|
||||
}
|
||||
const req = http.request(opts, () => {
|
||||
resolve()
|
||||
})
|
||||
req.on("error", reject)
|
||||
req.write(JSON.stringify(request))
|
||||
req.end()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import * as cp from "child_process"
|
||||
import * as path from "path"
|
||||
import * as rfs from "rotating-file-stream"
|
||||
import { Emitter } from "../common/emitter"
|
||||
import { DefaultedArgs } from "./cli"
|
||||
import { DefaultedArgs, redactArgs } from "./cli"
|
||||
import { paths } from "./util"
|
||||
|
||||
const timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
|
||||
@@ -44,10 +44,11 @@ export function onMessage<M, T extends M>(
|
||||
}
|
||||
|
||||
const onMessage = (message: M) => {
|
||||
;(customLogger || logger).trace("got message", field("message", message))
|
||||
if (fn(message)) {
|
||||
cleanup()
|
||||
resolve(message)
|
||||
} else {
|
||||
;(customLogger || logger).debug("got unhandled message", field("message", message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +148,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.
|
||||
*/
|
||||
class ChildProcess extends Process {
|
||||
export class ChildProcess extends Process {
|
||||
public logger = logger.named(`child:${process.pid}`)
|
||||
|
||||
public constructor(private readonly parentPid: number) {
|
||||
@@ -172,6 +173,7 @@ class ChildProcess extends Process {
|
||||
* Initiate the handshake and wait for a response from the parent.
|
||||
*/
|
||||
public async handshake(): Promise<DefaultedArgs> {
|
||||
this.logger.debug("initiating handshake")
|
||||
this.send({ type: "handshake" })
|
||||
const message = await onMessage<ParentMessage, ParentHandshakeMessage>(
|
||||
process,
|
||||
@@ -180,6 +182,13 @@ class ChildProcess extends Process {
|
||||
},
|
||||
this.logger,
|
||||
)
|
||||
this.logger.debug(
|
||||
"got message",
|
||||
field("message", {
|
||||
type: message.type,
|
||||
args: redactArgs(message.args),
|
||||
}),
|
||||
)
|
||||
return message.args
|
||||
}
|
||||
|
||||
@@ -280,6 +289,10 @@ export class ParentProcess extends Process {
|
||||
}
|
||||
|
||||
public start(args: DefaultedArgs): Promise<void> {
|
||||
// Our logger was created before we parsed CLI arguments so update the level
|
||||
// in case it has changed.
|
||||
this.logger.level = logger.level
|
||||
|
||||
// Store for relaunches.
|
||||
this.args = args
|
||||
if (!this.started) {
|
||||
@@ -306,7 +319,7 @@ export class ParentProcess extends Process {
|
||||
})
|
||||
}
|
||||
|
||||
this.logger.debug(`spawned inner process ${child.pid}`)
|
||||
this.logger.debug(`spawned child process ${child.pid}`)
|
||||
|
||||
await this.handshake(child)
|
||||
|
||||
@@ -334,13 +347,14 @@ export class ParentProcess extends Process {
|
||||
if (!this.args) {
|
||||
throw new Error("started without args")
|
||||
}
|
||||
await onMessage<ChildMessage, ChildHandshakeMessage>(
|
||||
const message = await onMessage<ChildMessage, ChildHandshakeMessage>(
|
||||
child,
|
||||
(message): message is ChildHandshakeMessage => {
|
||||
return message.type === "handshake"
|
||||
},
|
||||
this.logger,
|
||||
)
|
||||
this.logger.debug("got message", field("message", message))
|
||||
this.send(child, { type: "handshake", args: this.args })
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ 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(
|
||||
|
||||
@@ -117,40 +117,26 @@ export class CodeServer {
|
||||
* directories.
|
||||
*/
|
||||
private async spawn(): Promise<CodeServerProcess> {
|
||||
// This will be used both as the workspace and data directory to ensure
|
||||
// instances don't bleed into each other.
|
||||
const dir = await this.createWorkspace()
|
||||
|
||||
const args = await this.argsWithDefaults([
|
||||
"--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",
|
||||
"127.0.0.1:0",
|
||||
])
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = [
|
||||
this.entry,
|
||||
"--extensions-dir",
|
||||
path.join(dir, "extensions"),
|
||||
"--auth",
|
||||
"none",
|
||||
...this.args,
|
||||
// Using port zero will spawn on a random port.
|
||||
"--bind-addr",
|
||||
"127.0.0.1:0",
|
||||
// Setting the XDG variables would be easier and more thorough but the
|
||||
// modules we import ignores those variables for non-Linux operating
|
||||
// systems so use these flags instead.
|
||||
"--config",
|
||||
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, {
|
||||
cwd: path.join(__dirname, "../../.."),
|
||||
env: {
|
||||
...process.env,
|
||||
...this.env,
|
||||
// Set to empty string to prevent code-server from
|
||||
// using the existing instance when running the e2e tests
|
||||
// from an integrated terminal.
|
||||
// Prevent code-server from using the existing instance when running
|
||||
// the e2e tests from an integrated terminal.
|
||||
VSCODE_IPC_HOOK_CLI: "",
|
||||
PASSWORD,
|
||||
},
|
||||
@@ -173,11 +159,15 @@ export class CodeServer {
|
||||
reject(error)
|
||||
})
|
||||
|
||||
// Tracks when the HTTP and session servers are ready.
|
||||
let httpAddress: string | undefined
|
||||
let sessionAddress: string | undefined
|
||||
|
||||
let resolved = false
|
||||
proc.stdout.setEncoding("utf8")
|
||||
onLine(proc, (line) => {
|
||||
// As long as we are actively getting input reset the timer. If we stop
|
||||
// getting input and still have not found the address the timer will
|
||||
// getting input and still have not found the addresses the timer will
|
||||
// reject.
|
||||
timer.reset()
|
||||
|
||||
@@ -186,20 +176,69 @@ export class CodeServer {
|
||||
if (resolved) {
|
||||
return
|
||||
}
|
||||
const match = line.trim().match(/HTTPS? server listening on (https?:\/\/[.:\d]+)\/?$/)
|
||||
|
||||
let match = line.trim().match(/HTTPS? server listening on (https?:\/\/[.:\d]+)\/?$/)
|
||||
if (match) {
|
||||
// Cookies don't seem to work on IP address so swap to localhost.
|
||||
// Cookies don't seem to work on IP addresses so swap to localhost.
|
||||
// TODO: Investigate whether this is a bug with code-server.
|
||||
const address = match[1].replace("127.0.0.1", "localhost")
|
||||
this.logger.debug(`spawned on ${address}`)
|
||||
httpAddress = match[1].replace("127.0.0.1", "localhost")
|
||||
}
|
||||
|
||||
match = line.trim().match(/Session server listening on (.+)$/)
|
||||
if (match) {
|
||||
sessionAddress = match[1]
|
||||
}
|
||||
|
||||
if (typeof httpAddress !== "undefined" && typeof sessionAddress !== "undefined") {
|
||||
resolved = true
|
||||
timer.dispose()
|
||||
resolve({ process: proc, address })
|
||||
this.logger.debug(`code-server is ready: ${httpAddress} ${sessionAddress}`)
|
||||
resolve({ process: proc, address: httpAddress })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a short-lived command.
|
||||
*/
|
||||
async run(args: string[]): Promise<void> {
|
||||
args = await this.argsWithDefaults(args)
|
||||
this.logger.debug("executing `node " + args.join(" ") + "`")
|
||||
await util.promisify(cp.exec)("node " + args.join(" "), {
|
||||
cwd: path.join(__dirname, "../../.."),
|
||||
env: {
|
||||
...process.env,
|
||||
...this.env,
|
||||
// Prevent code-server from using the existing instance when running
|
||||
// the e2e tests from an integrated terminal.
|
||||
VSCODE_IPC_HOOK_CLI: "",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine arguments with defaults.
|
||||
*/
|
||||
private async argsWithDefaults(args: string[]): Promise<string[]> {
|
||||
// This will be used both as the workspace and data directory to ensure
|
||||
// instances don't bleed into each other.
|
||||
const dir = await this.workspaceDir
|
||||
return [
|
||||
this.entry,
|
||||
"--extensions-dir",
|
||||
path.join(dir, "extensions"),
|
||||
...args,
|
||||
// Setting the XDG variables would be easier and more thorough but the
|
||||
// modules we import ignores those variables for non-Linux operating
|
||||
// systems so use these flags instead.
|
||||
"--config",
|
||||
path.join(dir, "config.yaml"),
|
||||
"--user-data-dir",
|
||||
dir,
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the code-server process.
|
||||
*/
|
||||
@@ -364,6 +403,13 @@ export class CodeServerPage {
|
||||
await this.waitForTab(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a file through an external command.
|
||||
*/
|
||||
async openFileExternally(file: string) {
|
||||
await this.codeServer.run(["--reuse-window", file])
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a tab to open for the specified file.
|
||||
*/
|
||||
|
||||
111
test/e2e/routes.test.ts
Normal file
111
test/e2e/routes.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
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")
|
||||
})
|
||||
})
|
||||
@@ -35,13 +35,19 @@ describe("Integrated Terminal", ["--disable-workspace-trust"], {}, () => {
|
||||
const tmpFolderPath = await tmpdir(testName)
|
||||
const tmpFile = path.join(tmpFolderPath, "test-file")
|
||||
await fs.writeFile(tmpFile, "test")
|
||||
const fileName = path.basename(tmpFile)
|
||||
|
||||
await codeServerPage.focusTerminal()
|
||||
|
||||
await codeServerPage.page.keyboard.type(`code-server ${tmpFile}`)
|
||||
await codeServerPage.page.keyboard.press("Enter")
|
||||
|
||||
await codeServerPage.waitForTab(fileName)
|
||||
await codeServerPage.waitForTab(path.basename(tmpFile))
|
||||
|
||||
const externalTmpFile = path.join(tmpFolderPath, "test-external-file")
|
||||
await fs.writeFile(externalTmpFile, "foobar")
|
||||
|
||||
await codeServerPage.openFileExternally(externalTmpFile)
|
||||
|
||||
await codeServerPage.waitForTab(path.basename(externalTmpFile))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
28
test/e2e/webview.test.ts
Normal file
28
test/e2e/webview.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { promises as fs } from "fs"
|
||||
import * as path from "path"
|
||||
import { describe, test, expect } from "./baseFixture"
|
||||
|
||||
describe("Webviews", ["--disable-workspace-trust"], {}, () => {
|
||||
test("should preview a Markdown file", async ({ codeServerPage }) => {
|
||||
// Create Markdown file
|
||||
const heading = "Hello world"
|
||||
const dir = await codeServerPage.workspaceDir
|
||||
const file = path.join(dir, "text.md")
|
||||
await fs.writeFile(file, `# ${heading}`)
|
||||
await codeServerPage.openFile(file)
|
||||
|
||||
// Open Preview
|
||||
await codeServerPage.executeCommandViaMenus("Markdown: Open Preview to the Side")
|
||||
// Wait for the iframe to open and load
|
||||
await codeServerPage.waitForTab(`Preview ${file}`)
|
||||
|
||||
// It's an iframe within an iframe
|
||||
// so we have to do .frameLocator twice
|
||||
const renderedText = await codeServerPage.page
|
||||
.frameLocator("iframe.webview.ready")
|
||||
.frameLocator("#active-frame")
|
||||
.locator("text=Hello world")
|
||||
|
||||
expect(renderedText).toBeVisible
|
||||
})
|
||||
})
|
||||
@@ -12,7 +12,7 @@ describe("--install-extension", () => {
|
||||
setupFlags = ["--extensions-dir", tempDir]
|
||||
})
|
||||
it("should use EXTENSIONS_GALLERY when set", async () => {
|
||||
const extName = `author.extension-1.0.0`
|
||||
const extName = "author.extension"
|
||||
const { stderr } = await runCodeServerCommand([...setupFlags, "--install-extension", extName], {
|
||||
EXTENSIONS_GALLERY: "{}",
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user