├── .github └── workflows │ ├── hydra-build-status-darwin.yml │ └── hydra-build-status-linux.yml ├── LICENSE ├── README.md ├── hydra-build-status.py ├── maintainer-packages.nix └── nix-develop-interactive.bash /.github/workflows/hydra-build-status-darwin.yml: -------------------------------------------------------------------------------- 1 | name: Build status (Darwin) 2 | 3 | on: 4 | schedule: 5 | - cron: '0 6 * * *' 6 | workflow_dispatch: 7 | 8 | env: 9 | NIX_CONFIG: "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" 10 | 11 | jobs: 12 | check-hydra-build: 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | platform: 18 | - x86_64-darwin 19 | - aarch64-darwin 20 | 21 | 22 | name: Check build status 23 | runs-on: ubuntu-22.04 24 | 25 | steps: 26 | - name: Check out repository 27 | uses: actions/checkout@v4 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: Install Nix 32 | uses: cachix/install-nix-action@v25 33 | 34 | - name: Generate packages file 35 | run: > 36 | nix eval --json --file maintainer-packages.nix --arg showBroken false packages 37 | | jq '[.. | objects | select(has("name")) | .name]' 38 | > packages.json 39 | 40 | - name: Check build status 41 | run: > 42 | ./hydra-build-status.py --platforms ${{ matrix.platform }} --file packages.json > $GITHUB_STEP_SUMMARY 43 | -------------------------------------------------------------------------------- /.github/workflows/hydra-build-status-linux.yml: -------------------------------------------------------------------------------- 1 | name: Build status (Linux) 2 | 3 | on: 4 | schedule: 5 | - cron: '0 6 * * *' 6 | workflow_dispatch: 7 | 8 | env: 9 | NIX_CONFIG: "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" 10 | 11 | jobs: 12 | check-hydra-build: 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | platform: 18 | - x86_64-linux 19 | - aarch64-linux 20 | 21 | name: Check build status 22 | runs-on: ubuntu-22.04 23 | 24 | steps: 25 | - name: Check out repository 26 | uses: actions/checkout@v4 27 | with: 28 | token: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Install Nix 31 | uses: cachix/install-nix-action@v25 32 | 33 | - name: Generate packages file 34 | run: > 35 | nix eval --json --file maintainer-packages.nix --arg showBroken false packages 36 | | jq '[.. | objects | select(has("name")) | .name]' 37 | > packages.json 38 | 39 | - name: Check build status 40 | run: > 41 | ./hydra-build-status.py --platforms ${{ matrix.platform }} --file packages.json > $GITHUB_STEP_SUMMARY 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ivan Mincik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nix-utils 2 | 3 | * **nix-develop-interactive.bash:** interactively run nix build phases 4 | * **hydra-build-status.py:** report last Hydra build status for given packages 5 | * **maintainer-packages.nix:** list packages maintained by a person 6 | 7 | 8 | ## Build status 9 | 10 | [![Linux packages ](https://github.com/imincik/nix-utils/actions/workflows/hydra-build-status-linux.yml/badge.svg)](https://github.com/imincik/nix-utils/actions/workflows/hydra-build-status-linux.yml) 11 | [![Darwin packages](https://github.com/imincik/nix-utils/actions/workflows/hydra-build-status-darwin.yml/badge.svg)](https://github.com/imincik/nix-utils/actions/workflows/hydra-build-status-darwin.yml) 12 | 13 | 14 | ## Automatic updates (nixpkgs-update bot) 15 | 16 | * [Update queue](https://nixpkgs-update-logs.nix-community.org/~supervisor/queue.html) 17 | * [Update logs](https://nixpkgs-update-logs.nix-community.org/) 18 | 19 | 20 | ## Reports 21 | 22 | * [Outdated packages](https://repology.org/projects/?maintainer=ivan.mincik%40gmail.com&inrepo=nix_unstable&outdated=1) 23 | * [Vulnerable packages](https://repology.org/projects/?maintainer=ivan.mincik%40gmail.com&inrepo=nix_unstable&vulnerable=on) 24 | 25 | 26 | ## Notes 27 | 28 | * Convert new line separated list of packages to JSON file suitable for 29 | `hydra-build-status.py` 30 | ``` 31 | cat packages.txt | jq -s --raw-input '. | split("\n")' > packages.json 32 | hydra-build-status.py -f ./packages.json -p x86_64-linux 33 | ``` 34 | -------------------------------------------------------------------------------- /hydra-build-status.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env nix 2 | #! nix shell --impure --expr `` 3 | #! nix with (import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/bffc22e.tar.gz") {}); 4 | #! nix python3.withPackages (ps: with ps; [ requests beautifulsoup4 ]) 5 | #! nix `` 6 | #! nix --command python3 7 | 8 | # Report last Hydra build status for given platforms and packages. All supported 9 | # platforms will be checked, if no platform is specified. Fail with exit code 1 10 | # if status of any checked package is not good. 11 | 12 | # USAGE: 13 | # hydra-build-status.py --file --platforms=[PLATFORM,PLATFORM,...] 14 | 15 | import sys 16 | from getopt import getopt 17 | 18 | import json 19 | import requests 20 | import time 21 | import datetime 22 | from bs4 import BeautifulSoup 23 | 24 | USER_AGENT="hydra-build-status.py; Nix Geospatial team; Ivan Mincik (@imincik)" 25 | HYDRA_URL = "https://hydra.nixos.org" 26 | OK_STATUSES = ["Succeeded", "Cancelled", "No data", "No recent data"] 27 | 28 | opts, args = getopt(sys.argv[1:], "f:p:", ["file=", "platforms="]) 29 | 30 | # list of platforms 31 | platforms = ["x86_64-linux", "aarch64-linux", "x86_64-darwin", "aarch64-darwin"] 32 | for opt, arg in opts: 33 | if opt in ["-p", "--platforms"]: 34 | platforms = arg.split(",") 35 | 36 | elif opt in ["-f", "--file"]: 37 | pkgs_file = arg 38 | 39 | # list of packages 40 | with open(pkgs_file, 'r') as file: 41 | pkgs = json.load(file) 42 | 43 | 44 | exit_code = 0 45 | for platform in platforms: 46 | print(f"\n### PLATFORM: {platform}\n") 47 | 48 | print("| {f1: <50} | {f2: <20} | {f3: <80} |".format(f1="PACKAGE", f2="STATUS", f3="URL")) 49 | print("| {sep: <50} | {sep: <20} | {sep: <80} |".format(sep=20*"-")) 50 | 51 | for pkg in pkgs: 52 | headers = {'User-Agent': USER_AGENT} 53 | url = f"{HYDRA_URL}/job/nixpkgs/trunk/{pkg}.{platform}/all" 54 | page = requests.get(url, headers=headers) 55 | 56 | soup = BeautifulSoup(page.content, "html.parser") 57 | 58 | results_table = soup.find("table", class_="table") 59 | build_results = results_table.find_all("tr") 60 | 61 | if len(build_results) > 1: 62 | build_result = build_results[1].find_all("td") 63 | 64 | now_time = datetime.datetime.now() 65 | build_time = datetime.datetime.fromtimestamp(int(build_result[2].time["data-timestamp"])) 66 | 67 | # Ignore too old builds. 68 | # For example: https://hydra.nixos.org/job/nixpkgs/trunk/qgis.x86_64-darwin/all 69 | if (now_time - build_time).days < 365: 70 | build_status = build_result[0].img["alt"] 71 | else: 72 | build_status = "No recent data" 73 | else: 74 | build_status = "No data" 75 | 76 | print(f"| {pkg: <50} | {build_status: <20} | {url: <80} |") 77 | 78 | if build_status not in OK_STATUSES: 79 | exit_code = 1 80 | 81 | time.sleep(1) # don't overload Hydra 82 | 83 | sys.exit(exit_code) 84 | -------------------------------------------------------------------------------- /maintainer-packages.nix: -------------------------------------------------------------------------------- 1 | # List packages maintained by a person. 2 | 3 | # USAGE: 4 | # nix eval --json -f maintainer-packages.nix packages | jq '[.. | objects | select(has("name")) | .name]' 5 | 6 | 7 | { pkgs ? import 8 | (fetchTarball "https://github.com/NixOS/nixpkgs/archive/master.tar.gz") 9 | { config.allowBroken = true; config.allowUnfree = true; } 10 | 11 | , maintainer ? "imincik" 12 | 13 | , showBroken ? true # show broken packages 14 | }: 15 | 16 | let 17 | inherit (pkgs.lib.debug) traceVal; 18 | inherit (pkgs.lib) 19 | attrValues 20 | elem 21 | filterAttrsRecursive 22 | flatten 23 | isAttrs 24 | isDerivation 25 | map 26 | mapAttrs 27 | ; 28 | 29 | myMaintainer = pkgs.lib.maintainers.${maintainer}; 30 | 31 | isMaintainedBy = pkg: 32 | elem 33 | myMaintainer 34 | (pkg.meta.maintainers or [ ] ++ (flatten (map (x: x.members) (pkg.meta.teams or [ ])))); 35 | 36 | isDerivationRobust = pkg: 37 | let 38 | result = builtins.tryEval ( 39 | isDerivation pkg 40 | ); 41 | in 42 | if result.success then 43 | result.value 44 | else false; 45 | 46 | brokenFilter = pkg: 47 | let 48 | isBroken = pkg.meta.broken; 49 | in 50 | if showBroken then true 51 | else if isBroken == false then 52 | true 53 | else false; 54 | 55 | isPkgSet = pkg: 56 | let 57 | result = builtins.tryEval ( 58 | (isAttrs pkg) && (pkg.recurseForDerivations or false) 59 | ); 60 | in 61 | if result.success then 62 | result.value 63 | else false; 64 | 65 | recursePackageSet = pkgSetName: pkgs: 66 | mapAttrs 67 | (name: pkg: 68 | if isDerivationRobust pkg then 69 | if isMaintainedBy pkg && brokenFilter pkg then 70 | { name = "${if pkgSetName != null then pkgSetName + "." + name else name}"; } else null 71 | else if isPkgSet pkg then 72 | recursePackageSet name pkg 73 | else null 74 | ) 75 | pkgs; 76 | 77 | in 78 | { 79 | packages = 80 | attrValues 81 | (filterAttrsRecursive 82 | (n: v: v != null || v != { }) 83 | (recursePackageSet null pkgs) 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /nix-develop-interactive.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Interactively run Nix build phases. 4 | # This script must be sourced in Nix development shell environent ! 5 | 6 | # USAGE: 7 | # mkdir dev; cd dev 8 | # nix develop nixpkgs# 9 | # source nix-develop-interactive.bash 10 | 11 | 12 | SHELL=$(which bash) 13 | export SHELL 14 | 15 | # make sure that script is sourced 16 | (return 0 2>/dev/null) && sourced=1 || sourced=0 17 | if [ "$sourced" -eq 0 ]; then 18 | echo -e "ERROR, this script must be sourced (run 'source $0')." 19 | exit 1 20 | fi 21 | 22 | # make sure that script is sourced from nix shell 23 | (type -t genericBuild &>/dev/null) && in_nix_shell=1 || in_nix_shell=0 24 | if [ "$in_nix_shell" -eq 0 ]; then 25 | echo -e "ERROR, this script must be sourced from nix shell environment (run 'nix develop nixpkgs#')." 26 | return 1 27 | fi 28 | 29 | # phases detection taken from 30 | # https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh 31 | all_phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \ 32 | configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \ 33 | ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \ 34 | ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}"; 35 | 36 | # run phases 37 | for phase in ${all_phases[*]}; do 38 | phases_pretty=$(echo "${all_phases[*]}" | sed "s|$phase|**$phase**|g" | tr -s '[:blank:]') 39 | echo -e "\n>>> Phase: $phases_pretty" 40 | echo ">>> Command: phases=$phase genericBuild" 41 | echo ">>> Press ENTER to run, CTRL-C to exit" 42 | read 43 | 44 | phases=runPhase $phase 45 | done 46 | --------------------------------------------------------------------------------