├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yaml ├── .envrc ├── .github ├── CODEOWNERS └── workflows │ ├── build-prod.yaml │ └── build.yaml ├── .gitignore ├── .hlint.yaml ├── .last-exported-commit ├── .nix-bootstrap.dhall ├── .vscode ├── extensions.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app └── Main.hs ├── branding ├── nix-bootstrap-circle.png ├── nix-bootstrap-circle.svg ├── nix-bootstrap-email-signature.png ├── nix-bootstrap-email-signature.svg ├── nix-bootstrap.png └── nix-bootstrap.svg ├── cabal.project ├── cabal.project.freeze ├── flake.lock ├── flake.nix ├── hie.yaml ├── nix-bootstrap.cabal ├── nix ├── haskell-env.nix ├── pre-commit-hooks.nix ├── release.nix └── systems.nix ├── package.yaml ├── scripts ├── build-binary-cache.sh └── run.sh ├── src ├── Bootstrap.hs ├── Bootstrap │ ├── Cli.hs │ ├── Data │ │ ├── Bootstrappable.hs │ │ ├── Bootstrappable │ │ │ ├── BootstrapState.hs │ │ │ ├── BuildNix.hs │ │ │ ├── DevContainer.hs │ │ │ ├── Elm │ │ │ │ ├── ElmJson.hs │ │ │ │ ├── IndexHtml.hs │ │ │ │ ├── IndexJs.hs │ │ │ │ ├── MainElm.hs │ │ │ │ ├── PackageJson.hs │ │ │ │ └── Review │ │ │ │ │ ├── Config.hs │ │ │ │ │ └── ElmJson.hs │ │ │ ├── Envrc.hs │ │ │ ├── FlakeNix.hs │ │ │ ├── GitPodYml.hs │ │ │ ├── Gitignore.hs │ │ │ ├── GitlabCIConfig.hs │ │ │ ├── Go │ │ │ │ └── Modfile.hs │ │ │ ├── Haskell │ │ │ │ ├── LibHs.hs │ │ │ │ ├── MainHs.hs │ │ │ │ ├── PackageYaml.hs │ │ │ │ ├── PreludeHs.hs │ │ │ │ └── ServerHs.hs │ │ │ ├── HaskellPackagesNix.hs │ │ │ ├── NixPreCommitHookConfig.hs │ │ │ ├── Python │ │ │ │ └── Requirements.hs │ │ │ ├── Readme.hs │ │ │ ├── Rust │ │ │ │ ├── CargoLock.hs │ │ │ │ ├── CargoToml.hs │ │ │ │ └── MainRs.hs │ │ │ ├── SystemsNix.hs │ │ │ ├── VSCodeExtensions.hs │ │ │ └── VSCodeSettings.hs │ │ ├── BuildPlan.hs │ │ ├── Config.hs │ │ ├── Config │ │ │ ├── Internal.hs │ │ │ └── Internal │ │ │ │ ├── CurrentVersion.hs │ │ │ │ ├── TH.hs │ │ │ │ └── THHelpers.hs │ │ ├── ContinuousIntegration.hs │ │ ├── DevContainer.hs │ │ ├── GHCVersion.hs │ │ ├── HList.hs │ │ ├── HaskellDependency.hs │ │ ├── PreCommitHook.hs │ │ ├── ProjectName.hs │ │ ├── ProjectType.hs │ │ ├── Target.hs │ │ ├── VSCodeExtension.hs │ │ └── Version.hs │ ├── Error.hs │ ├── GitPod.hs │ ├── Monad.hs │ ├── Nix │ │ ├── Command.hs │ │ ├── Evaluate.hs │ │ ├── Expr.hs │ │ ├── Expr │ │ │ ├── BuildInputs.hs │ │ │ ├── FlakeInputs.hs │ │ │ ├── Haskell.hs │ │ │ ├── MkShell.hs │ │ │ ├── Nixpkgs.hs │ │ │ ├── PreCommitHooks.hs │ │ │ ├── Python.hs │ │ │ ├── ReproducibleBuild.hs │ │ │ └── ReproducibleBuild │ │ │ │ ├── Go.hs │ │ │ │ ├── Haskell.hs │ │ │ │ ├── Java.hs │ │ │ │ └── Rust.hs │ │ └── Flake.hs │ ├── State.hs │ ├── Terminal.hs │ ├── Terminal │ │ └── Icon.hs │ └── Unix.hs └── Prelude.hs ├── test ├── Bootstrap │ ├── Data │ │ ├── Bootstrappable │ │ │ ├── BootstrapStateSpec.hs │ │ │ ├── BuildNixSpec.hs │ │ │ ├── DevContainerSpec.hs │ │ │ ├── Elm │ │ │ │ ├── ElmJsonSpec.hs │ │ │ │ ├── IndexHtmlSpec.hs │ │ │ │ ├── IndexJsSpec.hs │ │ │ │ ├── MainElmSpec.hs │ │ │ │ ├── PackageJsonSpec.hs │ │ │ │ └── Review │ │ │ │ │ ├── ConfigSpec.hs │ │ │ │ │ └── ElmJsonSpec.hs │ │ │ ├── EnvrcSpec.hs │ │ │ ├── FlakeNixSpec.hs │ │ │ ├── GitPodYmlSpec.hs │ │ │ ├── GitignoreSpec.hs │ │ │ ├── GitlabCIConfigSpec.hs │ │ │ ├── Go │ │ │ │ └── ModfileSpec.hs │ │ │ ├── Haskell │ │ │ │ ├── LibHsSpec.hs │ │ │ │ ├── MainHsSpec.hs │ │ │ │ ├── PreludeHsSpec.hs │ │ │ │ └── ServerHsSpec.hs │ │ │ ├── HaskellPackagesNixSpec.hs │ │ │ ├── NixPreCommitHookConfigSpec.hs │ │ │ ├── ReadmeSpec.hs │ │ │ ├── Rust │ │ │ │ ├── CargoLockSpec.hs │ │ │ │ ├── CargoTomlSpec.hs │ │ │ │ └── MainRsSpec.hs │ │ │ ├── VSCodeExtensionsSpec.hs │ │ │ └── VSCodeSettingsSpec.hs │ │ ├── BuildPlanSpec.hs │ │ ├── Config │ │ │ └── InternalSpec.hs │ │ ├── DevContainerSpec.hs │ │ ├── PreCommitHookSpec.hs │ │ ├── ProjectNameSpec.hs │ │ ├── ProjectTypeSpec.hs │ │ └── VersionSpec.hs │ ├── Nix │ │ ├── CommandSpec.hs │ │ ├── Expr │ │ │ ├── MkShellSpec.hs │ │ │ └── NixpkgsSpec.hs │ │ ├── ExprSpec.hs │ │ └── FlakeSpec.hs │ └── StateSpec.hs ├── PreludeSpec.hs ├── Spec.hs └── Test │ ├── Util.hs │ └── Util │ ├── CanDieOnError.hs │ └── RunConfig.hs └── vulnerability-whitelist.toml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | FROM ubuntu:22.04 as base 15 | 16 | # Set shell and check for pipe fails 17 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 18 | 19 | # Install deps required by Nix installer 20 | RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ 21 | ca-certificates \ 22 | curl \ 23 | sudo \ 24 | xz-utils 25 | 26 | # Create user 27 | RUN groupadd -g 1001 vscode && \ 28 | useradd -u 1001 -g 1001 -G sudo -m vscode -s /bin/bash 29 | 30 | # Configure sudo and Nix 31 | RUN sed -i 's/%sudo.*ALL/%sudo ALL=(ALL:ALL) NOPASSWD:ALL/' /etc/sudoers && \ 32 | echo "sandbox = false" > /etc/nix.conf && \ 33 | echo "experimental-features = nix-command flakes" >> /etc/nix.conf 34 | 35 | # Install Nix and enable flakes 36 | USER vscode 37 | ENV USER=vscode 38 | ENV NIX_PATH=/home/vscode/.nix-defexpr/channels:/nix/var/nix/profiles/per-user/root/channels 39 | ENV NIX_CONF_DIR /etc 40 | RUN curl -L https://nixos.org/nix/install | NIX_INSTALLER_NO_MODIFY_PROFILE=1 sh 41 | 42 | FROM ubuntu:22.04 43 | 44 | ENV NIX_CONF_DIR /etc 45 | 46 | ## Install vscode deps 47 | RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ 48 | ca-certificates \ 49 | git \ 50 | gnupg \ 51 | gnupg2 \ 52 | locales \ 53 | ssh \ 54 | sudo && \ 55 | rm -rf /var/lib/apt/lists/* 56 | 57 | # Create user 58 | RUN groupadd -g 1001 vscode && \ 59 | useradd -u 1001 -g 1001 -G sudo -m vscode -s /bin/bash 60 | COPY --from=base --chown=vscode:vscode /home/vscode /home/vscode 61 | 62 | # Configure en_US.UTF-8 locale 63 | RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ 64 | locale-gen 65 | 66 | # Configure sudo 67 | RUN sed -i 's/%sudo.*ALL/%sudo ALL=(ALL:ALL) NOPASSWD:ALL/' /etc/sudoers 68 | 69 | # Setup Nix environment 70 | RUN echo "source /home/vscode/.nix-profile/etc/profile.d/nix.sh" >> /etc/bash.bashrc && \ 71 | echo "source /home/vscode/.nix-profile/etc/profile.d/nix.sh" >> /etc/zshrc 72 | 73 | # Copy nix and configs 74 | COPY --from=base /nix /nix 75 | COPY --from=base /etc/nix.conf /etc/nix.conf 76 | 77 | USER vscode 78 | 79 | # Setup vscode 80 | RUN mkdir -p /home/vscode/.vscode-server/extensions && \ 81 | mkdir -p /home/vscode/.vscode-server-insiders/extensions 82 | 83 | RUN export PATH=$PATH:/home/vscode/.nix-profile/bin \ 84 | && nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs && nix-channel --update \ 85 | && nix-env -iA nixpkgs.bashInteractive nixpkgs.direnv nixpkgs.git \ 86 | && echo 'eval "$(direnv hook bash)"' >> ~/.bashrc 87 | 88 | RUN mkdir -p ~/.config/direnv && touch ~/.config/direnv/direnvrc 89 | 90 | ENV SHELL="/home/vscode/.nix-profile/bin/bash" 91 | ENV PATH="${PATH}:/home/vscode/.nix-profile/bin" 92 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nix-bootstrap DevContainer", 3 | "dockerComposeFile": "docker-compose.yaml", 4 | "extensions": [ 5 | "arrterian.nix-env-selector", 6 | "haskell.haskell", 7 | "jnoortheen.nix-ide" 8 | ], 9 | "postCreateCommand": "git config --global core.editor 'code --wait' && git config --global --add safe.directory /workspaces/nix-bootstrap && direnv allow && nix-shell --run setUpHaskellLanguageServer", 10 | "postStartCommand": "nix-shell --run \"echo 'Setup complete!'\"", 11 | "service": "nix-bootstrap-dev-container", 12 | "workspaceFolder": "/workspaces/nix-bootstrap" 13 | } 14 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | services: 15 | nix-bootstrap-dev-container: 16 | build: 17 | context: . 18 | dockerfile: Dockerfile 19 | # Override the default command so things don't shut down after the process ends. 20 | command: /bin/sh -c 'while sleep 1000; do :; done' 21 | container_name: nix-bootstrap-dev-container 22 | volumes: 23 | - ../:/workspaces/nix-bootstrap:cached 24 | - ${HOME}/.ssh:/home/vscode/.ssh 25 | version: "3" 26 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | direnv version 2.23.0 || exit 1 15 | if [ $(nix-env --version | grep -oE '[0-9]+\.[0-9]+' | head -n1 | sed 's/\./000/') -lt 20004 ]; then 16 | echo 'This project is set up to work with Nix Flakes, which your version of nix doesn'"'"'t support.' 17 | echo 'Please upgrade your nix version to at least 2.4 to continue.' 18 | exit 1 19 | fi 20 | if ! nix show-config --extra-experimental-features nix-command | grep experimental-features | grep flakes 1>/dev/null 2>&1; then 21 | printf '\033[31m' 22 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 23 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 24 | printf '\033[0m' 25 | exit 1 26 | fi 27 | if ! nix show-config 1>/dev/null 2>&1; then 28 | printf '\033[31m' 29 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 30 | echo 'Specifically, the "nix-command" option is missing from your nix experimental-features configuration.' 31 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 32 | printf '\033[0m' 33 | exit 1 34 | fi 35 | use flake 36 | eval "$shellHook" 37 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sd234678 2 | -------------------------------------------------------------------------------- /.github/workflows/build-prod.yaml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | name: Build for production 15 | concurrency: 16 | group: production 17 | cancel-in-progress: true 18 | on: 19 | push: 20 | branches: 21 | - main 22 | jobs: 23 | build-nix-bootstrap: 24 | name: Build nix-bootstrap 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | with: 29 | fetch-depth: 0 30 | - uses: cachix/install-nix-action@v18 31 | with: 32 | nix_path: "nixpkgs=channel:nixpkgs-unstable" 33 | - name: Check version number has been updated 34 | run: > 35 | (git fetch --all && git diff "$(git describe --tags --abbrev=0)" -- package.yaml | grep version) || 36 | (echo "You must bump the nix-bootstrap version number in package.yaml!" && exit 1) 37 | - name: Get new version number 38 | run: echo "version_number=$(grep -E '^version' package.yaml | cut -d' ' -f2)" >> $GITHUB_ENV 39 | - name: Run pre-commit hooks 40 | run: nix flake check 41 | - name: Build nix-bootstrap 42 | run: nix build 43 | - name: Check for vulnerabilities 44 | run: nix run .\#ciPackages_vulnix -- -C -w vulnerability-whitelist.toml result/ 45 | - name: Build release artefact 46 | run: nix run .\#ciPackages_buildBinaryCache 47 | - uses: actions/upload-artifact@v4 48 | with: 49 | name: "release-${{ env.version_number }}" 50 | path: releases/ 51 | - uses: rickstaa/action-create-tag@v1 52 | with: 53 | tag: "${{ env.version_number }}" 54 | message: "Release version ${{ env.version_number }}" 55 | - name: Create release 56 | run: > 57 | printf '%s %s -r %s -t %s %s releases/' '-u' "$GITHUB_REPOSITORY_OWNER" "${GITHUB_REPOSITORY#*/}" 58 | "${{ secrets.GITHUB_TOKEN }}" "${{ env.version_number }}" | xargs -d ' ' nix run nixpkgs\#legacyPackages.x86_64-linux.ghr -- 59 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | name: Build 15 | on: 16 | pull_request: 17 | 18 | jobs: 19 | build-nix-bootstrap: 20 | name: Build nix-bootstrap 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v3 24 | with: 25 | fetch-depth: 0 26 | - uses: cachix/install-nix-action@v18 27 | with: 28 | nix_path: "nixpkgs=channel:nixpkgs-unstable" 29 | - name: Check version number has been updated 30 | run: > 31 | (git fetch --all && git diff "$(git describe --tags --abbrev=0)" -- package.yaml | grep version) || 32 | (echo "You must bump the nix-bootstrap version number in package.yaml!" && exit 1) 33 | - name: Run pre-commit hooks 34 | run: nix flake check 35 | - name: Build nix-bootstrap 36 | run: nix build 37 | - name: Check for vulnerabilities 38 | run: nix run .\#ciPackages_vulnix -- -C -w vulnerability-whitelist.toml result/ 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Direnv 16 | /.direnv 17 | 18 | # Haskell 19 | dist 20 | dist-* 21 | cabal-dev 22 | *.o 23 | *.hi 24 | *.hie 25 | *.chi 26 | *.chs.h 27 | *.dyn_o 28 | *.dyn_hi 29 | .hpc 30 | .hsenv 31 | .cabal-sandbox 32 | cabal.sandbox.config 33 | *.prof 34 | *.aux 35 | *.hp 36 | *.eventlog 37 | .stack-work/ 38 | cabal.project.local 39 | cabal.project.local~ 40 | .HTF/ 41 | .ghc.environment.* 42 | 43 | # Miscellaneous 44 | *~ 45 | 46 | # Nix 47 | result 48 | result-* 49 | 50 | # Pre-commit hooks 51 | .pre-commit-config.yaml 52 | 53 | # Releases 54 | /releases 55 | 56 | -------------------------------------------------------------------------------- /.last-exported-commit: -------------------------------------------------------------------------------- 1 | Last exported commit from parent repo: 74075fe52d4ef4cc3fe2e97bd35e30fde9a84b5c -------------------------------------------------------------------------------- /.nix-bootstrap.dhall: -------------------------------------------------------------------------------- 1 | -- This file was generated by nix-bootstrap. 2 | -- It should be checked into version control. 3 | -- It is used to aid migration between nix-bootstrap versions and preserve idempotence. 4 | let NodePackageManager = < NPM | PNPm | Yarn > 5 | 6 | let ElmMode = < Bare | Node : NodePackageManager > 7 | 8 | let ElmOptions = { elmMode : ElmMode, provideElmReview : Bool } 9 | 10 | let HaskellProjectType = < ReplOnly | Basic : Bool | Server : Bool > 11 | 12 | let HaskellOptions = 13 | { ghcVersion : { major : Natural, minor : Natural, patch : Natural } 14 | , haskellProjectType : HaskellProjectType 15 | } 16 | 17 | let JavaOptions = 18 | { installMinishift : Bool 19 | , installLombok : Bool 20 | , setUpJavaBuild : < SetUpJavaBuild : Text | NoJavaBuild > 21 | , jdk : < OpenJDK | GraalVM > 22 | } 23 | 24 | let ProjectType = 25 | < Minimal 26 | | Elm : ElmOptions 27 | | Haskell : HaskellOptions 28 | | Node : NodePackageManager 29 | | Go : Bool 30 | | Java : JavaOptions 31 | | Python 32 | | Rust 33 | > 34 | 35 | in { projectName = "nix-bootstrap" 36 | , projectType = ProjectType.Minimal 37 | , setUpPreCommitHooks = True 38 | , setUpContinuousIntegration = True 39 | , setUpVSCodeDevContainer = True 40 | , target = {=} 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "haskell.haskell", 4 | "arrterian.nix-env-selector", 5 | "jnoortheen.nix-ide", 6 | "arcanis.vscode-zipfs" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", 3 | "nix.enableLanguageServer": true, 4 | "nix.formatterPath": "alejandra", 5 | "haskell.manageHLS": "PATH", 6 | "haskell.serverExecutablePath": "haskell-language-server-wrapper", 7 | "search.exclude": { 8 | "**/.yarn": true, 9 | "**/.pnp.*": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [oss@gchq.gov.uk](oss@gchq.gov.uk). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to nix-bootstrap 2 | 3 | This file details the steps necessary in order to contribute to the project, 4 | providing guidance and development practices to follow in order to make successful contributions. 5 | 6 | ## Contributions 7 | 8 | Please read our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing to this project. 9 | 10 | Before your contributions can be accepted, you must: 11 | 12 | - Sign the [GCHQ Contributor Licence Agreement](https://cla-assistant.io/gchq/nix-bootstrap). 13 | - Push your changes to new branch. 14 | - Submit a pull request. 15 | 16 | ## Coding Practices 17 | 18 | ### Versioning 19 | 20 | `nix-bootstrap` broadly follows the [PVP Versioning Specification](https://pvp.haskell.org/), 21 | but with some modifications on the basis it's an end product rather than a library: 22 | 23 | - The flagship version (first component) is bumped only at major, defining releases. 24 | - The major version (second component) is bumped whenever the user input changes - this is 25 | analagous to a breaking change. 26 | - The minor version (third component) is bumped for all other features. 27 | - The patch version (last component) is bumped for all other changes which don't affect 28 | nix-bootstrap's feature set. 29 | 30 | The version number must be updated for every (set of) commits added to `main`. 31 | 32 | ### Comments 33 | 34 | - When considering adding a comment, first consider if the code could be better explained by 35 | restructuring it (e.g. by pulling out a function) 36 | - If the code cannot be made clear by restructuring, add a comment to explain it 37 | - Haddock comments should be added to all exposed items in libraries 38 | 39 | ### Property Ordering 40 | 41 | For the purpose of this point, a logical order is one of the following: 42 | 43 | - Something universally recognisable, like alphabetical 44 | - Something recognisable in context, such as CSS properties being grouped into width and height, 45 | flex options, colour options etc. 46 | 47 | - In this case, the groups should each be logically ordered 48 | 49 | Based on those definitions: 50 | 51 | - In cases where a logical order of properties is already being used, that order must be followed 52 | unless changing to a new logical order is justified 53 | - In cases where no logical order is given already, developers touching that property set should re-order the set to fit a logical order 54 | - New property sets must be given a logical order 55 | 56 | ### Use of Type Systems 57 | 58 | - Type systems must be leveraged where possible to ensure code correctness 59 | 60 | ### Identifiers / Variable Names 61 | 62 | Identifiers must be: 63 | 64 | - Consistent 65 | - Meaningful 66 | - Given in line with the idioms/best practice of the language/toolchain in which it's written 67 | 68 | ## Version Control Practices 69 | 70 | ### Branching Strategies 71 | 72 | - Rebase and Fast Forward will be the default merge strategy for PRs 73 | - No merge commits from the base branch (e.g. `main`) will be permitted into feature branches. 74 | If conflicts arise, these should instead be solved by rebasing the feature branch onto the HEAD of the base branch 75 | 76 | ### Branch Naming 77 | 78 | - The main branch will be called `main` 79 | - Branch names should consist of the ticket number (where a ticket exists) and a short description 80 | - Words in branch names should be separated by dashes 81 | 82 | ### Commit Conventions 83 | 84 | Commits should be made as frequently as possible, and each commit should successfully build 85 | 86 | ### Commit Messages 87 | 88 | Commit messages must: 89 | 90 | - Be short where possible 91 | - Be meaningful 92 | - Have no reliance on external context (e.g. should not say things like "make PR changes") 93 | 94 | ### Pull Requests 95 | 96 | Pull requests will undergo an in depth review by a project contributor to check the code 97 | changes are compliant with our coding style. 98 | 99 | This is a community so please be respectful of other members - offer encouragement, support and suggestions. 100 | 101 | Please agree to the [GCHQ OSS Contributor License Agreement](https://cla-assistant.io/gchq/nix-bootstrap) 102 | before submitting a pull request. 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | © Crown Copyright GCHQ 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /app/Main.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Main (main) where 3 | 4 | import Bootstrap (nixBootstrap) 5 | 6 | main :: IO () 7 | main = nixBootstrap 8 | -------------------------------------------------------------------------------- /branding/nix-bootstrap-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gchq/nix-bootstrap/bd027810d8c9a717a9572b88c9e9f16185f166a9/branding/nix-bootstrap-circle.png -------------------------------------------------------------------------------- /branding/nix-bootstrap-email-signature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gchq/nix-bootstrap/bd027810d8c9a717a9572b88c9e9f16185f166a9/branding/nix-bootstrap-email-signature.png -------------------------------------------------------------------------------- /branding/nix-bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gchq/nix-bootstrap/bd027810d8c9a717a9572b88c9e9f16185f166a9/branding/nix-bootstrap.png -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: 2 | . 3 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1733328505, 7 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "gitignore": { 20 | "inputs": { 21 | "nixpkgs": ["pre-commit-hooks-lib", "nixpkgs"] 22 | }, 23 | "locked": { 24 | "lastModified": 1709087332, 25 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 26 | "owner": "hercules-ci", 27 | "repo": "gitignore.nix", 28 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "hercules-ci", 33 | "repo": "gitignore.nix", 34 | "type": "github" 35 | } 36 | }, 37 | "nixpkgs-src": { 38 | "locked": { 39 | "lastModified": 1748026580, 40 | "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", 41 | "owner": "NixOS", 42 | "repo": "nixpkgs", 43 | "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", 44 | "type": "github" 45 | }, 46 | "original": { 47 | "id": "nixpkgs", 48 | "ref": "25.05", 49 | "type": "indirect" 50 | } 51 | }, 52 | "nixpkgs-src-previous": { 53 | "locked": { 54 | "lastModified": 1729449015, 55 | "narHash": "sha256-Gf04dXB0n4q0A9G5nTGH3zuMGr6jtJppqdeljxua1fo=", 56 | "owner": "NixOS", 57 | "repo": "nixpkgs", 58 | "rev": "89172919243df199fe237ba0f776c3e3e3d72367", 59 | "type": "github" 60 | }, 61 | "original": { 62 | "owner": "NixOS", 63 | "repo": "nixpkgs", 64 | "rev": "89172919243df199fe237ba0f776c3e3e3d72367", 65 | "type": "github" 66 | } 67 | }, 68 | "pre-commit-hooks-lib": { 69 | "inputs": { 70 | "flake-compat": "flake-compat", 71 | "gitignore": "gitignore", 72 | "nixpkgs": ["nixpkgs-src"] 73 | }, 74 | "locked": { 75 | "lastModified": 1742058297, 76 | "narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=", 77 | "owner": "cachix", 78 | "repo": "pre-commit-hooks.nix", 79 | "rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a", 80 | "type": "github" 81 | }, 82 | "original": { 83 | "owner": "cachix", 84 | "repo": "pre-commit-hooks.nix", 85 | "type": "github" 86 | } 87 | }, 88 | "root": { 89 | "inputs": { 90 | "nixpkgs-src": "nixpkgs-src", 91 | "nixpkgs-src-previous": "nixpkgs-src-previous", 92 | "pre-commit-hooks-lib": "pre-commit-hooks-lib" 93 | } 94 | } 95 | }, 96 | "root": "root", 97 | "version": 7 98 | } 99 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | { 15 | description = "Development infrastructure for nix-bootstrap"; 16 | inputs = { 17 | nixpkgs-src.url = "nixpkgs/25.05"; 18 | nixpkgs-src-previous.url = "github:NixOS/nixpkgs?rev=89172919243df199fe237ba0f776c3e3e3d72367"; 19 | pre-commit-hooks-lib = { 20 | inputs.nixpkgs.follows = "nixpkgs-src"; 21 | url = "github:cachix/pre-commit-hooks.nix"; 22 | }; 23 | }; 24 | 25 | outputs = { 26 | self, 27 | nixpkgs-src, 28 | nixpkgs-src-previous, 29 | pre-commit-hooks-lib, 30 | ... 31 | }: let 32 | systemsHelpers = import nix/systems.nix; 33 | allSystems = nixpkgs-src.lib.platforms.all; 34 | supportedSystems = with systemsHelpers.system allSystems; [x86_64-linux aarch64-linux]; 35 | in 36 | systemsHelpers.forEachSystem supportedSystems ( 37 | system: let 38 | nixpkgs = nixpkgs-src.legacyPackages.${system}; 39 | inherit 40 | (import nix/haskell-env.nix {inherit nixpkgs;}) 41 | baseHaskellPackages 42 | haskellDevTools 43 | ; 44 | inherit (import nix/release.nix {inherit baseHaskellPackages nixpkgs;}) nix-bootstrap; 45 | buildBinaryCache = nixpkgs.writeShellScriptBin "buildBinaryCache" '' 46 | sed=${nixpkgs.gnused}/bin/sed 47 | tar=${nixpkgs.gnutar}/bin/tar 48 | ${builtins.readFile scripts/build-binary-cache.sh} 49 | ''; 50 | pre-commit-hooks = import nix/pre-commit-hooks.nix { 51 | inherit nixpkgs pre-commit-hooks-lib system; 52 | inherit (nixpkgs) alejandra vulnix; 53 | src = ./.; 54 | }; 55 | extraDevShellArgs = { 56 | inherit (pre-commit-hooks.allHooks) shellHook; 57 | }; 58 | in { 59 | checks = {pre-commit-check = pre-commit-hooks.pureHooks;}; 60 | devShells = { 61 | default = nixpkgs.mkShell ({ 62 | buildInputs = 63 | [buildBinaryCache] 64 | ++ haskellDevTools 65 | ++ pre-commit-hooks.tools 66 | ++ ( 67 | with nixpkgs; [ 68 | coreutils 69 | dhall 70 | gawk 71 | glab 72 | gnused 73 | gnutar 74 | which 75 | ] 76 | ); 77 | } 78 | // extraDevShellArgs); 79 | }; 80 | packages = { 81 | default = nix-bootstrap; 82 | inherit nix-bootstrap; 83 | # To be used as tools in CI 84 | ciPackages_buildBinaryCache = buildBinaryCache; 85 | ciPackages_vulnix = nixpkgs.vulnix; 86 | }; 87 | } 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | cradle: 15 | cabal: 16 | - path: "./src" 17 | component: "lib:nix-bootstrap" 18 | 19 | - path: "./app/Main.hs" 20 | component: "nix-bootstrap:exe:app" 21 | 22 | - path: "./app/Paths_nix_bootstrap.hs" 23 | component: "nix-bootstrap:exe:app" 24 | 25 | - path: "./test" 26 | component: "nix-bootstrap:test:nix-bootstrap-test" 27 | -------------------------------------------------------------------------------- /nix/haskell-env.nix: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | {nixpkgs}: let 15 | # haskell package set 16 | baseHaskellPackages = nixpkgs.haskell.packages.ghc984; 17 | 18 | # dev tools 19 | haskellEnv = baseHaskellPackages.ghcWithPackages ( 20 | haskellPackages: 21 | with haskellPackages; [ 22 | cabal-install 23 | haskell-language-server 24 | ] 25 | ); 26 | in { 27 | inherit baseHaskellPackages; 28 | haskellDevTools = [ 29 | haskellEnv 30 | # cabal uses wget but doesn't package it. This ensures a compatible version is used, 31 | # as devcontainers otherwise package the busybox version by default, which accepts 32 | # different arguments. 33 | nixpkgs.wget 34 | ]; 35 | } 36 | -------------------------------------------------------------------------------- /nix/pre-commit-hooks.nix: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | { 15 | alejandra, 16 | nixpkgs, 17 | pre-commit-hooks-lib, 18 | src, 19 | system, 20 | vulnix, 21 | }: let 22 | # Function to make a set of pre-commit hooks 23 | makeHooks = hooks: 24 | pre-commit-hooks-lib.lib.${system}.run { 25 | inherit hooks src; 26 | }; 27 | # Hooks which don't depend on running in a dev environment 28 | pureHooks = { 29 | alejandra = { 30 | enable = true; 31 | entry = nixpkgs.lib.mkOverride 0 "${alejandra}/bin/alejandra"; 32 | }; 33 | hlint = { 34 | enable = true; 35 | entry = nixpkgs.lib.mkOverride 0 "${pre-commit-hooks-lib.packages.${system}.hlint}/bin/hlint -XNoCPP"; 36 | }; 37 | hpack = { 38 | enable = true; 39 | entry = nixpkgs.lib.mkOverride 0 "${ 40 | nixpkgs.writeShellScriptBin 41 | "hpack-override" 42 | "(${pre-commit-hooks-lib.packages.${system}.hpack}/bin/hpack)" 43 | }/bin/hpack-override"; 44 | }; 45 | ormolu.enable = true; 46 | prettier = { 47 | enable = true; 48 | excludes = [".pnp.*" ".pre-commit-config.yaml"]; 49 | }; 50 | shellcheck = { 51 | enable = true; 52 | entry = nixpkgs.lib.mkOverride 0 "${ 53 | pre-commit-hooks-lib.packages."${system}".shellcheck 54 | }/bin/shellcheck -x"; 55 | types_or = ["shell"]; 56 | excludes = [".envrc"]; 57 | }; 58 | }; 59 | # Hooks which can run on pre-commit but not in CI 60 | impureHooks = { 61 | hpack-with-version-bump = { 62 | enable = true; 63 | entry = "${ 64 | nixpkgs.writeShellScriptBin "hpack-with-version-bump.sh" '' 65 | set -e 66 | (${nixpkgs.git}/bin/git fetch --all && \ 67 | ${nixpkgs.git}/bin/git diff "$(git describe --tags --abbrev=0)" -- package.yaml | ${nixpkgs.gnugrep}/bin/grep version) \ 68 | || (echo "You must bump the nix-bootstrap version number in package.yaml!" && set -e && exit 1) 69 | (${pre-commit-hooks-lib.packages.${system}.hpack}/bin/hpack) 70 | '' 71 | }/bin/hpack-with-version-bump.sh"; 72 | files = ".*"; 73 | name = "Update config and version"; 74 | pass_filenames = false; 75 | }; 76 | # Prefixed with z_ to make it run last 77 | z_build_nix_bootstrap = { 78 | enable = true; 79 | entry = "nix build"; 80 | files = "\\.(cabal|hs|nix|yaml)$"; 81 | name = "build"; 82 | pass_filenames = false; 83 | }; 84 | z_vulnerabilities = { 85 | enable = true; 86 | entry = "${ 87 | nixpkgs.writeShellScriptBin 88 | "check-for-vulnerabilities" 89 | "${vulnix}/bin/vulnix -C -w vulnerability-whitelist.toml result/" 90 | }/bin/check-for-vulnerabilities"; 91 | files = "nix-bootstrap"; 92 | name = "check-for-vulnerabilities"; 93 | pass_filenames = false; 94 | }; 95 | }; 96 | in { 97 | allHooks = makeHooks (builtins.removeAttrs (pureHooks // impureHooks) ["hpack"]); 98 | pureHooks = makeHooks pureHooks; 99 | tools = 100 | [alejandra vulnix] 101 | ++ (with pre-commit-hooks-lib.packages.${system}; [ 102 | hlint 103 | hpack 104 | hpack-dir 105 | ormolu 106 | prettier 107 | shellcheck 108 | ]); 109 | } 110 | -------------------------------------------------------------------------------- /nix/release.nix: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | { 15 | baseHaskellPackages, 16 | nixpkgs, 17 | }: let 18 | src = ../.; 19 | nix-bootstrap-unstripped = (nixpkgs.haskell.lib.overrideCabal (baseHaskellPackages.callCabal2nix "nix-bootstrap" src {}) 20 | ( 21 | _: { 22 | configureFlags = ["-fprod"]; 23 | } 24 | )) 25 | .overrideAttrs (_: prev: {buildInputs = (prev.buildInputs or []) ++ [nixpkgs.alejandra];}); 26 | in { 27 | nix-bootstrap = nixpkgs.stdenv.mkDerivation { 28 | inherit src; 29 | inherit (nix-bootstrap-unstripped) name version; 30 | buildInputs = with nixpkgs; [ 31 | alejandra 32 | glibc 33 | gmp 34 | libffi 35 | ncurses 36 | zlib 37 | ]; 38 | installPhase = '' 39 | mkdir -p $out/bin 40 | cp ${(nixpkgs.haskell.lib.enableSeparateBinOutput nix-bootstrap-unstripped).bin}/bin/app $out/bin/nix-bootstrap 41 | ''; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /nix/systems.nix: -------------------------------------------------------------------------------- 1 | # A minimal subset of numtide/flake-utils 2 | { 3 | forEachSystem = systems: f: 4 | builtins.foldl' ( 5 | attrs: system: let 6 | ret = f system; 7 | in 8 | builtins.foldl' ( 9 | attrs: key: 10 | attrs 11 | // { 12 | ${key} = 13 | (attrs.${key} or {}) 14 | // { 15 | ${system} = ret.${key}; 16 | }; 17 | } 18 | ) 19 | attrs (builtins.attrNames ret) 20 | ) {} 21 | systems; 22 | 23 | system = allSystems: 24 | builtins.listToAttrs 25 | (builtins.map (system: { 26 | name = system; 27 | value = system; 28 | }) 29 | allSystems); 30 | } 31 | -------------------------------------------------------------------------------- /package.yaml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | name: nix-bootstrap 15 | version: 2.4.0.1 16 | author: gchquser 17 | maintainer: 48051938+sd234678@users.noreply.github.com 18 | copyright: Crown Copyright 19 | license: Apache-2.0 20 | default-extensions: 21 | - BlockArguments 22 | - DerivingStrategies 23 | - GADTs 24 | - LambdaCase 25 | - NamedFieldPuns 26 | - OverloadedStrings 27 | - RecordWildCards 28 | - StrictData 29 | dependencies: 30 | - name: base 31 | version: "== 4.19.2.0" 32 | mixin: 33 | - hiding (Prelude) 34 | - relude == 1.2.2.0 35 | flags: 36 | prod: 37 | default: false 38 | description: Enable production defaults 39 | manual: true 40 | ghc-options: 41 | - "-Wall" 42 | - "-Wcpp-undef" 43 | - "-Widentities" 44 | - "-Wincomplete-patterns" 45 | - "-Wincomplete-record-updates" 46 | - "-Wincomplete-uni-patterns" 47 | - "-Wmissing-deriving-strategies" 48 | - "-Wmissing-export-lists" 49 | - "-Wmissing-import-lists" 50 | - "-Wmissing-signatures" 51 | - "-Wpartial-fields" 52 | - "-Wredundant-constraints" 53 | when: 54 | - condition: flag(prod) 55 | ghc-options: 56 | - "-O2" 57 | - "-Werror" 58 | library: 59 | source-dirs: src 60 | dependencies: 61 | - aeson == 2.2.3.0 62 | - aeson-pretty == 0.8.10 63 | - blaze-html == 0.9.2.0 64 | - directory == 1.3.8.5 65 | - dhall == 1.42.2 66 | - exceptions == 0.10.7 67 | - extra == 1.7.16 68 | - filepath == 1.4.301.0 69 | - lens == 5.3.4 70 | - megaparsec == 9.7.0 71 | - mtl == 2.3.1 72 | - parser-combinators == 1.3.0 73 | - process == 1.6.25.0 74 | - raw-strings-qq == 1.1 75 | - regex-compat == 0.95.2.2 76 | - silently == 1.2.5.4 77 | - singletons == 3.0.4 78 | - template-haskell == 2.21.0.0 79 | - terminal == 0.2.0.0 80 | - th-abstraction == 0.7.1.0 81 | - tomland == 1.3.3.3 82 | - which == 0.2.0.3 83 | - yaml == 0.11.11.2 84 | executables: 85 | app: 86 | main: Main.hs 87 | source-dirs: app 88 | ghc-options: 89 | - "-O2" 90 | - "-threaded" 91 | - "-rtsopts" 92 | - "-with-rtsopts=-N" 93 | dependencies: 94 | - nix-bootstrap 95 | tests: 96 | nix-bootstrap-test: 97 | main: Spec.hs 98 | source-dirs: test 99 | dependencies: 100 | - dhall == 1.42.2 101 | - hspec == 2.11.12 102 | - hspec-expectations-pretty-diff == 0.7.2.6 103 | - nix-bootstrap 104 | - QuickCheck == 2.14.3 105 | - raw-strings-qq == 1.1 106 | - tomland == 1.3.3.3 107 | build-tools: 108 | - hspec-discover == 2.11.12 109 | -------------------------------------------------------------------------------- /scripts/build-binary-cache.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | # 3 | # 4 | # 5 | # © Crown Copyright GCHQ 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | function runBuildBinaryCache() { 20 | # shellcheck disable=SC2154 21 | # (sed is defined in ../flake.nix) 22 | local sedCmd=$sed 23 | # shellcheck disable=SC2154 24 | # (tar is defined in ../flake.nix) 25 | local tarCmd=$tar 26 | 27 | set -e 28 | nix build 29 | # shellcheck disable=SC2046 30 | # (We want word splitting in the output from nix-store -qR) 31 | nix-store --export $(nix-store -qR "$(readlink result)") > nix-bootstrap-binary-cache 32 | $tarCmd cvzf "$(readlink result | $sedCmd 's:.*/::').tgz" nix-bootstrap-binary-cache 33 | mkdir -p releases 34 | mv ./*nix-bootstrap*.tgz releases 35 | rm -r nix-bootstrap-binary-cache 36 | } 37 | 38 | runBuildBinaryCache 39 | 40 | -------------------------------------------------------------------------------- /src/Bootstrap/Cli.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Cli 3 | ( Command (..), 4 | ErrorMessage, 5 | RunConfig (..), 6 | allowDirtyFlagName, 7 | fromScratchFlagName, 8 | parseCommand, 9 | showHelp, 10 | ) 11 | where 12 | 13 | import Bootstrap.Data.DevContainer (DevContainerConfig (DevContainerConfig)) 14 | import Bootstrap.Monad (MonadBootstrap) 15 | import Bootstrap.Terminal (putErrorLn, withAttribute, withAttributes) 16 | import Data.Char (isSpace) 17 | import qualified Data.Text as T 18 | import System.Console.GetOpt as GetOpt 19 | ( ArgDescr (NoArg), 20 | ArgOrder (Permute), 21 | OptDescr (Option), 22 | getOpt, 23 | usageInfo, 24 | ) 25 | import System.Terminal 26 | ( MonadColorPrinter (blue, foreground), 27 | MonadFormattingPrinter (bold), 28 | MonadPrinter (putLn), 29 | putText, 30 | putTextLn, 31 | yellow, 32 | ) 33 | 34 | data Command 35 | = CommandHelp [ErrorMessage] 36 | | CommandRun RunConfig 37 | | CommandVersion 38 | 39 | newtype ErrorMessage = ErrorMessage {unErrorMessage :: Text} 40 | 41 | data RunConfig = RunConfig 42 | { rcAllowDirty :: Bool, 43 | rcFromScratch :: Bool, 44 | rcNonInteractive :: Bool, 45 | rcWithDevContainer :: Maybe DevContainerConfig 46 | } 47 | 48 | parseCommand :: (MonadIO m) => m Command 49 | parseCommand = do 50 | args <- getArgs 51 | let (options, _, errors) = getOpt Permute cliOptions args 52 | if not $ null errors 53 | then pure . CommandHelp $ ErrorMessage . T.strip . toText <$> errors 54 | else 55 | if Help `elem` options 56 | then pure $ CommandHelp [] 57 | else 58 | if Version `elem` options 59 | then pure CommandVersion 60 | else do 61 | let rcAllowDirty = AllowDirty `elem` options 62 | rcFromScratch = FromScratch `elem` options 63 | rcNonInteractive = NonInteractive `elem` options 64 | rcWithDevContainer = 65 | if WithDevContainer `elem` options 66 | then Just (DevContainerConfig True) 67 | else Nothing 68 | pure $ CommandRun RunConfig {..} 69 | 70 | data Flag 71 | = AllowDirty 72 | | FromScratch 73 | | Help 74 | | NonInteractive 75 | | Version 76 | | WithDevContainer 77 | deriving stock (Eq) 78 | 79 | allowDirtyFlagName :: (IsString s) => s 80 | allowDirtyFlagName = "allow-dirty" 81 | 82 | fromScratchFlagName :: (IsString s) => s 83 | fromScratchFlagName = "from-scratch" 84 | 85 | cliOptions :: [OptDescr Flag] 86 | cliOptions = 87 | [ GetOpt.Option [] [allowDirtyFlagName] (NoArg AllowDirty) "Allow nix-bootstrap to run even if the current state of the git repo is dirty.", 88 | GetOpt.Option [] [fromScratchFlagName] (NoArg FromScratch) $ 89 | "Ignore any previous nix-bootstrap state files and re-prompt for every " 90 | <> "configuration question. Note that this doesn't delete files which are not overwritten in the new configuration.", 91 | GetOpt.Option [] ["help"] (NoArg Help) "Print the program information and usage.", 92 | GetOpt.Option [] ["non-interactive"] (NoArg NonInteractive) "When working from an existing state file, don't require any user input.", 93 | GetOpt.Option [] ["version"] (NoArg Version) "Print the program version.", 94 | GetOpt.Option [] ["with-devcontainer"] (NoArg WithDevContainer) "Make sure nix-bootstrap sets up a VSCode DevContainer." 95 | ] 96 | 97 | showHelp :: (MonadBootstrap m) => [ErrorMessage] -> m () 98 | showHelp errors = do 99 | mapM_ (putErrorLn . ("Error: " <>) . unErrorMessage) errors 100 | withAttributes [bold, foreground blue] $ putTextLn "Usage: nix-bootstrap [OPTION]..." 101 | let usageInfoLines = lines . toText $ usageInfo "Generate infrastructure for common types of project.\n" cliOptions 102 | case uncons usageInfoLines of 103 | Nothing -> putLn 104 | Just (firstLine, restOfLines) -> do 105 | putTextLn firstLine 106 | putLn 107 | forM_ restOfLines \line -> do 108 | case words line of 109 | [] -> pass 110 | (w : ws) -> do 111 | putText $ T.takeWhile isSpace line 112 | withAttribute bold $ putText w 113 | let postFlagSpacing = 114 | T.takeWhile isSpace 115 | . T.dropWhile (not . isSpace) 116 | $ T.dropWhile isSpace line 117 | putText postFlagSpacing 118 | forM_ ws \word -> do 119 | if word == "(EXPERIMENTAL)" 120 | then withAttributes [bold, foreground yellow] $ putText word 121 | else putText word 122 | putText " " 123 | putLn 124 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/BootstrapState.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.BootstrapState 3 | {-# DEPRECATED "Use Bootstrap.Data.Config instead." #-} 4 | ( BootstrapState 5 | ( stateVersion, 6 | stateProjectName, 7 | stateProjectType, 8 | statePreCommitHooksConfig, 9 | stateContinuousIntegrationConfig, 10 | stateDevContainerConfig, 11 | stateUseFlakes 12 | ), 13 | bootstrapStateCodec, 14 | bootstrapStateFor, 15 | unBootstrapVersion, 16 | BootstrapVersion, 17 | bootstrapStateFileName, 18 | ) 19 | where 20 | 21 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason)) 22 | import Bootstrap.Data.ContinuousIntegration (ContinuousIntegrationConfig, continuousIntegrationConfigCodec) 23 | import Bootstrap.Data.DevContainer (DevContainerConfig, devContainerConfigCodec) 24 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig, preCommitHooksConfigCodec) 25 | import Bootstrap.Data.ProjectName (ProjectName) 26 | import qualified Bootstrap.Data.ProjectName as ProjectName 27 | import Bootstrap.Data.ProjectType (ProjectTypeV2, projectTypeCodec) 28 | import Data.Version (showVersion) 29 | import Paths_nix_bootstrap (version) 30 | import Toml (TomlCodec, (.=)) 31 | import qualified Toml 32 | 33 | data BootstrapState = BootstrapState 34 | { stateVersion :: BootstrapVersion, 35 | stateProjectName :: ProjectName, 36 | stateProjectType :: ProjectTypeV2, 37 | statePreCommitHooksConfig :: PreCommitHooksConfig, 38 | stateContinuousIntegrationConfig :: ContinuousIntegrationConfig, 39 | stateDevContainerConfig :: DevContainerConfig, 40 | stateUseFlakes :: Bool 41 | } 42 | deriving stock (Eq, Show) 43 | 44 | instance Bootstrappable BootstrapState where 45 | bootstrapName = const bootstrapStateFileName 46 | bootstrapReason = const "This holds nix-bootstrap's configuration to ensure upgrades are reliable." 47 | bootstrapContent state = pure . Right $ introComment <> Toml.encode bootstrapStateCodec state 48 | where 49 | introComment :: Text 50 | introComment = 51 | unlines 52 | [ "# This file was generated by nix-bootstrap.", 53 | "# It should be checked into version control.", 54 | "# It is used to aid migration between nix-bootstrap versions and preserve idempotence.", 55 | "" 56 | ] 57 | 58 | bootstrapStateFileName :: FilePath 59 | bootstrapStateFileName = ".nix-bootstrap.toml" 60 | 61 | bootstrapStateCodec :: TomlCodec BootstrapState 62 | bootstrapStateCodec = 63 | BootstrapState 64 | <$> Toml.diwrap (Toml.string "version") .= stateVersion 65 | <*> Toml.match ProjectName.tomlBiMap "projectName" .= stateProjectName 66 | <*> projectTypeCodec .= stateProjectType 67 | <*> preCommitHooksConfigCodec .= statePreCommitHooksConfig 68 | <*> continuousIntegrationConfigCodec .= stateContinuousIntegrationConfig 69 | <*> devContainerConfigCodec .= stateDevContainerConfig 70 | <*> Toml.diwrap (Toml.bool "useFlakes") .= stateUseFlakes 71 | 72 | newtype BootstrapVersion = BootstrapVersion {unBootstrapVersion :: String} 73 | deriving stock (Eq, Show) 74 | 75 | bootstrapStateFor :: 76 | ProjectName -> 77 | ProjectTypeV2 -> 78 | PreCommitHooksConfig -> 79 | ContinuousIntegrationConfig -> 80 | DevContainerConfig -> 81 | Bool -> 82 | BootstrapState 83 | bootstrapStateFor = BootstrapState (BootstrapVersion (showVersion version)) 84 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/BuildNix.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | 4 | -- Copyright : (c) Crown Copyright GCHQ 5 | -- Description : The code for build.nix 6 | module Bootstrap.Data.Bootstrappable.BuildNix (BuildNix (unBuildNix), buildNixFor) where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 10 | bootstrapContentNix, 11 | ) 12 | import Bootstrap.Data.ProjectName (ProjectName) 13 | import Bootstrap.Data.ProjectType 14 | ( JavaOptions (JavaOptions), 15 | ProjectType 16 | ( Elm, 17 | Go, 18 | Haskell, 19 | Java, 20 | Minimal, 21 | Node, 22 | Python, 23 | Rust 24 | ), 25 | SetUpGoBuild (SetUpGoBuild), 26 | SetUpJavaBuild (SetUpJavaBuild), 27 | haskellOptionsRequireBuild, 28 | ) 29 | import Bootstrap.Nix.Expr (Expr (EFunc), FunctionArgs (FASet), IsNixExpr (toNixExpr), nix) 30 | import Bootstrap.Nix.Expr.ReproducibleBuild 31 | ( ReproducibleBuildExpr (ReproducibleBuildExpr, rbeExpr, rbeRequirements), 32 | reproducibleBuildRequirementIdentifier, 33 | ) 34 | import Bootstrap.Nix.Expr.ReproducibleBuild.Go (reproducibleGoBuild) 35 | import Bootstrap.Nix.Expr.ReproducibleBuild.Haskell (reproducibleHaskellBuild) 36 | import Bootstrap.Nix.Expr.ReproducibleBuild.Java (reproducibleJavaBuild) 37 | import Bootstrap.Nix.Expr.ReproducibleBuild.Rust (reproducibleRustBuild) 38 | 39 | -- | A separate nix file defining reproducible builds for flake projects, 40 | -- to save it all being kept in flake.nix 41 | newtype BuildNix = BuildNix {unBuildNix :: ReproducibleBuildExpr} 42 | deriving stock (Eq, Show) 43 | 44 | instance Bootstrappable BuildNix where 45 | bootstrapName = const "nix/build.nix" 46 | bootstrapReason = const "This configures your reproducible project builds." 47 | bootstrapContent = bootstrapContentNix 48 | 49 | instance IsNixExpr BuildNix where 50 | toNixExpr (BuildNix ReproducibleBuildExpr {..}) = 51 | EFunc 52 | (FASet $ reproducibleBuildRequirementIdentifier <$> rbeRequirements) 53 | rbeExpr 54 | 55 | -- | Gives a `BuildNix` (or `Nothing`) as appropriate for the project details 56 | -- given. 57 | buildNixFor :: ProjectName -> ProjectType -> Maybe BuildNix 58 | buildNixFor flakeNixProjectName flakeNixProjectType = 59 | BuildNix 60 | <$> buildExprFor SrcDirParent flakeNixProjectName flakeNixProjectType 61 | 62 | -- | The source directory for the build 63 | data SrcDir 64 | = -- | src = ./.; 65 | SrcDirCurrent 66 | | -- | src = ../.; 67 | SrcDirParent 68 | 69 | srcDirExpr :: SrcDir -> Expr 70 | srcDirExpr = \case 71 | SrcDirCurrent -> [nix|./.|] 72 | SrcDirParent -> [nix|../.|] 73 | 74 | buildExprFor :: SrcDir -> ProjectName -> ProjectType -> Maybe ReproducibleBuildExpr 75 | buildExprFor srcDir projectName = \case 76 | Minimal -> Nothing 77 | Elm _ -> Nothing 78 | Haskell haskellOptions 79 | | haskellOptionsRequireBuild haskellOptions -> 80 | Just . reproducibleHaskellBuild projectName $ srcDirExpr srcDir 81 | Haskell _ -> Nothing 82 | Node _ -> Nothing 83 | Go (SetUpGoBuild True) -> Just $ reproducibleGoBuild projectName 84 | Go _ -> Nothing 85 | Java (JavaOptions _ _ (SetUpJavaBuild artefactId) jdk) -> Just $ reproducibleJavaBuild projectName artefactId jdk 86 | Java _ -> Nothing 87 | Python _ -> Nothing 88 | Rust -> Just . reproducibleRustBuild $ srcDirExpr srcDir 89 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/ElmJson.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.ElmJson 5 | ( ElmJson, 6 | elmJsonFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | bootstrapContentPrettyJson, 13 | ) 14 | import Bootstrap.Data.ProjectType (ProjectType (Elm)) 15 | import Data.Aeson (ToJSON (toJSON)) 16 | import Data.Aeson.QQ.Simple (aesonQQ) 17 | 18 | data ElmJson = ElmJson 19 | 20 | instance Bootstrappable ElmJson where 21 | bootstrapName = const "elm.json" 22 | bootstrapReason = const "The configuration of your elm project" 23 | bootstrapContent = bootstrapContentPrettyJson ["type", "source-directories", "elm-version"] 24 | 25 | instance ToJSON ElmJson where 26 | toJSON ElmJson = 27 | [aesonQQ|{ 28 | "type": "application", 29 | "source-directories": [ 30 | "src" 31 | ], 32 | "elm-version": "0.19.1", 33 | "dependencies": { 34 | "direct": { 35 | "elm/browser": "1.0.2", 36 | "elm/core": "1.0.5", 37 | "elm/html": "1.0.0" 38 | }, 39 | "indirect": { 40 | "elm/json": "1.1.3", 41 | "elm/time": "1.0.0", 42 | "elm/url": "1.0.0", 43 | "elm/virtual-dom": "1.0.3" 44 | } 45 | }, 46 | "test-dependencies": { 47 | "direct": {}, 48 | "indirect": {} 49 | } 50 | } 51 | |] 52 | 53 | elmJsonFor :: ProjectType -> Maybe ElmJson 54 | elmJsonFor = \case 55 | Elm _ -> Just ElmJson 56 | _ -> Nothing 57 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/IndexHtml.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Elm.IndexHtml 3 | ( ElmIndexHtml, 4 | elmIndexHtmlFor, 5 | ) 6 | where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 10 | ) 11 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 12 | import Bootstrap.Data.ProjectType (ElmMode (ElmModeNode), ElmOptions (ElmOptions), ProjectType (Elm)) 13 | import Text.Blaze.Html.Renderer.Pretty (renderHtml) 14 | import Text.Blaze.XHtml5 ((!)) 15 | import qualified Text.Blaze.XHtml5 as H 16 | import qualified Text.Blaze.XHtml5.Attributes as HAttr 17 | 18 | newtype ElmIndexHtml = ElmIndexHtml ProjectName 19 | 20 | instance Bootstrappable ElmIndexHtml where 21 | bootstrapName = const "src/index.html" 22 | bootstrapReason = const "The top-level HTML file for your site" 23 | bootstrapContent (ElmIndexHtml projectName) = 24 | pure . Right . toText . renderHtml $ H.docTypeHtml do 25 | H.head do 26 | H.meta ! HAttr.charset "utf-8" 27 | H.title . H.toHtml $ unProjectName projectName 28 | H.script ! HAttr.type_ "module" ! HAttr.src "./index.js" $ mempty 29 | H.body $ H.div ! HAttr.id "root" $ mempty 30 | 31 | elmIndexHtmlFor :: ProjectName -> ProjectType -> Maybe ElmIndexHtml 32 | elmIndexHtmlFor projectName = \case 33 | Elm (ElmOptions (ElmModeNode _) _) -> Just $ ElmIndexHtml projectName 34 | _ -> Nothing 35 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/IndexJs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.IndexJs 5 | ( ElmIndexJs, 6 | elmIndexJsFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | ) 13 | import Bootstrap.Data.ProjectType 14 | ( ElmMode (ElmModeNode), 15 | ElmOptions (ElmOptions), 16 | ProjectType (Elm), 17 | ) 18 | import Text.RawString.QQ (r) 19 | 20 | data ElmIndexJs = ElmIndexJs 21 | 22 | instance Bootstrappable ElmIndexJs where 23 | bootstrapName = const "src/index.js" 24 | bootstrapReason = const "Inserts your Elm code into index.html" 25 | bootstrapContent ElmIndexJs = 26 | pure $ 27 | Right 28 | [r|import { Elm } from "./Main.elm"; 29 | 30 | Elm.Main.init({ 31 | node: document.getElementById("root"), 32 | }); 33 | |] 34 | 35 | elmIndexJsFor :: ProjectType -> Maybe ElmIndexJs 36 | elmIndexJsFor = \case 37 | Elm (ElmOptions (ElmModeNode _) _) -> Just ElmIndexJs 38 | _ -> Nothing 39 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/MainElm.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | module Bootstrap.Data.Bootstrappable.Elm.MainElm (MainElm, mainElmFor) where 4 | 5 | import Bootstrap.Data.Bootstrappable 6 | ( Bootstrappable 7 | ( bootstrapContent, 8 | bootstrapName, 9 | bootstrapReason 10 | ), 11 | ) 12 | import Bootstrap.Data.ProjectType (ProjectType (Elm)) 13 | import Text.RawString.QQ (r) 14 | 15 | data MainElm = MainElm 16 | 17 | instance Bootstrappable MainElm where 18 | bootstrapName = const "src/Main.elm" 19 | bootstrapReason = const "The entrypoint for your Elm code" 20 | bootstrapContent = 21 | const . pure $ 22 | Right 23 | [r|module Main exposing (Model, Msg, main) 24 | 25 | import Browser 26 | import Html exposing (Html, button, text) 27 | import Html.Events exposing (onClick) 28 | 29 | 30 | main : Program () Model Msg 31 | main = 32 | Browser.sandbox 33 | { init = init 34 | , update = update 35 | , view = view 36 | } 37 | 38 | 39 | type alias Model = 40 | () 41 | 42 | 43 | init : Model 44 | init = 45 | () 46 | 47 | 48 | type Msg 49 | = NoOp 50 | 51 | 52 | update : Msg -> Model -> Model 53 | update msg model = 54 | case msg of 55 | NoOp -> 56 | model 57 | 58 | 59 | view : Model -> Html Msg 60 | view () = 61 | button [ onClick NoOp ] [ text "Hello, world!" ] 62 | |] 63 | 64 | mainElmFor :: ProjectType -> Maybe MainElm 65 | mainElmFor = \case 66 | Elm _ -> Just MainElm 67 | _ -> Nothing 68 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/PackageJson.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.PackageJson 5 | ( ElmPackageJson, 6 | elmPackageJsonFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | bootstrapContentPrettyJson, 13 | ) 14 | import Bootstrap.Data.ProjectType (ElmMode (ElmModeNode), ElmOptions (ElmOptions), ProjectType (Elm)) 15 | import Data.Aeson (ToJSON (toJSON)) 16 | import Data.Aeson.QQ.Simple (aesonQQ) 17 | 18 | data ElmPackageJson = ElmPackageJson 19 | 20 | instance Bootstrappable ElmPackageJson where 21 | bootstrapName = const "package.json" 22 | bootstrapReason = const "The node configuration of your Elm project" 23 | bootstrapContent = bootstrapContentPrettyJson [] 24 | 25 | instance ToJSON ElmPackageJson where 26 | toJSON ElmPackageJson = 27 | [aesonQQ|{ 28 | "devDependencies": { 29 | "@babel/core": ">=7.13.0 <8.0.0", 30 | "@babel/preset-env": "^7.1.6", 31 | "@parcel/core": ">=2.8.3 <3.0.0", 32 | "@parcel/transformer-elm": "^2.8.3", 33 | "elm": "^0.19.1-5", 34 | "parcel": "^2.8.3" 35 | }, 36 | "scripts": { 37 | "dev": "NODE_ENV=development parcel", 38 | "build": "NODE_ENV=production parcel build" 39 | }, 40 | "source": "src/index.html", 41 | "peerDependencies": { 42 | "@babel/core": ">=7.13.0 <8.0.0", 43 | "@babel/preset-env": "^7.1.6", 44 | "@parcel/core": ">=2.8.3 <3.0.0", 45 | "elm": "^0.19.1-5" 46 | }, 47 | "dependencies": { 48 | "buffer": "^5.5.0", 49 | "process": "^0.11.10", 50 | "punycode": "^1.4.1", 51 | "querystring-es3": "^0.2.1", 52 | "url": "^0.11.0" 53 | } 54 | } 55 | |] 56 | 57 | elmPackageJsonFor :: ProjectType -> Maybe ElmPackageJson 58 | elmPackageJsonFor = \case 59 | Elm (ElmOptions (ElmModeNode _) _) -> Just ElmPackageJson 60 | _ -> Nothing 61 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/Review/Config.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.Review.Config 5 | ( ElmReviewConfig, 6 | elmReviewConfigFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | ) 13 | import Bootstrap.Data.ProjectType 14 | ( ElmOptions (ElmOptions, elmOptionElmMode, elmOptionProvideElmReview), 15 | ProjectType (Elm), 16 | ) 17 | import Text.RawString.QQ (r) 18 | 19 | data ElmReviewConfig = ElmReviewConfig 20 | 21 | instance Bootstrappable ElmReviewConfig where 22 | bootstrapName = const "review/src/ReviewConfig.elm" 23 | bootstrapReason = const "The configuration of the elm-review tool" 24 | bootstrapContent = 25 | const . pure $ 26 | Right 27 | [r|module ReviewConfig exposing (config) 28 | 29 | import NoBooleanCase 30 | import NoDuplicatePorts 31 | import NoExposingEverything 32 | import NoImportingEverything 33 | import NoMissingSubscriptionsCall 34 | import NoMissingTypeAnnotation 35 | import NoMissingTypeConstructor 36 | import NoMissingTypeExpose 37 | import NoPrematureLetComputation 38 | import NoRecursiveUpdate 39 | import NoSimpleLetBody 40 | import NoUnused.CustomTypeConstructorArgs 41 | import NoUnused.CustomTypeConstructors 42 | import NoUnused.Dependencies 43 | import NoUnused.Exports 44 | import NoUnused.Modules 45 | import NoUnused.Parameters 46 | import NoUnused.Patterns 47 | import NoUnused.Variables 48 | import NoUnusedPorts 49 | import NoUselessSubscriptions 50 | import Review.Rule exposing (Rule) 51 | import Simplify 52 | 53 | 54 | config : List Rule 55 | config = 56 | [ NoBooleanCase.rule 57 | , NoDuplicatePorts.rule 58 | , NoExposingEverything.rule 59 | , NoImportingEverything.rule [] 60 | , NoMissingSubscriptionsCall.rule 61 | , NoMissingTypeAnnotation.rule 62 | , NoMissingTypeConstructor.rule 63 | , NoMissingTypeExpose.rule 64 | , NoPrematureLetComputation.rule 65 | , NoRecursiveUpdate.rule 66 | , NoSimpleLetBody.rule 67 | , NoUnusedPorts.rule 68 | , NoUnused.CustomTypeConstructors.rule [] 69 | , NoUnused.CustomTypeConstructorArgs.rule 70 | , NoUnused.Dependencies.rule 71 | , NoUnused.Exports.rule 72 | , NoUnused.Modules.rule 73 | , NoUnused.Parameters.rule 74 | , NoUnused.Patterns.rule 75 | , NoUnused.Variables.rule 76 | , NoUselessSubscriptions.rule 77 | , Simplify.rule Simplify.defaults 78 | ] 79 | |] 80 | 81 | elmReviewConfigFor :: ProjectType -> Maybe ElmReviewConfig 82 | elmReviewConfigFor (Elm ElmOptions {..}) 83 | | elmOptionProvideElmReview = 84 | Just ElmReviewConfig 85 | elmReviewConfigFor _ = Nothing 86 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Elm/Review/ElmJson.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.Review.ElmJson 5 | ( ElmReviewElmJson, 6 | elmReviewElmJsonFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | bootstrapContentPrettyJson, 13 | ) 14 | import Bootstrap.Data.ProjectType 15 | ( ElmOptions (ElmOptions, elmOptionElmMode, elmOptionProvideElmReview), 16 | ProjectType (Elm), 17 | ) 18 | import Data.Aeson (ToJSON (toJSON)) 19 | import Data.Aeson.QQ.Simple (aesonQQ) 20 | 21 | data ElmReviewElmJson = ElmReviewElmJson 22 | 23 | instance Bootstrappable ElmReviewElmJson where 24 | bootstrapName = const "review/elm.json" 25 | bootstrapReason = const "The configuration of the dependencies of the elm-review tool" 26 | bootstrapContent = bootstrapContentPrettyJson ["type", "source-directories", "elm-version"] 27 | 28 | instance ToJSON ElmReviewElmJson where 29 | toJSON ElmReviewElmJson = 30 | [aesonQQ| 31 | { 32 | "type": "application", 33 | "source-directories": ["src"], 34 | "elm-version": "0.19.1", 35 | "dependencies": { 36 | "direct": { 37 | "Arkham/elm-review-no-missing-type-constructor": "1.0.2", 38 | "elm/core": "1.0.5", 39 | "jfmengels/elm-review": "2.12.2", 40 | "jfmengels/elm-review-code-style": "1.1.3", 41 | "jfmengels/elm-review-common": "1.3.2", 42 | "jfmengels/elm-review-simplify": "2.0.28", 43 | "jfmengels/elm-review-the-elm-architecture": "1.0.3", 44 | "jfmengels/elm-review-unused": "1.1.29", 45 | "sparksp/elm-review-ports": "1.3.1", 46 | "stil4m/elm-syntax": "7.2.9", 47 | "truqu/elm-review-nobooleancase": "1.0.1" 48 | }, 49 | "indirect": { 50 | "elm/bytes": "1.0.8", 51 | "elm/html": "1.0.0", 52 | "elm/json": "1.1.3", 53 | "elm/parser": "1.1.0", 54 | "elm/project-metadata-utils": "1.0.2", 55 | "elm/random": "1.0.0", 56 | "elm/time": "1.0.0", 57 | "elm/virtual-dom": "1.0.3", 58 | "elm-community/list-extra": "8.7.0", 59 | "elm-explorations/test": "2.1.1", 60 | "miniBill/elm-unicode": "1.0.3", 61 | "pzp1997/assoc-list": "1.0.0", 62 | "rtfeldman/elm-hex": "1.0.0", 63 | "stil4m/structured-writer": "1.0.3" 64 | } 65 | }, 66 | "test-dependencies": { 67 | "direct": { 68 | "elm-explorations/test": "2.1.1" 69 | }, 70 | "indirect": {} 71 | } 72 | } 73 | |] 74 | 75 | elmReviewElmJsonFor :: ProjectType -> Maybe ElmReviewElmJson 76 | elmReviewElmJsonFor (Elm ElmOptions {..}) 77 | | elmOptionProvideElmReview = 78 | Just ElmReviewElmJson 79 | elmReviewElmJsonFor _ = Nothing 80 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Envrc.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Envrc (Envrc (Envrc)) where 3 | 4 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason)) 5 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig, unPreCommitHooksConfig) 6 | 7 | newtype Envrc = Envrc {preCommitHooksConfig :: PreCommitHooksConfig} 8 | 9 | instance Bootstrappable Envrc where 10 | bootstrapName = const ".envrc" 11 | bootstrapReason Envrc {preCommitHooksConfig} = 12 | "This tells direnv to load the nix shell" 13 | <> if unPreCommitHooksConfig preCommitHooksConfig 14 | then " and set up the pre-commit hooks." 15 | else "." 16 | bootstrapContent Envrc {preCommitHooksConfig} = 17 | pure . Right . unlines $ 18 | [ "direnv version 2.23.0 || exit 1", 19 | "if [ $(nix-env --version | grep -oE '[0-9]+\\.[0-9]+' | head -n1 | sed 's/\\./000/') -lt 20004 ]; then", 20 | " echo 'This project is set up to work with Nix Flakes, which your version of nix doesn'\"'\"'t support.'", 21 | " echo 'Please upgrade your nix version to at least 2.4 to continue.'", 22 | " exit 1", 23 | "fi", 24 | "if ! nix show-config --extra-experimental-features nix-command | grep experimental-features | grep flakes 1>/dev/null 2>&1; then", 25 | " printf '\\033[31m'", 26 | " echo 'This project is set up to work with Nix Flakes, which you don'\"'\"'t currently have enabled.'", 27 | " echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes'", 28 | " printf '\\033[0m'", 29 | " exit 1", 30 | "fi", 31 | "if ! nix show-config 1>/dev/null 2>&1; then", 32 | " printf '\\033[31m'", 33 | " echo 'This project is set up to work with Nix Flakes, which you don'\"'\"'t currently have enabled.'", 34 | " echo 'Specifically, the \"nix-command\" option is missing from your nix experimental-features configuration.'", 35 | " echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes'", 36 | " printf '\\033[0m'", 37 | " exit 1", 38 | "fi", 39 | "", 40 | "use flake" 41 | ] 42 | <> ["eval \"$shellHook\"" | unPreCommitHooksConfig preCommitHooksConfig] 43 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/GitPodYml.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.GitPodYml (GitPodYml, gitPodYmlFor) where 3 | 4 | import Bootstrap.Data.Bootstrappable 5 | ( Bootstrappable 6 | ( bootstrapContent, 7 | bootstrapName, 8 | bootstrapReason 9 | ), 10 | bootstrapContentYaml, 11 | ) 12 | import Bootstrap.Data.ProjectType (ProjectType) 13 | import Bootstrap.Data.VSCodeExtension (vsCodeExtensionsFor) 14 | import Data.Aeson (KeyValue ((.=)), ToJSON (toJSON)) 15 | import qualified Data.Aeson as Aeson 16 | import Data.Aeson.Types (emptyArray) 17 | 18 | newtype GitPodYml = GitPodYml ProjectType 19 | 20 | instance Bootstrappable GitPodYml where 21 | bootstrapName = const ".gitpod.yml" 22 | bootstrapReason = const "This overrides GitPod's automated tasks; they are not needed." 23 | bootstrapContent = pure . Right . bootstrapContentYaml 24 | 25 | instance ToJSON GitPodYml where 26 | toJSON (GitPodYml projectType) = 27 | Aeson.object 28 | [ "tasks" .= emptyArray, 29 | "vscode" .= Aeson.object ["extensions" .= toJSON (vsCodeExtensionsFor projectType)] 30 | ] 31 | 32 | gitPodYmlFor :: ProjectType -> Maybe GitPodYml 33 | gitPodYmlFor = Just . GitPodYml 34 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/GitlabCIConfig.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.GitlabCIConfig 3 | ( GitlabCIConfig, 4 | gitlabCIConfigFor, 5 | ) 6 | where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable 10 | ( bootstrapContent, 11 | bootstrapName, 12 | bootstrapReason 13 | ), 14 | ) 15 | import Bootstrap.Data.Bootstrappable.NixPreCommitHookConfig 16 | ( NixPreCommitHookConfig (nixPreCommitHookConfigImpureHookCommand), 17 | ) 18 | import Bootstrap.Data.ContinuousIntegration 19 | ( ContinuousIntegrationConfig (ContinuousIntegrationConfig), 20 | ) 21 | import Bootstrap.Data.ProjectType 22 | ( ElmMode (ElmModeBare, ElmModeNode), 23 | ElmOptions (ElmOptions, elmOptionElmMode), 24 | JavaOptions (JavaOptions), 25 | NodePackageManager (NPM, PNPm, Yarn), 26 | ProjectType (Elm, Go, Java), 27 | SetUpGoBuild (SetUpGoBuild), 28 | SetUpJavaBuild (SetUpJavaBuild), 29 | ) 30 | 31 | data GitlabCIConfig = GitlabCIConfig 32 | { gitlabCIConfigProjectType :: ProjectType, 33 | gitlabCIConfigPreCommitHooksConfig :: Maybe NixPreCommitHookConfig 34 | } 35 | 36 | instance Bootstrappable GitlabCIConfig where 37 | bootstrapName = const ".gitlab-ci.yml" 38 | bootstrapReason = const "This file sets up GitLab CI." 39 | bootstrapContent GitlabCIConfig {..} = do 40 | pure . Right . unlines $ 41 | [ "image: nixos/nix@sha256:473a2b527958665554806aea24d0131bacec46d23af09fef4598eeab331850fa", 42 | "", 43 | "default:", 44 | " before_script:", 45 | " - nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs", 46 | " - nix-channel --update", 47 | " - nix-env -iA nixpkgs.bash nixpkgs.openssh", 48 | " - echo \"experimental-features = nix-command flakes\" >> /etc/nix/nix.conf", 49 | "", 50 | case nixPreCommitHookConfigImpureHookCommand <$> gitlabCIConfigPreCommitHooksConfig of 51 | Just c | c /= "echo ok" -> "check-dev-environment-and-run-impure-hooks:" 52 | _ -> "check-dev-environment:", 53 | " stage: build", 54 | " script:", 55 | commandInShell $ 56 | maybe 57 | "echo ok" 58 | nixPreCommitHookConfigImpureHookCommand 59 | gitlabCIConfigPreCommitHooksConfig 60 | ] 61 | <> ( if isJust gitlabCIConfigPreCommitHooksConfig 62 | then 63 | [ "", 64 | "pre-commit-check:", 65 | " stage: build", 66 | " script:", 67 | " - nix flake check" 68 | ] 69 | else [] 70 | ) 71 | <> case gitlabCIConfigProjectType of 72 | Elm opts -> elmSiteJob opts 73 | Go (SetUpGoBuild True) -> buildJob 74 | Java (JavaOptions _ _ (SetUpJavaBuild _) _) -> buildJob 75 | _ -> [] 76 | where 77 | buildJob :: [Text] 78 | buildJob = 79 | [ "", 80 | "build:", 81 | " stage: build", 82 | " script:", 83 | " - nix build" 84 | ] 85 | elmSiteJob :: ElmOptions -> [Text] 86 | elmSiteJob ElmOptions {elmOptionElmMode} = 87 | [ "", 88 | "build-site:", 89 | " stage: build", 90 | " script:" 91 | ] 92 | <> ( commandInShell <$> case elmOptionElmMode of 93 | ElmModeBare -> ["elm make src/Main.elm"] 94 | ElmModeNode packageManager -> 95 | [ nodePackageManagerInstall packageManager, 96 | runWithPackageManager packageManager <> "build" 97 | ] 98 | ) 99 | commandInShell :: Text -> Text 100 | commandInShell = (" - nix develop -c " <>) 101 | nodePackageManagerInstall :: NodePackageManager -> Text 102 | nodePackageManagerInstall = \case 103 | NPM -> "npm ci" 104 | Yarn -> "yarn install --frozen-lockfile" 105 | PNPm -> "pnpm install --frozen-lockfile" 106 | runWithPackageManager :: NodePackageManager -> Text 107 | runWithPackageManager = 108 | (<> " run ") . \case 109 | NPM -> "npm" 110 | Yarn -> "yarn" 111 | PNPm -> "pnpm" 112 | 113 | gitlabCIConfigFor :: 114 | ContinuousIntegrationConfig -> 115 | ProjectType -> 116 | Maybe NixPreCommitHookConfig -> 117 | Maybe GitlabCIConfig 118 | gitlabCIConfigFor (ContinuousIntegrationConfig False) _ _ = Nothing 119 | gitlabCIConfigFor (ContinuousIntegrationConfig True) t p = 120 | Just $ GitlabCIConfig t p 121 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Go/Modfile.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Go.Modfile (GoModfile (GoModfile), goModfileFor) where 3 | 4 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason)) 5 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 6 | import Bootstrap.Error (CanDieOnError (dieOnErrorWithPrefix)) 7 | import Bootstrap.Monad (MonadBootstrap) 8 | import Bootstrap.Nix.Evaluate 9 | ( NixBinaryPaths, 10 | getVersionOfNixpkgsAttribute, 11 | ) 12 | import Text.Regex (mkRegex, subRegex) 13 | 14 | data GoModfile = GoModfile {goModfileProjectName :: ProjectName, goModfileGoVersion :: Text} 15 | 16 | instance Bootstrappable GoModfile where 17 | bootstrapName = const "go.mod" 18 | bootstrapReason = const "The declaration of your Go module and its dependencies" 19 | bootstrapContent GoModfile {..} = 20 | pure . Right $ 21 | unlines 22 | [ "module " <> unProjectName goModfileProjectName, 23 | "", 24 | "go " <> goModfileGoVersion 25 | ] 26 | 27 | goModfileFor :: (MonadBootstrap m) => NixBinaryPaths -> ProjectName -> m GoModfile 28 | goModfileFor nixBinaryPaths projectName = do 29 | goVersion <- 30 | dieOnErrorWithPrefix "Could not get bootstrapped Go version" 31 | . ExceptT 32 | $ getVersionOfNixpkgsAttribute nixBinaryPaths "go" 33 | let majorMinor = toText $ subRegex (mkRegex "^([0-9]+\\.[0-9]+).*") goVersion "\\1" 34 | pure $ GoModfile projectName majorMinor 35 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Haskell/LibHs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Haskell.LibHs 5 | ( LibHs, 6 | libHsFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | bootstrapContentHaskell, 13 | haskellModule, 14 | haskellModuleDecs, 15 | ) 16 | import Bootstrap.Data.ProjectType 17 | ( HaskellOptions (HaskellOptions), 18 | HaskellProjectType (HaskellProjectTypeBasic, HaskellProjectTypeReplOnly, HaskellProjectTypeServer), 19 | ProjectType (Haskell), 20 | ) 21 | import Control.Lens ((?~)) 22 | import Language.Haskell.TH (conT, varE) 23 | import Language.Haskell.TH.Syntax 24 | ( Body (NormalB), 25 | Clause (Clause), 26 | Dec (FunD, SigD), 27 | ModName (ModName), 28 | mkName, 29 | ) 30 | 31 | data LibHs = LibHs 32 | 33 | instance Bootstrappable LibHs where 34 | bootstrapName = const "src/Lib.hs" 35 | bootstrapReason = const "The entrypoint of your haskell library" 36 | bootstrapContent LibHs = do 37 | let lib = mkName "lib" 38 | ioUnit <- [t|$(conT $ mkName "IO") ()|] 39 | errorLine <- [|$(varE $ mkName "error") "todo: write the body of the lib function in src/Lib.hs"|] 40 | pure . pure . bootstrapContentHaskell $ 41 | haskellModule (ModName "Lib") (one "lib") 42 | & haskellModuleDecs 43 | ?~ SigD lib ioUnit 44 | :| [FunD lib [Clause [] (NormalB errorLine) []]] 45 | 46 | libHsFor :: ProjectType -> Maybe LibHs 47 | libHsFor = \case 48 | Haskell (HaskellOptions _ haskellProjectType) -> case haskellProjectType of 49 | HaskellProjectTypeReplOnly -> Nothing 50 | HaskellProjectTypeBasic _ -> Just LibHs 51 | HaskellProjectTypeServer _ -> Nothing 52 | _ -> Nothing 53 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Haskell/MainHs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Data.Bootstrappable.Haskell.MainHs 6 | ( MainHs, 7 | mainHsFor, 8 | ) 9 | where 10 | 11 | import Bootstrap.Data.Bootstrappable 12 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 13 | HaskellImport (HaskellImport), 14 | bootstrapContentHaskell, 15 | haskellModule, 16 | haskellModuleDecs, 17 | haskellModuleImports, 18 | ) 19 | import Bootstrap.Data.ProjectType 20 | ( HaskellOptions (HaskellOptions), 21 | HaskellProjectType (HaskellProjectTypeBasic, HaskellProjectTypeReplOnly, HaskellProjectTypeServer), 22 | ProjectType (Haskell), 23 | ) 24 | import Control.Lens ((?~)) 25 | import Language.Haskell.TH (appE, conT, varE) 26 | import Language.Haskell.TH.Syntax 27 | ( Body (NormalB), 28 | Clause (Clause), 29 | Dec (FunD, SigD), 30 | Exp (DoE, VarE), 31 | ModName (ModName), 32 | Stmt (NoBindS), 33 | mkName, 34 | ) 35 | 36 | data MainHs 37 | = -- | Simply calls Lib.lib 38 | MainHsLib 39 | | -- | Uses Server.app (a WAI Application) to run a warp server on port 8080 40 | MainHsWarp 41 | 42 | instance Bootstrappable MainHs where 43 | bootstrapName = const "app/Main.hs" 44 | bootstrapReason = const "The entrypoint of your haskell executable" 45 | bootstrapContent mainHs = do 46 | let app = mkName "app" 47 | lib = mkName "lib" 48 | main = mkName "main" 49 | run = mkName "run" 50 | ioUnit <- [t|$(conT $ mkName "IO") ()|] 51 | logServing <- [|putTextLn "Serving on 8080..."|] 52 | runServer <- varE run `appE` [|8080|] `appE` varE app 53 | pure . pure . bootstrapContentHaskell $ 54 | haskellModule (ModName "Main") (one "main") 55 | & haskellModuleImports 56 | ?~ ( case mainHs of 57 | MainHsLib -> one (HaskellImport (ModName "Lib") ["lib"]) 58 | MainHsWarp -> 59 | HaskellImport (ModName "Network.Wai.Handler.Warp") ["run"] 60 | :| [HaskellImport (ModName "Server") ["app"]] 61 | ) 62 | & haskellModuleDecs 63 | ?~ SigD main ioUnit 64 | :| [ FunD 65 | main 66 | [ Clause 67 | [] 68 | ( NormalB $ case mainHs of 69 | MainHsLib -> VarE lib 70 | MainHsWarp -> DoE Nothing [NoBindS logServing, NoBindS runServer] 71 | ) 72 | [] 73 | ] 74 | ] 75 | 76 | mainHsFor :: ProjectType -> Maybe MainHs 77 | mainHsFor = \case 78 | Haskell (HaskellOptions _ haskellProjectType) -> case haskellProjectType of 79 | HaskellProjectTypeReplOnly -> Nothing 80 | HaskellProjectTypeBasic _ -> Just MainHsLib 81 | HaskellProjectTypeServer _ -> Just MainHsWarp 82 | _ -> Nothing 83 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Haskell/PreludeHs.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Haskell.PreludeHs 3 | ( PreludeHs, 4 | preludeHsFor, 5 | ) 6 | where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 10 | HaskellImport (HaskellImportAll), 11 | bootstrapContentHaskell, 12 | haskellModule, 13 | haskellModuleImports, 14 | haskellModulePragmas, 15 | ) 16 | import Bootstrap.Data.ProjectType 17 | ( HaskellOptions (HaskellOptions), 18 | HaskellProjectType (HaskellProjectTypeBasic, HaskellProjectTypeReplOnly, HaskellProjectTypeServer), 19 | ProjectType (Haskell), 20 | ) 21 | import Control.Lens ((?~)) 22 | import Language.Haskell.TH.Syntax (ModName (ModName)) 23 | 24 | data PreludeHs = PreludeHs 25 | 26 | instance Bootstrappable PreludeHs where 27 | bootstrapName = const "src/Prelude.hs" 28 | bootstrapReason = const "The haskell prelude - what this exports is implicitly imported into every other module" 29 | bootstrapContent PreludeHs = 30 | pure . pure . bootstrapContentHaskell $ 31 | haskellModule (ModName "Prelude") (one "module Relude") 32 | & haskellModulePragmas 33 | ?~ one "{-# OPTIONS_GHC -Wno-missing-import-lists #-}" 34 | & haskellModuleImports 35 | ?~ one (HaskellImportAll (ModName "Relude")) 36 | 37 | preludeHsFor :: ProjectType -> Maybe PreludeHs 38 | preludeHsFor = \case 39 | Haskell (HaskellOptions _ haskellProjectType) -> case haskellProjectType of 40 | HaskellProjectTypeReplOnly -> Nothing 41 | HaskellProjectTypeBasic _ -> Just PreludeHs 42 | HaskellProjectTypeServer _ -> Just PreludeHs 43 | _ -> Nothing 44 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/HaskellPackagesNix.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | 4 | -- Copyright : (c) Crown Copyright GCHQ 5 | -- Description : A file defining a haskell package set 6 | module Bootstrap.Data.Bootstrappable.HaskellPackagesNix (HaskellPackagesNix, haskellPackagesNixFor) where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 10 | bootstrapContentNix, 11 | ) 12 | import Bootstrap.Data.ProjectType (HaskellOptions, ProjectType (Haskell)) 13 | import Bootstrap.Nix.Expr (Expr (EFunc), FunctionArgs (FASet), IsNixExpr (toNixExpr), nixident) 14 | import Bootstrap.Nix.Expr.Haskell (haskellPackagesExpr) 15 | 16 | -- | A separate nix file defining the haskell package set 17 | newtype HaskellPackagesNix = HaskellPackagesNix HaskellOptions 18 | deriving stock (Eq, Show) 19 | 20 | instance Bootstrappable HaskellPackagesNix where 21 | bootstrapName = const "nix/haskell-packages.nix" 22 | bootstrapReason = const "This configures the haskell package set from which your dependencies will be pulled." 23 | bootstrapContent = bootstrapContentNix 24 | 25 | instance IsNixExpr HaskellPackagesNix where 26 | toNixExpr (HaskellPackagesNix opts) = 27 | EFunc 28 | (FASet $ one [nixident|nixpkgs|]) 29 | (haskellPackagesExpr opts) 30 | 31 | -- | Gives a `HaskellPackagesNix` (or `Nothing`) as appropriate for the project details 32 | -- given. 33 | haskellPackagesNixFor :: ProjectType -> Maybe HaskellPackagesNix 34 | haskellPackagesNixFor (Haskell haskellOptions) = Just $ HaskellPackagesNix haskellOptions 35 | haskellPackagesNixFor _ = Nothing 36 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Python/Requirements.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Python.Requirements (Requirements (Requirements)) where 3 | 4 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason)) 5 | 6 | data Requirements = Requirements 7 | 8 | instance Bootstrappable Requirements where 9 | bootstrapName = const "requirements.txt" 10 | bootstrapReason = const "A file to contain python modules to be installed" 11 | bootstrapContent = const . pure $ Right "# Add your python dependencies to this file:" 12 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Rust/CargoLock.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE ViewPatterns #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Data.Bootstrappable.Rust.CargoLock 6 | ( CargoLock, 7 | cargoLockFor, 8 | ) 9 | where 10 | 11 | import Bootstrap.Data.Bootstrappable 12 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 13 | ) 14 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 15 | import Bootstrap.Data.ProjectType 16 | ( ProjectType (Rust), 17 | ) 18 | import Text.RawString.QQ (r) 19 | 20 | newtype CargoLock = CargoLock ProjectName 21 | 22 | instance Bootstrappable CargoLock where 23 | bootstrapName = const "Cargo.lock" 24 | bootstrapReason = const "The locked dependencies of your rust project" 25 | bootstrapContent (CargoLock (unProjectName -> n)) = 26 | pure . Right $ 27 | [r|# This file is automatically @generated by Cargo. 28 | # It is not intended for manual editing. 29 | version = 3 30 | 31 | [[package]] 32 | name = "|] 33 | <> n 34 | <> [r|" 35 | version = "0.1.0" 36 | |] 37 | 38 | cargoLockFor :: ProjectType -> ProjectName -> Maybe CargoLock 39 | cargoLockFor = \case 40 | Rust -> Just . CargoLock 41 | _ -> const Nothing 42 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Rust/CargoToml.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE ViewPatterns #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Data.Bootstrappable.Rust.CargoToml 6 | ( CargoToml, 7 | cargoTomlFor, 8 | ) 9 | where 10 | 11 | import Bootstrap.Data.Bootstrappable 12 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 13 | ) 14 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 15 | import Bootstrap.Data.ProjectType 16 | ( ProjectType (Rust), 17 | ) 18 | import Text.RawString.QQ (r) 19 | 20 | newtype CargoToml = CargoToml ProjectName 21 | 22 | instance Bootstrappable CargoToml where 23 | bootstrapName = const "Cargo.toml" 24 | bootstrapReason = const "The configuration of your rust project" 25 | bootstrapContent (CargoToml (unProjectName -> n)) = 26 | pure . Right $ 27 | [r|[package] 28 | name = "|] 29 | <> n 30 | <> [r|" 31 | version = "0.1.0" 32 | edition = "2021" 33 | 34 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 35 | 36 | [dependencies] 37 | |] 38 | 39 | cargoTomlFor :: ProjectType -> ProjectName -> Maybe CargoToml 40 | cargoTomlFor = \case 41 | Rust -> Just . CargoToml 42 | _ -> const Nothing 43 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/Rust/MainRs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Rust.MainRs 5 | ( MainRs, 6 | mainRsFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | ) 13 | import Bootstrap.Data.ProjectType 14 | ( ProjectType (Rust), 15 | ) 16 | import Text.RawString.QQ (r) 17 | 18 | data MainRs = MainRs 19 | 20 | instance Bootstrappable MainRs where 21 | bootstrapName = const "src/main.rs" 22 | bootstrapReason = const "Your rust application's entrypoint" 23 | bootstrapContent MainRs = 24 | pure $ 25 | Right 26 | [r|fn main() { 27 | println!("Hello, world!"); 28 | } 29 | |] 30 | 31 | mainRsFor :: ProjectType -> Maybe MainRs 32 | mainRsFor = \case 33 | Rust -> Just MainRs 34 | _ -> Nothing 35 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/SystemsNix.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | 4 | -- Copyright : (c) Crown Copyright GCHQ 5 | -- Description : The code for nix/systems.nix 6 | module Bootstrap.Data.Bootstrappable.SystemsNix (SystemsNix (SystemsNix)) where 7 | 8 | import Bootstrap.Data.Bootstrappable 9 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 10 | bootstrapContentNix, 11 | ) 12 | import Bootstrap.Nix.Expr (IsNixExpr (toNixExpr), nix) 13 | 14 | -- | A separate nix file defining functions for working with multiple systems easily, 15 | -- based on numtide/flake-utils 16 | data SystemsNix = SystemsNix 17 | deriving stock (Eq, Show) 18 | 19 | instance Bootstrappable SystemsNix where 20 | bootstrapName = const "nix/systems.nix" 21 | bootstrapReason = const "This contains helpers for working with different systems." 22 | bootstrapContent = bootstrapContentNix 23 | 24 | instance IsNixExpr SystemsNix where 25 | toNixExpr SystemsNix = 26 | [nix|# A minimal subset of numtide/flake-utils 27 | { 28 | forEachSystem = systems: f: 29 | builtins.foldl' ( 30 | attrs: system: let 31 | ret = f system; 32 | in 33 | builtins.foldl' (attrs: key: 34 | attrs // 35 | { 36 | ${key} = 37 | (attrs.${key} or {}) 38 | // { 39 | ${system} = ret.${key}; 40 | }; 41 | }) 42 | attrs (builtins.attrNames ret)) {} 43 | systems; 44 | 45 | system = allSystems: 46 | builtins.listToAttrs 47 | (builtins.map (system: { 48 | name = system; 49 | value = system; 50 | }) 51 | allSystems); 52 | }|] 53 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/VSCodeExtensions.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Description : Bootstraps .vscode/extensions.json 3 | -- Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.VSCodeExtensions 5 | ( VSCodeExtensions, 6 | vsCodeExtensionsFileFor, 7 | ) 8 | where 9 | 10 | import Bootstrap.Data.Bootstrappable 11 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 12 | bootstrapContentPrettyJson, 13 | ) 14 | import Bootstrap.Data.ProjectType (ProjectType) 15 | import Bootstrap.Data.VSCodeExtension (vsCodeExtensionsFor) 16 | import Data.Aeson (KeyValue ((.=)), ToJSON (toJSON)) 17 | import qualified Data.Aeson as Aeson 18 | 19 | -- | Represents the .vscode/extensions.json that we bootstrap 20 | newtype VSCodeExtensions = VSCodeExtensions ProjectType 21 | 22 | instance Bootstrappable VSCodeExtensions where 23 | bootstrapName = const ".vscode/extensions.json" 24 | bootstrapReason = const "This configures the extensions we recommend for VSCode." 25 | bootstrapContent = bootstrapContentPrettyJson [] 26 | 27 | instance ToJSON VSCodeExtensions where 28 | toJSON (VSCodeExtensions projectType) = 29 | Aeson.object 30 | ["recommendations" .= Aeson.Array (fromList . (toJSON <$>) $ vsCodeExtensionsFor projectType)] 31 | 32 | -- | Constructs `VSCodeExtensions` for the given `ProjectType` 33 | vsCodeExtensionsFileFor :: ProjectType -> Maybe VSCodeExtensions 34 | vsCodeExtensionsFileFor = Just . VSCodeExtensions 35 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Bootstrappable/VSCodeSettings.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.VSCodeSettings (VSCodeSettings, vsCodeSettingsFor) where 3 | 4 | import Bootstrap.Data.Bootstrappable 5 | ( Bootstrappable (bootstrapContent, bootstrapName, bootstrapReason), 6 | bootstrapContentPrettyJson, 7 | ) 8 | import Bootstrap.Data.DevContainer 9 | ( DevContainerConfig (DevContainerConfig), 10 | ) 11 | import Data.Aeson (KeyValue ((.=)), ToJSON (toJSON)) 12 | import qualified Data.Aeson as Aeson 13 | 14 | data VSCodeSettings = VSCodeSettings 15 | 16 | instance Bootstrappable VSCodeSettings where 17 | bootstrapName = const ".vscode/settings.json" 18 | bootstrapReason = const "This configures the settings for the extensions we recommend for VSCode." 19 | bootstrapContent = bootstrapContentPrettyJson [] 20 | 21 | instance ToJSON VSCodeSettings where 22 | toJSON = 23 | const $ 24 | Aeson.object 25 | [ "nixEnvSelector.nixFile" .= Aeson.String "${workspaceRoot}/flake.nix", 26 | "nixEnvSelector.useFlakes" .= Aeson.Bool True 27 | ] 28 | 29 | vsCodeSettingsFor :: DevContainerConfig -> Maybe VSCodeSettings 30 | vsCodeSettingsFor (DevContainerConfig True) = Just VSCodeSettings 31 | vsCodeSettingsFor (DevContainerConfig False) = Nothing 32 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Config.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TemplateHaskell #-} 3 | 4 | -- | 5 | -- Description : Utilities for working with nix-bootstrap's configuration 6 | -- Copyright : (c) Crown Copyright GCHQ 7 | module Bootstrap.Data.Config 8 | ( -- * Config Data Types 9 | Config, 10 | 11 | -- * Loading an existing `Config` 12 | LoadConfigResult (..), 13 | loadConfig, 14 | 15 | -- * Creating a `Config` from scratch 16 | configFor, 17 | 18 | -- * Accessing `Config` values 19 | configProjectName, 20 | configProjectType, 21 | configSetUpPreCommitHooks, 22 | configSetUpContinuousIntegration, 23 | configSetUpVSCodeDevContainer, 24 | configTarget, 25 | 26 | -- * Exceptions 27 | NonFlakeConfigException, 28 | ) 29 | where 30 | 31 | import Bootstrap.Data.Config.Internal 32 | ( Config, 33 | ConfigV10 (ConfigV10), 34 | LoadConfigResult 35 | ( LoadConfigResultError, 36 | LoadConfigResultFound, 37 | LoadConfigResultNotFound 38 | ), 39 | NonFlakeConfigException, 40 | VersionedConfig (VersionedConfigV10), 41 | VersionedProjectType (VPT10), 42 | configV10ProjectName, 43 | configV10ProjectType, 44 | configV10SetUpContinuousIntegration, 45 | configV10SetUpPreCommitHooks, 46 | configV10SetUpVSCodeDevContainer, 47 | configV10Target, 48 | loadConfig, 49 | _Current, 50 | ) 51 | import Bootstrap.Data.Config.Internal.TH (makeConfigLenses) 52 | import Bootstrap.Data.ContinuousIntegration 53 | ( ContinuousIntegrationConfig, 54 | ) 55 | import Bootstrap.Data.DevContainer (DevContainerConfig) 56 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig) 57 | import Bootstrap.Data.ProjectName (ProjectName) 58 | import Bootstrap.Data.ProjectType (ProjectType) 59 | import Bootstrap.Data.Target (Target) 60 | 61 | makeConfigLenses 'ConfigV10 62 | 63 | -- | Initialise a new `Config` from scratch 64 | configFor :: 65 | ProjectName -> 66 | ProjectType -> 67 | PreCommitHooksConfig -> 68 | ContinuousIntegrationConfig -> 69 | DevContainerConfig -> 70 | Target -> 71 | Config 72 | configFor a1 a2 a3 a4 a5 a6 = 73 | VersionedConfigV10 $ 74 | ConfigV10 a1 (VPT10 a2) a3 a4 a5 a6 75 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Config/Internal/CurrentVersion.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | -- Description : Defines the most recent config version, from which 3 | -- many of the config types are generated. 4 | module Bootstrap.Data.Config.Internal.CurrentVersion (currentVersionNumber, versionUniverse) where 5 | 6 | -- | The most recent version of the config 7 | currentVersionNumber :: Int 8 | currentVersionNumber = 10 9 | 10 | versionUniverse :: [Int] 11 | versionUniverse = [1 .. currentVersionNumber] 12 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Config/Internal/TH.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} 3 | 4 | -- | 5 | -- Description : Functions used for generating code relating to Config 6 | -- Copyright : (c) Crown Copyright GCHQ 7 | module Bootstrap.Data.Config.Internal.TH (makeConfigLenses) where 8 | 9 | import Bootstrap.Data.Config.Internal (Config, _VersionedProjectType) 10 | import Bootstrap.Data.ProjectType (ProjectType) 11 | import Control.Lens (Lens') 12 | import qualified Data.Char as C 13 | import Data.Foldable (foldrM) 14 | import qualified Data.Text as T 15 | import Language.Haskell.TH 16 | ( Body (NormalB), 17 | Dec (SigD, ValD), 18 | DecsQ, 19 | Exp (InfixE, VarE), 20 | Name, 21 | Pat (VarP), 22 | Quote (newName), 23 | Type (AppT, ConT), 24 | lookupValueName, 25 | nameBase, 26 | ) 27 | import Language.Haskell.TH.Datatype as D 28 | ( ConstructorInfo (constructorFields, constructorVariant), 29 | ConstructorVariant (RecordConstructor), 30 | reifyConstructor, 31 | ) 32 | import qualified Relude.Unsafe as Unsafe 33 | 34 | -- | Makes lenses for the given data constructor without 35 | -- the config version number in them 36 | makeConfigLenses :: Name -> DecsQ 37 | makeConfigLenses name = do 38 | constructorInfo <- D.reifyConstructor name 39 | let D.RecordConstructor fieldNames = D.constructorVariant constructorInfo 40 | zipped = zip fieldNames (D.constructorFields constructorInfo) 41 | foldrM makeConfigLens [] zipped 42 | where 43 | makeConfigLens :: (Name, Language.Haskell.TH.Type) -> [Dec] -> DecsQ 44 | makeConfigLens (constructorName, constructorType) acc = do 45 | let baseFieldName = toText $ nameBase constructorName 46 | digits = T.takeWhile C.isDigit $ T.dropWhile (not . C.isDigit) baseFieldName 47 | fieldPrefix <- 48 | if T.null digits 49 | then fail "Field constructor should have config version digit in it" 50 | else pure $ "_configV" <> digits 51 | lensNameBody <- 52 | maybe 53 | (fail $ "Expected constructor to have prefix " <> toString fieldPrefix) 54 | pure 55 | (T.stripPrefix fieldPrefix baseFieldName) 56 | let lensNameStr = toString . ("config" <>) . (\(c, s) -> T.toUpper (one c) <> s) . Unsafe.fromJust $ T.uncons lensNameBody 57 | lensName <- newName lensNameStr 58 | versionedLensName <- Unsafe.fromJust <$> lookupValueName (drop 1 (toString fieldPrefix) <> toString lensNameBody) 59 | projectTypeType <- [t|ProjectType|] 60 | _VersionedProjectType' <- Just <$> [|_VersionedProjectType|] 61 | InfixE wrapped compose _ <- [e|_Current . x|] 62 | let isProjectTypeField = nameBase lensName == "configProjectType" 63 | versionedLens = Just $ VarE versionedLensName 64 | constructorType' = if isProjectTypeField then projectTypeType else constructorType 65 | body = 66 | NormalB . InfixE wrapped compose $ 67 | if isProjectTypeField 68 | then Just (InfixE versionedLens compose _VersionedProjectType') 69 | else versionedLens 70 | sig = SigD lensName (AppT (AppT (ConT ''Lens') (ConT ''Config)) constructorType') 71 | impl = ValD (VarP lensName) body [] 72 | pure $ acc <> [sig, impl] 73 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Config/Internal/THHelpers.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | -- Description : Common TH functions for use in ...Config.Internal 5 | module Bootstrap.Data.Config.Internal.THHelpers (isoForName) where 6 | 7 | import Control.Lens (iso) 8 | import Language.Haskell.TH 9 | ( ExpQ, 10 | Name, 11 | Quote (newName), 12 | appE, 13 | conE, 14 | conP, 15 | lamE, 16 | varE, 17 | varP, 18 | ) 19 | 20 | -- | Creates an iso like 21 | -- iso (\(VPT9 x) -> x) VPT9 22 | -- if VPT9 is the constructor name 23 | isoForName :: Name -> ExpQ 24 | isoForName constructorName = do 25 | x <- newName "x" 26 | [|iso|] `appE` lamE [conP constructorName [varP x]] (varE x) `appE` conE constructorName 27 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/ContinuousIntegration.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GeneralisedNewtypeDeriving #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.ContinuousIntegration 5 | ( ContinuousIntegrationConfig (..), 6 | continuousIntegrationConfigCodec, 7 | ) 8 | where 9 | 10 | import Dhall (FromDhall, ToDhall) 11 | import Toml (TomlCodec) 12 | import qualified Toml 13 | 14 | newtype ContinuousIntegrationConfig = ContinuousIntegrationConfig {unContinuousIntegrationConfig :: Bool} 15 | deriving newtype (FromDhall, ToDhall) 16 | deriving stock (Eq, Show) 17 | 18 | continuousIntegrationConfigCodec :: TomlCodec ContinuousIntegrationConfig 19 | continuousIntegrationConfigCodec = Toml.diwrap (Toml.bool "setUpContinuousIntegration") 20 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/DevContainer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GeneralisedNewtypeDeriving #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.DevContainer (DevContainerConfig (..), devContainerConfigCodec) where 5 | 6 | import Dhall (FromDhall, ToDhall) 7 | import Toml (TomlCodec) 8 | import qualified Toml 9 | 10 | newtype DevContainerConfig = DevContainerConfig {unDevContainerConfig :: Bool} 11 | deriving newtype (FromDhall, ToDhall) 12 | deriving stock (Eq, Show) 13 | 14 | devContainerConfigCodec :: TomlCodec DevContainerConfig 15 | devContainerConfigCodec = Toml.diwrap (Toml.bool "setUpDevContainer") 16 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/GHCVersion.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | {-# LANGUAGE DerivingVia #-} 4 | {-# LANGUAGE TypeOperators #-} 5 | 6 | -- | Copyright : (c) Crown Copyright GCHQ 7 | -- Description : Represent versions of the Glasgow Haskell Compiler 8 | module Bootstrap.Data.GHCVersion 9 | ( -- * Data 10 | GHCVersion (..), 11 | 12 | -- * Parsing 13 | parseGHCVersion, 14 | 15 | -- * Formatting 16 | printGHCVersion, 17 | 18 | -- * Use in Nix expressions 19 | ghcVersionProperty, 20 | ) 21 | where 22 | 23 | import Bootstrap.Nix.Expr (Identifier (Identifier), Property (PIdent)) 24 | import qualified Data.Char as C 25 | import qualified Data.Text as T 26 | import Dhall (FromDhall, ToDhall) 27 | import Dhall.Deriving 28 | ( CamelCase, 29 | Codec (Codec), 30 | DropPrefix, 31 | Field, 32 | type (<<<), 33 | ) 34 | import qualified Relude.Unsafe as Unsafe 35 | import Text.Megaparsec 36 | ( MonadParsec (label, takeWhile1P), 37 | Parsec, 38 | failure, 39 | ) 40 | import Text.Megaparsec.Char (string) 41 | import qualified Text.Megaparsec.Error as ParseError 42 | 43 | -- | Represents a version of the Glasgow Haskell Compiler 44 | data GHCVersion = GHCVersion 45 | { ghcVersionMajor :: Natural, 46 | ghcVersionMinor :: Natural, 47 | ghcVersionPatch :: Natural 48 | } 49 | deriving stock (Eq, Generic, Ord, Show) 50 | deriving (FromDhall, ToDhall) via Codec (Field (CamelCase <<< DropPrefix "ghcVersion")) GHCVersion 51 | 52 | -- | Gets the attribute name of the GHC version, able to be queried in nixpkgs 53 | ghcVersionProperty :: GHCVersion -> Property 54 | ghcVersionProperty = PIdent . Identifier . ("ghc" <>) . T.filter (/= '.') . printGHCVersion 55 | 56 | -- | Parses a nixpkgs attribute representing a GHC version 57 | -- 58 | -- Use `ghcVersionAttributeName` to produce this form 59 | parseGHCVersion :: Parsec Void String GHCVersion 60 | parseGHCVersion = label "GHC Version attribute" do 61 | void $ string "ghc" 62 | -- Safe because we have the `isDigit` predicate 63 | digits <- Unsafe.read . one <<$>> takeWhile1P (Just "version digits") C.isDigit 64 | case digits of 65 | [x, y, z] -> pure $ GHCVersion x y z 66 | [x, y1, y2, z] -> pure $ GHCVersion x (twoDigitVersionNumber y1 y2) z 67 | [x1, x2, y1, y2, z] -> pure $ GHCVersion (twoDigitVersionNumber x1 x2) (twoDigitVersionNumber y1 y2) z 68 | _ -> failure Nothing $ fromList [ParseError.Label ('v' :| "ersion digits")] 69 | where 70 | twoDigitVersionNumber :: Natural -> Natural -> Natural 71 | twoDigitVersionNumber x1 x2 = Unsafe.read $ show x1 <> show x2 72 | 73 | -- | Print out the version in a human-readable format 74 | -- 75 | -- Not an inverse of `parseGHCVersion` - see `ghcVersionAttributeName` 76 | printGHCVersion :: GHCVersion -> Text 77 | printGHCVersion (GHCVersion major minor patch) = 78 | T.intercalate "." $ show <$> [major, minor, patch] 79 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/HList.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeFamilies #-} 3 | {-# LANGUAGE TypeOperators #-} 4 | 5 | -- | Description : Defines a type for heterogeneous lists. 6 | -- Copyright : (c) Crown Copyright GCHQ 7 | module Bootstrap.Data.HList (HList (..), (~:)) where 8 | 9 | -- | Represents a heterogeneous list (a list where the type of 10 | -- each element can be different). 11 | data HList :: [Type] -> Type where 12 | HNil :: HList '[] 13 | HCons :: x -> HList xs -> HList (x ': xs) 14 | 15 | infixr 5 ~: 16 | 17 | -- | Operator alias for `HCons`. 18 | (~:) :: x -> HList xs -> HList (x ': xs) 19 | (~:) = HCons 20 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/PreCommitHook.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GeneralisedNewtypeDeriving #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.PreCommitHook (PreCommitHooksConfig (..), preCommitHooksConfigCodec) where 5 | 6 | import Dhall (FromDhall, ToDhall) 7 | import Toml (TomlCodec) 8 | import qualified Toml 9 | 10 | newtype PreCommitHooksConfig = PreCommitHooksConfig {unPreCommitHooksConfig :: Bool} 11 | deriving newtype (FromDhall, ToDhall) 12 | deriving stock (Eq, Show) 13 | 14 | preCommitHooksConfigCodec :: TomlCodec PreCommitHooksConfig 15 | preCommitHooksConfigCodec = Toml.diwrap (Toml.bool "setUpPreCommitHooks") 16 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/ProjectName.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GeneralisedNewtypeDeriving #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.ProjectName 5 | ( ProjectName (unProjectName), 6 | mkProjectName, 7 | replaceSpacesWithDashes, 8 | tomlBiMap, 9 | ) 10 | where 11 | 12 | import qualified Data.Char as C 13 | import qualified Data.Text as T 14 | import Dhall (FromDhall, ToDhall) 15 | import qualified Toml 16 | 17 | -- | The name of a project. It can be comprised of characters matching [-_a-zA-Z0-9 ] 18 | newtype ProjectName = ProjectName {unProjectName :: Text} 19 | deriving stock (Eq, Show) 20 | deriving newtype (FromDhall, ToDhall) 21 | 22 | -- | A smart constructor which gives back Nothing if the project name 23 | -- contains invalid characters or begins with a non-alphabetical character. 24 | mkProjectName :: Text -> Maybe ProjectName 25 | mkProjectName name = 26 | if T.all C.isAlpha (T.take 1 name) && T.all isValidChar name 27 | then Just $ ProjectName name 28 | else Nothing 29 | where 30 | isValidChar :: Char -> Bool 31 | isValidChar = \case 32 | '_' -> True 33 | '-' -> True 34 | ' ' -> True 35 | c -> C.isAlphaNum c 36 | 37 | replaceSpacesWithDashes :: ProjectName -> ProjectName 38 | replaceSpacesWithDashes = ProjectName . T.replace " " "-" . unProjectName 39 | 40 | tomlBiMap :: Toml.TomlBiMap ProjectName Toml.AnyValue 41 | tomlBiMap = Toml.BiMap {forward, backward} 42 | where 43 | forward :: ProjectName -> Either Toml.TomlBiMapError Toml.AnyValue 44 | forward projectName = Right . Toml.AnyValue . Toml.Text $ unProjectName projectName 45 | backward :: Toml.AnyValue -> Either Toml.TomlBiMapError ProjectName 46 | backward (Toml.AnyValue v) = case v of 47 | Toml.Text t -> 48 | maybeToRight 49 | (Toml.ArbitraryError "Invalid project name") 50 | (mkProjectName t) 51 | _ -> first Toml.WrongValue $ Toml.mkMatchError Toml.TText v 52 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Target.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | {-# LANGUAGE DerivingVia #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | -- Description : A representation of the system to which we're bootstrapping 6 | module Bootstrap.Data.Target (Target (..)) where 7 | 8 | import Dhall (FromDhall, ToDhall) 9 | import Dhall.Deriving (AsIs, Codec (Codec), Constructor) 10 | 11 | -- | The system to which we're bootstrapping. 12 | -- 13 | -- For public purposes this will always be `TargetDefault`. 14 | data Target 15 | = TargetDefault 16 | deriving stock (Eq, Generic, Show) 17 | deriving (FromDhall, ToDhall) via Codec (Constructor AsIs) Target 18 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/VSCodeExtension.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GeneralisedNewtypeDeriving #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | -- Description : Utilities for working with VSCode extensions 5 | module Bootstrap.Data.VSCodeExtension (VSCodeExtension (..), vsCodeExtensionsFor) where 6 | 7 | import Bootstrap.Data.ProjectType 8 | ( HaskellOptions (HaskellOptions), 9 | InstallLombok (unInstallLombok), 10 | JavaOptions (JavaOptions), 11 | ProjectType 12 | ( Elm, 13 | Go, 14 | Haskell, 15 | Java, 16 | Minimal, 17 | Node, 18 | Python, 19 | Rust 20 | ), 21 | ) 22 | import Data.Aeson (ToJSON) 23 | 24 | -- | Represents the ID of an individual extension 25 | newtype VSCodeExtension = VSCodeExtension Text 26 | deriving newtype (ToJSON) 27 | 28 | -- | The list of extensions we recommend for the given `ProjectType` 29 | vsCodeExtensionsFor :: ProjectType -> [VSCodeExtension] 30 | vsCodeExtensionsFor = 31 | (VSCodeExtension <$>) . (["arrterian.nix-env-selector", "jnoortheen.nix-ide"] <>) . \case 32 | Minimal -> [] 33 | Elm _ -> ["Elmtooling.elm-ls-vscode"] 34 | (Haskell (HaskellOptions _ _)) -> ["haskell.haskell"] 35 | Node _ -> [] 36 | Go _ -> ["golang.Go"] 37 | Java (JavaOptions _ installLombok _ _) -> 38 | ["vscjava.vscode-java-pack"] 39 | <> ["gabrielbb.vscode-lombok" | unInstallLombok installLombok] 40 | Python _ -> ["ms-python.python"] 41 | Rust -> ["rust-lang.rust-analyzer"] 42 | -------------------------------------------------------------------------------- /src/Bootstrap/Data/Version.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Version (MajorVersion (MajorVersion), displayMajorVersion, parseMajorVersion, toMajorVersion) where 3 | 4 | import qualified Data.Version as V 5 | import Text.ParserCombinators.ReadP (readP_to_S) 6 | 7 | data MajorVersion = MajorVersion 8 | { flagshipVersion :: Int, 9 | majorVersion :: Int 10 | } 11 | deriving stock (Eq, Show) 12 | 13 | instance Ord MajorVersion where 14 | a <= b = 15 | flagshipVersion a < flagshipVersion b 16 | || (flagshipVersion a == flagshipVersion b && majorVersion a <= majorVersion b) 17 | 18 | displayMajorVersion :: (IsString s, Semigroup s) => MajorVersion -> s 19 | displayMajorVersion MajorVersion {..} = show flagshipVersion <> "." <> show majorVersion 20 | 21 | parseMajorVersion :: String -> Maybe MajorVersion 22 | parseMajorVersion = toMajorVersion <=< (fmap fst . viaNonEmpty last . readP_to_S V.parseVersion) 23 | 24 | toMajorVersion :: V.Version -> Maybe MajorVersion 25 | toMajorVersion version = do 26 | case V.versionBranch version of 27 | (flagship : major : _) -> Just $ MajorVersion flagship major 28 | _ -> Nothing 29 | -------------------------------------------------------------------------------- /src/Bootstrap/Error.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# LANGUAGE UndecidableInstances #-} 4 | 5 | -- | Copyright : (c) Crown Copyright GCHQ 6 | module Bootstrap.Error 7 | ( InProgressDuration (..), 8 | runWithProgressMsg, 9 | CanDieOnError (dieOnError, dieOnError', dieOnErrorWithPrefix), 10 | ) 11 | where 12 | 13 | import Bootstrap.Monad (MonadBootstrap) 14 | import Bootstrap.Terminal (putErrorLn, withAttributes) 15 | import System.Terminal 16 | ( MonadColorPrinter (cyan, foreground, green, red), 17 | MonadFormattingPrinter (bold, italic), 18 | MonadPrinter (putText, putTextLn), 19 | ) 20 | 21 | data InProgressDuration = Quick | LongRunning 22 | 23 | runWithProgressMsg :: 24 | forall e m a. 25 | (MonadBootstrap m) => 26 | InProgressDuration -> 27 | Text -> 28 | ExceptT e m a -> 29 | m (Either e a) 30 | runWithProgressMsg duration msg action = do 31 | withAttributes [italic, foreground cyan] . putText $ msg <> msgEnder 32 | res <- runExceptT action 33 | case res of 34 | Left _ -> 35 | withAttributes [bold, foreground red] $ putTextLn "ERROR" 36 | Right _ -> 37 | withAttributes [bold, foreground green] $ putTextLn "DONE" 38 | pure res 39 | where 40 | msgEnder :: Text 41 | msgEnder = case duration of 42 | Quick -> "... " 43 | LongRunning -> " (this may take a while)... " 44 | 45 | class CanDieOnError m where 46 | dieOnError :: (e -> Text) -> ExceptT e m a -> m a 47 | 48 | -- | Convenience function to print exceptions unmodified when dying 49 | dieOnError' :: (Exception e) => ExceptT e m a -> m a 50 | dieOnError' = dieOnError (toText . displayException) 51 | 52 | -- | Convenience function to print exceptions unmodified but with a prefix when dying. 53 | -- 54 | -- Adds a colon and space to the end of the prefix. 55 | dieOnErrorWithPrefix :: (Exception e) => Text -> ExceptT e m a -> m a 56 | dieOnErrorWithPrefix prefix = dieOnError (((prefix <> ": ") <>) . toText . displayException) 57 | 58 | instance (MonadBootstrap m) => CanDieOnError m where 59 | dieOnError displayError action = 60 | runExceptT action >>= \case 61 | Left e -> putErrorLn (displayError e) >> exitFailure 62 | Right a -> pure a 63 | -------------------------------------------------------------------------------- /src/Bootstrap/GitPod.hs: -------------------------------------------------------------------------------- 1 | -- | Description : Defines additional helpers used when nix-bootstrap is running in GitPod 2 | -- Copyright : (c) Crown Copyright GCHQ 3 | module Bootstrap.GitPod (resetPermissionsInGitPod) where 4 | 5 | import Bootstrap.Error 6 | ( CanDieOnError (dieOnError'), 7 | InProgressDuration (LongRunning), 8 | runWithProgressMsg, 9 | ) 10 | import Bootstrap.Monad (MonadBootstrap) 11 | import Bootstrap.Terminal (putErrorLn) 12 | import Bootstrap.Unix (runCommand, whoami) 13 | 14 | -- | If running in GitPod, chowns the /nix directory to the gitpod 15 | -- user to prevent permissions errors. 16 | resetPermissionsInGitPod :: (MonadBootstrap m) => m () 17 | resetPermissionsInGitPod = 18 | whoami >>= \case 19 | Right "gitpod" -> do 20 | void 21 | . dieOnError' 22 | . ExceptT 23 | . runWithProgressMsg LongRunning "Fixing GitPod's permissions on /nix" 24 | . ExceptT 25 | $ runCommand "/usr/bin/sudo" ["chown", "-R", "gitpod:gitpod", "/nix"] 26 | Right _ -> pass 27 | Left e -> do 28 | putErrorLn $ "Could not get current user: " <> toText (displayException e) 29 | putErrorLn "If running in GitPod, later steps may fail." 30 | -------------------------------------------------------------------------------- /src/Bootstrap/Monad.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE UndecidableInstances #-} 4 | {-# OPTIONS_GHC -Wno-orphans #-} 5 | 6 | -- | Copyright : (c) Crown Copyright GCHQ 7 | module Bootstrap.Monad (MonadBootstrap) where 8 | 9 | import Control.Monad.Catch (MonadCatch, MonadMask) 10 | import Language.Haskell.TH (Quote (newName)) 11 | import System.Terminal 12 | ( MonadColorPrinter, 13 | MonadFormattingPrinter, 14 | MonadInput, 15 | MonadMarkupPrinter, 16 | MonadScreen, 17 | TerminalT, 18 | ) 19 | 20 | type MonadBootstrap m = 21 | ( MonadCatch m, 22 | MonadColorPrinter m, 23 | MonadFormattingPrinter m, 24 | MonadInput m, 25 | MonadIO m, 26 | MonadMarkupPrinter m, 27 | MonadMask m, 28 | MonadScreen m, 29 | Quote m 30 | ) 31 | 32 | instance (Monad a, MonadIO (TerminalT m a)) => Quote (TerminalT m a) where 33 | newName = liftIO . newName 34 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Command.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ScopedTypeVariables #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.Command 5 | ( NixCommandVariant (..), 6 | NixCommand (NixCommand), 7 | writeNixCommand, 8 | ) 9 | where 10 | 11 | data NixCommandVariant = NCVBuild 12 | 13 | newtype NixCommand = NixCommand 14 | { ncVariant :: NixCommandVariant 15 | } 16 | 17 | -- | Prints a nix command in the form it would be used in a shell. 18 | -- 19 | -- >>> writeNixCommand (NixCommand NCVBuild) 20 | -- nix build 21 | writeNixCommand :: forall s. (IsString s, Semigroup s) => NixCommand -> s 22 | writeNixCommand NixCommand {..} = "nix " <> commandPart 23 | where 24 | commandPart :: s 25 | commandPart = case ncVariant of 26 | NCVBuild -> "build" 27 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/BuildInputs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.BuildInputs (BuildInputSpec (..), buildInputsBindings) where 6 | 7 | import Bootstrap.Data.PreCommitHook 8 | ( PreCommitHooksConfig (unPreCommitHooksConfig), 9 | ) 10 | import Bootstrap.Data.ProjectType 11 | ( HasProjectSuperType (projectSuperType), 12 | ProjectSuperType (PSTPython), 13 | ) 14 | import Bootstrap.Nix.Expr 15 | ( Binding, 16 | Expr (EGrouping, EList, EWith), 17 | IsNixExpr (toNixExpr), 18 | nix, 19 | nixbinding, 20 | nixproperty, 21 | (|++), 22 | (|=), 23 | ) 24 | 25 | data BuildInputGroup 26 | = BIGNixpkgs [Expr] 27 | | BIGOther [Expr] 28 | | BIGPreCommitHooks 29 | | BIGPythonPackages 30 | | BIGNativeNixpkgsInputs [Expr] 31 | 32 | instance IsNixExpr BuildInputGroup where 33 | toNixExpr = \case 34 | BIGNixpkgs buildInputs -> EWith [nix|nixpkgs|] $ EList buildInputs 35 | BIGOther otherPackages -> EList otherPackages 36 | BIGPreCommitHooks -> [nix|preCommitHooks.tools|] 37 | BIGPythonPackages -> [nix|[pythonPackages]|] 38 | BIGNativeNixpkgsInputs nativeBuildInputs -> EWith [nix|nixpkgs|] $ EList nativeBuildInputs 39 | 40 | -- | Whether this group must be wrapped with brackets to be concatenated 41 | requiresGrouping :: BuildInputGroup -> Bool 42 | requiresGrouping = \case 43 | BIGNixpkgs _ -> True 44 | BIGOther _ -> False 45 | BIGPreCommitHooks -> False 46 | BIGPythonPackages -> False 47 | BIGNativeNixpkgsInputs _ -> True 48 | 49 | data BuildInputSpec projectType = BuildInputSpec 50 | { bisNixpkgsPackages :: [Expr], 51 | bisOtherPackages :: [Expr], 52 | bisPreCommitHooksConfig :: PreCommitHooksConfig, 53 | bisProjectType :: projectType, 54 | bisNativeNixpkgsPackages :: [Expr] 55 | } 56 | 57 | buildInputsBindings :: (HasProjectSuperType t) => BuildInputSpec t -> [Binding] 58 | buildInputsBindings spec@BuildInputSpec {bisNativeNixpkgsPackages} = 59 | catMaybes 60 | [ case buildInputGroupExprs of 61 | [] -> 62 | Just 63 | [nixbinding|buildInputs = [ 64 | # Insert any dependencies that should exist in the dev shell environment here 65 | ];|] 66 | [buildInputGroupExpr1] -> 67 | Just $ [nixproperty|buildInputs|] |= buildInputGroupExpr1 68 | (buildInputGroupExpr1 : otherBuildInputGroupExprs) -> 69 | Just $ [nixproperty|buildInputs|] |= foldr (|++) buildInputGroupExpr1 otherBuildInputGroupExprs, 70 | if null bisNativeNixpkgsPackages 71 | then Nothing 72 | else Just $ [nixproperty|nativeBuildInputs|] |= groupToExpr (BIGNativeNixpkgsInputs bisNativeNixpkgsPackages) 73 | ] 74 | where 75 | buildInputGroups :: [BuildInputGroup] 76 | buildInputGroups = buildInputGroupsFor spec 77 | groupPackageSets :: Bool 78 | groupPackageSets = length buildInputGroups > 1 79 | groupToExpr :: BuildInputGroup -> Expr 80 | groupToExpr g = (if groupPackageSets && requiresGrouping g then EGrouping else id) (toNixExpr g) 81 | buildInputGroupExprs :: [Expr] 82 | buildInputGroupExprs = groupToExpr <$> buildInputGroups 83 | 84 | buildInputGroupsFor :: (HasProjectSuperType t) => BuildInputSpec t -> [BuildInputGroup] 85 | buildInputGroupsFor BuildInputSpec {..} = 86 | catMaybes 87 | [ if null bisNixpkgsPackages then Nothing else Just (BIGNixpkgs bisNixpkgsPackages), 88 | if unPreCommitHooksConfig bisPreCommitHooksConfig then Just BIGPreCommitHooks else Nothing, 89 | if projectSuperType bisProjectType == PSTPython then Just BIGPythonPackages else Nothing, 90 | case bisOtherPackages of 91 | [] -> Nothing 92 | otherPackages -> Just $ BIGOther otherPackages 93 | ] 94 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/FlakeInputs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | -- Description : Manages inputs for bootstrapped flakes 5 | module Bootstrap.Nix.Expr.FlakeInputs (nixpkgsSrcInputBinding) where 6 | 7 | import Bootstrap.Data.Target (Target (TargetDefault)) 8 | import Bootstrap.Nix.Expr 9 | ( Binding, 10 | nix, 11 | nixproperty, 12 | (|=), 13 | ) 14 | 15 | nixpkgsSrcInputBinding :: Target -> Binding 16 | nixpkgsSrcInputBinding target = 17 | [nixproperty|nixpkgs-src.url|] |= case target of 18 | TargetDefault -> [nix|"nixpkgs"|] 19 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/Haskell.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | -- Description : Nix expressions specific to haskell projects 5 | module Bootstrap.Nix.Expr.Haskell (haskellPackagesExpr) where 6 | 7 | import Bootstrap.Data.GHCVersion (ghcVersionProperty) 8 | import Bootstrap.Data.ProjectType 9 | ( HaskellOptions (HaskellOptions, haskellOptionsGHCVersion, haskellOptionsHaskellProjectType), 10 | HaskellProjectType 11 | ( HaskellProjectTypeBasic, 12 | HaskellProjectTypeReplOnly, 13 | HaskellProjectTypeServer 14 | ), 15 | ) 16 | import Bootstrap.Nix.Expr 17 | ( Expr (ESet), 18 | nix, 19 | nixargs, 20 | nixbinding, 21 | nixproperty, 22 | (|*), 23 | (|.), 24 | (|:), 25 | (|=), 26 | ) 27 | 28 | -- | An expression representing the haskell package set bootstrapped. Depends on 29 | -- nixpkgs being in scope. 30 | haskellPackagesExpr :: HaskellOptions -> Expr 31 | haskellPackagesExpr HaskellOptions {..} = 32 | basePackageSet 33 | |. [nixproperty|override|] 34 | |* ESet 35 | False 36 | [ [nixproperty|overrides|] 37 | |= ( [nixargs|_:|] 38 | |: ( [nixargs|super:|] 39 | |: ESet 40 | False 41 | ( ( let overridePrettySimple = 42 | [ [nixbinding|# The override of pretty-simple below may be needed to circumvent a bug in nixpkgs.|], 43 | [nixbinding|# If the devshell builds successfully without it, feel free to remove it.|], 44 | [nixbinding|pretty-simple = super.pretty-simple.overrideAttrs { doCheck = false; };|] 45 | ] 46 | in case haskellOptionsHaskellProjectType of 47 | HaskellProjectTypeReplOnly -> [] 48 | HaskellProjectTypeBasic _ -> overridePrettySimple 49 | HaskellProjectTypeServer _ -> overridePrettySimple 50 | ) 51 | <> [[nixbinding|# You can overide packages here if you need any dependencies not in this set by default|]] 52 | ) 53 | ) 54 | ) 55 | ] 56 | where 57 | basePackageSet :: Expr 58 | basePackageSet = [nix|nixpkgs.haskell.packages|] |. ghcVersionProperty haskellOptionsGHCVersion 59 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/MkShell.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.MkShell (BuildInputSpec (..), mkShell) where 6 | 7 | import Bootstrap.Data.PreCommitHook 8 | ( PreCommitHooksConfig (PreCommitHooksConfig), 9 | ) 10 | import Bootstrap.Data.ProjectType 11 | ( HasProjectSuperType (projectSuperType), 12 | ProjectSuperType (PSTJava, PSTRust), 13 | ProjectType, 14 | jdkPackageName, 15 | ) 16 | import Bootstrap.Nix.Expr 17 | ( Binding, 18 | Expr (ELit, ESet), 19 | Literal (LMultilineString), 20 | nix, 21 | nixbinding, 22 | nixproperty, 23 | (|*), 24 | (|=), 25 | ) 26 | import Bootstrap.Nix.Expr.BuildInputs 27 | ( BuildInputSpec (BuildInputSpec, bisNixpkgsPackages, bisPreCommitHooksConfig, bisProjectType), 28 | buildInputsBindings, 29 | ) 30 | 31 | -- | A nixpkgs.mkShell expression. Expects `nixpkgs` to be in scope. 32 | mkShell :: BuildInputSpec ProjectType -> Expr 33 | mkShell buildInputSpec@BuildInputSpec {bisPreCommitHooksConfig, bisProjectType} = 34 | [nix|nixpkgs.mkShell|] 35 | |* ESet 36 | False 37 | ( buildInputsBindings buildInputSpec 38 | <> toList (shellHookBinding <$> shellHookFor bisPreCommitHooksConfig bisProjectType) 39 | ) 40 | 41 | data ShellHook 42 | = ShellHookFromPreCommit 43 | | ShellHookJava Text 44 | | ShellHookRust 45 | | ShellHookCombined (NonEmpty ShellHook) 46 | 47 | shellHookFor :: PreCommitHooksConfig -> ProjectType -> Maybe ShellHook 48 | shellHookFor pchc pt = case (pchc, projectSuperType pt) of 49 | (PreCommitHooksConfig True, PSTJava) -> Just $ ShellHookCombined (ShellHookJava (jdkPackageName pt) :| [ShellHookFromPreCommit]) 50 | (PreCommitHooksConfig False, PSTJava) -> Just $ ShellHookJava (jdkPackageName pt) 51 | (PreCommitHooksConfig True, PSTRust) -> Just $ ShellHookCombined (ShellHookRust :| [ShellHookFromPreCommit]) 52 | (PreCommitHooksConfig False, PSTRust) -> Just ShellHookRust 53 | (PreCommitHooksConfig True, _) -> Just ShellHookFromPreCommit 54 | _ -> Nothing 55 | 56 | shellHookBinding :: ShellHook -> Binding 57 | shellHookBinding = \case 58 | ShellHookFromPreCommit -> [nixbinding|inherit (preCommitHooks.allHooks) shellHook;|] 59 | x -> withComponents $ shellHookComponentBinding x 60 | where 61 | withComponents :: [Text] -> Binding 62 | withComponents xs = 63 | [nixproperty|shellHook|] 64 | |= ELit 65 | (LMultilineString $ mconcat (("\n " <>) <$> xs) <> "\n ") 66 | shellHookComponentBinding :: ShellHook -> [Text] 67 | shellHookComponentBinding = \case 68 | ShellHookFromPreCommit -> ["${preCommitHooks.allHooks.shellHook}"] 69 | ShellHookJava jdk -> ["export JAVA_HOME=\"${nixpkgs."] ++ [jdk] ++ ["}\""] 70 | ShellHookRust -> ["export RUST_SRC_PATH=${nixpkgs.rustPlatform.rustLibSrc}"] 71 | ShellHookCombined xs -> sconcat $ shellHookComponentBinding <$> xs 72 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/Nixpkgs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.Nixpkgs (nixpkgsBinding, nixpkgsExpr) where 6 | 7 | import Bootstrap.Nix.Expr (Binding, Expr, nix, nixproperty, (|=)) 8 | 9 | -- | nixpkgs = `nixpkgsExpr`; 10 | nixpkgsBinding :: Binding 11 | nixpkgsBinding = [nixproperty|nixpkgs|] |= nixpkgsExpr 12 | 13 | -- | An expression which imports nixpkgs from the flake.lock generated by the intermediate flake 14 | nixpkgsExpr :: Expr 15 | nixpkgsExpr = 16 | [nix| 17 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); 18 | in import (builtins.fetchTarball { 19 | url = "https://github.com/NixOS/nixpkgs/tarball/${lock.nodes.nixpkgs-src.locked.rev}"; 20 | sha256 = lock.nodes.nixpkgs-src.locked.narHash; 21 | }) {}|] 22 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/PreCommitHooks.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.PreCommitHooks (ImportPreCommitHooksArgs (..), importPreCommitHooks) where 6 | 7 | import Bootstrap.Nix.Expr (Binding (BInherit), Expr (ESet), nix, nixident, (|*)) 8 | 9 | data ImportPreCommitHooksArgs = ImportPreCommitHooksArgs 10 | { passNixpkgsThrough :: Bool, 11 | passSystemThrough :: Bool 12 | } 13 | 14 | -- | An expression which imports pre-commit hooks, assuming they're at nix/pre-commit-hooks.nix. 15 | -- 16 | -- Passes pre-commit-hooks-lib and optionally nixpkgs and/or sustem through as arguments to the pre-commit hooks 17 | -- config - these must be in scope. 18 | importPreCommitHooks :: ImportPreCommitHooksArgs -> Expr 19 | importPreCommitHooks ImportPreCommitHooksArgs {..} = 20 | [nix|import nix/pre-commit-hooks.nix|] 21 | |* ESet 22 | False 23 | [ BInherit 24 | ( [nixident|pre-commit-hooks-lib|] 25 | :| [[nixident|nixpkgs|] | passNixpkgsThrough] 26 | <> [[nixident|system|] | passSystemThrough] 27 | ) 28 | ] 29 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/Python.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.Python (machNixLegacyNixBinding, machNixFlakeInput, pythonPackagesBinding) where 6 | 7 | import Bootstrap.Nix.Expr 8 | ( Binding, 9 | Expr (EGrouping, ELit, ESet), 10 | Literal (LString), 11 | nix, 12 | nixbinding, 13 | nixproperty, 14 | (|*), 15 | (|=), 16 | ) 17 | 18 | machNixVersion :: Text 19 | machNixVersion = "3.5.0" 20 | 21 | -- | A binding which pulls mach-nix using builtins.fetchGit 22 | machNixLegacyNixBinding :: Binding 23 | machNixLegacyNixBinding = 24 | [nixproperty|mach-nix|] 25 | |= [nix|import|] 26 | |* EGrouping 27 | ( [nix|builtins.fetchGit|] 28 | |* ESet 29 | False 30 | [ [nixbinding|url = "https://github.com/DavHau/mach-nix";|], 31 | [nixproperty|ref|] |= ELit (LString $ "refs/tags/" <> machNixVersion) 32 | ] 33 | ) 34 | |* [nix|{}|] 35 | 36 | -- | A binding to be used as an input in a flake to pull mach-nix 37 | machNixFlakeInput :: Binding 38 | machNixFlakeInput = 39 | [nixproperty|mach-nix.url|] 40 | |= ELit (LString $ "github:DavHau/mach-nix?ref=" <> machNixVersion) 41 | 42 | -- | A binding which provides python packages based on requirements.txt. 43 | -- 44 | -- It expects mach-nix any system to be in scope. 45 | pythonPackagesBinding :: 46 | Binding 47 | pythonPackagesBinding = 48 | [nixbinding|pythonPackages = mach-nix.lib.${system}.mkPython (rec { 49 | requirements = builtins.readFile ./requirements.txt; 50 | python = "python39"; 51 | });|] 52 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/ReproducibleBuild.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | -- Description : Definitions for reproducible build expressions 5 | module Bootstrap.Nix.Expr.ReproducibleBuild 6 | ( ReproducibleBuildExpr (..), 7 | ReproducibleBuildRequirement (..), 8 | reproducibleBuildRequirementIdentifier, 9 | sortRbeRequirements, 10 | ) 11 | where 12 | 13 | import Bootstrap.Nix.Expr (Expr, Identifier, nixident) 14 | import qualified Data.List.NonEmpty as NE 15 | 16 | -- | A representation of an identifier the reproducible build expression expects to be in scope 17 | data ReproducibleBuildRequirement 18 | = RBRHaskellPackages 19 | | RBRNixpkgs 20 | deriving stock (Eq, Show) 21 | 22 | reproducibleBuildRequirementIdentifier :: ReproducibleBuildRequirement -> Identifier 23 | reproducibleBuildRequirementIdentifier = \case 24 | RBRHaskellPackages -> [nixident|haskellPackages|] 25 | RBRNixpkgs -> [nixident|nixpkgs|] 26 | 27 | -- | An expression defining the reproducible build for some software 28 | data ReproducibleBuildExpr = ReproducibleBuildExpr 29 | { rbeExpr :: Expr, 30 | -- | What identifiers the expression expects to be in scope 31 | rbeRequirements :: NonEmpty ReproducibleBuildRequirement 32 | } 33 | deriving stock (Eq, Show) 34 | 35 | -- | Ensures requirements with dependencies on each other are properly ordered in a let-in style, 36 | -- such that the dependencies are defined before the things that depend on them. 37 | sortRbeRequirements :: NonEmpty ReproducibleBuildRequirement -> NonEmpty ReproducibleBuildRequirement 38 | sortRbeRequirements = NE.sortBy f 39 | where 40 | f RBRNixpkgs _ = LT 41 | f _ RBRNixpkgs = GT 42 | f _ _ = EQ 43 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/ReproducibleBuild/Go.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.ReproducibleBuild.Go (reproducibleGoBuild) where 6 | 7 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 8 | import Bootstrap.Nix.Expr 9 | ( Binding (BLineComment), 10 | CommentsPolicy (ShowComments), 11 | Expr (ELit, ESet), 12 | Literal (LString), 13 | nix, 14 | nixbinding, 15 | nixproperty, 16 | writeBinding, 17 | (|*), 18 | (|=), 19 | ) 20 | import Bootstrap.Nix.Expr.ReproducibleBuild 21 | ( ReproducibleBuildExpr (ReproducibleBuildExpr), 22 | ReproducibleBuildRequirement (RBRNixpkgs), 23 | ) 24 | 25 | -- | Produces a reproducible build for a Golang project. 26 | reproducibleGoBuild :: ProjectName -> ReproducibleBuildExpr 27 | reproducibleGoBuild projectName = 28 | ReproducibleBuildExpr 29 | ( [nix|nixpkgs.buildGoModule|] 30 | |* ESet 31 | False 32 | [ [nixproperty|pname|] |= ELit (LString $ unProjectName projectName), 33 | [nixbinding|version = "0.1.0";|], 34 | [nixbinding|src = ./.;|], 35 | [nixbinding|vendorSha256 = null;|], 36 | [nixbinding|# Swap out the line above for the one below once you start adding dependencies. 37 | |], 38 | [nixbinding|# After your dependencies change, builds will fail until you update the hash below. 39 | |], 40 | [nixbinding|# When the build fails, it will tell you what the expected hash is. 41 | |], 42 | BLineComment (writeBinding ShowComments [nixbinding|vendorSha256 = "sha256-00000000000000000000000000000000000000000000";|]) 43 | ] 44 | ) 45 | (one RBRNixpkgs) 46 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/ReproducibleBuild/Haskell.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.Expr.ReproducibleBuild.Haskell (reproducibleHaskellBuild) where 5 | 6 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 7 | import Bootstrap.Nix.Expr 8 | ( Expr (EIdent, ELetIn, ELit, ESet), 9 | Literal (LMultilineString, LString), 10 | nix, 11 | nixbinding, 12 | nixproperty, 13 | (|*), 14 | (|.), 15 | (|=), 16 | ) 17 | import Bootstrap.Nix.Expr.ReproducibleBuild 18 | ( ReproducibleBuildExpr (ReproducibleBuildExpr), 19 | ReproducibleBuildRequirement (RBRHaskellPackages, RBRNixpkgs), 20 | reproducibleBuildRequirementIdentifier, 21 | ) 22 | import Text.RawString.QQ (r) 23 | 24 | reproducibleHaskellBuild :: 25 | ProjectName -> 26 | -- | src path 27 | Expr -> 28 | ReproducibleBuildExpr 29 | reproducibleHaskellBuild projectName srcDir = 30 | ReproducibleBuildExpr 31 | ( ELetIn 32 | ( [nixbinding|# This is the core build of your program, but its closure includes all build inputs|] 33 | :| [ [nixproperty|unstripped|] 34 | |= ( (EIdent (reproducibleBuildRequirementIdentifier RBRHaskellPackages) |. [nixproperty|callCabal2nix|]) 35 | |* ELit (LString $ unProjectName projectName) 36 | |* srcDir 37 | |* ESet False [] 38 | ) 39 | ] 40 | ) 41 | ( [nix|nixpkgs.stdenv.mkDerivation|] 42 | |* ESet 43 | False 44 | [ [nixbinding|# This derivation strips out the build inputs from `unstripped` above to leave just your program|], 45 | [nixbinding|inherit (unstripped) name src version;|], 46 | [nixbinding|buildInputs = with nixpkgs; [ 47 | # This is an assumed set of system libraries needed; you can add to this as necessary. 48 | # You can find out what your package needs by reading the output of `ldd` on your built binary. 49 | glibc 50 | gmp 51 | libffi 52 | # ncurses and zlib are not needed for the bootstrapped program, but are needed by lots of common Haskell libraries 53 | ncurses 54 | zlib 55 | ];|], 56 | [nixproperty|installPhase|] 57 | |= ( ELit . LMultilineString $ 58 | [r| 59 | mkdir -p $out/bin 60 | cp ${(nixpkgs.haskell.lib.enableSeparateBinOutput unstripped).bin}/bin/app $out/bin/|] 61 | <> unProjectName projectName 62 | ) 63 | ] 64 | ) 65 | ) 66 | (RBRHaskellPackages :| [RBRNixpkgs]) 67 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/ReproducibleBuild/Java.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Nix.Expr.ReproducibleBuild.Java (reproducibleJavaBuild) where 6 | 7 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 8 | import Bootstrap.Data.ProjectType (ArtefactId (unArtefactId), JdkPackage (GraalVM, OpenJDK)) 9 | import Bootstrap.Nix.Expr 10 | ( Expr (ELetIn, EList, ELit), 11 | Literal (LString), 12 | nix, 13 | nixbinding, 14 | nixproperty, 15 | (|=), 16 | ) 17 | import Bootstrap.Nix.Expr.ReproducibleBuild 18 | ( ReproducibleBuildExpr (ReproducibleBuildExpr), 19 | ReproducibleBuildRequirement (RBRNixpkgs), 20 | ) 21 | 22 | reproducibleJavaBuild :: ProjectName -> ArtefactId -> JdkPackage -> ReproducibleBuildExpr 23 | reproducibleJavaBuild projectName artefactId jdk = 24 | ReproducibleBuildExpr 25 | ( ELetIn 26 | ( ([nixproperty|projectName|] |= ELit (LString $ unProjectName projectName)) 27 | :| [ [nixproperty|artefactId|] |= ELit (LString $ unArtefactId artefactId), 28 | [nixproperty|buildInputs|] |= EList (mvnOverride jdk), 29 | [nixbinding| 30 | repository = nixpkgs.stdenv.mkDerivation { 31 | inherit buildInputs; 32 | name = "${projectName}-repository"; 33 | src = ./.; 34 | buildPhase = "mvn package -Dmaven.repo.local=$out"; 35 | # keep only *.{pom,jar,sha1,nbm} and delete all ephemeral files with lastModified timestamps inside 36 | installPhase = '' 37 | find $out -type f \\ 38 | -name \\*.lastUpdated -or \\ 39 | -name resolver-status.properties -or \\ 40 | -name _remote.repositories \\ 41 | -delete 42 | ''; 43 | dontFixup = true; 44 | outputHashAlgo = "sha256"; 45 | outputHashMode = "recursive"; 46 | # This hash will need updating when changing your dependencies; the correct 47 | # hash will be displayed when the build fails if so. 48 | outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 49 | };|], 50 | [nixbinding| 51 | builtJar = nixpkgs.stdenv.mkDerivation rec { 52 | inherit buildInputs; 53 | pname = projectName; 54 | version = "0.0.1-SNAPSHOT"; 55 | src = ./.; 56 | buildPhase = '' 57 | echo "Using repository ${repository}" 58 | mvn --offline -Dmaven.repo.local=${repository} package; 59 | ''; 60 | installPhase = '' 61 | install -Dm644 target/${artefactId}-${version}.jar $out/${projectName} 62 | ''; 63 | };|], 64 | [nixbinding| 65 | fromImage = nixpkgs.dockerTools.pullImage { 66 | imageName = "eclipse-temurin"; 67 | imageDigest = "sha256:4cc7dfdfb7837f35c3820bcfbc5f666521364e2198960322848ab7d3e2ca3e88"; 68 | finalImageName = "eclipse-temurin"; 69 | finalImageTag = "17.0.4.1_1-jre"; 70 | sha256 = "sha256-OIib6PQJc1xX+Xu2xtaFEo/jZtxypkg8Y9RFMcJf39w="; 71 | };|] 72 | ] 73 | ) 74 | [nix| 75 | nixpkgs.dockerTools.buildLayeredImage { 76 | inherit fromImage; 77 | name = projectName; 78 | enableFakechroot = true; 79 | fakeRootCommands = '' 80 | cp ${builtJar}/${projectName} /${projectName} 81 | chown root:root /${projectName} 82 | chmod 774 /${projectName} 83 | ''; 84 | config = { 85 | Entrypoint = ["/bin/sh" "-c" "java $JAVA_OPTS -jar /${projectName}"]; 86 | }; 87 | }|] 88 | ) 89 | (one RBRNixpkgs) 90 | 91 | mvnOverride :: JdkPackage -> [Expr] 92 | mvnOverride = \case 93 | OpenJDK -> [[nix|nixpkgs.maven|]] 94 | GraalVM -> [[nix|(nixpkgs.maven.override { jdk = nixpkgs.graalvm-ce; })|]] 95 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Expr/ReproducibleBuild/Rust.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.Expr.ReproducibleBuild.Rust (reproducibleRustBuild) where 5 | 6 | import Bootstrap.Nix.Expr 7 | ( Expr (ELetIn), 8 | nix, 9 | nixbinding, 10 | nixproperty, 11 | (|=), 12 | ) 13 | import Bootstrap.Nix.Expr.ReproducibleBuild 14 | ( ReproducibleBuildExpr (ReproducibleBuildExpr), 15 | ReproducibleBuildRequirement (RBRNixpkgs), 16 | ) 17 | 18 | reproducibleRustBuild :: 19 | -- | src path 20 | Expr -> 21 | ReproducibleBuildExpr 22 | reproducibleRustBuild srcDir = 23 | ReproducibleBuildExpr 24 | ( ELetIn 25 | ( ([nixproperty|src|] |= srcDir) 26 | :| [[nixbinding|cargoToml = builtins.fromTOML (builtins.readFile (src + "/Cargo.toml"));|]] 27 | ) 28 | [nix|nixpkgs.rustPlatform.buildRustPackage { 29 | inherit src; 30 | inherit (cargoToml.package) name version; 31 | cargoLock.lockFile = src + "/Cargo.lock"; 32 | }|] 33 | ) 34 | (one RBRNixpkgs) 35 | -------------------------------------------------------------------------------- /src/Bootstrap/Nix/Flake.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE TemplateHaskellQuotes #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | 5 | -- | Copyright : (c) Crown Copyright GCHQ 6 | module Bootstrap.Nix.Flake (generateIntermediateFlake, intermediateFlake) where 7 | 8 | import Bootstrap.Cli (RunConfig (RunConfig, rcNonInteractive)) 9 | import Bootstrap.Data.ProjectName (ProjectName (unProjectName)) 10 | import Bootstrap.Data.Target (Target) 11 | import Bootstrap.Error 12 | ( CanDieOnError (dieOnError'), 13 | InProgressDuration (LongRunning), 14 | runWithProgressMsg, 15 | ) 16 | import Bootstrap.GitPod (resetPermissionsInGitPod) 17 | import Bootstrap.Monad (MonadBootstrap) 18 | import Bootstrap.Nix.Evaluate (NixBinaryPaths, runNix) 19 | import Bootstrap.Nix.Expr 20 | ( CommentsPolicy (ShowComments), 21 | Expr (ELit, ESet), 22 | Literal (LString), 23 | nixbinding, 24 | nixproperty, 25 | writeExpr, 26 | (|=), 27 | ) 28 | import Bootstrap.Nix.Expr.FlakeInputs (nixpkgsSrcInputBinding) 29 | import Bootstrap.Terminal (promptYesNoWithDefault) 30 | import Bootstrap.Unix (git) 31 | import Control.Exception (IOException) 32 | import Control.Monad.Catch (MonadCatch, try) 33 | import System.Terminal (MonadPrinter (putTextLn)) 34 | 35 | generateIntermediateFlake :: (MonadBootstrap m) => NixBinaryPaths -> RunConfig -> ProjectName -> Target -> m () 36 | generateIntermediateFlake nixBinaryPaths RunConfig {rcNonInteractive} projectName target = 37 | promptYesNoWithDefault 38 | (if rcNonInteractive then Just True else Nothing) 39 | "First, I need to pin a version of nixpkgs in flake.nix. Is that okay?" 40 | >>= \case 41 | True -> do 42 | resetPermissionsInGitPod 43 | void 44 | . dieOnError' 45 | . ExceptT 46 | . runWithProgressMsg LongRunning "Pinning a version of nixpkgs" 47 | $ do 48 | writeIntermediateFlake projectName target 49 | void . ExceptT $ git ["add", "--intent-to-add", "flake.nix"] 50 | void . ExceptT $ runNix nixBinaryPaths ["flake", "lock"] 51 | False -> putTextLn "Okay, exiting." *> exitFailure 52 | 53 | writeIntermediateFlake :: (MonadCatch m, MonadIO m) => ProjectName -> Target -> ExceptT IOException m () 54 | writeIntermediateFlake projectName target = 55 | void 56 | . try @_ @IOException 57 | . writeFileText "flake.nix" 58 | . (<> "\n") 59 | . writeExpr ShowComments 60 | $ intermediateFlake projectName target 61 | 62 | intermediateFlake :: ProjectName -> Target -> Expr 63 | intermediateFlake projectName target = 64 | ESet 65 | False 66 | [ [nixproperty|description|] |= ELit (LString ("Development infrastructure for " <> unProjectName projectName)), 67 | [nixproperty|inputs|] |= ESet False [nixpkgsSrcInputBinding target], 68 | [nixbinding|outputs = _: {};|] 69 | ] 70 | -------------------------------------------------------------------------------- /src/Bootstrap/State.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.State 3 | ( CursorPos, 4 | InputLine (..), 5 | blankInputLine, 6 | TextInputState (..), 7 | initialTextInputState, 8 | updateCursorPos, 9 | handleCharEntry, 10 | handleBackspacePress, 11 | ChoiceInputState (..), 12 | MultipleChoiceInputState (..), 13 | initialMultipleChoiceInputState, 14 | -- internal; exported for testing 15 | insertIntoInputLine, 16 | backspaceInputLine, 17 | inputLineLength, 18 | ) 19 | where 20 | 21 | import qualified Data.Set as Set 22 | import qualified Data.Text as T 23 | 24 | type CursorPos = Int 25 | 26 | newtype InputLine = InputLine {unInputLine :: Text} deriving stock (Eq, Show) 27 | 28 | blankInputLine :: InputLine 29 | blankInputLine = InputLine "" 30 | 31 | insertIntoInputLine :: InputLine -> CursorPos -> Char -> InputLine 32 | insertIntoInputLine (InputLine t) pos c = InputLine $ T.take pos t <> one c <> T.drop pos t 33 | 34 | backspaceInputLine :: InputLine -> CursorPos -> InputLine 35 | backspaceInputLine (InputLine t) pos = InputLine $ T.take (pos - 1) t <> T.drop pos t 36 | 37 | inputLineLength :: InputLine -> CursorPos 38 | inputLineLength = T.length . unInputLine 39 | 40 | data TextInputState = TextInputState 41 | { cursorPos :: CursorPos, 42 | currentLine :: InputLine 43 | } 44 | deriving stock (Eq, Show) 45 | 46 | initialTextInputState :: TextInputState 47 | initialTextInputState = TextInputState 0 blankInputLine 48 | 49 | updateCursorPos :: TextInputState -> (CursorPos -> CursorPos) -> TextInputState 50 | updateCursorPos s f = s {cursorPos = clamp 0 (inputLineLength $ currentLine s) (f $ cursorPos s)} 51 | 52 | handleCharEntry :: TextInputState -> Char -> TextInputState 53 | handleCharEntry s@TextInputState {cursorPos, currentLine} c = 54 | s 55 | { cursorPos = cursorPos + 1, 56 | currentLine = insertIntoInputLine currentLine cursorPos c 57 | } 58 | 59 | handleBackspacePress :: TextInputState -> TextInputState 60 | handleBackspacePress s@TextInputState {cursorPos, currentLine} = 61 | if cursorPos == 0 62 | then s 63 | else 64 | s 65 | { cursorPos = cursorPos - 1, 66 | currentLine = backspaceInputLine currentLine cursorPos 67 | } 68 | 69 | newtype ChoiceInputState a = ChoiceInputState {chosenItem :: a} 70 | 71 | data MultipleChoiceInputState a = MultipleChoiceInputState {chosenItems :: Set a, cursorItem :: a} 72 | 73 | initialMultipleChoiceInputState :: (Bounded a) => MultipleChoiceInputState a 74 | initialMultipleChoiceInputState = MultipleChoiceInputState Set.empty minBound 75 | -------------------------------------------------------------------------------- /src/Bootstrap/Terminal/Icon.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Terminal.Icon (icon) where 3 | 4 | icon :: [Text] 5 | icon = 6 | [ " #######", 7 | "##########", 8 | "[=] #######", 9 | " | #######", 10 | "[=] #######", 11 | " | #######", 12 | "[=] ########", 13 | " | #######", 14 | "[=] ########", 15 | " | ##########", 16 | "-+=+ #############", 17 | " '|| ####### ########", 18 | " `| ####### ,#######", 19 | "`==` ######## ########", 20 | " ######## #######", 21 | " Welcome to nix-bootstrap!" 22 | ] 23 | -------------------------------------------------------------------------------- /src/Bootstrap/Unix.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | -- | Description : Defines utilities for working with command-line tools 4 | -- Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Unix (alejandra, git, runCommand, which, whoami) where 6 | 7 | import Bootstrap.Monad (MonadBootstrap) 8 | import Bootstrap.Terminal (putErrorLn) 9 | import Control.Exception (IOException) 10 | import Control.Monad.Catch (try) 11 | import qualified Data.Text as T 12 | import GHC.IO.Exception (userError) 13 | import GHC.IO.Handle (hGetContents) 14 | import System.IO.Silently (hSilence) 15 | import System.Process 16 | ( CreateProcess (std_err, std_in, std_out), 17 | StdStream (CreatePipe, UseHandle), 18 | createProcess, 19 | proc, 20 | readProcess, 21 | ) 22 | import qualified System.Which as Which 23 | 24 | -- | Finds and runs git, returning the result of the command with the given args. 25 | -- 26 | -- Shows an error message and exits with failure status (diverges) 27 | -- if git is not available on the system. 28 | git :: (MonadBootstrap m) => [String] -> m (Either IOException String) 29 | git args = 30 | which "git" >>= \case 31 | Just g -> runCommand g args 32 | Nothing -> do 33 | putErrorLn "Git does not appear to be installed. Please install it and then re-run nix-bootstrap." 34 | exitFailure 35 | 36 | -- | Runs alejandra against the given nix expression 37 | alejandra :: (MonadIO m) => String -> ExceptT IOException m String 38 | alejandra expr = do 39 | -- hExpr is the pipe from printf with the correctly sanitised (for terminal use) 40 | -- expression in it 41 | (_, hExpr, _, _) <- 42 | ExceptT 43 | . liftIO 44 | . try 45 | $ createProcess (proc "printf" ["%s", expr]) {std_out = CreatePipe} 46 | case hExpr of 47 | Just hExpr' -> do 48 | -- hFormatted is stdout from alejandra 49 | (_, hFormatted, hError, _) <- 50 | ExceptT 51 | . liftIO 52 | . try 53 | $ createProcess 54 | (proc $(Which.staticWhich "alejandra") []) 55 | { std_err = CreatePipe, 56 | std_in = UseHandle hExpr', 57 | std_out = CreatePipe 58 | } 59 | case hFormatted of 60 | Just h -> do 61 | formatted <- ExceptT . liftIO . try $ hGetContents h 62 | if not (null formatted) 63 | then pure formatted 64 | else case hError of 65 | Just hErr -> ExceptT (Left . userError <$> liftIO (hGetContents hErr)) 66 | Nothing -> hoistEither (Left $ userError "hError was Nothing; should not happen") 67 | Nothing -> hoistEither (Left $ userError "hFormatted was Nothing; should not happen") 68 | Nothing -> hoistEither (Left $ userError "hExpr was Nothing; should not happen") 69 | 70 | -- | Gets the path to the requested executable on the system using `which` 71 | which :: (MonadIO m) => FilePath -> m (Maybe FilePath) 72 | which = liftIO . Which.which 73 | 74 | -- | Gets the name of the current user using `whoami` 75 | whoami :: (MonadIO m) => m (Either IOException FilePath) 76 | whoami = (toString . T.strip . toText) <<$>> runCommand "whoami" [] 77 | 78 | -- | Runs the given command, silencing (and capturing) output 79 | runCommand :: (MonadIO m) => FilePath -> [String] -> m (Either IOException String) 80 | runCommand binaryPath args = 81 | liftIO 82 | . try 83 | . hSilence [stdout, stderr] 84 | $ readProcess binaryPath args "" 85 | -------------------------------------------------------------------------------- /src/Prelude.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-missing-import-lists #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Prelude (module Relude, clamp) where 5 | 6 | import Relude hiding (putText, putTextLn, state) 7 | 8 | -- | Given a minimum value and a maximum value, clamp a value to that range 9 | -- (values less than the minimum map to the minimum and values greater than 10 | -- the maximum map to the maximum). 11 | -- 12 | -- >>> clamp 1 10 11 13 | -- 10 14 | -- >>> clamp 1 10 2 15 | -- 2 16 | -- >>> clamp 5 10 1 17 | -- 5 18 | clamp :: 19 | (Ord a) => 20 | -- | The minimum value 21 | a -> 22 | -- | The maximum value 23 | a -> 24 | -- | The value to clamp 25 | a -> 26 | a 27 | clamp mn mx val = max mn (min val mx) 28 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/BootstrapStateSpec.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-deprecations #-} 2 | {-# OPTIONS_GHC -Wno-orphans #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Data.Bootstrappable.BootstrapStateSpec (spec) where 6 | 7 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 8 | import Bootstrap.Data.Bootstrappable.BootstrapState (BootstrapState, bootstrapStateCodec, bootstrapStateFor) 9 | import Bootstrap.Data.ContinuousIntegration (ContinuousIntegrationConfig (ContinuousIntegrationConfig)) 10 | import Bootstrap.Data.DevContainer (DevContainerConfig (DevContainerConfig)) 11 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig (PreCommitHooksConfig)) 12 | import Bootstrap.Data.ProjectName (mkProjectName) 13 | import Bootstrap.Data.ProjectType 14 | ( JavaOptionsV2 (JavaOptionsV2), 15 | ProjectTypeV2 16 | ( PTV2Go, 17 | PTV2Java, 18 | PTV2Minimal, 19 | PTV2Node, 20 | PTV2Python 21 | ), 22 | ) 23 | import Bootstrap.Data.ProjectTypeSpec () 24 | import Data.Version (showVersion) 25 | import Paths_nix_bootstrap (version) 26 | import qualified Relude.Unsafe as Unsafe 27 | import Test.Hspec (Spec, describe, it) 28 | import Test.Hspec.Expectations.Pretty (shouldBe) 29 | import Test.Hspec.QuickCheck (prop) 30 | import Test.QuickCheck 31 | ( Arbitrary (arbitrary), 32 | arbitraryBoundedEnum, 33 | generate, 34 | oneof, 35 | ) 36 | import Test.Util (tomlRoundtripTest) 37 | 38 | instance Arbitrary BootstrapState where 39 | arbitrary = do 40 | let projectName = Unsafe.fromJust $ mkProjectName "test-project" 41 | projectType <- arbitrary 42 | preCommitHooksConfig <- PreCommitHooksConfig <$> arbitrary 43 | devContainerConfig <- DevContainerConfig <$> arbitrary 44 | ciConfig <- ContinuousIntegrationConfig <$> arbitrary 45 | bootstrapStateFor projectName projectType preCommitHooksConfig ciConfig devContainerConfig <$> arbitrary 46 | 47 | instance Arbitrary ProjectTypeV2 where 48 | arbitrary = 49 | oneof 50 | [ pure PTV2Minimal, 51 | PTV2Node <$> arbitraryBoundedEnum, 52 | PTV2Go <$> arbitraryBoundedEnum, 53 | PTV2Java <$> (JavaOptionsV2 <$> arbitraryBoundedEnum <*> arbitraryBoundedEnum <*> arbitrary), 54 | PTV2Python <$> arbitraryBoundedEnum 55 | ] 56 | 57 | spec :: Spec 58 | spec = describe ".nix-bootstrap.toml rendering" do 59 | it "renders correctly" do 60 | let projectName = Unsafe.fromJust $ mkProjectName "test-project" 61 | nodePackageManager <- generate Test.QuickCheck.arbitraryBoundedEnum 62 | bootstrapContent 63 | ( bootstrapStateFor 64 | projectName 65 | (PTV2Node nodePackageManager) 66 | (PreCommitHooksConfig True) 67 | (ContinuousIntegrationConfig True) 68 | (DevContainerConfig True) 69 | False 70 | ) 71 | >>= ( `shouldBe` 72 | Right 73 | ( unlines 74 | [ "# This file was generated by nix-bootstrap.", 75 | "# It should be checked into version control.", 76 | "# It is used to aid migration between nix-bootstrap versions and preserve idempotence.", 77 | "", 78 | "projectName = \"test-project\"", 79 | "setUpContinuousIntegration = true", 80 | "setUpDevContainer = true", 81 | "setUpPreCommitHooks = true", 82 | "useFlakes = false", 83 | "version = \"" <> toText (showVersion version) <> "\"", 84 | "", 85 | "[projectType]", 86 | " nodePackageManager = \"" <> show nodePackageManager <> "\"", 87 | " projectSuperType = \"PSTNode\"" 88 | ] 89 | ) 90 | ) 91 | prop "roundtrips" $ tomlRoundtripTest bootstrapStateCodec 92 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/ElmJsonSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.ElmJsonSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.ElmJson (elmJsonFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeBare), 10 | ElmOptions (ElmOptions), 11 | ProjectType (Elm), 12 | ) 13 | import Test.Hspec (Spec, describe, it) 14 | import Test.Hspec.Expectations.Pretty (shouldBe) 15 | import Text.RawString.QQ (r) 16 | 17 | spec :: Spec 18 | spec = describe "elm.json rendering" do 19 | it "renders the elm.json correctly" do 20 | bootstrapContent (elmJsonFor . Elm $ ElmOptions ElmModeBare True) 21 | >>= ( `shouldBe` 22 | Right 23 | [r|{ 24 | "type": "application", 25 | "source-directories": [ 26 | "src" 27 | ], 28 | "elm-version": "0.19.1", 29 | "dependencies": { 30 | "direct": { 31 | "elm/browser": "1.0.2", 32 | "elm/core": "1.0.5", 33 | "elm/html": "1.0.0" 34 | }, 35 | "indirect": { 36 | "elm/json": "1.1.3", 37 | "elm/time": "1.0.0", 38 | "elm/url": "1.0.0", 39 | "elm/virtual-dom": "1.0.3" 40 | } 41 | }, 42 | "test-dependencies": { 43 | "direct": {}, 44 | "indirect": {} 45 | } 46 | } 47 | |] 48 | ) 49 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/IndexHtmlSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.IndexHtmlSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.IndexHtml 8 | ( elmIndexHtmlFor, 9 | ) 10 | import Bootstrap.Data.ProjectName (mkProjectName) 11 | import Bootstrap.Data.ProjectType 12 | ( ElmMode (ElmModeNode), 13 | ElmOptions (ElmOptions), 14 | NodePackageManager (NPM), 15 | ProjectType (Elm), 16 | ) 17 | import qualified Relude.Unsafe as Unsafe 18 | import Test.Hspec (Spec, describe, it) 19 | import Test.Hspec.Expectations.Pretty (shouldBe) 20 | import Text.RawString.QQ (r) 21 | 22 | spec :: Spec 23 | spec = describe "index.html rendering" do 24 | it "renders the index.html correctly" do 25 | bootstrapContent 26 | ( elmIndexHtmlFor (Unsafe.fromJust $ mkProjectName "test-project") 27 | . Elm 28 | $ ElmOptions (ElmModeNode NPM) True 29 | ) 30 | >>= ( `shouldBe` 31 | Right 32 | [r| 33 | 34 | 35 | 36 | 37 | 38 | test-project 39 | 40 | 42 | 43 | 44 |
45 |
46 | 47 | 48 | |] 49 | ) 50 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/IndexJsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.IndexJsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.IndexJs (elmIndexJsFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeNode), 10 | ElmOptions (ElmOptions), 11 | NodePackageManager (NPM), 12 | ProjectType (Elm), 13 | ) 14 | import Test.Hspec (Spec, describe, it) 15 | import Test.Hspec.Expectations.Pretty (shouldBe) 16 | import Text.RawString.QQ (r) 17 | 18 | spec :: Spec 19 | spec = describe "index.js rendering" do 20 | it "renders the index.js correctly" do 21 | bootstrapContent (elmIndexJsFor . Elm $ ElmOptions (ElmModeNode NPM) True) 22 | >>= ( `shouldBe` 23 | Right 24 | [r|import { Elm } from "./Main.elm"; 25 | 26 | Elm.Main.init({ 27 | node: document.getElementById("root"), 28 | }); 29 | |] 30 | ) 31 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/MainElmSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.MainElmSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.MainElm (mainElmFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeBare), 10 | ElmOptions (ElmOptions), 11 | ProjectType (Elm), 12 | ) 13 | import Test.Hspec (Spec, describe, it) 14 | import Test.Hspec.Expectations.Pretty (shouldBe) 15 | import Text.RawString.QQ (r) 16 | 17 | spec :: Spec 18 | spec = describe "Main.elm rendering" do 19 | it "renders the Main.elm correctly" do 20 | bootstrapContent (mainElmFor . Elm $ ElmOptions ElmModeBare True) 21 | >>= ( `shouldBe` 22 | Right 23 | [r|module Main exposing (Model, Msg, main) 24 | 25 | import Browser 26 | import Html exposing (Html, button, text) 27 | import Html.Events exposing (onClick) 28 | 29 | 30 | main : Program () Model Msg 31 | main = 32 | Browser.sandbox 33 | { init = init 34 | , update = update 35 | , view = view 36 | } 37 | 38 | 39 | type alias Model = 40 | () 41 | 42 | 43 | init : Model 44 | init = 45 | () 46 | 47 | 48 | type Msg 49 | = NoOp 50 | 51 | 52 | update : Msg -> Model -> Model 53 | update msg model = 54 | case msg of 55 | NoOp -> 56 | model 57 | 58 | 59 | view : Model -> Html Msg 60 | view () = 61 | button [ onClick NoOp ] [ text "Hello, world!" ] 62 | |] 63 | ) 64 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/PackageJsonSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.PackageJsonSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.PackageJson (elmPackageJsonFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeNode), 10 | ElmOptions (ElmOptions), 11 | NodePackageManager (NPM), 12 | ProjectType (Elm), 13 | ) 14 | import Test.Hspec (Spec, describe, it) 15 | import Test.Hspec.Expectations.Pretty (shouldBe) 16 | import Text.RawString.QQ (r) 17 | 18 | spec :: Spec 19 | spec = describe "package.json rendering" do 20 | it "renders the package.json correctly" do 21 | bootstrapContent (elmPackageJsonFor . Elm $ ElmOptions (ElmModeNode NPM) True) 22 | >>= ( `shouldBe` 23 | Right 24 | [r|{ 25 | "dependencies": { 26 | "buffer": "^5.5.0", 27 | "process": "^0.11.10", 28 | "punycode": "^1.4.1", 29 | "querystring-es3": "^0.2.1", 30 | "url": "^0.11.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": ">=7.13.0 <8.0.0", 34 | "@babel/preset-env": "^7.1.6", 35 | "@parcel/core": ">=2.8.3 <3.0.0", 36 | "@parcel/transformer-elm": "^2.8.3", 37 | "elm": "^0.19.1-5", 38 | "parcel": "^2.8.3" 39 | }, 40 | "peerDependencies": { 41 | "@babel/core": ">=7.13.0 <8.0.0", 42 | "@babel/preset-env": "^7.1.6", 43 | "@parcel/core": ">=2.8.3 <3.0.0", 44 | "elm": "^0.19.1-5" 45 | }, 46 | "scripts": { 47 | "build": "NODE_ENV=production parcel build", 48 | "dev": "NODE_ENV=development parcel" 49 | }, 50 | "source": "src/index.html" 51 | } 52 | |] 53 | ) 54 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/Review/ConfigSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.Review.ConfigSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.Review.Config (elmReviewConfigFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeBare), 10 | ElmOptions (ElmOptions), 11 | ProjectType (Elm), 12 | ) 13 | import Test.Hspec (Spec, describe, it) 14 | import Test.Hspec.Expectations.Pretty (shouldBe) 15 | import Text.RawString.QQ (r) 16 | 17 | spec :: Spec 18 | spec = describe "ReviewConfig.elm rendering" do 19 | it "renders the ReviewConfig.elm correctly" do 20 | bootstrapContent (elmReviewConfigFor . Elm $ ElmOptions ElmModeBare True) 21 | >>= ( `shouldBe` 22 | Right 23 | [r|module ReviewConfig exposing (config) 24 | 25 | import NoBooleanCase 26 | import NoDuplicatePorts 27 | import NoExposingEverything 28 | import NoImportingEverything 29 | import NoMissingSubscriptionsCall 30 | import NoMissingTypeAnnotation 31 | import NoMissingTypeConstructor 32 | import NoMissingTypeExpose 33 | import NoPrematureLetComputation 34 | import NoRecursiveUpdate 35 | import NoSimpleLetBody 36 | import NoUnused.CustomTypeConstructorArgs 37 | import NoUnused.CustomTypeConstructors 38 | import NoUnused.Dependencies 39 | import NoUnused.Exports 40 | import NoUnused.Modules 41 | import NoUnused.Parameters 42 | import NoUnused.Patterns 43 | import NoUnused.Variables 44 | import NoUnusedPorts 45 | import NoUselessSubscriptions 46 | import Review.Rule exposing (Rule) 47 | import Simplify 48 | 49 | 50 | config : List Rule 51 | config = 52 | [ NoBooleanCase.rule 53 | , NoDuplicatePorts.rule 54 | , NoExposingEverything.rule 55 | , NoImportingEverything.rule [] 56 | , NoMissingSubscriptionsCall.rule 57 | , NoMissingTypeAnnotation.rule 58 | , NoMissingTypeConstructor.rule 59 | , NoMissingTypeExpose.rule 60 | , NoPrematureLetComputation.rule 61 | , NoRecursiveUpdate.rule 62 | , NoSimpleLetBody.rule 63 | , NoUnusedPorts.rule 64 | , NoUnused.CustomTypeConstructors.rule [] 65 | , NoUnused.CustomTypeConstructorArgs.rule 66 | , NoUnused.Dependencies.rule 67 | , NoUnused.Exports.rule 68 | , NoUnused.Modules.rule 69 | , NoUnused.Parameters.rule 70 | , NoUnused.Patterns.rule 71 | , NoUnused.Variables.rule 72 | , NoUselessSubscriptions.rule 73 | , Simplify.rule Simplify.defaults 74 | ] 75 | |] 76 | ) 77 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Elm/Review/ElmJsonSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Elm.Review.ElmJsonSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Elm.Review.ElmJson (elmReviewElmJsonFor) 8 | import Bootstrap.Data.ProjectType 9 | ( ElmMode (ElmModeBare), 10 | ElmOptions (ElmOptions), 11 | ProjectType (Elm), 12 | ) 13 | import Test.Hspec (Spec, describe, it) 14 | import Test.Hspec.Expectations.Pretty (shouldBe) 15 | import Text.RawString.QQ (r) 16 | 17 | spec :: Spec 18 | spec = describe "elm-review elm.json rendering" do 19 | it "renders the elm-review elm.json correctly" do 20 | bootstrapContent (elmReviewElmJsonFor . Elm $ ElmOptions ElmModeBare True) 21 | >>= ( `shouldBe` 22 | Right 23 | [r|{ 24 | "type": "application", 25 | "source-directories": [ 26 | "src" 27 | ], 28 | "elm-version": "0.19.1", 29 | "dependencies": { 30 | "direct": { 31 | "Arkham/elm-review-no-missing-type-constructor": "1.0.2", 32 | "elm/core": "1.0.5", 33 | "jfmengels/elm-review": "2.12.2", 34 | "jfmengels/elm-review-code-style": "1.1.3", 35 | "jfmengels/elm-review-common": "1.3.2", 36 | "jfmengels/elm-review-simplify": "2.0.28", 37 | "jfmengels/elm-review-the-elm-architecture": "1.0.3", 38 | "jfmengels/elm-review-unused": "1.1.29", 39 | "sparksp/elm-review-ports": "1.3.1", 40 | "stil4m/elm-syntax": "7.2.9", 41 | "truqu/elm-review-nobooleancase": "1.0.1" 42 | }, 43 | "indirect": { 44 | "elm-community/list-extra": "8.7.0", 45 | "elm-explorations/test": "2.1.1", 46 | "elm/bytes": "1.0.8", 47 | "elm/html": "1.0.0", 48 | "elm/json": "1.1.3", 49 | "elm/parser": "1.1.0", 50 | "elm/project-metadata-utils": "1.0.2", 51 | "elm/random": "1.0.0", 52 | "elm/time": "1.0.0", 53 | "elm/virtual-dom": "1.0.3", 54 | "miniBill/elm-unicode": "1.0.3", 55 | "pzp1997/assoc-list": "1.0.0", 56 | "rtfeldman/elm-hex": "1.0.0", 57 | "stil4m/structured-writer": "1.0.3" 58 | } 59 | }, 60 | "test-dependencies": { 61 | "direct": { 62 | "elm-explorations/test": "2.1.1" 63 | }, 64 | "indirect": {} 65 | } 66 | } 67 | |] 68 | ) 69 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/EnvrcSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.EnvrcSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Envrc (Envrc (Envrc)) 8 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig (PreCommitHooksConfig)) 9 | import Test.Hspec (Spec, describe, it) 10 | import Test.Hspec.Expectations.Pretty (shouldBe) 11 | import Text.RawString.QQ (r) 12 | 13 | spec :: Spec 14 | spec = describe ".envrc rendering" do 15 | it "render correctly without a shell hook" do 16 | bootstrapContent (Envrc $ PreCommitHooksConfig False) 17 | >>= ( `shouldBe` 18 | Right 19 | [r|direnv version 2.23.0 || exit 1 20 | if [ $(nix-env --version | grep -oE '[0-9]+\.[0-9]+' | head -n1 | sed 's/\./000/') -lt 20004 ]; then 21 | echo 'This project is set up to work with Nix Flakes, which your version of nix doesn'"'"'t support.' 22 | echo 'Please upgrade your nix version to at least 2.4 to continue.' 23 | exit 1 24 | fi 25 | if ! nix show-config --extra-experimental-features nix-command | grep experimental-features | grep flakes 1>/dev/null 2>&1; then 26 | printf '\033[31m' 27 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 28 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 29 | printf '\033[0m' 30 | exit 1 31 | fi 32 | if ! nix show-config 1>/dev/null 2>&1; then 33 | printf '\033[31m' 34 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 35 | echo 'Specifically, the "nix-command" option is missing from your nix experimental-features configuration.' 36 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 37 | printf '\033[0m' 38 | exit 1 39 | fi 40 | 41 | use flake 42 | |] 43 | ) 44 | it "renders correctly with a shell hook" do 45 | bootstrapContent (Envrc $ PreCommitHooksConfig True) 46 | >>= ( `shouldBe` 47 | Right 48 | [r|direnv version 2.23.0 || exit 1 49 | if [ $(nix-env --version | grep -oE '[0-9]+\.[0-9]+' | head -n1 | sed 's/\./000/') -lt 20004 ]; then 50 | echo 'This project is set up to work with Nix Flakes, which your version of nix doesn'"'"'t support.' 51 | echo 'Please upgrade your nix version to at least 2.4 to continue.' 52 | exit 1 53 | fi 54 | if ! nix show-config --extra-experimental-features nix-command | grep experimental-features | grep flakes 1>/dev/null 2>&1; then 55 | printf '\033[31m' 56 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 57 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 58 | printf '\033[0m' 59 | exit 1 60 | fi 61 | if ! nix show-config 1>/dev/null 2>&1; then 62 | printf '\033[31m' 63 | echo 'This project is set up to work with Nix Flakes, which you don'"'"'t currently have enabled.' 64 | echo 'Specifically, the "nix-command" option is missing from your nix experimental-features configuration.' 65 | echo 'Please enable flakes by following the instructions at https://nixos.wiki/wiki/flakes#Installing_flakes' 66 | printf '\033[0m' 67 | exit 1 68 | fi 69 | 70 | use flake 71 | eval "$shellHook" 72 | |] 73 | ) 74 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/GitPodYmlSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.GitPodYmlSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.GitPodYml (gitPodYmlFor) 8 | import Bootstrap.Data.ProjectType 9 | ( NodePackageManager (NPM), 10 | ProjectType (Node), 11 | ) 12 | import Test.Hspec (Spec, describe, it) 13 | import Test.Hspec.Expectations.Pretty (shouldBe) 14 | import Text.RawString.QQ (r) 15 | 16 | spec :: Spec 17 | spec = describe ".gitpod.yml rendering" do 18 | it "renders the yml correctly" do 19 | bootstrapContent (gitPodYmlFor (Node NPM)) 20 | >>= ( `shouldBe` 21 | Right 22 | [r|tasks: [] 23 | vscode: 24 | extensions: 25 | - arrterian.nix-env-selector 26 | - jnoortheen.nix-ide 27 | |] 28 | ) 29 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/GitignoreSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.GitignoreSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Gitignore (gitignoreFor) 8 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig (PreCommitHooksConfig)) 9 | import Bootstrap.Data.ProjectType 10 | ( NodePackageManager (Yarn), 11 | ProjectType (Go, Node), 12 | SetUpGoBuild (SetUpGoBuild), 13 | ) 14 | import Test.Hspec (Spec, describe, it) 15 | import Test.Hspec.Expectations.Pretty (shouldBe) 16 | import Text.RawString.QQ (r) 17 | 18 | spec :: Spec 19 | spec = describe ".gitignore rendering" do 20 | it "renders blocks correctly with go and flakes, without pre-commit hooks" do 21 | bootstrapContent (gitignoreFor (Go $ SetUpGoBuild False) (PreCommitHooksConfig False)) 22 | >>= ( `shouldBe` 23 | Right 24 | [r|# Binaries for programs and plugins 25 | *.exe 26 | *.exe~ 27 | *.dll 28 | *.so 29 | *.dylib 30 | 31 | # Direnv Config 32 | /.direnv 33 | 34 | # Go workspace file 35 | go.work 36 | 37 | # Nix Artefacts 38 | result 39 | result-* 40 | 41 | # OS-Specific 42 | .DS_Store 43 | *~ 44 | 45 | # Output of go coverage tool 46 | *.out 47 | 48 | # Test binary, built with `go test -c` 49 | *.test 50 | |] 51 | ) 52 | 53 | it "renders blocks correctly with yarn and pre-commit hooks" do 54 | bootstrapContent (gitignoreFor (Node Yarn) (PreCommitHooksConfig True)) 55 | >>= ( `shouldBe` 56 | Right 57 | [r|# Direnv Config 58 | /.direnv 59 | 60 | # Nix Artefacts 61 | result 62 | result-* 63 | 64 | # Node 65 | /node_modules 66 | 67 | # OS-Specific 68 | .DS_Store 69 | *~ 70 | 71 | # Pre-Commit Hooks 72 | /.pre-commit-config.yaml 73 | 74 | # Yarn error log 75 | yarn-error.log 76 | |] 77 | ) 78 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Go/ModfileSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.Go.ModfileSpec (spec) where 3 | 4 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 5 | import Bootstrap.Data.Bootstrappable.Go.Modfile (GoModfile (GoModfile)) 6 | import Bootstrap.Data.ProjectName (mkProjectName) 7 | import qualified Relude.Unsafe as Unsafe 8 | import Test.Hspec (Spec, describe, it) 9 | import Test.Hspec.Expectations.Pretty (shouldBe) 10 | 11 | spec :: Spec 12 | spec = describe "go.mod rendering" do 13 | it "renders correctly given a dummy version" do 14 | let projectName = Unsafe.fromJust $ mkProjectName "test-project" 15 | bootstrapContent (GoModfile projectName "dummy-version") 16 | >>= (`shouldBe` Right "module test-project\n\ngo dummy-version\n") 17 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Haskell/LibHsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Haskell.LibHsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Haskell.LibHs (libHsFor) 8 | import Bootstrap.Data.GHCVersion (GHCVersion (GHCVersion)) 9 | import Bootstrap.Data.ProjectType 10 | ( HaskellOptions (HaskellOptions), 11 | HaskellProjectType (HaskellProjectTypeBasic), 12 | ProjectType (Haskell), 13 | SetUpHaskellBuild (SetUpHaskellBuild), 14 | ) 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Lib.hs rendering" do 21 | it "renders correctly" do 22 | bootstrapContent (libHsFor . Haskell $ HaskellOptions (GHCVersion 9 0 2) (HaskellProjectTypeBasic $ SetUpHaskellBuild True)) 23 | >>= ( `shouldBe` 24 | Right 25 | [r|module Lib (lib) where 26 | 27 | lib :: IO () 28 | lib = error "todo: write the body of the lib function in src/Lib.hs" 29 | |] 30 | ) 31 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Haskell/MainHsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Haskell.MainHsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Haskell.MainHs (mainHsFor) 8 | import Bootstrap.Data.GHCVersion (GHCVersion (GHCVersion)) 9 | import Bootstrap.Data.ProjectType 10 | ( HaskellOptions (HaskellOptions), 11 | HaskellProjectType (HaskellProjectTypeBasic, HaskellProjectTypeServer), 12 | ProjectType (Haskell), 13 | SetUpHaskellBuild (SetUpHaskellBuild), 14 | ) 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Main.hs rendering" do 21 | it "renders correctly for a simple library" do 22 | bootstrapContent (mainHsFor . Haskell $ HaskellOptions (GHCVersion 9 0 2) (HaskellProjectTypeBasic $ SetUpHaskellBuild True)) 23 | >>= ( `shouldBe` 24 | Right 25 | [r|module Main (main) where 26 | 27 | import Lib (lib) 28 | 29 | main :: IO () 30 | main = lib 31 | |] 32 | ) 33 | it "renders correctly for a WAI app served by warp" do 34 | bootstrapContent (mainHsFor . Haskell $ HaskellOptions (GHCVersion 9 0 2) (HaskellProjectTypeServer $ SetUpHaskellBuild True)) 35 | >>= ( `shouldBe` 36 | Right 37 | [r|module Main (main) where 38 | 39 | import Network.Wai.Handler.Warp (run) 40 | import Server (app) 41 | 42 | main :: IO () 43 | main = do {putTextLn "Serving on 8080..."; run 8080 app} 44 | |] 45 | ) 46 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Haskell/PreludeHsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Haskell.PreludeHsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Haskell.PreludeHs (preludeHsFor) 8 | import Bootstrap.Data.GHCVersion (GHCVersion (GHCVersion)) 9 | import Bootstrap.Data.ProjectType 10 | ( HaskellOptions (HaskellOptions), 11 | HaskellProjectType (HaskellProjectTypeBasic), 12 | ProjectType (Haskell), 13 | SetUpHaskellBuild (SetUpHaskellBuild), 14 | ) 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Prelude.hs rendering" do 21 | it "renders correctly" do 22 | bootstrapContent (preludeHsFor . Haskell $ HaskellOptions (GHCVersion 9 0 2) (HaskellProjectTypeBasic $ SetUpHaskellBuild True)) 23 | >>= ( `shouldBe` 24 | Right 25 | [r|{-# OPTIONS_GHC -Wno-missing-import-lists #-} 26 | 27 | module Prelude (module Relude) where 28 | 29 | import Relude 30 | |] 31 | ) 32 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Haskell/ServerHsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Haskell.ServerHsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Haskell.ServerHs (serverHsFor) 8 | import Bootstrap.Data.GHCVersion (GHCVersion (GHCVersion)) 9 | import Bootstrap.Data.ProjectType 10 | ( HaskellOptions (HaskellOptions), 11 | HaskellProjectType (HaskellProjectTypeServer), 12 | ProjectType (Haskell), 13 | SetUpHaskellBuild (SetUpHaskellBuild), 14 | ) 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Server.hs rendering" do 21 | it "renders correctly" do 22 | bootstrapContent (serverHsFor . Haskell $ HaskellOptions (GHCVersion 9 0 2) (HaskellProjectTypeServer $ SetUpHaskellBuild True)) 23 | >>= ( `shouldBe` 24 | Right 25 | [r|{-# LANGUAGE DataKinds #-} 26 | {-# LANGUAGE TemplateHaskell #-} 27 | {-# LANGUAGE TypeApplications #-} 28 | {-# LANGUAGE TypeOperators #-} 29 | 30 | module Server (app) where 31 | 32 | import Data.Aeson (defaultOptions) 33 | import Data.Aeson.TH (deriveJSON) 34 | import Servant (Application, Get, Handler, JSON, NoContent (NoContent), PutNoContent, ReqBody, Server, serve, type (:<|>) ((:<|>)), type (:>)) 35 | 36 | type API = "user" :> (ListUsersEndpoint :<|> PutUserEndpoint) 37 | type ListUsersEndpoint = Get '[JSON] [User] 38 | type PutUserEndpoint = ReqBody '[JSON] User :> PutNoContent 39 | data User = User {name :: String, age :: Int} deriving stock Show 40 | 41 | deriveJSON defaultOptions ''User 42 | 43 | exampleUsers :: [User] 44 | exampleUsers = [User "A. Example" 46, User "A. N. Other" 51] 45 | app :: Application 46 | app = serve (Proxy @API) server 47 | server :: Server API 48 | server = listUsersHandler :<|> putUserHandler 49 | listUsersHandler :: Handler [User] 50 | listUsersHandler = pure exampleUsers 51 | putUserHandler :: User -> Handler NoContent 52 | putUserHandler user = do {putTextLn ("I would insert the following user into the DB if this were real: " <> show user); 53 | pure NoContent} 54 | |] 55 | ) 56 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/HaskellPackagesNixSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.HaskellPackagesNixSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.HaskellPackagesNix (haskellPackagesNixFor) 8 | import Bootstrap.Data.GHCVersion (GHCVersion (GHCVersion)) 9 | import Bootstrap.Data.ProjectType 10 | ( HaskellOptions (HaskellOptions), 11 | HaskellProjectType (HaskellProjectTypeBasic, HaskellProjectTypeReplOnly), 12 | ProjectType (Go, Haskell), 13 | SetUpGoBuild (SetUpGoBuild), 14 | SetUpHaskellBuild (SetUpHaskellBuild), 15 | ) 16 | import Test.Hspec (Spec, describe, it) 17 | import Test.Hspec.Expectations.Pretty (shouldBe) 18 | import Text.RawString.QQ (r) 19 | 20 | spec :: Spec 21 | spec = describe "haskell-packages.nix rendering" do 22 | it "renders nothing for a non-haskell project" do 23 | haskellPackagesNixFor (Go $ SetUpGoBuild True) 24 | `shouldBe` Nothing 25 | it "renders correctly for a Haskell repl-only project" do 26 | case haskellPackagesNixFor (Haskell $ HaskellOptions (GHCVersion 9 4 2) HaskellProjectTypeReplOnly) of 27 | Just haskellPackagesNix -> 28 | bootstrapContent haskellPackagesNix 29 | >>= ( `shouldBe` 30 | Right 31 | [r|{nixpkgs}: 32 | nixpkgs.haskell.packages.ghc942.override { 33 | overrides = _: super: { 34 | # You can overide packages here if you need any dependencies not in this set by default 35 | }; 36 | } 37 | |] 38 | ) 39 | Nothing -> fail "Gave nothing for a project which should've had a haskell-packages.nix generated." 40 | it "renders correctly for a full Haskell project" do 41 | case haskellPackagesNixFor (Haskell $ HaskellOptions (GHCVersion 9 4 2) (HaskellProjectTypeBasic $ SetUpHaskellBuild True)) of 42 | Just haskellPackagesNix -> 43 | bootstrapContent haskellPackagesNix 44 | >>= ( `shouldBe` 45 | Right 46 | [r|{nixpkgs}: 47 | nixpkgs.haskell.packages.ghc942.override { 48 | overrides = _: super: { 49 | # The override of pretty-simple below may be needed to circumvent a bug in nixpkgs. 50 | # If the devshell builds successfully without it, feel free to remove it. 51 | pretty-simple = super.pretty-simple.overrideAttrs { 52 | doCheck = false; 53 | }; 54 | # You can overide packages here if you need any dependencies not in this set by default 55 | }; 56 | } 57 | |] 58 | ) 59 | Nothing -> fail "Gave nothing for a project which should've had a haskell-packages.nix generated." 60 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/NixPreCommitHookConfigSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.NixPreCommitHookConfigSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.NixPreCommitHookConfig 8 | ( nixPreCommitHookConfigFor, 9 | ) 10 | import Bootstrap.Data.ProjectType 11 | ( InstallLombok (InstallLombok), 12 | InstallMinishift (InstallMinishift), 13 | JavaOptions (JavaOptions), 14 | JdkPackage (OpenJDK), 15 | NodePackageManager (NPM), 16 | ProjectType (Go, Java, Node), 17 | SetUpGoBuild (SetUpGoBuild), 18 | SetUpJavaBuild (NoJavaBuild), 19 | ) 20 | import Test.Hspec (Spec, describe, it) 21 | import Test.Hspec.Expectations.Pretty (shouldBe) 22 | import Text.RawString.QQ (r) 23 | 24 | spec :: Spec 25 | spec = describe "nix/pre-commit-hooks.nix rendering" do 26 | it "renders correctly when using default Go hooks" do 27 | bootstrapContent (nixPreCommitHookConfigFor $ Go $ SetUpGoBuild False) 28 | >>= ( `shouldBe` 29 | Right 30 | [r|{ 31 | pre-commit-hooks-lib, 32 | system, 33 | nixpkgs, 34 | }: let 35 | # Function to make a set of pre-commit hooks 36 | makeHooks = hooks: 37 | pre-commit-hooks-lib.lib.${system}.run { 38 | inherit hooks; 39 | src = ../.; 40 | }; 41 | # Hooks which don't depend on running in a dev environment 42 | pureHooks = { 43 | alejandra.enable = true; 44 | go-fmt = { 45 | enable = true; 46 | entry = "${nixpkgs.go}/bin/go fmt"; 47 | files = "\\.go$"; 48 | pass_filenames = false; 49 | }; 50 | go-test = { 51 | enable = true; 52 | entry = "${nixpkgs.go}/bin/go test"; 53 | files = "\\.(go|mod)$"; 54 | pass_filenames = false; 55 | }; 56 | }; 57 | # Hooks which can run on pre-commit but not in CI 58 | impureHooks = {}; 59 | in { 60 | pureHooks = makeHooks pureHooks; 61 | allHooks = makeHooks (pureHooks // impureHooks); 62 | tools = (with pre-commit-hooks-lib.packages.${system}; [alejandra]) ++ (with nixpkgs; [go]); 63 | } 64 | |] 65 | ) 66 | it "renders correctly when using default NPM hooks" do 67 | bootstrapContent (nixPreCommitHookConfigFor $ Node NPM) 68 | >>= ( `shouldBe` 69 | Right 70 | [r|{ 71 | pre-commit-hooks-lib, 72 | system, 73 | }: let 74 | # Function to make a set of pre-commit hooks 75 | makeHooks = hooks: 76 | pre-commit-hooks-lib.lib.${system}.run { 77 | inherit hooks; 78 | src = ../.; 79 | }; 80 | # Hooks which don't depend on running in a dev environment 81 | pureHooks = { 82 | alejandra.enable = true; 83 | prettier.enable = true; 84 | }; 85 | # Hooks which can run on pre-commit but not in CI 86 | impureHooks = {}; 87 | in { 88 | pureHooks = makeHooks pureHooks; 89 | allHooks = makeHooks (pureHooks // impureHooks); 90 | tools = with pre-commit-hooks-lib.packages.${system}; [alejandra prettier]; 91 | } 92 | |] 93 | ) 94 | it "renders correctly when using default Java hooks" do 95 | bootstrapContent 96 | ( nixPreCommitHookConfigFor 97 | (Java $ JavaOptions (InstallMinishift False) (InstallLombok False) NoJavaBuild OpenJDK) 98 | ) 99 | >>= ( `shouldBe` 100 | Right 101 | [r|{ 102 | pre-commit-hooks-lib, 103 | system, 104 | nixpkgs, 105 | }: let 106 | # Function to make a set of pre-commit hooks 107 | makeHooks = hooks: 108 | pre-commit-hooks-lib.lib.${system}.run { 109 | inherit hooks; 110 | src = ../.; 111 | }; 112 | # Hooks which don't depend on running in a dev environment 113 | pureHooks = { 114 | alejandra.enable = true; 115 | google-java-format = { 116 | enable = true; 117 | entry = "${nixpkgs.google-java-format}/bin/google-java-format -i"; 118 | files = "\\.java$"; 119 | pass_filenames = true; 120 | }; 121 | }; 122 | # Hooks which can run on pre-commit but not in CI 123 | impureHooks = {}; 124 | in { 125 | pureHooks = makeHooks pureHooks; 126 | allHooks = makeHooks (pureHooks // impureHooks); 127 | tools = (with pre-commit-hooks-lib.packages.${system}; [alejandra]) ++ (with nixpkgs; [google-java-format]); 128 | } 129 | |] 130 | ) 131 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Rust/CargoLockSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Rust.CargoLockSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Rust.CargoLock 8 | ( cargoLockFor, 9 | ) 10 | import Bootstrap.Data.ProjectName (mkProjectName) 11 | import Bootstrap.Data.ProjectType 12 | ( ProjectType (Rust), 13 | ) 14 | import qualified Relude.Unsafe as Unsafe 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Cargo.lock rendering" do 21 | it "renders correctly" do 22 | bootstrapContent (cargoLockFor Rust . Unsafe.fromJust $ mkProjectName "test-project") 23 | >>= ( `shouldBe` 24 | Right 25 | [r|# This file is automatically @generated by Cargo. 26 | # It is not intended for manual editing. 27 | version = 3 28 | 29 | [[package]] 30 | name = "test-project" 31 | version = "0.1.0" 32 | |] 33 | ) 34 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Rust/CargoTomlSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Rust.CargoTomlSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Rust.CargoToml 8 | ( cargoTomlFor, 9 | ) 10 | import Bootstrap.Data.ProjectName (mkProjectName) 11 | import Bootstrap.Data.ProjectType 12 | ( ProjectType (Rust), 13 | ) 14 | import qualified Relude.Unsafe as Unsafe 15 | import Test.Hspec (Spec, describe, it) 16 | import Test.Hspec.Expectations.Pretty (shouldBe) 17 | import Text.RawString.QQ (r) 18 | 19 | spec :: Spec 20 | spec = describe "Cargo.toml rendering" do 21 | it "renders correctly" do 22 | bootstrapContent (cargoTomlFor Rust . Unsafe.fromJust $ mkProjectName "test-project") 23 | >>= ( `shouldBe` 24 | Right 25 | [r|[package] 26 | name = "test-project" 27 | version = "0.1.0" 28 | edition = "2021" 29 | 30 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 31 | 32 | [dependencies] 33 | |] 34 | ) 35 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/Rust/MainRsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.Rust.MainRsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.Rust.MainRs 8 | ( mainRsFor, 9 | ) 10 | import Bootstrap.Data.ProjectType 11 | ( ProjectType (Rust), 12 | ) 13 | import Test.Hspec (Spec, describe, it) 14 | import Test.Hspec.Expectations.Pretty (shouldBe) 15 | import Text.RawString.QQ (r) 16 | 17 | spec :: Spec 18 | spec = describe "main.rs rendering" do 19 | it "renders correctly" do 20 | bootstrapContent (mainRsFor Rust) 21 | >>= ( `shouldBe` 22 | Right 23 | [r|fn main() { 24 | println!("Hello, world!"); 25 | } 26 | |] 27 | ) 28 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/VSCodeExtensionsSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Data.Bootstrappable.VSCodeExtensionsSpec (spec) where 5 | 6 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 7 | import Bootstrap.Data.Bootstrappable.VSCodeExtensions (vsCodeExtensionsFileFor) 8 | import Bootstrap.Data.ProjectType 9 | ( InstallLombok (InstallLombok), 10 | InstallMinishift (InstallMinishift), 11 | JavaOptions (JavaOptions), 12 | JdkPackage (OpenJDK), 13 | ProjectType (Java), 14 | SetUpJavaBuild (NoJavaBuild), 15 | ) 16 | import Test.Hspec (Spec, describe, it) 17 | import Test.Hspec.Expectations.Pretty (shouldBe) 18 | import Text.RawString.QQ (r) 19 | 20 | spec :: Spec 21 | spec = describe ".vscode/extensions.json rendering" do 22 | it "renders the json correctly" do 23 | bootstrapContent 24 | ( vsCodeExtensionsFileFor 25 | ( Java $ 26 | JavaOptions 27 | (InstallMinishift True) 28 | (InstallLombok True) 29 | NoJavaBuild 30 | OpenJDK 31 | ) 32 | ) 33 | >>= ( `shouldBe` 34 | Right 35 | [r|{ 36 | "recommendations": [ 37 | "arrterian.nix-env-selector", 38 | "jnoortheen.nix-ide", 39 | "vscjava.vscode-java-pack", 40 | "gabrielbb.vscode-lombok" 41 | ] 42 | } 43 | |] 44 | ) 45 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Bootstrappable/VSCodeSettingsSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.Bootstrappable.VSCodeSettingsSpec (spec) where 3 | 4 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 5 | import Bootstrap.Data.Bootstrappable.VSCodeSettings (vsCodeSettingsFor) 6 | import Bootstrap.Data.DevContainer (DevContainerConfig (DevContainerConfig)) 7 | import Test.Hspec (Spec, describe, it) 8 | import Test.Hspec.Expectations.Pretty (shouldBe) 9 | 10 | spec :: Spec 11 | spec = describe ".vscode/settings.json rendering" do 12 | it "renders the json correctly" do 13 | bootstrapContent (vsCodeSettingsFor (DevContainerConfig True)) 14 | >>= ( `shouldBe` 15 | Right 16 | ( unlines 17 | ["{", " \"nixEnvSelector.nixFile\": \"${workspaceRoot}/flake.nix\",", " \"nixEnvSelector.useFlakes\": true", "}"] 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/Config/InternalSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE QuasiQuotes #-} 3 | {-# OPTIONS_GHC -Wno-orphans #-} 4 | 5 | -- | Copyright : (c) Crown Copyright GCHQ 6 | module Bootstrap.Data.Config.InternalSpec (spec) where 7 | 8 | import Bootstrap.Data.Bootstrappable (Bootstrappable (bootstrapContent)) 9 | import Bootstrap.Data.Config (configFor) 10 | import Bootstrap.Data.ContinuousIntegration (ContinuousIntegrationConfig (ContinuousIntegrationConfig)) 11 | import Bootstrap.Data.DevContainer (DevContainerConfig (DevContainerConfig)) 12 | import Bootstrap.Data.PreCommitHook (PreCommitHooksConfig (PreCommitHooksConfig)) 13 | import Bootstrap.Data.ProjectName (mkProjectName) 14 | import Bootstrap.Data.ProjectType (NodePackageManager (PNPm), ProjectType (Node)) 15 | import Bootstrap.Data.ProjectTypeSpec () 16 | import Bootstrap.Data.Target (Target (TargetDefault)) 17 | import qualified Relude.Unsafe as Unsafe 18 | import Test.Hspec (Spec, describe, it) 19 | import Test.Hspec.Expectations.Pretty (shouldBe) 20 | import Text.RawString.QQ (r) 21 | 22 | spec :: Spec 23 | spec = describe ".nix-bootstrap.dhall rendering" do 24 | it "renders correctly" do 25 | let projectName = Unsafe.fromJust $ mkProjectName "test-project" 26 | bootstrapContent 27 | ( configFor 28 | projectName 29 | (Node PNPm) 30 | (PreCommitHooksConfig True) 31 | (ContinuousIntegrationConfig True) 32 | (DevContainerConfig True) 33 | TargetDefault 34 | ) 35 | >>= ( `shouldBe` 36 | Right 37 | [r|-- This file was generated by nix-bootstrap. 38 | -- It should be checked into version control. 39 | -- It is used to aid migration between nix-bootstrap versions and preserve idempotence. 40 | let NodePackageManager = < NPM | PNPm | Yarn > 41 | 42 | let ElmMode = < Bare | Node : NodePackageManager > 43 | 44 | let ElmOptions = { elmMode : ElmMode, provideElmReview : Bool } 45 | 46 | let HaskellProjectType = < ReplOnly | Basic : Bool | Server : Bool > 47 | 48 | let HaskellOptions = 49 | { ghcVersion : { major : Natural, minor : Natural, patch : Natural } 50 | , haskellProjectType : HaskellProjectType 51 | } 52 | 53 | let JavaOptions = 54 | { installMinishift : Bool 55 | , installLombok : Bool 56 | , setUpJavaBuild : < SetUpJavaBuild : Text | NoJavaBuild > 57 | , jdk : < OpenJDK | GraalVM > 58 | } 59 | 60 | let ProjectType = 61 | < Minimal 62 | | Elm : ElmOptions 63 | | Haskell : HaskellOptions 64 | | Node : NodePackageManager 65 | | Go : Bool 66 | | Java : JavaOptions 67 | | Python 68 | | Rust 69 | > 70 | 71 | in { projectName = "test-project" 72 | , projectType = ProjectType.Node NodePackageManager.PNPm 73 | , setUpPreCommitHooks = True 74 | , setUpContinuousIntegration = True 75 | , setUpVSCodeDevContainer = True 76 | , target = {=} 77 | } 78 | |] 79 | ) 80 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/DevContainerSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.DevContainerSpec (spec) where 3 | 4 | import Bootstrap.Data.DevContainer 5 | ( DevContainerConfig (DevContainerConfig), 6 | devContainerConfigCodec, 7 | ) 8 | import Test.Hspec (Spec, describe, it) 9 | import Test.Util (tomlRoundtripTest) 10 | 11 | spec :: Spec 12 | spec = describe "DevContainerConfig" do 13 | it "roundtrips to TOML when True" $ tomlRoundtripTest devContainerConfigCodec (DevContainerConfig True) 14 | it "roundtrips to TOML when False" $ tomlRoundtripTest devContainerConfigCodec (DevContainerConfig False) 15 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/PreCommitHookSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.PreCommitHookSpec (spec) where 3 | 4 | import Bootstrap.Data.PreCommitHook 5 | ( PreCommitHooksConfig (PreCommitHooksConfig), 6 | preCommitHooksConfigCodec, 7 | ) 8 | import Test.Hspec (Spec, describe, it) 9 | import Test.Util (tomlRoundtripTest) 10 | 11 | spec :: Spec 12 | spec = describe "PreCommitHooksConfig" do 13 | it "roundtrips to TOML when True" $ tomlRoundtripTest preCommitHooksConfigCodec (PreCommitHooksConfig True) 14 | it "roundtrips to TOML when False" $ tomlRoundtripTest preCommitHooksConfigCodec (PreCommitHooksConfig False) 15 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/ProjectNameSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.ProjectNameSpec (spec) where 3 | 4 | import Bootstrap.Data.ProjectName 5 | ( ProjectName (unProjectName), 6 | mkProjectName, 7 | replaceSpacesWithDashes, 8 | tomlBiMap, 9 | ) 10 | import qualified Relude.Unsafe as Unsafe 11 | import Test.Hspec (Spec, describe, it) 12 | import Test.Hspec.Expectations.Pretty (shouldBe) 13 | import qualified Toml 14 | 15 | spec :: Spec 16 | spec = describe "ProjectName" do 17 | describe "mkProjectName" do 18 | it "Does not remove spaces" do 19 | unProjectName (Unsafe.fromJust $ mkProjectName "my Project") `shouldBe` "my Project" 20 | describe "replaceSpacesWithDashes" do 21 | it "Replaces spaces with dashes" do 22 | replaceSpacesWithDashes <$> mkProjectName "my Project" `shouldBe` mkProjectName "my-Project" 23 | describe "tomlBiMap" do 24 | it "Converts valid project names bidirectionally" do 25 | let Toml.BiMap {..} = tomlBiMap 26 | projectName = Unsafe.fromJust $ mkProjectName "my-Project" 27 | forward projectName `shouldBe` Right (Toml.AnyValue (Toml.Text "my-Project")) 28 | backward (Toml.AnyValue (Toml.Text "my-Project")) `shouldBe` Right projectName 29 | it "Fails if the serialised project name is invalid" do 30 | let Toml.BiMap {..} = tomlBiMap 31 | backward (Toml.AnyValue (Toml.Text "1y-Project")) `shouldBe` Left (Toml.ArbitraryError "Invalid project name") 32 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/ProjectTypeSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | {-# OPTIONS_GHC -Wno-orphans #-} 3 | 4 | -- | Copyright : (c) Crown Copyright GCHQ 5 | module Bootstrap.Data.ProjectTypeSpec (spec) where 6 | 7 | import Bootstrap.Data.ProjectType 8 | ( ArtefactId (ArtefactId), 9 | ElmMode (ElmModeBare, ElmModeNode), 10 | ElmOptions (ElmOptions), 11 | JavaOptions (JavaOptions), 12 | ProjectType (Elm, Go, Java, Minimal, Node, Python), 13 | SetUpJavaBuild (NoJavaBuild, SetUpJavaBuild), 14 | ) 15 | import Data.Char (isAsciiLower, isAsciiUpper) 16 | import Test.Hspec (Spec, describe) 17 | import Test.Hspec.QuickCheck (prop) 18 | import Test.QuickCheck 19 | ( Arbitrary (arbitrary), 20 | arbitraryBoundedEnum, 21 | choose, 22 | oneof, 23 | suchThat, 24 | vectorOf, 25 | ) 26 | import Test.Util (dhallRoundtripTest) 27 | 28 | instance Arbitrary ProjectType where 29 | arbitrary = 30 | oneof 31 | [ pure Minimal, 32 | Elm 33 | <$> ( ElmOptions 34 | <$> oneof [pure ElmModeBare, ElmModeNode <$> arbitraryBoundedEnum] 35 | <*> arbitrary 36 | ), 37 | Node <$> arbitraryBoundedEnum, 38 | Go <$> arbitraryBoundedEnum, 39 | Java <$> (JavaOptions <$> arbitraryBoundedEnum <*> arbitraryBoundedEnum <*> arbitrary <*> arbitraryBoundedEnum), 40 | Python <$> arbitraryBoundedEnum 41 | ] 42 | 43 | instance Arbitrary SetUpJavaBuild where 44 | arbitrary = 45 | oneof 46 | [ SetUpJavaBuild <$> arbitrary, 47 | pure NoJavaBuild 48 | ] 49 | 50 | instance Arbitrary ArtefactId where 51 | arbitrary = do 52 | l <- choose (1, 10) 53 | artefactIdChars <- vectorOf l (arbitrary `suchThat` \c -> isAsciiUpper c || isAsciiLower c) 54 | pure . ArtefactId $ toText artefactIdChars 55 | 56 | spec :: Spec 57 | spec = describe "ProjectType" do 58 | prop "roundtrips to Dhall" (dhallRoundtripTest @ProjectType) 59 | -------------------------------------------------------------------------------- /test/Bootstrap/Data/VersionSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Data.VersionSpec (spec) where 3 | 4 | import Bootstrap.Data.Version (toMajorVersion) 5 | import qualified Data.Version as V 6 | import qualified Relude.Unsafe as Unsafe 7 | import Test.Hspec (Spec, describe, it, shouldSatisfy) 8 | 9 | spec :: Spec 10 | spec = describe "MajorVersion's Ord instance" $ 11 | it "orders versions correctly" do 12 | let mkVersion = Unsafe.fromJust . toMajorVersion . V.makeVersion 13 | a = mkVersion [0, 1, 0] 14 | b = mkVersion [1, 0, 0] 15 | c = mkVersion [1, 0, 1] 16 | d = mkVersion [2, 0, 1] 17 | a `shouldSatisfy` (<= b) 18 | b `shouldSatisfy` (<= c) 19 | c `shouldSatisfy` (<= d) 20 | a `shouldSatisfy` (<= d) 21 | a `shouldSatisfy` (<= a) 22 | -------------------------------------------------------------------------------- /test/Bootstrap/Nix/CommandSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.CommandSpec (spec) where 5 | 6 | import Bootstrap.Nix.Command 7 | ( NixCommand (NixCommand), 8 | NixCommandVariant (NCVBuild), 9 | writeNixCommand, 10 | ) 11 | import Test.Hspec (Spec, describe, it, shouldBe) 12 | 13 | spec :: Spec 14 | spec = describe "writeNixCommand" do 15 | it "correctly writes build command" $ 16 | writeNixCommand @Text (NixCommand NCVBuild) `shouldBe` "nix build" 17 | -------------------------------------------------------------------------------- /test/Bootstrap/Nix/Expr/MkShellSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.Expr.MkShellSpec (spec) where 5 | 6 | import Bootstrap.Data.PreCommitHook 7 | ( PreCommitHooksConfig (PreCommitHooksConfig), 8 | ) 9 | import Bootstrap.Data.ProjectType 10 | ( ProjectType (Minimal, Rust), 11 | ) 12 | import Bootstrap.Nix.Expr (nix) 13 | import Bootstrap.Nix.Expr.MkShell 14 | ( BuildInputSpec (BuildInputSpec), 15 | mkShell, 16 | ) 17 | import Test.Hspec (Spec, describe, it) 18 | import Test.Hspec.Expectations.Pretty (shouldBe) 19 | 20 | spec :: Spec 21 | spec = do 22 | describe "mkShell" do 23 | it "gives no shell hook when one isn't needed" do 24 | mkShell (BuildInputSpec [] [] (PreCommitHooksConfig False) Minimal []) 25 | `shouldBe` [nix|nixpkgs.mkShell { 26 | buildInputs = [ 27 | # Insert any dependencies that should exist in the dev shell environment here 28 | ]; 29 | }|] 30 | it "gives a proper shell hook for projects with pre-commit hooks" do 31 | mkShell (BuildInputSpec [] [] (PreCommitHooksConfig True) Minimal []) 32 | `shouldBe` [nix|nixpkgs.mkShell { 33 | buildInputs = preCommitHooks.tools; 34 | inherit (preCommitHooks.allHooks) shellHook; 35 | }|] 36 | it "gives a proper shell hook for Rust projects" do 37 | mkShell (BuildInputSpec [] [] (PreCommitHooksConfig False) Rust []) 38 | `shouldBe` [nix|nixpkgs.mkShell { 39 | buildInputs = [ 40 | # Insert any dependencies that should exist in the dev shell environment here 41 | ]; 42 | shellHook = '' 43 | export RUST_SRC_PATH=${nixpkgs.rustPlatform.rustLibSrc} 44 | ''; 45 | }|] 46 | it "gives a proper shell hook for Rust projects with pre-commit hooks" do 47 | mkShell (BuildInputSpec [] [] (PreCommitHooksConfig True) Rust []) 48 | `shouldBe` [nix|nixpkgs.mkShell { 49 | buildInputs = preCommitHooks.tools; 50 | shellHook = '' 51 | export RUST_SRC_PATH=${nixpkgs.rustPlatform.rustLibSrc} 52 | ${preCommitHooks.allHooks.shellHook} 53 | ''; 54 | }|] 55 | -------------------------------------------------------------------------------- /test/Bootstrap/Nix/Expr/NixpkgsSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.Nix.Expr.NixpkgsSpec (spec) where 3 | 4 | import Bootstrap.Nix.Expr (isMostlyCorrectlyScoped) 5 | import Bootstrap.Nix.Expr.Nixpkgs (nixpkgsExpr) 6 | import Test.Hspec (Spec, describe, it) 7 | import Test.Hspec.Expectations.Pretty (shouldBe) 8 | 9 | spec :: Spec 10 | spec = do 11 | describe "nixpkgsExpr" do 12 | it "has no external scope requirements" do 13 | isMostlyCorrectlyScoped nixpkgsExpr `shouldBe` Right () 14 | -------------------------------------------------------------------------------- /test/Bootstrap/Nix/FlakeSpec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | -- | Copyright : (c) Crown Copyright GCHQ 4 | module Bootstrap.Nix.FlakeSpec (spec) where 5 | 6 | import Bootstrap.Data.ProjectName (mkProjectName) 7 | import Bootstrap.Data.Target (Target (TargetDefault)) 8 | import Bootstrap.Nix.Expr (CommentsPolicy (ShowComments), writeExprFormatted) 9 | import Bootstrap.Nix.Flake (intermediateFlake) 10 | import qualified Relude.Unsafe as Unsafe 11 | import Test.Hspec (Spec, describe, it) 12 | import Test.Hspec.Expectations.Pretty (shouldBe) 13 | import Text.RawString.QQ (r) 14 | 15 | spec :: Spec 16 | spec = describe "intermediateFlake" do 17 | it "correctly writes the intermediate flake" do 18 | e <- writeExprFormatted ShowComments (intermediateFlake (Unsafe.fromJust $ mkProjectName "test-project") TargetDefault) 19 | e 20 | `shouldBe` Right 21 | [r|{ 22 | description = "Development infrastructure for test-project"; 23 | inputs = { 24 | nixpkgs-src.url = "nixpkgs"; 25 | }; 26 | outputs = _: {}; 27 | } 28 | |] 29 | -------------------------------------------------------------------------------- /test/Bootstrap/StateSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Bootstrap.StateSpec (spec) where 3 | 4 | import Bootstrap.State 5 | ( InputLine (InputLine), 6 | TextInputState (TextInputState), 7 | backspaceInputLine, 8 | handleBackspacePress, 9 | handleCharEntry, 10 | inputLineLength, 11 | insertIntoInputLine, 12 | updateCursorPos, 13 | ) 14 | import Test.Hspec (Spec, describe, it) 15 | import Test.Hspec.Expectations.Pretty (shouldBe) 16 | 17 | spec :: Spec 18 | spec = do 19 | describe "InputLine" do 20 | describe "insertIntoInputLine" do 21 | it "inserts into the inputLine at the expected position" $ 22 | insertIntoInputLine (InputLine "hit") 2 'n' `shouldBe` InputLine "hint" 23 | describe "backspaceInputLine" do 24 | it "deletes from the inputLine at the expected position" $ 25 | backspaceInputLine (InputLine "hint") 3 `shouldBe` InputLine "hit" 26 | describe "inputLineLength" do 27 | it "gives the correct length" $ 28 | inputLineLength (InputLine "hint") `shouldBe` 4 29 | describe "TextInputState" do 30 | describe "updateCursorPos" do 31 | it "correctly applies the update function" $ 32 | updateCursorPos (TextInputState 3 (InputLine "abcdefg")) (+ 3) `shouldBe` TextInputState 6 (InputLine "abcdefg") 33 | it "cannot move left of zero" $ 34 | updateCursorPos (TextInputState 3 (InputLine "abcdefg")) (\v -> v - 5) `shouldBe` TextInputState 0 (InputLine "abcdefg") 35 | it "cannot move right of the end of the string" $ 36 | updateCursorPos (TextInputState 3 (InputLine "abcdefg")) (+ 20) `shouldBe` TextInputState 7 (InputLine "abcdefg") 37 | describe "handleCharEntry" do 38 | it "inserts a character and updates the cursor position" $ 39 | handleCharEntry (TextInputState 3 (InputLine "abcdefg")) '!' `shouldBe` TextInputState 4 (InputLine "abc!defg") 40 | describe "handleBackspacePress" do 41 | it "deletes a character and updates the cursor position" $ 42 | handleBackspacePress (TextInputState 3 (InputLine "abcdefg")) `shouldBe` TextInputState 2 (InputLine "abdefg") 43 | it "does not update the state when the cursor position is 0" $ 44 | handleBackspacePress (TextInputState 0 (InputLine "abcdefg")) `shouldBe` TextInputState 0 (InputLine "abcdefg") 45 | -------------------------------------------------------------------------------- /test/PreludeSpec.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module PreludeSpec (spec) where 3 | 4 | import Test.Hspec (Spec, describe, it) 5 | import Test.Hspec.Expectations.Pretty (shouldBe) 6 | 7 | spec :: Spec 8 | spec = describe "clamp" do 9 | it "successfully clamps below the lower end" $ clamp 5 10 0 `shouldBe` (5 :: Int) 10 | it "successfully clamps above the upper end" $ clamp 5 10 100 `shouldBe` (10 :: Int) 11 | it "does not modify on the lower end" $ clamp 5 10 5 `shouldBe` (5 :: Int) 12 | it "does not modify on the upper end" $ clamp 5 10 10 `shouldBe` (10 :: Int) 13 | it "does not modify in the allowed range" $ clamp 5 10 7 `shouldBe` (7 :: Int) 14 | -------------------------------------------------------------------------------- /test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-} 2 | {-# OPTIONS_GHC -Wno-missing-export-lists #-} 3 | {-# OPTIONS_GHC -Wno-missing-import-lists #-} 4 | -------------------------------------------------------------------------------- /test/Test/Util.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Test.Util (dhallRoundtripTest, tomlRoundtripTest) where 3 | 4 | import Dhall 5 | ( Encoder (embed), 6 | FromDhall, 7 | ToDhall, 8 | auto, 9 | inject, 10 | input, 11 | ) 12 | import Dhall.Core (pretty) 13 | import Test.Hspec.Expectations.Pretty (Expectation, shouldBe) 14 | import Toml (TomlCodec) 15 | import qualified Toml 16 | 17 | dhallRoundtripTest :: (Eq a, FromDhall a, Show a, ToDhall a) => a -> Expectation 18 | dhallRoundtripTest a = input auto (pretty $ embed inject a) >>= (`shouldBe` a) 19 | 20 | tomlRoundtripTest :: (Show a, Eq a) => TomlCodec a -> a -> Expectation 21 | tomlRoundtripTest codec a = Toml.decode codec (Toml.encode codec a) `shouldBe` Right a 22 | -------------------------------------------------------------------------------- /test/Test/Util/CanDieOnError.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | -- | Description : This module provides an alternate instance for 4 | -- CanDieOnError which doesn't require connection to a terminal. 5 | -- Copyright : (c) Crown Copyright GCHQ 6 | module Test.Util.CanDieOnError () where 7 | 8 | import Bootstrap.Error (CanDieOnError (dieOnError)) 9 | 10 | instance {-# OVERLAPPING #-} CanDieOnError IO where 11 | dieOnError toErr a = 12 | runExceptT a >>= \case 13 | Right v -> pure v 14 | Left e -> error (toErr e) 15 | -------------------------------------------------------------------------------- /test/Test/Util/RunConfig.hs: -------------------------------------------------------------------------------- 1 | -- | Copyright : (c) Crown Copyright GCHQ 2 | module Test.Util.RunConfig (rcDefault) where 3 | 4 | import Bootstrap.Cli 5 | ( RunConfig 6 | ( RunConfig, 7 | rcAllowDirty, 8 | rcFromScratch, 9 | rcNonInteractive, 10 | rcWithDevContainer 11 | ), 12 | ) 13 | 14 | -- | A RunConfig with all its fields set to the simplest possible values 15 | rcDefault :: RunConfig 16 | rcDefault = 17 | RunConfig 18 | { rcAllowDirty = False, 19 | rcFromScratch = False, 20 | rcNonInteractive = False, 21 | rcWithDevContainer = Nothing 22 | } 23 | -------------------------------------------------------------------------------- /vulnerability-whitelist.toml: -------------------------------------------------------------------------------- 1 | # © Crown Copyright GCHQ 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ["gcc"] 16 | cve = ["CVE-2023-4039"] 17 | comment = "Reasonable worst-case is loss of availability; risk acceptable." 18 | 19 | ["zlib-1.3.1"] 20 | cve = ["CVE-2023-6992"] 21 | comment = "We do not call the affected code with untrusted data." 22 | --------------------------------------------------------------------------------