├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ └── issue.yaml ├── PULL_REQUEST_TEMPLATE.md ├── scripts │ └── check-commits.sh └── workflows │ ├── build.yml │ ├── lint.yml │ └── verify.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── FAQ.md ├── LICENSE ├── NOTICE.html ├── README.md ├── SECURITY.md ├── TELEMETRY.md ├── build └── downloader.mjs ├── package-lock.json ├── package.json ├── resources ├── docker-logo-vertical-blue.png └── readme │ ├── docker-bake-editing.png │ ├── docker-bake-inline-completion.png │ ├── docker-compose-code-completion.png │ ├── docker-compose-hover.png │ └── dockerfile-problems.png ├── src ├── extension.ts ├── telemetry │ ├── client.ts │ └── filter.ts ├── ui-test │ ├── dockerbakeDiagnostics.test.ts │ └── dockerfileDiagnostics.test.ts └── utils │ ├── lsp │ ├── languageClient.ts │ └── lsp.ts │ ├── monitor.ts │ ├── os.ts │ ├── prompt.ts │ ├── settings.ts │ └── spawnDockerCommand.ts ├── test ├── resources │ ├── Dockerfile │ └── docker-bake.hcl └── settings.json ├── tsconfig.json └── webpack.config.js /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23.7-alpine3.21 AS builder 2 | 3 | RUN apk add --no-cache git make 4 | 5 | WORKDIR /workspaces/docker-language-server 6 | 7 | RUN git clone https://github.com/docker/docker-language-server . 8 | 9 | RUN make build 10 | 11 | FROM mcr.microsoft.com/devcontainers/typescript-node:20 12 | 13 | COPY --from=builder /workspaces/docker-language-server/docker-language-server* /usr/local/bin/ 14 | 15 | RUN apt-get update && apt-get install -y ca-certificates curl 16 | RUN install -m 0755 -d /etc/apt/keyrings 17 | RUN curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && \ 18 | chmod a+r /etc/apt/keyrings/docker.asc 19 | RUN echo \ 20 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ 21 | $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ 22 | tee /etc/apt/sources.list.d/docker.list > /dev/null 23 | RUN apt-get update 24 | RUN apt-get install docker-ce-cli docker-buildx-plugin docker-compose-plugin -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "Dockerfile" 4 | }, 5 | "mounts": [ 6 | "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" 7 | ], 8 | "remoteUser": "root", 9 | "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/vscode-extension,type=bind", 10 | "workspaceFolder": "/workspaces/vscode-extension", 11 | "postCreateCommand": "mkdir -p /workspaces/vscode-extension/bin && cp $(ls /usr/local/bin/docker-language-server*) /workspaces/vscode-extension/bin && npm ci", 12 | "customizations": { 13 | "vscode": { 14 | "settings": { 15 | "eslint.validate": ["javascript", "typescript"], 16 | "editor.codeActionsOnSave": { 17 | "source.fixAll.eslint": "explicit" 18 | } 19 | }, 20 | "extensions": [ 21 | "ms-azuretools.vscode-docker", 22 | "dbaeumer.vscode-eslint", 23 | "esbenp.prettier-vscode" 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/naming-convention": [ 11 | "error", 12 | { 13 | "selector": "import", 14 | "format": ["camelCase", "PascalCase"] 15 | } 16 | ], 17 | "@typescript-eslint/semi": "error", 18 | "curly": "error", 19 | "eqeqeq": "error", 20 | "no-throw-literal": "error", 21 | "semi": "off" 22 | }, 23 | "ignorePatterns": ["out", "dist", "**/*.d.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 2 | 3 | name: Issue 4 | description: Report an issue about the Docker DX extension. 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to open an issue! 10 | Please take a look at our [FAQ](https://github.com/docker/vscode-extension/blob/main/FAQ.md) to see if it addresses the issue you are reporting. 11 | If this is a security issue please report it to the [Docker Security team](mailto:security@docker.com). 12 | 13 | - type: textarea 14 | attributes: 15 | label: Description 16 | description: | 17 | Provide a description of the issue you are reporting here. 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | attributes: 23 | label: Visual Studio Code version 24 | description: | 25 | Output of `code -v` command. 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | attributes: 31 | label: Installed extensions in Visual Studio Code 32 | description: | 33 | Output of `code --list-extensions --show-versions` command. 34 | validations: 35 | required: true 36 | 37 | - type: textarea 38 | attributes: 39 | label: Additional info 40 | description: | 41 | Please provide any additional information that could be useful. 42 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Problem Description 2 | 3 | 6 | 7 | ## Proposed Solution 8 | 9 | 12 | 13 | ## Proof of Work 14 | 15 | 19 | -------------------------------------------------------------------------------- /.github/scripts/check-commits.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | signoff_regex="Signed-off-by:" 6 | commits=$(gh pr view "$1" --json commits -q ".commits[].oid") 7 | 8 | missing=0 9 | 10 | for sha in $commits; do 11 | echo "🔍 Checking commit $sha..." 12 | message=$(git log -1 --pretty=format:%B "$sha") 13 | 14 | if echo "$message" | grep -q "$signoff_regex"; then 15 | echo "✔ 'Signed-off-by' message found." 16 | continue 17 | fi 18 | 19 | is_verified=$(gh api repos/${GITHUB_REPOSITORY}/commits/$sha --jq '.commit.verification.verified') 20 | if [ "$is_verified" = "true" ]; then 21 | echo "✔ Verified signature found." 22 | continue 23 | fi 24 | 25 | echo "::error file=.git/COMMIT_EDITMSG::❌ Commit $sha is missing either a valid Signed-off-by message or a verified (GPG, SSH, or S/MIME) signature." 26 | missing=$((missing + 1)) 27 | done 28 | 29 | if [ "$missing" -gt 0 ]; then 30 | echo "❌ Some commits are not properly signed." 31 | exit 1 32 | fi 33 | 34 | echo "✅ All commits are properly signed." -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | tags: 8 | - v* 9 | paths-ignore: 10 | - '**/*.md' 11 | 12 | jobs: 13 | 14 | test-latest: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: actions/checkout@v4 (docker/docker-language-server) 19 | uses: actions/checkout@v4 20 | with: 21 | repository: docker/docker-language-server 22 | path: docker-language-server 23 | 24 | - name: actions/checkout@v4 (docker/vscode-extension) 25 | uses: actions/checkout@v4 26 | with: 27 | repository: docker/vscode-extension 28 | path: vscode-extension 29 | 30 | - uses: actions/setup-node@v4 31 | with: 32 | node-version: "22.x" 33 | 34 | - uses: actions/setup-go@v5 35 | with: 36 | go-version: "1.23.8" 37 | 38 | - run: npm install 39 | working-directory: vscode-extension 40 | 41 | - run: rm -rf vscode-extension/bin 42 | 43 | - run: mkdir vscode-extension/bin 44 | 45 | - run: make build 46 | working-directory: docker-language-server 47 | 48 | - run: mv docker-language-server/docker-language-server-linux-amd64 vscode-extension/bin 49 | 50 | - run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 51 | 52 | - run: xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' npm test 53 | working-directory: vscode-extension 54 | 55 | test-release: 56 | runs-on: ubuntu-latest 57 | 58 | steps: 59 | - uses: actions/checkout@v4 60 | 61 | - uses: actions/setup-node@v4 62 | with: 63 | node-version: "22.x" 64 | 65 | - run: npm install 66 | 67 | - run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 68 | 69 | - run: xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' npm test 70 | 71 | build: 72 | runs-on: ubuntu-latest 73 | needs: 74 | - test-latest 75 | - test-release 76 | 77 | strategy: 78 | matrix: 79 | os: [alpine, linux, win32, darwin] 80 | arch: [amd64, arm64] 81 | include: 82 | - arch: amd64 83 | nodearch: x64 84 | - arch: arm64 85 | nodearch: arm64 86 | - os: win32 87 | nodeos: win32 88 | ext: .exe 89 | - os: darwin 90 | nodeos: darwin 91 | ext: "" 92 | - os: linux 93 | nodeos: linux 94 | ext: "" 95 | - os: alpine 96 | nodeos: linux 97 | ext: "" 98 | 99 | steps: 100 | - name: actions/checkout@v4 (docker/vscode-extension) 101 | uses: actions/checkout@v4 102 | with: 103 | path: vscode-extension 104 | 105 | - uses: actions/setup-node@v4 106 | with: 107 | node-version: "22.x" 108 | 109 | - working-directory: vscode-extension 110 | run: | 111 | NODE_OS=${{ matrix.nodeos }} NODE_ARCH=${{ matrix.nodearch }} npm install 112 | 113 | - name: Set variables 114 | id: set-variables 115 | working-directory: vscode-extension 116 | run: | 117 | VERSION=$(npm pkg get version | tr -d \") 118 | echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" 119 | 120 | SHA=$(git rev-parse --short HEAD) 121 | echo "SHA=$SHA" >> "$GITHUB_OUTPUT" 122 | 123 | - name: Build the extension (refs/heads) 124 | if: startsWith(github.ref, 'refs/heads') 125 | env: 126 | VERSION: ${{ steps.set-variables.outputs.VERSION }} 127 | SHA: ${{ steps.set-variables.outputs.SHA }} 128 | working-directory: vscode-extension 129 | run: | 130 | npm install -g @vscode/vsce 131 | vsce package --target ${{ matrix.os }}-${{ matrix.nodearch }} -o docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-$VERSION-$SHA.vsix 132 | 133 | - name: actions/upload-artifact@v4 (refs/heads) 134 | if: startsWith(github.ref, 'refs/heads') 135 | uses: actions/upload-artifact@v4 136 | with: 137 | name: docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-${{ steps.set-variables.outputs.VERSION }}-${{ steps.set-variables.outputs.SHA }}.vsix 138 | path: vscode-extension/docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-${{ steps.set-variables.outputs.VERSION }}-${{ steps.set-variables.outputs.SHA }}.vsix 139 | if-no-files-found: error 140 | 141 | - name: Build the extension (refs/tags/v) 142 | if: startsWith(github.ref, 'refs/tags/v') 143 | env: 144 | VERSION: ${{ steps.set-variables.outputs.VERSION }} 145 | working-directory: vscode-extension 146 | run: | 147 | npm install -g @vscode/vsce 148 | vsce package --target ${{ matrix.os }}-${{ matrix.nodearch }} -o docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-$VERSION.vsix 149 | 150 | - name: actions/upload-artifact@v4 (refs/tags/v) 151 | uses: actions/upload-artifact@v4 152 | if: startsWith(github.ref, 'refs/tags/v') 153 | with: 154 | name: docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-${{ steps.set-variables.outputs.VERSION }}.vsix 155 | path: vscode-extension/docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-${{ steps.set-variables.outputs.VERSION }}.vsix 156 | if-no-files-found: error 157 | 158 | - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 https://github.com/softprops/action-gh-release/commit/c062e08bd532815e2082a85e87e3ef29c3e6d191 159 | if: startsWith(github.ref, 'refs/tags/v') 160 | env: 161 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 162 | with: 163 | files: vscode-extension/docker-vscode-extension-${{ matrix.os }}-${{ matrix.nodearch }}-${{ steps.set-variables.outputs.VERSION }}.vsix 164 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: 'Checkout code' 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: '22.x' 20 | 21 | - name: Install dependencies 22 | run: npm install 23 | 24 | - name: Run ESLint 25 | run: npm run lint 26 | 27 | - name: Run Prettier 28 | run: npm run prettier:check 29 | -------------------------------------------------------------------------------- /.github/workflows/verify.yml: -------------------------------------------------------------------------------- 1 | name: Verify commit signatures 2 | on: 3 | pull_request: 4 | branches: 5 | - '**' 6 | 7 | jobs: 8 | check-commits: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Run commit sign-off check script 17 | run: | 18 | .github/scripts/check-commits.sh "${{ github.event.pull_request.number }}" 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | bin 5 | .vscode-test/ 6 | syntaxes/ 7 | *.vsix 8 | .test-extensions 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | yarn.lock 3 | __check-ts__ 4 | dist 5 | .vscode 6 | node_modules 7 | out 8 | bin 9 | .vscode-test/ 10 | .test-extensions 11 | syntaxes/ 12 | 13 | .github/workflows/build.yml 14 | 15 | 16 | */coverage 17 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | trailingComma: 'all' # default value from Prettier >= 3.0.0, but we want to be explicit here 3 | 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Debug Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.formatOnSave": true, 4 | "[typescriptreact]": { 5 | "editor.defaultFormatter": "esbenp.prettier-vscode" 6 | }, 7 | "[typescript]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "typescript.updateImportsOnFileMove.enabled": "always", 11 | "files.exclude": { 12 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 13 | "dist": false // set this to true to hide the "dist" folder with the compiled JS files 14 | }, 15 | "search.exclude": { 16 | "out": true, // set this to false to include "out" folder in search results 17 | "dist": true // set this to false to include "dist" folder in search results 18 | }, 19 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 20 | "typescript.tsc.autoDetect": "off" 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$ts-webpack-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never", 13 | "group": "watchers" 14 | }, 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | } 19 | }, 20 | { 21 | "type": "npm", 22 | "script": "watch-tests", 23 | "problemMatcher": "$tsc-watch", 24 | "isBackground": true, 25 | "presentation": { 26 | "reveal": "never", 27 | "group": "watchers" 28 | }, 29 | "group": "build" 30 | }, 31 | { 32 | "label": "tasks: watch-tests", 33 | "dependsOn": [ 34 | "npm: watch", 35 | "npm: watch-tests" 36 | ], 37 | "problemMatcher": [] 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .github/** 2 | .vscode/** 3 | .vscode-test/** 4 | node_modules/** 5 | build/** 6 | out/** 7 | resources/readme/** 8 | src/** 9 | test/** 10 | .gitignore 11 | .yarnrc 12 | webpack.config.js 13 | vsc-extension-quickstart.md 14 | **/tsconfig.json 15 | **/.eslintrc.json 16 | **/*.map 17 | **/*.ts 18 | **/.vscode-test.* 19 | .devcontainer/** 20 | .prettierignore 21 | .prettierrc.yml 22 | .test-extensions 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the Docker DX extension will be documented in this file. 4 | 5 | ## [0.8.1] - 2025-06-06 6 | 7 | ### Fixed 8 | 9 | - lock cache manager when deleting to prevent concurrent map writes ([docker/docker-language-server#298](https://github.com/docker/docker-language-server/issues/298)) 10 | - initialize 11 | - return JSON-RPC error if an invalid URI was sent with the request ([docker/docker-language-server#292](https://github.com/docker/docker-language-server/issues/292)) 12 | - Compose 13 | - textDocument/completion 14 | - check for whitespace when performing prefix calculations for build target suggestions ([docker/docker-language-server#294](https://github.com/docker/docker-language-server/issues/294)) 15 | - return an empty result instead of an internal server error if the request's parameters are outside the document's bounds ([docker/docker-language-server#296](https://github.com/docker/docker-language-server/issues/296)) 16 | - check the node path's length before recursing deeper for pattern properties matches ([docker/docker-language-server#300](https://github.com/docker/docker-language-server/issues/300)) 17 | - textDocument/hover 18 | - fix error caused by casting a node without checking its type first ([docker/docker-language-server#290](https://github.com/docker/docker-language-server/issues/290)) 19 | 20 | ## [0.8.0] - 2025-06-05 21 | 22 | ### Added 23 | 24 | - send errors to BugSnag if error telemetry is configured to be allowed and sent 25 | - Dockerfile 26 | - provide code actions for Scout vulnerabilities that will open the settings page so that users can opt-out of them easily ([#130](https://github.com/docker/vscode-extension/issues/130)) 27 | - textDocument/hover 28 | - support configuring specific vulnerability hovers with an experimental setting ([#101](https://github.com/docker/vscode-extension/issues/101)) 29 | - textDocument/publishDiagnostics 30 | - support filtering specific vulnerability diagnostics with an experimental setting ([#101](https://github.com/docker/vscode-extension/issues/101)) 31 | - Compose 32 | - created `docker.extension.enableComposeLanguageServer` for globally toggling Compose editor features 33 | - updated Compose schema to the latest version ([docker/docker-language-server#117](https://github.com/docker/docker-language-server/issues/117)) 34 | - textDocument/completion 35 | - add support for attribute name and value completion 36 | - suggest dependent service names for the `depends_on` attribute ([docker/docker-language-server#131](https://github.com/docker/docker-language-server/issues/131)) 37 | - suggest dependent network names for the `networks` attribute ([docker/docker-language-server#132](https://github.com/docker/docker-language-server/issues/132)) 38 | - suggest dependent volume names for the `volumes` attribute ([docker/docker-language-server#133](https://github.com/docker/docker-language-server/issues/133)) 39 | - suggest dependent config names for the `configs` attribute ([docker/docker-language-server#134](https://github.com/docker/docker-language-server/issues/134)) 40 | - suggest dependent secret names for the `secrets` attribute ([docker/docker-language-server#135](https://github.com/docker/docker-language-server/issues/135)) 41 | - improve code completion by automatically including required attributes in completion items ([docker/docker-language-server#155](https://github.com/docker/docker-language-server/issues/155)) 42 | - support build stage names for the `target` attribute ([docker/docker-language-server#173](https://github.com/docker/docker-language-server/issues/173)) 43 | - suggest service names for a service's `extends` or `extends.service` attribute ([docker/docker-language-server#184](https://github.com/docker/docker-language-server/issues/184)) 44 | - textDocument/definition 45 | - support looking up volume references ([docker/docker-language-server#147](https://github.com/docker/docker-language-server/issues/147)) 46 | - support navigating to a dependency that is defined in another file ([docker/docker-language-server#190](https://github.com/docker/docker-language-server/issues/190)) 47 | - support navigating to the defined YAML anchor from an alias reference ([#264](https://github.com/docker/docker-language-server/issues/264)) 48 | - textDocument/documentHighlight 49 | - support highlighting object references and anchors and aliases 50 | - textDocument/documentLink 51 | - support opening a referenced Dockerfile from the `build` object's `dockerfile` attribute ([#69](https://github.com/docker/docker-language-server/issues/69)) 52 | - support opening a referenced file from a config's `file` attribute ([#271](https://github.com/docker/docker-language-server/issues/271)) 53 | - support opening a referenced file from a secret's `file` attribute ([#272](https://github.com/docker/docker-language-server/issues/272)) 54 | - provide document links when an included file is also a YAML anchor ([#275](https://github.com/docker/docker-language-server/issues/275)) 55 | - consider quotes when calculating the link's range ([#242](https://github.com/docker/docker-language-server/issues/242)) 56 | - consider anchors and aliases instead of assuming everything are strings ([#266](https://github.com/docker/docker-language-server/issues/266)) 57 | - textDocument/formatting 58 | - add support to format YAML files that do not have clear syntactical errors ([docker/docker-language-server#165](https://github.com/docker/docker-language-server/issues/165)) 59 | - textDocument/hover 60 | - add support for hovering over attribute keys and showing the descriptions in the schema with links to the schema and the online documentation 61 | - render a referenced object's or YAML anchor or alias's textual YAML content as a hover 62 | - include the range of the hovered element to clearly identify what is being hovered over for the client ([#256](https://github.com/docker/docker-language-server/issues/256)) 63 | - textDocument/inlayHint 64 | - show the parent service's value if it is being overridden and they are not object attributes ([docker/docker-language-server#156](https://github.com/docker/docker-language-server/issues/156)) 65 | - textDocument/publishDiagnostics 66 | - report YAML syntax errors ([docker/docker-language-server#167](https://github.com/docker/docker-language-server/issues/167)) 67 | - textDocument/prepareRename 68 | - support rename preparation requests ([docker/docker-language-server#150](https://github.com/docker/docker-language-server/issues/150)) 69 | - textDocument/rename 70 | - support renaming named object references and YAML anchors and aliases 71 | - Bake 72 | - textDocument/publishDiagnostics 73 | - support filtering specific vulnerability diagnostics with an experimental setting ([#101](https://github.com/docker/vscode-extension/issues/101)) 74 | 75 | ### Changed 76 | 77 | - diagnostics will now include Docker DX in its name to help users identify which diagnostics are coming from this extension ([#127](https://github.com/docker/vscode-extension/issues/127)) 78 | - Dockerfile 79 | - textDocument/hover 80 | - `recommended_tag` diagnostics are now hidden by default ([docker/docker-language-server#223](https://github.com/docker/docker-language-server/issues/223)) 81 | - textDocument/publishDiagnostics 82 | - hide `not_pinned_digest` diagnostics from Scout by default ([docker/docker-language-server#216](https://github.com/docker/docker-language-server/issues/216)) 83 | - recommended tag hovers are now hidden by default ([docker/docker-language-server#223](https://github.com/docker/docker-language-server/issues/223)) 84 | 85 | ### Fixed 86 | 87 | - Dockerfile 88 | - textDocument/codeAction 89 | - preserve instruction flags when fixing a `not_pinned_digest` diagnostic ([docker/docker-language-server#123](https://github.com/docker/docker-language-server/issues/123)) 90 | - textDocument/definition 91 | - fix range calculation when the element is quoted ([#255](https://github.com/docker/docker-language-server/issues/255)) 92 | - textDocument/hover 93 | - hide vulnerability hovers if the top level setting is disabled ([docker/docker-language-server#226](https://github.com/docker/docker-language-server/issues/226)) 94 | - textDocument/publishDiagnostics 95 | - ignore the diagnostic's URL and do not set it if it is evaluated to be the empty string ([docker/docker-language-server#219](https://github.com/docker/docker-language-server/issues/219)) 96 | - consider flag changes when determining whether to scan a file again or not ([docker/docker-language-server#224](https://github.com/docker/docker-language-server/issues/224)) 97 | - Compose 98 | - textDocument/completion 99 | - resolved a spacing offset issue with object or array completions ([docker/docker-language-server#115](https://github.com/docker/docker-language-server/issues/115)) 100 | - suggest completion items for array items that use an object schema directly ([docker/docker-language-server#161](https://github.com/docker/docker-language-server/issues/161)) 101 | - textDocument/definition 102 | - consider `extends` when looking up a service reference ([docker/docker-language-server#170](https://github.com/docker/docker-language-server/issues/170)) 103 | - recurse into YAML anchors if they are defined on a service object ([#287](https://github.com/docker/docker-language-server/issues/287)) 104 | - textDocument/hover 105 | - fixed a case where an object reference's description would not be returned in a hover result ([docker/docker-language-server#233](https://github.com/docker/docker-language-server/issues/233)) 106 | - Bake 107 | - textDocument/publishDiagnostics 108 | - stop flagging `BUILDKIT_SYNTAX` as an unrecognized `ARG` ([docker/docker-language-server#187](https://github.com/docker/docker-language-server/issues/187)) 109 | - use inheritance to determine if an `ARG` is truly unused ([docker/docker-language-server#198](https://github.com/docker/docker-language-server/issues/198)) 110 | - correct range calculations for malformed variable interpolation errors ([docker/docker-language-server#203](https://github.com/docker/docker-language-server/issues/203)) 111 | - filter out variables when resolving Dockerfile paths to prevent false positives from being reported ([docker/docker-language-server#263](https://github.com/docker/docker-language-server/issues/263)) 112 | 113 | ### Removed 114 | 115 | - Compose 116 | - removed the `docker.extension.experimental.composeCompletions` setting in favour for the new `docker.extension.enableComposeLanguageServer` setting 117 | 118 | ## [0.7.0] - 2025-05-21 119 | 120 | ### Added 121 | 122 | - tagged appropriate settings to make them easier to search for 123 | - suggest the user install Docker Desktop if Scout cannot be found 124 | - prompt the user about duplicated Compose features if Red Hat's YAML extension is also installed 125 | 126 | ## [0.6.0] - 2025-04-29 127 | 128 | ### Added 129 | 130 | - Compose 131 | - textDocument/definition 132 | - support lookup of `configs`, `networks`, and `secrets` referenced inside `services` object ([#91](https://github.com/docker/docker-language-server/issues/91)) 133 | - textDocument/documentLink 134 | - support opening a referenced image's page as a link ([#91](https://github.com/docker/docker-language-server/issues/91)) 135 | - textDocument/hover 136 | - extract descriptions and enum values from the Compose specification and display them as hovers ([#101](https://github.com/docker/docker-language-server/issues/101)) 137 | 138 | ## [0.5.0] - 2025-04-28 139 | 140 | ### Added 141 | 142 | - add support for the `alpine-x64` and `alpine-arm64` targets ([#93](https://github.com/docker/vscode-extension/issues/93)) 143 | - Bake 144 | - textDocument/definition 145 | - allow jumping to a target block when referencing its attribute ([docker/docker-language-server#78](https://github.com/docker/docker-language-server/issues/78)) 146 | - Compose 147 | - textDocument/definition 148 | - allow looking up referenced services when using the short form syntax for `depends_on` ([docker/docker-language-server#67](https://github.com/docker/docker-language-server/issues/67)) 149 | - allow looking up referenced services when using the long form syntax for `depends_on` ([docker/docker-language-server#68](https://github.com/docker/docker-language-server/issues/68)) 150 | 151 | ### Fixed 152 | 153 | - Bake 154 | - textDocument/semanticTokens/full 155 | - ensure semantic tokens are only calculated for Bake files ([docker/docker-language-server#85](https://github.com/docker/docker-language-server/pull/85)) 156 | 157 | ## [0.4.10] - 2025-04-21 158 | 159 | ### Changed 160 | 161 | - updated the included Docker Language Server from 0.3.5 to 0.3.7 162 | - Bake 163 | - textDocument/publishDiagnostics 164 | - consider the context attribute when determining which Dockerfile the Bake target is for ([docker/docker-language-server#57](https://github.com/docker/docker-language-server/issues/57)) 165 | - textDocument/inlayHints 166 | - consider the context attribute when determining which Dockerfile to use for inlaying default values of `ARG` variables ([docker/docker-language-server#60](https://github.com/docker/docker-language-server/pull/60)) 167 | - textDocument/completion 168 | - consider the context attribute when determining which Dockerfile to use for looking up build stages ([docker/docker-language-server#61](https://github.com/docker/docker-language-server/pull/61)) 169 | - textDocument/definition 170 | - consider the context attribute when trying to resolve the Dockerfile to use for `ARG` variable definitions ([docker/docker-language-server#62](https://github.com/docker/docker-language-server/pull/62)) 171 | - fix a panic that may occur if a for loop did not have a conditional expression ([docker/docker-language-server#65](https://github.com/docker/docker-language-server/pull/65)) 172 | 173 | ### Fixed 174 | 175 | - set the Docker Desktop prompt setting correctly ([#90](https://github.com/docker/vscode-extension/issues/90)) 176 | 177 | ## [0.4.9] - 2025-04-15 178 | 179 | ### Fixed 180 | 181 | - apply the Scout vulnerability setting correctly if multiple files are opened ([#82](https://github.com/docker/vscode-extension/pull/82)) 182 | - capture more error telemetry to try to understand the last few crashes ([#83](https://github.com/docker/vscode-extension/pull/83)) 183 | - make the language server binary executable before trying to start it ([#84](https://github.com/docker/vscode-extension/pull/84)) 184 | 185 | ## [0.4.8] - 2025-04-14 186 | 187 | ### Added 188 | 189 | - capture errors from the language server not being able to start 190 | 191 | ## [0.4.7] - 2025-04-11 192 | 193 | ### Fixed 194 | 195 | - pick the user's home folder when scanning for CVEs with Scout if no workspace folder has been opened ([#76](https://github.com/docker/vscode-extension/issues/76)) 196 | - ignore incorrect scalar values in Compose files so that they stop getting incorrectly rendered in the outline ([docker/docker-language-server#50](https://github.com/docker/docker-language-server/pull/50)) 197 | 198 | ## [0.4.6] - 2025-04-09 199 | 200 | ### Added 201 | 202 | - capture some more error messages to better understand why the language server is crashing on some systems 203 | - updated the readme so it calls out how this extension is getting installed 204 | 205 | ## [0.4.5] - 2025-04-09 206 | 207 | ### Fixed 208 | 209 | - update the language server so that it will not crash when handling messages 210 | 211 | ## [0.4.4] - 2025-04-09 212 | 213 | ### Fixed 214 | 215 | - include a language server fix to prevent it from crashing when opening Bake files with comments placed at the end of a line 216 | 217 | ## [0.4.3] - 2025-04-09 218 | 219 | ### Fixed 220 | 221 | - surface errors with Docker Bake or Docker Scout to the user instead of failing silently 222 | 223 | ## [0.4.2] - 2025-04-08 224 | 225 | ### Changed 226 | 227 | - include recognizable error messages in the telemetry data 228 | 229 | ## [0.4.1] - 2025-04-08 230 | 231 | ### Removed 232 | 233 | - removed references to the feature flag in public-facing documentation 234 | 235 | ## [0.4.0] - 2025-04-08 236 | 237 | ### Changed 238 | 239 | - automatically download a binary of the language server when `npm install` is run to make development a little easier 240 | 241 | ### Removed 242 | 243 | - removed the feature flag so that the extension is live for everyone 244 | 245 | ## [0.3.0] 246 | 247 | ### Changed 248 | 249 | - suppress duplicated errors that are reported by both the Dockerfile Language Server and the Docker Language Server ([#33](https://github.com/docker/vscode-extension/issues/33)) 250 | 251 | ### Fixed 252 | 253 | - always register the Scout command so that the gradual rollout will not prevent the command from working ([#44](https://github.com/docker/vscode-extension/issues/44)) 254 | 255 | ## [0.2.0] - 2025-03-28 256 | 257 | ### Added 258 | 259 | - Include the feature flag's value in the telemetry event ([#39](https://github.com/docker/vscode-extension/issues/39)) 260 | - Contribute a context menu item to ms-azuretools.vscode-docker to scan an image with Docker Scout ([#38](https://github.com/docker/vscode-extension/issues/38)) 261 | 262 | ### Changed 263 | 264 | - README images and `.github` folder can be excluded from VSIX ([#30](https://github.com/docker/vscode-extension/issues/30)) 265 | 266 | ### Fixed 267 | 268 | - Running "Build with Bake" without a Bakefile yields an error ([#32](https://github.com/docker/vscode-extension/issues/32)) 269 | - Has "tag recommendations available" but doesn't actually show what tags are recommended ([#34](https://github.com/docker/vscode-extension/issues/34)) 270 | 271 | ## [0.1.1] - 2025-03-26 272 | 273 | ### Changed 274 | 275 | - removed the "Beta" label from the extension's name ([#27](https://github.com/docker/vscode-extension/pull/27)) 276 | 277 | ## 0.1.0 - 2025-03-26 278 | 279 | ### Added 280 | 281 | - BuildKit and BuildX build check integrations in a Dockerfile 282 | - image vulnerability analysis, supporting hovers and problem reporting in a Dockerfile (experimental) 283 | - Bake support 284 | - works for `docker-bake.hcl` and `docker-bake.override.hcl` 285 | - code completion 286 | - code navigation 287 | - document links 288 | - inline suggestions 289 | - error reporting 290 | - Compose outline support 291 | 292 | [Unreleased]: https://github.com/docker/vscode-extension/compare/v0.8.1...main 293 | [0.8.1]: https://github.com/docker/vscode-extension/compare/v0.8.0...v0.8.1 294 | [0.8.0]: https://github.com/docker/vscode-extension/compare/v0.7.0...v0.8.0 295 | [0.7.0]: https://github.com/docker/vscode-extension/compare/v0.6.0...v0.7.0 296 | [0.6.0]: https://github.com/docker/vscode-extension/compare/v0.5.0...v0.6.0 297 | [0.5.0]: https://github.com/docker/vscode-extension/compare/v0.4.10...v0.5.0 298 | [0.4.10]: https://github.com/docker/vscode-extension/compare/v0.4.9...v0.4.10 299 | [0.4.9]: https://github.com/docker/vscode-extension/compare/v0.4.8...v0.4.9 300 | [0.4.8]: https://github.com/docker/vscode-extension/compare/v0.4.7...v0.4.8 301 | [0.4.7]: https://github.com/docker/vscode-extension/compare/v0.4.6...v0.4.7 302 | [0.4.6]: https://github.com/docker/vscode-extension/compare/v0.4.5...v0.4.6 303 | [0.4.5]: https://github.com/docker/vscode-extension/compare/v0.4.4...v0.4.5 304 | [0.4.4]: https://github.com/docker/vscode-extension/compare/v0.4.3...v0.4.4 305 | [0.4.3]: https://github.com/docker/vscode-extension/compare/v0.4.2...v0.4.3 306 | [0.4.2]: https://github.com/docker/vscode-extension/compare/v0.4.1...v0.4.2 307 | [0.4.1]: https://github.com/docker/vscode-extension/compare/v0.4.0...v0.4.1 308 | [0.4.0]: https://github.com/docker/vscode-extension/compare/v0.3.0...v0.4.0 309 | [0.3.0]: https://github.com/docker/vscode-extension/compare/v0.2.0...v0.3.0 310 | [0.2.0]: https://github.com/docker/vscode-extension/compare/v0.1.1...v0.2.0 311 | [0.1.1]: https://github.com/docker/vscode-extension/compare/v0.1.0...v0.1.1 312 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to the Docker DX Visual Studio Code extension Project 2 | 3 | ## Sign your work 4 | 5 | The sign-off is a simple line at the end of the explanation for the patch. Your 6 | signature certifies that you wrote the patch or otherwise have the right to pass 7 | it on as an open-source patch. The rules are pretty simple: if you can certify 8 | the below (from [developercertificate.org](http://developercertificate.org/)): 9 | 10 | ``` 11 | Developer Certificate of Origin 12 | Version 1.1 13 | 14 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 15 | 1 Letterman Drive 16 | Suite D4700 17 | San Francisco, CA, 94129 18 | 19 | Everyone is permitted to copy and distribute verbatim copies of this 20 | license document, but changing it is not allowed. 21 | 22 | Developer's Certificate of Origin 1.1 23 | 24 | By making a contribution to this project, I certify that: 25 | 26 | (a) The contribution was created in whole or in part by me and I 27 | have the right to submit it under the open source license 28 | indicated in the file; or 29 | 30 | (b) The contribution is based upon previous work that, to the best 31 | of my knowledge, is covered under an appropriate open source 32 | license and I have the right under that license to submit that 33 | work with modifications, whether created in whole or in part 34 | by me, under the same open source license (unless I am 35 | permitted to submit under a different license), as indicated 36 | in the file; or 37 | 38 | (c) The contribution was provided directly to me by some other 39 | person who certified (a), (b) or (c) and I have not modified 40 | it. 41 | 42 | (d) I understand and agree that this project and the contribution 43 | are public and that a record of the contribution (including all 44 | personal information I submit with it, including my sign-off) is 45 | maintained indefinitely and may be redistributed consistent with 46 | this project or the open source license(s) involved. 47 | ``` 48 | 49 | Then you just add a line to every git commit message: 50 | 51 | Signed-off-by: Joe Smith 52 | 53 | Use your real name (sorry, no pseudonyms or anonymous contributions.) 54 | 55 | If you set your `user.name` and `user.email` git configs, you can sign your 56 | commit automatically with `git commit -s`. 57 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### Where can I get help if I cannot find the answer in this FAQ? 4 | 5 | You can search our [list of issues](https://github.com/docker/vscode-extension/issues) or the [discussions page](https://github.com/docker/vscode-extension/discussions) to see if someone else has asked about something similar. If not, feel free to open a new issue or discussion. We look forward to hearing from you! 6 | 7 | ### Why is this extension on my system? I do not remember installing it. 8 | 9 | Please refer to [this blog post](https://www.docker.com/blog/docker-dx-extension-for-vs-code-update/) and/or [docker/vscode-extension#103](https://github.com/docker/vscode-extension/issues/103). 10 | 11 | ### Where I can learn more about the telemetry that the Docker DX extension collects? 12 | 13 | For information regarding telemetry, please refer to [TELEMETRY.md](./TELEMETRY.md). 14 | 15 | ### I am seeing duplicated editor features (such as code completion suggestions, hover tooltips, etc.) in Compose files. 16 | 17 | Do you have any of the following extensions installed? 18 | 19 | - [Red Hat's YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) 20 | - [Microsoft's Container Tools extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers) 21 | - [Microsoft's Docker extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) 22 | 23 | If yes, you can refer to the steps below to remove the duplicates. Alternatively, if you would prefer to disable the Compose editing features that _this_ extension is providing, you can set the `docker.extension.enableComposeLanguageServer` setting to `false` and then restart Visual Studio Code. 24 | 25 | - [Red Hat's YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) (powered by [redhat-developer/yaml-language-server](https://github.com/redhat-developer/yaml-language-server)) 26 | 1. To disable duplicates from this extension, create a JSON file with `{}` as its content and save it somewhere. Let's say it is at `/home/user/empty.json`. 27 | 2. Open the [Command Palette](https://code.visualstudio.com/api/ux-guidelines/command-palette) in Visual Studio Code and open "Preferences: Open User Settings (JSON)". 28 | 3. Set `docker.extension.enableComposeLanguageServer` to `true` by following the snippet below. 29 | 4. Create an object attribute for `yaml.schemas` if it does not already exist. 30 | 5. Inside the `yaml.schemas` object, map your empty JSON file to Compose YAML files by following the snippet below. 31 | 6. YAML files named `compose*y*ml` or `docker-compose*y*ml` will now no longer have the Compose schema associated with them in Red Hat's extension so Red Hat's extension will stop providing YAML features for Compose files. This admittedly is a strange way to disable YAML features for a given file but it is the only known workaround for resolving this until [redhat-developer/vscode-yaml#1088](https://github.com/redhat-developer/vscode-yaml/issues/1088) is implemented. 32 | 33 | ```JSONC 34 | { 35 | // this must be explicitly set to true in your settings.json file or 36 | // the auto-deduplication logic will programmatically set the value to 37 | // false if it detects that Red Hat's YAML extension is installed 38 | "docker.extension.enableComposeLanguageServer": true, 39 | "yaml.schemas": { 40 | // this tells Red Hat's YAML extension to consider Compose YAML 41 | // files as not having a schema so it will stop suggesting code 42 | // completion items, hover tooltips, and so on 43 | "/home/user/empty.json": ["compose*y*ml", "docker-compose*y*ml"] 44 | } 45 | } 46 | ``` 47 | 48 | - [Microsoft's Container Tools extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers) (powered by [microsoft/compose-language-service](https://github.com/microsoft/compose-language-service)) 49 | - If [microsoft/vscode-containers#75](https://github.com/microsoft/vscode-containers/pull/75) is merged and you are on a release with this change, then the duplicates should already be taken of. 50 | - If not, you can you can set the `containers.enableComposeLanguageService` setting to `false` and restart Visual Studio Code. 51 | - [Microsoft's Docker extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) (powered by [microsoft/compose-language-service](https://github.com/microsoft/compose-language-service)) 52 | - If you have version 1.x installed, you can set the `docker.enableDockerComposeLanguageService` setting to `false` and restart Visual Studio Code. 53 | - If you have version 2.x installed, then you can refer to the steps above for Microsoft's Container Tools extension instead. 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2013-2018 Docker, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | https://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /NOTICE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NOTICES AND INFORMATION 5 | 12 | 13 | 14 |

NOTICES AND INFORMATION

15 |

Do Not Translate or Localize

16 |

This software incorporates material from third parties.

17 |

18 | Notwithstanding any other terms, you may reverse engineer this software to 19 | the extent required to debug changes to any libraries licensed under the 20 | GNU Lesser General Public License. 21 |

22 |
    23 |
  1. 24 |
    25 | HashiCorp HCL - MPL-2.0 26 |

    27 | https://www.hashicorp.com/ 28 |

    29 |
      30 |
    • Copyright (c) 2020 HashiCorp, Inc.
    • 31 |
    32 |
     33 |             Copyright (c) 2020 HashiCorp, Inc.
     34 | 
     35 | Mozilla Public License Version 2.0
     36 | ==================================
     37 | 
     38 | 1. Definitions
     39 | --------------
     40 | 
     41 | 1.1. "Contributor"
     42 |     means each individual or legal entity that creates, contributes to
     43 |     the creation of, or owns Covered Software.
     44 | 
     45 | 1.2. "Contributor Version"
     46 |     means the combination of the Contributions of others (if any) used
     47 |     by a Contributor and that particular Contributor's Contribution.
     48 | 
     49 | 1.3. "Contribution"
     50 |     means Covered Software of a particular Contributor.
     51 | 
     52 | 1.4. "Covered Software"
     53 |     means Source Code Form to which the initial Contributor has attached
     54 |     the notice in Exhibit A, the Executable Form of such Source Code
     55 |     Form, and Modifications of such Source Code Form, in each case
     56 |     including portions thereof.
     57 | 
     58 | 1.5. "Incompatible With Secondary Licenses"
     59 |     means
     60 | 
     61 |     (a) that the initial Contributor has attached the notice described
     62 |         in Exhibit B to the Covered Software; or
     63 | 
     64 |     (b) that the Covered Software was made available under the terms of
     65 |         version 1.1 or earlier of the License, but not also under the
     66 |         terms of a Secondary License.
     67 | 
     68 | 1.6. "Executable Form"
     69 |     means any form of the work other than Source Code Form.
     70 | 
     71 | 1.7. "Larger Work"
     72 |     means a work that combines Covered Software with other material, in
     73 |     a separate file or files, that is not Covered Software.
     74 | 
     75 | 1.8. "License"
     76 |     means this document.
     77 | 
     78 | 1.9. "Licensable"
     79 |     means having the right to grant, to the maximum extent possible,
     80 |     whether at the time of the initial grant or subsequently, any and
     81 |     all of the rights conveyed by this License.
     82 | 
     83 | 1.10. "Modifications"
     84 |     means any of the following:
     85 | 
     86 |     (a) any file in Source Code Form that results from an addition to,
     87 |         deletion from, or modification of the contents of Covered
     88 |         Software; or
     89 | 
     90 |     (b) any new file in Source Code Form that contains any Covered
     91 |         Software.
     92 | 
     93 | 1.11. "Patent Claims" of a Contributor
     94 |     means any patent claim(s), including without limitation, method,
     95 |     process, and apparatus claims, in any patent Licensable by such
     96 |     Contributor that would be infringed, but for the grant of the
     97 |     License, by the making, using, selling, offering for sale, having
     98 |     made, import, or transfer of either its Contributions or its
     99 |     Contributor Version.
    100 | 
    101 | 1.12. "Secondary License"
    102 |     means either the GNU General Public License, Version 2.0, the GNU
    103 |     Lesser General Public License, Version 2.1, the GNU Affero General
    104 |     Public License, Version 3.0, or any later versions of those
    105 |     licenses.
    106 | 
    107 | 1.13. "Source Code Form"
    108 |     means the form of the work preferred for making modifications.
    109 | 
    110 | 1.14. "You" (or "Your")
    111 |     means an individual or a legal entity exercising rights under this
    112 |     License. For legal entities, "You" includes any entity that
    113 |     controls, is controlled by, or is under common control with You. For
    114 |     purposes of this definition, "control" means (a) the power, direct
    115 |     or indirect, to cause the direction or management of such entity,
    116 |     whether by contract or otherwise, or (b) ownership of more than
    117 |     fifty percent (50%) of the outstanding shares or beneficial
    118 |     ownership of such entity.
    119 | 
    120 | 2. License Grants and Conditions
    121 | --------------------------------
    122 | 
    123 | 2.1. Grants
    124 | 
    125 | Each Contributor hereby grants You a world-wide, royalty-free,
    126 | non-exclusive license:
    127 | 
    128 | (a) under intellectual property rights (other than patent or trademark)
    129 |     Licensable by such Contributor to use, reproduce, make available,
    130 |     modify, display, perform, distribute, and otherwise exploit its
    131 |     Contributions, either on an unmodified basis, with Modifications, or
    132 |     as part of a Larger Work; and
    133 | 
    134 | (b) under Patent Claims of such Contributor to make, use, sell, offer
    135 |     for sale, have made, import, and otherwise transfer either its
    136 |     Contributions or its Contributor Version.
    137 | 
    138 | 2.2. Effective Date
    139 | 
    140 | The licenses granted in Section 2.1 with respect to any Contribution
    141 | become effective for each Contribution on the date the Contributor first
    142 | distributes such Contribution.
    143 | 
    144 | 2.3. Limitations on Grant Scope
    145 | 
    146 | The licenses granted in this Section 2 are the only rights granted under
    147 | this License. No additional rights or licenses will be implied from the
    148 | distribution or licensing of Covered Software under this License.
    149 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
    150 | Contributor:
    151 | 
    152 | (a) for any code that a Contributor has removed from Covered Software;
    153 |     or
    154 | 
    155 | (b) for infringements caused by: (i) Your and any other third party's
    156 |     modifications of Covered Software, or (ii) the combination of its
    157 |     Contributions with other software (except as part of its Contributor
    158 |     Version); or
    159 | 
    160 | (c) under Patent Claims infringed by Covered Software in the absence of
    161 |     its Contributions.
    162 | 
    163 | This License does not grant any rights in the trademarks, service marks,
    164 | or logos of any Contributor (except as may be necessary to comply with
    165 | the notice requirements in Section 3.4).
    166 | 
    167 | 2.4. Subsequent Licenses
    168 | 
    169 | No Contributor makes additional grants as a result of Your choice to
    170 | distribute the Covered Software under a subsequent version of this
    171 | License (see Section 10.2) or under the terms of a Secondary License (if
    172 | permitted under the terms of Section 3.3).
    173 | 
    174 | 2.5. Representation
    175 | 
    176 | Each Contributor represents that the Contributor believes its
    177 | Contributions are its original creation(s) or it has sufficient rights
    178 | to grant the rights to its Contributions conveyed by this License.
    179 | 
    180 | 2.6. Fair Use
    181 | 
    182 | This License is not intended to limit any rights You have under
    183 | applicable copyright doctrines of fair use, fair dealing, or other
    184 | equivalents.
    185 | 
    186 | 2.7. Conditions
    187 | 
    188 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
    189 | in Section 2.1.
    190 | 
    191 | 3. Responsibilities
    192 | -------------------
    193 | 
    194 | 3.1. Distribution of Source Form
    195 | 
    196 | All distribution of Covered Software in Source Code Form, including any
    197 | Modifications that You create or to which You contribute, must be under
    198 | the terms of this License. You must inform recipients that the Source
    199 | Code Form of the Covered Software is governed by the terms of this
    200 | License, and how they can obtain a copy of this License. You may not
    201 | attempt to alter or restrict the recipients' rights in the Source Code
    202 | Form.
    203 | 
    204 | 3.2. Distribution of Executable Form
    205 | 
    206 | If You distribute Covered Software in Executable Form then:
    207 | 
    208 | (a) such Covered Software must also be made available in Source Code
    209 |     Form, as described in Section 3.1, and You must inform recipients of
    210 |     the Executable Form how they can obtain a copy of such Source Code
    211 |     Form by reasonable means in a timely manner, at a charge no more
    212 |     than the cost of distribution to the recipient; and
    213 | 
    214 | (b) You may distribute such Executable Form under the terms of this
    215 |     License, or sublicense it under different terms, provided that the
    216 |     license for the Executable Form does not attempt to limit or alter
    217 |     the recipients' rights in the Source Code Form under this License.
    218 | 
    219 | 3.3. Distribution of a Larger Work
    220 | 
    221 | You may create and distribute a Larger Work under terms of Your choice,
    222 | provided that You also comply with the requirements of this License for
    223 | the Covered Software. If the Larger Work is a combination of Covered
    224 | Software with a work governed by one or more Secondary Licenses, and the
    225 | Covered Software is not Incompatible With Secondary Licenses, this
    226 | License permits You to additionally distribute such Covered Software
    227 | under the terms of such Secondary License(s), so that the recipient of
    228 | the Larger Work may, at their option, further distribute the Covered
    229 | Software under the terms of either this License or such Secondary
    230 | License(s).
    231 | 
    232 | 3.4. Notices
    233 | 
    234 | You may not remove or alter the substance of any license notices
    235 | (including copyright notices, patent notices, disclaimers of warranty,
    236 | or limitations of liability) contained within the Source Code Form of
    237 | the Covered Software, except that You may alter any license notices to
    238 | the extent required to remedy known factual inaccuracies.
    239 | 
    240 | 3.5. Application of Additional Terms
    241 | 
    242 | You may choose to offer, and to charge a fee for, warranty, support,
    243 | indemnity or liability obligations to one or more recipients of Covered
    244 | Software. However, You may do so only on Your own behalf, and not on
    245 | behalf of any Contributor. You must make it absolutely clear that any
    246 | such warranty, support, indemnity, or liability obligation is offered by
    247 | You alone, and You hereby agree to indemnify every Contributor for any
    248 | liability incurred by such Contributor as a result of warranty, support,
    249 | indemnity or liability terms You offer. You may include additional
    250 | disclaimers of warranty and limitations of liability specific to any
    251 | jurisdiction.
    252 | 
    253 | 4. Inability to Comply Due to Statute or Regulation
    254 | ---------------------------------------------------
    255 | 
    256 | If it is impossible for You to comply with any of the terms of this
    257 | License with respect to some or all of the Covered Software due to
    258 | statute, judicial order, or regulation then You must: (a) comply with
    259 | the terms of this License to the maximum extent possible; and (b)
    260 | describe the limitations and the code they affect. Such description must
    261 | be placed in a text file included with all distributions of the Covered
    262 | Software under this License. Except to the extent prohibited by statute
    263 | or regulation, such description must be sufficiently detailed for a
    264 | recipient of ordinary skill to be able to understand it.
    265 | 
    266 | 5. Termination
    267 | --------------
    268 | 
    269 | 5.1. The rights granted under this License will terminate automatically
    270 | if You fail to comply with any of its terms. However, if You become
    271 | compliant, then the rights granted under this License from a particular
    272 | Contributor are reinstated (a) provisionally, unless and until such
    273 | Contributor explicitly and finally terminates Your grants, and (b) on an
    274 | ongoing basis, if such Contributor fails to notify You of the
    275 | non-compliance by some reasonable means prior to 60 days after You have
    276 | come back into compliance. Moreover, Your grants from a particular
    277 | Contributor are reinstated on an ongoing basis if such Contributor
    278 | notifies You of the non-compliance by some reasonable means, this is the
    279 | first time You have received notice of non-compliance with this License
    280 | from such Contributor, and You become compliant prior to 30 days after
    281 | Your receipt of the notice.
    282 | 
    283 | 5.2. If You initiate litigation against any entity by asserting a patent
    284 | infringement claim (excluding declaratory judgment actions,
    285 | counter-claims, and cross-claims) alleging that a Contributor Version
    286 | directly or indirectly infringes any patent, then the rights granted to
    287 | You by any and all Contributors for the Covered Software under Section
    288 | 2.1 of this License shall terminate.
    289 | 
    290 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
    291 | end user license agreements (excluding distributors and resellers) which
    292 | have been validly granted by You or Your distributors under this License
    293 | prior to termination shall survive termination.
    294 | 
    295 | ************************************************************************
    296 | *                                                                      *
    297 | *  6. Disclaimer of Warranty                                           *
    298 | *  -------------------------                                           *
    299 | *                                                                      *
    300 | *  Covered Software is provided under this License on an "as is"       *
    301 | *  basis, without warranty of any kind, either expressed, implied, or  *
    302 | *  statutory, including, without limitation, warranties that the       *
    303 | *  Covered Software is free of defects, merchantable, fit for a        *
    304 | *  particular purpose or non-infringing. The entire risk as to the     *
    305 | *  quality and performance of the Covered Software is with You.        *
    306 | *  Should any Covered Software prove defective in any respect, You     *
    307 | *  (not any Contributor) assume the cost of any necessary servicing,   *
    308 | *  repair, or correction. This disclaimer of warranty constitutes an   *
    309 | *  essential part of this License. No use of any Covered Software is   *
    310 | *  authorized under this License except under this disclaimer.         *
    311 | *                                                                      *
    312 | ************************************************************************
    313 | 
    314 | ************************************************************************
    315 | *                                                                      *
    316 | *  7. Limitation of Liability                                          *
    317 | *  --------------------------                                          *
    318 | *                                                                      *
    319 | *  Under no circumstances and under no legal theory, whether tort      *
    320 | *  (including negligence), contract, or otherwise, shall any           *
    321 | *  Contributor, or anyone who distributes Covered Software as          *
    322 | *  permitted above, be liable to You for any direct, indirect,         *
    323 | *  special, incidental, or consequential damages of any character      *
    324 | *  including, without limitation, damages for lost profits, loss of    *
    325 | *  goodwill, work stoppage, computer failure or malfunction, or any    *
    326 | *  and all other commercial damages or losses, even if such party      *
    327 | *  shall have been informed of the possibility of such damages. This   *
    328 | *  limitation of liability shall not apply to liability for death or   *
    329 | *  personal injury resulting from such party's negligence to the       *
    330 | *  extent applicable law prohibits such limitation. Some               *
    331 | *  jurisdictions do not allow the exclusion or limitation of           *
    332 | *  incidental or consequential damages, so this exclusion and          *
    333 | *  limitation may not apply to You.                                    *
    334 | *                                                                      *
    335 | ************************************************************************
    336 | 
    337 | 8. Litigation
    338 | -------------
    339 | 
    340 | Any litigation relating to this License may be brought only in the
    341 | courts of a jurisdiction where the defendant maintains its principal
    342 | place of business and such litigation shall be governed by laws of that
    343 | jurisdiction, without reference to its conflict-of-law provisions.
    344 | Nothing in this Section shall prevent a party's ability to bring
    345 | cross-claims or counter-claims.
    346 | 
    347 | 9. Miscellaneous
    348 | ----------------
    349 | 
    350 | This License represents the complete agreement concerning the subject
    351 | matter hereof. If any provision of this License is held to be
    352 | unenforceable, such provision shall be reformed only to the extent
    353 | necessary to make it enforceable. Any law or regulation which provides
    354 | that the language of a contract shall be construed against the drafter
    355 | shall not be used to construe this License against a Contributor.
    356 | 
    357 | 10. Versions of the License
    358 | ---------------------------
    359 | 
    360 | 10.1. New Versions
    361 | 
    362 | Mozilla Foundation is the license steward. Except as provided in Section
    363 | 10.3, no one other than the license steward has the right to modify or
    364 | publish new versions of this License. Each version will be given a
    365 | distinguishing version number.
    366 | 
    367 | 10.2. Effect of New Versions
    368 | 
    369 | You may distribute the Covered Software under the terms of the version
    370 | of the License under which You originally received the Covered Software,
    371 | or under the terms of any subsequent version published by the license
    372 | steward.
    373 | 
    374 | 10.3. Modified Versions
    375 | 
    376 | If you create software not governed by this License, and you want to
    377 | create a new license for such software, you may create and use a
    378 | modified version of this License if you rename the license and remove
    379 | any references to the name of the license steward (except to note that
    380 | such modified license differs from this License).
    381 | 
    382 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
    383 | Licenses
    384 | 
    385 | If You choose to distribute Source Code Form that is Incompatible With
    386 | Secondary Licenses under the terms of this version of the License, the
    387 | notice described in Exhibit B of this License must be attached.
    388 | 
    389 | Exhibit A - Source Code Form License Notice
    390 | -------------------------------------------
    391 | 
    392 |   This Source Code Form is subject to the terms of the Mozilla Public
    393 |   License, v. 2.0. If a copy of the MPL was not distributed with this
    394 |   file, You can obtain one at http://mozilla.org/MPL/2.0/.
    395 | 
    396 | If it is not possible or desirable to put the notice in a particular
    397 | file, then You may include the notice in a location (such as a LICENSE
    398 | file in a relevant directory) where a recipient would be likely to look
    399 | for such a notice.
    400 | 
    401 | You may add additional accurate notices of copyright ownership.
    402 | 
    403 | Exhibit B - "Incompatible With Secondary Licenses" Notice
    404 | ---------------------------------------------------------
    405 | 
    406 |   This Source Code Form is "Incompatible With Secondary Licenses", as
    407 |   defined by the Mozilla Public License, v. 2.0.
    408 |                 
    410 |
    411 |
  2. 412 |
413 | 414 | 415 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker DX 2 | 3 | The **Docker DX (Developer Experience)** Visual Studio Code extension enhances your Visual Studio Code experience with Docker-related development by adding rich editing features and vulnerability scanning. 4 | 5 | ## Key features 6 | 7 | - [Dockerfile linting](https://docs.docker.com/reference/build-checks/): Get build warnings and best-practice suggestions via BuildKit and BuildX. 8 | - [Compose editing features](https://docs.docker.com/compose/): Provides contextual code completion, reference navigation, and schema descriptions in hovers. 9 | - [Bake editing features](https://docs.docker.com/build/bake/): Includes code completion, variable navigation, and inline suggestions for generating targets based on your Dockerfile stages. 10 | - Image vulnerability scanning (experimental): Flags references to container images with known vulnerabilities directly within Dockerfiles. 11 | 12 | ## Requirements 13 | 14 | The extension requires Docker Engine to be running. [Install Docker Desktop](https://www.docker.com/get-started/) on your machine and make sure the `docker` CLI is available in your system `PATH`. 15 | 16 | This extension currently supports the following operating systems and architectures: 17 | 18 | | Operating system | Architectures | 19 | | ---------------- | ---------------- | 20 | | Windows | `amd64`, `arm64` | 21 | | macOS | `amd64`, `arm64` | 22 | | Linux | `amd64`, `arm64` | 23 | | Alpine | `amd64`, `arm64` | 24 | 25 | If you are on an unsupported system, let us know of your interest in this extension so we can prioritize the work accordingly. 26 | 27 | ## Feature overview 28 | 29 | ### Editing Dockerfiles 30 | 31 | You can get linting checks from [BuildKit](https://github.com/moby/buildkit) and [BuildX](https://github.com/docker/buildx) when editing your Dockerfiles. 32 | 33 | Any references to images with vulnerabilities are also flagged. Note: This is an experimental feature. 34 | 35 | Errors are visible directly in your editor or you can look at them by opening up the Problems panel (Ctrl+Shift+M on Windows/Linux, Shift+Command+M on Mac). 36 | 37 | ![Linting a Dockerfile for build warnings and the use of vulnerable images](resources/readme/dockerfile-problems.png) 38 | 39 | ### Editing Compose files 40 | 41 | Code completion support in the Docker DX extension goes beyond just the Compose schema. The Docker DX extension understands the contextual link between your Compose file and your Dockerfile. 42 | 43 | ![Editing a Compose file with code completion suggestions inferred from another file](resources/readme/docker-compose-code-completion.png) 44 | 45 | Compose files can get pretty big and complicated when you start including other files. Docker DX lets you hover over a reference to quickly see how something is defined. 46 | 47 | ![Hover over object references to see its YAML content](resources/readme/docker-compose-hover.png) 48 | 49 | ### Editing Bake files 50 | 51 | You can get code completion when editing your `docker-bake.hcl` file. You are also able to hover over variables and navigate around the file by jumping to a variable's definition or jumping to the build stage within a Dockerfile. 52 | 53 | ![Editing a Bake file with code completion and cross-file linking support](resources/readme/docker-bake-editing.png) 54 | 55 | The extension provides inline suggestions to generate a Bake target to correspond to each build stage in your Dockerfile. 56 | 57 | ![Suggesting Bake targets based on the content of the local Dockerfile](resources/readme/docker-bake-inline-completion.png) 58 | 59 | ## Builds 60 | 61 | [GitHub Actions](https://github.com/docker/vscode-extension/actions) builds eight `.vsix` files - one for each platform combination (Windows, macOS, Linux, Alpine Linux x `amd64`/`arm64`). 62 | 63 | Note: The language server binary from these builds are not signed and/or notarized. You may encounter issues when using `.vsix` files from this repository as your operating system may refuse to open an unsigned binary. 64 | 65 | ## Development 66 | 67 | To debug the VS Code extension, clone this repository and then run `npm install`. This will download a binary of the [Docker Language Server](https://github.com/docker/docker-language-server/releases) to the `bin` folder. If you would like to test your own custom build of the language server, simply replace the file in the `bin` folder with your own binary. 68 | 69 | ### Debugging both the extension and language server 70 | 71 | 1. Clone the [docker/docker-language-server repository](https://github.com/docker/docker-language-server) 72 | 2. Start the language server in debug mode with the `--address :49201` argument. 73 | 3. In VS Code, update the `docker.lsp.debugServerPort` setting to `49201`. This is the default port that is used for any launch configurations saved in Git. 74 | 4. Launch the extension in debug mode. It will connect to the language server you started in debug mode instead of trying to execute a binary in `bin/`. 75 | 76 | ### Testing 77 | 78 | Run `npm test` to launch the UI tests. 79 | 80 | ## Telemetry 81 | 82 | The Docker DX extension collects telemetry. We collect this telemetry so that we can improve the extension by understanding usage patterns and catching crashes and errors for diagnostic purposes. Note that if you have already opted out of sending telemetry in Visual Studio Code then no telemetry will be sent. 83 | 84 | See [TELEMETRY.md](./TELEMETRY.md) for details about what kind of telemetry we collect and how to configure your telemetry settings. 85 | 86 | ## FAQ 87 | 88 | Please refer to [FAQ.md](./FAQ.md) for our list of frequently asked questions. 89 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The maintainers of the Docker DX Visual Studio Code extension take security seriously. 4 | If you discover a security issue, please bring it to their attention right away! 5 | 6 | ## Reporting a Vulnerability 7 | 8 | Please **DO NOT** file a public issue, instead send your report privately 9 | to [security@docker.com](mailto:security@docker.com). 10 | 11 | Reporter(s) can expect a response within 72 hours, acknowledging the issue was 12 | received. 13 | 14 | ## Review Process 15 | 16 | After receiving the report, an initial triage and technical analysis is 17 | performed to confirm the report and determine its scope. We may request 18 | additional information in this stage of the process. 19 | 20 | Once a reviewer has confirmed the relevance of the report, a draft security 21 | advisory will be created on GitHub. The draft advisory will be used to discuss 22 | the issue with maintainers, the reporter(s), and where applicable, other 23 | affected parties under embargo. 24 | 25 | If the vulnerability is accepted, a timeline for developing a patch, public 26 | disclosure, and patch release will be determined. If there is an embargo period 27 | on public disclosure before the patch release, the reporter(s) are expected to 28 | participate in the discussion of the timeline and abide by agreed upon dates 29 | for public disclosure. 30 | 31 | ## Accreditation 32 | 33 | Security reports are greatly appreciated and we will publicly thank you, 34 | although we will keep your name confidential if you request it. We also like to 35 | send gifts - if you're into swag, make sure to let us know. We do not currently 36 | offer a paid security bounty program at this time. 37 | 38 | ## Further Information 39 | 40 | Should anything in this document be unclear or if you are looking for additional 41 | information about how Docker reviews and responds to security vulnerabilities, 42 | please take a look at Docker's 43 | [Vulnerability Disclosure Policy](https://www.docker.com/trust/vulnerability-disclosure-policy/). 44 | -------------------------------------------------------------------------------- /TELEMETRY.md: -------------------------------------------------------------------------------- 1 | # Telemetry 2 | 3 | The Docker DX extension collects telemetry. We collect this telemetry so that we can improve the extension by understanding usage patterns and catching crashes and errors for diagnostic purposes. 4 | 5 | ## Configuring Telemetry Collection 6 | 7 | You can configue the telemetry you would like to send by changing the `docker.lsp.telemetry` setting. This can be one of three values: 8 | 9 | - `"all"` - all telemetry will be sent 10 | - `"error"` - send only errors and crash information 11 | - `"off"` - stop all telemetry from being sent 12 | 13 | If you have already opted out of sending telemetry in Visual Studio Code then no telemetry will be sent to Docker regardless of the value of the `docker.lsp.telemetry` setting. 14 | 15 | ## Telemetry Data Collected 16 | 17 | - operating system 18 | - CPU architecture 19 | - application name 20 | - `vscode.env.machineId` 21 | - `vscode.env.sessionId` 22 | - version of the installed extension 23 | - Docker version 24 | - function names and parameters for diagnosing errors and crashes 25 | - error messages when the language server is unable to start or is crashing 26 | - if certain extensions are also installed 27 | - [Microsoft's Container Tools extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers) 28 | - [Microsoft's Docker extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) 29 | - [Red Hat's YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) 30 | - values of certain settings from this extension 31 | - `docker.extension.enableComposeLanguageServer` 32 | 33 | The list above does _not_ include any telemetry collected by the [Docker Language Server](https://github.com/docker/docker-language-server). For telemetry collected by the Docker Language Server, please refer to the telemetry documentation of that project. 34 | 35 | ## BugSnag 36 | 37 | This extension integrates with BugSnag and sends errors and stack traces to BugSnag if telemetry is configured. 38 | 39 | ## Privacy Policy 40 | 41 | Read our [privacy policy](https://www.docker.com/legal/docker-privacy-policy/) to learn more about how the information is collected and used. 42 | -------------------------------------------------------------------------------- /build/downloader.mjs: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import axios from 'axios'; 5 | 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = path.dirname(__filename); 8 | 9 | async function fileFromUrl(url) { 10 | const response = await axios.get(url, { responseType: 'arraybuffer' }); 11 | return Buffer.from(response.data, 'binary'); 12 | } 13 | 14 | async function downloadFile(url, dest) { 15 | console.info(`Downloading ${url}...`); 16 | console.info(`Saving to ${dest}...`); 17 | try { 18 | const buffer = await fileFromUrl(url); 19 | fs.writeFileSync(dest, buffer); 20 | } catch (error) { 21 | console.error(`Error downloading file: ${error.message}`); 22 | process.exit(1); 23 | } 24 | } 25 | 26 | async function run(directory, url, file) { 27 | const cwd = path.resolve(__dirname); 28 | const buildDir = path.basename(cwd); 29 | const repoDir = cwd.replace(buildDir, ''); 30 | const installPath = path.join(repoDir, directory); 31 | 32 | if (fs.existsSync(installPath)) { 33 | if (process.env.downloader_log === 'true') { 34 | console.info(`Target folder path exists at ${installPath}. Removing`); 35 | } 36 | fs.rmSync(installPath, { recursive: true }); 37 | } 38 | 39 | fs.mkdirSync(installPath); 40 | 41 | await downloadFile(url, path.join(installPath, file)); 42 | } 43 | 44 | async function downloadSyntaxesFile() { 45 | const hclSyntaxFile = `hcl.tmGrammar.json`; 46 | const url = `https://github.com/hashicorp/syntax/releases/download/v0.7.1/${hclSyntaxFile}`; 47 | run('syntaxes', url, hclSyntaxFile); 48 | } 49 | 50 | function getPlatform() { 51 | const platform = 52 | process.env['NODE_OS'] === undefined 53 | ? process.platform 54 | : process.env['NODE_OS']; 55 | if (platform === 'win32') { 56 | return 'windows'; 57 | } 58 | return platform === 'alpine' ? 'linux' : platform; 59 | } 60 | 61 | function getArch() { 62 | const arch = 63 | process.env['NODE_ARCH'] === undefined 64 | ? process.arch 65 | : process.env['NODE_ARCH']; 66 | return arch === 'x64' ? 'amd64' : 'arm64'; 67 | } 68 | 69 | async function downloadLanguageServerBinary() { 70 | if (process.arch !== 'x64' && process.arch !== 'arm64') { 71 | console.error( 72 | `No language server binary can be found for the ${process.arch} architecture.`, 73 | ); 74 | process.exit(1); 75 | } 76 | 77 | const platform = getPlatform(); 78 | const arch = getArch(); 79 | const suffix = platform === 'windows' ? '.exe' : ''; 80 | const version = '0.10.2'; 81 | const binaryFile = `docker-language-server-${platform}-${arch}-v${version}${suffix}`; 82 | const targetFile = `docker-language-server-${platform}-${arch}${suffix}`; 83 | const url = `https://github.com/docker/docker-language-server/releases/download/v${version}/${binaryFile}`; 84 | await run('bin', url, targetFile); 85 | fs.chmodSync(`bin/${targetFile}`, 0o755); 86 | } 87 | 88 | downloadSyntaxesFile(); 89 | downloadLanguageServerBinary(); 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker", 3 | "displayName": "Docker DX", 4 | "description": "Edit smarter, ship faster with an enhanced Docker-development experience", 5 | "version": "0.8.1", 6 | "icon": "resources/docker-logo-vertical-blue.png", 7 | "license": "Apache-2.0", 8 | "engines": { 9 | "vscode": "^1.92.0" 10 | }, 11 | "publisher": "docker", 12 | "categories": [ 13 | "Programming Languages", 14 | "Linters" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/docker/vscode-extension" 19 | }, 20 | "activationEvents": [ 21 | "onLanguage:dockerbake", 22 | "onLanguage:dockercompose", 23 | "onLanguage:dockerfile" 24 | ], 25 | "main": "./dist/extension.js", 26 | "contributes": { 27 | "commands": [ 28 | { 29 | "title": "Build with Bake", 30 | "command": "dockerLspClient.bake.build", 31 | "enablement": "never" 32 | }, 33 | { 34 | "title": "Scan for CVEs with Docker Scout", 35 | "command": "docker.scout.imageScan", 36 | "enablement": "(view == dockerImages || view == vscode-containers.views.images) && viewItem == image" 37 | } 38 | ], 39 | "languages": [ 40 | { 41 | "id": "dockerbake", 42 | "filenames": [ 43 | "docker-bake.hcl", 44 | "docker-bake.override.hcl" 45 | ] 46 | } 47 | ], 48 | "grammars": [ 49 | { 50 | "language": "dockerbake", 51 | "scopeName": "source.hcl", 52 | "path": "./syntaxes/hcl.tmGrammar.json" 53 | } 54 | ], 55 | "menus": { 56 | "view/item/context": [ 57 | { 58 | "command": "docker.scout.imageScan", 59 | "when": "(view == dockerImages || view == vscode-containers.views.images) && viewItem == image", 60 | "group": "images_group_dockerdx" 61 | } 62 | ] 63 | }, 64 | "configuration": { 65 | "title": "Docker DX", 66 | "properties": { 67 | "docker.extension.enableComposeLanguageServer": { 68 | "type": "boolean", 69 | "description": "Enable Compose editing features from the Docker DX extension. Note that changing this value requires a restart of Visual Studio Code to take effect.", 70 | "markdownDescription": "Enable Compose editing features from the Docker DX extension. Note that changing this value requires a **restart** of Visual Studio Code to take effect.\n\nIf you have [Red Hat's YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml), note that both Docker DX and Red Hat's YAML extension are handling Compose files. This creates duplicate completions, hovers, and syntax errors.\n\nYou can follow the steps [here](https://github.com/docker/vscode-extension/blob/main/FAQ.md) to update your `settings.json` to turn off Compose support in the YAML extension and streamline your experience.", 71 | "default": true, 72 | "scope": "application" 73 | }, 74 | "docker.extension.dockerEngineAvailabilityPrompt": { 75 | "type": "boolean", 76 | "description": "Be notified when Docker Engine is not available.", 77 | "default": true, 78 | "scope": "application" 79 | }, 80 | "docker.lsp.telemetry": { 81 | "type": "string", 82 | "description": "Determines what telemetry is collected by Docker. If vscode.env.isTelemetryEnabled is false, then telemetry collection is disabled regardless of what has been set for this configuration value.", 83 | "enum": [ 84 | "all", 85 | "error", 86 | "off" 87 | ], 88 | "default": "all", 89 | "tags": [ 90 | "telemetry", 91 | "usesOnlineServices" 92 | ] 93 | }, 94 | "docker.lsp.debugServerPort": { 95 | "type": [ 96 | "number", 97 | "null" 98 | ], 99 | "description": "Enter the port on localhost where the language server is running, used for debugging.", 100 | "default": null, 101 | "scope": "machine-overridable" 102 | }, 103 | "docker.lsp.experimental.vulnerabilityScanning": { 104 | "type": "boolean", 105 | "description": "Enable image analysis of Dockerfiles (Experimental)", 106 | "default": true, 107 | "tags": [ 108 | "experimental" 109 | ] 110 | }, 111 | "docker.lsp.experimental.scout.criticalHighVulnerabilities": { 112 | "markdownDescription": "Determines if `critical_high_vulnerabilities` diagnostics should be shown. If `docker.lsp.experimental.vulnerabilityScanning` is false then this setting will be ignored.", 113 | "default": true, 114 | "type": "boolean", 115 | "tags": [ 116 | "experimental" 117 | ] 118 | }, 119 | "docker.lsp.experimental.scout.notPinnedDigest": { 120 | "markdownDescription": "Determines if `not_pinned_digest` diagnostics should be shown. If `docker.lsp.experimental.vulnerabilityScanning` is false then this setting will be ignored.", 121 | "default": false, 122 | "type": "boolean", 123 | "tags": [ 124 | "experimental" 125 | ] 126 | }, 127 | "docker.lsp.experimental.scout.recommendedTag": { 128 | "markdownDescription": "Determines if `recommended_tag` diagnostics should be shown. If `docker.lsp.experimental.vulnerabilityScanning` is false then this setting will be ignored.", 129 | "default": false, 130 | "type": "boolean", 131 | "tags": [ 132 | "experimental" 133 | ] 134 | }, 135 | "docker.lsp.experimental.scout.vulnerabilities": { 136 | "markdownDescription": "Determines if `vulnerabilities` diagnostics should be shown. If `docker.lsp.experimental.vulnerabilityScanning` is false then this setting will be ignored.", 137 | "default": true, 138 | "type": "boolean", 139 | "tags": [ 140 | "experimental" 141 | ] 142 | } 143 | } 144 | } 145 | }, 146 | "scripts": { 147 | "prepare": "npm run download:artifacts", 148 | "vscode:prepublish": "npm run package", 149 | "compile": "webpack", 150 | "watch": "webpack --watch", 151 | "package": "webpack --mode production --devtool hidden-source-map", 152 | "download:artifacts": "node build/downloader.mjs", 153 | "compile-tests": "tsc -p . --outDir out", 154 | "watch-tests": "tsc -p . -w --outDir out", 155 | "pretest": "npm run compile-tests && npm run compile && npm run lint", 156 | "lint": "eslint src --ext ts", 157 | "test": "extest setup-and-run './out/ui-test/*.test.js' --code_version max --code_settings test/settings.json --extensions_dir .test-extensions", 158 | "make-vsix": "vsce package", 159 | "prettier:check": "prettier --check --config .prettierrc.yml --ignore-path .prettierignore .", 160 | "prettier:fix": "prettier --write --config .prettierrc.yml --ignore-path .prettierignore ." 161 | }, 162 | "dependencies": { 163 | "@bugsnag/js": "~8.2.0", 164 | "vscode-languageclient": "9.0.1" 165 | }, 166 | "devDependencies": { 167 | "@types/chai": "^4.3.16", 168 | "@types/mocha": "^10.0.7", 169 | "@types/node": "20.x", 170 | "@types/vscode": "^1.8.8", 171 | "@typescript-eslint/eslint-plugin": "^7.14.1", 172 | "@typescript-eslint/parser": "^7.11.0", 173 | "@vscode/test-cli": "^0.0.9", 174 | "@vscode/test-electron": "^2.4.0", 175 | "@vscode/vsce": "^3.3.0", 176 | "axios": "^1.8.4", 177 | "chai": "^4.5.0", 178 | "eslint": "^8.57.0", 179 | "prettier": "^3.5.3", 180 | "ts-loader": "^9.5.1", 181 | "typescript": "^5.4.5", 182 | "vscode-extension-tester": "^8.13.0", 183 | "webpack": "^5.92.1", 184 | "webpack-cli": "^5.1.4" 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /resources/docker-logo-vertical-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/docker-logo-vertical-blue.png -------------------------------------------------------------------------------- /resources/readme/docker-bake-editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/readme/docker-bake-editing.png -------------------------------------------------------------------------------- /resources/readme/docker-bake-inline-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/readme/docker-bake-inline-completion.png -------------------------------------------------------------------------------- /resources/readme/docker-compose-code-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/readme/docker-compose-code-completion.png -------------------------------------------------------------------------------- /resources/readme/docker-compose-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/readme/docker-compose-hover.png -------------------------------------------------------------------------------- /resources/readme/dockerfile-problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/resources/readme/dockerfile-problems.png -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'child_process'; 2 | import { homedir } from 'os'; 3 | import * as vscode from 'vscode'; 4 | import Bugsnag, { Event, Session } from '@bugsnag/js'; 5 | import { 6 | activateDockerNativeLanguageClient, 7 | getNativeClient, 8 | } from './utils/lsp/lsp'; 9 | import { DidChangeConfigurationNotification } from 'vscode-languageclient/node'; 10 | import { 11 | EVENT_CLIENT_HEARTBEAT, 12 | notifyBugsnag, 13 | publishTelemetry, 14 | queueTelemetryEvent, 15 | } from './telemetry/client'; 16 | import { checkForDockerEngine } from './utils/monitor'; 17 | import { spawnDockerCommand } from './utils/spawnDockerCommand'; 18 | import { 19 | disableEnableComposeLanguageServer, 20 | getExtensionSetting, 21 | inspectExtensionSetting, 22 | } from './utils/settings'; 23 | import { redact } from './telemetry/filter'; 24 | 25 | export const BakeBuildCommandId = 'dockerLspClient.bake.build'; 26 | export const ScoutImageScanCommandId = 'docker.scout.imageScan'; 27 | 28 | export let extensionVersion: string; 29 | 30 | const errorRegExp = new RegExp('(E[A-Z]+)'); 31 | 32 | function registerCommands(ctx: vscode.ExtensionContext) { 33 | registerCommand(ctx, BakeBuildCommandId, async (commandArgs: any) => { 34 | const result = await spawnDockerCommand( 35 | 'buildx', 36 | ['bake', '--help'], 37 | "Bake is not available. To access Docker Bake's features, install Docker Desktop.", 38 | ); 39 | const args = ['buildx', 'bake']; 40 | 41 | if (commandArgs['call'] === 'print') { 42 | args.push('--print'); 43 | args.push(commandArgs['target']); 44 | } else { 45 | args.push('--call'); 46 | args.push(commandArgs['call']); 47 | args.push(commandArgs['target']); 48 | } 49 | 50 | const task = new vscode.Task( 51 | { type: 'shell' }, 52 | vscode.TaskScope.Workspace, 53 | 'docker buildx bake', 54 | 'docker-vscode-extension', 55 | new vscode.ShellExecution('docker', args, { 56 | cwd: commandArgs['cwd'], 57 | }), 58 | ); 59 | vscode.tasks.executeTask(task); 60 | return result; 61 | }); 62 | 63 | registerCommand(ctx, ScoutImageScanCommandId, async (args) => { 64 | const result = spawnDockerCommand( 65 | 'scout', 66 | [], 67 | "Docker Scout is not available. To access Docker Scout's features, install Docker Desktop.", 68 | ); 69 | const options: vscode.ShellExecutionOptions = {}; 70 | if ( 71 | vscode.workspace.workspaceFolders === undefined || 72 | vscode.workspace.workspaceFolders.length === 0 73 | ) { 74 | options.cwd = homedir(); 75 | } 76 | const task = new vscode.Task( 77 | { type: 'shell' }, 78 | vscode.TaskScope.Workspace, 79 | 'docker scout', 80 | 'docker scout', 81 | new vscode.ShellExecution( 82 | 'docker', 83 | ['scout', 'cves', args.fullTag], 84 | options, 85 | ), 86 | ); 87 | vscode.tasks.executeTask(task); 88 | return result; 89 | }); 90 | } 91 | 92 | function registerCommand( 93 | ctx: vscode.ExtensionContext, 94 | id: string, 95 | commandCallback: (...args: any[]) => Promise, 96 | ): void { 97 | ctx.subscriptions.push( 98 | vscode.commands.registerCommand(id, async (args) => { 99 | const result = await commandCallback(args); 100 | queueTelemetryEvent('client_user_action', false, { 101 | action_id: id, 102 | result, 103 | }); 104 | }), 105 | ); 106 | } 107 | 108 | const activateDockerLSP = async (ctx: vscode.ExtensionContext) => { 109 | if (await activateDockerNativeLanguageClient(ctx)) { 110 | getNativeClient() 111 | .start() 112 | .then( 113 | () => {}, 114 | (reject) => { 115 | notifyBugsnag(reject); 116 | if (typeof reject === 'string') { 117 | const matches = reject.match(errorRegExp); 118 | if (matches !== null && matches.length > 0) { 119 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 120 | error_function: 'DockerLanguageClient.start', 121 | message: matches[0], 122 | }); 123 | return; 124 | } 125 | } else if (reject.code !== undefined) { 126 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 127 | error_function: 'DockerLanguageClient.start', 128 | message: String(reject.code), 129 | }); 130 | return; 131 | } 132 | queueTelemetryEvent('client_heartbeat', true, { 133 | error_function: 'DockerLanguageClient.start', 134 | message: 'unrecognized', 135 | }); 136 | }, 137 | ); 138 | } 139 | }; 140 | 141 | async function toggleComposeLanguageServerSetting(): Promise { 142 | const setting = inspectExtensionSetting('enableComposeLanguageServer'); 143 | if (vscode.extensions.getExtension('redhat.vscode-yaml') !== undefined) { 144 | // if Red Hat's YAML extension is installed, we will auto-disable 145 | // the Compose language server features to prevent duplicates 146 | if (setting !== undefined && setting.globalValue === undefined) { 147 | await disableEnableComposeLanguageServer(); 148 | return 'false'; 149 | } 150 | } 151 | return setting === undefined ? 'undefined' : String(setting.globalValue); 152 | } 153 | 154 | export async function activate(ctx: vscode.ExtensionContext) { 155 | const composeSetting = await toggleComposeLanguageServerSetting(); 156 | extensionVersion = String(ctx.extension.packageJSON.version); 157 | Bugsnag.start({ 158 | apiKey: 'c5b75b41a335069129747c7196ec207a', 159 | appType: 'vscode', 160 | appVersion: extensionVersion, 161 | autoDetectErrors: false, 162 | hostname: vscode.env.machineId, 163 | metadata: { 164 | system: { 165 | os: process.platform === 'win32' ? 'windows' : process.platform, 166 | arch: process.arch === 'x64' ? 'amd64' : process.arch, 167 | app_host: vscode.env.appHost, 168 | app_name: vscode.env.appName, 169 | machine_id: vscode.env.machineId, 170 | client_session: vscode.env.sessionId, 171 | }, 172 | }, 173 | onError: (event: Event): void => { 174 | for (let i = 0; i < event.errors.length; i++) { 175 | event.errors[i].errorMessage = redact(event.errors[i].errorMessage); 176 | for (let j = 0; j < event.errors[i].stacktrace.length; j++) { 177 | event.errors[i].stacktrace[j].file = redact( 178 | event.errors[i].stacktrace[j].file, 179 | ); 180 | } 181 | } 182 | event.device.hostname = vscode.env.machineId; 183 | }, 184 | onSession: (session: Session): void | boolean => { 185 | session.id = vscode.env.sessionId; 186 | }, 187 | sendCode: false, 188 | }); 189 | 190 | recordVersionTelemetry(composeSetting); 191 | registerCommands(ctx); 192 | listenForOpenedDocuments(); 193 | activateDockerLSP(ctx); 194 | listenForConfigurationChanges(ctx); 195 | } 196 | 197 | async function listenForOpenedDocument( 198 | languageId: string, 199 | shouldReact: () => boolean, 200 | fileOpened: () => Promise, 201 | ) { 202 | let reacted = false; 203 | for (const document of vscode.workspace.textDocuments) { 204 | if (document.languageId === languageId && document.uri.scheme === 'file') { 205 | // if the expected file type is currently opened, react if necessary 206 | if (shouldReact()) { 207 | await fileOpened(); 208 | } 209 | reacted = true; 210 | break; 211 | } 212 | } 213 | 214 | if (!reacted) { 215 | // no documents for the expected file type is currently opened, 216 | // listen for one being opened and react if necessary 217 | const disposable = vscode.workspace.onDidOpenTextDocument((document) => { 218 | if ( 219 | document.languageId === languageId && 220 | document.uri.scheme === 'file' 221 | ) { 222 | if (shouldReact()) { 223 | fileOpened(); 224 | } 225 | disposable.dispose(); 226 | } 227 | }); 228 | } 229 | } 230 | 231 | /** 232 | * Listen for Dockerfiles files being opened. 233 | * 234 | * If a Dockerfile is opened, we want to check to see if a Docker Engine 235 | * is available. If not, we will prompt the user to install Docker 236 | * Desktop or open Docker Desktop. 237 | */ 238 | function listenForOpenedDocuments(): void { 239 | listenForOpenedDocument( 240 | 'dockerfile', 241 | () => getExtensionSetting('dockerEngineAvailabilityPrompt') === true, 242 | checkForDockerEngine, 243 | ); 244 | } 245 | 246 | function listenForConfigurationChanges(ctx: vscode.ExtensionContext) { 247 | ctx.subscriptions.push( 248 | vscode.env.onDidChangeTelemetryEnabled(() => { 249 | const client = getNativeClient(); 250 | if (client === undefined || !client.isRunning()) { 251 | return; 252 | } 253 | 254 | client.sendNotification(DidChangeConfigurationNotification.type, { 255 | settings: ['docker.lsp.telemetry'], 256 | }); 257 | }), 258 | ); 259 | 260 | ctx.subscriptions.push( 261 | vscode.workspace.onDidChangeConfiguration( 262 | (e: vscode.ConfigurationChangeEvent) => { 263 | const client = getNativeClient(); 264 | if (client === undefined || !client.isRunning()) { 265 | return; 266 | } 267 | 268 | const configurations = [ 269 | 'docker.lsp.telemetry', 270 | 'docker.lsp.experimental.vulnerabilityScanning', 271 | 'docker.lsp.experimental.scout.criticalHighVulnerabilities', 272 | 'docker.lsp.experimental.scout.notPinnedDigest', 273 | 'docker.lsp.experimental.scout.recommendedTag', 274 | 'docker.lsp.experimental.scout.vulnerabilities', 275 | ]; 276 | const filtered = configurations.filter((config) => 277 | e.affectsConfiguration(config), 278 | ); 279 | if (filtered.length > 0) { 280 | client.sendNotification(DidChangeConfigurationNotification.type, { 281 | settings: filtered, 282 | }); 283 | } 284 | }, 285 | ), 286 | ); 287 | } 288 | 289 | function recordVersionTelemetry(composeSetting: string) { 290 | const installedExtensions = vscode.extensions.all 291 | .filter((extension) => { 292 | return ( 293 | extension.id === 'ms-azuretools.vscode-docker' || 294 | extension.id === 'ms-azuretools.vscode-containers' || 295 | extension.id === 'redhat.vscode-yaml' 296 | ); 297 | }) 298 | .map((extension) => extension.id); 299 | let versionString: string | null = null; 300 | const process = spawn('docker', ['-v']); 301 | process.stdout.on('data', (data) => { 302 | if (versionString === null) { 303 | versionString = ''; 304 | } 305 | versionString += String(data).trim(); 306 | }); 307 | process.on('error', () => { 308 | // this happens if docker cannot be found on the PATH 309 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, false, { 310 | docker_version: 'spawn docker -v failed', 311 | installedExtensions, 312 | settings: { 313 | 'docker.extension.enableComposeLanguageServer': composeSetting, 314 | }, 315 | }); 316 | publishTelemetry(); 317 | }); 318 | process.on('exit', (code) => { 319 | if (code === 0) { 320 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, false, { 321 | docker_version: String(versionString), 322 | installedExtensions, 323 | settings: { 324 | 'docker.extension.enableComposeLanguageServer': composeSetting, 325 | }, 326 | }); 327 | } else { 328 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, false, { 329 | docker_version: String(code), 330 | installedExtensions, 331 | settings: { 332 | 'docker.extension.enableComposeLanguageServer': composeSetting, 333 | }, 334 | }); 335 | } 336 | publishTelemetry(); 337 | }); 338 | } 339 | 340 | export async function deactivate() { 341 | const client = getNativeClient(); 342 | if (client !== undefined) { 343 | client.stop(); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/telemetry/client.ts: -------------------------------------------------------------------------------- 1 | import https from 'https'; 2 | import * as process from 'process'; 3 | import * as vscode from 'vscode'; 4 | import { extensionVersion } from '../extension'; 5 | import Bugsnag from '@bugsnag/js'; 6 | 7 | export const EVENT_CLIENT_HEARTBEAT = 'client_heartbeat'; 8 | 9 | interface TelemetryRecord { 10 | event: string; 11 | source: string; 12 | event_timestamp: number; 13 | properties: { [key: string]: boolean | number | string | object | undefined }; 14 | } 15 | 16 | const events: TelemetryRecord[] = []; 17 | 18 | function shouldReportToBugsnag(): boolean { 19 | if (!vscode.env.isTelemetryEnabled) { 20 | return false; 21 | } 22 | 23 | const config = vscode.workspace 24 | .getConfiguration('docker.lsp') 25 | .get('telemetry'); 26 | return config === 'error' || config === 'all'; 27 | } 28 | 29 | export function notifyBugsnag(err: any): void { 30 | if (shouldReportToBugsnag()) { 31 | Bugsnag.notify(err); 32 | } 33 | } 34 | 35 | export function queueTelemetryEvent( 36 | event: string, 37 | error: boolean, 38 | properties: { [key: string]: boolean | number | string | object | undefined }, 39 | ) { 40 | if (!vscode.env.isTelemetryEnabled) { 41 | return; 42 | } 43 | 44 | const config = vscode.workspace 45 | .getConfiguration('docker.lsp') 46 | .get('telemetry'); 47 | if (config === 'off') { 48 | return; 49 | } 50 | 51 | if (config === 'error' && !error) { 52 | return; 53 | } 54 | 55 | properties.os = process.platform === 'win32' ? 'windows' : process.platform; 56 | properties.arch = process.arch === 'x64' ? 'amd64' : process.arch; 57 | properties.app_host = vscode.env.appHost; 58 | properties.app_name = vscode.env.appName; 59 | properties.client_name = 'vscode'; 60 | properties.machine_id = vscode.env.machineId; 61 | properties.client_session = vscode.env.sessionId; 62 | properties.extension_version = extensionVersion; 63 | 64 | events.push({ 65 | event, 66 | event_timestamp: Date.now(), 67 | properties, 68 | source: 'editor_integration', 69 | }); 70 | } 71 | 72 | export function publishTelemetry(): void { 73 | setTimeout(publishTelemetry, 30000); 74 | const records = events.splice(0, 500); 75 | if (records.length === 0) { 76 | return; 77 | } 78 | 79 | const request = https.request( 80 | { 81 | host: 'api.docker.com', 82 | path: '/events/v1/track', 83 | method: 'POST', 84 | headers: { 85 | 'Content-Type': 'application/json', 86 | 'User-Agent': 'docker-vscode-extension', 87 | 'x-api-key': 'eIxc3dSmud2vuJRKiq9hJ6wORVWfoLxp1nqb4qXz', 88 | }, 89 | }, 90 | (response) => { 91 | response.on('data', () => {}); 92 | response.on('end', () => {}); 93 | }, 94 | ); 95 | request.write(JSON.stringify({ records })); 96 | request.end(); 97 | } 98 | -------------------------------------------------------------------------------- /src/telemetry/filter.ts: -------------------------------------------------------------------------------- 1 | function redactFilePath(path: string): string { 2 | const split = path.split(/[/\\]/); 3 | if (split.length < 2) { 4 | return path; 5 | } 6 | const redacted = split.pop(); 7 | if (redacted === undefined) { 8 | return ''; 9 | } 10 | return `REDACTED/${redacted}`; 11 | } 12 | 13 | export function redact(s: string): string { 14 | const words = s.split(/\s+/); 15 | for (let i = 0; i < words.length; i++) { 16 | words[i] = redactFilePath(words[i]); 17 | } 18 | return words.join(' '); 19 | } 20 | -------------------------------------------------------------------------------- /src/ui-test/dockerbakeDiagnostics.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BottomBarPanel, 3 | EditorView, 4 | MarkerType, 5 | ProblemsView, 6 | VSBrowser, 7 | } from 'vscode-extension-tester'; 8 | import * as path from 'path'; 9 | import { expect } from 'chai'; 10 | 11 | describe('Docker Bake', function () { 12 | let bottomBar: BottomBarPanel; 13 | 14 | before(async function () { 15 | bottomBar = new BottomBarPanel(); 16 | await bottomBar.toggle(true); 17 | }); 18 | 19 | after(async function () { 20 | await bottomBar.toggle(false); 21 | }); 22 | 23 | describe('Problems', function () { 24 | let view: ProblemsView; 25 | 26 | async function problemsExist(view: ProblemsView) { 27 | const markers = await view.getAllVisibleMarkers(MarkerType.Any); 28 | return markers.length > 0; 29 | } 30 | 31 | before(async function () { 32 | this.timeout(30000); 33 | 34 | view = await bottomBar.openProblemsView(); 35 | 36 | await VSBrowser.instance.openResources( 37 | path.join('test', 'resources', 'docker-bake.hcl'), 38 | ); 39 | 40 | await view.getDriver().wait(async function () { 41 | return await problemsExist(view); 42 | }, 15000); 43 | }); 44 | 45 | after(async function () { 46 | await new EditorView().closeAllEditors(); 47 | }); 48 | 49 | it('Bake file has an error about not being able to find an ARG', async function () { 50 | const errors = await view.getAllVisibleMarkers(MarkerType.Error); 51 | expect(errors.length).equals(1); 52 | 53 | const errorText = await errors[0].getText(); 54 | expect(errorText).equal( 55 | "Error: 'var' not defined as an ARG in your Dockerfile at line 3 and character 9. generated by Docker DX (docker-language-server)", 56 | ); 57 | }); 58 | 59 | it('Bake file has no warnings', async function () { 60 | const warnings = await view.getAllVisibleMarkers(MarkerType.Warning); 61 | expect(warnings).is.empty; 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/ui-test/dockerfileDiagnostics.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BottomBarPanel, 3 | EditorView, 4 | MarkerType, 5 | ProblemsView, 6 | TextEditor, 7 | VSBrowser, 8 | } from 'vscode-extension-tester'; 9 | import * as fs from 'fs'; 10 | import * as path from 'path'; 11 | import { expect } from 'chai'; 12 | 13 | describe('Dockerfile', function () { 14 | let bottomBar: BottomBarPanel; 15 | 16 | before(async function () { 17 | bottomBar = new BottomBarPanel(); 18 | await bottomBar.toggle(true); 19 | }); 20 | 21 | after(async function () { 22 | await bottomBar.toggle(false); 23 | }); 24 | 25 | describe('Problems', function () { 26 | let editorView: EditorView; 27 | let view: ProblemsView; 28 | 29 | async function problemsExist(view: ProblemsView) { 30 | const markers = await view.getAllVisibleMarkers(MarkerType.Any); 31 | return markers.length > 0; 32 | } 33 | 34 | before(async function () { 35 | this.timeout(30000); 36 | 37 | editorView = new EditorView(); 38 | view = await bottomBar.openProblemsView(); 39 | 40 | const dockerfilePath = path.join('test', 'resources', 'Dockerfile'); 41 | fs.writeFileSync(dockerfilePath, 'FROM scratch\nENTRYPOINT ""\n'); 42 | 43 | await VSBrowser.instance.openResources(dockerfilePath); 44 | 45 | await view.getDriver().wait(async function () { 46 | return await problemsExist(view); 47 | }, 15000); 48 | }); 49 | 50 | after(async function () { 51 | await editorView.closeAllEditors(); 52 | 53 | const dockerfilePath = path.join('test', 'resources', 'Dockerfile'); 54 | fs.writeFileSync(dockerfilePath, ''); 55 | }); 56 | 57 | it('Dockerfile has no errors', async function () { 58 | const errors = await view.getAllVisibleMarkers(MarkerType.Error); 59 | expect(errors).is.empty; 60 | }); 61 | 62 | it('Dockerfile has a JSONArgsRecommended warning', async function () { 63 | const warnings = await view.getAllVisibleMarkers(MarkerType.Warning); 64 | expect(warnings.length).equals(1); 65 | 66 | const warningText = await warnings[0].getText(); 67 | expect(warningText).equal( 68 | 'Warning: JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals (JSON arguments recommended for ENTRYPOINT to prevent unintended behavior related to OS signals) at line 2 and character 1. generated by Docker DX (docker-language-server)', 69 | ); 70 | }); 71 | 72 | it('Dockerfile modified with a FromAsCasing warning', async function () { 73 | this.timeout(30000); 74 | 75 | const editor = (await editorView.openEditor('Dockerfile')) as TextEditor; 76 | await editor.clearText(); 77 | await editor.setText('FROM scratch\n'); 78 | await editor.typeTextAt(2, 1, 'FROM scratch as base'); 79 | 80 | await view.getDriver().wait(async function () { 81 | return await problemsExist(view); 82 | }, 15000); 83 | 84 | const warnings = await view.getAllVisibleMarkers(MarkerType.Warning); 85 | expect(warnings.length).equals(1); 86 | 87 | const warningText = await warnings[0].getText(); 88 | expect(warningText).equal( 89 | "Warning: The 'as' keyword should match the case of the 'from' keyword ('as' and 'FROM' keywords' casing do not match) at line 2 and character 1. generated by Docker DX (docker-language-server)", 90 | ); 91 | 92 | await editor.save(); 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/utils/lsp/languageClient.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { 3 | LanguageClient, 4 | InitializeParams, 5 | ErrorHandler, 6 | Message, 7 | ErrorHandlerResult, 8 | CloseHandlerResult, 9 | } from 'vscode-languageclient/node'; 10 | import { BakeBuildCommandId } from '../../extension'; 11 | import { getTelemetryValue } from './lsp'; 12 | import { 13 | EVENT_CLIENT_HEARTBEAT, 14 | notifyBugsnag, 15 | queueTelemetryEvent, 16 | } from '../../telemetry/client'; 17 | 18 | export class DockerLanguageClient extends LanguageClient { 19 | protected fillInitializeParams(params: InitializeParams): void { 20 | super.fillInitializeParams(params); 21 | const dockerConfiguration = vscode.workspace.getConfiguration('docker.lsp'); 22 | const extensionConfiguration = 23 | vscode.workspace.getConfiguration('docker.extension'); 24 | params.initializationOptions = { 25 | dockercomposeExperimental: { 26 | composeSupport: extensionConfiguration.get( 27 | 'enableComposeLanguageServer', 28 | ), 29 | }, 30 | dockerfileExperimental: { 31 | removeOverlappingIssues: true, 32 | }, 33 | telemetry: getTelemetryValue(dockerConfiguration.get('telemetry')), 34 | }; 35 | params.capabilities.experimental = { 36 | dockerLanguageServerCapabilities: { 37 | clientInfoExtras: { 38 | client_name: 'vscode', 39 | client_session: vscode.env.sessionId, 40 | }, 41 | commands: [BakeBuildCommandId], 42 | }, 43 | }; 44 | } 45 | 46 | public error( 47 | message: string, 48 | data?: any, 49 | showNotification?: boolean | 'force', 50 | ): void { 51 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 52 | message: filterMessage(message), 53 | error_function: 'DockerLanguageClient.error', 54 | show_notification: showNotification, 55 | }); 56 | return super.error(message, data, showNotification); 57 | } 58 | 59 | public createDefaultErrorHandler(maxRestartCount?: number): ErrorHandler { 60 | const handler = super.createDefaultErrorHandler(maxRestartCount); 61 | return { 62 | error( 63 | error: Error, 64 | message: Message | undefined, 65 | count: number | undefined, 66 | ): ErrorHandlerResult | Promise { 67 | const result: any = handler.error(error, message, count); 68 | if (result.action !== undefined) { 69 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 70 | error_function: 'ErrorHandler.error', 71 | count: count, 72 | action: result.action, 73 | }); 74 | } 75 | notifyBugsnag(error); 76 | return result; 77 | }, 78 | 79 | closed(): CloseHandlerResult | Promise { 80 | const result: any = handler.closed(); 81 | if (result.action !== undefined) { 82 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 83 | message: filterMessage(result.message), 84 | error_function: 'ErrorHandler.closed', 85 | action: result.action, 86 | }); 87 | } 88 | return result; 89 | }, 90 | }; 91 | } 92 | } 93 | 94 | function filterMessage(message: string): string { 95 | if ( 96 | message === 97 | 'The Docker Language Server server crashed 5 times in the last 3 minutes. The server will not be restarted. See the output for more information.' || 98 | message === 99 | "Docker Language Server client: couldn't create connection to server." || 100 | message === 101 | 'Connection to server got closed. Server will not be restarted.' || 102 | message === 'Server initialization failed.' || 103 | message === 'Connection to server got closed. Server will restart.' || 104 | message === 'Restarting server failed' || 105 | message === 'Stopping server failed' 106 | ) { 107 | return message; 108 | } 109 | return 'unrecognized'; 110 | } 111 | -------------------------------------------------------------------------------- /src/utils/lsp/lsp.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as net from 'net'; 3 | import * as path from 'path'; 4 | import * as process from 'process'; 5 | import * as vscode from 'vscode'; 6 | import { 7 | LanguageClient, 8 | LanguageClientOptions, 9 | ServerOptions, 10 | TransportKind, 11 | RevealOutputChannelOn, 12 | StreamInfo, 13 | } from 'vscode-languageclient/node'; 14 | import { DockerLanguageClient } from './languageClient'; 15 | import { InlineCompletionItemFeature } from 'vscode-languageclient/lib/common/inlineCompletion'; 16 | import { 17 | EVENT_CLIENT_HEARTBEAT, 18 | queueTelemetryEvent, 19 | } from '../../telemetry/client'; 20 | 21 | let nativeClient: LanguageClient; 22 | 23 | export const getNativeClient = () => nativeClient; 24 | 25 | function isSupported(): boolean { 26 | if (process.arch === 'x64' || process.arch === 'arm64') { 27 | switch (process.platform) { 28 | case 'win32': 29 | case 'linux': 30 | case 'darwin': 31 | return true; 32 | } 33 | } 34 | return false; 35 | } 36 | 37 | function getBinaryPath(ctx: vscode.ExtensionContext): string | null { 38 | if (!isSupported()) { 39 | return null; 40 | } 41 | 42 | // Windows Intel/AMD 64-bit binary would be named docker-language-server-windows-amd64.exe 43 | const platform = process.platform === 'win32' ? 'windows' : process.platform; 44 | const arch = process.arch === 'x64' ? 'amd64' : process.arch; 45 | const extension = process.platform === 'win32' ? '.exe' : ''; 46 | return path.join( 47 | ctx.extensionPath, 48 | 'bin', 49 | `docker-language-server-${platform}-${arch}${extension}`, 50 | ); 51 | } 52 | 53 | async function checkForDebugLspServer(): Promise { 54 | const lspConfig = vscode.workspace.getConfiguration('docker.lsp'); 55 | const port = lspConfig.get('debugServerPort'); 56 | if (port === undefined || port === null || port <= 0) { 57 | return null; 58 | } 59 | 60 | return new Promise((resolve) => { 61 | try { 62 | const socket = net.connect({ port: port }); 63 | socket.on('connect', () => { 64 | resolve(port); 65 | socket.destroy(); 66 | }); 67 | socket.on('error', () => { 68 | resolve(null); 69 | }); 70 | } catch (error) { 71 | resolve(null); 72 | } 73 | }); 74 | } 75 | 76 | async function getServerOptions( 77 | ctx: vscode.ExtensionContext, 78 | ): Promise { 79 | const port = await checkForDebugLspServer(); 80 | if (port !== null) { 81 | return () => { 82 | // alter the port here to point to the locally launched instance of the language server 83 | const socket = net.connect({ port: Number(port) }); 84 | const result: StreamInfo = { 85 | writer: socket, 86 | reader: socket, 87 | }; 88 | return Promise.resolve(result); 89 | }; 90 | } 91 | 92 | const binaryPath = getBinaryPath(ctx); 93 | if (binaryPath !== null) { 94 | await chmod(binaryPath); 95 | return { 96 | command: binaryPath, 97 | transport: TransportKind.stdio, 98 | args: ['start'], 99 | }; 100 | } 101 | return null; 102 | } 103 | 104 | async function chmod(binaryPath: string): Promise { 105 | return new Promise((resolve) => { 106 | fs.access(binaryPath, fs.constants.X_OK, (accessErr) => { 107 | if (accessErr !== null) { 108 | fs.chmod(binaryPath, 0o755, (chmodErr) => { 109 | if (chmodErr !== null) { 110 | queueTelemetryEvent(EVENT_CLIENT_HEARTBEAT, true, { 111 | error_function: 'chmod', 112 | code: chmodErr.code, 113 | errno: chmodErr.errno, 114 | }); 115 | } 116 | resolve(); 117 | }); 118 | } else { 119 | resolve(); 120 | } 121 | }); 122 | }); 123 | } 124 | 125 | /** 126 | * Returns the telemetry setting that should be used for the extension and the Docker Language Server. 127 | * If the user has telemetry disabled then "off" regardless of what the Docker telemetry setting has been set to. 128 | */ 129 | export function getTelemetryValue( 130 | configurationValue: string | undefined, 131 | ): 'all' | 'error' | 'off' { 132 | if (!vscode.env.isTelemetryEnabled) { 133 | return 'off'; 134 | } 135 | 136 | switch (configurationValue) { 137 | case 'all': 138 | case 'error': 139 | case 'off': 140 | return configurationValue; 141 | } 142 | return 'all'; 143 | } 144 | 145 | /** 146 | * Registers a code actions provider for Dockerfiles so that diagnostics 147 | * from Scout will have a corresponding code action for opening the 148 | * Settings editor to disable the warning. 149 | */ 150 | function registerSettingsToggleCodeActions() { 151 | vscode.languages.registerCodeActionsProvider('dockerfile', { 152 | provideCodeActions(_document, _range, ctx) { 153 | const filtered = ctx.diagnostics.filter((diagnostic) => { 154 | if (diagnostic.source === 'Docker DX (docker-language-server)') { 155 | let code = diagnostic.code; 156 | if (typeof code === 'object' && code.value !== undefined) { 157 | code = code.value; 158 | } 159 | switch (code) { 160 | case 'critical_high_vulnerabilities': 161 | case 'not_pinned_digest': 162 | case 'recommended_tag': 163 | case 'vulnerabilities': 164 | return true; 165 | } 166 | } 167 | return false; 168 | }); 169 | return filtered.map((diagnostic) => { 170 | let code = diagnostic.code; 171 | if (typeof code === 'object' && code.value !== undefined) { 172 | code = code.value; 173 | } 174 | const command: vscode.Command = { 175 | command: 'workbench.action.openSettings', 176 | title: `Ignore ${code} errors`, 177 | }; 178 | switch (code) { 179 | case 'critical_high_vulnerabilities': 180 | command.arguments = [ 181 | 'docker.lsp.experimental.scout.criticalHighVulnerabilities', 182 | ]; 183 | break; 184 | case 'not_pinned_digest': 185 | command.arguments = [ 186 | 'docker.lsp.experimental.scout.notPinnedDigest', 187 | ]; 188 | break; 189 | case 'recommended_tag': 190 | command.arguments = [ 191 | 'docker.lsp.experimental.scout.recommendedTag', 192 | ]; 193 | break; 194 | case 'vulnerabilities': 195 | command.arguments = [ 196 | 'docker.lsp.experimental.scout.vulnerabilities', 197 | ]; 198 | break; 199 | } 200 | return command; 201 | }); 202 | }, 203 | }); 204 | } 205 | 206 | async function createNative(ctx: vscode.ExtensionContext): Promise { 207 | const clientOptions: LanguageClientOptions = { 208 | progressOnInitialization: true, 209 | outputChannel: vscode.window.createOutputChannel('Docker Language Server'), 210 | revealOutputChannelOn: RevealOutputChannelOn.Never, 211 | markdown: { 212 | isTrusted: false, 213 | supportHtml: true, 214 | }, 215 | middleware: { 216 | handleDiagnostics(uri, diagnostics, next) { 217 | diagnostics.map((diagnostic) => { 218 | diagnostic.source = `Docker DX (${diagnostic.source})`; 219 | }); 220 | next(uri, diagnostics); 221 | }, 222 | 223 | workspace: { 224 | configuration: (params, token, next) => { 225 | const result = next(params, token) as any[]; 226 | return result.map((value) => { 227 | value.telemetry = getTelemetryValue(value.telemetry); 228 | return value; 229 | }); 230 | }, 231 | }, 232 | }, 233 | }; 234 | 235 | clientOptions.documentSelector = [ 236 | { scheme: 'file', language: 'dockerfile' }, 237 | { scheme: 'file', language: 'dockerbake' }, 238 | { scheme: 'file', language: 'dockercompose' }, 239 | ]; 240 | 241 | const serverOptions = await getServerOptions(ctx); 242 | if (serverOptions === null) { 243 | return false; 244 | } 245 | 246 | nativeClient = new DockerLanguageClient( 247 | 'dockerLanguageClient', 248 | 'Docker Language Server', 249 | serverOptions, 250 | clientOptions, 251 | ); 252 | nativeClient.registerFeature(new InlineCompletionItemFeature(nativeClient)); 253 | return true; 254 | } 255 | 256 | export async function activateDockerNativeLanguageClient( 257 | ctx: vscode.ExtensionContext, 258 | ): Promise { 259 | if (await createNative(ctx)) { 260 | registerSettingsToggleCodeActions(); 261 | ctx.subscriptions.push(nativeClient); 262 | return true; 263 | } 264 | return false; 265 | } 266 | -------------------------------------------------------------------------------- /src/utils/monitor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | promptOpenDockerDesktop, 3 | promptInstallDesktop, 4 | promptUnauthenticatedDesktop, 5 | } from './prompt'; 6 | import { isDockerDesktopInstalled } from './os'; 7 | import { getExtensionSetting } from './settings'; 8 | import { spawnDockerCommand } from './spawnDockerCommand'; 9 | 10 | enum DockerEngineStatus { 11 | Unavailable, 12 | Unauthenticated, 13 | Available, 14 | } 15 | 16 | /** 17 | * Checks if a Docker Engine is available. If not, prompts the user to 18 | * either install or open Docker Desktop. 19 | */ 20 | export async function checkForDockerEngine(): Promise { 21 | if (!getExtensionSetting('dockerEngineAvailabilityPrompt')) { 22 | return; 23 | } 24 | 25 | const status = await checkDockerStatus(); 26 | if (status === DockerEngineStatus.Unavailable) { 27 | if (await isDockerDesktopInstalled()) { 28 | promptOpenDockerDesktop(); 29 | } else { 30 | promptInstallDesktop(); 31 | } 32 | } else if (status === DockerEngineStatus.Unauthenticated) { 33 | promptUnauthenticatedDesktop(); 34 | } 35 | } 36 | 37 | /** 38 | * Verify and returns whether a Docker daemon is working and accessible. 39 | */ 40 | function checkDockerStatus(): Promise { 41 | return new Promise((resolve) => { 42 | let output = ''; 43 | spawnDockerCommand('ps', [], undefined, { 44 | onError: () => resolve(DockerEngineStatus.Unavailable), 45 | onStderr: (chunk) => { 46 | output += String(chunk); 47 | }, 48 | onExit: (code) => { 49 | if (code === 0) { 50 | return resolve(DockerEngineStatus.Available); 51 | } 52 | if ( 53 | output.includes('Sign-in enforcement is enabled') || 54 | output.includes( 55 | 'request returned Internal Server Error for API route and version', 56 | ) 57 | ) { 58 | return resolve(DockerEngineStatus.Unauthenticated); 59 | } 60 | return resolve(DockerEngineStatus.Unavailable); 61 | }, 62 | }); 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /src/utils/os.ts: -------------------------------------------------------------------------------- 1 | import { access } from 'fs'; 2 | import { spawnDockerCommand } from './spawnDockerCommand'; 3 | 4 | export function getDockerDesktopPath(): string { 5 | switch (process.platform) { 6 | case 'win32': 7 | return 'C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe'; 8 | case 'darwin': 9 | return '/Applications/Docker.app'; 10 | } 11 | return '/opt/docker-desktop/bin/com.docker.backend'; 12 | } 13 | 14 | export async function isDockerDesktopInstalled(): Promise { 15 | return new Promise((resolve) => { 16 | spawnDockerCommand('desktop', ['version'], undefined, { 17 | onError: () => resolve(false), 18 | 19 | onExit: (code) => { 20 | if (code === 0) { 21 | return resolve(true); 22 | } 23 | 24 | access(getDockerDesktopPath(), (err) => { 25 | resolve(err === null); 26 | }); 27 | }, 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/prompt.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { spawn } from 'child_process'; 3 | import { disableDockerEngineAvailabilityPrompt } from './settings'; 4 | import { getDockerDesktopPath } from './os'; 5 | 6 | /** 7 | * Prompts the user to login to Docker Desktop. 8 | */ 9 | export async function promptUnauthenticatedDesktop(): Promise { 10 | const response = await vscode.window.showInformationMessage( 11 | 'Docker is not running. To get help with your Dockerfile, sign in to Docker Desktop.', 12 | "Don't show again", 13 | ); 14 | if (response === "Don't show again") { 15 | await disableDockerEngineAvailabilityPrompt(); 16 | } 17 | } 18 | 19 | /** 20 | * Prompts the user to open Docker Desktop. 21 | */ 22 | export async function promptOpenDockerDesktop(): Promise { 23 | const response = await vscode.window.showInformationMessage( 24 | 'Docker is not running. To get help with your Dockerfile, start Docker.', 25 | "Don't show again", 26 | 'Open Docker Desktop', 27 | ); 28 | if (response === "Don't show again") { 29 | await disableDockerEngineAvailabilityPrompt(); 30 | } else if (response === 'Open Docker Desktop') { 31 | const dockerDesktopPath = getDockerDesktopPath(); 32 | if (process.platform === 'darwin') { 33 | spawn('open', [dockerDesktopPath]).on('exit', (code) => { 34 | if (code !== 0) { 35 | vscode.window.showErrorMessage( 36 | `Failed to open Docker Desktop: open ${dockerDesktopPath}`, 37 | { modal: true }, 38 | ); 39 | } 40 | }); 41 | } else { 42 | spawn(dockerDesktopPath).on('exit', (code) => { 43 | if (code !== 0) { 44 | vscode.window.showErrorMessage( 45 | `Failed to open Docker Desktop: ${dockerDesktopPath}`, 46 | { modal: true }, 47 | ); 48 | } 49 | }); 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * Prompts the user to install Docker Desktop by navigating to the 56 | * website. 57 | */ 58 | export async function promptInstallDesktop( 59 | message: string = 'Docker is not running. To get help with your Dockerfile, install Docker.', 60 | ): Promise { 61 | const response = await vscode.window.showInformationMessage( 62 | message, 63 | "Don't show again", 64 | 'Install Docker Desktop', 65 | ); 66 | if (response === "Don't show again") { 67 | await disableDockerEngineAvailabilityPrompt(); 68 | } else if (response === 'Install Docker Desktop') { 69 | vscode.env.openExternal( 70 | vscode.Uri.parse('https://docs.docker.com/install/'), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/utils/settings.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | type DockerExtensionSettings = 4 | | 'dockerEngineAvailabilityPrompt' 5 | | 'enableComposeLanguageServer'; 6 | 7 | /** 8 | * Retrieves the value of a specified setting from the Docker extension's configuration. 9 | * 10 | * @param setting - The name of the setting to retrieve. 11 | * @returns The value of the specified setting, or `undefined` if the setting is not found. 12 | */ 13 | 14 | export function getExtensionSetting(setting: DockerExtensionSettings) { 15 | return vscode.workspace.getConfiguration('docker.extension').get(setting); 16 | } 17 | 18 | export function inspectExtensionSetting(setting: DockerExtensionSettings) { 19 | return vscode.workspace.getConfiguration('docker.extension').inspect(setting); 20 | } 21 | 22 | /** 23 | * Updates the value of a specified setting in the Docker extension's configuration. 24 | * 25 | * @param setting - The name of the setting to update. 26 | * @param value - The new value to set for the specified setting. 27 | */ 28 | async function setExtensionSetting( 29 | setting: string, 30 | value: string | boolean, 31 | configurationTarget: vscode.ConfigurationTarget = vscode.ConfigurationTarget 32 | .Global, 33 | ): Promise { 34 | return vscode.workspace 35 | .getConfiguration('docker.extension') 36 | .update(setting, value, configurationTarget); 37 | } 38 | 39 | export async function disableDockerEngineAvailabilityPrompt(): Promise { 40 | return setExtensionSetting('dockerEngineAvailabilityPrompt', false); 41 | } 42 | 43 | export async function disableEnableComposeLanguageServer(): Promise { 44 | return setExtensionSetting('enableComposeLanguageServer', false); 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/spawnDockerCommand.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Spawns a Docker command as a child process and handles its execution. 3 | * 4 | * @param command - The Docker command to execute (e.g., "run", "build"). 5 | * @param args - An optional array of arguments to pass to the Docker command. 6 | * @param processEvents - An object containing optional callback functions for handling process events: 7 | * - onExit: Invoked when the process exits with the exit code. 8 | * - onError: Invoked when an error occurs during process execution. 9 | * - onStdout: Invoked when there is data written to the standard output stream. 10 | * - onStderr: Invoked when there is data written to the standard error stream. 11 | * @returns A promise that resolves to `true` if the command exits with a code of 0, or `false` otherwise. 12 | */ 13 | import { spawn } from 'child_process'; 14 | import { promptInstallDesktop } from './prompt'; 15 | import { getExtensionSetting } from './settings'; 16 | 17 | export async function spawnDockerCommand( 18 | command: string, 19 | args: string[] = [], 20 | message?: string, 21 | processEvents: { 22 | onExit?: (code: number | null) => void; 23 | onError?: (error: Error) => void; 24 | onStderr?: (chunk: any) => void; 25 | onStdout?: (chunk: any) => void; 26 | } = {}, 27 | ) { 28 | return await new Promise((resolve) => { 29 | const { onExit, onError, onStdout, onStderr } = processEvents; 30 | const process = spawn('docker', [command, ...args]); 31 | process.on('error', (error) => { 32 | if (onError) { 33 | onError(error); 34 | } 35 | resolve(false); 36 | }); 37 | process.on('exit', (code) => { 38 | if (onExit) { 39 | onExit(code); 40 | } 41 | resolve(code === 0); 42 | }); 43 | 44 | process.stderr.on('data', (chunk: string) => { 45 | if ( 46 | chunk.includes('docker: unknown command') && 47 | getExtensionSetting('dockerEngineAvailabilityPrompt') 48 | ) { 49 | promptInstallDesktop(message); 50 | } 51 | if (onStderr) { 52 | onStderr(chunk); 53 | } 54 | }); 55 | 56 | if (onStdout) { 57 | process.stdout.on('data', (chunk) => { 58 | onStdout(chunk); 59 | }); 60 | } 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /test/resources/Dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/vscode-extension/c31e0803379a2a86bdf302dc949bd02afd31825c/test/resources/Dockerfile -------------------------------------------------------------------------------- /test/resources/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "a" { 2 | args = { 3 | var = "value" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensions.ignoreRecommendations": true, 3 | "git.autoRepositoryDetection": false, 4 | "telemetry.telemetryLevel": "off", 5 | "workbench.editor.enablePreview": false, 6 | "workbench.startupEditor": "none" 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "target": "ES2022", 5 | "lib": ["ES2022"], 6 | "sourceMap": true, 7 | "rootDir": "src", 8 | "skipLibCheck": true, 9 | "strict": true /* enable all strict type-checking options */ 10 | /* Additional Checks */ 11 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 12 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 13 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 14 | }, 15 | "exclude": ["node_modules", "src/ui-test/resources/**", ".test-extensions"], 16 | "include": ["src"] 17 | } 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | 7 | //@ts-check 8 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 9 | 10 | /** @type WebpackConfig */ 11 | const extensionConfig = { 12 | target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 13 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 14 | 15 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 16 | output: { 17 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 18 | path: path.resolve(__dirname, 'dist'), 19 | filename: 'extension.js', 20 | libraryTarget: 'commonjs2', 21 | }, 22 | stats: { 23 | colors: true, 24 | modules: true, 25 | reasons: true, 26 | errorDetails: true, 27 | }, 28 | externals: { 29 | vscode: 'commonjs vscode', // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 30 | // modules added here also need to be added in the .vscodeignore file 31 | }, 32 | resolve: { 33 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 34 | extensions: ['.ts', '.js'], 35 | // see https://github.com/microsoft/vscode-languageserver-node/issues/1355 36 | conditionNames: ['import', 'require'], 37 | mainFields: ['module', 'main'], 38 | }, 39 | module: { 40 | rules: [ 41 | { 42 | test: /\.ts$/, 43 | exclude: /node_modules/, 44 | use: [ 45 | { 46 | loader: 'ts-loader', 47 | }, 48 | ], 49 | }, 50 | ], 51 | }, 52 | devtool: 'nosources-source-map', 53 | infrastructureLogging: { 54 | level: 'log', // enables logging required for problem matchers 55 | }, 56 | }; 57 | module.exports = [extensionConfig]; 58 | --------------------------------------------------------------------------------