diff --git a/.github/semantic.yaml b/.github/semantic.yaml index 27a663ed5662..55d345cc2ac9 100644 --- a/.github/semantic.yaml +++ b/.github/semantic.yaml @@ -61,3 +61,6 @@ types: # implementations. For example, if a commit adds a fix + test, it's a fix # commit. If a commit is simply bumping coverage, it's a test commit. - test + + # A new release. + - release diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6906cc145647..a977cd5077b5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,6 +29,9 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: true - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -38,21 +41,17 @@ jobs: - name: Install helm uses: azure/setup-helm@v1.1 - # NOTE@jsjoeio - # disabling this until we can audit the build process - # and the usefulness of this step - # See: https://github.com/coder/code-server/issues/4287 - # - name: Fetch dependencies from cache - # id: cache-yarn - # uses: actions/cache@v2 - # with: - # path: "**/node_modules" - # key: yarn-build-${{ hashFiles('**/yarn.lock') }} - # restore-keys: | - # yarn-build- + - name: Fetch dependencies from cache + id: cache-yarn + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: yarn-build-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn-build- - name: Install dependencies - # if: steps.cache-yarn.outputs.cache-hit != 'true' + if: steps.cache-yarn.outputs.cache-hit != 'true' run: yarn --frozen-lockfile - name: Run yarn fmt @@ -71,6 +70,9 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: true - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -79,7 +81,7 @@ jobs: - name: Fetch dependencies from cache id: cache-yarn - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: "**/node_modules" key: yarn-build-${{ hashFiles('**/yarn.lock') }} @@ -102,56 +104,53 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 with: fetch-depth: 0 + submodules: true + + - name: Install quilt + run: sudo apt update && sudo apt install quilt + + - name: Patch Code + run: quilt push -a - name: Install Node.js v14 uses: actions/setup-node@v3 with: node-version: "14" - # TODO@Teffen investigate why this omits code-oss-dev/node_modules - # - name: Fetch dependencies from cache - # id: cache-yarn - # uses: actions/cache@v2 - # with: - # path: | - # "**/node_modules" - # "**/vendor/modules" - # "**/vendor/modules/code-oss-dev/node_modules" - # key: yarn-build-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/vendor/yarn.lock') }} - # restore-keys: | - # yarn-build- + - name: Fetch dependencies from cache + id: cache-yarn + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: yarn-build-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn-build- - name: Install dependencies - # if: steps.cache-yarn.outputs.cache-hit != 'true' + if: steps.cache-yarn.outputs.cache-hit != 'true' run: yarn --frozen-lockfile - name: Build code-server run: yarn build - # Parse the hash of the latest commit inside vendor/modules/code-oss-dev - # use this to avoid rebuilding it if nothing changed - # How it works: the `git log` command fetches the hash of the last commit - # that changed a file inside `vendor/modules/code-oss-dev`. If a commit changes any file in there, - # the hash returned will change, and we rebuild vscode. If the hash did not change, - # (for example, a change to `src/` or `docs/`), we reuse the same build as last time. - # This saves a lot of time in CI, as compiling VSCode can take anywhere from 5-10 minutes. - - name: Get latest vendor/modules/code-oss-dev rev + # 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::$(jq -r '.devDependencies["code-oss-dev"]' vendor/package.json | sed -r 's|.*#(.*)$|\1|')" + run: echo "::set-output name=rev::$(git rev-parse HEAD:./lib/vscode)" - - name: Attempt to fetch vscode build from cache + # We need to rebuild when we have a new version of Code or when any of + # the patches changed. Use VSCODE_CACHE_VERSION to force a rebuild. + - name: Fetch prebuilt Code package from cache id: cache-vscode - uses: actions/cache@v2 + uses: actions/cache@v3 with: - path: | - vendor/modules/code-oss-dev/.build - vendor/modules/code-oss-dev/out-build - vendor/modules/code-oss-dev/out-vscode-reh-web - vendor/modules/code-oss-dev/out-vscode-reh-web-min - key: vscode-reh-build-${{ steps.vscode-rev.outputs.rev }} + path: lib/vscode-reh-web-* + key: vscode-reh-package-${{ secrets.VSCODE_CACHE_VERSION }}-${{ steps.vscode-rev.outputs.rev }}-${{ hashFiles('patches/*.diff') }} - name: Build vscode if: steps.cache-vscode.outputs.cache-hit != 'true' @@ -179,7 +178,7 @@ jobs: run: tar -czf package.tar.gz release - name: Upload npm package artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: npm-package path: ./package.tar.gz @@ -196,9 +195,13 @@ jobs: if: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - uses: actions/download-artifact@v3 + - name: Download artifact + uses: actions/download-artifact@v3 id: download with: name: "npm-package" @@ -214,6 +217,26 @@ jobs: # 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 }} + ``` + # TODO: cache building yarn --production # possibly 2m30s of savings(?) # this requires refactoring our release scripts @@ -225,7 +248,10 @@ jobs: container: "centos:7" steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -268,7 +294,7 @@ jobs: run: yarn package - name: Upload release artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: release-packages path: ./release-packages @@ -314,7 +340,10 @@ jobs: NODE_VERSION: v14.17.4 steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -352,7 +381,7 @@ jobs: run: yarn package ${NPM_CONFIG_ARCH} - name: Upload release artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: release-packages path: ./release-packages @@ -363,7 +392,10 @@ jobs: runs-on: macos-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -393,7 +425,7 @@ jobs: run: yarn package - name: Upload release artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: release-packages path: ./release-packages @@ -408,7 +440,11 @@ jobs: # since VS Code will load faster due to the bundling. CODE_SERVER_TEST_ENTRY: "./release-packages/code-server-linux-amd64" steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: true - name: Install Node.js v14 uses: actions/setup-node@v3 @@ -417,7 +453,7 @@ jobs: - name: Fetch dependencies from cache id: cache-yarn - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: "**/node_modules" key: yarn-build-${{ hashFiles('**/yarn.lock') }} @@ -450,7 +486,7 @@ jobs: - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: failed-test-videos path: ./test/test-results @@ -459,13 +495,18 @@ jobs: run: rm -rf ./release-packages ./test/test-results trivy-scan-repo: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results runs-on: ubuntu-20.04 steps: - - name: Checkout code + - name: Checkout repo uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Run Trivy vulnerability scanner in repo mode - #Commit SHA for v0.0.17 - uses: aquasecurity/trivy-action@296212627a1e693efa09c00adc3e03b2ba8edf18 + uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c with: scan-type: "fs" scan-ref: "." @@ -474,6 +515,7 @@ jobs: template: "@/contrib/sarif.tpl" output: "trivy-repo-results.sarif" severity: "HIGH,CRITICAL" + - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v1 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e4b02aaa0cdb..efe068fbdbd2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -17,8 +17,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} +permissions: + contents: read + jobs: analyze: + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/autobuild to send a status report name: Analyze runs-on: ubuntu-20.04 diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 7cc0c1e27e11..23e08c2454c2 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -35,6 +35,20 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Get version + id: version + run: echo "::set-output name=version::$(jq -r .version package.json)" + + - name: Download artifact + uses: dawidd6/action-download-artifact@v2 + id: download + with: + branch: v${{ steps.version.outputs.version }} + workflow: ci.yaml + workflow_conclusion: completed + name: "release-packages" + path: release-packages + - name: Run ./ci/steps/docker-buildx-push.sh run: ./ci/steps/docker-buildx-push.sh env: diff --git a/.github/workflows/docs-preview.yaml b/.github/workflows/docs-preview.yaml index df149117578d..7f89c9c70129 100644 --- a/.github/workflows/docs-preview.yaml +++ b/.github/workflows/docs-preview.yaml @@ -17,89 +17,28 @@ permissions: security-events: none statuses: none -# Cancel in-progress runs for pull requests when developers push -# additional changes, and serialize builds in branches. -# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - jobs: preview: name: Docs preview runs-on: ubuntu-20.04 - environment: CI - # 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 steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.9.1 - - - name: Checkout m - uses: actions/checkout@v3 - with: - repository: coder/m - ref: refs/heads/master - ssh-key: ${{ secrets.READONLY_M_DEPLOY_KEY }} - submodules: true - fetch-depth: 0 - - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: 14 - - - name: Cache Node Modules - uses: actions/cache@v2 - with: - path: "/node_modules" - key: node-${{ hashFiles('yarn.lock') }} + - uses: actions/checkout@v3 - - name: Create Deployment - id: deployment - run: ./ci/scripts/github_deployment.sh create - env: - GITHUB_TOKEN: ${{ github.token }} - DEPLOY_ENVIRONMENT: codercom-preview-docs - - - name: Deploy Preview to Vercel - id: preview - run: ./ci/scripts/deploy_vercel.sh - env: - VERCEL_ORG_ID: team_tGkWfhEGGelkkqUUm9nXq17r - VERCEL_PROJECT_ID: QmZRucMRh3GFk1817ZgXjRVuw5fhTspHPHKct3JNQDEPGd - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - CODE_SERVER_DOCS_MAIN_BRANCH: ${{ github.event.pull_request.head.sha }} - - - name: Install node_modules - run: yarn install - - - name: Check docs - run: yarn ts-node ./product/coder.com/site/scripts/checkDocs.ts - env: - BASE_URL: ${{ steps.preview.outputs.url }} - - - name: Update Deployment - # If we don't specify always, it won't run this check if failed. - # This means the deployment would be stuck pending. - if: always() - run: ./ci/scripts/github_deployment.sh update - env: - GITHUB_DEPLOYMENT: ${{ steps.deployment.outputs.id }} - GITHUB_TOKEN: ${{ github.token }} - DEPLOY_STATUS: ${{ steps.preview.outcome }} - DEPLOY_URL: ${{ steps.preview.outputs.url }} + - 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 - if: always() + # 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: | - ✨ Coder.com for PR #${{ github.event.number }} deployed! It will be updated on every commit. - - * _Host_: ${{ steps.preview.outputs.url }}/docs/code-server - * _Last deploy status_: ${{ steps.preview.outcome }} + ✨ 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 }} diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml index 5002c3e814a5..bf0f4eaa0881 100644 --- a/.github/workflows/installer.yml +++ b/.github/workflows/installer.yml @@ -19,6 +19,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} +permissions: + contents: read + jobs: ubuntu: name: Test installer on Ubuntu diff --git a/.github/workflows/npm-brew.yaml b/.github/workflows/npm-brew.yaml index 8c8b84fe2c38..7ea182d8c0f2 100644 --- a/.github/workflows/npm-brew.yaml +++ b/.github/workflows/npm-brew.yaml @@ -23,9 +23,17 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - name: Get version + id: version + run: echo "::set-output name=version::$(jq -r .version package.json)" + + - name: Download artifact + uses: dawidd6/action-download-artifact@v2 id: download with: + branch: v${{ steps.version.outputs.version }} + workflow: ci.yaml + workflow_conclusion: completed name: "npm-package" path: release-npm-package @@ -37,10 +45,8 @@ jobs: NPM_ENVIRONMENT: "production" homebrew: - # The newest version of code-server needs to be available on npm when this runs - # otherwise, it will 404 and won't open a PR to bump version on homebrew/homebrew-core needs: npm - runs-on: macos-latest + runs-on: ubuntu-latest steps: # Ensure things are up to date # Suggested by homebrew maintainers @@ -49,11 +55,14 @@ jobs: id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@master - - uses: actions/checkout@v3 + - name: Checkout code-server + uses: actions/checkout@v3 + - name: Configure git run: | - git config user.name github-actions - git config user.email github-actions@github.com + git config user.name cdrci + git config user.email opensource@coder.com + - name: Bump code-server homebrew version env: HOMEBREW_GITHUB_API_TOKEN: ${{secrets.HOMEBREW_GITHUB_API_TOKEN}} diff --git a/.github/workflows/trivy-docker.yaml b/.github/workflows/trivy-docker.yaml new file mode 100644 index 000000000000..840b85e1f19e --- /dev/null +++ b/.github/workflows/trivy-docker.yaml @@ -0,0 +1,65 @@ +name: Trivy Nightly Docker Scan + +on: + # Run scans if the workflow is modified, in order to test the + # workflow itself. This results in some spurious notifications, + # but seems okay for testing. + pull_request: + branches: + - main + paths: + - .github/workflows/trivy-docker.yaml + + # Run scans against master whenever changes are merged. + push: + branches: + - main + paths: + - .github/workflows/trivy-docker.yaml + + schedule: + # Run at 10:15 am UTC (3:15am PT/5:15am CT) + # Run at 0 minutes 0 hours of every day. + - cron: "15 10 * * *" + + workflow_dispatch: + +permissions: + actions: none + checks: none + contents: read + deployments: none + issues: none + packages: none + pull-requests: none + repository-projects: none + security-events: write + statuses: none + +# Cancel in-progress runs for pull requests when developers push +# additional changes, and serialize builds in branches. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + +jobs: + trivy-scan-image: + runs-on: ubuntu-20.04 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run Trivy vulnerability scanner in image mode + uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c + with: + image-ref: "docker.io/codercom/code-server:latest" + ignore-unfixed: true + format: "sarif" + output: "trivy-image-results.sarif" + severity: "HIGH,CRITICAL" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: "trivy-image-results.sarif" diff --git a/.gitignore b/.gitignore index 3cc6e31d7af3..d453ee2e969f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,18 @@ release-packages/ release-gcp/ release-images/ node_modules -vendor/modules -node-* /plugins /lib/coder-cloud-agent .home coverage **/.DS_Store + +# Code packages itself here. +/lib/vscode-reh-web-* + # Failed e2e test videos are saved here test/test-results + +# Quilt's internal data. +/.pc +/patches/*.diff~ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000000..9854a1b1dd30 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/vscode"] + path = lib/vscode + url = https://github.com/microsoft/vscode diff --git a/.prettierrc.yaml b/.prettierrc.yaml index bf4b4a7d239b..a58621d69ead 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -7,7 +7,7 @@ useTabs: false overrides: # Attempt to keep VScode's existing code style intact. - - files: "vendor/modules/code-oss-dev/**/*.ts" + - files: "lib/vscode/**/*.ts" options: # No limit defined upstream. printWidth: 10000 diff --git a/.tours/contributing.tour b/.tours/contributing.tour index 95799b6abb7e..970543343632 100644 --- a/.tours/contributing.tour +++ b/.tours/contributing.tour @@ -143,7 +143,7 @@ "description": "Static images and the manifest live here in `src/browser/media` (see the explorer)." }, { - "directory": "vendor/modules/code-oss-dev", + "directory": "lib/vscode", "line": 1, "description": "code-server makes use of VS Code's frontend web/remote support. Most of the modifications implement the remote server since that portion of the code is closed source and not released with VS Code.\n\nWe also have a few bug fixes and have added some features (like client-side extensions). See [https://github.com/coder/code-server/blob/master/docs/CONTRIBUTING.md#modifications-to-vs-code](https://github.com/coder/code-server/blob/master/docs/CONTRIBUTING.md#modifications-to-vs-code) for a list.\n\nWe make an effort to keep the modifications as few as possible." } diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4d25b71382..b994c6d7f264 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [9.99.999] - 9090-09-09 -VS Code v99.99.999 +Code v99.99.999 -### Changed ### Added +### Changed ### Deprecated ### Removed ### Fixed @@ -20,17 +20,103 @@ VS Code v99.99.999 --> -## [Unreleased](https://github.com/coder/code-server/releases) +## [4.3.0](https://github.com/coder/code-server/releases/tag/v4.3.0) - 2022-04-14 -VS Code v0.00.0 +Code v1.65.2 ### Changed -- Add here +- Excluded .deb files from release Docker image which drops the compressed and + uncompressed size by 58% and 34%. +- Upgraded to Code 1.65.2. + +### Added + +- Added a new CLI flag called `--disable-file-downloads` which allows you to + disable the "Download..." option that shows in the UI when right-clicking on a + file. This can also set by running `CS_DISABLE_FILE_DOWNLOADS=1`. +- Aligned the dependencies for binary and npm release artifacts. + +### Fixed + +- Fixed the code-server version from not displaying in the Help > About dialog. +- Fixed issues with the TypeScript and JavaScript Language Features Extension + failing to activate. +- Fixed missing files in ipynb extension. +- Fixed the homebrew release workflow. +- Fixed the Docker release workflow from not always publishing version tags. + +## [4.2.0](https://github.com/coder/code-server/releases/tag/v4.2.0) - 2022-03-22 + +Code v1.64.2 + +### Added + +- Added tests for `handleArgsSocketCatchError`, `setDefaults` and + `optionDescriptions`. + +### Changed + +- We switched from using the fork `coder/vscode` to a submodule of + `microsoft/vscode` + patches managed by `quilt` for how Code sits inside the + code-server codebase. +- Upgraded to Code 1.64.2. + +### Fixed + +- Update popup notification through `--disable-update-check` is now fixed. +- Fixed PWA icons not loading on iPad +- Fixed the homebrew release process. Our `cdrci` bot should now automatically + update the version as part of the release pipeline. +- Fixed titleBar color setting being ignored in PWA. + +### Security + +- Updated to `minimist-list`. +- Updated `cloud-agent` to `v0.2.4` which uses `nhooyr.io/webscoket` `v1.8.7`. + +## [4.1.0](https://github.com/coder/code-server/releases/tag/v4.1.0) - 2022-03-03 + +Code v1.63.0 + +### Added + +- Support for injecting GitHub token into Code so extensions can make use of it. + This can be done with the `GITHUB_TOKEN` environment variable or `github-auth` + in the config file. +- New flag `--socket-mode` allows setting the mode (file permissions) of the + socket created when using `--socket`. +- The version of Code bundled with code-server now appears when using the + `--version` flag. For example: `4.0.2 5cdfe74686aa73e023f8354a9a6014eb30caa7dd with Code 1.63.0`. + If you have been parsing this flag for the version you might want to use + `--version --json` instead as doing that will be more stable. + +### Changed + +- The workspace or folder passed on the CLI will now use the same redirect + method that the last opened workspace or folder uses. This means if you use + something like `code-server /path/to/dir` you will now get a query parameter + added (like so: `my-domain.tld?folder=/path/to/dir`), making it easier to edit + by hand and making it consistent with the last opened and menu open behaviors. +- The folder/workspace query parameter no longer has encoded slashes, making + them more readable and editable by hand. This was only affecting the last + opened behavior, not opens from the menu. + +### Fixed + +- Fix web sockets not connecting when using `--cert`. +- Prevent workspace state collisions when opening a workspace that shares the + same file path with another workspace on a different machine that shares the + same domain. This was causing files opened in one workspace to be "re-"opened + in the other workspace when the other workspace is opened. +- Pin the Express version which should make installing from npm work again. +- Propagate signals to code-server in the Docker image which means it should + stop more quickly and gracefully. +- Fix missing argon binaries in the standalone releases on arm machines. ## [4.0.2](https://github.com/coder/code-server/releases/tag/v4.0.2) - 2022-01-27 -VS Code v1.63.0 +Code v1.63.0 ### Fixed @@ -41,7 +127,7 @@ VS Code v1.63.0 ## [4.0.1](https://github.com/coder/code-server/releases/tag/v4.0.1) - 2022-01-04 -VS Code v1.63.0 +Code v1.63.0 code-server has been rebased on upstream's newly open-sourced server implementation (#4414). @@ -57,7 +143,7 @@ implementation (#4414). settings file (we rely on the already-existing query object instead). - The marketplace override environment variables `SERVICE_URL` and `ITEM_URL` have been replaced with a single `EXTENSIONS_GALLERY` variable that - corresponds to `extensionsGallery` in VS Code's `product.json`. + corresponds to `extensionsGallery` in Code's `product.json`. ### Added @@ -79,11 +165,11 @@ implementation (#4414). ## [3.12.0](https://github.com/coder/code-server/releases/tag/v3.12.0) - 2021-09-15 -VS Code v1.60.0 +Code v1.60.0 ### Changed -- Upgrade VS Code to 1.60.0. +- Upgrade Code to 1.60.0. ### Fixed @@ -99,7 +185,7 @@ Undocumented (see releases page). ## [3.10.2](https://github.com/coder/code-server/releases/tag/v3.10.2) - 2021-05-21 -VS Code v1.56.1 +Code v1.56.1 ### Added @@ -115,7 +201,7 @@ VS Code v1.56.1 ## [3.10.1](https://github.com/coder/code-server/releases/tag/v3.10.1) - 2021-05-17 -VS Code v1.56.1 +Code v1.56.1 ### Fixed @@ -129,13 +215,13 @@ VS Code v1.56.1 ## [3.10.0](https://github.com/coder/code-server/releases/tag/v3.10.0) - 2021-05-10 -VS Code v1.56.0 +Code v1.56.0 ### Changed -- Update to VS Code 1.56.0 (#3269). +- Update to Code 1.56.0 (#3269). - Minor connections refactor (#3178). Improves connection stability. -- Use ptyHostService (#3308). This brings us closer to upstream VS Code. +- Use ptyHostService (#3308). This brings us closer to upstream Code. ### Added diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/ci/build/build-packages.sh b/ci/build/build-packages.sh index 8da6aec38332..6c85ccd33cde 100755 --- a/ci/build/build-packages.sh +++ b/ci/build/build-packages.sh @@ -50,6 +50,11 @@ release_nfpm() { export NFPM_ARCH + # Code deletes some files from the extension node_modules directory which + # leaves broken symlinks in the corresponding .bin directory. nfpm will fail + # on these broken symlinks so clean them up. + rm -fr "./release-standalone/lib/vscode/extensions/node_modules/.bin" + PKG_FORMAT="deb" NFPM_ARCH="$(get_nfpm_arch $PKG_FORMAT "$ARCH")" nfpm_config="$(envsubst < ./ci/build/nfpm.yaml)" diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index 7f152e1701f9..07a879c28441 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -15,8 +15,8 @@ main() { source ./ci/lib.sh - VSCODE_SRC_PATH="vendor/modules/code-oss-dev" - VSCODE_OUT_PATH="$RELEASE_PATH/vendor/modules/code-oss-dev" + VSCODE_SRC_PATH="lib/vscode" + VSCODE_OUT_PATH="$RELEASE_PATH/lib/vscode" mkdir -p "$RELEASE_PATH" @@ -24,8 +24,8 @@ main() { bundle_vscode rsync ./docs/README.md "$RELEASE_PATH" - rsync LICENSE.txt "$RELEASE_PATH" - rsync ./vendor/modules/code-oss-dev/ThirdPartyNotices.txt "$RELEASE_PATH" + rsync LICENSE "$RELEASE_PATH" + rsync ./lib/vscode/ThirdPartyNotices.txt "$RELEASE_PATH" } bundle_code_server() { @@ -55,6 +55,17 @@ bundle_code_server() { EOF ) > "$RELEASE_PATH/package.json" rsync yarn.lock "$RELEASE_PATH" + + # To ensure deterministic dependency versions (even when code-server is installed with NPM), we seed + # an npm-shrinkwrap file from our yarn lockfile and the current node-modules installed. + synp --source-file yarn.lock + npm shrinkwrap + # HACK@edvincent: The shrinkwrap file will contain the devDependencies, which by default + # are installed if present in a lockfile. To avoid every user having to specify --production + # to skip them, we carefully remove them from the shrinkwrap file. + json -f npm-shrinkwrap.json -I -e "Object.keys(this.dependencies).forEach(dependency => { if (this.dependencies[dependency].dev) { delete this.dependencies[dependency] } } )" + mv npm-shrinkwrap.json "$RELEASE_PATH" + rsync ci/build/npm-postinstall.sh "$RELEASE_PATH/postinstall.sh" if [ "$KEEP_MODULES" = 1 ]; then @@ -66,31 +77,29 @@ EOF bundle_vscode() { mkdir -p "$VSCODE_OUT_PATH" - rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH" - rsync "$VSCODE_SRC_PATH/out-vscode-reh-web${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out" - - rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions" - if [ "$KEEP_MODULES" = 0 ]; then - rm -Rf "$VSCODE_OUT_PATH/extensions/node_modules" - else - rsync "$VSCODE_SRC_PATH/node_modules/" "$VSCODE_OUT_PATH/node_modules" + + local rsync_opts=() + if [[ ${DEBUG-} = 1 ]]; then + rsync_opts+=(-vh) fi - rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions" - rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions" - rsync "$VSCODE_SRC_PATH/extensions/postinstall.js" "$VSCODE_OUT_PATH/extensions" - - mkdir -p "$VSCODE_OUT_PATH/resources/" - rsync "$VSCODE_SRC_PATH/resources/" "$VSCODE_OUT_PATH/resources/" - - # TODO: We should look into using VS Code's packaging task (see - # gulpfile.reh.js). For now copy this directory into the right spot (for some - # reason VS Code uses a different path in production). - mkdir -p "$VSCODE_OUT_PATH/bin/helpers" - rsync "$VSCODE_SRC_PATH/resources/server/bin/helpers/" "$VSCODE_OUT_PATH/bin/helpers" - chmod +x "$VSCODE_OUT_PATH/bin/helpers/browser.sh" - - # Add the commit and date and enable telemetry. This just makes telemetry - # available; telemetry can still be disabled by flag or setting. + + # Some extensions have a .gitignore which excludes their built source from the + # npm package so exclude any .gitignore files. + rsync_opts+=(--exclude .gitignore) + + # Exclude Node as we will add it ourselves for the standalone and will not + # need it for the npm package. + rsync_opts+=(--exclude /node) + + # Exclude Node modules. + if [[ $KEEP_MODULES = 0 ]]; then + rsync_opts+=(--exclude node_modules) + fi + + rsync "${rsync_opts[@]}" ./lib/vscode-reh-web-*/ "$VSCODE_OUT_PATH" + + # Add the commit, date, our name, links, and enable telemetry. This just makes + # telemetry available; telemetry can still be disabled by flag or setting. jq --slurp '.[0] * .[1]' "$VSCODE_SRC_PATH/product.json" <( cat << EOF { @@ -98,15 +107,46 @@ bundle_vscode() { "commit": "$(cd "$VSCODE_SRC_PATH" && git rev-parse HEAD)", "quality": "stable", "date": $(jq -n 'now | todate'), - "codeServerVersion": "$VERSION" + "codeServerVersion": "$VERSION", + "nameShort": "code-server", + "nameLong": "code-server", + "applicationName": "code-server", + "dataFolderName": ".code-server", + "win32MutexName": "codeserver", + "licenseUrl": "https://github.com/coder/code-server/blob/main/LICENSE", + "win32DirName": "code-server", + "win32NameVersion": "code-server", + "win32AppUserModelId": "coder.code-server", + "win32ShellNameShort": "c&ode-server", + "darwinBundleIdentifier": "com.coder.code.server", + "linuxIconName": "com.coder.code.server", + "reportIssueUrl": "https://github.com/coder/code-server/issues/new", + "documentationUrl": "https://go.microsoft.com/fwlink/?LinkID=533484#vscode", + "keyboardShortcutsUrlMac": "https://go.microsoft.com/fwlink/?linkid=832143", + "keyboardShortcutsUrlLinux": "https://go.microsoft.com/fwlink/?linkid=832144", + "keyboardShortcutsUrlWin": "https://go.microsoft.com/fwlink/?linkid=832145", + "introductoryVideosUrl": "https://go.microsoft.com/fwlink/?linkid=832146", + "tipsAndTricksUrl": "https://go.microsoft.com/fwlink/?linkid=852118", + "newsletterSignupUrl": "https://www.research.net/r/vsc-newsletter", + "linkProtectionTrustedDomains": [ + "https://open-vsx.org" + ] } EOF ) > "$VSCODE_OUT_PATH/product.json" - # We remove the scripts field so that later on we can run - # yarn to fetch node_modules if necessary without build scripts running. - # We cannot use --no-scripts because we still want dependent package scripts to run. - jq 'del(.scripts)' < "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json" + # Use the package.json for the web/remote server. It does not have the right + # version though so pull that from the main package.json. + jq --slurp '.[0] * {version: .[1].version}' \ + "$VSCODE_SRC_PATH/remote/package.json" \ + "$VSCODE_SRC_PATH/package.json" > "$VSCODE_OUT_PATH/package.json" + + rsync "$VSCODE_SRC_PATH/remote/yarn.lock" "$VSCODE_OUT_PATH/yarn.lock" + + # Include global extension dependencies as well. + rsync "$VSCODE_SRC_PATH/extensions/package.json" "$VSCODE_OUT_PATH/extensions/package.json" + rsync "$VSCODE_SRC_PATH/extensions/yarn.lock" "$VSCODE_OUT_PATH/extensions/yarn.lock" + rsync "$VSCODE_SRC_PATH/extensions/postinstall.js" "$VSCODE_OUT_PATH/extensions/postinstall.js" pushd "$VSCODE_OUT_PATH" symlink_asar diff --git a/ci/build/build-standalone-release.sh b/ci/build/build-standalone-release.sh index 481110b47b39..2bc553a619ce 100755 --- a/ci/build/build-standalone-release.sh +++ b/ci/build/build-standalone-release.sh @@ -29,12 +29,6 @@ main() { cd "$RELEASE_PATH" yarn --production --frozen-lockfile - - # HACK: the version of Typescript vscode 1.57 uses in extensions/ - # leaves a few stray symlinks. Clean them up so nfpm does not fail. - # Remove this line when its no longer needed. - - rm -fr "$RELEASE_PATH/vendor/modules/code-oss-dev/extensions/node_modules/.bin" } main "$@" diff --git a/ci/build/build-vscode.sh b/ci/build/build-vscode.sh index be996fceef56..bb3225a2b517 100755 --- a/ci/build/build-vscode.sh +++ b/ci/build/build-vscode.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# Builds vscode into vendor/modules/code-oss-dev/out-vscode. +# Builds vscode into lib/vscode/out-vscode. # MINIFY controls whether a minified version of vscode is built. MINIFY=${MINIFY-true} @@ -9,7 +9,7 @@ MINIFY=${MINIFY-true} main() { cd "$(dirname "${0}")/../.." - cd vendor/modules/code-oss-dev + cd lib/vscode # Any platform works since we have our own packaging step (for now). yarn gulp "vscode-reh-web-linux-x64${MINIFY:+-min}" diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index 43c3262ec341..fd437a65228d 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -90,22 +90,14 @@ symlink_asar() { } vscode_yarn() { - echo 'Installing vendor dependencies...' - cd vendor/modules/code-oss-dev + echo 'Installing Code dependencies...' + cd lib/vscode yarn --production --frozen-lockfile symlink_asar cd extensions yarn --production --frozen-lockfile - - for ext in */; do - ext="${ext%/}" - echo "extensions/$ext: installing dependencies" - cd "$ext" - yarn --production --frozen-lockfile - cd "$OLDPWD" - done } main "$@" diff --git a/ci/build/release-github-assets.sh b/ci/build/release-github-assets.sh index 29f27566816a..6395adcb7eec 100755 --- a/ci/build/release-github-assets.sh +++ b/ci/build/release-github-assets.sh @@ -9,6 +9,15 @@ set -euo pipefail main() { cd "$(dirname "$0")/../.." source ./ci/lib.sh + source ./ci/steps/steps-lib.sh + + # NOTE@jsjoeio - only needed if we use the download_artifact + # because we talk to the GitHub API. + # Needed to use GitHub API + if ! is_env_var_set "GITHUB_TOKEN"; then + echo "GITHUB_TOKEN is not set. Cannot download npm release-packages without GitHub credentials." + exit 1 + fi download_artifact release-packages ./release-packages local assets=(./release-packages/code-server*"$VERSION"*{.tar.gz,.deb,.rpm}) diff --git a/ci/build/release-prep.sh b/ci/build/release-prep.sh index a7f9967a5474..173dd2db1fc8 100755 --- a/ci/build/release-prep.sh +++ b/ci/build/release-prep.sh @@ -81,7 +81,7 @@ main() { read -r -p "What version of code-server do you want to update to?"$'\n' CODE_SERVER_VERSION_TO_UPDATE echo -e "Great! We'll prep a PR for updating to $CODE_SERVER_VERSION_TO_UPDATE\n" - $CMD rg -g '!yarn.lock' -g '!*.svg' -g '!CHANGELOG.md' --files-with-matches --fixed-strings "${CODE_SERVER_CURRENT_VERSION}" | $CMD xargs sd "$CODE_SERVER_CURRENT_VERSION" "$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" diff --git a/ci/dev/fmt.sh b/ci/dev/fmt.sh index d5d7ffeed9c7..447186212d2f 100755 --- a/ci/dev/fmt.sh +++ b/ci/dev/fmt.sh @@ -19,7 +19,7 @@ main() { "*.sh" ) prettier --write --loglevel=warn $( - git ls-files "${prettierExts[@]}" | grep -v "lib/vscode" | grep -v "vendor/modules/code-oss-dev" | grep -v 'helm-chart' + git ls-files "${prettierExts[@]}" | grep -v "lib/vscode" | grep -v 'helm-chart' ) doctoc --title '# FAQ' docs/FAQ.md > /dev/null diff --git a/ci/dev/lint.sh b/ci/dev/lint.sh index 34b2e8f78fd9..58a999c4f2f0 100755 --- a/ci/dev/lint.sh +++ b/ci/dev/lint.sh @@ -4,10 +4,10 @@ set -euo pipefail main() { cd "$(dirname "$0")/../.." - eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js" | grep -v "vendor/modules/code-oss-dev" | grep -v "lib/vscode") - stylelint $(git ls-files "*.css" | grep -v "vendor/modules/code-oss-dev" | grep -v "lib/vscode") + eslint --max-warnings=0 --fix $(git ls-files "*.ts" "*.tsx" "*.js" | grep -v "lib/vscode") + stylelint $(git ls-files "*.css" | grep -v "lib/vscode") tsc --noEmit --skipLibCheck - shellcheck -e SC2046,SC2164,SC2154,SC1091,SC1090,SC2002 $(git ls-files "*.sh" | grep -v "vendor/modules/code-oss-dev" | grep -v "lib/vscode") + shellcheck -e SC2046,SC2164,SC2154,SC1091,SC1090,SC2002 $(git ls-files "*.sh" | grep -v "lib/vscode") if command -v helm && helm kubeval --help > /dev/null; then helm kubeval ci/helm-chart fi diff --git a/ci/dev/postinstall.sh b/ci/dev/postinstall.sh index 78f26cc631bd..170cdb46fd66 100755 --- a/ci/dev/postinstall.sh +++ b/ci/dev/postinstall.sh @@ -1,50 +1,32 @@ #!/usr/bin/env bash set -euo pipefail -main() { - cd "$(dirname "$0")/../.." - source ./ci/lib.sh - - pushd test - echo "Installing dependencies for $PWD" - yarn install - popd - +# Install dependencies in $1. +install-deps() { local args=(install) if [[ ${CI-} ]]; then args+=(--frozen-lockfile) fi - - pushd test - echo "Installing dependencies for $PWD" - yarn "${args[@]}" - popd - - pushd test/e2e/extensions/test-extension + # If there is no package.json then yarn will look upward and end up installing + # from the root resulting in an infinite loop (this can happen if you have not + # checked out the submodule yet for example). + if [[ ! -f "$1/package.json" ]]; then + echo "$1/package.json is missing; did you run git submodule update --init?" + exit 1 + fi + pushd "$1" echo "Installing dependencies for $PWD" yarn "${args[@]}" popd +} - pushd vendor - echo "Installing dependencies for $PWD" - - # We install in 'modules' instead of 'node_modules' because VS Code's - # extensions use a webpack config which cannot differentiate between its own - # node_modules and itself being in a directory with the same name. - args+=(--modules-folder modules) - - # We ignore scripts because NPM/Yarn's default behavior is to assume that - # devDependencies are not needed, and that even git repo based packages are - # assumed to be compiled. Because the default behavior for VS Code's - # `postinstall` assumes we're also compiled, this needs to be ignored. - args+=(--ignore-scripts) - - yarn "${args[@]}" - - # Finally, run the vendor `postinstall` - yarn run postinstall +main() { + cd "$(dirname "$0")/../.." + source ./ci/lib.sh - popd + install-deps test + install-deps test/e2e/extensions/test-extension + install-deps lib/vscode } main "$@" diff --git a/ci/dev/test-e2e.sh b/ci/dev/test-e2e.sh index cf3e53d118e9..00724ac2b5f2 100755 --- a/ci/dev/test-e2e.sh +++ b/ci/dev/test-e2e.sh @@ -37,7 +37,7 @@ main() { exit 1 fi - if [[ ! -d $dir/vendor/modules/code-oss-dev/out ]]; then + if [[ ! -d $dir/lib/vscode/out ]]; then echo >&2 "No VS Code build detected" help exit 1 diff --git a/ci/dev/test-unit.sh b/ci/dev/test-unit.sh index 3578d87e647d..f2dbf41b5159 100755 --- a/ci/dev/test-unit.sh +++ b/ci/dev/test-unit.sh @@ -14,11 +14,17 @@ main() { # 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. - local vscode="vendor/modules/code-oss-dev" - local link="$vscode/out" - local target="out-build" - if [[ ! -e $link ]] && [[ -d $vscode/$target ]]; then - ln -s "$target" "$link" + 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 diff --git a/ci/dev/watch.ts b/ci/dev/watch.ts index 55a5b14d1f4c..66480b29e7e5 100644 --- a/ci/dev/watch.ts +++ b/ci/dev/watch.ts @@ -14,7 +14,7 @@ class Watcher { private rootPath = path.resolve(process.cwd()) private readonly paths = { /** Path to uncompiled VS Code source. */ - vscodeDir: path.join(this.rootPath, "vendor", "modules", "code-oss-dev"), + vscodeDir: path.join(this.rootPath, "lib/vscode"), pluginDir: process.env.PLUGIN_DIR, } diff --git a/ci/helm-chart/Chart.yaml b/ci/helm-chart/Chart.yaml index c68fb2b20c4e..3eeb0584b5f2 100644 --- a/ci/helm-chart/Chart.yaml +++ b/ci/helm-chart/Chart.yaml @@ -15,9 +15,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 2.1.0 +version: 2.4.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.0.2 +appVersion: 4.3.0 diff --git a/ci/helm-chart/values.yaml b/ci/helm-chart/values.yaml index f37771999fe2..6ea263c7f3c0 100644 --- a/ci/helm-chart/values.yaml +++ b/ci/helm-chart/values.yaml @@ -6,7 +6,7 @@ replicaCount: 1 image: repository: codercom/code-server - tag: '4.0.2' + tag: '4.3.0' pullPolicy: Always # Specifies one or more secrets to be used when pulling images from a @@ -70,6 +70,8 @@ extraArgs: [] extraVars: [] # - name: DISABLE_TELEMETRY # value: true +# - name: DOCKER_HOST +# value: "tcp://localhost:2375" ## ## Init containers parameters: @@ -126,6 +128,7 @@ persistence: ## Enable an Specify container in extraContainers. ## This is meant to allow adding code-server dependencies, like docker-dind. extraContainers: | +# If docker-dind is used, DOCKER_HOST env is mandatory to set in "extraVars" #- name: docker-dind # image: docker:19.03-dind # imagePullPolicy: IfNotPresent diff --git a/ci/lib.sh b/ci/lib.sh index 0e357986153f..c7b5a420aca6 100755 --- a/ci/lib.sh +++ b/ci/lib.sh @@ -14,7 +14,7 @@ pkg_json_version() { } vscode_version() { - jq -r .version vendor/modules/code-oss-dev/package.json + jq -r .version lib/vscode/package.json } os() { diff --git a/ci/release-image/Dockerfile b/ci/release-image/Dockerfile index cd82972aad4a..f665a1588d74 100644 --- a/ci/release-image/Dockerfile +++ b/ci/release-image/Dockerfile @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:experimental + +FROM scratch AS packages +COPY release-packages/code-server*.deb /tmp/ + FROM debian:11 RUN apt-get update \ @@ -34,9 +39,8 @@ RUN ARCH="$(dpkg --print-architecture)" && \ mkdir -p /etc/fixuid && \ printf "user: coder\ngroup: coder\n" > /etc/fixuid/config.yml -COPY release-packages/code-server*.deb /tmp/ COPY ci/release-image/entrypoint.sh /usr/bin/entrypoint.sh -RUN dpkg -i /tmp/code-server*$(dpkg --print-architecture).deb && rm /tmp/code-server*.deb +RUN --mount=from=packages,src=/tmp,dst=/tmp/packages dpkg -i /tmp/packages/code-server*$(dpkg --print-architecture).deb EXPOSE 8080 # This way, if someone sets $DOCKER_USER, docker-exec will still work as diff --git a/ci/steps/brew-bump.sh b/ci/steps/brew-bump.sh index 7df17da92431..c1828decdb2d 100755 --- a/ci/steps/brew-bump.sh +++ b/ci/steps/brew-bump.sh @@ -2,7 +2,6 @@ set -euo pipefail main() { - cd "$(dirname "$0")/../.." # Only sourcing this so we get access to $VERSION source ./ci/lib.sh source ./ci/steps/steps-lib.sh @@ -21,94 +20,18 @@ main() { exit 1 fi - # NOTE: we need to make sure coderci/homebrew-core - # is up-to-date - # otherwise, brew bump-formula-pr will use an - # outdated base - echo "Cloning coderci/homebrew-core" - git clone https://github.com/coderci/homebrew-core.git - - # Make sure the git clone step is successful - if directory_exists "homebrew-core"; then - echo "git clone failed. Cannot find homebrew-core directory." - ls -la - exit 1 - fi - - echo "Changing into homebrew-core directory" - pushd homebrew-core && pwd - - echo "Adding Homebrew/homebrew-core" - git remote add upstream https://github.com/Homebrew/homebrew-core.git - - # Make sure the git remote step is successful - if ! git config remote.upstream.url > /dev/null; then - echo "git remote add upstream failed." - echo "Could not find upstream in list of remotes." - git remote -v - exit 1 - fi - - # TODO@jsjoeio - can I somehow check that this succeeded? - echo "Fetching upstream Homebrew/hombrew-core commits" - git fetch upstream - - # TODO@jsjoeio - can I somehow check that this succeeded? - echo "Merging in latest Homebrew/homebrew-core changes" - git merge upstream/master - - echo "Pushing changes to coderci/homebrew-core fork on GitHub" - - # GIT_ASKPASS lets us use the password when pushing without revealing it in the process list - # See: https://serverfault.com/a/912788 - PATH_TO_GIT_ASKPASS="$HOME/git-askpass.sh" - # Source: https://serverfault.com/a/912788 - # shellcheck disable=SC2016,SC2028 - echo 'echo $HOMEBREW_GITHUB_API_TOKEN' > "$PATH_TO_ASKPASS" - - # Make sure the git-askpass.sh file creation is successful - if file_exists "$PATH_TO_GIT_ASKPASS"; then - echo "git-askpass.sh not found in $HOME." - ls -la "$HOME" - exit 1 - fi - - # Ensure it's executable since we just created it - chmod +x "$PATH_TO_GIT_ASKPASS" - - # Make sure the git-askpass.sh file is executable - if is_executable "$PATH_TO_GIT_ASKPASS"; then - echo "$PATH_TO_GIT_ASKPASS is not executable." - ls -la "$PATH_TO_GIT_ASKPASS" - exit 1 - fi - - # Export the variables so git sees them - export HOMEBREW_GITHUB_API_TOKEN="$HOMEBREW_GITHUB_API_TOKEN" - export GIT_ASKPASS="$PATH_TO_ASKPASS" - git push https://coder-oss@github.com/coder-oss/homebrew-core.git --all - # Find the docs for bump-formula-pr here # https://github.com/Homebrew/brew/blob/master/Library/Homebrew/dev-cmd/bump-formula-pr.rb#L18 local output if ! output=$(brew bump-formula-pr --version="${VERSION}" code-server --no-browse --no-audit 2>&1); then if [[ $output == *"Duplicate PRs should not be opened"* ]]; then echo "$VERSION is already submitted" + exit 0 else echo "$output" exit 1 fi fi - - # Clean up and remove homebrew-core - popd - rm -rf homebrew-core - - # Make sure homebrew-core is removed - if directory_exists "homebrew-core"; then - echo "rm -rf homebrew-core failed." - ls -la - fi } main "$@" diff --git a/ci/steps/docker-buildx-push.sh b/ci/steps/docker-buildx-push.sh index c3bb0579635a..ebe86047c0bd 100755 --- a/ci/steps/docker-buildx-push.sh +++ b/ci/steps/docker-buildx-push.sh @@ -3,13 +3,13 @@ set -euo pipefail main() { cd "$(dirname "$0")/../.." - - # ci/lib.sh sets VERSION and provides download_artifact here + # 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 - # Download the release-packages artifact - download_artifact release-packages ./release-packages - + # NOTE@jsjoeio - this script assumes that you've downloaded + # the release-packages artifact to ./release-packages before + # running this docker buildx step docker buildx bake -f ci/release-image/docker-bake.hcl --push } diff --git a/ci/steps/publish-npm.sh b/ci/steps/publish-npm.sh index 7ba3a0f97d2f..847c46ab5a20 100755 --- a/ci/steps/publish-npm.sh +++ b/ci/steps/publish-npm.sh @@ -13,14 +13,6 @@ main() { exit 1 fi - # NOTE@jsjoeio - only needed if we use the download_artifact - # because we talk to the GitHub API. - # Needed to use GitHub API - if ! is_env_var_set "GITHUB_TOKEN"; then - echo "GITHUB_TOKEN is not set. Cannot download npm release artifact without GitHub credentials." - exit 1 - fi - ## Publishing Information # All the variables below are used to determine how we should publish # the npm package. We also use this information for bumping the version. @@ -51,6 +43,13 @@ main() { exit 1 fi + # Check that we're using at least v7 of npm CLI + if ! command -v jq &> /dev/null; then + echo "Couldn't find jq" + echo "We need this in order to modify the package.json for dev builds." + exit 1 + fi + # This allows us to publish to npm in CI workflows if [[ ${CI-} ]]; then echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc @@ -86,6 +85,10 @@ main() { # See: https://github.com/coder/code-server/pull/3935 echo "node_modules.asar" > release/.npmignore + # We use this to set the name of the package in the + # package.json + PACKAGE_NAME="code-server" + # NOTES:@jsjoeio # We only need to run npm version for "development" and "staging". # This is because our release:prep script automatically bumps the version @@ -112,12 +115,14 @@ main() { # Source: https://github.com/actions/checkout/issues/58#issuecomment-614041550 PR_NUMBER=$(echo "$GITHUB_REF" | awk 'BEGIN { FS = "/" } ; { print $3 }') NPM_VERSION="$VERSION-$PR_NUMBER-$COMMIT_SHA" + PACKAGE_NAME="@coder/code-server-pr" # This means the npm version will be tagged with "" # and installed when a user runs `yarn install code-server@` NPM_TAG="$PR_NUMBER" fi echo "using tag: $NPM_TAG" + echo "using package name: $PACKAGE_NAME" # We modify the version in the package.json # to be the current version + the PR number + commit SHA @@ -125,9 +130,17 @@ main() { # Example: "version": "4.0.1-4769-ad7b23cfe6ffd72914e34781ef7721b129a23040" # Example: "version": "4.0.1-beta-ad7b23cfe6ffd72914e34781ef7721b129a23040" pushd release - # NOTE:@jsjoeio + # NOTE@jsjoeio # I originally tried to use `yarn version` but ran into issues and abandoned it. npm version "$NPM_VERSION" + # NOTE@jsjoeio + # Use the development package name + # This is so we don't clutter the code-server versions on npm + # with development versions. + # jq can't edit in place so we must store in memory and echo + local contents + contents="$(jq ".name |= \"$PACKAGE_NAME\"" package.json)" + echo "${contents}" > package.json popd fi @@ -141,7 +154,10 @@ main() { return fi - yarn publish --non-interactive release --tag "$NPM_TAG" + # NOTE@jsjoeio + # Since the dev builds are scoped to @coder + # We pass --access public to ensure npm knows it's not private. + yarn publish --non-interactive release --tag "$NPM_TAG" --access public } main "$@" diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 3e01c6d0a67a..eaa4b736a770 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -7,7 +7,8 @@ - [Creating pull requests](#creating-pull-requests) - [Commits and commit history](#commits-and-commit-history) - [Development workflow](#development-workflow) - - [Updates to VS Code](#updates-to-vs-code) + - [Version updates to Code](#version-updates-to-code) + - [Patching Code](#patching-code) - [Build](#build) - [Help](#help) - [Test](#test) @@ -16,7 +17,7 @@ - [Integration tests](#integration-tests) - [End-to-end tests](#end-to-end-tests) - [Structure](#structure) - - [Modifications to VS Code](#modifications-to-vs-code) + - [Modifications to Code](#modifications-to-code) - [Currently Known Issues](#currently-known-issues) @@ -44,6 +45,8 @@ Here is what is needed: signature verification](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification) or follow [this tutorial](https://joeprevite.com/verify-commits-on-github) +- `quilt` + - Used to manage patches to Code - `rsync` and `unzip` - Used for code-server releases - `bats` @@ -57,7 +60,7 @@ If you're developing code-server on Linux, make sure you have installed or insta sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev python-is-python3 ``` -These are required by VS Code. See [their Wiki](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites) for more information. +These are required by Code. See [their Wiki](https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites) for more information. ## Creating pull requests @@ -78,41 +81,44 @@ we'll guide you. ## Development workflow -The current development workflow is a bit tricky because we have this repo and we use our `coder/vscode` fork inside it with [`yarn link`](https://classic.yarnpkg.com/lang/en/docs/cli/link/). - -Here are these steps you should follow to get your dev environment setup: - 1. `git clone https://github.com/coder/code-server.git` - Clone `code-server` -2. `git clone https://github.com/coder/vscode.git` - Clone `vscode` -3. `cd vscode && yarn install` - install the dependencies in the `vscode` repo -4. `cd code-server && yarn install` - install the dependencies in the `code-server` repo -5. `cd vscode && yarn link` - use `yarn` to create a symlink to the `vscode` repo (`code-oss-dev` package) -6. `cd code-server && yarn link code-oss-dev --modules-folder vendor/modules` - links your local `vscode` repo (`code-oss-dev` package) inside your local version of code-server -7. `cd code-server && yarn watch` - this will spin up code-server on localhost:8080 which you can start developing. It will live reload changes to the source. - -### Updates to VS Code - -If changes are made and merged into `main` in the [`coder/vscode`](https://github.com/coder/vscode) repo, then you'll need to update the version in the `code-server` repo by following these steps: - -1. Update the package tag listed in `vendor/package.json`: - -```json -{ - "devDependencies": { - "vscode": "coder/vscode#" - } -} -``` - -2. From the code-server **project root**, run `yarn install`. - Then, test code-server locally to make sure everything works. -3. Check the Node.js version that's used by Electron (which is shipped with VS +2. `git submodule update --init` - Clone `vscode` submodule +3. `quilt push -a` - Apply patches to the `vscode` submodule. +4. `yarn` - Install dependencies +5. `yarn watch` - Launch code-server localhost:8080. code-server will be live + reloaded when changes are made; the browser needs to be refreshed manually. + +When pulling down changes that include modifications to the patches you will +need to apply them with `quilt`. If you pull down changes that update the +`vscode` submodule you will need to run `git submodule update --init` and +re-apply the patches. + +### Version updates to Code + +1. Update the `lib/vscode` submodule to the desired upstream version branch. +2. Apply the patches (`quilt push -a`) or restore your stashed changes. At this + stage you may need to resolve conflicts. For example use `quilt push -f`, + manually apply the rejected portions, then `quilt refresh`. +3. From the code-server **project root**, run `yarn install`. +4. Test code-server locally to make sure everything works. +5. Check the Node.js version that's used by Electron (which is shipped with VS Code. If necessary, update your version of Node.js to match. -4. Open a PR - -> Watch for updates to -> `vendor/modules/code-oss-dev/src/vs/code/browser/workbench/workbench.html`. You may need to -> make changes to `src/browser/pages/vscode.html`. +6. Commit the updated submodule and patches to `code-server`. +7. Open a PR. + +### Patching Code + +0. You can go through the patch stack with `quilt push` and `quilt pop`. +1. Create a new patch (`quilt new {name}.diff`) or use an existing patch. +2. Add the file(s) you are patching (`quilt add [-P patch] {file}`). A file + **must** be added before you make changes to it. +3. Make your changes. Patches do not need to be independent of each other but + each patch must result in a working code-server without any broken in-between + states otherwise they are difficult to test and modify. +4. Add your changes to the patch (`quilt refresh`) +5. Add a comment in the patch about the reason for the patch and how to + reproduce the behavior it fixes or adds. Every patch should have an e2e test + as well. ### Build @@ -208,99 +214,46 @@ code-server running locally. In CI, this is taken care of for you. ## Structure -The `code-server` script serves as an HTTP API for login and starting a remote VS +The `code-server` script serves as an HTTP API for login and starting a remote Code process. The CLI code is in [src/node](../src/node) and the HTTP routes are implemented in [src/node/routes](../src/node/routes). -Most of the meaty parts are in the VS Code portion of the codebase under -[vendor/modules/code-oss-dev](../vendor/modules/code-oss-dev), which we describe next. - -### Modifications to VS Code - -In v1 of code-server, we had a patch of VS Code that split the codebase into a -front-end and a server. The front-end consisted of the UI code, while the server -ran the extensions and exposed an API to the front-end for file access and all -UI needs. - -Over time, Microsoft added support to VS Code to run it on the web. They have -made the front-end open source, but not the server. As such, code-server v2 (and -later) uses the VS Code front-end and implements the server. We do this by using -a Git subtree to fork and modify VS Code. This code lives under -[vendor/modules/code-oss-dev](../vendor/modules/code-oss-dev). - -Some noteworthy changes in our version of VS Code include: - -- Adding our build file, [`vendor/modules/code-oss-dev/coder.js`](../vendor/modules/code-oss-dev/coder.js), which includes build steps specific to code-server -- Node.js version detection changes in [`build/lib/node.ts`](../vendor/modules/code-oss-dev/build/lib/node.ts) and [`build/lib/util.ts`](../vendor/modules/code-oss-dev/build/lib/util.ts) -- Allowing extra extension directories - - Added extra arguments to [`src/vs/platform/environment/common/argv.ts`](../vendor/modules/code-oss-dev/src/vs/platform/environment/common/argv.ts) and to [`src/vs/platform/environment/node/argv.ts`](../vendor/modules/code-oss-dev/src/vs/platform/environment/node/argv.ts) - - Added extra environment state to [`src/vs/platform/environment/common/environment.ts`](../vendor/modules/code-oss-dev/src/vs/platform/environment/common/environment.ts); - - Added extra getters to [`src/vs/platform/environment/common/environmentService.ts`](../vendor/modules/code-oss-dev/src/vs/platform/environment/common/environmentService.ts) - - Added extra scanning paths to [`src/vs/platform/extensionManagement/node/extensionsScanner.ts`](../vendor/modules/code-oss-dev/src/vs/platform/extensionManagement/node/extensionsScanner.ts) -- Additions/removals from [`package.json`](../vendor/modules/code-oss-dev/package.json): - - Removing `electron`, `keytar` and `native-keymap` to avoid pulling in desktop dependencies during build on Linux - - Removing `gulp-azure-storage` and `gulp-tar` (unsued in our build process, may pull in outdated dependencies) - - Adding `proxy-agent`, `proxy-from-env` (for proxying) and `rimraf` (used during build/install steps) -- Adding our branding/custom URLs/version: - - [`product.json`](../vendor/modules/code-oss-dev/product.json) - - [`src/vs/base/common/product.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/product.ts) - - [`src/vs/workbench/browser/parts/dialogs/dialogHandler.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts) - - [`src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts) - - [`src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts) -- Removing azure/macOS signing related dependencies from [`build/package.json`](../vendor/modules/code-oss-dev/build/package.json) -- Modifying `.gitignore` to allow us to add files to `src/vs/server` and modifying `.eslintignore` to ignore lint on the shared files below (we use different formatter settings than VS Code). -- Sharing some files with our codebase via symlinks: - - [`src/vs/base/common/ipc.d.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/ipc.d.ts) points to [`typings/ipc.d.ts`](../typings/ipc.d.ts) - - [`src/vs/base/common/util.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/util.ts) points to [`src/common/util.ts`](../src/common/util.ts) - - [`src/vs/base/node/proxy_agent.ts`](../vendor/modules/code-oss-dev/src/vs/base/node/proxy_agent.ts) points to [`src/node/proxy_agent.ts`](../src/node/proxy_agent.ts) -- Allowing socket changes by adding `setSocket` in [`src/vs/base/parts/ipc/common/ipc.net.ts`](../vendor/modules/code-oss-dev/src/vs/base/parts/ipc/common/ipc.net.ts) - - We use this for connection persistence in our server-side code. -- Added our server-side Node.JS code to `src/vs/server`. - - This code includes the logic to spawn the various services (extension host, terminal, etc.) and some glue -- Added [`src/vs/workbench/browser/client.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/browser/client.ts) to hold some server customizations. - - Includes the functionality for the Log Out command and menu item - - Also, imported and called `initialize` from the main web file, [`src/vs/workbench/browser/web.main.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/browser/web.main.ts) -- Added a (hopefully temporary) hotfix to [`src/vs/workbench/common/resources.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/common/resources.ts) to get context menu actions working for the Git integration. -- Added connection type to WebSocket query parameters in [`src/vs/platform/remote/common/remoteAgentConnection.ts`](../vendor/modules/code-oss-dev/src/vs/platform/remote/common/remoteAgentConnection.ts) -- Added `CODE_SERVER*` variables to the sanitization list in [`src/vs/base/common/processes.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/processes.ts) -- Fix localization support: - - Added file [`src/vs/workbench/services/localizations/browser/localizationsService.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/localizations/browser/localizationsService.ts). - - Modified file [`src/vs/base/common/platform.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/platform.ts) - - Modified file [`src/vs/base/node/languagePacks.js`](../vendor/modules/code-oss-dev/src/vs/base/node/languagePacks.js) -- Added code to allow server to inject settings to [`src/vs/platform/product/common/product.ts`](../vendor/modules/code-oss-dev/src/vs/platform/product/common/product.ts) -- Extension fixes: - - Avoid disabling extensions by extensionKind in [`src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts) (Needed for vscode-icons) - - Remove broken symlinks in [`extensions/postinstall.js`](../vendor/modules/code-oss-dev/extensions/postinstall.js) - - Add tip about extension gallery in [`src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts) - - Use our own server for GitHub authentication in [`extensions/github-authentication/src/githubServer.ts`](../vendor/modules/code-oss-dev/extensions/github-authentication/src/githubServer.ts) - - Settings persistence on the server in [`src/vs/workbench/services/environment/browser/environmentService.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/environment/browser/environmentService.ts) - - Add extension install fallback in [`src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts) - - Add proxy-agent monkeypatch and keep extension host indefinitely running in [`src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts) - - Patch build system to avoid removing extension dependencies for `yarn global add` users in [`build/lib/extensions.ts`](../vendor/modules/code-oss-dev/build/lib/extensions.ts) - - Allow all extensions to use proposed APIs in [`src/vs/workbench/services/environment/browser/environmentService.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/services/environment/browser/environmentService.ts) - - Make storage writes async to allow extensions to wait for them to complete in [`src/vs/platform/storage/common/storage.ts`](../vendor/modules/code-oss-dev/src/vs/platform/storage/common/storage.ts) -- Specify webview path in [`src/vs/code/browser/workbench/workbench.ts`](../vendor/modules/code-oss-dev/src/vs/code/browser/workbench/workbench.ts) -- URL readability improvements for folder/workspace in [`src/vs/code/browser/workbench/workbench.ts`](../vendor/modules/code-oss-dev/src/vs/code/browser/workbench/workbench.ts) -- Socket/Authority-related fixes (for remote proxying etc.): - - [`src/vs/code/browser/workbench/workbench.ts`](../vendor/modules/code-oss-dev/src/vs/code/browser/workbench/workbench.ts) - - [`src/vs/platform/remote/browser/browserSocketFactory.ts`](../vendor/modules/code-oss-dev/src/vs/platform/remote/browser/browserSocketFactory.ts) - - [`src/vs/base/common/network.ts`](../vendor/modules/code-oss-dev/src/vs/base/common/network.ts) -- Added code to write out IPC path in [`src/vs/workbench/api/node/extHostCLIServer.ts`](../vendor/modules/code-oss-dev/src/vs/workbench/api/node/extHostCLIServer.ts) - -As the web portion of VS Code matures, we'll be able to shrink and possibly -eliminate our modifications. In the meantime, upgrading the VS Code version requires -us to ensure that our changes are still applied and work as intended. In the future, -we'd like to run VS Code unit tests against our builds to ensure that features -work as expected. +Most of the meaty parts are in the Code portion of the codebase under +[lib/vscode](../lib/vscode), which we describe next. + +### Modifications to Code + +Our modifications to Code can be found in the [patches](../patches) directory. +We pull in Code as a submodule pointing to an upstream release branch. + +In v1 of code-server, we had Code as a submodule and used a single massive patch +that split the codebase into a front-end and a server. The front-end consisted +of the UI code, while the server ran the extensions and exposed an API to the +front-end for file access and all UI needs. + +Over time, Microsoft added support to Code to run it on the web. They had made +the front-end open source, but not the server. As such, code-server v2 (and +later) uses the Code front-end and implements the server. We did this by using a +Git subtree to fork and modify Code. + +Microsoft eventually made the server open source and we were able to reduce our +changes significantly. Some time later we moved back to a submodule and patches +(managed by `quilt` this time instead of the mega-patch). + +As the web portion of Code continues to mature, we'll be able to shrink and +possibly eliminate our patches. In the meantime, upgrading the Code version +requires us to ensure that our changes are still applied correctly and work as +intended. In the future, we'd like to run Code unit tests against our builds to +ensure that features work as expected. > We have [extension docs](../ci/README.md) on the CI and build system. -If the functionality you're working on does NOT depend on code from VS Code, please +If the functionality you're working on does NOT depend on code from Code, please move it out and into code-server. ### Currently Known Issues -- Creating custom VS Code extensions and debugging them doesn't work +- Creating custom Code extensions and debugging them doesn't work - Extension profiling and tips are currently disabled diff --git a/docs/MAINTAINING.md b/docs/MAINTAINING.md index 8bec1e9c4d7f..0c55d4cff167 100644 --- a/docs/MAINTAINING.md +++ b/docs/MAINTAINING.md @@ -19,7 +19,7 @@ - [Docker](#docker) - [Homebrew](#homebrew) - [npm](#npm) -- [Syncing with Upstream VS Code](#syncing-with-upstream-vs-code) +- [Syncing with upstream Code](#syncing-with-upstream-code) - [Testing](#testing) - [Documentation](#documentation) - [Troubleshooting](#troubleshooting) @@ -34,7 +34,6 @@ as well as share our workflow for maintaining the project. Current maintainers: - @code-asher -- @TeffenEllis - @jsjoeio Occasionally, other Coder employees may step in time to time to assist with code-server. @@ -165,7 +164,7 @@ If you're the current release manager, follow these steps: ### Publishing a release -1. Create a release branch called `v0.0.0` but replace with new version +1. Create a new branch called `v0.0.0` (replace 0s with actual version aka v4.3.0) 1. Run `yarn release:prep` and type in the new version (e.g., `3.8.1`) 1. GitHub Actions will generate the `npm-package`, `release-packages` and `release-images` artifacts. You do not have to wait for this step to complete @@ -215,18 +214,9 @@ We publish code-server as a npm package [here](https://www.npmjs.com/package/cod This is currently automated with the release process. -## Syncing with Upstream VS Code +## Syncing with upstream Code -The VS Code portion of code-server lives under [`coder/vscode`](https://github.com/coder/vscode). To update VS Code for code-server, follow these steps: - -1. `git checkout -b vscode-update` - Create a new branch locally based off `main` -2. `git fetch upstream` - Fetch upstream (VS Code)'s latest branches -3. `git merge upstream/release/1.64` - Merge it locally - 1. replace `1.64` with the version you're upgrading to - 1. If there are merge conflicts, commit first, then fix them locally. -4. Open a PR merging your branch (`vscode-update`) into `main` and add the code-server review team - -Ideally, our fork stays as close to upstream as possible. See the differences between our fork and upstream [here](https://github.com/microsoft/vscode/compare/main...coder:main). +Refer to the [contributing docs](https://coder.com/docs/code-server/latest/CONTRIBUTING#version-updates-to-code) for information on how to update Code within code-server. ## Testing diff --git a/docs/README.md b/docs/README.md index f02649af76c6..b5fc7c7e129f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # code-server -[!["GitHub Discussions"](https://img.shields.io/badge/%20GitHub-%20Discussions-gray.svg?longCache=true&logo=github&colorB=purple)](https://github.com/coder/code-server/discussions) [!["Join us on Slack"](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://coder.com/community) [![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq) [![codecov](https://codecov.io/gh/coder/code-server/branch/main/graph/badge.svg?token=5iM9farjnC)](https://codecov.io/gh/coder/code-server) [![See v4.0.2 docs](https://img.shields.io/static/v1?label=Docs&message=see%20v4.0.2%20&color=blue)](https://github.com/coder/code-server/tree/v4.0.2/docs) +[!["GitHub Discussions"](https://img.shields.io/badge/%20GitHub-%20Discussions-gray.svg?longCache=true&logo=github&colorB=purple)](https://github.com/coder/code-server/discussions) [!["Join us on Slack"](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen)](https://coder.com/community) [![Twitter Follow](https://img.shields.io/twitter/follow/CoderHQ?label=%40CoderHQ&style=social)](https://twitter.com/coderhq) [![codecov](https://codecov.io/gh/coder/code-server/branch/main/graph/badge.svg?token=5iM9farjnC)](https://codecov.io/gh/coder/code-server) [![See latest](https://img.shields.io/static/v1?label=Docs&message=see%20latest&color=blue)](https://coder.com/docs/code-server/latest) Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and access it in the browser. diff --git a/docs/collaboration.md b/docs/collaboration.md index 5ae803d5b937..dda091545ef0 100644 --- a/docs/collaboration.md +++ b/docs/collaboration.md @@ -60,6 +60,6 @@ As `code-server` is based on VS Code, you can follow the steps described on Duck code-server --enable-proposed-api genuitecllc.codetogether ``` - Another option would be to add a value in code-server's [config file](https://coder.com/docs/code-server/v4.0.2/FAQ#how-does-the-config-file-work). + Another option would be to add a value in code-server's [config file](https://coder.com/docs/code-server/v4.3.0/FAQ#how-does-the-config-file-work). 3. Refresh code-server and navigate to the CodeTogether icon in the sidebar to host or join a coding session. diff --git a/docs/guide.md b/docs/guide.md index 81e37d7d148d..b08a3ed17208 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -52,7 +52,7 @@ There are several approaches to operating and exposing code-server securely: We highly recommend using [port forwarding via SSH](https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding) to access code-server. If you have an SSH server on your remote machine, this approach -doesn't required additional setup. +doesn't require any additional setup at all. The downside to SSH forwarding, however, is that you can't access code-server when using machines without SSH clients (such as iPads). If this applies to you, diff --git a/docs/helm.md b/docs/helm.md index b404919c5917..7f3dfb7abdde 100644 --- a/docs/helm.md +++ b/docs/helm.md @@ -1,6 +1,6 @@ # code-server Helm Chart -[![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square)](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square) [![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square)](https://img.shields.io/badge/Type-application-informational?style=flat-square) [![AppVersion: 4.0.2](https://img.shields.io/badge/AppVersion-4.0.2-informational?style=flat-square)](https://img.shields.io/badge/AppVersion-4.0.2-informational?style=flat-square) +[![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square)](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square) [![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square)](https://img.shields.io/badge/Type-application-informational?style=flat-square) [![AppVersion: 4.3.0](https://img.shields.io/badge/AppVersion-4.3.0-informational?style=flat-square)](https://img.shields.io/badge/AppVersion-4.3.0-informational?style=flat-square) [code-server](https://github.com/coder/code-server) code-server is VS Code running on a remote server, accessible through the browser. @@ -73,7 +73,7 @@ and their default values. | hostnameOverride | string | `""` | | image.pullPolicy | string | `"Always"` | | image.repository | string | `"codercom/code-server"` | -| image.tag | string | `"4.0.2"` | +| image.tag | string | `"4.3.0"` | | imagePullSecrets | list | `[]` | | ingress.enabled | bool | `false` | | nameOverride | string | `""` | diff --git a/docs/ios.md b/docs/ios.md index d804a33c6094..fc484e19b0dd 100644 --- a/docs/ios.md +++ b/docs/ios.md @@ -1,7 +1,9 @@ # Using code-server on iOS with iSH 1. Install iSH from the [App Store](https://apps.apple.com/us/app/ish-shell/id1436902243) -2. Install `curl` with `apk add curl` -3. Install code-server with `curl -fsSL https://code-server.dev/install.sh | sh` -4. Run code-server with `code-server` -5. Access on localhost:8080 in your browser +2. Install `curl` and `nano` with `apk add curl nano` +3. Configure iSH to use an earlier version of NodeJS with `nano /etc/apk/repositories` and edit `v3.14` to `v3.12` on both repository links. +4. Install `nodejs` and `npm` with `apk add nodejs npm` +5. Install code-server with `curl -fsSL https://code-server.dev/install.sh | sh` +6. Run code-server with `code-server` +7. Access on localhost:8080 in your browser diff --git a/docs/manifest.json b/docs/manifest.json index 8f0265078ddd..890ede663390 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1,5 +1,5 @@ { - "versions": ["v4.0.2"], + "versions": ["v4.3.0"], "routes": [ { "title": "Home", diff --git a/docs/termux.md b/docs/termux.md index 8a9b7a87034d..cfaec3607740 100644 --- a/docs/termux.md +++ b/docs/termux.md @@ -10,40 +10,45 @@ - [Create a new user](#create-a-new-user) - [Install Go](#install-go) - [Install Python](#install-python) + - [Working with PRoot](#working-with-proot) ## Install 1. Get [Termux](https://f-droid.org/en/packages/com.termux/) from **F-Droid**. -2. Install Debian by running the following. +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`.\ - If you used the Andronix command then you may have to edit the `start-debian.sh` script to mount `/sdcard` just as simple as uncommenting the `command+=" -b /sdcard"` line. - > The following command was extracted from [Andronix](https://andronix.app/) you can also use [proot-distro](https://github.com/termux/proot-distro). + > 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 wget curl proot tar -y && wget https://raw.githubusercontent.com/AndronixApp/AndronixOrigin/master/Installer/Debian/debian.sh -O debian.sh && chmod +x debian.sh && bash debian.sh +pkg update -y && pkg install proot-distro -y && proot-distro install debian && proot-distro login debian ``` -3. Run the following commands to setup Debian. +3. Run the following commands to setup Debian: ```bash -apt update -apt upgrade -y -apt-get install nano vim sudo curl wget git -y +apt update && apt upgrade -y && apt-get install sudo vim git -y ``` -4. Install [NVM](https://github.com/nvm-sh/nvm) 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; +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 and make sure to replace `$HOME` with `/root` - - Now run `exit` and start Debain again. + - 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 +``` -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) using `nvm install version_here`. -7. To install `code-server` run the following. +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 @@ -82,11 +87,11 @@ Potential Workaround : To create a new user follow these simple steps - -1. Create a new user by running `useradd username -m`. -2. Change the password by running `passwd username`. -3. Give your new user sudo access by runnning `visudo`, scroll down to `User privilege specification` and add the following line after root `username ALL=(ALL:ALL) ALL`. -4. Now edit the `/etc/passwd` file with your commadline editor of choice and at the end of the line that specifies your user change `/bin/sh` to `/bin/bash`. -5. Now switch users, by running `su - username` +1. Create a new user by running `useradd -m`. +2. Change the password by running `passwd `. +3. Give your new user sudo access by running `visudo`, scroll down to `User privilege specification` and add the following line after root `username ALL=(ALL:ALL) ALL`. +4. Now edit the `/etc/passwd` file with your command line editor of choice and at the end of the line that specifies your user change `/bin/sh` to `/bin/bash`. +5. Now switch users by running `su - ` - Remember the `-` betweeen `su` and username is required to execute `/etc/profile`,\ since `/etc/profile` may have some necessary things to be executed you should always add a `-`. @@ -95,7 +100,7 @@ To create a new user follow these simple steps - > From https://golang.org/doc/install -1. Go to https://golang.org/dl/ and copy the download link for `linux arm` and run the following. +1. Go to https://golang.org/dl/ and copy the download link for `linux arm` and run the following: ```bash wget download_link @@ -115,7 +120,7 @@ rm -rf /usr/local/go && tar -C /usr/local -xzf archive_name > Run these commands as root -1. Run the following command to install required packages to build python. +1. Run the following commands to install required packages to build python: ```bash sudo apt-get update @@ -124,13 +129,13 @@ sudo apt-get install make build-essential libssl-dev zlib1g-dev \ libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev ``` -2. Install [pyenv](https://github.com/pyenv/pyenv/) from [pyenv-installer](https://github.com/pyenv/pyenv-installer) by running. +2. Install [pyenv](https://github.com/pyenv/pyenv/) from [pyenv-installer](https://github.com/pyenv/pyenv-installer) by running: ```bash curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash ``` -3. Run `nano /etc/profile` and add the following +3. Run `nano /etc/profile` and add the following: ```bash export PYENV_ROOT="/root/.pyenv" @@ -139,10 +144,42 @@ eval "$(pyenv init --path)" eval "$(pyenv virtualenv-init -)" ``` -4. Exit start Debian again. +4. Exit and start Debian again. 5. Run `pyenv versions` to list all installable versions. 6. Run `pyenv install version` to install the desired python version. > The build process may take some time (an hour or 2 depending on your device). 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 ` 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). diff --git a/install.sh b/install.sh index b53720f23fd2..aad80dd4e47a 100755 --- a/install.sh +++ b/install.sh @@ -55,7 +55,7 @@ The detection method works as follows: - Debian, Ubuntu, Raspbian: install the deb package from GitHub. - Fedora, CentOS, RHEL, openSUSE: install the rpm package from GitHub. - Arch Linux: install from the AUR (which pulls releases from GitHub). - - FreeBSD, Alpine: install from yarn/npm. + - FreeBSD, Alpine: install from npm. - macOS: install using Homebrew if installed otherwise install from GitHub. - All others: install the release from GitHub. @@ -419,19 +419,9 @@ install_npm() { echoh "Installing latest from npm." echoh - YARN_PATH="${YARN_PATH-yarn}" NPM_PATH="${YARN_PATH-npm}" - if command_exists "$YARN_PATH"; then - sh_c="sh_c" - if [ ! "${DRY_RUN-}" ] && [ ! -w "$($YARN_PATH global bin)" ]; then - sh_c="sudo_sh_c" - fi - echoh "Installing with yarn." - echoh - "$sh_c" "$YARN_PATH" global add code-server --unsafe-perm - NPM_BIN_DIR="\$($YARN_PATH global bin)" echo_npm_postinstall - return - elif command_exists "$NPM_PATH"; then + + if command_exists "$NPM_PATH"; then sh_c="sh_c" if [ ! "${DRY_RUN-}" ] && [ ! -w "$(NPM_PATH config get prefix)" ]; then sh_c="sudo_sh_c" @@ -442,9 +432,9 @@ install_npm() { NPM_BIN_DIR="\$($NPM_PATH bin -g)" echo_npm_postinstall return fi - echoerr "Please install npm or yarn to install code-server!" + echoerr "Please install npm to install code-server!" echoerr "You will need at least node v12 and a few C dependencies." - echoerr "See the docs https://coder.com/docs/code-server/latest/install#yarn-npm" + echoerr "See the docs https://coder.com/docs/code-server/latest/install#npm" exit 1 } diff --git a/lib/vscode b/lib/vscode new file mode 160000 index 000000000000..c722ca6c7eed --- /dev/null +++ b/lib/vscode @@ -0,0 +1 @@ +Subproject commit c722ca6c7eed3d7987c0d5c3df5c45f6b15e77d1 diff --git a/package.json b/package.json index 759cfe0236f9..f9fece3781a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-server", "license": "MIT", - "version": "4.0.2", + "version": "4.3.0", "description": "Run VS Code on a remote server.", "homepage": "https://github.com/coder/code-server", "bugs": { @@ -51,7 +51,7 @@ "@types/ws": "^8.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", - "audit-ci": "^5.0.0", + "audit-ci": "^6.0.0", "codecov": "^3.8.3", "doctoc": "^2.0.0", "eslint": "^7.7.0", @@ -59,17 +59,19 @@ "eslint-import-resolver-typescript": "^2.5.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-prettier": "^4.0.0", + "json": "^11.0.0", "prettier": "^2.2.1", - "prettier-plugin-sh": "^0.8.0", + "prettier-plugin-sh": "^0.10.0", "shellcheck": "^1.0.0", "stylelint": "^13.0.0", "stylelint-config-recommended": "^5.0.0", + "synp": "^1.9.10", "ts-node": "^10.0.0", "typescript": "^4.4.0-dev.20210528" }, "resolutions": { "ansi-regex": "^5.0.1", - "normalize-package-data": "^3.0.0", + "normalize-package-data": "^4.0.0", "doctoc/underscore": "^1.13.1", "doctoc/**/trim": "^1.0.0", "postcss": "^8.2.1", @@ -81,7 +83,8 @@ "vm2": "^3.9.6", "follow-redirects": "^1.14.8", "node-fetch": "^2.6.7", - "nanoid": "^3.1.31" + "nanoid": "^3.1.31", + "minimist": "npm:minimist-lite@2.2.1" }, "dependencies": { "@coder/logger": "1.1.16", @@ -127,7 +130,6 @@ "testEnvironment": "node", "testPathIgnorePatterns": [ "/node_modules/", - "/vendor/", "/lib/", "/out/", "test/e2e" @@ -158,7 +160,7 @@ "/release-npm-package", "/release-gcp", "/release-images", - "/vendor" + "/lib" ], "moduleNameMapper": { "^.+\\.(css|less)$": "/test/utils/cssStub.ts" diff --git a/patches/base-path.diff b/patches/base-path.diff new file mode 100644 index 000000000000..c7d032b1810c --- /dev/null +++ b/patches/base-path.diff @@ -0,0 +1,326 @@ +Add base path support + +Some users will host code-server behind a path-rewriting reverse proxy, for +example domain.tld/my/base/path. This patch adds support for that since Code +assumes everything is on / by default. + +To test this serve code-server behind a reverse proxy with a path like /code. + +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 +@@ -151,8 +151,10 @@ class RemoteAuthoritiesImpl { + } + return URI.from({ + scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, +- authority: `${host}:${port}`, +- path: `/vscode-remote-resource`, ++ authority: platform.isWeb ? window.location.host : `${host}:${port}`, ++ path: platform.isWeb ++ ? URI.joinPath(URI.parse(window.location.href), `/vscode-remote-resource`).path ++ : `/vscode-remote-resource`, + query + }); + } +Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html +=================================================================== +--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html ++++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html +@@ -11,8 +11,8 @@ + + + +- +- ++ ++ + + + +@@ -27,23 +27,26 @@ + + + +- +- +- ++ ++ ++ + + + + + + +- +- ++ ++ + +- ++ ++ + +- +- +- ++ ++ ++ + +Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts ++++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts +@@ -274,7 +274,7 @@ export class BrowserSocketFactory implem + + connect(host: string, port: number, query: string, debugLabel: string, callback: IConnectCallback): void { + const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws'); +- const socket = this._webSocketFactory.create(`${webSocketSchema}://${/:/.test(host) ? `[${host}]` : host}:${port}/?${query}&skipWebSocketFrames=false`, debugLabel); ++ const socket = this._webSocketFactory.create(`${webSocketSchema}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`, debugLabel); + const errorListener = socket.onError((err) => callback(err, undefined)); + socket.onOpen(() => { + errorListener.dispose(); +@@ -282,6 +282,3 @@ export class BrowserSocketFactory implem + }); + } + } +- +- +- +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 +@@ -252,7 +252,10 @@ export class WebClientServer { + return res.end(); + } + +- const remoteAuthority = req.headers.host; ++ // It is not possible to reliably detect the remote authority on the server ++ // in all cases. Set this to something invalid to make sure we catch code ++ // that is using this when it should not. ++ const remoteAuthority = 'remote'; + + function escapeAttribute(value: string): string { + return value.replace(/"/g, '"'); +@@ -272,6 +275,8 @@ export class WebClientServer { + accessToken: this._environmentService.args['github-auth'], + scopes: [['user:email'], ['repo']] + } : undefined; ++ const base = relativeRoot(getOriginalUrl(req)) ++ const vscodeBase = relativePath(getOriginalUrl(req)) + const data = (await util.promisify(fs.readFile)(filePath)).toString() + .replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({ + remoteAuthority, +@@ -279,6 +284,7 @@ export class WebClientServer { + developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined }, + settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, + productConfiguration: >{ ++ rootEndpoint: base, + codeServerVersion: this._productService.codeServerVersion, + embedderIdentifier: 'server-distro', + extensionsGallery: this._webExtensionResourceUrlTemplate ? { +@@ -291,7 +297,9 @@ export class WebClientServer { + } : undefined + } + }))) +- .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : ''); ++ .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '') ++ .replace(/{{BASE}}/g, base) ++ .replace(/{{VS_BASE}}/g, vscodeBase); + + const cspDirectives = [ + 'default-src \'self\';', +@@ -370,3 +378,70 @@ export class WebClientServer { + return res.end(data); + } + } ++ ++/** ++ * Remove extra slashes in a URL. ++ * ++ * This is meant to fill the job of `path.join` so you can concatenate paths and ++ * then normalize out any extra slashes. ++ * ++ * If you are using `path.join` you do not need this but note that `path` is for ++ * file system paths, not URLs. ++ */ ++export const normalizeUrlPath = (url: string, keepTrailing = false): string => { ++ return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "") ++} ++ ++/** ++ * Get the relative path that will get us to the root of the page. For each ++ * slash we need to go up a directory. Will not have a trailing slash. ++ * ++ * For example: ++ * ++ * / => . ++ * /foo => . ++ * /foo/ => ./.. ++ * /foo/bar => ./.. ++ * /foo/bar/ => ./../.. ++ * ++ * All paths must be relative in order to work behind a reverse proxy since we ++ * we do not know the base path. Anything that needs to be absolute (for ++ * example cookies) must get the base path from the frontend. ++ * ++ * All relative paths must be prefixed with the relative root to ensure they ++ * work no matter the depth at which they happen to appear. ++ * ++ * For Express `req.originalUrl` should be used as they remove the base from the ++ * standard `url` property making it impossible to get the true depth. ++ */ ++export const relativeRoot = (originalUrl: string): string => { ++ const depth = (originalUrl.split("?", 1)[0].match(/\//g) || []).length ++ return normalizeUrlPath("./" + (depth > 1 ? "../".repeat(depth - 1) : "")) ++} ++ ++/** ++ * Get the relative path to the current resource. ++ * ++ * For example: ++ * ++ * / => . ++ * /foo => ./foo ++ * /foo/ => . ++ * /foo/bar => ./bar ++ * /foo/bar/ => . ++ */ ++export const relativePath = (originalUrl: string): string => { ++ const parts = originalUrl.split("?", 1)[0].split("/") ++ return normalizeUrlPath("./" + parts[parts.length - 1]) ++} ++ ++/** ++ * code-server serves Code using Express. Express removes the base from the url ++ * and puts the original in `originalUrl` so we must use this to get the correct ++ * depth. Code is not aware it is behind Express so the types do not match. We ++ * may want to continue moving code into Code and eventually remove the Express ++ * wrapper or move the web server back into code-server. ++ */ ++export const getOriginalUrl = (req: http.IncomingMessage): string => { ++ return (req as any).originalUrl || req.url ++} +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 + + export interface IProductConfiguration { + readonly codeServerVersion?: string ++ readonly rootEndpoint?: string + + readonly version: string; + readonly date?: string; +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 +@@ -482,6 +482,7 @@ function doCreateUri(path: string, query + }); + } + ++ path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/") + return URI.parse(window.location.href).with({ path, query }); + } + +@@ -493,7 +494,7 @@ function doCreateUri(path: string, query + if (!configElement || !configElementAttribute) { + throw new Error('Missing web configuration element'); + } +- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); ++ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host } + + // Create workbench + create(document.body, { +Index: code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts ++++ code-server/lib/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts +@@ -16,7 +16,6 @@ import { getServiceMachineId } from 'vs/ + import { IStorageService } from 'vs/platform/storage/common/storage'; + import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; + import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +-import { RemoteAuthorities } from 'vs/base/common/network'; + + export const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; + +@@ -72,7 +71,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' })); +- return this._isWebExtensionResourceEndPoint(uri) ? uri.with({ scheme: RemoteAuthorities.getPreferredWebSchema() }) : uri; ++ return this._isWebExtensionResourceEndPoint(uri) ? URI.joinPath(URI.parse(window.location.href), uri.path) : uri; + } + return undefined; + } diff --git a/patches/connection-type.diff b/patches/connection-type.diff new file mode 100644 index 000000000000..9f04bc29918d --- /dev/null +++ b/patches/connection-type.diff @@ -0,0 +1,19 @@ +Add connection type to web sockets + +This allows the backend to distinguish them. In our case we use them to count a +single "open" of Code so we need to be able to distinguish between web sockets +from two instances and two web sockets used in a single instance. + +Index: code-server/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts ++++ code-server/lib/vscode/src/vs/platform/remote/common/remoteAgentConnection.ts +@@ -231,7 +231,7 @@ async function connectToRemoteExtensionH + + let socket: ISocket; + try { +- socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); ++ socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `type=${connectionTypeToString(connectionType)}&reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); + } catch (error) { + options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`); + options.logService.error(error); diff --git a/patches/disable-downloads.diff b/patches/disable-downloads.diff new file mode 100644 index 000000000000..3c4b87da9102 --- /dev/null +++ b/patches/disable-downloads.diff @@ -0,0 +1,183 @@ +Add option to disable file downloads via CLI + +This patch adds support for a new CLI flag called `--disable-file-downloads` +which allows a user to remove the "Download..." option that shows up when you +right-click files in Code. The default value for this is `false`. + +To test this, start code-server with `--disable-file-downloads`, open editor, +right-click on a file (not a folder) and you should **not** see the +"Download..." option. + +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 +@@ -210,6 +210,11 @@ export interface IWorkbenchConstructionO + */ + readonly userDataPath?: string + ++ /** ++ * Whether the "Download..." option is enabled for files. ++ */ ++ readonly isEnabledFileDownloads?: boolean ++ + //#endregion + + +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 +@@ -30,6 +30,11 @@ export interface IBrowserWorkbenchEnviro + * Options used to configure the workbench. + */ + readonly options?: IWorkbenchConstructionOptions; ++ ++ /** ++ * Enable downloading files via menu actions. ++ */ ++ readonly isEnabledFileDownloads?: boolean; + } + + export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { +@@ -61,6 +66,13 @@ export class BrowserWorkbenchEnvironment + return this.options.userDataPath; + } + ++ get isEnabledFileDownloads(): boolean { ++ if (typeof this.options.isEnabledFileDownloads === "undefined") { ++ throw new Error('isEnabledFileDownloads was not provided to the browser'); ++ } ++ return this.options.isEnabledFileDownloads; ++ } ++ + @memoize + get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } + +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 +@@ -15,6 +15,7 @@ export const serverOptions: OptionDescri + 'disable-update-check': { type: 'boolean' }, + 'auth': { type: 'string' }, + 'locale': { type: 'string' }, ++ 'disable-file-downloads': { type: 'boolean' }, + + /* ----- server setup ----- */ + +@@ -92,6 +93,7 @@ export interface ServerParsedArgs { + 'disable-update-check'?: boolean; + 'auth'?: string + 'locale'?: string ++ 'disable-file-downloads'?: boolean; + + /* ----- server setup ----- */ + +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 +@@ -290,6 +290,7 @@ export class WebClientServer { + logLevel: this._logService.getLevel(), + }, + userDataPath: this._environmentService.userDataPath, ++ isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], + settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, + productConfiguration: >{ + rootEndpoint: base, +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 + import { Disposable } from 'vs/base/common/lifecycle'; + import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext } 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 } 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, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; + import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; + import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; + import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; + import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; +@@ -24,6 +23,7 @@ import { IEditorResolverService } from ' + import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; + import { Schemas } from 'vs/base/common/network'; + import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; ++import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService'; + + export class WorkbenchContextKeysHandler extends Disposable { + private inputFocusedContext: IContextKey; +@@ -75,7 +75,7 @@ export class WorkbenchContextKeysHandler + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IConfigurationService private readonly configurationService: IConfigurationService, +- @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, ++ @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, + @IEditorService private readonly editorService: IEditorService, + @IEditorResolverService private readonly editorResolverService: IEditorResolverService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, +@@ -194,6 +194,9 @@ export class WorkbenchContextKeysHandler + this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService); + this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)); + ++ // code-server ++ IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) ++ + this.registerListeners(); + } + +Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +=================================================================== +--- 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 +@@ -21,7 +21,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 { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; + import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; + import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +@@ -475,13 +475,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo + id: DOWNLOAD_COMMAND_ID, + title: DOWNLOAD_LABEL + }, +- when: ContextKeyExpr.or( +- // native: for any remote resource +- ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)), +- // web: for any files +- ContextKeyExpr.and(IsWebContext, ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated()), +- // web: for any folders if file system API support is provided +- ContextKeyExpr.and(IsWebContext, HasWebFileSystemAccess) ++ when: ContextKeyExpr.and( ++ IsEnabledFileDownloads, ++ ContextKeyExpr.or( ++ // native: for any remote resource ++ ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)), ++ // web: for any files ++ ContextKeyExpr.and(IsWebContext, ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated()), ++ // web: for any folders if file system API support is provided ++ ContextKeyExpr.and(IsWebContext, HasWebFileSystemAccess) ++ ) + ) + })); + +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 +@@ -30,6 +30,8 @@ export const IsFullscreenContext = new R + + export const HasWebFileSystemAccess = new RawContextKey('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access) + ++export const IsEnabledFileDownloads = new RawContextKey('isEnabledFileDownloads', true, true); ++ + //#endregion + + diff --git a/patches/display-language.diff b/patches/display-language.diff new file mode 100644 index 000000000000..6bfc0c75d969 --- /dev/null +++ b/patches/display-language.diff @@ -0,0 +1,265 @@ +Add display language support + +This likely needs tweaking if we want to upstream. + +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 +@@ -188,6 +188,9 @@ export async function setupServerService + const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)); + socketServer.registerChannel('extensions', channel); + ++ const localizationsChannel = ProxyChannel.fromService(accessor.get(ILocalizationsService)); ++ socketServer.registerChannel('localizations', localizationsChannel); ++ + const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService)); + socketServer.registerChannel('encryption', encryptionChannel); + +Index: code-server/lib/vscode/src/vs/base/common/platform.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/base/common/platform.ts ++++ code-server/lib/vscode/src/vs/base/common/platform.ts +@@ -84,6 +84,17 @@ if (typeof navigator === 'object' && !is + _isWeb = true; + _locale = navigator.language; + _language = _locale; ++ ++ 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); ++ _locale = nlsConfig.locale; ++ _translationsConfigFile = nlsConfig._translationsConfigFile; ++ _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; ++ } catch (error) { /* Oh well. */ } ++ } + } + + // Native environment +Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html +=================================================================== +--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html ++++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html +@@ -23,6 +23,9 @@ + + + ++ ++ ++ + + + +@@ -38,6 +41,27 @@ + + + +