diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml new file mode 100644 index 00000000..c430196b --- /dev/null +++ b/.github/workflows/lints.yml @@ -0,0 +1,50 @@ +name: "Lints" + +on: + workflow_dispatch: + pull_request: + branches: + - master + + push: + branches: + - master + +defaults: + run: + shell: bash + +# do we really want nixos-unstable? +env: + NIX_PATH: "nixpkgs=channel:nixos-unstable" + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # PR_NUMBER: ${{ github.event.number }} + +jobs: + deadnix: + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v3 + - run: | + nix run github:astro/deadnix -- --edit --no-lambda-pattern-names + TMPFILE=$(mktemp) + git diff >"${TMPFILE}" + git stash -u && git stash drop + nix-shell -p reviewdog --run "reviewdog -f=diff -f.diff.strip=1 -reporter=github-pr-review < \"${TMPFILE}\"" + + nixprof: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v22 + with: + nix_path: nixpkgs=channel:nixos-unstable + - run: | + nix profile install github:Kha/nixprof + nixprof record nix develop .#ghc8107 --accept-flake-config + nixprof report -p + nixprof report -a + nixprof report -s + # FIXME: is that the right way to publish a report on GitHub? + # curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST -d "{\"body\": \"$report\"}" "https://api.github.com/repos/input-output-hk/haskell.nix/issues/$PR_NUMBER/comments" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 865ef785..65447b47 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,30 +18,9 @@ jobs: name: "Wait for hydra status" runs-on: ubuntu-latest steps: - - name: Get specific check run status - run: | - # start with a random sleep to prevent hitting the api too hard. - while true; do - # For GitHub Apps - # conclusion=$(gh api repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status --jq '.check_runs[] | select(.name == "ci/hydra-build:$DEV_SHELL") | .conclusion') - # For GitHub Statuses; we need --paginate because there are so many statuses - echo "Querying: gh api repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status --paginate --jq '.statuses[] | select(.context == \"$HYDRA_JOB\") | .state'" - conclusion=$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status" --paginate --jq ".statuses[] | select(.context == \"$HYDRA_JOB\") | .state") - case "$conclusion" in - success) - echo "$HYDRA_JOB succeeded" - exit 0;; - failure) - echo "$HYDRA_JOB failed" - exit 1;; - *) - echo "conclusion is: '$conclusion'" - gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status" --paginate --jq '.statuses[] | .state+"\t"+.context'|sort - WAIT=$((30 + RANDOM % 30)) - echo "$HYDRA_JOB pending. Waiting ${WAIT}s..." - sleep $WAIT;; - esac - done + - uses: input-output-hk/actions/wait-for-hydra@latest + with: + status: ci/hydra-build:required upload: needs: wait-for-hydra-eval @@ -108,4 +87,4 @@ jobs: target-platform: ${{ matrix.target-platform }} compiler-nix-name: ${{ matrix.compiler-nix-name }} minimal: ${{ matrix.variant == '-minimal' }} - iog: ${{ matrix.iog == '-iog' }} \ No newline at end of file + iog: ${{ matrix.iog == '-iog' }} diff --git a/cross-windows.nix b/cross-windows.nix index fdf17054..a6ed2e0f 100644 --- a/cross-windows.nix +++ b/cross-windows.nix @@ -161,6 +161,11 @@ pkgs.pkgsBuildBuild.mkShell ({ (tool "happy") (tool "alex") stdenv.cc.cc.lib ]) + ++ map pkgs.lib.getDev (with pkgs; [ + zlib pcre openssl + windows.mcfgthreads + windows.mingw_w64_pthreads + ]) ++ pkgs.lib.optional (withHLS && (compiler-not-in ( pkgs.lib.optional (builtins.compareVersions compiler.version "9.9" >= 0) compiler-nix-name ++ pkgs.lib.optional (pkgs.stdenv.hostPlatform.isDarwin && pkgs.stdenv.hostPlatform.isAarch64) "ghc902") "Haskell Language Server")) (tool "haskell-language-server") diff --git a/docs/bootstrap.sh b/docs/bootstrap.sh new file mode 100755 index 00000000..2afd6879 --- /dev/null +++ b/docs/bootstrap.sh @@ -0,0 +1,209 @@ +#!/usr/bin/env bash +set -euo pipefail + +# End-user DevX could also be potentially improved through this script: e.g, +# by helping new users to set up Nix using Determinate Systems new installer, +# or detect that it's used in a GitHub Actions context and advise installing Nix with Cachix's +# install Nix action. + +verbosity=1 + +while getopts "v" opt; do + case $opt in + v) verbosity=$((verbosity + 1));; + *) echo "Invalid option: -$OPTARG" >&2; exit 1;; + esac +done + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + local level="$1" + local msg="$2" + local color="$NC" + + case "$level" in + DEBUG) color="$BLUE" ;; + INFO) color="$GREEN" ;; + WARN) color="$YELLOW";; + ERROR) color="$RED" ;; + *) echo "Unknown log level: $level" >&2; return 1;; + esac + + if [ $verbosity -ge 2 ] || [ "$level" != "DEBUG" ]; then + echo -e "${color}${level}: ${NC}${msg}" + fi +} + +log "INFO" "This script will set up nix and direnv, with opinionated default for Haskell development." + + +log "INFO" "[1/7] Install nix if it's not already installed ..." + +if ! which nix >/dev/null 2>&1; then + if [ "$GITHUB_ACTIONS" == "true" ]; then + log "ERROR" "This script requires nix to be installed; you don't appear to have nix available. Since it seems that you're running this inside a GitHub Action, you can set up Nix using https://github.com/cachix/install-nix-action, e.g.: + + - name: Install Nix with good defaults + uses: cachix/install-nix-action@v20 + with: + extra_nix_config: | + trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= loony-tools:pr9m4BkM/5/eSTZlkQyRt57Jz7OMBxNSUiMC4FkcNfk= + substituters = https://cache.iog.io/ https://cache.zw3rk.com/ https://cache.nixos.org/ + nix_path: nixpkgs=channel:nixos-unstable" + exit 1 + else + log "DEBUG" "nix is not installed. Would you like to install it now? (y/n)" + read -r confirm + if [[ "$confirm" == [yY] || "$confirm" == [yY][eE][sS] ]]; then + log "DEBUG" "Installing nix..." + curl -L https://nixos.org/nix/install | sh + . "$HOME/.nix-profile/etc/profile.d/nix.sh" + log "DEBUG" "nix installed successfully." + else + log "ERROR" "nix is required for the setup process. Please install nix and re-run the script." + exit 1 + fi + fi +else + log "DEBUG" "nix is already installed." +fi + + +log "INFO" "[2/7] Tweak nix.conf with good defaults (may need sudo) ..." + +if [ -z "${XDG_CONFIG_HOME:-}" ]; then + XDG_CONFIG_HOME="$HOME/.config" +fi + +nix_conf_file="$XDG_CONFIG_HOME/nix/nix.conf" +if grep -q "experimental-features" "$nix_conf_file"; then + log "WARN" "The 'experimental-features' option already exists in '$nix_conf_file'. Please check that it's set to 'experimental-features = nix-command flakes'." + echo "Press ENTER to open $nix_conf_file in $EDITOR:" + read -r confirm + $EDITOR "$nix_conf_file" +else + echo "experimental-features = nix-command flakes" >> "$nix_conf_file" + log "DEBUG" "'experimental-features = nix-command flakes' added to nix configuration." +fi +if grep -q "accept-flake-config" "$nix_conf_file"; then + log "WARN" "The 'accept-flake-config' option already exists in '$nix_conf_file'. Please check that it's set to 'accept-flake-config = true'." + echo "Press ENTER to open $nix_conf_file in $EDITOR:" + read -r confirm + $EDITOR "$nix_conf_file" +else + echo "accept-flake-config = true" >> "$nix_conf_file" + log "DEBUG" "'accept-flake-config = true' added to nix configuration." +fi + +nix_conf_file="/etc/nix/nix.conf" +if grep -q "trusted-users" "$nix_conf_file"; then + log "WARN" "The 'trusted-users' option already exists in '$nix_conf_file'. Please ensure that your user is part of 'trusted-users'." + echo "Press ENTER to open $nix_conf_file in $EDITOR:" + read -r confirm + $EDITOR "$nix_conf_file" +else + echo "trusted-users = root $USER" | sudo tee "$nix_conf_file" > /dev/null + log "DEBUG" "'trusted-users = root $USER' added to nix configuration." +fi + + +log "INFO" "[3/7] Restart nix-daemon (need sudo) ..." + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if which systemctl >/dev/null 2>&1 && systemctl is-active --quiet systemd; then + log "DEBUG" "Restarting nix-daemon..." + sudo systemctl restart nix-daemon.service + else + log "WARN" "Please restart nix-daemon manually." + echo "Press ENTER to continue ..." + read -r confirm + fi +elif [[ "$OSTYPE" == "darwin"* ]]; then + if which launchctl >/dev/null 2>&1; then + log "DEBUG" "Restarting nix-daemon..." + sudo launchctl kickstart -k system/org.nixos.nix-daemon + else + log "WARN" "Please restart nix-daemon manually." + echo "Press ENTER to continue ..." + read -r confirm + fi +fi + + +log "INFO" "[4/7] Install direnv if it's not already installed ..." + +if ! which direnv >/dev/null 2>&1; then + log "DEBUG" "direnv is not installed. Would you like to install it now? (y/n)" + read -r confirm + if [[ "$confirm" == [yY] || "$confirm" == [yY][eE][sS] ]]; then + log "DEBUG" "Installing direnv..." + nix profile install nixpkgs#direnv + log "DEBUG" "direnv installed successfully." + else + log "ERROR" "direnv is required for the setup process. Please install direnv and re-run the script." + exit 1 + fi +else + log "DEBUG" "direnv is already installed." +fi + + +log "INFO" "[5/7] Hook direnv into the user shell ..." + +# shellcheck disable=SC2016 +if grep -q 'direnv hook' "$HOME/.bashrc"; then + log "DEBUG" "direnv seems already hooked into '$HOME/.bashrc'." +else + echo 'eval "$(direnv hook bash)"' >> "$HOME/.bashrc" + log "DEBUG" "direnv hook added to '$HOME/.bashrc'." +fi +# shellcheck disable=SC2016 +if ! which zsh >/dev/null 2>&1 || grep -q 'direnv hook' "$HOME/.zshrc"; then + log "DEBUG" "zsh isn't in PATH or direnv seems already hooked into '$HOME/.zshrc'." +else + echo 'eval "$(direnv hook zsh)"' >> "$HOME/.zshrc" + log "DEBUG" "direnv hook added to '$HOME/.zshrc'." +fi +# shellcheck disable=SC2016 +if ! which fish >/dev/null 2>&1 || grep -q 'direnv hook' "$XDG_CONFIG_HOME/fish/config.fish"; then + log "DEBUG" "fish isn't in PATH or direnv seems already hooked into '$XDG_CONFIG_HOME/fish/config.fish'." +else + echo 'eval "$(direnv hook fish)"' >> "$XDG_CONFIG_HOME/fish/config.fish" + log "DEBUG" "direnv hook added to '$XDG_CONFIG_HOME/fish/config.fish'." +fi + + +log "INFO" "[6/7] Configure .envrc in the project directory ..." + +if [ -f ".envrc" ]; then + log "DEBUG" ".envrc file already exists." +else + echo 'if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" + fi' >> ".envrc" + if [ -f "flake.nix" ]; then + echo "use flake" >> ".envrc" + elif [ -f "shell.nix" ]; then + echo "use nix" >> ".envrc" + else + # FIXME: maybe we could ask interactively what compiler version and shell flavor the user want to use? + echo "use flake \"github:input-output-hk/devx#ghc8107-iog\"" >> ".envrc" + fi + log "DEBUG" "Successfully created and populated .envrc file." +fi + + +log "INFO" "[7/7] Allow direnv to load the .envrc ..." + +direnv allow +log "DEBUG" ".envrc configuration allowed." + +log "INFO" "Setup process is complete! 🎉" + +# FIXME: maybe directly install VSCode extension with `code --install-extension pinage404.nix-extension-pack; code --install-extension haskell.haskell` if `code` is in PATH?! +log "INFO" "For proper editor integration, you need to install the direnv extension for your specific editor. E.g., if you use VSCode, search for \"direnv\" in the extensions marketplace and install the plugin. Reload your editor and you're all set!" diff --git a/docs/direnv.md b/docs/direnv.md index 9d70e965..b09cd217 100644 --- a/docs/direnv.md +++ b/docs/direnv.md @@ -24,7 +24,7 @@ To install `nix`, follow the steps provided on the [Nix installer page](https:// If you're not listed, you need to add the line `trusted-users = $USER` in your configuration file. Additionally, two more lines should be added: `experimental-features = nix-command flakes` and `accept-flake-config = true` for the convenince of having flake features enabled globaly. -You can add these lines in either of the two configuration files, `$XDG_CONFIG_HOME/nix/nix.conf` or `/etc/nix/nix.conf`. After making these edits, remember to restart the `nix-daemon`. If your system is based on `systemd`, you can do so by running `sudo systemctl restart nix-daemon`. +You should add the `trusted-users` line in `/etc/nix/nix.conf`, others options could be added there or in `$XDG_CONFIG_HOME/nix/nix.conf` if you only want to change the configuration of your current user. After making these edits, remember to restart the `nix-daemon`. If you use a Linux distribution based on `systemd`, you can do so by running `sudo systemctl restart nix-daemon`, if you're running macOS, it's `launchctl kickstart -k system/org.nixos.nix-daemon`. ## Install and configure `direnv` @@ -152,3 +152,6 @@ After installing and reloading Vim, when you open a Haskell file in your project --- Remember that this tutorial provides a very basic setup. You can further customize your environment and editor to better fit your workflow. Happy coding! + +> **Note** +> You can run the whole tutorial (except the IDE configuration part) in one command `curl https://input-output-hk.github.io/devx | sh` (handy if you have several machines to configure) :) diff --git a/docs/index.html b/docs/index.html new file mode 120000 index 00000000..eba53897 --- /dev/null +++ b/docs/index.html @@ -0,0 +1 @@ +bootstrap.sh \ No newline at end of file diff --git a/flake.lock b/flake.lock index 63d080ee..1eaf865a 100644 --- a/flake.lock +++ b/flake.lock @@ -122,16 +122,15 @@ "systems": "systems" }, "locked": { - "lastModified": 1681378341, - "narHash": "sha256-2qUN04W6X9cHHytEsJTM41CmusifPTC0bgTtYsHSNY8=", - "owner": "hamishmack", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", "repo": "flake-utils", - "rev": "2767bafdb189cd623354620c2dacbeca8fd58b17", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { - "owner": "hamishmack", - "ref": "hkm/nested-hydraJobs", + "owner": "numtide", "repo": "flake-utils", "type": "github" } diff --git a/flake.nix b/flake.nix index 5bc55043..ce4fc590 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs.haskellNix.url = "github:input-output-hk/haskell.nix"; inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable"; - inputs.flake-utils.url = "github:hamishmack/flake-utils/hkm/nested-hydraJobs"; + inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.iohk-nix.url = "github:input-output-hk/iohk-nix"; outputs = { self, nixpkgs, flake-utils, haskellNix, iohk-nix }: @@ -47,7 +47,7 @@ "aarch64-linux" "aarch64-darwin" ]; - in flake-utils.lib.eachSystem supportedSystems (system: + in let flake-outputs = flake-utils.lib.eachSystem supportedSystems (system: let pkgs = import nixpkgs { overlays = [haskellNix.overlay] ++ builtins.attrValues overlays; @@ -167,7 +167,7 @@ inherit devShells; hydraJobs = devShells // { # *-dev sentinel job. Singals all -env have been built. - required = pkgs.runCommand "test-dependencies" { + required = pkgs.runCommand "required dependencies (${system})" { _hydraAggregate = true; constituents = map (name: "${system}.${name}-env") (builtins.attrNames devShellsWithEvalOnLinux); } "touch $out"; @@ -213,6 +213,15 @@ packages.cabalProjectLocal.cross-js = (import ./quirks.nix { pkgs = js-pkgs; }).template; packages.cabalProjectLocal.cross-windows = (import ./quirks.nix { pkgs = windows-pkgs; }).template; }); + # we use flake-outputs here to inject a required job that aggregates all required jobs. + in flake-outputs // { + hydraJobs = flake-outputs.hydraJobs // { + required = (import nixpkgs { system = "x86_64-linux"; }).runCommand "required dependencies" { + _hydraAggregate = true; + constituents = map (s: "${s}.required") supportedSystems; + } "touch $out"; + }; + }; # --- Flake Local Nix Configuration ---------------------------- nixConfig = {