├── .env ├── .envrc ├── .github ├── config.yaml ├── settings.yml └── workflows │ └── ci.yaml ├── .gitignore ├── .markdownlint.jsonc ├── LICENSE ├── README.md ├── cabal.project ├── data ├── cache │ ├── open-vsx-latest.json │ ├── open-vsx-release.json │ ├── vscode-marketplace-latest.json │ └── vscode-marketplace-release.json └── extra-extensions │ └── generated.nix ├── default.nix ├── extensions ├── default.nix ├── ms-vsliveshare │ └── vsliveshare │ │ └── latest │ │ └── default.nix ├── rust-lang │ └── rust-analyzer │ │ └── latest │ │ ├── default.nix │ │ ├── update.sh │ │ └── version.txt ├── sumneko │ └── lua │ │ └── latest │ │ ├── default.nix │ │ └── remove-chmod.patch └── vadimcn │ └── vscode-lldb │ ├── 1.10.0 │ └── default.nix │ └── latest │ ├── adapter.nix │ ├── default.nix │ ├── node_deps.nix │ ├── patches │ └── cmake-build-extension-only.patch │ ├── update-shell.nix │ └── update.sh ├── extra-extensions.toml ├── flake.lock ├── flake.nix ├── haskell ├── .env ├── .envrc ├── .ghcid ├── .gitignore ├── .hlint.yaml ├── .markdownlint.jsonc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app │ └── Main.hs ├── cabal.project ├── config.yaml ├── default.nix ├── flake.lock ├── flake.nix ├── fourmolu.yaml ├── package.yaml ├── requests.sh ├── src │ ├── Configs.hs │ ├── Extensions.hs │ ├── Logger.hs │ └── Requests.hs └── updater.cabal ├── mkExtension.nix ├── nix-dev ├── default.nix ├── flake.lock └── flake.nix ├── removed.nix └── template ├── flake.lock └── flake.nix /.env: -------------------------------------------------------------------------------- 1 | LANG=C.utf8 2 | CONFIG=haskell/config.yaml -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | dotenv .env 2 | use flake -------------------------------------------------------------------------------- /.github/config.yaml: -------------------------------------------------------------------------------- 1 | collectGarbage: true 2 | garbageCollectorDelay: 10 3 | logSeverity: Info 4 | openVSX: 5 | enable: true 6 | pageCount: 10 7 | pageSize: 1000 8 | nThreads: 50 9 | release: 10 | eamodio: 11 | - gitlens 12 | rust-lang: 13 | - rust-analyzer 14 | stkb: 15 | - rewrap 16 | vscodeMarketplace: 17 | pageCount: 100 18 | pageSize: 1000 19 | release: 20 | eamodio: 21 | - gitlens 22 | GitHub: 23 | - copilot 24 | - copilot-chat 25 | - vscode-pull-request-github 26 | ms-toolsai: 27 | - jupyter 28 | ms-vscode-remote: 29 | - remote-ssh 30 | rust-lang: 31 | - rust-analyzer 32 | stkb: 33 | - rewrap 34 | maxMissingTimes: 10 35 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by Probot's Settings app 2 | # Settings App GitHub Repo: https://github.com/repository-settings/app 3 | 4 | repository: 5 | # See https://docs.github.com/en/rest/reference/repos#update-a-repository for all available settings. 6 | 7 | # The name of the repository. Changing this will rename the repository 8 | name: nix-vscode-extensions 9 | 10 | # A short description of the repository that will show up on GitHub 11 | description: VSCode and OpenVSX extensions as Nix expressions 12 | 13 | # A URL with more information about the repository 14 | homepage: https://github.com/nix-community/nix-vscode-marketplace 15 | 16 | # A comma-separated list of topics to set on the repository 17 | topics: vscode, openvsx, vscode-extension 18 | 19 | # Either `true` to make the repository private, or `false` to make it public. 20 | private: false 21 | 22 | # Either `true` to enable issues for this repository, `false` to disable them. 23 | has_issues: true 24 | 25 | # Either `true` to enable projects for this repository, or `false` to disable them. 26 | # If projects are disabled for the organization, passing `true` will cause an API error. 27 | has_projects: false 28 | 29 | # Either `true` to enable the wiki for this repository, `false` to disable it. 30 | has_wiki: false 31 | 32 | # Updates the default branch for this repository. 33 | default_branch: master 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Nix CI 2 | "on": 3 | pull_request: 4 | push: 5 | branches: [master] 6 | schedule: 7 | - cron: 0 0 * * * 8 | workflow_dispatch: {} 9 | 10 | env: 11 | nix_conf: access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 12 | substituters = https://cache.nixos.org/ https://nix-community.cachix.org https://cache.iog.io 13 | trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= 14 | keep-env-derivations = true 15 | keep-outputs = true 16 | 17 | jobs: 18 | test: 19 | name: Test extensions 20 | permissions: 21 | actions: write 22 | strategy: 23 | matrix: 24 | os: 25 | - ubuntu-24.04 26 | - ubuntu-24.04-arm 27 | - macos-13 28 | - macos-15 29 | runs-on: ${{ matrix.os }} 30 | steps: 31 | - name: Checkout this repo 32 | uses: actions/checkout@v4 33 | - name: Install Nix 34 | uses: nixbuild/nix-quick-install-action@v30 35 | with: 36 | nix_conf: ${{ env.nix_conf }} 37 | - uses: nix-community/cache-nix-action/restore@v6 38 | with: 39 | primary-key: nix-test-${{ runner.os }}-${{ runner.arch }}-${{ hashfiles('*.{nix,lock}', 'data/cache/vscode-marketplace-release.json') }} 40 | restore-prefixes-first-match: nix-test-${{ runner.os }}-${{ runner.arch }}- 41 | - name: Build VS Code with extensions 42 | run: nix run .# -- --list-extensions 43 | - name: Check flake 44 | # --impure is necessary for checking the updater 45 | run: nix flake check --impure 46 | - name: Save flake attributes from GC 47 | if: always() 48 | run: nix profile install .#saveFromGC.ci.jobs.test 49 | - uses: nix-community/cache-nix-action/save@v6 50 | if: always() 51 | with: 52 | primary-key: nix-test-${{ runner.os }}-${{ runner.arch }}-${{ hashfiles('*.{nix,lock}', 'data/cache/vscode-marketplace-release.json') }} 53 | gc-max-store-size: 0 54 | purge: true 55 | purge-prefixes: nix-test-${{ runner.os }}-${{ runner.arch }}- 56 | purge-created: 0 57 | purge-primary-key: never 58 | 59 | update: 60 | name: Update data and flakes 61 | if: github.ref_name == 'master' 62 | permissions: 63 | actions: write 64 | contents: write 65 | pull-requests: write 66 | runs-on: ubuntu-latest 67 | steps: 68 | - name: Checkout this repo 69 | uses: actions/checkout@v4 70 | - name: Install Nix 71 | uses: nixbuild/nix-quick-install-action@v30 72 | with: 73 | nix_conf: ${{ env.nix_conf }} 74 | - uses: nix-community/cache-nix-action/restore@v6 75 | with: 76 | primary-key: nix-update-${{ hashfiles('{.,nix-dev}/*.{nix,lock}', 'haskell') }} 77 | restore-prefixes-first-match: nix-update- 78 | - name: Configure git 79 | env: 80 | # required for gh 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | run: | 83 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 84 | git config --global user.name "github-actions[bot]" 85 | - name: Pull latest changes 86 | run: git pull --rebase --autostash origin master 87 | - name: Update template flake locks 88 | run: | 89 | cd template 90 | nix flake update 91 | - name: Format Nix files 92 | run: nix fmt 93 | - name: Save flake attributes from GC 94 | # Need this to run before updateExtensions* 95 | # because the following steps run GC 96 | if: always() 97 | run: nix profile install .#saveFromGC.ci.jobs.update 98 | - name: Update extensions 99 | run: nix run .#updateExtensions -- --config .github/config.yaml 100 | - name: Update extra extensions 101 | run: nix run .#updateExtraExtensions 102 | - name: Commit and push changes 103 | run: |- 104 | git pull --rebase --autostash origin master 105 | 106 | git add . 107 | 108 | git commit \ 109 | -m "action" \ 110 | -m "Update flake locks" \ 111 | -m "Update extensions" \ 112 | -m "Update extra extensions" \ 113 | || echo "commit failed!" 114 | 115 | git push 116 | - uses: nix-community/cache-nix-action/save@v6 117 | if: always() 118 | with: 119 | primary-key: nix-update-${{ hashfiles('{.,nix-dev}/*.{nix,lock}', 'haskell') }} 120 | gc-max-store-size: 3G 121 | purge: true 122 | purge-prefixes: nix-update- 123 | purge-created: 0 124 | purge-primary-key: never 125 | 126 | test-template: 127 | name: Test template works 128 | if: github.ref_name == 'master' 129 | needs: 130 | - update 131 | - test 132 | permissions: 133 | actions: write 134 | strategy: 135 | matrix: 136 | os: 137 | - ubuntu-24.04 138 | - ubuntu-24.04-arm 139 | - macos-13 140 | - macos-15 141 | runs-on: ${{ matrix.os }} 142 | steps: 143 | - name: Checkout this repo 144 | uses: actions/checkout@v4 145 | - name: Install Nix 146 | uses: nixbuild/nix-quick-install-action@v30 147 | with: 148 | nix_conf: ${{env.nix_conf}} 149 | - name: Pull latest changes 150 | run: git pull --rebase --autostash origin master 151 | - name: Restore and cache Nix store 152 | uses: nix-community/cache-nix-action/restore@v6 153 | with: 154 | primary-key: nix-template-${{ runner.os }}-${{ runner.arch }}-${{ hashfiles('template/flake.*') }} 155 | - name: Check template VSCodium 156 | run: nix develop -vvv template/#vscodium 157 | - name: Save flake attributes from GC 158 | if: always() 159 | run: nix profile install .#saveFromGC.ci.jobs.test-template 160 | - uses: nix-community/cache-nix-action/save@v6 161 | if: always() 162 | with: 163 | primary-key: nix-template-${{ runner.os }}-${{ runner.arch }}-${{ hashfiles('template/flake.*') }} 164 | purge: true 165 | purge-prefixes: nix-template-${{ runner.os }}-${{ runner.arch }}- 166 | purge-created: 0 167 | purge-primary-key: never 168 | gc-max-store-size: 1G 169 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv/ 2 | result 3 | tmp 4 | .vscode 5 | dist-newstyle 6 | data/tmp 7 | !data/cache 8 | data/extra-extensions/generated.json 9 | !data/extra/generated.nix 10 | data_* -------------------------------------------------------------------------------- /.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "code-block-style": false, 3 | "line-length": false 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ameer Taweel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nix expressions for VS Code Extensions 2 | 3 | At the time of writing this, `nixpkgs` contains **271** `VS Code` extensions. This is a small fraction of the more than **40,000** extensions in the `VS Code Marketplace`! In addition, many of the extensions in `nixpkgs` are significantly out-of-date. 4 | 5 | This flake provides Nix expressions for the majority of available extensions from [Open VSX](https://open-vsx.org/) and [VS Code Marketplace](https://marketplace.visualstudio.com/vscode). A `GitHub Action` updates the extensions daily. 6 | 7 | That said, you can now use a different set of extensions for `VS Code`/`VSCodium` in each of your projects. Moreover, you can share your flakes and cache them so that other people don't need to install these extensions manually! 8 | 9 | ## Note 10 | 11 | ### API crawler 12 | 13 | - We have a permission from MS to use a crawler on their API (see the [discussion](https://github.com/NixOS/nixpkgs/issues/208456)). Don't abuse this flake! 14 | 15 | ### nix4vscode 16 | 17 | - Check [nix4vscode](https://github.com/nix-community/nix4vscode) (and contribute!) if you need a more individual approach to extensions. 18 | 19 | ### Prerequisites 20 | 21 | - [VS Code](https://wiki.nixos.org/wiki/Visual_Studio_Code) page on the NixOS wiki. 22 | - (Optional) [Flakes](https://wiki.nixos.org/wiki/Flakes). 23 | 24 | ## History 25 | 26 | You can search for an extension in the repository history: 27 | 28 | - Get commits containing the extension: `git log -S '"copilot"' --oneline data/cache/vscode-marketplace-latest.json` 29 | - Select a commit, e.g.: `0910d1e` 30 | - Search in that commit: `git grep '"copilot"' 0910d1e -- data/cache/vscode-marketplace-latest.json` 31 | 32 | ## Example 33 | 34 | The [flake.nix](./flake.nix) provides an example (`packages.${builtins.currentSystem}.default`) of [vscode-with-extensions](https://github.com/NixOS/nixpkgs/blob/81b9a5f9d1f7f87619df26a4eaf48bf6dec8c82c/pkgs/applications/editors/vscode/with-extensions.nix). 35 | 36 | This package is `VS Code` with a couple of extensions. 37 | 38 | Run `VS Code` and list installed extensions. 39 | 40 | ```console 41 | nix run github:nix-community/nix-vscode-extensions/00e11463876a04a77fb97ba50c015ab9e5bee90d# -- --list-extensions 42 | ``` 43 | 44 | ## Template 45 | 46 | This repository has a flake [template](template/flake.nix). 47 | 48 | This template provides a [VSCodium](https://github.com/VSCodium/vscodium) with a couple of extensions. 49 | 50 | 1. Create a flake from the template (see [nix flake new](https://nixos.org/manual/nix/latest/command-ref/new-cli/nix3-flake-new.html)). 51 | 52 | ```console 53 | nix flake new vscodium-project -t github:nix-community/nix-vscode-extensions 54 | cd vscodium-project 55 | git init && git add . 56 | ``` 57 | 58 | 1. Run `VSCodium`. 59 | 60 | ```console 61 | nix run .# . 62 | ``` 63 | 64 | 1. Alternatively, start a devShell and run `VSCodium`. A `shellHook` will print extensions available in the `VSCodium`. 65 | 66 | ```console 67 | nix develop .#vscodium 68 | codium . 69 | ``` 70 | 71 | In case of problems see [Troubleshooting](#troubleshooting). 72 | 73 | ## Overlay 74 | 75 | See [Overlays](https://wiki.nixos.org/wiki/Overlays#Using_overlays). 76 | 77 | If you use NixOS, Home Manager, or similar: 78 | 79 | 1. If you use flakes, add `nix-vscode-extensions` to your flake inputs (see [example](https://github.com/maurerf/nix-darwin-config/blob/0f88b77e712f14e3da72ec0b640e206a37da7afe/flake.nix#L16)). 80 | 81 | ```nix 82 | inputs.nix-vscode-extensions.url = "github:nix-community/nix-vscode-extensions/00e11463876a04a77fb97ba50c015ab9e5bee90d"; 83 | outputs = inputs@{ nix-vscode-extensions, ... }: ... 84 | ``` 85 | 86 | 1. If you don't use flakes, import the `nix-vscode-extensions` repository. 87 | 88 | ```nix 89 | let 90 | nix-vscode-extensions = import ( 91 | builtins.fetchGit { 92 | url = "https://github.com/nix-community/nix-vscode-extensions"; 93 | ref = "refs/heads/master"; 94 | rev = "00e11463876a04a77fb97ba50c015ab9e5bee90d"; 95 | } 96 | ); 97 | in 98 | ``` 99 | 100 | 1. Add the `nix-vscode-extensions.overlays.default` to `nixpkgs` overlays (see [Get `extensions` via the overlay](#get-extensions-via-the-overlay), [example](https://github.com/maurerf/nix-darwin-config/blob/0f88b77e712f14e3da72ec0b640e206a37da7afe/flake.nix#L48)). 101 | 102 | 1. (Optional) Allow unfree packages (see [Unfree extensions](#unfree-extensions)). 103 | 104 | 1. Use `pkgs.nix-vscode-extensions.vscode-marketplace`, `pkgs.nix-vscode-extensions.open-vsx` and others (see [`extensions` attrsets](#extensions-attrsets), [example](https://github.com/maurerf/nix-darwin-config/blob/0f88b77e712f14e3da72ec0b640e206a37da7afe/flake.nix#L131)). 105 | 106 | > [!NOTE] 107 | > See [With-expressions](https://nix.dev/manual/nix/latest/language/syntax#with-expressions). 108 | > 109 | > In `with A; with B;`, the attributes of `B` shadow the attributes of `A`. 110 | > 111 | > Keep in mind this property of `with` when writing `with vscode-marketplace; with vscode-marketplace-release;`. 112 | 113 | ## Get `extensions` 114 | 115 | ### Start REPL 116 | 117 | See [nix repl](https://nix.dev/manual/nix/latest/command-ref/new-cli/nix3-repl.html). 118 | 119 | ```console 120 | nix repl 121 | ``` 122 | 123 | ### Get your system 124 | 125 | ```console 126 | nix-repl> builtins.currentSystem 127 | ``` 128 | 129 | Output on my machine: 130 | 131 | ```console 132 | x86_64-linux 133 | ``` 134 | 135 | ### Get `nixpkgs` 136 | 137 | #### Get `nixpkgs` with flakes 138 | 139 | ```console 140 | nix-repl> nixpkgs = builtins.getFlake github:nixos/nixpkgs/ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c 141 | ``` 142 | 143 | #### Get `nixpkgs` without flakes 144 | 145 | ```console 146 | nix-repl> nixpkgs = (import (builtins.fetchGit { 147 | url = "https://github.com/NixOS/nixpkgs"; 148 | ref = "refs/heads/master"; 149 | rev = "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c"; 150 | })) 151 | ``` 152 | 153 | ### Get `nix-vscode-extensions` 154 | 155 | #### Get `nix-vscode-extensions` with flakes 156 | 157 | ```console 158 | nix-repl> nix-vscode-extensions = builtins.getFlake github:nix-community/nix-vscode-extensions/00e11463876a04a77fb97ba50c015ab9e5bee90d 159 | ``` 160 | 161 | #### Get `nix-vscode-extensions` without flakes 162 | 163 | ```console 164 | nix-repl> nix-vscode-extensions = (import (builtins.fetchGit { 165 | url = "https://github.com/nix-community/nix-vscode-extensions"; 166 | ref = "refs/heads/master"; 167 | rev = "00e11463876a04a77fb97ba50c015ab9e5bee90d"; 168 | })) 169 | ``` 170 | 171 | ### Get `extensions` via the overlay 172 | 173 | ```console 174 | nix-repl> extensions = import nixpkgs { system = builtins.currentSystem; config.allowUnfree = true; overlays = [ nix-vscode-extensions.overlays.default ]; } 175 | ``` 176 | 177 | If you want `extensions` to have only [`extensions` attrsets](#extensions-attrsets), get `extensions` as follows: 178 | 179 | ```console 180 | nix-repl> extensions = (import nixpkgs { system = builtins.currentSystem; config.allowUnfree = true; overlays = [ nix-vscode-extensions.overlays.default ]; }).nix-vscode-extensions 181 | ``` 182 | 183 | ### Get `extensions` from `nix-vscode-extensions` 184 | 185 | ```console 186 | nix-repl> extensions = nix-vscode-extensions.extensions.x86_64-linux 187 | ``` 188 | 189 | ## Extensions 190 | 191 | ### Platforms 192 | 193 | We provide attrsets that contain both universal and platform-specific extensions. 194 | 195 | We use a reasonable mapping between the sites target platforms and Nix-supported platforms (see `systemPlatform` in [flake.nix](./flake.nix), [issue](https://github.com/nix-community/nix-vscode-extensions/issues/20)). 196 | 197 | ### `extensions` attrsets 198 | 199 | The [Get `extensions`](#get-extensions) section explains how to get the `extensions` attrset. 200 | 201 | This attrset contains the following attributes: 202 | 203 | - `vscode-marketplace` and `open-vsx` contain the latest versions of extensions, including pre-release ones. Such pre-release versions expire in some time. That's why, there are `*-release` attrsets. 204 | - `vscode-marketplace-release` and `open-vsx-release` contain the release versions of extensions (see [Release extensions](#release-extensions)). 205 | - `forVSCodeVersion` - `forVSCodeVersion "1.78.2"` produces an attrset containing only the extensions [compatible](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#visual-studio-code-compatibility) with the `"1.78.2"` version of `VS Code` (see [Versions compatible with a given version of VS Code](#versions-compatible-with-a-given-version-of-vs-code)). 206 | - You may supply the actual version of your `VS Code` instead of `"1.78.2"`. 207 | - `usingFixesFrom` - `usingFixesFrom nixpkgsWithFixes` produces an attrset where particular extensions have fixes specified in the supplied `nixpkgsWithFixes` (see `mkExtensionNixpkgs` in [mkExtension.nix](./mkExtension.nix), [Versions with fixes from particular `nixpkgs`](#versions-with-fixes-from-particular-nixpkgs)). 208 | - The supplied `nixpkgsWithFixes` can be any version of `nixpkgs` (see [Get `nixpkgs`](#get-nixpkgs)). 209 | - The supplied `nixpkgsWithFixes` is used only to look up the fixes in its source code and is independent of the `nixpkgs` that you apply the overlay to. 210 | - The top-level `vscode-marketplace*` and `open-vsx*` attributes are constructed using fixes from `nixpkgs` that you apply the overlay to (if you [get `extensions` via the overlay](#get-extensions-via-the-overlay)) or `nixpkgs` from the `nix-vscode-extensions` repository (if you [get `extensions` from `nix-vscode-extensions`](#get-extensions-from-nix-vscode-extensions)). 211 | 212 | ### Extension identifiers 213 | 214 | - Extension publishers and names are lowercased only in Nix. 215 | - They're not lowercased in `.json` cache files such as [data/cache/open-vsx-latest.json](./data/cache/open-vsx-latest.json). 216 | - Access an extension in the format `..`, where `` is `vscode-marketplace`, `open-vsx`, etc. (see [Explore](#explore)). 217 | - If an extension publisher or name aren't valid Nix identifiers, quote them like `."4"."2"`. 218 | 219 | ### Missing extensions 220 | 221 | - Some previously available extensions may be unavailable in newer versions of this flake. 222 | - An extension is missing if it doesn't appear during a particular workflow run in a `VS Code Marketplace` or an `Open VSX` response about the full set of available extensions ([discussion](https://github.com/nix-community/nix-vscode-extensions/issues/16#issuecomment-1441025955)). 223 | - We let missing extensions remain in cache files (see [data/cache](./data/cache)) at most `maxMissingTimes` (specified in [.github/config.yaml](.github/config.yaml)). 224 | 225 | ### Extension packs 226 | 227 | - We don't automatically handle extension packs. You should look up extensions in a pack and explicitly write all necessary extensions. 228 | 229 | ### Unfree extensions 230 | 231 | - We use derivations and code from `nixpkgs` for some extensions (see [Versions with fixes from particular `nixpkgs`](#versions-with-fixes-from-particular-nixpkgs)). 232 | - Unfree extensions from `nixpkgs` stay unfree here (see [Unfree software](https://wiki.nixos.org/wiki/Unfree_software), [Special extensions](#special-extensions)). 233 | - If you want to use unfree extensions, try one of the following ways: 234 | 235 | - [Installing unfree packages](https://nixos.org/manual/nixpkgs/stable/#sec-allow-unfree). 236 | - [Global configuration](https://nixos.org/manual/nixpkgs/stable/#chap-packageconfig) - [Example](https://github.com/maurerf/nix-darwin-config/blob/0f88b77e712f14e3da72ec0b640e206a37da7afe/flake.nix#L45). 237 | - Set `config.allowUnfree = true` when constructing `pkgs`. 238 | 239 | ```nix 240 | pkgs = import nixpkgs { 241 | system = builtins.currentSystem; 242 | config.allowUnfree = true; 243 | overlays = [ overlays.default ]; 244 | } 245 | ``` 246 | 247 | - Override the license of a particular extension. 248 | 249 | ```nix 250 | let 251 | resetLicense = 252 | drv: 253 | drv.overrideAttrs (prev: { 254 | meta = prev.meta // { 255 | license = [ ]; 256 | }; 257 | }); 258 | in 259 | resetLicense . 260 | ``` 261 | 262 | ## Explore 263 | 264 | [Start REPL](#start-repl). 265 | 266 | > [!NOTE] 267 | > Press the `Tab` button (denoted as `` below) to see attrset attributes. 268 | 269 | ### Explore `extensions` 270 | 271 | ```console 272 | nix-repl> extensions. 273 | extensions.forVSCodeVersion extensions.usingFixesFrom 274 | extensions.open-vsx extensions.vscode-marketplace 275 | extensions.open-vsx-release extensions.vscode-marketplace-release 276 | ``` 277 | 278 | ### Latest versions 279 | 280 | ```console 281 | nix-repl> extensions.vscode-marketplace.rust-lang.rust-analyzer 282 | «derivation /nix/store/v2dyb61zg6faalpcz4faf6dd0ckgbcsp-vscode-extension-rust-lang-rust-analyzer-0.4.2434.drv» 283 | ``` 284 | 285 | ### Release versions 286 | 287 | ```console 288 | nix-repl> extensions.vscode-marketplace-release.rust-lang.rust-analyzer 289 | «derivation /nix/store/5xhr4a3j62awpnsd9l0llq2yn9q4gb6r-vscode-extension-rust-lang-rust-analyzer-0.3.2433.drv» 290 | ``` 291 | 292 | ### Versions compatible with a given version of VS Code 293 | 294 | ```console 295 | nix-repl> extensionsCompatible = extensions.forVSCodeVersion "1.78.2" 296 | ``` 297 | 298 | The `extensionsCompatible` attrset contains some of the [`extensions` attrsets](#extensions-attrsets). 299 | 300 | ### Versions with fixes from particular `nixpkgs` 301 | 302 | Some extensions require non-trivial fixes ([example](https://github.com/nix-community/nix-vscode-extensions/issues/69)). 303 | 304 | These fixes may be available in a particular version of `nixpkgs`. 305 | 306 | These fixes are read from the source code of that `nixpkgs` version (see `mkExtensionNixpkgs` in [mkExtension.nix](./mkExtension.nix)). 307 | 308 | #### Use fixes from `nixpkgs` 309 | 310 | [Get `extensions`](#get-extensions). 311 | 312 | In this case, we use the same version of `nixpkgs` that was used to get `extensions`. 313 | You can use any other version instead. 314 | 315 | ```console 316 | nix-repl> extensionsFixed = extensions.usingFixesFrom nixpkgs 317 | ``` 318 | 319 | The `extensionsFixed` attrset contains some of the [`extensions` attrsets](#extensions-attrsets). 320 | 321 | ### Removed extensions 322 | 323 | Some extensions are unavailable or don't work on particular platforms. 324 | 325 | These extensions are disabled via [removed.nix](./removed.nix). 326 | 327 | ## Contribute 328 | 329 | ### Issues 330 | 331 | Resolve [issues](https://github.com/nix-community/nix-vscode-extensions/issues). 332 | 333 | ### README 334 | 335 | - Fix links. 336 | - Write new sections. 337 | - Update commit hashes used in examples if they're too old. 338 | - Enhance text. 339 | 340 | ### Release extensions 341 | 342 | The [config](.github/config.yaml) contains several extensions. 343 | We cache the information about the latest **release** versions of these extensions (see [Extensions](#extensions)). 344 | 345 | You can add new extensions to the config and make a Pull Request. 346 | Use the original extension publisher and name, e.g. `GitHub` and `copilot`. 347 | 348 | ### Extra extensions 349 | 350 | The [extra-extensions.toml](extra-extensions.toml) file contains a list of extensions to be fetched from sites other than `VS Code Marketplace` and `Open VSX`. 351 | These extensions replace ones fetched from `VS Code Marketplace` and `Open VSX`. 352 | Add necessary extensions there, preferrably, for all supported platforms (see [Extensions](#extensions)). 353 | [nvfetcher](https://github.com/berberman/nvfetcher) will fetch the latest release versions of these extensions and write configs to [generated.nix](data/extra-extensions/generated.nix). 354 | 355 | ### Special extensions 356 | 357 | Certain extensions require special treatment. 358 | 359 | Provide functions to build such extension in the [extensions](extensions) directory (see [extensions/default.nix](./extensions/default.nix)). 360 | 361 | Optionally, create and link issues explaining chosen functions. 362 | 363 | Each extension, including [Extra extensions](#extra-extensions), is built via one of the functions in [mkExtension.nix](mkExtension.nix). 364 | 365 | These functions don't modify the license of ([unfree](https://wiki.nixos.org/wiki/Unfree_software)) extensions from `nixpkgs`. 366 | 367 | #### Build problems 368 | 369 | - Extension with multiple extensions in a zipfile ([issue](https://github.com/nix-community/nix-vscode-extensions/issues/31)) 370 | - Platform-specific extensions ([comment](https://github.com/nix-community/nix-vscode-extensions/issues/20#issuecomment-1543679655)) 371 | 372 | ### Main flake 373 | 374 | 1. (Optionally) [Install](https://direnv.net/#basic-installation), e.g., via `nix profile install nixpkgs#direnv`. 375 | 376 | 1. Run a devshell. When prompted about `extra-trusted-substituters` answer `y`. This is to use binary caches. 377 | 378 | ```console 379 | nix develop nix-dev/ 380 | ``` 381 | 382 | 1. (Optionally) Start `VSCodium` with necessary extensions and tools. 383 | 384 | ```console 385 | nix run nix-dev/#codium . 386 | ``` 387 | 388 | ### Haskell script 389 | 390 | 1. See the [README](./haskell/README.md). 391 | 392 | 1. Set the environment. 393 | 394 | ```console 395 | set -a 396 | source .env 397 | ``` 398 | 399 | 1. Run the script. 400 | 401 | ```console 402 | nix run haskell/#updateExtensions 403 | ``` 404 | 405 | ### Pull requests 406 | 407 | Pull requests are welcome! 408 | 409 | ## Troubleshooting 410 | 411 | - If `Nix`-provided `VSCodium` doesn't pick up the extensions: 412 | - Close other instances of `Nix`-provided `VSCodium` and start `VSCodium` again. 413 | - Try to reboot your computer and start `VSCodium` again. 414 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: haskell -------------------------------------------------------------------------------- /data/cache/open-vsx-release.json: -------------------------------------------------------------------------------- 1 | [ {"engineVersion":"^1.92.0","lastUpdated":"2025-05-21T15:58:23.072139Z","missingTimes":0,"name":"gitlens","platform":"universal","publisher":"eamodio","sha256":"sha256-hlhq4bR3v0AqI3lxilgNEgjjEEBVL0xfvIWbV/Ronh4=","version":"17.1.1"} 2 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:36:08.986224Z","missingTimes":4,"name":"rust-analyzer","platform":"linux-x64","publisher":"rust-lang","sha256":"sha256-SgkHekU1NSHszCsD7oz6DrAIQigxZgNr5SmaGpBuK2k=","version":"0.3.2474"} 3 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:36:09.456988Z","missingTimes":4,"name":"rust-analyzer","platform":"linux-arm64","publisher":"rust-lang","sha256":"sha256-JSOg9rlAeh0ZCZYQZeGQa7exraN2JMJpmX0oWshJGs4=","version":"0.3.2474"} 4 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:36:08.568475Z","missingTimes":4,"name":"rust-analyzer","platform":"darwin-x64","publisher":"rust-lang","sha256":"sha256-CAQUjTm7pzKQjd8nHhfpX7TrT3xkrMrV1oqanff0hA4=","version":"0.3.2474"} 5 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:36:07.510182Z","missingTimes":4,"name":"rust-analyzer","platform":"darwin-arm64","publisher":"rust-lang","sha256":"sha256-blOtqesupQ1xW93ZNIFxRVKJWdB50mAxrL6neaeI9b4=","version":"0.3.2474"} 6 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:21:03.696627Z","missingTimes":0,"name":"rust-analyzer","platform":"linux-x64","publisher":"rust-lang","sha256":"sha256-But7rG3y7pOXYmgoYEEqFi3n7OcAS8ga/DbjyqbVtGc=","version":"0.3.2482"} 7 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:21:05.057159Z","missingTimes":0,"name":"rust-analyzer","platform":"linux-arm64","publisher":"rust-lang","sha256":"sha256-y0m/STL7uv1BUtYRyOCqXmRocmI0550XZ5WeIDug2ZA=","version":"0.3.2482"} 8 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:21:04.607026Z","missingTimes":0,"name":"rust-analyzer","platform":"darwin-x64","publisher":"rust-lang","sha256":"sha256-qfbj5wdIrnZWLH2Hj1eNGrIRJqElwl7E3WUhDIBU5/g=","version":"0.3.2482"} 9 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:21:02.731149Z","missingTimes":0,"name":"rust-analyzer","platform":"darwin-arm64","publisher":"rust-lang","sha256":"sha256-dEMIHGKUBCBg60yFy26G6jQOFcrvmjW3kAWYrYuh6/g=","version":"0.3.2482"} 10 | , {"engineVersion":"^1.45.0","lastUpdated":"2022-02-21T10:35:25.067856Z","missingTimes":0,"name":"rewrap","platform":"universal","publisher":"stkb","sha256":"sha256-WHeLTN992ltEZw2W7B3sJrHfAFsOGMq3llV4C0hXLNA=","version":"1.16.3"} 11 | ] -------------------------------------------------------------------------------- /data/cache/vscode-marketplace-release.json: -------------------------------------------------------------------------------- 1 | [ {"engineVersion":"^1.98.0","lastUpdated":"2025-05-21T19:32:33.157Z","missingTimes":9,"name":"copilot","platform":"universal","publisher":"GitHub","sha256":"sha256-rTAq6snn3HAARrYbMJYy7aZ5rDucLfFS/t01VPjgXAo=","version":"1.323.0"} 2 | , {"engineVersion":"^1.98.0","lastUpdated":"2025-05-28T20:14:40.267Z","missingTimes":8,"name":"copilot","platform":"universal","publisher":"GitHub","sha256":"sha256-vAsyAR14KXQAQiWKadsIrXuGPBbTt6UKkINdEpW1RoY=","version":"1.325.0"} 3 | , {"engineVersion":"^1.98.0","lastUpdated":"2025-05-30T00:22:57.033Z","missingTimes":1,"name":"copilot","platform":"universal","publisher":"GitHub","sha256":"sha256-bZ8Cm3bowUCWq4mMv/7rWIBOdw1U6UoH7RODz20/r9U=","version":"1.326.0"} 4 | , {"engineVersion":"^1.98.0","lastUpdated":"2025-06-05T21:26:30.457Z","missingTimes":0,"name":"copilot","platform":"universal","publisher":"GitHub","sha256":"sha256-NbRnEHqL8lQEJFFZwt57zD3pRNi9qzRErVVquXxwjCI=","version":"1.330.0"} 5 | , {"engineVersion":"^1.100.0-20250426","lastUpdated":"2025-05-21T17:31:09.73Z","missingTimes":3,"name":"copilot-chat","platform":"universal","publisher":"GitHub","sha256":"sha256-nwBDQNs5qrA0TxQZVtuXRiOy0iBNOCFpIim0x2k37YA=","version":"0.27.2"} 6 | , {"engineVersion":"^1.100.0-20250426","lastUpdated":"2025-06-03T19:51:53.393Z","missingTimes":0,"name":"copilot-chat","platform":"universal","publisher":"GitHub","sha256":"sha256-b7zvbDzwJcHAp9tn2ibtyeErrH2KNbgqT4Ir7aqLMQg=","version":"0.27.3"} 7 | , {"engineVersion":"^1.100.0","lastUpdated":"2025-05-08T16:27:59.59Z","missingTimes":0,"name":"vscode-pull-request-github","platform":"universal","publisher":"GitHub","sha256":"sha256-roD6ugBm04L2IOKIQiAWULhhq4wo1O9VMYiYtdwCrCc=","version":"0.110.0"} 8 | , {"engineVersion":"^1.92.0","lastUpdated":"2025-05-21T16:03:20.42Z","missingTimes":0,"name":"gitlens","platform":"universal","publisher":"eamodio","sha256":"sha256-hlhq4bR3v0AqI3lxilgNEgjjEEBVL0xfvIWbV/Ronh4=","version":"17.1.1"} 9 | , {"engineVersion":"^1.82.0","lastUpdated":"2023-09-06T16:44:01.333Z","missingTimes":0,"name":"jupyter","platform":"universal","publisher":"ms-toolsai","sha256":"sha256-Kp6tCTmji81cGrCF64NofQr0SqyK84kVr7qkWuV18Ts=","version":"2023.8.1002501831"} 10 | , {"engineVersion":"^1.100.0","lastUpdated":"2025-05-21T00:53:35.533Z","missingTimes":0,"name":"jupyter","platform":"linux-x64","publisher":"ms-toolsai","sha256":"sha256-ZRN/Mvfq0Ei3rZ2o0tADPHwZvkFHIaKgTLrCP9zcFkw=","version":"2025.4.1"} 11 | , {"engineVersion":"^1.100.0","lastUpdated":"2025-05-21T00:51:08.193Z","missingTimes":0,"name":"jupyter","platform":"linux-arm64","publisher":"ms-toolsai","sha256":"sha256-rB1K62t8Vr8Mhen6NDhynL9LSob8XqxXyeeVSZHWkpQ=","version":"2025.4.1"} 12 | , {"engineVersion":"^1.100.0","lastUpdated":"2025-05-21T00:53:27.34Z","missingTimes":0,"name":"jupyter","platform":"darwin-x64","publisher":"ms-toolsai","sha256":"sha256-o7GlsA/itWUd+QPvdL/sYDq7Dybv0TJdlIDtAM0LgPs=","version":"2025.4.1"} 13 | , {"engineVersion":"^1.100.0","lastUpdated":"2025-05-21T00:53:14.62Z","missingTimes":0,"name":"jupyter","platform":"darwin-arm64","publisher":"ms-toolsai","sha256":"sha256-Jc4ob+KOfUaVCNEOylax21tUrFZIGRC48iI8FG49Uoo=","version":"2025.4.1"} 14 | , {"engineVersion":"^1.96.0","lastUpdated":"2025-05-08T20:06:59.99Z","missingTimes":0,"name":"remote-ssh","platform":"universal","publisher":"ms-vscode-remote","sha256":"sha256-D9YmLKGDtIb2wGfLNRbczqL4fzLASbZC/563ewzqGV0=","version":"0.120.0"} 15 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:39:49.677Z","missingTimes":4,"name":"rust-analyzer","platform":"linux-x64","publisher":"rust-lang","sha256":"sha256-SgkHekU1NSHszCsD7oz6DrAIQigxZgNr5SmaGpBuK2k=","version":"0.3.2474"} 16 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:40:18.597Z","missingTimes":4,"name":"rust-analyzer","platform":"linux-arm64","publisher":"rust-lang","sha256":"sha256-JSOg9rlAeh0ZCZYQZeGQa7exraN2JMJpmX0oWshJGs4=","version":"0.3.2474"} 17 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:40:06.51Z","missingTimes":4,"name":"rust-analyzer","platform":"darwin-x64","publisher":"rust-lang","sha256":"sha256-CAQUjTm7pzKQjd8nHhfpX7TrT3xkrMrV1oqanff0hA4=","version":"0.3.2474"} 18 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-05-26T05:40:38.83Z","missingTimes":4,"name":"rust-analyzer","platform":"darwin-arm64","publisher":"rust-lang","sha256":"sha256-blOtqesupQ1xW93ZNIFxRVKJWdB50mAxrL6neaeI9b4=","version":"0.3.2474"} 19 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:24:36.38Z","missingTimes":0,"name":"rust-analyzer","platform":"linux-x64","publisher":"rust-lang","sha256":"sha256-But7rG3y7pOXYmgoYEEqFi3n7OcAS8ga/DbjyqbVtGc=","version":"0.3.2482"} 20 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:24:31.457Z","missingTimes":0,"name":"rust-analyzer","platform":"linux-arm64","publisher":"rust-lang","sha256":"sha256-y0m/STL7uv1BUtYRyOCqXmRocmI0550XZ5WeIDug2ZA=","version":"0.3.2482"} 21 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:25:21.51Z","missingTimes":0,"name":"rust-analyzer","platform":"darwin-x64","publisher":"rust-lang","sha256":"sha256-qfbj5wdIrnZWLH2Hj1eNGrIRJqElwl7E3WUhDIBU5/g=","version":"0.3.2482"} 22 | , {"engineVersion":"^1.93.0","lastUpdated":"2025-06-02T06:25:11.31Z","missingTimes":0,"name":"rust-analyzer","platform":"darwin-arm64","publisher":"rust-lang","sha256":"sha256-dEMIHGKUBCBg60yFy26G6jQOFcrvmjW3kAWYrYuh6/g=","version":"0.3.2482"} 23 | , {"engineVersion":"^1.45.0","lastUpdated":"2022-02-21T10:37:36.17Z","missingTimes":0,"name":"rewrap","platform":"universal","publisher":"stkb","sha256":"sha256-WHeLTN992ltEZw2W7B3sJrHfAFsOGMq3llV4C0hXLNA=","version":"1.16.3"} 24 | ] -------------------------------------------------------------------------------- /data/extra-extensions/generated.nix: -------------------------------------------------------------------------------- 1 | # This file was generated by nvfetcher, please do not modify it manually. 2 | { fetchgit, fetchurl, fetchFromGitHub, dockerTools }: 3 | { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import (import ./nix-dev).inputs.flake-compat { src = ./.; }).defaultNix 2 | -------------------------------------------------------------------------------- /extensions/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | let 3 | inherit (pkgs) callPackage lib; 4 | in 5 | { 6 | # Each extension may have several fixes that depend on the extension version. 7 | # 8 | # Fixes MUST be provided in the subdirectories of the ./extensions directory. 9 | # 10 | # A fix that works for versions up to `` 11 | # of the extension `` published by `` 12 | # MUST be in the directory `./extensions///` 13 | # 14 | # Each `${publisher}.${name}` in this file (`extensions/default.nix`) MUST provide 15 | # a function that produces an extension derivation. 16 | # 17 | # ``` 18 | # { pkgs, lib, mktplcRef, vsix, buildVscodeMarketplaceExtension } -> Derivation 19 | # ``` 20 | # 21 | # You may use less available attributes available in the function argument attrset. 22 | # 23 | # ``` 24 | # { mktplcRef, ... } -> Derivation 25 | # ``` 26 | 27 | ms-vsliveshare.vsliveshare = callPackage ./ms-vsliveshare/vsliveshare/latest; 28 | 29 | rust-lang.rust-analyzer = callPackage ./rust-lang/rust-analyzer/latest; 30 | 31 | sumneko.lua = callPackage ./sumneko/lua/latest; 32 | 33 | vadimcn.vscode-lldb = 34 | config@{ mktplcRef, ... }: 35 | if lib.versionAtLeast mktplcRef.version "1.11.0" then 36 | # https://github.com/NixOS/nixpkgs/pull/383013 37 | callPackage ./vadimcn/vscode-lldb/latest config 38 | else 39 | callPackage ./vadimcn/vscode-lldb/1.10.0 config; 40 | } 41 | -------------------------------------------------------------------------------- /extensions/ms-vsliveshare/vsliveshare/latest/default.nix: -------------------------------------------------------------------------------- 1 | # Original implementation: https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/editors/vscode/extensions/ms-vsliveshare.vsliveshare 2 | { 3 | vscode-utils, 4 | xsel, 5 | 6 | mktplcRef, 7 | vsix, 8 | 9 | ... 10 | }: 11 | vscode-utils.buildVscodeMarketplaceExtension { 12 | inherit mktplcRef vsix; 13 | 14 | # Similar to https://github.com/NixOS/nixpkgs/blob/6f5808c6534d514751d6de0e20aae83f45d9f798/pkgs/applications/editors/vscode/extensions/ms-vsliveshare.vsliveshare/default.nix#L15-L18 15 | # Not sure it's necessary 16 | postPatch = '' 17 | substituteInPlace vendor.js \ 18 | --replace-fail '"xsel"' '"${xsel}/bin/xsel"' 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /extensions/rust-lang/rust-analyzer/latest/default.nix: -------------------------------------------------------------------------------- 1 | # Original implementation: https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/editors/vscode/extensions/rust-lang.rust-analyzer 2 | { 3 | lib, 4 | jq, 5 | rust-analyzer, 6 | moreutils, 7 | setDefaultServerPath ? true, 8 | vscode-utils, 9 | 10 | mktplcRef, 11 | vsix, 12 | 13 | ... 14 | }: 15 | vscode-utils.buildVscodeMarketplaceExtension { 16 | inherit mktplcRef vsix; 17 | 18 | nativeBuildInputs = lib.optionals setDefaultServerPath [ 19 | jq 20 | moreutils 21 | ]; 22 | 23 | preInstall = lib.optionalString setDefaultServerPath '' 24 | jq '(.contributes.configuration[] | select(.title == "server") | .properties."rust-analyzer.server.path".default) = $s' \ 25 | --arg s "${rust-analyzer}/bin/rust-analyzer" \ 26 | package.json | sponge package.json 27 | ''; 28 | 29 | meta = { 30 | description = "Alternative rust language server to the RLS"; 31 | homepage = "https://github.com/rust-lang/rust-analyzer"; 32 | license = [ 33 | lib.licenses.mit 34 | lib.licenses.asl20 35 | ]; 36 | maintainers = [ ]; 37 | platforms = lib.platforms.all; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /extensions/rust-lang/rust-analyzer/latest/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -i bash -p curl jq libarchive 3 | #shellcheck shell=bash 4 | set -euo pipefail 5 | cd "$(dirname "$0")" 6 | nixpkgs=../../../../../../ 7 | owner=rust-lang 8 | repo=rust-analyzer 9 | ver=$( 10 | curl -s "https://api.github.com/repos/$owner/$repo/releases" | 11 | jq 'map(select(.prerelease | not)) | .[0].tag_name' --raw-output 12 | ) 13 | node_src="$(nix-build "$nixpkgs" -A rust-analyzer.src --no-out-link)/editors/code" 14 | 15 | # Check vscode compatibility 16 | req_vscode_ver="$(jq '.engines.vscode' "$node_src/package.json" --raw-output)" 17 | req_vscode_ver="${req_vscode_ver#^}" 18 | cur_vscode_ver="$(nix-instantiate --eval --strict "$nixpkgs" -A vscode.version | tr -d '"')" 19 | if [[ "$(nix-instantiate --eval --strict -E "(builtins.compareVersions \"$req_vscode_ver\" \"$cur_vscode_ver\")")" -gt 0 ]]; then 20 | echo "vscode $cur_vscode_ver is incompatible with the extension requiring ^$req_vscode_ver" 21 | exit 1 22 | fi 23 | 24 | extension_ver=$(curl "https://github.com/$owner/$repo/releases/download/$ver/rust-analyzer-linux-x64.vsix" -L | 25 | bsdtar -xf - --to-stdout extension/package.json | # Use bsdtar to extract vsix(zip). 26 | jq --raw-output '.version') 27 | echo -n $extension_ver > version.txt 28 | echo "Extension version: $extension_ver" 29 | 30 | echo "Remember to also update the releaseTag and hash in default.nix!" 31 | -------------------------------------------------------------------------------- /extensions/rust-lang/rust-analyzer/latest/version.txt: -------------------------------------------------------------------------------- 1 | 0.3.2308 2 | -------------------------------------------------------------------------------- /extensions/sumneko/lua/latest/default.nix: -------------------------------------------------------------------------------- 1 | # Original implementation: https://github.com/NixOS/nixpkgs/blob/4f48368f11e7329735ab76d890f18f8d4be3f60f/pkgs/applications/editors/vscode/extensions/sumneko.lua/default.nix 2 | { 3 | lua-language-server, 4 | vscode-utils, 5 | 6 | mktplcRef, 7 | vsix, 8 | 9 | ... 10 | }: 11 | vscode-utils.buildVscodeMarketplaceExtension { 12 | inherit mktplcRef vsix; 13 | 14 | patches = [ ./remove-chmod.patch ]; 15 | 16 | postInstall = '' 17 | ln -sf ${lua-language-server}/bin/lua-language-server \ 18 | $out/$installPrefix/server/bin/lua-language-server 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /extensions/sumneko/lua/latest/remove-chmod.patch: -------------------------------------------------------------------------------- 1 | --- a/client/out/src/languageserver.js 2 | +++ b/client/out/src/languageserver.js 3 | @@ -193,11 +193,11 @@ class LuaClient extends vscode_1.Disposable { 4 | break; 5 | case "linux": 6 | command = this.context.asAbsolutePath(path.join("server", binDir ? binDir : "bin-Linux", "lua-language-server")); 7 | - await fs.promises.chmod(command, "777"); 8 | + 9 | break; 10 | case "darwin": 11 | command = this.context.asAbsolutePath(path.join("server", binDir ? binDir : "bin-macOS", "lua-language-server")); 12 | - await fs.promises.chmod(command, "777"); 13 | + 14 | break; 15 | default: 16 | throw new Error(`Unsupported operating system "${platform}"!`); 17 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/1.10.0/default.nix: -------------------------------------------------------------------------------- 1 | # Credit to https://github.com/nix-community/nix-vscode-extensions/issues/52#issue-2129112776 2 | { 3 | pkgs, 4 | lib, 5 | mktplcRef, 6 | vsix, 7 | buildVscodeMarketplaceExtension, 8 | ... 9 | }: 10 | buildVscodeMarketplaceExtension { 11 | inherit mktplcRef vsix; 12 | 13 | postInstall = lib.optionalString pkgs.stdenv.isLinux '' 14 | cd "$out/$installPrefix" 15 | patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" ./adapter/codelldb 16 | patchelf --add-rpath "${lib.makeLibraryPath [ pkgs.zlib ]}" ./lldb/lib/liblldb.so 17 | ''; 18 | } 19 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/adapter.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | lldb, 4 | makeWrapper, 5 | rustPlatform, 6 | stdenv, 7 | 8 | pname, 9 | src, 10 | version, 11 | 12 | cargoHash, 13 | }: 14 | let 15 | # debugservers on macOS require the 'com.apple.security.cs.debugger' 16 | # entitlement which nixpkgs' lldb-server does not yet provide; see 17 | # for details 18 | lldbServer = 19 | if stdenv.hostPlatform.isDarwin then 20 | "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/debugserver" 21 | else 22 | "${lib.getBin lldb}/bin/lldb-server"; 23 | LLVM_TRIPLE = stdenv.buildPlatform.rust.rustcTarget; 24 | in 25 | rustPlatform.buildRustPackage { 26 | pname = "${pname}-adapter"; 27 | inherit version src; 28 | 29 | useFetchCargoVendor = true; 30 | inherit cargoHash; 31 | 32 | # Environment variables, based on 33 | # The LLDB_* variables are used in adapter/lldb/build.rs. 34 | "CC_${LLVM_TRIPLE}" = "${stdenv.cc}/bin/cc"; 35 | "CXX_${LLVM_TRIPLE}" = "${stdenv.cc}/bin/c++"; 36 | LLDB_INCLUDE = "${lib.getDev lldb}/include"; 37 | LLDB_LINK_LIB = "lldb"; 38 | LLDB_LINK_SEARCH = "${lib.getLib lldb}/lib"; 39 | 40 | nativeBuildInputs = [ makeWrapper ]; 41 | 42 | buildAndTestSubdir = "adapter"; 43 | 44 | # There's isn't much point in enabling the `weaklink` feature 45 | # when we provide lldb via Nix. 46 | # buildFeatures = [ "weaklink" ]; 47 | 48 | cargoBuildFlags = [ 49 | "--bin=codelldb" 50 | ]; 51 | 52 | postFixup = '' 53 | mkdir -p $out/share/{adapter,lang_support} 54 | mv $out/bin/* $out/share/adapter 55 | cp -r adapter/scripts $out/share/adapter 56 | cp -t $out/share/lang_support lang_support/*.py 57 | ln -s ${lib.getLib lldb} $out/share/lldb 58 | makeWrapper $out/share/adapter/codelldb $out/bin/codelldb \ 59 | --set-default LLDB_DEBUGSERVER_PATH "${lldbServer}" 60 | ''; 61 | 62 | # Tests fail to build (as of version 1.11.4). 63 | doCheck = false; 64 | 65 | passthru = { inherit lldbServer; }; 66 | } 67 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/default.nix: -------------------------------------------------------------------------------- 1 | # Original implementation: https://github.com/NixOS/nixpkgs/pull/383013 2 | # After merging should be in https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/editors/vscode/extensions/vadimcn.vscode-lldb 3 | { 4 | callPackage, 5 | cargo, 6 | cmake, 7 | fetchFromGitHub, 8 | lib, 9 | llvmPackages_19, 10 | makeRustPlatform, 11 | makeWrapper, 12 | nodejs, 13 | python3, 14 | rustc, 15 | stdenv, 16 | unzip, 17 | 18 | mktplcRef, 19 | 20 | ... 21 | }: 22 | assert lib.versionAtLeast python3.version "3.5"; 23 | let 24 | inherit (mktplcRef) publisher version; 25 | pname = mktplcRef.name; 26 | 27 | vscodeExtUniqueId = "${publisher}.${pname}"; 28 | vscodeExtPublisher = publisher; 29 | vscodeExtName = pname; 30 | 31 | # If you want to add a new version and a hash, run `nix repl` and load the `nixpkgs` flake. 32 | 33 | # nix-repl> :lf nixpkgs 34 | # nix-repl> pkgs = legacyPackages.${builtins.currentSystem} 35 | 36 | # - Build `f` with the version and the hash of the `src` of the new version. 37 | # - Use the `got:` hash. 38 | 39 | # Hashes of the source code in releases (https://github.com/vadimcn/codelldb/releases) 40 | # nix-repl> f = rev: pkgs.fetchFromGitHub { owner = "vadimcn"; repo = "codelldb"; rev = "v${rev}"; hash = ""; } 41 | # nix-repl> :b f "1.11.4" 42 | hash = 43 | { 44 | "1.11.0" = "sha256-BzLKRs1fbLN4XSltnxPsgUG7ZJSMz/yJ/jQDZ9OTVxY="; 45 | "1.11.1" = "sha256-b063cCuiDpaeSSWxY0sbKsZucY7BCxI5s+35soJRFFQ="; 46 | "1.11.2" = "sha256-wj0X7nAcMU+kwl2qQRKixF+kTbTlnpgU7BYwaibIIKQ="; 47 | "1.11.3" = "sha256-zqaJzRTYc2gsipnbn4t16u62C/gkIohenWJDTEvZRvU="; 48 | "1.11.4" = "sha256-+Pe7ij5ukF5pLgwvr+HOHjIv1TQDiPOEeJtkpIW9XWI="; 49 | "1.11.5" = "sha256-mp50QmYmqMjIUfGKAt8fWcov4Bn9ruya+SwXGT3T/zk="; 50 | } 51 | .${version}; 52 | 53 | # nix-repl> f = rev: hash: pkgs.rustPlatform.buildRustPackage { cargoHash = ""; name = "dummy"; src = pkgs.fetchFromGitHub { owner = "vadimcn"; repo = "codelldb"; rev = rev; hash = hash; }; useFetchCargoVendor = true; } 54 | # nix-repl> :b f "1.11.4" "sha256-+Pe7ij5ukF5pLgwvr+HOHjIv1TQDiPOEeJtkpIW9XWI=" 55 | cargoHash = 56 | { 57 | "1.11.0" = "sha256-cLmL+QnFh2HwS2FcKTmGYI1NsrGV7MLWf3UBhNzBo0g="; 58 | "1.11.1" = "sha256-HFu3u/DX+SOIwwgk7+2EbQZ1tp9yqaV1CxiCN1PgXwM="; 59 | "1.11.2" = "sha256-Bl7bD+ulRJkeTdzyS8T/eMBmFaeqgMFFg3OTwSfo/RY="; 60 | "1.11.3" = "sha256-Nh4YesgWa1JR8tLfrIRps9TBdsAfilXu6G2/kB08co8="; 61 | "1.11.4" = "sha256-Nh4YesgWa1JR8tLfrIRps9TBdsAfilXu6G2/kB08co8="; 62 | "1.11.5" = "sha256-nTQbgYDDDI+pnKpCAUWDtk5rujjlK+7ZLUgPp1C/foo="; 63 | } 64 | .${version}; 65 | 66 | # nix-repl> f = rev: hash: pkgs.buildNpmPackage { npmDepsHash = ""; name = "dummy"; src = pkgs.fetchFromGitHub { owner = "vadimcn"; repo = "codelldb"; rev = rev; hash = hash; }; dontNpmBuild = true; } 67 | # nix-repl> :b f "1.11.4" "sha256-+Pe7ij5ukF5pLgwvr+HOHjIv1TQDiPOEeJtkpIW9XWI=" 68 | npmDepsHash = 69 | { 70 | "1.11.0" = "sha256-JRLXPsru+4cJe/WInYSr57+Js7mohL1CMR9LLCXORDg="; 71 | "1.11.1" = "sha256-4CCvOh7XOUsdI/gzDfx0OwzE7rhdCYFO49wVv6Gn/J0="; 72 | "1.11.2" = "sha256-oqRV9oDYPJkSkvYJA0jCgDyfzy6AnYq/ftRPM3swDyE="; 73 | "1.11.3" = "sha256-Efeun7AFMAnoNXLbTGH7OWHaBHT2tO9CodfjKrIYw40="; 74 | "1.11.4" = "sha256-Efeun7AFMAnoNXLbTGH7OWHaBHT2tO9CodfjKrIYw40="; 75 | "1.11.5" = "sha256-mHSY4LqcQiaVs6qvusxjybdKyrMh9sQatBanpIo6xk4="; 76 | } 77 | .${version}; 78 | 79 | src = fetchFromGitHub { 80 | owner = "vadimcn"; 81 | repo = "codelldb"; 82 | rev = "v${version}"; 83 | hash = hash; 84 | }; 85 | 86 | lldb = llvmPackages_19.lldb; 87 | 88 | adapter = ( 89 | callPackage ./adapter.nix { 90 | # The adapter is meant to be compiled with clang++, 91 | # based on the provided CMake toolchain files. 92 | # 93 | rustPlatform = makeRustPlatform { 94 | stdenv = llvmPackages_19.libcxxStdenv; 95 | inherit cargo rustc; 96 | }; 97 | stdenv = llvmPackages_19.libcxxStdenv; 98 | 99 | inherit 100 | pname 101 | src 102 | version 103 | cargoHash 104 | ; 105 | } 106 | ); 107 | 108 | nodeDeps = ( 109 | callPackage ./node_deps.nix { 110 | inherit 111 | pname 112 | src 113 | version 114 | npmDepsHash 115 | ; 116 | } 117 | ); 118 | in 119 | lib.customisation.makeOverridable stdenv.mkDerivation { 120 | pname = "vscode-extension-${publisher}-${pname}"; 121 | inherit 122 | src 123 | version 124 | vscodeExtUniqueId 125 | vscodeExtPublisher 126 | vscodeExtName 127 | ; 128 | 129 | installPrefix = "share/vscode/extensions/${vscodeExtUniqueId}"; 130 | 131 | nativeBuildInputs = [ 132 | cmake 133 | makeWrapper 134 | nodejs 135 | unzip 136 | ]; 137 | 138 | patches = [ ./patches/cmake-build-extension-only.patch ]; 139 | 140 | # Make devDependencies available to tools/prep-package.js 141 | preConfigure = '' 142 | cp -r ${nodeDeps}/lib/node_modules . 143 | ''; 144 | 145 | postConfigure = 146 | '' 147 | cp -r ${nodeDeps}/lib/node_modules . 148 | '' 149 | + lib.optionalString stdenv.hostPlatform.isDarwin '' 150 | export HOME="$TMPDIR/home" 151 | mkdir $HOME 152 | ''; 153 | 154 | cmakeFlags = [ 155 | # Do not append timestamp to version. 156 | "-DVERSION_SUFFIX=" 157 | ]; 158 | makeFlags = [ "vsix_bootstrap" ]; 159 | 160 | preBuild = lib.optionalString stdenv.hostPlatform.isDarwin '' 161 | export HOME=$TMPDIR 162 | ''; 163 | 164 | installPhase = '' 165 | ext=$out/$installPrefix 166 | runHook preInstall 167 | 168 | unzip ./codelldb-bootstrap.vsix 'extension/*' -d ./vsix-extracted 169 | 170 | mkdir -p $ext/adapter 171 | mv -t $ext vsix-extracted/extension/* 172 | cp -t $ext/ -r ${adapter}/share/* 173 | wrapProgram $ext/adapter/codelldb \ 174 | --prefix LD_LIBRARY_PATH : "$ext/lldb/lib" \ 175 | --set-default LLDB_DEBUGSERVER_PATH "${adapter.lldbServer}" 176 | # Mark that all components are installed. 177 | touch $ext/platform.ok 178 | 179 | runHook postInstall 180 | ''; 181 | 182 | # `adapter` will find python binary and libraries at runtime. 183 | postFixup = '' 184 | wrapProgram $out/$installPrefix/adapter/codelldb \ 185 | --prefix PATH : "${python3}/bin" \ 186 | --prefix LD_LIBRARY_PATH : "${python3}/lib" 187 | ''; 188 | 189 | passthru = { 190 | inherit lldb adapter; 191 | updateScript = ./update.sh; 192 | }; 193 | 194 | meta = { 195 | description = "Native debugger extension for VSCode based on LLDB"; 196 | homepage = "https://github.com/vadimcn/vscode-lldb"; 197 | license = [ lib.licenses.mit ]; 198 | maintainers = [ lib.maintainers.r4v3n6101 ]; 199 | platforms = lib.platforms.all; 200 | }; 201 | } 202 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/node_deps.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildNpmPackage, 3 | 4 | libsecret, 5 | python3, 6 | pkg-config, 7 | 8 | pname, 9 | src, 10 | version, 11 | 12 | npmDepsHash, 13 | }: 14 | buildNpmPackage { 15 | pname = "${pname}-node-deps"; 16 | inherit version src; 17 | 18 | inherit npmDepsHash; 19 | 20 | nativeBuildInputs = [ 21 | python3 22 | pkg-config 23 | ]; 24 | 25 | buildInputs = [ libsecret ]; 26 | 27 | dontNpmBuild = true; 28 | 29 | installPhase = '' 30 | runHook preInstall 31 | 32 | mkdir -p $out/lib 33 | cp -r node_modules $out/lib 34 | 35 | runHook postInstall 36 | ''; 37 | } 38 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/patches/cmake-build-extension-only.patch: -------------------------------------------------------------------------------- 1 | diff --git a/CMakeLists.txt b/CMakeLists.txt 2 | index 5cab8b1..0b500d5 100644 3 | --- a/CMakeLists.txt 4 | +++ b/CMakeLists.txt 5 | @@ -16,13 +16,6 @@ endif() 6 | set(VERSION "${VERSION}${VERSION_SUFFIX}") 7 | message("Version ${VERSION}") 8 | 9 | -set(LLDB_PACKAGE $ENV{LLDB_PACKAGE} CACHE PATH "Zip archive containing LLDB files") 10 | -if (LLDB_PACKAGE) 11 | - message("Using LLDB_PACKAGE=${LLDB_PACKAGE}") 12 | -else() 13 | - message(FATAL_ERROR "LLDB_PACKAGE not set." ) 14 | -endif() 15 | - 16 | if (CMAKE_SYSROOT) 17 | set(CMAKE_C_FLAGS "--sysroot=${CMAKE_SYSROOT} ${CMAKE_C_FLAGS}") 18 | set(CMAKE_CXX_FLAGS "--sysroot=${CMAKE_SYSROOT} ${CMAKE_CXX_FLAGS}") 19 | @@ -116,16 +109,6 @@ configure_file(package.json ${CMAKE_CURRENT_BINARY_DIR}/package.json @ONLY) 20 | configure_file(webpack.config.js ${CMAKE_CURRENT_BINARY_DIR}/webpack.config.js) 21 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 22 | 23 | -# Install node_modules 24 | -execute_process( 25 | - COMMAND ${NPM} --loglevel verbose ci # like install, but actually respects package-lock file. 26 | - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 27 | - RESULT_VARIABLE Result 28 | -) 29 | -if (NOT ${Result} EQUAL 0) 30 | - message(FATAL_ERROR "npm install failed: ${Result}") 31 | -endif() 32 | - 33 | # Resolve $refs 34 | execute_process( 35 | COMMAND ${WithEnv} NODE_PATH=${CMAKE_CURRENT_BINARY_DIR}/node_modules node ${CMAKE_CURRENT_SOURCE_DIR}/tools/prep-package.js ${CMAKE_CURRENT_BINARY_DIR}/package.json ${CMAKE_CURRENT_BINARY_DIR}/package.json 36 | @@ -183,6 +166,7 @@ add_custom_target(adapter_tests 37 | 38 | add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_BINARY_DIR}/README.md) 39 | add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG.md ${CMAKE_CURRENT_BINARY_DIR}/CHANGELOG.md) 40 | +add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE) 41 | add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/images/lldb.png ${CMAKE_CURRENT_BINARY_DIR}/images/lldb.png) 42 | add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/images/user.svg ${CMAKE_CURRENT_BINARY_DIR}/images/user.svg) 43 | add_copy_file(PackageFiles ${CMAKE_CURRENT_SOURCE_DIR}/images/users.svg ${CMAKE_CURRENT_BINARY_DIR}/images/users.svg) 44 | @@ -199,6 +183,7 @@ add_custom_target(dev_debugging 45 | set(PackagedFilesBootstrap 46 | README.md 47 | CHANGELOG.md 48 | + LICENSE 49 | extension.js 50 | images/* 51 | syntaxes/* 52 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/update-shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import ../../../../../.. { }, 3 | }: 4 | 5 | # Ideally, pkgs points to default.nix file of Nixpkgs official tree 6 | with pkgs; 7 | 8 | mkShell { 9 | inputsFrom = [ 10 | (import ../../update-shell.nix { inherit pkgs; }) 11 | ]; 12 | packages = [ 13 | nix-prefetch-github 14 | nurl 15 | prefetch-npm-deps 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /extensions/vadimcn/vscode-lldb/latest/update.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env nix-shell 2 | #! nix-shell update-shell.nix -i bash 3 | 4 | set -eo pipefail 5 | cd "$(dirname "${BASH_SOURCE[0]}")" 6 | 7 | echo " 8 | FIXME: This script doesn't update patched lldb. Please manually check branches 9 | of https://github.com/vadimcn/llvm-project and update lldb with correct version of LLVM. 10 | " 11 | 12 | # Ideally, nixpkgs points to default.nix file of Nixpkgs official tree 13 | nixpkgs=../../../../../.. 14 | nixFile=./default.nix 15 | adapterNixFile=./adapter.nix 16 | nodeDepsNixFile=./node_deps.nix 17 | owner=vadimcn 18 | repo=codelldb 19 | version="$1" 20 | if [[ $# -ne 1 ]]; then 21 | # no version specified, find the newest one 22 | version=$( 23 | curl -sL "https://api.github.com/repos/$owner/$repo/releases" | 24 | jq 'map(select(.prerelease | not)) | .[0].tag_name' --raw-output | 25 | sed 's/[\"v]//' 26 | ) 27 | fi 28 | old_version=$(sed -nE 's/.*\bversion = "(.*)".*/\1/p' ./default.nix) 29 | if grep -q 'cargoHash = ""' ./default.nix; then 30 | old_version='broken' 31 | fi 32 | if [[ "$version" == "$old_version" ]]; then 33 | echo "Up to date: $version" 34 | fi 35 | echo "$old_version -> $version" 36 | 37 | # update hashes 38 | sed -E 's/\bversion = ".*?"/version = "'$version'"/' --in-place "$nixFile" 39 | srcHash=$(nix-prefetch-github vadimcn codelldb --rev "v$version" | jq --raw-output .hash) 40 | sed -E 's#\bhash = ".*?"#hash = "'$srcHash'"#' --in-place "$nixFile" 41 | cargoHash=$(nurl --expr "(import $nixpkgs {}).vscode-extensions.vadimcn.vscode-lldb.adapter.cargoDeps.vendorStaging") 42 | sed -E 's#\bcargoHash = ".*?"#cargoHash = "'$cargoHash'"#' --in-place "$adapterNixFile" 43 | 44 | pushd $TMPDIR 45 | curl -LO https://raw.githubusercontent.com/$owner/$repo/v${version}/package-lock.json 46 | npmDepsHash=$(prefetch-npm-deps ./package-lock.json) 47 | popd 48 | sed -E 's#\bnpmDepsHash = ".*?"#npmDepsHash = "'$npmDepsHash'"#' --in-place "$nodeDepsNixFile" 49 | 50 | -------------------------------------------------------------------------------- /extra-extensions.toml: -------------------------------------------------------------------------------- 1 | # TODO remove these entries if building from source works (see `vadimcn.vscode-lldb` in `extensions/default.nix`) 2 | 3 | # [vadimcn--vscode-lldb--linux-x64] 4 | # src.github = "vadimcn/codelldb" 5 | # fetch.url = "https://github.com/vadimcn/codelldb/releases/download/$ver/codelldb-linux-x64.vsix" 6 | # passthru = { platform = "linux-x64", publisher = "vadimcn", name = "vscode-lldb", engineVersion = "^1.60.0" } 7 | 8 | # [vadimcn--vscode-lldb--linux-arm64] 9 | # src.github = "vadimcn/codelldb" 10 | # fetch.url = "https://github.com/vadimcn/codelldb/releases/download/$ver/codelldb-linux-arm64.vsix" 11 | # passthru = { platform = "linux-arm64", publisher = "vadimcn", name = "vscode-lldb", engineVersion = "^1.60.0" } 12 | 13 | # [vadimcn--vscode-lldb--darwin-x64] 14 | # src.github = "vadimcn/codelldb" 15 | # fetch.url = "https://github.com/vadimcn/codelldb/releases/download/$ver/codelldb-darwin-x64.vsix" 16 | # passthru = { platform = "darwin-x64", publisher = "vadimcn", name = "vscode-lldb", engineVersion = "^1.60.0" } 17 | 18 | # [vadimcn--vscode-lldb--darwin-arm64] 19 | # src.github = "vadimcn/codelldb" 20 | # fetch.url = "https://github.com/vadimcn/codelldb/releases/download/$ver/codelldb-darwin-arm64.vsix" 21 | # passthru = { platform = "darwin-arm64", publisher = "vadimcn", name = "vscode-lldb", engineVersion = "^1.60.0" } 22 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1744868846, 24 | "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "repo": "nixpkgs", 33 | "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = " 3 | `VS Code Marketplace` (~40K) and `Open VSX` (~3K) extensions as `Nix` expressions. 4 | Learn more in the flake [repo](https://github.com/nix-community/nix-vscode-extensions). 5 | "; 6 | 7 | inputs = { 8 | nixpkgs.url = "github:NixOS/nixpkgs/ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c"; 9 | flake-utils.url = "github:numtide/flake-utils"; 10 | }; 11 | 12 | outputs = 13 | inputs@{ 14 | self, 15 | nixpkgs, 16 | flake-utils, 17 | ... 18 | }: 19 | let 20 | nix-dev = import ./nix-dev; 21 | inputsCombined = nix-dev.inputs // inputs; 22 | in 23 | nix-dev.inputs.flake-parts.lib.mkFlake { inputs = inputsCombined; } { 24 | systems = import flake-utils.inputs.systems; 25 | 26 | imports = [ 27 | nix-dev.inputs.devshell.flakeModule 28 | nix-dev.inputs.treefmt-nix.flakeModule 29 | nix-dev.inputs.nix-unit.modules.flake.default 30 | ]; 31 | 32 | flake = 33 | let 34 | vscode-marketplace = "vscode-marketplace"; 35 | open-vsx = "open-vsx"; 36 | universal = "universal"; 37 | 38 | systemPlatform = { 39 | "aarch64-darwin" = "darwin-arm64"; 40 | "x86_64-linux" = "linux-x64"; 41 | "aarch64-linux" = "linux-arm64"; 42 | "x86_64-darwin" = "darwin-x64"; 43 | }; 44 | 45 | overlays = { 46 | default = 47 | final: prev: 48 | let 49 | pkgs = prev; 50 | inherit (pkgs) lib; 51 | currentPlatform = systemPlatform.${final.system}; 52 | isCompatibleVersion = 53 | vscodeVersion: engineVersion: 54 | if lib.strings.hasPrefix "^" engineVersion then 55 | lib.versionAtLeast vscodeVersion (lib.strings.removePrefix "^" engineVersion) 56 | else 57 | vscodeVersion == engineVersion; 58 | filterByPlatform = 59 | { 60 | checkVSCodeVersion, 61 | # version of VSCode or VSCodium 62 | vscodeVersion, 63 | }: 64 | (builtins.filter ( 65 | x: 66 | (x.platform == universal || x.platform == currentPlatform) 67 | && (if checkVSCodeVersion then (isCompatibleVersion vscodeVersion x.engineVersion) else true) 68 | )); 69 | loadGenerated = 70 | { 71 | needLatest ? true, 72 | checkVSCodeVersion ? false, 73 | vscodeVersion ? "*", 74 | site, 75 | pkgsWithFixes ? pkgs, 76 | }: 77 | lib.pipe site [ 78 | (x: ./data/cache/${site}${if needLatest then "-latest" else "-release"}.json) 79 | builtins.readFile 80 | builtins.fromJSON 81 | (filterByPlatform { inherit checkVSCodeVersion vscodeVersion; }) 82 | (map ( 83 | extension@{ 84 | name, 85 | publisher, 86 | version, 87 | platform, 88 | ... 89 | }: 90 | extension 91 | // { 92 | url = 93 | if site == vscode-marketplace then 94 | let 95 | platformSuffix = if platform == universal then "" else "targetPlatform=${platform}"; 96 | in 97 | "https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage?${platformSuffix}" 98 | else 99 | let 100 | platformSuffix = if platform == universal then "" else "@${platform}"; 101 | platformInfix = if platform == universal then "" else "/${platform}"; 102 | in 103 | "https://open-vsx.org/api/${publisher}/${name}${platformInfix}/${version}/file/${publisher}.${name}-${version}${platformSuffix}.vsix"; 104 | } 105 | )) 106 | ( 107 | x: 108 | builtins.map (ext: ext // { hash = ext.sha256; }) x 109 | ++ filterByPlatform { inherit checkVSCodeVersion vscodeVersion; } ( 110 | pkgs.lib.attrsets.mapAttrsToList 111 | (name: value: { 112 | url = value.src.url; 113 | hash = value.src.outputHash; 114 | inherit (value) 115 | publisher 116 | name 117 | version 118 | platform 119 | engineVersion 120 | ; 121 | }) 122 | ( 123 | # append extra extensions fetched from elsewhere to overwrite site extensions 124 | import ./data/extra-extensions/generated.nix { 125 | inherit (pkgs) 126 | fetchgit 127 | fetchurl 128 | fetchFromGitHub 129 | dockerTools 130 | ; 131 | } 132 | ) 133 | ) 134 | ) 135 | # lowercase all names and publishers 136 | (map ( 137 | extension@{ name, publisher, ... }: 138 | extension 139 | // { 140 | name = lib.toLower name; 141 | publisher = lib.toLower publisher; 142 | } 143 | )) 144 | ( 145 | let 146 | # keep outside of map to improve performance 147 | # TODO pass user's nixpkgs 148 | mkExtension = import ./mkExtension.nix { inherit pkgs pkgsWithFixes; }; 149 | in 150 | map ( 151 | { 152 | name, 153 | publisher, 154 | version, 155 | hash, 156 | url, 157 | engineVersion, 158 | ... 159 | }: 160 | let 161 | extensionConfig = { 162 | mktplcRef = { 163 | inherit 164 | name 165 | publisher 166 | version 167 | hash 168 | ; 169 | }; 170 | 171 | # We use not only VSCode Markeplace but also Open VSX 172 | # so we need to provide the URL for the extension 173 | vsix = prev.fetchurl { 174 | inherit url hash; 175 | name = "${name}-${version}.zip"; 176 | }; 177 | 178 | inherit engineVersion; 179 | }; 180 | in 181 | { 182 | inherit name; 183 | value = mkExtension extensionConfig; 184 | inherit (extensionConfig.mktplcRef) publisher; 185 | } 186 | ) 187 | ) 188 | # group by publisher 189 | (builtins.groupBy ({ publisher, ... }: publisher)) 190 | # platform-specific extensions will overwrite universal extensions 191 | # due to the sorting order of platforms in the Haskell script 192 | (builtins.mapAttrs ( 193 | _: 194 | builtins.foldl' ( 195 | k: 196 | { name, value, ... }: 197 | k 198 | // { 199 | ${name} = 200 | if !(lib.attrsets.isDerivation value) then 201 | builtins.throw '' 202 | The extension '${value.vscodeExtPublisher}.${name}' has been removed on ${pkgs.system}. 203 | See '${./removed.nix}' for details. 204 | '' 205 | else 206 | value; 207 | } 208 | ) { } 209 | )) 210 | ]; 211 | mkSet = 212 | attrs@{ 213 | checkVSCodeVersion ? false, 214 | vscodeVersion ? "*", 215 | pkgsWithFixes ? pkgs, 216 | }: 217 | { 218 | vscode-marketplace = loadGenerated (attrs // { site = vscode-marketplace; }); 219 | open-vsx = loadGenerated (attrs // { site = open-vsx; }); 220 | vscode-marketplace-release = loadGenerated ( 221 | attrs 222 | // { 223 | needLatest = false; 224 | site = vscode-marketplace; 225 | } 226 | ); 227 | open-vsx-release = loadGenerated ( 228 | attrs 229 | // { 230 | needLatest = false; 231 | site = open-vsx; 232 | } 233 | ); 234 | }; 235 | 236 | nix-vscode-extensions = 237 | (mkSet { }) 238 | // ( 239 | let 240 | mkFun = closure@{ ... }: args: (mkSet (args // closure)) // { __argsCombined = args // closure; }; 241 | forVSCodeVersion = 242 | vscodeVersion: 243 | mkFun { 244 | checkVSCodeVersion = true; 245 | inherit vscodeVersion; 246 | }; 247 | usingFixesFrom = pkgsWithFixes: mkFun { inherit pkgsWithFixes; }; 248 | in 249 | { 250 | forVSCodeVersion = 251 | vscodeVersion: 252 | let 253 | attrPrev = forVSCodeVersion vscodeVersion { }; 254 | in 255 | attrPrev 256 | // { 257 | usingFixesFrom = pkgsWithFixes: usingFixesFrom pkgsWithFixes attrPrev.__argsCombined; 258 | }; 259 | 260 | usingFixesFrom = 261 | pkgsWithFixes: 262 | let 263 | attrPrev = usingFixesFrom pkgsWithFixes { }; 264 | in 265 | attrPrev 266 | // { 267 | forVSCodeVersion = vscodeVersion: forVSCodeVersion vscodeVersion attrPrev.__argsCombined; 268 | }; 269 | } 270 | ); 271 | in 272 | nix-vscode-extensions // { inherit nix-vscode-extensions; }; 273 | }; 274 | 275 | templates = { 276 | default = { 277 | path = ./template; 278 | description = "VSCodium with extensions"; 279 | }; 280 | }; 281 | in 282 | { 283 | inherit overlays templates; 284 | } 285 | // flake-utils.lib.eachDefaultSystem ( 286 | system: 287 | let 288 | pkgs = import nixpkgs { 289 | inherit system; 290 | # Uncomment to allow unfree extensions 291 | # config.allowUnfree = true; 292 | overlays = [ self.overlays.default ]; 293 | }; 294 | 295 | extensions = { 296 | inherit (pkgs) 297 | vscode-marketplace 298 | open-vsx 299 | vscode-marketplace-release 300 | open-vsx-release 301 | forVSCodeVersion 302 | usingFixesFrom 303 | ; 304 | }; 305 | in 306 | { 307 | inherit extensions; 308 | } 309 | ); 310 | 311 | perSystem = 312 | { 313 | self', 314 | system, 315 | lib, 316 | pkgs, 317 | inputs', 318 | ... 319 | }: 320 | let 321 | devshells.default = { 322 | commands = { 323 | tools = [ 324 | { 325 | expose = true; 326 | packages = { 327 | inherit (pkgs) nvfetcher; 328 | }; 329 | } 330 | { 331 | prefix = "nix run .#"; 332 | packages = { 333 | inherit (self'.packages) updateExtensions updateExtraExtensions; 334 | }; 335 | } 336 | ]; 337 | }; 338 | }; 339 | 340 | mkShellApps = lib.mapAttrs ( 341 | name: value: 342 | if !(lib.isDerivation value) && lib.isAttrs value then 343 | pkgs.writeShellApplication (value // { inherit name; }) 344 | else 345 | value 346 | ); 347 | 348 | haskell = import ./haskell; 349 | 350 | resetLicense = 351 | drv: 352 | drv.overrideAttrs (prev: { 353 | meta = prev.meta // { 354 | license = [ ]; 355 | }; 356 | }); 357 | 358 | packages = 359 | { 360 | default = 361 | (pkgs.vscode-with-extensions.override { 362 | # vscode = pkgs.vscodium; 363 | vscode = resetLicense pkgs.vscode; 364 | vscodeExtensions = 365 | let 366 | extensions = import inputs.nixpkgs { 367 | inherit system; 368 | # Uncomment to allow unfree extensions 369 | # config.allowUnfree = true; 370 | overlays = [ self.overlays.default ]; 371 | }; 372 | in 373 | with extensions.vscode-marketplace; 374 | [ 375 | golang.go 376 | vlanguage.vscode-vlang 377 | rust-lang.rust-analyzer 378 | vadimcn.vscode-lldb 379 | ms-dotnettools.vscode-dotnet-runtime 380 | mkhl.direnv 381 | jnoortheen.nix-ide 382 | tamasfe.even-better-toml 383 | ] 384 | ++ (lib.lists.optionals (builtins.elem system lib.platforms.linux) [ 385 | # Exclusively for testing purpose 386 | (resetLicense ms-vscode.cpptools) 387 | # Local build hangs 388 | # yzane.markdown-pdf 389 | ]); 390 | }).overrideAttrs 391 | (prev: { 392 | meta = prev.meta // { 393 | description = "VSCodium with a few extensions."; 394 | longDescription = '' 395 | This is a sample overridden VSCodium (a FOSS fork of VS Code) with a few extensions. 396 | You can override this package and set `vscodeExtensions` to a list of extension 397 | derivations, specifically those provided by this flake. 398 | The [repository] offers approximately 40,000 extensions from the [Visual Studio Marketplace] 399 | and an additional 4,500 from the [Open VSX Registry]. 400 | [repository]: https://github.com/nix-community/nix-vscode-extensions 401 | [Visual Studio Marketplace]: https://marketplace.visualstudio.com/vscode 402 | [Open VSX Registry]: https://open-vsx.org/ 403 | ''; 404 | }; 405 | }); 406 | } 407 | // mkShellApps { 408 | updateExtensions = { 409 | text = ''${lib.meta.getExe haskell.outputs.packages.${system}.default} "$@"''; 410 | meta.description = "Update extensions"; 411 | }; 412 | updateExtraExtensions = { 413 | text = "${lib.meta.getExe pkgs.nvfetcher} -c extra-extensions.toml -o data/extra-extensions"; 414 | meta.description = "Update extra extensions"; 415 | }; 416 | }; 417 | 418 | legacyPackages.saveFromGC.ci.jobs = 419 | let 420 | mkSaveFromGC = 421 | attrs: import "${nix-dev.inputs.cache-nix-action}/saveFromGC.nix" ({ inherit pkgs; } // attrs); 422 | template = (import nix-dev.inputs.flake-compat { src = ./template; }).defaultNix; 423 | in 424 | { 425 | test = 426 | (mkSaveFromGC { 427 | inputs = { 428 | self.inputs = inputsCombined; 429 | }; 430 | derivations = [ self'.packages.default ]; 431 | }).saveFromGC; 432 | 433 | update = 434 | (mkSaveFromGC { 435 | inputs = { 436 | self.inputs = inputsCombined; 437 | inherit haskell; 438 | }; 439 | derivations = [ 440 | self'.packages.updateExtensions 441 | self'.packages.updateExtraExtensions 442 | self'.formatter 443 | ]; 444 | }).saveFromGC; 445 | 446 | test-template = 447 | (mkSaveFromGC { 448 | inputs = { 449 | self = template; 450 | inherit template; 451 | }; 452 | derivations = [ template.devShells.${system}.default ]; 453 | }).saveFromGC; 454 | }; 455 | 456 | nix-unit = { 457 | allowNetwork = true; 458 | 459 | inputs = { 460 | inherit (inputsCombined) 461 | nixpkgs 462 | flake-parts 463 | nix-unit 464 | flake-compat 465 | ; 466 | }; 467 | 468 | tests = 469 | let 470 | inherit (self.extensions.${system}) vscode-marketplace; 471 | in 472 | { 473 | "test: ms-python.vscode-pylance fails if unfree" = { 474 | expr = 475 | # https://discourse.nixos.org/t/evaluating-possibly-nonfree-derivations/24835/2 476 | (builtins.tryEval (builtins.unsafeDiscardStringContext vscode-marketplace.ms-python.vscode-pylance)) 477 | .success; 478 | expected = false; 479 | }; 480 | "test: ms-vscode.cpptools passes only on " = { 481 | expr = (builtins.tryEval vscode-marketplace.ms-vscode.cpptools).success; 482 | expected = builtins.elem system lib.platforms.linux; 483 | }; 484 | "test: ms-python.vscode-pylance passes if not unfree" = { 485 | expr = (builtins.tryEval (resetLicense vscode-marketplace.ms-python.vscode-pylance)).success; 486 | expected = true; 487 | }; 488 | "test: rust-lang.rust-analyzer passes" = { 489 | expr = (builtins.tryEval vscode-marketplace.rust-lang.rust-analyzer).success; 490 | expected = true; 491 | }; 492 | }; 493 | }; 494 | 495 | treefmt = { 496 | flakeCheck = false; 497 | 498 | programs = { 499 | nixfmt.enable = true; 500 | prettier.enable = true; 501 | }; 502 | 503 | settings.global.excludes = [ 504 | "haskell/**" 505 | "data/**" 506 | # ".github/**" 507 | ".envrc" 508 | ".env" 509 | "LICENSE" 510 | # "README.md" 511 | "cabal.project" 512 | "extra-extensions.toml" 513 | ".markdownlint.jsonc" 514 | ]; 515 | }; 516 | in 517 | { 518 | inherit 519 | devshells 520 | packages 521 | legacyPackages 522 | nix-unit 523 | treefmt 524 | ; 525 | }; 526 | }; 527 | 528 | nixConfig = { 529 | extra-trusted-substituters = [ 530 | "https://nix-community.cachix.org" 531 | "https://hydra.iohk.io" 532 | ]; 533 | extra-trusted-public-keys = [ 534 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 535 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 536 | ]; 537 | }; 538 | } 539 | -------------------------------------------------------------------------------- /haskell/.env: -------------------------------------------------------------------------------- 1 | LANG="en_US.UTF-8" 2 | CONFIG="config.yaml" -------------------------------------------------------------------------------- /haskell/.envrc: -------------------------------------------------------------------------------- 1 | dotenv .env 2 | use flake -------------------------------------------------------------------------------- /haskell/.ghcid: -------------------------------------------------------------------------------- 1 | -c cabal repl app/Main.hs 2 | -W 3 | -r=:main 4 | --reload=./ 5 | --reverse-errors 6 | --allow-eval -------------------------------------------------------------------------------- /haskell/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | *~ 3 | .direnv 4 | .vscode 5 | dist-newstyle 6 | tmp 7 | data* -------------------------------------------------------------------------------- /haskell/.hlint.yaml: -------------------------------------------------------------------------------- 1 | # HLint configuration file 2 | # https://github.com/ndmitchell/hlint 3 | ########################## 4 | 5 | # This file contains a template configuration file, which is typically 6 | # placed as .hlint.yaml in the root of your project 7 | 8 | 9 | # Warnings currently triggered by your code 10 | - ignore: {name: "Parse error: on input `{'"} # 3 hints 11 | 12 | 13 | # Specify additional command line arguments 14 | # 15 | - arguments: [-XQuasiQuotes] 16 | 17 | 18 | # Control which extensions/flags/modules/functions can be used 19 | # 20 | # - extensions: 21 | # - default: false # all extension are banned by default 22 | # - name: [PatternGuards, ViewPatterns] # only these listed extensions can be used 23 | # - {name: CPP, within: CrossPlatform} # CPP can only be used in a given module 24 | # 25 | # - flags: 26 | # - {name: -w, within: []} # -w is allowed nowhere 27 | # 28 | # - modules: 29 | # - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set' 30 | # - {name: Control.Arrow, within: []} # Certain modules are banned entirely 31 | # 32 | # - functions: 33 | # - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules 34 | 35 | 36 | # Add custom hints for this project 37 | # 38 | # Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar" 39 | # - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x} 40 | 41 | # The hints are named by the string they display in warning messages. 42 | # For example, if you see a warning starting like 43 | # 44 | # Main.hs:116:51: Warning: Redundant == 45 | # 46 | # You can refer to that hint with `{name: Redundant ==}` (see below). 47 | 48 | # Turn on hints that are off by default 49 | # 50 | # Ban "module X(module X) where", to require a real export list 51 | # - warn: {name: Use explicit module export list} 52 | # 53 | # Replace a $ b $ c with a . b $ c 54 | # - group: {name: dollar, enabled: true} 55 | # 56 | # Generalise map to fmap, ++ to <> 57 | # - group: {name: generalise, enabled: true} 58 | # 59 | # Warn on use of partial functions 60 | # - group: {name: partial, enabled: true} 61 | 62 | 63 | # Ignore some builtin hints 64 | # - ignore: {name: Use let} 65 | # - ignore: {name: Use const, within: SpecialModule} # Only within certain modules 66 | 67 | 68 | # Define some custom infix operators 69 | # - fixity: infixr 3 ~^#^~ 70 | 71 | 72 | # To generate a suitable file for HLint do: 73 | # $ hlint --default > .hlint.yaml 74 | -------------------------------------------------------------------------------- /haskell/.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "no-duplicate-header": false, 3 | "line-length": false 4 | } -------------------------------------------------------------------------------- /haskell/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for `haskell` 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to the 7 | [Haskell Package Versioning Policy](https://pvp.haskell.org/). 8 | 9 | ## Unreleased 10 | 11 | ## 0.1.0.0 - YYYY-MM-DD 12 | -------------------------------------------------------------------------------- /haskell/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright value (c) 2022 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of value nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | MIT License 33 | 34 | Copyright (c) 2022 Sridhar Ratnakumar 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 40 | copies of the Software, and to permit persons to whom the Software is 41 | furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all 44 | copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 52 | SOFTWARE. -------------------------------------------------------------------------------- /haskell/README.md: -------------------------------------------------------------------------------- 1 | # Haskell 2 | 3 | ## Prerequisites 4 | 5 | - [flake.nix](./flake.nix) - extensively commented code 6 | - [Prerequisites](https://github.com/deemp/flakes#prerequisites) 7 | - [Haskell](https://github.com/deemp/flakes/blob/main/README/Haskell.md) 8 | 9 | ## Quick start 10 | 11 | 1. Install Nix - see [how](https://github.com/deemp/flakes/blob/main/README/InstallNix.md). 12 | 13 | 1. In a new terminal, run a devshell from the `hs` dir. When prompted about `extra-trusted-substituters` answer `y`. This is to use binary caches. 14 | 15 | ```console 16 | cd hs 17 | nix develop 18 | ``` 19 | 20 | 1. (Optionally) Edit the [config](./config.yaml). 21 | 22 | 1. Run the app. 23 | 24 | ```console 25 | set -a 26 | source .env 27 | cabal run 28 | ``` 29 | 30 | 1. The fetched extensions will be in [data](./data). 31 | 32 | 1. (Optionally) Run `VSCodium`. 33 | 34 | ```console 35 | nix run .#codium . 36 | ``` 37 | 38 | 1. (Optionally) Open a `.hs` file and hover over a function. Wait until HLS gives the type info. 39 | 40 | ## Requests 41 | 42 | - [filterType](https://github.com/microsoft/vscode/blob/6aca75d9d09f7d9d32c18e58c836067f2d420495/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L185) 43 | 44 | - [flags](https://github.com/microsoft/vscode/blob/6aca75d9d09f7d9d32c18e58c836067f2d420495/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L101) 45 | 46 | - Run sample requests. Results will be printed to the `./tmp` dir. 47 | 48 | ```console 49 | bash requests.sh 50 | ``` 51 | 52 | ### Configs 53 | 54 | - [package.yaml] - used by `hpack` to generate a `.cabal` 55 | - [.markdownlint.jsonc](./.markdownlint.jsonc) - for `markdownlint` from the extension `davidanson.vscode-markdownlint` 56 | - [.ghcid](./.ghcid) - for [ghcid](https://github.com/ndmitchell/ghcid) 57 | - [.envrc](./.envrc) - for [direnv](https://github.com/direnv/direnv) 58 | - [fourmolu.yaml](./fourmolu.yaml) - for [fourmolu](https://github.com/fourmolu/fourmolu#configuration) 59 | 60 | ### Troubleshooting 61 | 62 | - If `VSCodium` doesn't pick up the extensions, try to reboot your computer and start `VSCodium` again. 63 | - See [troubleshooting](https://github.com/deemp/flakes/blob/main/README/Troubleshooting.md). 64 | -------------------------------------------------------------------------------- /haskell/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# HLINT ignore "Redundant bracket" #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE DeriveAnyClass #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE TupleSections #-} 6 | {-# LANGUAGE TypeOperators #-} 7 | {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} 8 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 9 | 10 | module Main (main) where 11 | 12 | import Colog (LogAction (..), Message, Msg (..), WithLog, cfilter, fmtMessage, formatWith, logDebug, logError, logInfo, logTextStdout, usingLoggerT) 13 | import Colog.Concurrent (defCapacity, withBackgroundLogger) 14 | import Configs 15 | import Control.Concurrent (threadDelay) 16 | import Control.Concurrent.Async.Pool (mapConcurrently, withTaskGroup) 17 | import Control.Concurrent.STM.TBMQueue (TBMQueue, closeTBMQueue, newTBMQueueIO, peekTBMQueue, tryReadTBMQueue, writeTBMQueue) 18 | import Control.Exception (throw) 19 | import Control.Lens (Bifunctor (bimap), Field1 (_1), Traversal', filtered, has, non, only, to, traversed, (+~), (.~), (<&>), (^.), (^..), (^?), _Just) 20 | import Control.Monad (forM_, guard, unless, void, when) 21 | import Control.Monad.IO.Class (MonadIO (..)) 22 | import Data.Aeson (ToJSON, Value (..), eitherDecodeFileStrict', encode, withObject, (.:), (.:?)) 23 | import Data.Aeson.Lens (key, nth, _Array, _String) 24 | import Data.Aeson.Types (parseMaybe) 25 | import Data.ByteString qualified as BS 26 | import Data.Default (def) 27 | import Data.Either (fromRight) 28 | import Data.Foldable (traverse_) 29 | import Data.Function (fix, (&)) 30 | import Data.Generics.Labels () 31 | import Data.HashMap.Strict qualified as Map 32 | import Data.HashSet qualified as Set 33 | import Data.Hashable 34 | import Data.List (partition) 35 | import Data.List qualified as DL 36 | import Data.Maybe (fromJust, isJust, isNothing) 37 | import Data.String (IsString (fromString)) 38 | import Data.String.Interpolate (i) 39 | import Data.Text (pack) 40 | import Data.Text qualified as Text 41 | import Data.Text.Encoding (decodeUtf8) 42 | import Data.Yaml (decodeFileEither) 43 | import Data.Yaml.Pretty (defConfig, encodePretty) 44 | import Extensions 45 | import GHC.IO.Handle (BufferMode (NoBuffering), Handle, hSetBuffering) 46 | import GHC.IO.IOMode (IOMode (AppendMode, WriteMode)) 47 | import Logger 48 | import Main.Utf8 (withUtf8) 49 | import Network.HTTP.Client (Response (..)) 50 | import Network.HTTP.Client.Conduit (Request (method)) 51 | import Network.HTTP.Simple (JSONException, httpJSONEither, setRequestBodyJSON, setRequestHeaders) 52 | import Options.Generic 53 | import Requests 54 | import Turtle (Alternative (..), mktree, rm, testfile) 55 | import Turtle.Bytes (shellStrictWithErr) 56 | import UnliftIO (MonadUnliftIO (withRunInIO), STM, SomeException, TMVar, TVar, atomically, forConcurrently, mapConcurrently_, newTMVarIO, newTVarIO, putTMVar, readTVar, readTVarIO, stdout, takeTMVar, timeout, try, tryReadTMVar, withFile, writeTVar) 57 | import UnliftIO.Exception (catchAny, finally) 58 | 59 | -- | Select a base API URL depending on the target 60 | apiUrl :: Target -> String 61 | apiUrl target = 62 | targetSelect 63 | target 64 | "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" 65 | "https://open-vsx.org/vscode/gallery/extensionquery" 66 | 67 | -- | Convert a target to a string 68 | showTarget :: Target -> String 69 | showTarget target = targetSelect target "vscode-marketplace" "open-vsx" 70 | 71 | -- | Handle the case when we need to write the first list of extensions' info into a file 72 | encodeFirstList :: ToJSON a => Handle -> [a] -> IO () 73 | encodeFirstList h (x : xs) = BS.hPutStr h ([i|#{encode x}\n|]) >> traverse_ (\y -> BS.hPutStr h ([i|, #{encode y}\n|])) xs 74 | encodeFirstList _ _ = error "Please, check the pattern matching at where you call this function" 75 | 76 | -- | Read everything from a queue into a list 77 | -- without retrying (sleeping if a queue is empty) 78 | -- 79 | -- this is to allow faster reading of a queue 80 | flushTBMQueue :: TBMQueue a -> STM (Maybe [a]) 81 | flushTBMQueue q = flip fix [] $ \ret contents -> do 82 | s <- tryReadTBMQueue q 83 | case s of 84 | Just (Just a) -> ret (a : contents) 85 | Nothing -> pure Nothing 86 | _ -> pure $ pure contents 87 | 88 | -- | Log info about extensions from a queue into a file 89 | extLogger :: ToJSON a => FilePath -> TBMQueue a -> MyLogger () 90 | extLogger file queue = liftIO $ 91 | withFile file AppendMode $ \h -> 92 | do 93 | -- let the logs go straight to a file 94 | hSetBuffering h NoBuffering 95 | -- write the initial symbol 96 | BS.hPutStr h "[ " 97 | -- if it's the first time we write into a file, 98 | -- we should correctly handle commas 99 | -- so, we introduce a flag 100 | flip fix True $ \ret isFirst -> 101 | do 102 | -- this is to make logger sleep when there's no data in the queue 103 | _ <- atomically $ peekTBMQueue queue 104 | -- read everything from a queue into a list 105 | extData <- atomically $ flushTBMQueue queue 106 | if isFirst 107 | then 108 | traverse_ 109 | ( \case 110 | -- handle the case when it's the first write 111 | -- but the queue is empty 112 | [] -> ret True 113 | -- handle another case 114 | xs -> encodeFirstList h xs 115 | ) 116 | extData 117 | else -- next time, we can write all values from the list in the same way 118 | traverse_ (traverse_ (\x -> BS.hPutStr h [i|, #{encode x}\n|])) extData 119 | -- this type of queue may become `closed` 120 | -- in this case, values that we read from it will be `Nothing` 121 | -- unless such a situation happens, we need to repeat our loop 122 | unless (isNothing extData) (ret False) 123 | -- even if this logger thread is killed by an exception, 124 | -- we want the opened file to store a valid JSON. 125 | -- so, we append a closing bracket 126 | `finally` BS.hPutStr h "]" 127 | 128 | collectGarbageOnce :: MyLogger () 129 | collectGarbageOnce = do 130 | logInfo [i|#{START} Collecting garbage in /nix/store.|] 131 | (_, infoText, errText) <- shellStrictWithErr [i|nix store gc |] empty 132 | logInfo [i|#{infoText}|] 133 | logDebug [i|#{errText}|] 134 | logInfo [i|#{FINISH} Collecting garbage in /nix/store.|] 135 | 136 | garbageCollector :: AppConfig' => TMVar () -> MyLogger () 137 | garbageCollector t = do 138 | t_ <- liftIO $ atomically $ tryReadTMVar t 139 | maybe 140 | (pure ()) 141 | ( const $ do 142 | collectGarbageOnce 143 | liftIO $ threadDelay (?config.garbageCollectorDelay * _MICROSECONDS) 144 | garbageCollector t 145 | ) 146 | t_ 147 | 148 | -- | Log info about the number of processed extensions 149 | processedLogger :: AppConfig' => Int -> TMVar Int -> MyLogger () 150 | processedLogger total processed = flip fix 0 $ \ret n -> do 151 | p <- liftIO $ atomically $ tryReadTMVar processed 152 | traverse_ 153 | ( \cnt -> do 154 | when (cnt /= n) $ logInfo [i|#{INFO} Processed (#{cnt}/#{total}) extensions|] 155 | liftIO $ threadDelay (?config.processedLoggerDelay * _MICROSECONDS) 156 | ret cnt 157 | ) 158 | p 159 | 160 | logAndForwardError :: MyLogger a -> String -> MyLogger a 161 | logAndForwardError action message = action `catchAny` \error' -> logError [i|Error #{message}:\n#{error'}|] >> throw error' 162 | 163 | -- | Get an extension from a target site and pass info about it to other threads 164 | -- 165 | -- We do this by using the thread-safe data structures like special queues and vars 166 | getExtension :: AppConfig' => Target -> TBMQueue ExtensionInfo -> TBMQueue ExtensionConfig -> TMVar Int -> TVar Int -> ExtensionConfig -> MyLogger () 167 | getExtension target extInfoQueue extFailedConfigQueue extProcessedN extFailedN extConfig@ExtensionConfig{platform, lastUpdated, missingTimes, engineVersion, version} = do 168 | let 169 | -- select an action for a target 170 | publisher = extConfig.publisher 171 | name = extConfig.name 172 | select :: a -> a -> a 173 | select = targetSelect target 174 | extName = [i|#{publisher}.#{name}|] :: Text 175 | logDebug [i|#{START} Requesting info about #{extName} from #{target}|] 176 | isFailed <- do 177 | let 178 | -- and prepare a url for a target site 179 | platformInfix :: Text = 180 | ( case platform of 181 | PUniversal -> "" 182 | x -> [i|/#{x}|] 183 | ) 184 | platformSuffix :: Text = 185 | ( case platform of 186 | PUniversal -> "" 187 | x -> select [i|targetPlatform=#{x}|] [i|@#{x}|] 188 | ) 189 | url :: Text 190 | url = 191 | select 192 | [i|https://#{publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/#{publisher}/extension/#{name}/#{version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage?#{platformSuffix}|] 193 | [i|https://open-vsx.org/api/#{publisher}/#{name}#{platformInfix}/#{version}/file/#{extName}-#{version}#{platformSuffix}.vsix|] 194 | 195 | logDebug [i|#{START} Fetching #{extName} from #{url}|] 196 | -- let nix fetch a file from that url 197 | let timeout' = ?config.requestResponseTimeout 198 | (_, decodeUtf8 -> stdout', errText) <- 199 | let command = [i|nix store prefetch-file --timeout #{timeout'} --json #{url} --name #{extName}-#{version}-#{platform}|] 200 | in shellStrictWithErr command empty 201 | `logAndForwardError` [i|during prefetch-file: #{command}|] 202 | let sha256Maybe = stdout' ^? key "hash" . _String 203 | -- if stderr was non-empty, there was an error 204 | if not (BS.null errText) 205 | then do 206 | logInfo [i|#{FAIL} Fetching #{extName} from #{url}. The stderr:\n#{errText}|] 207 | pure True 208 | else case sha256Maybe of 209 | Nothing -> do 210 | logInfo [i|#{FAIL} Fetching #{extName} from #{url}. Could not parse JSON: #{stdout'}|] 211 | pure True 212 | Just sha256 -> do 213 | logInfo [i|#{FINISH} Fetching extension #{extName} from #{url}|] 214 | -- when everything is ok, we write the extension info into a queue 215 | -- this is to let other threads read from it 216 | liftIO $ atomically $ writeTBMQueue extInfoQueue $ ExtensionInfo{..} 217 | pure False 218 | -- if at some point we failed to obtain an extension info, 219 | -- we write its config into a queue for failed configs 220 | when 221 | isFailed 222 | do 223 | liftIO $ atomically $ writeTBMQueue extFailedConfigQueue extConfig 224 | liftIO $ atomically $ readTVar extFailedN >>= \x -> writeTVar extFailedN (x + 1) 225 | -- when finished, we update a shared counter 226 | liftIO $ atomically $ takeTMVar extProcessedN >>= \x -> putTMVar extProcessedN (x + 1) 227 | 228 | data Key = Key 229 | { publisher :: Publisher 230 | , name :: Name 231 | , version :: Version 232 | , platform :: Platform 233 | , lastUpdated :: LastUpdated 234 | } 235 | deriving stock (Eq, Generic, Ord) 236 | deriving anyclass (Hashable) 237 | 238 | whenM :: Monad m => m Bool -> m () -> m () 239 | whenM cond action = cond >>= (`when` action) 240 | 241 | -- | Fetch the extensions given their configurations 242 | runFetcher :: AppConfig' => FetcherConfig IO -> MyLogger () 243 | runFetcher FetcherConfig{..} = do 244 | let 245 | fetchedTmpDir :: FilePath = [i|#{tmpDir}/fetched|] 246 | failedTmpDir :: FilePath = [i|#{tmpDir}/failed|] 247 | -- create directories for files with fetched, failed, and cached extensions 248 | -- just in case these directories don't exist 249 | forM_ [fetchedTmpDir, failedTmpDir, cacheDir] mktree 250 | -- if there were target files, remove them 251 | forM_ [fetchedTmpDir, failedTmpDir] (\(mkTargetJSON -> f) -> whenM (testfile f) (rm f)) 252 | 253 | let extensionInfoCachedJSON = mkTargetJSON cacheDir 254 | 255 | -- if there is a file with cached info, we read it into a list 256 | extensionInfoCached <- withRunInIO $ \runInIO -> 257 | ( eitherDecodeFileStrict' extensionInfoCachedJSON 258 | >>= \case 259 | Left err -> runInIO $ logError (pack $ show err) >> pure [] 260 | Right v -> pure v 261 | ) 262 | `catchAny` (\err -> runInIO $ logError (pack $ show err) >> pure []) 263 | 264 | let mkKeyInfo ExtensionInfo{..} = Key{..} 265 | mkKeyConfig ExtensionConfig{..} = Key{..} 266 | -- we load the cached info into a map for quicker access 267 | extensionInfoCacheMap = Map.fromList ((\d -> (mkKeyInfo d, d)) <$> extensionInfoCached) 268 | -- also, we filter out the duplicates among the extension configs 269 | configsSorted = Set.toList . Set.fromList $ extConfigs 270 | -- we partition the extension configs depending on if they're present in a cache 271 | (presentExtensionInfo, extensionConfigsMissing) = 272 | (partition (isJust . fst) ((\c -> (Map.lookup (mkKeyConfig c) extensionInfoCacheMap, c)) <$> configsSorted)) 273 | & bimap ((\x -> x & #missingTimes .~ 0) . fromJust . fst <$>) (snd <$>) 274 | -- the extension info that are present according to a response 275 | extensionInfoPresentMap = Map.fromList ((\d -> (mkKeyInfo d, d)) <$> presentExtensionInfo) 276 | -- extension info that are missing according to a response 277 | -- the missing counter is incremented 278 | extensionInfoMissing = 279 | (partition (\c -> isNothing $ Map.lookup (mkKeyInfo c) extensionInfoPresentMap) extensionInfoCached) 280 | ^.. _1 . traversed . filtered (\c -> c.missingTimes + 1 < ?config.maxMissingTimes) 281 | & traversed . #missingTimes +~ 1 282 | -- these missing info are turned into configs so that they can be fetched again 283 | -- this conversion preserves the missing times counter 284 | extensionInfoMissingConfigs = extensionInfoMissing <&> (\ExtensionInfo{..} -> ExtensionConfig{..}) 285 | -- combine new and old missing configs 286 | extensionConfigsMissing' = extensionConfigsMissing <> extensionInfoMissingConfigs 287 | -- and calculate the number of the configs of extensions that are missing 288 | numberExtensionConfigsMissing = length extensionConfigsMissing' 289 | -- collect extensions from cache that are not present 290 | traverse_ 291 | logInfo 292 | [ [i|#{START} Running a fetcher on #{target}|] 293 | , [i|#{INFO} From #{target} have #{length extensionInfoCached} cached extensions|] 294 | , [i|#{INFO} There are #{length extensionConfigsMissing} new extension configs.|] 295 | , [i|#{INFO} From #{length extensionInfoCached} cached extensions, #{length extensionInfoMissing} are not among the extensions available at #{target}.|] 296 | , [i|#{START} Updating cached info about #{numberExtensionConfigsMissing} extension(s) from #{target}|] 297 | ] 298 | 299 | -- we prepare shared queues and variables 300 | extInfoQueue <- liftIO $ newTBMQueueIO queueCapacity 301 | extFailedConfigQueue <- liftIO $ newTBMQueueIO queueCapacity 302 | -- this is a counter of processed extensions 303 | -- it should become empty when all extensions are processed 304 | extProcessedN <- newTMVarIO 0 305 | extProcessedNFinal <- newTVarIO 0 306 | extFailedN <- newTVarIO 0 307 | -- flag for garbage collector 308 | collectGarbage <- newTMVarIO () 309 | 310 | unless ?config.collectGarbage (atomically $ takeTMVar collectGarbage) 311 | 312 | -- we prepare file names where threads will write to 313 | let fetchedExtensionInfoFile = mkTargetJSON fetchedTmpDir 314 | failedExtensionConfigFile = mkTargetJSON failedTmpDir 315 | 316 | -- and run together 317 | ( mapConcurrently_ 318 | id 319 | [ -- a logger of info about the number of successfully processed extensions 320 | processedLogger numberExtensionConfigsMissing extProcessedN 321 | `logAndForwardError` [i|in "processed" logger thread|] 322 | , -- a logger that writes the info about successfully fetched extensions into a file 323 | extLogger fetchedExtensionInfoFile extInfoQueue 324 | `logAndForwardError` [i|in "fetched" logger thread|] 325 | , -- a logger that writes the info about failed extensions into a file 326 | extLogger failedExtensionConfigFile extFailedConfigQueue 327 | `logAndForwardError` [i|in "failed" logger thread|] 328 | , -- a garbage collector 329 | garbageCollector collectGarbage 330 | `logAndForwardError` [i|in "garbage collector" thread|] 331 | , -- and an action that uses a thread pool to fetch the extensions 332 | -- it's more efficient than spawning a thread per each element of a list with extensions' configs 333 | withRunInIO 334 | ( \runInIO -> 335 | withTaskGroup nThreads $ \g -> do 336 | void 337 | ( mapConcurrently 338 | g 339 | (runInIO . getExtension target extInfoQueue extFailedConfigQueue extProcessedN extFailedN) 340 | extensionConfigsMissing' 341 | ) 342 | ) 343 | `logAndForwardError` [i|in "worker" threads|] 344 | `finally` do 345 | -- when all configs are processed, we need to close both queues 346 | -- this will let loggers know that they should quit 347 | atomically $ closeTBMQueue extInfoQueue 348 | atomically $ closeTBMQueue extFailedConfigQueue 349 | -- make this var empty to notify the threads reading from it 350 | -- clone its value 351 | atomically $ takeTMVar extProcessedN >>= writeTVar extProcessedNFinal 352 | -- also, stop the garbage collector 353 | when ?config.collectGarbage do 354 | atomically $ takeTMVar collectGarbage 355 | collectGarbageOnce 356 | ] 357 | ) 358 | -- even if there are some errors 359 | -- we want to finally append the info about the newly fetched extensions to the cache 360 | `finally` do 361 | logInfo [i|#{START} Caching updated info about extensions from #{target}|] 362 | -- we combine into a sorted list the cached info and the new info that we read from a file 363 | extSorted <- 364 | DL.sortBy (\x y -> compare (mkKeyInfo x) (mkKeyInfo y)) 365 | . (<> presentExtensionInfo) 366 | . fromRight [] 367 | <$> liftIO (eitherDecodeFileStrict' fetchedExtensionInfoFile) 368 | -- after that, we compactly write the extensions info 369 | 370 | liftIO 371 | ( withFile extensionInfoCachedJSON WriteMode $ \h -> do 372 | BS.hPutStr h "[ " 373 | case extSorted of 374 | [] -> pure () 375 | xs -> encodeFirstList h xs 376 | BS.hPutStr h "]" 377 | ) 378 | `logAndForwardError` "when writing extensions to file" 379 | logInfo [i|#{FINISH} Caching updated info about extensions from #{target}|] 380 | extProcessedNFinal' <- readTVarIO extProcessedNFinal 381 | extFailedN' <- readTVarIO extFailedN 382 | logInfo [i|#{INFO} Processed #{extProcessedNFinal'}, failed #{extFailedN'} extensions|] 383 | logInfo [i|#{FINISH} Running a fetcher on #{target}|] 384 | 385 | -- | Retry an action a given number of times with a given delay and log about its status 386 | retry_ :: (MonadUnliftIO m, Alternative m, WithLog (LogAction m msg) Message m, AppConfig') => Int -> Text -> m b -> m b 387 | retry_ nAttempts msg action 388 | | nAttempts >= 0 = 389 | let retryDelay = ?config.retryDelay 390 | action_ n = do 391 | let n_ = nAttempts - n + 1 392 | res <- try (action >>= \res -> logDebug [i|#{INFO} Attempt (#{n_}/#{nAttempts}) succeeded. Continuing.|] >> pure res) 393 | case res of 394 | Left (err :: SomeException) 395 | | n >= 1 -> do 396 | logError [i|#{FAIL} (#{n_}/#{nAttempts}) #{msg}.\nError:\n#{err}\nRetrying in #{retryDelay} seconds.|] 397 | liftIO (threadDelay (?config.retryDelay * _MICROSECONDS)) 398 | action_ (n - 1) 399 | | otherwise -> do 400 | logError [i|#{ABORT} All #{nAttempts} attempts have failed. #{msg}|] 401 | throw err 402 | Right r -> pure r 403 | in action_ nAttempts 404 | | nAttempts < 0 = error [i|retry_: count must be 0 or more.\nCount: #{nAttempts}|] 405 | retry_ _ _ _ = error "retry_: count must be 0 or more." 406 | 407 | filteredByFlags :: Traversal' Value Value 408 | filteredByFlags = 409 | filtered 410 | ( \y -> 411 | let flags z = filter (not . Text.null) (Text.splitOn ", " z) 412 | in maybe 413 | False 414 | -- check if all flags are allowed 415 | (\z -> length (flags z) == length (flags z & DL.intersect extFlagsAllowed)) 416 | (y ^? key "flags" . _String) 417 | ) 418 | 419 | filteredExtensions :: Traversal' Value Value 420 | filteredExtensions = 421 | key "results" 422 | . nth 0 423 | . key "extensions" 424 | . _Array 425 | . traversed 426 | . filteredByFlags 427 | 428 | mkRequest :: ToJSON a => Target -> a -> Request 429 | mkRequest target request_ = 430 | setRequestBodyJSON request_ 431 | $ setRequestHeaders 432 | [ ("CONTENT-TYPE", "application/json") 433 | , ("ACCEPT", "application/json; api-version=6.1-preview.1") 434 | , ("ACCEPT-ENCODING", "gzip") 435 | ] 436 | $ (fromString (apiUrl target)){method = "POST"} 437 | 438 | mkEitherRequest :: (MonadIO w, ToJSON a) => Target -> a -> w (Response (Either JSONException Value)) 439 | mkEitherRequest target request_ = httpJSONEither @_ @Value (mkRequest target request_) 440 | 441 | -- | Get a list of extension configs from VSCode Marketplace 442 | getConfigs :: AppConfig' => Target -> MyLogger [ExtensionConfig] 443 | getConfigs target = 444 | let nRetry = ?config.nRetry 445 | siteConfig = targetSelect target ?config.vscodeMarketplace ?config.openVSX 446 | in retry_ nRetry [i|Collecting the extension configs from #{target}|] do 447 | let 448 | pageCount = siteConfig.pageCount 449 | pageSize = siteConfig.pageSize 450 | requestExtensionsList pageNumber = 451 | Req 452 | { filters = 453 | [ Filter 454 | { criteria = 455 | [ Criterion{filterType = 8, value = "Microsoft.VisualStudio.Code"} 456 | , Criterion{filterType = 12, value = "4096"} 457 | ] 458 | , sortBy = 4 459 | , sortOrder = 2 460 | , .. 461 | } 462 | ] 463 | , assetTypes = [] 464 | , flags = 946 465 | } 466 | logInfo [i|#{START} Collecting the latest versions of extensions|] 467 | logInfo [i|#{START} Collecting #{pageCount} page(s) of size #{pageSize}.|] 468 | -- we request the pages of extensions from VSCode Marketplace concurrently 469 | pages <- traverse responseBody <$> liftIO (forConcurrently [1 .. pageCount] (mkEitherRequest target . requestExtensionsList)) 470 | case pages of 471 | -- if we were unsuccessful, we need to retry 472 | Left l -> logError [i|#{FAIL} Getting info about extensions from #{target}|] >> error [i|#{l}|] 473 | Right r -> do 474 | pure $ 475 | r 476 | ^.. traversed 477 | . filteredExtensions 478 | . to 479 | ( parseMaybe 480 | ( withObject [i|Extension|] $ \o -> do 481 | name :: Name <- o .: "extensionName" 482 | publisher :: Publisher <- o .: "publisher" >>= \x -> x .: "publisherName" 483 | versions_ :: [Value] <- o .: "versions" 484 | pure $ 485 | versions_ 486 | ^.. traversed 487 | . to 488 | ( parseMaybe 489 | ( withObject [i|Version|] $ \o1 -> do 490 | lastUpdated <- o1 .: "lastUpdated" 491 | version <- o1 .: "version" 492 | platform <- o1 .:? "targetPlatform" <&> (^. non PUniversal) 493 | properties :: [Value] <- o1 .: "properties" 494 | let engineVersion = 495 | properties 496 | ^? traversed 497 | . filtered (has (key "key" . _String . only "Microsoft.VisualStudio.Code.Engine")) 498 | . key "value" 499 | . _String 500 | . _EngineVersion 501 | missingTimes = 0 502 | guard (isJust engineVersion) 503 | pure ExtensionConfig{engineVersion = fromJust engineVersion, ..} 504 | ) 505 | ) 506 | . _Just 507 | ) 508 | ) 509 | . _Just 510 | . traversed 511 | 512 | -- | Get a list of extension configs from VSCode Marketplace 513 | getConfigsRelease :: AppConfig' => Target -> MyLogger [ExtensionConfig] 514 | getConfigsRelease target = do 515 | logInfo [i|#{START} Collecting the release versions of extensions|] 516 | let nRetry = ?config.nRetry 517 | siteConfig = targetSelect target ?config.vscodeMarketplace ?config.openVSX 518 | in retry_ nRetry [i|Collecting the release extension configs from #{target}|] do 519 | let 520 | extensionCriteria = 521 | siteConfig.release.releaseExtensions 522 | ^.. traversed . to (\ReleaseExtension{..} -> Criterion{filterType = 7, value = [i|#{publisher}.#{name}|]}) 523 | pageSize = length extensionCriteria 524 | requestExtensionsList = 525 | Req 526 | { filters = 527 | [ Filter 528 | { criteria = [Criterion{filterType = 8, value = "Microsoft.VisualStudio.Code"}] <> extensionCriteria 529 | , sortBy = 0 530 | , sortOrder = 0 531 | , pageNumber = 1 532 | , .. 533 | } 534 | ] 535 | , assetTypes = [] 536 | , flags = 1073 537 | } 538 | logInfo [i|#{START} Collecting release extension pages.|] 539 | pages <- responseBody <$> liftIO (mkEitherRequest target requestExtensionsList) 540 | case pages of 541 | -- if we were unsuccessful, we need to retry 542 | Left l -> logError [i|#{FAIL} Getting info about extensions from #{target}|] >> error [i|#{l}|] 543 | Right r -> do 544 | pure $ 545 | r 546 | ^.. filteredExtensions 547 | . to 548 | ( parseMaybe 549 | ( withObject [i|Extension|] $ \o -> do 550 | name :: Name <- o .: "extensionName" 551 | publisher :: Publisher <- o .: "publisher" >>= \x -> x .: "publisherName" 552 | versions_ :: [Value] <- o .: "versions" 553 | let configs = 554 | versions_ 555 | ^.. traversed 556 | . to 557 | ( parseMaybe 558 | ( withObject [i|Version|] $ \o1 -> do 559 | lastUpdated <- o1 .: "lastUpdated" 560 | version <- o1 .: "version" 561 | platform <- o1 .:? "targetPlatform" <&> (^. non PUniversal) 562 | properties :: [Value] <- o1 .: "properties" 563 | let engineVersion = 564 | properties 565 | ^? traversed 566 | . filtered (has (key "key" . _String . only "Microsoft.VisualStudio.Code.Engine")) 567 | . key "value" 568 | . _String 569 | . _EngineVersion 570 | missingTimes = 0 571 | preRelease = 572 | properties 573 | ^? traversed 574 | . filtered (has (key "key" . _String . only "Microsoft.VisualStudio.Code.PreRelease")) 575 | guard (isNothing preRelease && isJust engineVersion) 576 | pure ExtensionConfig{engineVersion = fromJust engineVersion, ..} 577 | ) 578 | ) 579 | . _Just 580 | -- we need to possibly get the most recent config for each platform 581 | -- thus, we initialize a map `platform -> got a config` 582 | platformMap = (Map.fromList ((enumFrom minBound :: [Platform]) <&> (,Nothing))) 583 | -- we insert all configs 584 | -- if in the map, there's already a config for a platform, we don't insert it 585 | configsFiltered = foldl' (\mp conf -> Map.insertWith (\new old -> case old of Nothing -> new; x -> x) conf.platform (Just conf) mp) platformMap configs 586 | -- finally, we traverse the map values and filter out the non-existing configs 587 | -- btw. it may happen, that there's no config for a platform 588 | configsFiltered' = configsFiltered ^.. traversed . _Just 589 | pure configsFiltered' 590 | ) 591 | ) 592 | . _Just 593 | . traversed 594 | 595 | -- | Run a crawler depending on the target site to (hopefully) get the extension configs 596 | runCrawler :: AppConfig' => CrawlerConfig IO -> MyLogger ([ExtensionConfig], [ExtensionConfig]) 597 | runCrawler CrawlerConfig{..} = 598 | do 599 | logInfo [i|#{START} Updating info about extensions from #{target}.|] 600 | -- we select the target crawler and run it 601 | -- on release configs 602 | configsRelease <- getConfigsRelease target 603 | 604 | -- print the obtained release configs 605 | logDebug [i|#{configsRelease}|] 606 | 607 | -- on all configs 608 | configs <- getConfigs target 609 | 610 | logInfo [i|#{FINISH} Updating info about extensions from #{target}.|] 611 | -- finally, we return the configs 612 | pure (configs, configsRelease) 613 | 614 | processTarget :: AppConfig' => ProcessTargetConfig IO -> MyLogger () 615 | processTarget ProcessTargetConfig{..} = 616 | do 617 | let 618 | cacheDir :: FilePath = [i|#{dataDir}/cache|] 619 | tmpDir :: FilePath = [i|#{dataDir}/tmp|] 620 | 621 | mkTargetJSON :: FilePath -> FilePath 622 | mkTargetJSON x = [i|#{x}/#{showTarget target}-latest.json|] 623 | mkTargetReleaseJSON :: FilePath -> FilePath 624 | mkTargetReleaseJSON x = [i|#{x}/#{showTarget target}-release.json|] 625 | nRetry = ?config.nRetry 626 | 627 | -- we first run a crawler to get the extension configs 628 | (configs, configsRelease) <- 629 | runCrawler CrawlerConfig{..} 630 | `logAndForwardError` "when running crawler" 631 | 632 | -- then, we run a fetcher 633 | runFetcher FetcherConfig{extConfigs = configsRelease, mkTargetJSON = mkTargetReleaseJSON, ..} 634 | `logAndForwardError` "when running fetcher for release extensions" 635 | runFetcher FetcherConfig{extConfigs = configs, ..} 636 | `logAndForwardError` "when running fetcher for latest extensions" 637 | -- in case of errors, rethrow an exception 638 | `logAndForwardError` [i|when requesting #{target}|] 639 | 640 | newtype ConfigOptions w = ConfigOptions 641 | { config :: w ::: Maybe FilePath "Path to a config file" 642 | } 643 | deriving stock (Generic) 644 | 645 | instance ParseRecord (ConfigOptions Wrapped) 646 | deriving anyclass instance ParseRecord (ConfigOptions Unwrapped) 647 | 648 | main :: IO () 649 | main = withUtf8 do 650 | configOptions :: (ConfigOptions Unwrapped) <- unwrapRecord "Updater" 651 | -- we'll let logs be written to stdout as soon as they come 652 | hSetBuffering stdout NoBuffering 653 | config_ <- 654 | case configOptions.config of 655 | Nothing -> putStrLn [i|No path to config file specified. Using the default config.|] >> pure (mkDefaultAppConfig def) 656 | Just s -> do 657 | appConfig <- decodeFileEither s 658 | case appConfig of 659 | Left err -> error [i|Could not decode the config file\n\n#{err}. Aborting.|] 660 | Right appConfig_ -> pure $ mkDefaultAppConfig appConfig_ 661 | 662 | void $ 663 | timeout (config_.programTimeout * _MICROSECONDS) $ 664 | let ?config = config_ 665 | in do 666 | let AppConfig{..} = config_ 667 | withBackgroundLogger @IO defCapacity (cfilter (\(Msg sev _ _) -> sev >= logSeverity) $ formatWith fmtMessage logTextStdout) (pure ()) $ \logger -> usingLoggerT logger do 668 | logInfo [i|#{START} Updating extensions|] 669 | logInfo [i|#{START} Config:\n#{encodePretty defConfig config_}|] 670 | -- we'll run the extension crawler and a fetcher a given number of times on both target sites 671 | traverse_ 672 | ( \target -> 673 | _myLoggerT 674 | ( retry_ 675 | ?config.runN 676 | [i|Processing #{target}|] 677 | ( processTarget 678 | ProcessTargetConfig 679 | { dataDir = dataDir 680 | , nThreads = targetSelect target vscodeMarketplace.nThreads openVSX.nThreads 681 | , queueCapacity = queueCapacity 682 | , .. 683 | } 684 | ) 685 | ) 686 | ) 687 | ([VSCodeMarketplace | ?config.vscodeMarketplace.enable] <> [OpenVSX | ?config.openVSX.enable]) 688 | logInfo [i|#{FINISH} Updating extensions|] 689 | -------------------------------------------------------------------------------- /haskell/cabal.project: -------------------------------------------------------------------------------- 1 | packages: ./ 2 | 3 | package * 4 | ghc-options: -haddock -------------------------------------------------------------------------------- /haskell/config.yaml: -------------------------------------------------------------------------------- 1 | collectGarbage: false 2 | logSeverity: Info 3 | openVSX: 4 | release: 5 | eamodio: 6 | - gitlens 7 | rust-lang: 8 | - rust-analyzer 9 | stkb: 10 | - rewrap 11 | vscodeMarketplace: 12 | release: 13 | eamodio: 14 | - gitlens 15 | rust-lang: 16 | - rust-analyzer 17 | stkb: 18 | - rewrap -------------------------------------------------------------------------------- /haskell/default.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | let 3 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 4 | in 5 | fetchTarball { 6 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 7 | sha256 = lock.nodes.flake-compat.locked.narHash; 8 | } 9 | ) { src = ./.; }).defaultNix 10 | -------------------------------------------------------------------------------- /haskell/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "devshell": { 4 | "inputs": { 5 | "flake-utils": "flake-utils", 6 | "nixpkgs": [ 7 | "nixpkgs" 8 | ] 9 | }, 10 | "locked": { 11 | "lastModified": 1713447246, 12 | "narHash": "sha256-lafrzPN19LdRC3S0s6moAjI5ICyY9HSE1EJAXiEBLAw=", 13 | "owner": "deemp", 14 | "repo": "devshell", 15 | "rev": "eece9ce8466060972c2f8aa332b7e7263ad115d6", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "deemp", 20 | "repo": "devshell", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-compat": { 25 | "flake": false, 26 | "locked": { 27 | "lastModified": 1733328505, 28 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 29 | "owner": "edolstra", 30 | "repo": "flake-compat", 31 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "edolstra", 36 | "repo": "flake-compat", 37 | "type": "github" 38 | } 39 | }, 40 | "flake-parts": { 41 | "inputs": { 42 | "nixpkgs-lib": [ 43 | "nixpkgs-lib" 44 | ] 45 | }, 46 | "locked": { 47 | "lastModified": 1712014858, 48 | "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", 49 | "owner": "hercules-ci", 50 | "repo": "flake-parts", 51 | "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", 52 | "type": "github" 53 | }, 54 | "original": { 55 | "owner": "hercules-ci", 56 | "repo": "flake-parts", 57 | "type": "github" 58 | } 59 | }, 60 | "flake-utils": { 61 | "inputs": { 62 | "systems": "systems" 63 | }, 64 | "locked": { 65 | "lastModified": 1731533236, 66 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 67 | "owner": "numtide", 68 | "repo": "flake-utils", 69 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 70 | "type": "github" 71 | }, 72 | "original": { 73 | "id": "flake-utils", 74 | "type": "indirect" 75 | } 76 | }, 77 | "haskell-flake": { 78 | "locked": { 79 | "lastModified": 1712129417, 80 | "narHash": "sha256-9GZwTcmDW3zYOB+s9MpYsCJk1VIAVRwJ8WeMKVMeZ98=", 81 | "owner": "srid", 82 | "repo": "haskell-flake", 83 | "rev": "d9dbdd466afbda9df75b49e50a01f5ade61d42bc", 84 | "type": "github" 85 | }, 86 | "original": { 87 | "owner": "srid", 88 | "repo": "haskell-flake", 89 | "type": "github" 90 | } 91 | }, 92 | "nixpkgs": { 93 | "locked": { 94 | "lastModified": 1740547748, 95 | "narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=", 96 | "owner": "nixos", 97 | "repo": "nixpkgs", 98 | "rev": "3a05eebede89661660945da1f151959900903b6a", 99 | "type": "github" 100 | }, 101 | "original": { 102 | "owner": "nixos", 103 | "repo": "nixpkgs", 104 | "rev": "3a05eebede89661660945da1f151959900903b6a", 105 | "type": "github" 106 | } 107 | }, 108 | "nixpkgs-lib": { 109 | "locked": { 110 | "dir": "lib", 111 | "lastModified": 1740547748, 112 | "narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=", 113 | "owner": "nixos", 114 | "repo": "nixpkgs", 115 | "rev": "3a05eebede89661660945da1f151959900903b6a", 116 | "type": "github" 117 | }, 118 | "original": { 119 | "dir": "lib", 120 | "owner": "nixos", 121 | "repo": "nixpkgs", 122 | "rev": "3a05eebede89661660945da1f151959900903b6a", 123 | "type": "github" 124 | } 125 | }, 126 | "root": { 127 | "inputs": { 128 | "devshell": "devshell", 129 | "flake-compat": "flake-compat", 130 | "flake-parts": "flake-parts", 131 | "haskell-flake": "haskell-flake", 132 | "nixpkgs": "nixpkgs", 133 | "nixpkgs-lib": "nixpkgs-lib", 134 | "systems": "systems_2", 135 | "treefmt-nix": "treefmt-nix" 136 | } 137 | }, 138 | "systems": { 139 | "locked": { 140 | "lastModified": 1681028828, 141 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 142 | "owner": "nix-systems", 143 | "repo": "default", 144 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 145 | "type": "github" 146 | }, 147 | "original": { 148 | "owner": "nix-systems", 149 | "repo": "default", 150 | "type": "github" 151 | } 152 | }, 153 | "systems_2": { 154 | "locked": { 155 | "lastModified": 1681028828, 156 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 157 | "owner": "nix-systems", 158 | "repo": "default", 159 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 160 | "type": "github" 161 | }, 162 | "original": { 163 | "owner": "nix-systems", 164 | "repo": "default", 165 | "type": "github" 166 | } 167 | }, 168 | "treefmt-nix": { 169 | "inputs": { 170 | "nixpkgs": [ 171 | "nixpkgs" 172 | ] 173 | }, 174 | "locked": { 175 | "lastModified": 1711963903, 176 | "narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=", 177 | "owner": "numtide", 178 | "repo": "treefmt-nix", 179 | "rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac", 180 | "type": "github" 181 | }, 182 | "original": { 183 | "owner": "numtide", 184 | "repo": "treefmt-nix", 185 | "type": "github" 186 | } 187 | } 188 | }, 189 | "root": "root", 190 | "version": 7 191 | } 192 | -------------------------------------------------------------------------------- /haskell/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "srid/haskell-template: Nix template for Haskell projects"; 3 | inputs = { 4 | nixpkgs.url = "github:nixos/nixpkgs/3a05eebede89661660945da1f151959900903b6a"; 5 | nixpkgs-lib.url = "github:nixos/nixpkgs/3a05eebede89661660945da1f151959900903b6a?dir=lib"; 6 | systems.url = "github:nix-systems/default"; 7 | flake-parts = { 8 | url = "github:hercules-ci/flake-parts"; 9 | inputs.nixpkgs-lib.follows = "nixpkgs-lib"; 10 | }; 11 | haskell-flake.url = "github:srid/haskell-flake"; 12 | treefmt-nix = { 13 | url = "github:numtide/treefmt-nix"; 14 | inputs.nixpkgs.follows = "nixpkgs"; 15 | }; 16 | flake-compat = { 17 | url = "github:edolstra/flake-compat"; 18 | flake = false; 19 | }; 20 | devshell = { 21 | url = "github:deemp/devshell"; 22 | inputs.nixpkgs.follows = "nixpkgs"; 23 | inputs.flake-utils.inputs.systems.follows = "systems"; 24 | }; 25 | }; 26 | 27 | outputs = 28 | inputs: 29 | inputs.flake-parts.lib.mkFlake { inherit inputs; } { 30 | systems = import inputs.systems; 31 | imports = [ 32 | inputs.haskell-flake.flakeModule 33 | inputs.treefmt-nix.flakeModule 34 | inputs.devshell.flakeModule 35 | ]; 36 | perSystem = 37 | { 38 | self', 39 | system, 40 | lib, 41 | config, 42 | pkgs, 43 | ... 44 | }: 45 | let 46 | ghcVersion = "9101"; 47 | haskellPackages = pkgs.haskell.packages."ghc${ghcVersion}"; 48 | devTools = 49 | let 50 | wrapTool = 51 | pkgsName: pname: flags: 52 | let 53 | pkg = pkgs.${pkgsName}; 54 | in 55 | pkgs.symlinkJoin { 56 | name = pname; 57 | paths = [ pkg ]; 58 | meta = pkg.meta; 59 | version = pkg.version; 60 | buildInputs = [ pkgs.makeWrapper ]; 61 | postBuild = '' 62 | wrapProgram $out/bin/${pname} \ 63 | --add-flags "${flags}" 64 | ''; 65 | }; 66 | in 67 | { 68 | cabal = wrapTool "cabal-install" "cabal" "-v0"; 69 | hpack = pkgs.haskellPackages.hpack_0_37_0; 70 | ghc = builtins.head ( 71 | builtins.filter ( 72 | x: pkgs.lib.attrsets.isDerivation x && pkgs.lib.strings.hasPrefix "ghc-" x.name 73 | ) config.haskellProjects.default.outputs.devShell.nativeBuildInputs 74 | ); 75 | inherit (haskellPackages) haskell-language-server; 76 | }; 77 | 78 | # Our only Haskell project. You can have multiple projects, but this template 79 | # has only one. 80 | # See https://github.com/srid/haskell-flake/blob/master/example/flake.nix 81 | haskellProjects.default = { 82 | # To avoid unnecessary rebuilds, we filter projectRoot: 83 | # https://community.flake.parts/haskell-flake/local#rebuild 84 | projectRoot = builtins.toString ( 85 | lib.fileset.toSource { 86 | root = ./.; 87 | fileset = lib.fileset.unions [ 88 | ./app 89 | ./src 90 | ./updater.cabal 91 | ./LICENSE 92 | ./README.md 93 | ./CHANGELOG.md 94 | ]; 95 | } 96 | ); 97 | 98 | # The base package set (this value is the default) 99 | basePackages = haskellPackages.override { 100 | overrides = 101 | self: super: 102 | let 103 | jailbreakUnbreak = 104 | pkg: 105 | pkgs.haskell.lib.doJailbreak ( 106 | pkg.overrideAttrs (_: { 107 | meta = { }; 108 | }) 109 | ); 110 | packageFromHackage = 111 | pkg: ver: sha256: 112 | super.callHackageDirect { inherit pkg ver sha256; } { }; 113 | in 114 | { 115 | co-log = 116 | packageFromHackage "co-log" "0.6.1.2" 117 | "sha256-3drK/5n45xLc2DES0tTAqGvR6DHpgWnWvPjdx987DeE="; 118 | co-log-concurrent = jailbreakUnbreak super.co-log-concurrent; 119 | with-utf8 = super.with-utf8_1_1_0_0; 120 | bytebuild = super.bytebuild_0_3_16_3; 121 | chronos = super.chronos_1_1_6_2; 122 | }; 123 | }; 124 | 125 | settings = 126 | let 127 | default = { 128 | haddock = false; 129 | check = false; 130 | }; 131 | in 132 | { 133 | co-log = { 134 | check = false; 135 | }; 136 | co-log-concurrent = { 137 | check = false; 138 | }; 139 | with-utf8 = { 140 | check = false; 141 | }; 142 | bytebuild = default; 143 | chronos = default; 144 | PyF = { 145 | check = false; 146 | }; 147 | }; 148 | 149 | # Development shell configuration 150 | devShell = { 151 | hlsCheck.enable = false; 152 | hoogle = false; 153 | tools = hp: { 154 | cabal-install = null; 155 | hlint = null; 156 | haskell-language-server = null; 157 | ghcid = null; 158 | }; 159 | }; 160 | 161 | # What should haskell-flake add to flake outputs? 162 | autoWire = [ 163 | "packages" 164 | "apps" 165 | "checks" 166 | ]; # Wire all but the devShell 167 | }; 168 | 169 | # Auto formatters. This also adds a flake check to ensure that the 170 | # source tree was auto formatted. 171 | treefmt.config = { 172 | projectRootFile = "flake.nix"; 173 | programs.fourmolu.enable = true; 174 | programs.nixfmt-rfc-style.enable = true; 175 | programs.hlint.enable = true; 176 | }; 177 | 178 | # Default package & app. 179 | packages.default = 180 | let 181 | updater = pkgs.haskell.lib.justStaticExecutables self'.packages.updater; 182 | updaterBin = "$out/bin/${updater.meta.mainProgram}"; 183 | in 184 | pkgs.stdenv.mkDerivation { 185 | name = updater.name; 186 | phases = [ "installPhase" ]; 187 | installPhase = 188 | # https://sandervanderburg.blogspot.com/2015/10/deploying-prebuilt-binary-software-with.html 189 | # https://wiki.nixos.org/wiki/Packaging/Binaries 190 | # https://github.com/NixOS/patchelf 191 | '' 192 | mkdir -p $out/bin 193 | cp -p ${lib.getExe updater} $out/bin 194 | chmod +rw ${updaterBin} 195 | patchelf \ 196 | --set-interpreter "$(cat $${pkgs.stdenv.cc}/nix-support/dynamic-linker)" \ 197 | --add-needed libgcc_s.so.1 ${updaterBin} \ 198 | --set-rpath "$(patchelf --print-rpath ${updaterBin})":${lib.makeLibraryPath [ pkgs.gcc.cc.lib ]} \ 199 | ${updaterBin} 200 | ''; 201 | meta.mainProgram = "updater"; 202 | }; 203 | 204 | # Default shell. 205 | devshells.default = { 206 | commands = { 207 | tools = [ 208 | { 209 | expose = true; 210 | packages = devTools; 211 | } 212 | ]; 213 | }; 214 | }; 215 | in 216 | { 217 | inherit 218 | devshells 219 | treefmt 220 | packages 221 | haskellProjects 222 | ; 223 | }; 224 | }; 225 | } 226 | -------------------------------------------------------------------------------- /haskell/fourmolu.yaml: -------------------------------------------------------------------------------- 1 | # Number of spaces per indentation step 2 | indentation: 2 3 | 4 | # Max line length for automatic line breaking 5 | column-limit: none 6 | 7 | # Styling of arrows in type signatures (choices: trailing, leading, or leading-args) 8 | function-arrows: trailing 9 | 10 | # How to place commas in multi-line lists, records, etc. (choices: leading or trailing) 11 | comma-style: leading 12 | 13 | # Styling of import/export lists (choices: leading, trailing, or diff-friendly) 14 | import-export-style: diff-friendly 15 | 16 | # Whether to full-indent or half-indent 'where' bindings past the preceding body 17 | indent-wheres: false 18 | 19 | # Whether to leave a space before an opening record brace 20 | record-brace-space: false 21 | 22 | # Number of spaces between top-level declarations 23 | newlines-between-decls: 1 24 | 25 | # How to print Haddock comments (choices: single-line, multi-line, or multi-line-compact) 26 | haddock-style: single-line 27 | 28 | # How to print module docstring 29 | haddock-style-module: null 30 | 31 | # Styling of let blocks (choices: auto, inline, newline, or mixed) 32 | let-style: auto 33 | 34 | # How to align the 'in' keyword with respect to the 'let' keyword (choices: left-align, right-align, or no-space) 35 | in-style: right-align 36 | 37 | # Whether to put parentheses around a single constraint (choices: auto, always, or never) 38 | single-constraint-parens: never 39 | 40 | # Output Unicode syntax (choices: detect, always, or never) 41 | unicode: never 42 | 43 | # Give the programmer more choice on where to insert blank lines 44 | respectful: true 45 | 46 | # Fixity information for operators 47 | fixities: [] 48 | 49 | # Module reexports Fourmolu should know about 50 | reexports: [] 51 | 52 | -------------------------------------------------------------------------------- /haskell/package.yaml: -------------------------------------------------------------------------------- 1 | name: updater 2 | version: 0.1.0.0 3 | github: "nix-community/nix-vscode-extensions" 4 | license: BSD3 5 | author: "value" 6 | maintainer: "value" 7 | copyright: "value" 8 | 9 | extra-source-files: 10 | - README.md 11 | - CHANGELOG.md 12 | 13 | # Metadata used when publishing your package 14 | # synopsis: Short description of your package 15 | # category: value 16 | 17 | # To avoid duplicated efforts in documentation and dealing with the 18 | # complications of embedding Haddock markup inside cabal files, it is 19 | # common to point users to the README.md file. 20 | description: Please see the README on GitHub at 21 | 22 | default-extensions: 23 | - BangPatterns 24 | - BlockArguments 25 | - ConstraintKinds 26 | - DeriveGeneric 27 | - DerivingStrategies 28 | - DuplicateRecordFields 29 | - FlexibleContexts 30 | - FlexibleInstances 31 | - GeneralizedNewtypeDeriving 32 | - ImplicitParams 33 | - ImportQualifiedPost 34 | - InstanceSigs 35 | - LambdaCase 36 | - NamedFieldPuns 37 | - NumericUnderscores 38 | - OverloadedLabels 39 | - OverloadedRecordDot 40 | - OverloadedStrings 41 | - QuasiQuotes 42 | - RankNTypes 43 | - RecordWildCards 44 | - ScopedTypeVariables 45 | - StandaloneDeriving 46 | - TypeApplications 47 | - TypeFamilies 48 | - ViewPatterns 49 | 50 | dependencies: 51 | - aeson 52 | - async 53 | - async-pool 54 | - base >= 4.7 && < 5 55 | - bytestring 56 | - co-log 57 | - co-log-concurrent 58 | - data-default 59 | - generic-lens 60 | - hashable 61 | - http-client 62 | - http-conduit 63 | - http-types 64 | - lens 65 | - lens-aeson 66 | - megaparsec 67 | - mtl 68 | - optparse-generic 69 | - stm 70 | - stm-chans 71 | - string-interpolate 72 | - text 73 | - time 74 | - turtle 75 | - unliftio 76 | - unordered-containers 77 | - vector 78 | - versions 79 | - with-utf8 80 | - yaml 81 | 82 | ghc-options: 83 | - -Wall 84 | - -Wcompat 85 | - -Widentities 86 | - -Wincomplete-record-updates 87 | - -Wincomplete-uni-patterns 88 | - -Wmissing-export-lists 89 | - -Wmissing-home-modules 90 | - -Wpartial-fields 91 | - -Wredundant-constraints 92 | 93 | library: 94 | source-dirs: src 95 | 96 | executables: 97 | updater: 98 | main: Main.hs 99 | source-dirs: app 100 | ghc-options: 101 | - -threaded 102 | - -rtsopts 103 | - -with-rtsopts=-N 104 | dependencies: 105 | - updater -------------------------------------------------------------------------------- /haskell/requests.sh: -------------------------------------------------------------------------------- 1 | 2 | getReleaseVersions () { 3 | publisherExtension="$1" 4 | writeTo="$2" 5 | 6 | req=$(cat < "$writeTo" 32 | } 33 | 34 | getLatestVersion () { 35 | publisherExtension="$1" 36 | writeTo="$2" 37 | 38 | req=$(cat < "$writeTo" 71 | } 72 | 73 | mkdir -p tmp 74 | 75 | publisher="AdaCore" 76 | name="ada" 77 | 78 | getReleaseVersions "${publisher}.${name}" "tmp/${name}.versions.json" 79 | jq -M . tmp/${name}.versions.json > tmp/formatted 80 | cat tmp/formatted > tmp/${name}.versions.json 81 | 82 | getLatestVersion "${publisher}.${name}" "tmp/${name}.latest.json" 83 | jq -M . tmp/${name}.latest.json > tmp/formatted 84 | cat tmp/formatted > tmp/${name}.latest.json 85 | 86 | rm tmp/formatted -------------------------------------------------------------------------------- /haskell/src/Configs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | {-# OPTIONS_GHC -Wno-orphans #-} 3 | 4 | module Configs where 5 | 6 | import Colog 7 | import Control.Lens 8 | import Data.Aeson (FromJSON (parseJSON), ToJSON, Value (String), withArray, withText) 9 | import Data.Aeson.Key (toText) 10 | import Data.Aeson.Lens (members, _String) 11 | import Data.Aeson.Types (Parser, typeMismatch) 12 | import Data.Default (Default (..)) 13 | import Data.Text qualified as T 14 | import Data.Text.Encoding (decodeUtf8) 15 | import Data.Yaml (decodeFileEither, parseMaybe) 16 | import Data.Yaml.Pretty (defConfig, encodePretty) 17 | import Extensions 18 | import GHC.Generics (Generic) 19 | 20 | type family HKD f a where 21 | HKD Identity a = a 22 | HKD f a = f a 23 | 24 | data ReleaseExtension = ReleaseExtension {publisher :: Publisher, name :: Name} 25 | deriving stock (Eq, Show, Generic) 26 | deriving anyclass (ToJSON) 27 | 28 | newtype ReleaseExtensions = ReleaseExtensions {releaseExtensions :: [ReleaseExtension]} 29 | deriving stock (Eq, Show, Generic) 30 | deriving anyclass (ToJSON) 31 | 32 | data SiteConfig f = SiteConfig 33 | { pageSize :: HKD f Int 34 | -- ^ Number of extensions per page in a request 35 | , pageCount :: HKD f Int 36 | -- ^ Number of extension pages to request 37 | , nThreads :: HKD f Int 38 | -- ^ Number of threads to use for fetching 39 | , release :: HKD f ReleaseExtensions 40 | -- ^ Extensions that require the release version 41 | , enable :: HKD f Bool 42 | } 43 | deriving stock (Generic) 44 | 45 | data AppConfig f = AppConfig 46 | { runN :: HKD f Int 47 | -- ^ Times to process a target site 48 | , processedLoggerDelay :: HKD f Int 49 | -- ^ Period in seconds till the next logging about processed extensions 50 | , garbageCollectorDelay :: HKD f Int 51 | -- ^ Period in seconds till the next garbage collection 52 | , collectGarbage :: HKD f Bool 53 | -- ^ Whether to collect garbage in /nix/store 54 | , programTimeout :: HKD f Int 55 | -- ^ Total time a program may run 56 | , retryDelay :: HKD f Int 57 | -- ^ Seconds to wait before retrying 58 | , nRetry :: HKD f Int 59 | -- ^ Number of times to retry an action 60 | , logSeverity :: HKD f Severity 61 | -- ^ Log severity level 62 | , dataDir :: HKD f FilePath 63 | -- ^ Data directory 64 | , queueCapacity :: HKD f Int 65 | -- ^ Max number of elements to store in a queue 66 | , maxMissingTimes :: HKD f Int 67 | -- ^ Max number of times an extension may be missing before it's removed from cache 68 | , requestResponseTimeout :: HKD f Int 69 | -- ^ Seconds to wait until the site responds 70 | , openVSX :: HKD f (SiteConfig f) 71 | -- ^ Config for Open VSX 72 | , vscodeMarketplace :: HKD f (SiteConfig f) 73 | -- ^ Config for VSCode Marketplace 74 | } 75 | deriving stock (Generic) 76 | 77 | deriving instance Generic Severity 78 | 79 | instance ToJSON (SiteConfig Identity) 80 | 81 | instance ToJSON Severity 82 | 83 | instance ToJSON (AppConfig Identity) 84 | 85 | instance Default (SiteConfig Maybe) 86 | 87 | type AppConfig' = (?config :: AppConfig Identity) 88 | 89 | data ProcessTargetConfig a = ProcessTargetConfig 90 | { target :: Target 91 | , nThreads :: Int 92 | , queueCapacity :: Int 93 | , dataDir :: FilePath 94 | , logger :: LogAction a Message 95 | } 96 | 97 | -- | Config for a crawler 98 | data CrawlerConfig a = CrawlerConfig 99 | { target :: Target 100 | , nRetry :: Int 101 | , logger :: LogAction a Message 102 | } 103 | 104 | -- | Config of a fetcher 105 | data FetcherConfig a = FetcherConfig 106 | { target :: Target 107 | , nThreads :: Int 108 | , queueCapacity :: Int 109 | , extConfigs :: [ExtensionConfig] 110 | , cacheDir :: FilePath 111 | , mkTargetJSON :: FilePath -> FilePath 112 | , logger :: LogAction a Message 113 | , tmpDir :: FilePath 114 | } 115 | 116 | instance FromJSON (SiteConfig Identity) 117 | instance FromJSON (SiteConfig Maybe) 118 | deriving instance Eq (SiteConfig Maybe) 119 | 120 | instance FromJSON ReleaseExtensions where 121 | parseJSON obj = 122 | pure $ 123 | ReleaseExtensions $ 124 | (obj ^@.. members) 125 | ^.. traversed 126 | . to 127 | ( \(k, v) -> 128 | let publisher = Publisher (toText k) 129 | in parseMaybe 130 | ( withArray "Names" $ \a -> 131 | pure $ a ^.. traversed . _String . to (\name -> ReleaseExtension{name = Name name, ..}) 132 | ) 133 | v 134 | ) 135 | . _Just 136 | . traversed 137 | 138 | instance FromJSON Severity where 139 | parseJSON :: Value -> Data.Aeson.Types.Parser Severity 140 | parseJSON = withText 141 | "Severity" 142 | \case 143 | "Debug" -> pure Debug 144 | "Info" -> pure Info 145 | "Warning" -> pure Warning 146 | "Error" -> pure Error 147 | p -> typeMismatch "Severity" (String p) 148 | 149 | instance FromJSON (AppConfig Identity) 150 | instance FromJSON (AppConfig Maybe) 151 | 152 | instance Default (AppConfig Maybe) 153 | 154 | _MICROSECONDS :: Int 155 | _MICROSECONDS = 1_000_000 156 | 157 | defaultOpenVSXConfig :: SiteConfig Identity 158 | defaultOpenVSXConfig = 159 | SiteConfig 160 | { pageSize = 1_000 161 | , pageCount = 10 162 | , nThreads = 30 163 | , release = ReleaseExtensions [] 164 | , enable = True 165 | } 166 | 167 | defaultVSCodeMarketplaceConfig :: SiteConfig Identity 168 | defaultVSCodeMarketplaceConfig = 169 | SiteConfig 170 | { pageSize = 1_000 171 | , pageCount = 100 172 | , nThreads = 100 173 | , release = ReleaseExtensions [] 174 | , enable = True 175 | } 176 | 177 | mkDefaultConfig :: SiteConfig Identity -> SiteConfig Maybe -> SiteConfig Identity 178 | mkDefaultConfig config SiteConfig{..} = 179 | SiteConfig 180 | { pageSize = pageSize ^. non config.pageSize 181 | , pageCount = pageCount ^. non config.pageCount 182 | , nThreads = nThreads ^. non config.nThreads 183 | , release = release ^. non config.release 184 | , enable = enable ^. non config.enable 185 | } 186 | 187 | mkDefaultAppConfig :: AppConfig Maybe -> AppConfig Identity 188 | mkDefaultAppConfig AppConfig{..} = 189 | AppConfig 190 | { runN = runN ^. non 1 191 | , processedLoggerDelay = processedLoggerDelay ^. non 2 192 | , garbageCollectorDelay = garbageCollectorDelay ^. non 30 193 | , collectGarbage = collectGarbage ^. non False 194 | , programTimeout = programTimeout ^. non 900 195 | , retryDelay = retryDelay ^. non 20 196 | , nRetry = nRetry ^. non 3 197 | , logSeverity = logSeverity ^. non Info 198 | , dataDir = dataDir ^. non "data" 199 | , queueCapacity = queueCapacity ^. non 200 200 | , maxMissingTimes = maxMissingTimes ^. non 5 201 | , requestResponseTimeout = requestResponseTimeout ^. non 100 202 | , openVSX = openVSX ^. non def . to (mkDefaultConfig defaultOpenVSXConfig) 203 | , vscodeMarketplace = vscodeMarketplace ^. non def . to (mkDefaultConfig defaultVSCodeMarketplaceConfig) 204 | } 205 | 206 | -- | A type for printing multiline stuff when using HLS 207 | newtype Pretty = Pretty String 208 | 209 | instance Show Pretty where 210 | show :: Pretty -> String 211 | show (Pretty s) = s 212 | 213 | -- >>> prettyConfig 214 | -- collectGarbage: false 215 | -- dataDir: data 216 | -- garbageCollectorDelay: 30 217 | -- logSeverity: Info 218 | -- maxMissingTimes: 5 219 | -- nRetry: 3 220 | -- openVSX: 221 | -- enable: true 222 | -- nThreads: 30 223 | -- pageCount: 10 224 | -- pageSize: 1000 225 | -- release: 226 | -- releaseExtensions: 227 | -- - name: gitlens 228 | -- publisher: eamodio 229 | -- - name: rust-analyzer 230 | -- publisher: rust-lang 231 | -- - name: rewrap 232 | -- publisher: stkb 233 | -- processedLoggerDelay: 2 234 | -- programTimeout: 900 235 | -- queueCapacity: 200 236 | -- requestResponseTimeout: 100 237 | -- retryDelay: 20 238 | -- runN: 1 239 | -- vscodeMarketplace: 240 | -- enable: true 241 | -- nThreads: 100 242 | -- pageCount: 100 243 | -- pageSize: 1000 244 | -- release: 245 | -- releaseExtensions: 246 | -- - name: gitlens 247 | -- publisher: eamodio 248 | -- - name: rust-analyzer 249 | -- publisher: rust-lang 250 | -- - name: rewrap 251 | -- publisher: stkb 252 | prettyConfig :: IO Pretty 253 | prettyConfig = Pretty . either show (T.unpack . decodeUtf8 . encodePretty defConfig . mkDefaultAppConfig) <$> decodeFileEither @(AppConfig Maybe) "config.yaml" 254 | -------------------------------------------------------------------------------- /haskell/src/Extensions.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | 3 | module Extensions where 4 | 5 | import Control.Lens 6 | import Control.Monad (guard) 7 | import Data.Aeson (FromJSON (..), Options (unwrapUnaryRecords), ToJSON (toJSON), Value (..), defaultOptions, genericParseJSON, genericToJSON, withText) 8 | import Data.Aeson.Lens (_String) 9 | import Data.Aeson.Types (parseFail) 10 | import Data.Aeson.Types qualified 11 | import Data.Functor (void) 12 | import Data.Generics.Labels () 13 | import Data.Hashable (Hashable) 14 | import Data.Maybe (fromJust, fromMaybe) 15 | import Data.String (IsString) 16 | import Data.String.Interpolate (i) 17 | import Data.Text (Text, unpack) 18 | import Data.Text qualified as T 19 | import Data.Text qualified as Text 20 | import Data.Time (UTCTime) 21 | import Data.Versions (SemVer (..), prettySemVer, semver') 22 | import Data.Void (Void) 23 | import GHC.Generics (Generic) 24 | import Text.Megaparsec (Parsec, choice, many, (<|>)) 25 | import Text.Megaparsec qualified as TM (parse, parseMaybe) 26 | import Text.Megaparsec.Char (asciiChar, string) 27 | import Text.Megaparsec.Char.Lexer (decimal) 28 | 29 | -- | Possible targets 30 | data Target = VSCodeMarketplace | OpenVSX 31 | deriving stock (Eq) 32 | 33 | -- | Select an action depending on a target 34 | targetSelect :: Target -> p -> p -> p 35 | targetSelect target f g = 36 | case target of 37 | VSCodeMarketplace -> f 38 | OpenVSX -> g 39 | 40 | -- | Possible action statuses 41 | ppTarget :: Target -> Text 42 | ppTarget x = targetSelect x "VSCode Marketplace" "Open VSX" 43 | 44 | instance Show Target where 45 | show :: Target -> String 46 | show = T.unpack . ppTarget 47 | 48 | data Flags 49 | = Flags'Validated 50 | | Flags'Public 51 | | Flags'Preview 52 | | Flags'Verified 53 | | Flags'Trial 54 | deriving stock (Enum, Bounded) 55 | 56 | _Flags :: Prism' Text Flags 57 | _Flags = prism' embed_ match_ 58 | where 59 | embed_ = \case 60 | Flags'Validated -> "validated" 61 | Flags'Public -> "public" 62 | Flags'Preview -> "preview" 63 | Flags'Verified -> "verified" 64 | Flags'Trial -> "trial" 65 | match_ :: Text -> Maybe Flags 66 | match_ x 67 | | x == embed_ Flags'Validated = Just Flags'Validated 68 | | x == embed_ Flags'Public = Just Flags'Public 69 | | x == embed_ Flags'Preview = Just Flags'Preview 70 | | x == embed_ Flags'Verified = Just Flags'Verified 71 | | x == embed_ Flags'Trial = Just Flags'Trial 72 | | otherwise = Nothing 73 | 74 | instance Show Flags where 75 | show :: Flags -> String 76 | show = Text.unpack . (_Flags #) 77 | 78 | extFlagsAllowed :: [Text] 79 | extFlagsAllowed = enumFrom minBound ^.. traversed . to (_Flags #) 80 | 81 | newtype Name = Name {_name :: Text} 82 | deriving newtype (IsString, Eq, Ord, Hashable) 83 | deriving stock (Generic) 84 | 85 | newtype Publisher = Publisher {_publisher :: Text} 86 | deriving newtype (IsString, Eq, Ord, Hashable) 87 | deriving stock (Generic) 88 | 89 | newtype LastUpdated = LastUpdated {_lastUpdated :: UTCTime} 90 | deriving newtype (Eq, Ord, Hashable, Show) 91 | deriving stock (Generic) 92 | 93 | newtype Version = Version {_version :: SemVer} 94 | deriving newtype (Eq, Ord, Hashable) 95 | deriving stock (Generic) 96 | 97 | instance Show Version where 98 | show :: Version -> String 99 | show v = T.unpack $ prettySemVer v._version 100 | 101 | data VersionModifier = Veq | Vgeq 102 | deriving stock (Ord, Eq, Generic) 103 | deriving anyclass (Hashable) 104 | 105 | data EngineVersion = EngineVersion 106 | { _modifier :: VersionModifier 107 | , _version :: SemVer 108 | } 109 | deriving stock (Eq, Ord, Generic) 110 | deriving anyclass (Hashable) 111 | 112 | -- platform of an extension 113 | data Platform 114 | = -- | universal extensions should have the lowest order 115 | PUniversal 116 | | PLinux_x64 117 | | PLinux_arm64 118 | | PDarwin_x64 119 | | PDarwin_arm64 120 | deriving stock (Generic, Eq, Ord, Enum, Bounded) 121 | deriving anyclass (Hashable) 122 | 123 | instance FromJSON Platform where 124 | parseJSON :: Value -> Data.Aeson.Types.Parser Platform 125 | parseJSON (String s) = 126 | case s ^? _Platform of 127 | Just s' -> pure s' 128 | Nothing -> parseFail "Could not parse platform" 129 | parseJSON _ = parseFail "Expected a string" 130 | 131 | instance ToJSON Platform where 132 | toJSON :: Platform -> Value 133 | toJSON = String . review _Platform 134 | 135 | _Platform :: Prism' Text Platform 136 | _Platform = prism' embed_ match_ 137 | where 138 | embed_ :: Platform -> Text 139 | embed_ = \case 140 | PUniversal -> "universal" 141 | PLinux_x64 -> "linux-x64" 142 | PLinux_arm64 -> "linux-arm64" 143 | PDarwin_x64 -> "darwin-x64" 144 | PDarwin_arm64 -> "darwin-arm64" 145 | match_ :: Text -> Maybe Platform 146 | match_ x 147 | | x == embed_ PUniversal = Just PUniversal 148 | | x == embed_ PLinux_x64 = Just PLinux_x64 149 | | x == embed_ PLinux_arm64 = Just PLinux_arm64 150 | | x == embed_ PDarwin_x64 = Just PDarwin_x64 151 | | x == embed_ PDarwin_arm64 = Just PDarwin_arm64 152 | | otherwise = Nothing 153 | 154 | instance Show Platform where 155 | show :: Platform -> String 156 | show = unpack . review _Platform 157 | 158 | _VersionModifier :: Prism' Text VersionModifier 159 | _VersionModifier = prism' embed_ match_ 160 | where 161 | embed_ = \case 162 | Veq -> "" 163 | Vgeq -> "^" 164 | match_ x 165 | | x == embed_ Veq = Just Veq 166 | | x == embed_ Vgeq = Just Vgeq 167 | | x == ">=" = Just Vgeq 168 | | otherwise = Nothing 169 | 170 | instance Show VersionModifier where 171 | show = unpack . (_VersionModifier #) 172 | 173 | instance Show EngineVersion where 174 | show = unpack . (_EngineVersion #) 175 | 176 | type Parser = Parsec Void Text 177 | 178 | versions :: [Text] 179 | versions = 180 | [ "2022.09.290700" 181 | , "23.3.0-canary-open-0313-1900" 182 | , "1.0.0-beta.1" 183 | , "1.13.1712347770" 184 | , "0.1.8+vizcar" 185 | ] 186 | 187 | -- | Parse a SemVer-like 'Version'. 188 | -- 189 | -- Allow leading zeros. 190 | -- 191 | -- >>> TM.parseMaybe parseVersion <$> versions 192 | parseVersion :: Parser Version 193 | parseVersion = do 194 | _svMajor <- decimal 195 | void $ string "." 196 | _svMinor <- decimal 197 | void $ string "." 198 | _svPatch <- decimal 199 | rest <- many asciiChar 200 | let semVer = SemVer{_svPreRel = Nothing, _svMeta = Nothing, ..} 201 | pure $ Version $ fromMaybe semVer (TM.parseMaybe semver' (prettySemVer semVer <> T.pack rest)) 202 | 203 | -- | Examples of versions for VSCode engine used in extensions 204 | engineVersions :: [Text] 205 | engineVersions = 206 | [ "^0.0.0" 207 | , "^0.10.x" 208 | , "^1.27.0-insider" 209 | , ">=0.10.0" 210 | , ">=0.10.x" 211 | , ">=0.9.0-pre.1" 212 | , "0.1.x" 213 | , "1.57.0-insider" 214 | , "1.x.x" 215 | , "*" 216 | ] 217 | 218 | -- | Parse 'EngineVersion' 219 | -- 220 | -- >>> TM.parseMaybe parseEngineVersion <$> engineVersions 221 | -- [Just ^0.0.0,Just ^0.10.0,Just ^1.27.0-insider,Just ^0.10.0,Just ^0.10.0,Just ^0.9.0-pre.1,Just 0.1.0,Just 1.57.0-insider,Just 1.0.0,Just ^0.0.0] 222 | parseEngineVersion :: Parser EngineVersion 223 | parseEngineVersion = 224 | (string "*" >> pure defaultEngineVersion) 225 | <|> do 226 | _modifier <- 227 | choice 228 | [ Vgeq <$ (string "^" <|> string ">=") 229 | , Veq <$ string "" 230 | ] 231 | _svMajor <- decimal <|> (0 <$ string "x") 232 | void $ string "." 233 | _svMinor <- decimal <|> (0 <$ string "x") 234 | void $ string "." 235 | _svPatch <- decimal <|> (0 <$ string "x") 236 | rest <- many asciiChar 237 | let semVer = SemVer{_svPreRel = Nothing, _svMeta = Nothing, ..} 238 | pure EngineVersion{_version = fromMaybe semVer (TM.parseMaybe semver' (prettySemVer semVer <> T.pack rest)), ..} 239 | 240 | _EngineVersion :: Prism' Text EngineVersion 241 | _EngineVersion = prism' embed_ match_ 242 | where 243 | embed_ EngineVersion{..} = [i|#{review _VersionModifier _modifier}#{prettySemVer _version}|] 244 | match_ = TM.parseMaybe parseEngineVersion 245 | 246 | aesonOptions :: Options 247 | aesonOptions = defaultOptions{unwrapUnaryRecords = True} 248 | 249 | instance Show Name where show = Text.unpack . _name 250 | instance FromJSON Name where parseJSON = genericParseJSON aesonOptions 251 | instance ToJSON Name where toJSON = genericToJSON aesonOptions 252 | instance Show Publisher where show = Text.unpack . _publisher 253 | instance FromJSON Publisher where parseJSON = genericParseJSON aesonOptions 254 | instance ToJSON Publisher where toJSON = genericToJSON aesonOptions 255 | instance FromJSON LastUpdated where parseJSON = genericParseJSON aesonOptions 256 | instance ToJSON LastUpdated where toJSON = genericToJSON aesonOptions 257 | 258 | instance FromJSON Version where 259 | parseJSON :: Value -> Data.Aeson.Types.Parser Version 260 | parseJSON = withText "SemVer" $ either (parseFail . show) pure . TM.parse parseVersion "SemVer" 261 | 262 | instance ToJSON Version where 263 | toJSON :: Version -> Value 264 | toJSON v = String $ prettySemVer v._version 265 | 266 | instance FromJSON EngineVersion where 267 | parseJSON :: Value -> Data.Aeson.Types.Parser EngineVersion 268 | parseJSON = withText "Engine version" $ \engineVersion -> do 269 | let t' = engineVersion ^? _EngineVersion 270 | guard (has _Just t') 271 | pure (fromJust t') 272 | 273 | instance ToJSON EngineVersion where 274 | toJSON :: EngineVersion -> Value 275 | toJSON = (_String #) . (_EngineVersion #) 276 | 277 | -- | A simple config that is enough to fetch an extension 278 | data ExtensionConfig = ExtensionConfig 279 | { name :: Name 280 | , publisher :: Publisher 281 | , lastUpdated :: LastUpdated 282 | , version :: Version 283 | , platform :: Platform 284 | , missingTimes :: Int 285 | , engineVersion :: EngineVersion 286 | } 287 | deriving stock (Generic, Show, Eq) 288 | deriving anyclass (FromJSON, ToJSON, Hashable) 289 | 290 | defaultEngineVersion :: EngineVersion 291 | defaultEngineVersion = 292 | EngineVersion 293 | { _modifier = Vgeq 294 | , _version = 295 | SemVer 296 | { _svMajor = 0 297 | , _svMinor = 0 298 | , _svPatch = 0 299 | , _svPreRel = Nothing 300 | , _svMeta = Nothing 301 | } 302 | } 303 | 304 | -- | Full necessary info about an extension 305 | data ExtensionInfo = ExtensionInfo 306 | { name :: Name 307 | , publisher :: Publisher 308 | , lastUpdated :: LastUpdated 309 | , version :: Version 310 | , sha256 :: Text 311 | , platform :: Platform 312 | , missingTimes :: Int 313 | -- ^ how many times the extension could not be fetched 314 | , engineVersion :: EngineVersion 315 | -- ^ engine version that's required to run this extension 316 | -- 317 | -- See [Visual Studio Code compatibility](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#visual-studio-code-compatibility) 318 | } 319 | deriving stock (Generic, Show) 320 | deriving anyclass (FromJSON, ToJSON) 321 | -------------------------------------------------------------------------------- /haskell/src/Logger.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE MultiParamTypeClasses #-} 2 | 3 | module Logger where 4 | 5 | import Colog (LogAction (..), LoggerT (..), Message) 6 | import Control.Monad.IO.Class (MonadIO (..)) 7 | import Control.Monad.Reader (ReaderT (..)) 8 | import Control.Monad.Reader.Class (MonadReader (ask, local)) 9 | import Data.String.Interpolate (i) 10 | import Turtle (Alternative (..)) 11 | import UnliftIO (MonadUnliftIO (withRunInIO)) 12 | 13 | newtype MyLoggerT msg m a = MyLoggerT {_myLoggerT :: LoggerT msg m a} 14 | deriving newtype (Functor, Applicative, Monad, MonadIO) 15 | 16 | type MyLogger = MyLoggerT Message IO 17 | 18 | instance Alternative m => Alternative (MyLoggerT msg m) where 19 | empty = MyLoggerT $ LoggerT empty 20 | (<|>) :: MyLoggerT msg m a -> MyLoggerT msg m a -> MyLoggerT msg m a 21 | (<|>) (MyLoggerT (LoggerT r1)) (MyLoggerT (LoggerT r2)) = MyLoggerT (LoggerT (r1 <|> r2)) 22 | 23 | instance MonadUnliftIO m => MonadUnliftIO (MyLoggerT msg m) where 24 | withRunInIO :: ((forall a. MyLoggerT msg m a -> IO a) -> IO b) -> MyLoggerT msg m b 25 | withRunInIO action = MyLoggerT $ LoggerT $ withRunInIO $ \runInIO -> action $ \(MyLoggerT (LoggerT x)) -> runInIO x 26 | 27 | instance Monad m => MonadReader (LogAction (MyLoggerT msg m) msg) (MyLoggerT msg m) where 28 | ask :: MyLoggerT msg m (LogAction (MyLoggerT msg m) msg) 29 | ask = MyLoggerT $ LoggerT $ ReaderT $ \(LogAction l) -> pure $ LogAction $ MyLoggerT . l 30 | local :: (LogAction (MyLoggerT msg m) msg -> LogAction (MyLoggerT msg m) msg) -> MyLoggerT msg m a -> MyLoggerT msg m a 31 | local f g = do 32 | let MyLoggerT (LoggerT (ReaderT r)) = g 33 | MyLoggerT (LoggerT (ReaderT $ \(LogAction l) -> r (LogAction (_myLoggerT . unLogAction (f (LogAction (MyLoggerT . l))))))) 34 | 35 | data ActionStatus = INFO | START | FAIL | ABORT | FINISH 36 | 37 | instance Show ActionStatus where 38 | show :: ActionStatus -> String 39 | show d = 40 | let 41 | repr :: ActionStatus -> String 42 | repr e = case e of 43 | INFO -> "INFO" 44 | START -> "START" 45 | FAIL -> "FAIL" 46 | ABORT -> "ABORT" 47 | FINISH -> "FINISH" 48 | width = maximum $ length . repr <$> [INFO, START, FAIL, ABORT, FINISH] 49 | in 50 | (\x -> [i|[ #{x <> (replicate (width - length x) ' ') } ]|]) (repr d) 51 | -------------------------------------------------------------------------------- /haskell/src/Requests.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | 3 | module Requests where 4 | 5 | import Data.Aeson 6 | import Data.Text 7 | import GHC.Generics 8 | 9 | -- types for constructing a request to VSCode Marketplace 10 | data Criterion = Criterion 11 | { filterType :: Int 12 | , value :: Text 13 | } 14 | deriving stock (Generic) 15 | deriving anyclass (ToJSON) 16 | 17 | data Filter = Filter 18 | { criteria :: [Criterion] 19 | , pageNumber :: Int 20 | , pageSize :: Int 21 | , sortBy :: Int 22 | , sortOrder :: Int 23 | } 24 | deriving stock (Generic) 25 | deriving anyclass (ToJSON) 26 | 27 | data Req = Req 28 | { filters :: [Filter] 29 | , assetTypes :: [String] 30 | , flags :: Int 31 | } 32 | deriving stock (Generic) 33 | deriving anyclass (ToJSON) 34 | -------------------------------------------------------------------------------- /haskell/updater.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.36.0. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | 7 | name: updater 8 | version: 0.1.0.0 9 | description: Please see the README on GitHub at 10 | homepage: https://github.com/nix-community/nix-vscode-extensions#readme 11 | bug-reports: https://github.com/nix-community/nix-vscode-extensions/issues 12 | author: value 13 | maintainer: value 14 | copyright: value 15 | license: BSD3 16 | license-file: LICENSE 17 | build-type: Simple 18 | extra-source-files: 19 | README.md 20 | CHANGELOG.md 21 | 22 | source-repository head 23 | type: git 24 | location: https://github.com/nix-community/nix-vscode-extensions 25 | 26 | library 27 | exposed-modules: 28 | Configs 29 | Extensions 30 | Logger 31 | Requests 32 | other-modules: 33 | Paths_updater 34 | hs-source-dirs: 35 | src 36 | default-extensions: 37 | BangPatterns 38 | BlockArguments 39 | ConstraintKinds 40 | DeriveGeneric 41 | DerivingStrategies 42 | DuplicateRecordFields 43 | FlexibleContexts 44 | FlexibleInstances 45 | GeneralizedNewtypeDeriving 46 | ImplicitParams 47 | ImportQualifiedPost 48 | InstanceSigs 49 | LambdaCase 50 | NamedFieldPuns 51 | NumericUnderscores 52 | OverloadedLabels 53 | OverloadedRecordDot 54 | OverloadedStrings 55 | QuasiQuotes 56 | RankNTypes 57 | RecordWildCards 58 | ScopedTypeVariables 59 | StandaloneDeriving 60 | TypeApplications 61 | TypeFamilies 62 | ViewPatterns 63 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints 64 | build-depends: 65 | aeson 66 | , async 67 | , async-pool 68 | , base >=4.7 && <5 69 | , bytestring 70 | , co-log 71 | , co-log-concurrent 72 | , data-default 73 | , generic-lens 74 | , hashable 75 | , http-client 76 | , http-conduit 77 | , http-types 78 | , lens 79 | , lens-aeson 80 | , megaparsec 81 | , mtl 82 | , optparse-generic 83 | , stm 84 | , stm-chans 85 | , string-interpolate 86 | , text 87 | , time 88 | , turtle 89 | , unliftio 90 | , unordered-containers 91 | , vector 92 | , versions 93 | , with-utf8 94 | , yaml 95 | default-language: Haskell2010 96 | 97 | executable updater 98 | main-is: Main.hs 99 | other-modules: 100 | Paths_updater 101 | hs-source-dirs: 102 | app 103 | default-extensions: 104 | BangPatterns 105 | BlockArguments 106 | ConstraintKinds 107 | DeriveGeneric 108 | DerivingStrategies 109 | DuplicateRecordFields 110 | FlexibleContexts 111 | FlexibleInstances 112 | GeneralizedNewtypeDeriving 113 | ImplicitParams 114 | ImportQualifiedPost 115 | InstanceSigs 116 | LambdaCase 117 | NamedFieldPuns 118 | NumericUnderscores 119 | OverloadedLabels 120 | OverloadedRecordDot 121 | OverloadedStrings 122 | QuasiQuotes 123 | RankNTypes 124 | RecordWildCards 125 | ScopedTypeVariables 126 | StandaloneDeriving 127 | TypeApplications 128 | TypeFamilies 129 | ViewPatterns 130 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N 131 | build-depends: 132 | aeson 133 | , async 134 | , async-pool 135 | , base >=4.7 && <5 136 | , bytestring 137 | , co-log 138 | , co-log-concurrent 139 | , data-default 140 | , generic-lens 141 | , hashable 142 | , http-client 143 | , http-conduit 144 | , http-types 145 | , lens 146 | , lens-aeson 147 | , megaparsec 148 | , mtl 149 | , optparse-generic 150 | , stm 151 | , stm-chans 152 | , string-interpolate 153 | , text 154 | , time 155 | , turtle 156 | , unliftio 157 | , unordered-containers 158 | , updater 159 | , vector 160 | , versions 161 | , with-utf8 162 | , yaml 163 | default-language: Haskell2010 164 | -------------------------------------------------------------------------------- /mkExtension.nix: -------------------------------------------------------------------------------- 1 | # Each `${publisher}.${name}` MUST provide a function `{ mktplcRef, vsix } -> Derivation` 2 | # Each Derivation MUST be produced via overridable `buildVscodeMarketplaceExtension` (see below) 3 | 4 | # Custom fixes are loaded via `mkExtensionLocal` 5 | 6 | { pkgs, pkgsWithFixes }: 7 | let 8 | inherit (pkgs) lib; 9 | 10 | makeOverridable = 11 | f: args: 12 | lib.customisation.makeOverridable f ( 13 | if builtins.isFunction args then 14 | let 15 | x = args (f x); 16 | in 17 | x 18 | else 19 | args 20 | ); 21 | 22 | buildVscodeMarketplaceExtension = makeOverridable pkgs.vscode-utils.buildVscodeMarketplaceExtension; 23 | 24 | buildVscodeExtension = makeOverridable pkgs.vscode-utils.buildVscodeExtension; 25 | 26 | # We don't modify callPackage because extensions 27 | # may use its original version 28 | pkgs' = pkgs // { 29 | vscode-utils = pkgs.vscode-utils // { 30 | inherit buildVscodeMarketplaceExtension buildVscodeExtension; 31 | }; 32 | }; 33 | 34 | applyMkExtension = builtins.mapAttrs ( 35 | publisher: builtins.mapAttrs (name: f: { mktplcRef, vsix }@extensionConfig: f (extensionConfig)) 36 | ); 37 | 38 | mkExtensionLocal = applyMkExtension (import ./extensions { pkgs = pkgs'; }); 39 | 40 | extensionsRemoved = (import ./removed.nix).${pkgs.system} or [ ]; 41 | 42 | callPackage = pkgs.beam.beamLib.callPackageWith pkgs'; 43 | 44 | # TODO find a cleaner way to get the store path of nixpkgs from given pkgs 45 | pathNixpkgs = 46 | if pkgsWithFixes ? outPath then 47 | pkgsWithFixes.outPath 48 | else 49 | lib.trivial.pipe pkgsWithFixes.hello.inputDerivation._derivation_original_args [ 50 | builtins.tail 51 | builtins.head 52 | builtins.dirOf 53 | builtins.dirOf 54 | builtins.dirOf 55 | builtins.dirOf 56 | ]; 57 | 58 | extensionsNixpkgs = 59 | callPackage "${pathNixpkgs}/pkgs/applications/editors/vscode/extensions/default.nix" 60 | { config.allowAliases = false; }; 61 | 62 | extensionsProblematic = 63 | # Some arguments of the function that produces a derivation 64 | # are provided in the `let .. in` expression before the call to that function 65 | 66 | # TODO make a PR to nixpkgs to simplify overriding for these extensions 67 | [ 68 | "anweber.vscode-httpyac" 69 | "chenglou92.rescript-vscode" 70 | # Wait for https://github.com/NixOS/nixpkgs/pull/383013 to be merged 71 | "vadimcn.vscode-lldb" 72 | "rust-lang.rust-analyzer" 73 | ] 74 | ++ 75 | # Have old fixes 76 | [ 77 | # Doesn't build due to the patch 78 | # https://github.com/NixOS/nixpkgs/tree/a3cd526f08839bd963e7d106b7869694b0579a94/pkgs/applications/editors/vscode/extensions/hashicorp.terraform 79 | # TODO newer fix 80 | "hashicorp.terraform" 81 | ]; 82 | 83 | pathSpecial = { 84 | ms-ceintl = "language-packs.nix"; 85 | wakatime = "WakaTime.vscode-wakatime"; 86 | }; 87 | 88 | mkExtensionNixpkgs = builtins.mapAttrs ( 89 | publisher: 90 | builtins.mapAttrs ( 91 | name: extension: 92 | let 93 | extensionId = "${publisher}.${name}"; 94 | in 95 | if builtins.elem extensionId extensionsRemoved then 96 | _: { vscodeExtPublisher = publisher; } 97 | else 98 | let 99 | subPath = pathSpecial.${publisher} or extensionId; 100 | 101 | path = "${pathNixpkgs}/pkgs/applications/editors/vscode/extensions/${subPath}"; 102 | 103 | extension' = 104 | if builtins.pathExists path then 105 | let 106 | extension'' = callPackage path { }; 107 | in 108 | if publisher == "ms-ceintl" then extension''.${name} else extension'' 109 | else 110 | extension; 111 | in 112 | { mktplcRef, vsix }@extensionConfig: 113 | if builtins.elem extensionId extensionsProblematic then 114 | buildVscodeMarketplaceExtension extensionConfig 115 | else 116 | (extension'.override 117 | or (abort "The extension '${publisher}.${name}' doesn't have an 'override' attribute.") 118 | ) 119 | extensionConfig 120 | ) 121 | ) extensionsNixpkgs; 122 | 123 | chooseMkExtension = 124 | self: 125 | { 126 | mktplcRef, 127 | vsix, 128 | engineVersion, 129 | }@extensionConfig: 130 | let 131 | mkExtension = ( 132 | (self.${mktplcRef.publisher} or { }).${mktplcRef.name} or ( 133 | if builtins.elem "${mktplcRef.publisher}.${mktplcRef.name}" extensionsRemoved then 134 | # In `flake.nix`, there is a check whether the result is a derivation. 135 | _: { vscodeExtPublisher = mktplcRef.publisher; } 136 | else 137 | buildVscodeMarketplaceExtension 138 | ) 139 | ); 140 | in 141 | # TODO fix on macOS after enabling 142 | # (mkExtension { inherit mktplcRef vsix engineVersion; }).overrideAttrs (prev: { 143 | # passthru = prev.passthru // extensionConfig; 144 | # }) 145 | mkExtension { inherit mktplcRef vsix; }; 146 | in 147 | builtins.foldl' lib.attrsets.recursiveUpdate { } [ 148 | mkExtensionNixpkgs 149 | mkExtensionLocal 150 | { __functor = chooseMkExtension; } 151 | ] 152 | -------------------------------------------------------------------------------- /nix-dev/default.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | let 3 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 4 | in 5 | fetchTarball { 6 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 7 | sha256 = lock.nodes.flake-compat.locked.narHash; 8 | } 9 | ) { src = ./.; }).defaultNix 10 | -------------------------------------------------------------------------------- /nix-dev/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "cache-nix-action": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1740774711, 7 | "narHash": "sha256-4Qhlv3xZ8yDCYtHjrgeYIgXiPDMCFE1NZ9kmBb1ij6s=", 8 | "owner": "nix-community", 9 | "repo": "cache-nix-action", 10 | "rev": "3e3fc4f051e57b7cebc8a8887ba457d4dcfa6bcc", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "nix-community", 15 | "repo": "cache-nix-action", 16 | "type": "github" 17 | } 18 | }, 19 | "devshell": { 20 | "inputs": { 21 | "flake-utils": "flake-utils", 22 | "nixpkgs": [ 23 | "nixpkgs" 24 | ] 25 | }, 26 | "locked": { 27 | "lastModified": 1713447246, 28 | "narHash": "sha256-lafrzPN19LdRC3S0s6moAjI5ICyY9HSE1EJAXiEBLAw=", 29 | "owner": "deemp", 30 | "repo": "devshell", 31 | "rev": "eece9ce8466060972c2f8aa332b7e7263ad115d6", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "deemp", 36 | "repo": "devshell", 37 | "type": "github" 38 | } 39 | }, 40 | "flake-compat": { 41 | "flake": false, 42 | "locked": { 43 | "lastModified": 1733328505, 44 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 45 | "owner": "edolstra", 46 | "repo": "flake-compat", 47 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 48 | "type": "github" 49 | }, 50 | "original": { 51 | "owner": "edolstra", 52 | "repo": "flake-compat", 53 | "type": "github" 54 | } 55 | }, 56 | "flake-parts": { 57 | "inputs": { 58 | "nixpkgs-lib": "nixpkgs-lib" 59 | }, 60 | "locked": { 61 | "lastModified": 1740872218, 62 | "narHash": "sha256-ZaMw0pdoUKigLpv9HiNDH2Pjnosg7NBYMJlHTIsHEUo=", 63 | "owner": "hercules-ci", 64 | "repo": "flake-parts", 65 | "rev": "3876f6b87db82f33775b1ef5ea343986105db764", 66 | "type": "github" 67 | }, 68 | "original": { 69 | "owner": "hercules-ci", 70 | "repo": "flake-parts", 71 | "type": "github" 72 | } 73 | }, 74 | "flake-utils": { 75 | "inputs": { 76 | "systems": "systems" 77 | }, 78 | "locked": { 79 | "lastModified": 1731533236, 80 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 81 | "owner": "numtide", 82 | "repo": "flake-utils", 83 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 84 | "type": "github" 85 | }, 86 | "original": { 87 | "id": "flake-utils", 88 | "type": "indirect" 89 | } 90 | }, 91 | "nix-github-actions": { 92 | "inputs": { 93 | "nixpkgs": [ 94 | "nix-unit", 95 | "nixpkgs" 96 | ] 97 | }, 98 | "locked": { 99 | "lastModified": 1731952509, 100 | "narHash": "sha256-p4gB3Rhw8R6Ak4eMl8pqjCPOLCZRqaehZxdZ/mbFClM=", 101 | "owner": "nix-community", 102 | "repo": "nix-github-actions", 103 | "rev": "7b5f051df789b6b20d259924d349a9ba3319b226", 104 | "type": "github" 105 | }, 106 | "original": { 107 | "owner": "nix-community", 108 | "repo": "nix-github-actions", 109 | "type": "github" 110 | } 111 | }, 112 | "nix-unit": { 113 | "inputs": { 114 | "flake-parts": [ 115 | "flake-parts" 116 | ], 117 | "nix-github-actions": "nix-github-actions", 118 | "nixpkgs": [ 119 | "nixpkgs" 120 | ], 121 | "treefmt-nix": [ 122 | "treefmt-nix" 123 | ] 124 | }, 125 | "locked": { 126 | "lastModified": 1733705700, 127 | "narHash": "sha256-stdx1z86yj66bChYswqjRw8BpdJRwxkH2XIJrsygpiM=", 128 | "owner": "nix-community", 129 | "repo": "nix-unit", 130 | "rev": "d867d72d21da3b7d83f0feef73b0ac7f72b16437", 131 | "type": "github" 132 | }, 133 | "original": { 134 | "owner": "nix-community", 135 | "repo": "nix-unit", 136 | "type": "github" 137 | } 138 | }, 139 | "nixpkgs": { 140 | "locked": { 141 | "lastModified": 1740547748, 142 | "narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=", 143 | "owner": "nixos", 144 | "repo": "nixpkgs", 145 | "rev": "3a05eebede89661660945da1f151959900903b6a", 146 | "type": "github" 147 | }, 148 | "original": { 149 | "owner": "nixos", 150 | "repo": "nixpkgs", 151 | "rev": "3a05eebede89661660945da1f151959900903b6a", 152 | "type": "github" 153 | } 154 | }, 155 | "nixpkgs-lib": { 156 | "locked": { 157 | "dir": "lib", 158 | "lastModified": 1740547748, 159 | "narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=", 160 | "owner": "nixos", 161 | "repo": "nixpkgs", 162 | "rev": "3a05eebede89661660945da1f151959900903b6a", 163 | "type": "github" 164 | }, 165 | "original": { 166 | "dir": "lib", 167 | "owner": "nixos", 168 | "repo": "nixpkgs", 169 | "rev": "3a05eebede89661660945da1f151959900903b6a", 170 | "type": "github" 171 | } 172 | }, 173 | "root": { 174 | "inputs": { 175 | "cache-nix-action": "cache-nix-action", 176 | "devshell": "devshell", 177 | "flake-compat": "flake-compat", 178 | "flake-parts": "flake-parts", 179 | "nix-unit": "nix-unit", 180 | "nixpkgs": "nixpkgs", 181 | "systems": "systems_2", 182 | "treefmt-nix": "treefmt-nix" 183 | } 184 | }, 185 | "systems": { 186 | "locked": { 187 | "lastModified": 1681028828, 188 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 189 | "owner": "nix-systems", 190 | "repo": "default", 191 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 192 | "type": "github" 193 | }, 194 | "original": { 195 | "owner": "nix-systems", 196 | "repo": "default", 197 | "type": "github" 198 | } 199 | }, 200 | "systems_2": { 201 | "locked": { 202 | "lastModified": 1681028828, 203 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 204 | "owner": "nix-systems", 205 | "repo": "default", 206 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 207 | "type": "github" 208 | }, 209 | "original": { 210 | "owner": "nix-systems", 211 | "repo": "default", 212 | "type": "github" 213 | } 214 | }, 215 | "treefmt-nix": { 216 | "inputs": { 217 | "nixpkgs": [ 218 | "nixpkgs" 219 | ] 220 | }, 221 | "locked": { 222 | "lastModified": 1739829690, 223 | "narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=", 224 | "owner": "numtide", 225 | "repo": "treefmt-nix", 226 | "rev": "3d0579f5cc93436052d94b73925b48973a104204", 227 | "type": "github" 228 | }, 229 | "original": { 230 | "owner": "numtide", 231 | "repo": "treefmt-nix", 232 | "type": "github" 233 | } 234 | } 235 | }, 236 | "root": "root", 237 | "version": 7 238 | } 239 | -------------------------------------------------------------------------------- /nix-dev/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs/3a05eebede89661660945da1f151959900903b6a"; 4 | cache-nix-action = { 5 | url = "github:nix-community/cache-nix-action"; 6 | flake = false; 7 | }; 8 | systems.url = "github:nix-systems/default"; 9 | flake-parts = { 10 | url = "github:hercules-ci/flake-parts"; 11 | inputs.nixpkgs-lib.url = "github:nixos/nixpkgs/3a05eebede89661660945da1f151959900903b6a?dir=lib"; 12 | }; 13 | devshell = { 14 | url = "github:deemp/devshell"; 15 | inputs.nixpkgs.follows = "nixpkgs"; 16 | inputs.flake-utils.inputs.systems.follows = "systems"; 17 | }; 18 | flake-compat = { 19 | url = "github:edolstra/flake-compat"; 20 | flake = false; 21 | }; 22 | nix-unit = { 23 | url = "github:nix-community/nix-unit"; 24 | inputs.flake-parts.follows = "flake-parts"; 25 | inputs.nixpkgs.follows = "nixpkgs"; 26 | inputs.treefmt-nix.follows = "treefmt-nix"; 27 | }; 28 | treefmt-nix = { 29 | url = "github:numtide/treefmt-nix"; 30 | inputs.nixpkgs.follows = "nixpkgs"; 31 | }; 32 | }; 33 | outputs = _: { }; 34 | } 35 | -------------------------------------------------------------------------------- /removed.nix: -------------------------------------------------------------------------------- 1 | # See the Removed extensions section in the README.md 2 | { 3 | aarch64-darwin = [ 4 | # https://github.com/nix-community/nix-vscode-extensions/issues/100 5 | # https://github.com/NixOS/nixpkgs/blob/e983b262e7336dba29e057ce76f67ecb9bd495b3/pkgs/applications/editors/vscode/extensions/ms-vscode.cpptools/default.nix#L55 6 | "ms-vscode.cpptools" 7 | 8 | # https://github.com/NixOS/nixpkgs/blob/cb04ce958b9a3156922c7f8dcac09264d679ede8/pkgs/applications/editors/vscode/extensions/yzane.markdown-pdf/default.nix#L6 9 | # https://github.com/NixOS/nixpkgs/blob/cb04ce958b9a3156922c7f8dcac09264d679ede8/pkgs/applications/networking/browsers/chromium/browser.nix#L115 10 | "yzane.markdown-pdf" 11 | ]; 12 | x86_64-darwin = [ 13 | "ms-vscode.cpptools" 14 | "yzane.markdown-pdf" 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /template/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nix-vscode-extensions": { 22 | "inputs": { 23 | "flake-utils": "flake-utils", 24 | "nixpkgs": "nixpkgs" 25 | }, 26 | "locked": { 27 | "lastModified": 1749088964, 28 | "narHash": "sha256-l0nq/gnL6x+hBPuzNEBLFotPzwGyqUcPiwcmGcRyW0A=", 29 | "owner": "nix-community", 30 | "repo": "nix-vscode-extensions", 31 | "rev": "665c9161807e8885ee41391ac3c8cbf9f2d123cd", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "nix-community", 36 | "repo": "nix-vscode-extensions", 37 | "type": "github" 38 | } 39 | }, 40 | "nixpkgs": { 41 | "locked": { 42 | "lastModified": 1744868846, 43 | "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", 44 | "owner": "NixOS", 45 | "repo": "nixpkgs", 46 | "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "NixOS", 51 | "repo": "nixpkgs", 52 | "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", 53 | "type": "github" 54 | } 55 | }, 56 | "root": { 57 | "inputs": { 58 | "flake-utils": [ 59 | "nix-vscode-extensions", 60 | "flake-utils" 61 | ], 62 | "nix-vscode-extensions": "nix-vscode-extensions", 63 | "nixpkgs": [ 64 | "nix-vscode-extensions", 65 | "nixpkgs" 66 | ] 67 | } 68 | }, 69 | "systems": { 70 | "locked": { 71 | "lastModified": 1681028828, 72 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 73 | "owner": "nix-systems", 74 | "repo": "default", 75 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 76 | "type": "github" 77 | }, 78 | "original": { 79 | "owner": "nix-systems", 80 | "repo": "default", 81 | "type": "github" 82 | } 83 | } 84 | }, 85 | "root": "root", 86 | "version": 7 87 | } 88 | -------------------------------------------------------------------------------- /template/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nix-vscode-extensions.url = "github:nix-community/nix-vscode-extensions"; 4 | flake-utils.follows = "nix-vscode-extensions/flake-utils"; 5 | nixpkgs.follows = "nix-vscode-extensions/nixpkgs"; 6 | }; 7 | 8 | outputs = 9 | inputs: 10 | inputs.flake-utils.lib.eachDefaultSystem ( 11 | system: 12 | let 13 | pkgs = import inputs.nixpkgs { 14 | inherit system; 15 | config.allowUnfree = true; 16 | overlays = [ inputs.nix-vscode-extensions.overlays.default ]; 17 | }; 18 | 19 | inherit (pkgs) vscode-with-extensions vscodium; 20 | 21 | packages.default = vscode-with-extensions.override { 22 | vscode = vscodium; 23 | vscodeExtensions = [ 24 | pkgs.vscode-marketplace.golang.go 25 | pkgs.open-vsx-release.rust-lang.rust-analyzer 26 | # unfree 27 | pkgs.vscode-marketplace.ms-python.vscode-pylance 28 | ]; 29 | }; 30 | 31 | devShells.default = pkgs.mkShell { 32 | shellHook = '' 33 | printf "Run VSCodium using one of the following commands:\n\n" 34 | printf "nix run .# .\n\n" 35 | printf "nix develop .#vscodium -c codium .\n\n" 36 | ''; 37 | }; 38 | 39 | # In some projects, people may use the same default devShell, 40 | # but different code editors. 41 | # 42 | # Then, it's better to provide `VSCodium` 43 | # not in the default devShell. 44 | devShells.vscodium = pkgs.mkShell { 45 | buildInputs = [ packages.default ]; 46 | shellHook = '' 47 | printf "VSCodium with extensions:\n" 48 | codium --list-extensions 49 | ''; 50 | }; 51 | in 52 | { 53 | inherit packages devShells; 54 | } 55 | ); 56 | } 57 | --------------------------------------------------------------------------------