├── .envrc ├── .github ├── dependabot.yml └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .mergify.yml ├── LICENSE.rst ├── README.md ├── bin ├── create-release.sh └── nix-update ├── default.nix ├── flake.lock ├── flake.nix ├── nix_update ├── __init__.py ├── errors.py ├── eval.py ├── git.py ├── options.py ├── update.py ├── utils.py ├── version │ ├── __init__.py │ ├── bitbucket.py │ ├── crate.py │ ├── gitea.py │ ├── github.py │ ├── gitlab.py │ ├── npm.py │ ├── pypi.py │ ├── rubygems.py │ ├── savannah.py │ ├── sourcehut.py │ └── version.py └── version_info.py ├── pyproject.toml ├── renovate.json ├── tests ├── __init__.py ├── conftest.py ├── consul.patch ├── test_bitbucket.py ├── test_branch.py ├── test_branch_commits_master.atom ├── test_branch_releases.atom ├── test_cargo_lock_expand.py ├── test_cargo_lock_generate.py ├── test_cargo_lock_update.py ├── test_cargo_vendor_deps.py ├── test_composer.py ├── test_composer_old.py ├── test_fetchurl_github_release.py ├── test_flake.py ├── test_git.py ├── test_gitea.py ├── test_github.py ├── test_gitlab.py ├── test_maven.py ├── test_mix.py ├── test_npm.py ├── test_npm_lock_generate.py ├── test_npm_package.py ├── test_nuget_deps_generate.py ├── test_pnpm.py ├── test_pypi.py ├── test_savanna.py ├── test_sourcehut.py ├── test_src_only.py ├── test_subpackage.py ├── test_update.py ├── test_version_regex_no_rev.py └── testpkgs │ ├── bitbucket.nix │ ├── cargo-lock-expand │ ├── Cargo.lock │ └── default.nix │ ├── cargo-lock-generate │ ├── simple │ │ ├── Cargo.lock │ │ └── default.nix │ ├── with-lockfile-metadata-path-outside-workspace │ │ ├── Cargo.lock │ │ └── default.nix │ └── with-lockfile-metadata-path │ │ ├── Cargo.lock │ │ └── default.nix │ ├── cargo-lock-update │ └── default.nix │ ├── cargo-vendor-deps │ ├── non-rust-package.nix │ └── rust-package.nix │ ├── composer-old.nix │ ├── composer.nix │ ├── crate.nix │ ├── default.nix │ ├── fetchurl-github-release.nix │ ├── flake.nix │ ├── gitea.nix │ ├── github-fetchtree.nix │ ├── github-no-release.nix │ ├── github-tag.nix │ ├── github.nix │ ├── gitlab.nix │ ├── let-bound-version.nix │ ├── maven.nix │ ├── mix.nix │ ├── net-news-wire.nix │ ├── npm-lock-generate │ ├── default.nix │ └── package-lock.json │ ├── npm-package.nix │ ├── npm.nix │ ├── nuget-deps-generate │ ├── default.nix │ └── deps.json │ ├── pnpm.nix │ ├── pypi.nix │ ├── savanna.nix │ ├── set.nix │ ├── sourcehut-snapshot.nix │ ├── sourcehut.nix │ └── subpackage.nix └── treefmt.nix /.envrc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | if ! has nix_direnv_version || ! nix_direnv_version 2.2.0; then 3 | source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.0/direnvrc" "sha256-5EwyKnkJNQeXrRkYbwwRBcXbibosCJqyIUuz9Xq+LRc=" 4 | fi 5 | use flake 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish a flake to flakestry" 2 | on: 3 | push: 4 | tags: 5 | - "v?[0-9]+.[0-9]+.[0-9]+" 6 | - "v?[0-9]+.[0-9]+" 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: "The existing tag to publish" 11 | type: "string" 12 | required: true 13 | jobs: 14 | publish-flake: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | id-token: "write" 18 | contents: "read" 19 | steps: 20 | - uses: flakestry/flakestry-publish@main 21 | with: 22 | version: "${{ inputs.tag || github.ref_name }}" 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Test" 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - staging 8 | - trying 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: cachix/install-nix-action@v31 15 | with: 16 | nix_path: nixpkgs=channel:nixos-unstable 17 | extra_nix_config: | 18 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 19 | - name: build 20 | run: nix build 21 | - name: Run tests 22 | run: nix develop -c pytest -s . 23 | env: 24 | # used by nix-update tests 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | .direnv 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | .dmypy.json 107 | 108 | # Nix artifacts 109 | result 110 | result-* 111 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | Zaraqueue_rules: 2 | - name: default 3 | merge_conditions: 4 | - check-success=tests 5 | - check-success=buildbot/nix-build 6 | batch_size: 5 7 | merge_method: rebase 8 | pull_request_rules: 9 | - name: merge using the merge queue 10 | conditions: 11 | - base=main 12 | - label~=merge-queue|dependencies 13 | actions: 14 | queue: {} 15 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright 2020 Jörg Thalheim 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nix-update 2 | 3 | Nix-update updates versions/source hashes of nix packages. It is designed to 4 | work with nixpkgs but also other package sets. 5 | 6 | ## Features 7 | 8 | - automatically figure out the latest version of packages from: 9 | - BitBucket 10 | - Codeberg 11 | - crates.io 12 | - Gitea 13 | - GitHub 14 | - GitLab 15 | - PyPi 16 | - RubyGems.org 17 | - Sourcehut 18 | - Savannah 19 | - update buildRustPackage's cargoHash/cargoSha256/cargoLock and cargoSetupHook's 20 | cargoDeps 21 | - update buildGoModule's vendorHash/vendorSha256 22 | - update buildNpmPackage's npmDepsHash and npmConfigHook's npmDeps 23 | - update buildComposerProject's vendorHash 24 | - update buildMavenPackage's mvnHash 25 | - update mixRelease's mixFodDeps 26 | - update fetchYarnDeps offlineCache output hash 27 | - update flake outputs (see `--flake`) 28 | - generate the following lockfile, Cargo.lock, package-lock.json (see 29 | `--generate-lockfile` and `--lockfile-metadata-path`) 30 | - build and run the resulting package (see `--build`, `--run` or `--shell` 31 | - commit updated files (see `--commit` flag) 32 | - run update scripts (`passthru.updateScript`, see `--use-update-script` flag) 33 | - run package tests (see `--test` flag) 34 | - specify the system to use (see `--system` flag) 35 | 36 | ## Installation 37 | 38 | `nix-update` is included in nixpkgs. 39 | 40 | To run without installing it, use: 41 | 42 | ```console 43 | $ nix-shell -p nix-update 44 | ``` 45 | 46 | To install it: 47 | 48 | ```console 49 | $ nix-env -f '' -iA nix-update 50 | ``` 51 | 52 | To run it from the git repository: 53 | 54 | ```console 55 | $ nix-build 56 | $ ./result/bin/nix-update 57 | ``` 58 | 59 | If you have nix flakes enabled you can also do: 60 | 61 | ```console 62 | $ nix run github:Mic92/nix-update 63 | ``` 64 | 65 | ## Usage 66 | 67 | First change to your directory containing the nix expression (Could be a nixpkgs 68 | or your own repository). Then run `nix-update` as follows 69 | 70 | ```console 71 | $ nix-update attribute [--version version] 72 | ``` 73 | 74 | If your package is defined in a flake use the `--flake` flag instead: 75 | 76 | ```console 77 | $ nix-update attribute --flake [--version version] 78 | ``` 79 | 80 | `nix-update` will then try to update either the 81 | `packages.{currentSystem}.{attribute}` or `{attribute}` output attribute of the 82 | given flake. To update a package in `legacyPackages`, pass the full path to that 83 | package including the platform: `legacyPackages.{platform}.{attribute}`. 84 | 85 | This example will fetch the latest github release: 86 | 87 | ```console 88 | $ nix-update nixpkgs-review 89 | ``` 90 | 91 | It is also possible to specify the version manually 92 | 93 | ```console 94 | $ nix-update --version=2.1.1 nixpkgs-review 95 | ``` 96 | 97 | To update an unstable package to the latest commit of the default branch: 98 | 99 | ```console 100 | $ nix-update --version=branch nixpkgs-review 101 | ``` 102 | 103 | To update an unstable package to the latest commit from a certain branch: 104 | 105 | ```console 106 | $ nix-update --version=branch=develop nixpkgs-review 107 | ``` 108 | 109 | To only update sources hashes without updating the version: 110 | 111 | ```console 112 | $ nix-update --version=skip nixpkgs-review 113 | ``` 114 | 115 | To extract version information from versions with prefixes or suffixes, a regex 116 | can be used 117 | 118 | ```console 119 | $ nix-update jq --version-regex 'jq-(.*)' 120 | ``` 121 | 122 | By default `nix-update` will locate the file that needs to be patched using the 123 | `src` attribute of a derivation. In some cases this heuristic is wrong. One can 124 | override the behavior like that: 125 | 126 | ```console 127 | $ nix-update hello --override-filename pkgs/applications/misc/hello/default.nix 128 | ``` 129 | 130 | The `nix-update` command checks for new releases of a package using the `src` 131 | attribute. However, in some cases a package may use a non-standard release URL 132 | that is not supported by `nix-update`, but still has a repository with release 133 | information. For example, the Signal Desktop package in Nixpkgs fetches updates 134 | from https://updates.signal.org/, but also publishes release information on its 135 | GitHub page. In such cases, use the `--url` parameter to direct nix-update to 136 | the correct repository: 137 | 138 | ```console 139 | nix-update --url https://github.com/signalapp/Signal-Desktop --override-filename pkgs/applications/networking/instant-messengers/signal-desktop/default.nix signal-desktop 140 | ``` 141 | 142 | With the `--shell`, `--build`, `--test` and `--run` flags the update can be 143 | tested. Additionally, the `--review` flag can be used to initiate a run of 144 | [nixpkgs-review](https://github.com/Mic92/nixpkgs-review), which will ensure all 145 | dependent packages can be built. 146 | 147 | In order to ensure consistent formatting, the `--format` flag will invoke 148 | [nixfmt](https://github.com/NixOS/nixfmt) (`nixfmt-rfc-style` in nixpkgs). 149 | 150 | ```console 151 | # Also runs nix-build 152 | $ nix-update --build nixpkgs-review 153 | # Also runs nix-build nixpkgs-review.tests 154 | $ nix-update --test nixpkgs-review 155 | # Also runs nix-shell 156 | $ nix-update --shell nixpkgs-review 157 | # Also runs nix run 158 | $ nix-update --run nixpkgs-review 159 | # Run `nixpkgs-review wip` to validate dependent packages 160 | $ nix-update --review nixpkgs-review 161 | # Format file 162 | $ nix-update --format nixpkgs-review 163 | ``` 164 | 165 | Nix-update also can optionally generate a commit message in the form 166 | `attribute: old_version -> new_version` with the applied version update: 167 | 168 | ```console 169 | $ nix-update --commit bitcoin-abc 170 | ... 171 | [master 53d68a6a5a9] bitcoin-abc: 0.21.1 -> 0.21.2 172 | 1 file changed, 2 insertions(+), 2 deletions(-) 173 | ``` 174 | 175 | By default, nix-update will attempt to update to the next stable version of a 176 | package. Alphas, betas, release candidates and similar unstable releases will be 177 | ignored. This can be affected by changing the parameter `version` from its 178 | default value `stable` to `unstable`. 179 | 180 | ```console 181 | $ nix-update sbt 182 | Not updating version, already 1.4.6 183 | 184 | $ nix-update sbt --version=unstable 185 | Update 1.4.6 -> 1.5.0-M1 in sbt 186 | ``` 187 | 188 | Nix-update can also run the `passthru.updateScript` defined by the package. 189 | 190 | ```console 191 | $ nix-update sbt --use-update-script 192 | ``` 193 | 194 | Arguments can be passed to `nix-shell maintainers/scripts/update.nix` like so 195 | 196 | ```console 197 | $ nix-update sbt --use-update-script --update-script-args "--argstr skip-prompt true" 198 | ``` 199 | 200 | ## Subpackages 201 | 202 | Some packages consist of multiple fixed-output derivations derived from the same 203 | upstream source. For example, a Go project with Go module dependencies might 204 | also include a JavaScript project with npm dependencies. 205 | 206 | To support such use cases, `nix-update` allows specifying subpackages directly 207 | in the command line. Consider a package accessible via the `some-package` 208 | attribute, which also provides a second fixed-output derivation as a subpackage 209 | named `web-ui`: 210 | 211 | ```nix 212 | { 213 | buildGoModule, 214 | fetchFromGitHub, 215 | buildNpmPackage, 216 | }: 217 | 218 | let 219 | pname = "some-package"; 220 | version = "1.0.0"; 221 | src = fetchFromGitHub { 222 | owner = "owner"; 223 | repo = "repo"; 224 | rev = "v${version}"; 225 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 226 | }; 227 | 228 | web-ui = buildNpmPackage rec { 229 | ... 230 | npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 231 | }; 232 | in 233 | buildGoModule rec { 234 | inherit 235 | web-ui 236 | pname 237 | version 238 | src; 239 | 240 | vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 241 | 242 | preBuild = '' 243 | cp -r ${web-ui}/* web/dist 244 | ''; 245 | } 246 | ``` 247 | 248 | You can update the package and its subpackage using `nix-update` as follows: 249 | 250 | ``` 251 | nix-update --subpackage web-ui some-package 252 | ``` 253 | 254 | The `--subpackage` option can be repeated to update multiple subpackages in the 255 | same derivation. 256 | 257 | ## Development setup 258 | 259 | First clone the repo to your preferred location (in the following, we assume 260 | `~/` - your home): 261 | 262 | ```console 263 | $ git clone https://github.com/Mic92/nix-update/ ~/git/nix-update 264 | ``` 265 | 266 | Then enter the dev shell: 267 | 268 | ```console 269 | $ cd ~/nix-update 270 | $ nix develop 271 | ``` 272 | 273 | Change to the repository that contains the nix files you want to update, i.e. 274 | nixpkgs: 275 | 276 | ```console 277 | $ cd nixpkgs 278 | ``` 279 | 280 | Now you can run `nix-update` just by specifying the full path to its executable 281 | wrapper: 282 | 283 | ```console 284 | $ ~/git/nix-update/bin/nix-update --commit hello 285 | ``` 286 | 287 | This project uses [treefmt-nix](https://github.com/numtide/treefmt-nix) for 288 | formatting. It can be run on the repository like that: 289 | 290 | ``` 291 | $ nix fmt 292 | ``` 293 | 294 | ## TODO 295 | 296 | - create pull requests 297 | 298 | ## Known Bugs 299 | 300 | nix-update might not work correctly if a file contains multiple packages as it 301 | performs naive search and replace to update version numbers. This might be a 302 | problem if: 303 | 304 | - `name` is used instead of `pname` and/or `${version}` is injected into `name`. 305 | 306 | Related discussions: 307 | 308 | - 309 | - 310 | 311 | ## Related projects: 312 | 313 | - [nixpkgs-update](https://github.com/ryantm/nixpkgs-update) is optimized for 314 | mass-updates in nixpkgs while nix-update is better suited for interactive 315 | usage that might require user-intervention i.e. fixing the build and testing 316 | the result. nix-update is also not limited to nixpkgs. 317 | -------------------------------------------------------------------------------- /bin/create-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" 6 | cd "$SCRIPT_DIR/.." 7 | 8 | version=${1:-} 9 | if [[ -z $version ]]; then 10 | echo "USAGE: $0 version" >&2 11 | exit 1 12 | fi 13 | 14 | if [[ "$(git symbolic-ref --short HEAD)" != "main" ]]; then 15 | echo "must be on main branch" >&2 16 | exit 1 17 | fi 18 | 19 | # ensure we are up-to-date 20 | uncommitted_changes=$(git diff --compact-summary) 21 | if [[ -n $uncommitted_changes ]]; then 22 | echo -e "There are uncommitted changes, exiting:\n${uncommitted_changes}" >&2 23 | exit 1 24 | fi 25 | git pull git@github.com:Mic92/nix-update main 26 | unpushed_commits=$(git log --format=oneline origin/main..main) 27 | if [[ $unpushed_commits != "" ]]; then 28 | echo -e "\nThere are unpushed changes, exiting:\n$unpushed_commits" >&2 29 | exit 1 30 | fi 31 | sed -i -e "s!version = \".*\";!version = \"${version}\";!" default.nix 32 | sed -i -e "s!^version = \".*\"\$!version = \"${version}\"!" pyproject.toml 33 | echo "VERSION = \"${version}\"" >nix_update/version_info.py 34 | git add pyproject.toml default.nix nix_update/version_info.py 35 | nix flake check -vL 36 | nix develop -c pytest -s . 37 | git commit -m "bump version ${version}" 38 | git tag "${version}" 39 | 40 | echo "now run 'git push --tags origin main'" 41 | -------------------------------------------------------------------------------- /bin/nix-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | sys.path.insert( 6 | 0, os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) 7 | ) 8 | 9 | from nix_update import main # NOQA 10 | 11 | if __name__ == "__main__": 12 | main() 13 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { }, 3 | }: 4 | pkgs.python3Packages.buildPythonApplication { 5 | pname = "nix-update"; 6 | version = "1.11.0"; 7 | src = ./.; 8 | pyproject = true; 9 | buildInputs = [ pkgs.makeWrapper ]; 10 | build-system = [ pkgs.python3Packages.setuptools ]; 11 | nativeBuildInputs = [ 12 | pkgs.nixVersions.stable 13 | pkgs.nix-prefetch-git 14 | ]; 15 | nativeCheckInputs = [ 16 | pkgs.python3Packages.pytest 17 | pkgs.python3Packages.pytest-xdist 18 | ]; 19 | checkPhase = '' 20 | PYTHONPATH= $out/bin/nix-update --help 21 | ''; 22 | makeWrapperArgs = [ 23 | "--prefix PATH" 24 | ":" 25 | (pkgs.lib.makeBinPath [ 26 | pkgs.nixVersions.stable 27 | pkgs.nixpkgs-review 28 | pkgs.nix-prefetch-git 29 | ]) 30 | ]; 31 | } 32 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-parts": { 4 | "inputs": { 5 | "nixpkgs-lib": [ 6 | "nixpkgs" 7 | ] 8 | }, 9 | "locked": { 10 | "lastModified": 1748821116, 11 | "narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=", 12 | "owner": "hercules-ci", 13 | "repo": "flake-parts", 14 | "rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1", 15 | "type": "github" 16 | }, 17 | "original": { 18 | "owner": "hercules-ci", 19 | "repo": "flake-parts", 20 | "type": "github" 21 | } 22 | }, 23 | "nixpkgs": { 24 | "locked": { 25 | "lastModified": 1748792178, 26 | "narHash": "sha256-BHmgfHlCJVNisJShVaEmfDIr/Ip58i/4oFGlD1iK6lk=", 27 | "owner": "NixOS", 28 | "repo": "nixpkgs", 29 | "rev": "5929de975bcf4c7c8d8b5ca65c8cd9ef9e44523e", 30 | "type": "github" 31 | }, 32 | "original": { 33 | "owner": "NixOS", 34 | "ref": "nixpkgs-unstable", 35 | "repo": "nixpkgs", 36 | "type": "github" 37 | } 38 | }, 39 | "root": { 40 | "inputs": { 41 | "flake-parts": "flake-parts", 42 | "nixpkgs": "nixpkgs", 43 | "treefmt-nix": "treefmt-nix" 44 | } 45 | }, 46 | "treefmt-nix": { 47 | "inputs": { 48 | "nixpkgs": [ 49 | "nixpkgs" 50 | ] 51 | }, 52 | "locked": { 53 | "lastModified": 1748243702, 54 | "narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=", 55 | "owner": "numtide", 56 | "repo": "treefmt-nix", 57 | "rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007", 58 | "type": "github" 59 | }, 60 | "original": { 61 | "owner": "numtide", 62 | "repo": "treefmt-nix", 63 | "type": "github" 64 | } 65 | } 66 | }, 67 | "root": "root", 68 | "version": 7 69 | } 70 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Swiss-knife for updating nix packages."; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | flake-parts.url = "github:hercules-ci/flake-parts"; 7 | flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 8 | 9 | treefmt-nix.url = "github:numtide/treefmt-nix"; 10 | treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; 11 | }; 12 | 13 | outputs = 14 | inputs@{ flake-parts, ... }: 15 | flake-parts.lib.mkFlake { inherit inputs; } ( 16 | { lib, ... }: 17 | { 18 | imports = [ ./treefmt.nix ]; 19 | systems = [ 20 | "aarch64-linux" 21 | "x86_64-linux" 22 | "riscv64-linux" 23 | 24 | "x86_64-darwin" 25 | "aarch64-darwin" 26 | ]; 27 | perSystem = 28 | { 29 | config, 30 | pkgs, 31 | self', 32 | ... 33 | }: 34 | { 35 | packages.nix-update = pkgs.callPackage ./. { }; 36 | packages.default = config.packages.nix-update; 37 | 38 | devShells.default = pkgs.mkShell { 39 | inputsFrom = [ config.packages.default ]; 40 | 41 | # Make tests use our pinned Nixpkgs 42 | env.NIX_PATH = "nixpkgs=${pkgs.path}"; 43 | }; 44 | 45 | checks = 46 | let 47 | packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages; 48 | devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells; 49 | in 50 | packages // devShells; 51 | }; 52 | } 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /nix_update/__init__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import shlex 4 | import shutil 5 | import sys 6 | import tempfile 7 | from pathlib import Path 8 | from typing import NoReturn 9 | 10 | from . import utils 11 | from .eval import CargoLockInSource, Package, eval_attr 12 | from .options import Options 13 | from .update import update 14 | from .utils import info, run 15 | from .version.version import VersionPreference 16 | 17 | 18 | def die(msg: str) -> NoReturn: 19 | print(msg, file=sys.stderr) 20 | sys.exit(1) 21 | 22 | 23 | def parse_args(args: list[str]) -> Options: 24 | parser = argparse.ArgumentParser() 25 | help_msg = "File to import rather than default.nix. Examples, ./release.nix" 26 | parser.add_argument("-f", "--file", default="./.", help=help_msg) 27 | parser.add_argument( 28 | "-q", 29 | "--quiet", 30 | action="store_true", 31 | help="Hide informational messages", 32 | ) 33 | parser.add_argument( 34 | "-F", 35 | "--flake", 36 | action="store_true", 37 | help="Update a flake attribute instead", 38 | ) 39 | parser.add_argument("--build", action="store_true", help="build the package") 40 | parser.add_argument( 41 | "--test", 42 | action="store_true", 43 | help="Run package's `passthru.tests`", 44 | ) 45 | parser.add_argument( 46 | "--review", 47 | action="store_true", 48 | help="Run `nixpkgs-review wip`", 49 | ) 50 | parser.add_argument("--format", action="store_true", help="Run `nixfmt`") 51 | parser.add_argument( 52 | "--commit", 53 | action="store_true", 54 | help="Commit the updated package", 55 | ) 56 | parser.add_argument( 57 | "-u", 58 | "--use-update-script", 59 | action="store_true", 60 | help="Use passthru.updateScript instead if possible", 61 | ) 62 | parser.add_argument( 63 | "--update-script-args", 64 | default=[], 65 | type=shlex.split, 66 | help="Args to pass to `nix-shell maintainers/scripts/update.nix`, subject to splitting.", 67 | ) 68 | parser.add_argument( 69 | "--url", 70 | help="URL to the repository to check for a release instead of using the URL in the src attribute of the package", 71 | ) 72 | parser.add_argument( 73 | "--print-commit-message", 74 | action="store_true", 75 | help="Print commit message to stdout (implies --quiet)", 76 | ) 77 | parser.add_argument( 78 | "--write-commit-message", 79 | metavar="FILE", 80 | help="Write commit message to FILE", 81 | ) 82 | parser.add_argument( 83 | "-vr", 84 | "--version-regex", 85 | help="Regex to extract version with, i.e. 'jq-(.*)'", 86 | default="(.*)", 87 | ) 88 | parser.add_argument( 89 | "--run", 90 | action="store_true", 91 | help="provide a shell based on `nix run` with the package in $PATH", 92 | ) 93 | parser.add_argument( 94 | "--shell", 95 | action="store_true", 96 | help="provide a shell with the package", 97 | ) 98 | parser.add_argument( 99 | "--version", 100 | nargs="?", 101 | default=VersionPreference.STABLE, 102 | help="Version to update to. Possible values are: " 103 | + ", ".join(VersionPreference), 104 | ) 105 | parser.add_argument( 106 | "--override-filename", 107 | nargs="?", 108 | help="Set filename where nix-update will update version/hash", 109 | default=None, 110 | ) 111 | parser.add_argument( 112 | "--system", 113 | help="The system used to to calculate the hash and run other nix commands", 114 | default=None, 115 | ) 116 | 117 | default_attribute = os.getenv("UPDATE_NIX_ATTR_PATH") 118 | parser.add_argument( 119 | "attribute", 120 | default=default_attribute, 121 | nargs="?" if default_attribute else None, # type: ignore[arg-type] 122 | help="""Attribute name within the file evaluated (defaults to environment variable "UPDATE_NIX_ATTR_PATH")""", 123 | ) 124 | parser.add_argument( 125 | "--generate-lockfile", 126 | action="store_true", 127 | help="Generate lockfile and replace vendored one", 128 | ) 129 | parser.add_argument( 130 | "--lockfile-metadata-path", 131 | help="Path to the directory containing the metadata (e.g. Cargo.toml) referenced by the lockfile", 132 | default=".", 133 | ) 134 | parser.add_argument( 135 | "-s", 136 | "--subpackage", 137 | action="append", 138 | help="Attribute of a subpackage that nix-update should try to update hashes for", 139 | default=None, 140 | ) 141 | parser.add_argument( 142 | "--src-only", 143 | help="Only update the source, not dependencies such as npmDeps, cargoDeps or nugetDeps", 144 | action="store_true", 145 | ) 146 | parser.add_argument( 147 | "--option", 148 | help="Nix option to set", 149 | action="append", 150 | nargs=2, 151 | metavar=("name", "value"), 152 | default=[], 153 | ) 154 | 155 | a = parser.parse_args(args) 156 | extra_flags = ["--extra-experimental-features", "flakes nix-command"] 157 | if a.system: 158 | extra_flags.extend(["--system", a.system]) 159 | for name, value in a.option: 160 | extra_flags.extend(["--option", name, value]) 161 | 162 | return Options( 163 | import_path=os.path.realpath(a.file), 164 | quiet=a.quiet or a.print_commit_message, 165 | flake=a.flake, 166 | build=a.build, 167 | commit=a.commit, 168 | use_update_script=a.use_update_script, 169 | update_script_args=a.update_script_args, 170 | subpackages=a.subpackage, 171 | url=a.url, 172 | print_commit_message=a.print_commit_message, 173 | write_commit_message=a.write_commit_message, 174 | run=a.run, 175 | shell=a.shell, 176 | version=a.version, 177 | version_preference=VersionPreference.from_str(a.version), 178 | attribute=a.attribute, 179 | test=a.test, 180 | version_regex=a.version_regex, 181 | review=a.review, 182 | format=a.format, 183 | override_filename=a.override_filename, 184 | system=a.system, 185 | generate_lockfile=a.generate_lockfile, 186 | lockfile_metadata_path=a.lockfile_metadata_path, 187 | src_only=a.src_only, 188 | extra_flags=extra_flags, 189 | ) 190 | 191 | 192 | def nix_shell(options: Options) -> None: 193 | if options.flake: 194 | run( 195 | [ 196 | "nix", 197 | "shell", 198 | f"{options.import_path}#{options.attribute}", 199 | *options.extra_flags, 200 | ], 201 | stdout=None, 202 | check=False, 203 | ) 204 | else: 205 | expr = f"let pkgs = import {options.escaped_import_path} {{}}; in pkgs.mkShell {{ buildInputs = [ pkgs.{options.escaped_attribute} ]; }}" 206 | with tempfile.TemporaryDirectory() as d: 207 | path = Path(d) / "default.nix" 208 | path.write_text(expr) 209 | run( 210 | ["nix-shell", str(path), *options.extra_flags], 211 | stdout=None, 212 | check=False, 213 | ) 214 | 215 | 216 | def git_has_diff(git_dir: str, package: Package) -> bool: 217 | diff = run(["git", "-C", git_dir, "diff", "--", package.filename]) 218 | return len(diff.stdout) > 0 219 | 220 | 221 | def format_commit_message(package: Package) -> str: 222 | new_version = getattr(package.new_version, "number", None) 223 | if ( 224 | new_version 225 | and package.old_version != new_version 226 | and new_version.startswith("v") 227 | ): 228 | new_version = new_version[1:] 229 | msg = f"{package.attribute}: {package.old_version} -> {new_version}" 230 | if package.diff_url: 231 | msg += f"\n\nDiff: {package.diff_url}" 232 | if package.changelog: 233 | msg += f"\n\nChangelog: {package.changelog}" 234 | return msg 235 | 236 | 237 | def git_commit(git_dir: str, package: Package) -> None: 238 | msg = format_commit_message(package) 239 | new_version = package.new_version 240 | files_changed = [package.filename] 241 | if isinstance(package.cargo_lock, CargoLockInSource): 242 | files_changed.append(package.cargo_lock.path) 243 | if new_version and ( 244 | package.old_version != new_version.number 245 | or (new_version.rev and new_version.rev != package.rev) 246 | ): 247 | run( 248 | [ 249 | "git", 250 | "-C", 251 | git_dir, 252 | "commit", 253 | "--verbose", 254 | "--message", 255 | msg, 256 | *files_changed, 257 | ], 258 | stdout=None, 259 | ) 260 | else: 261 | with tempfile.NamedTemporaryFile(mode="w") as f: 262 | f.write(msg) 263 | f.flush() 264 | run( 265 | [ 266 | "git", 267 | "-C", 268 | git_dir, 269 | "commit", 270 | "--verbose", 271 | "--template", 272 | f.name, 273 | *files_changed, 274 | ], 275 | stdout=None, 276 | ) 277 | 278 | 279 | def print_commit_message(package: Package) -> None: 280 | print(format_commit_message(package)) 281 | print("\n") 282 | 283 | 284 | def write_commit_message(path: str, package: Package) -> None: 285 | with Path(path).open("w") as f: 286 | f.write(format_commit_message(package)) 287 | f.write("\n") 288 | 289 | 290 | def find_git_root(path: str) -> str | None: 291 | prefix = [path] 292 | release_nix = [".git"] 293 | while True: 294 | root_path = Path(*prefix) 295 | release_nix_path = root_path.joinpath(*release_nix) 296 | if release_nix_path.exists(): 297 | return str(root_path) 298 | if root_path.resolve() == Path("/"): 299 | return None 300 | prefix.append("..") 301 | 302 | 303 | def validate_git_dir(import_path: str) -> str: 304 | path = Path(import_path) 305 | if path.is_dir(): 306 | git_dir = find_git_root(import_path) 307 | else: 308 | git_dir = find_git_root(str(path.parent)) 309 | 310 | if git_dir is None: 311 | die(f"Could not find a git repository relative to {import_path}") 312 | 313 | return git_dir 314 | 315 | 316 | def nix_run(options: Options) -> None: 317 | cmd = ["nix", "shell", "-L", *options.extra_flags] 318 | 319 | if options.flake: 320 | cmd.append(f"{options.import_path}#{options.attribute}") 321 | else: 322 | cmd.extend(["-f", options.import_path, options.attribute]) 323 | run( 324 | cmd, 325 | stdout=None, 326 | check=False, 327 | ) 328 | 329 | 330 | def nix_build_tool() -> str: 331 | "Return `nom` if found in $PATH" 332 | if shutil.which("nom"): 333 | return "nom" 334 | return "nix" 335 | 336 | 337 | def nix_build(options: Options) -> None: 338 | cmd = [nix_build_tool(), "build", "-L", *options.extra_flags] 339 | if options.flake: 340 | cmd.append(f"{options.import_path}#{options.attribute}") 341 | else: 342 | cmd.extend(["-f", options.import_path, options.attribute]) 343 | run(cmd, stdout=None) 344 | 345 | 346 | def nix_test(opts: Options, package: Package) -> None: 347 | if not package.tests: 348 | die(f"Package '{package.name}' does not define any tests") 349 | 350 | cmd = [nix_build_tool(), "build", "-L", *opts.extra_flags] 351 | 352 | if opts.flake: 353 | cmd.extend( 354 | [ 355 | f"{opts.import_path}#{package.attribute}.tests.{t}" 356 | for t in package.tests 357 | ], 358 | ) 359 | else: 360 | cmd.extend(["-f", opts.import_path]) 361 | for t in package.tests: 362 | cmd.append("-A") 363 | cmd.append(f"{package.attribute}.tests.{t}") 364 | run(cmd, stdout=None) 365 | 366 | 367 | def nixpkgs_review() -> None: 368 | cmd = [ 369 | "nixpkgs-review", 370 | "wip", 371 | ] 372 | run(cmd, stdout=None) 373 | 374 | 375 | def main(args: list[str] = sys.argv[1:]) -> None: 376 | options = parse_args(args) 377 | if options.quiet: 378 | utils.LOG_LEVEL = utils.LogLevel.WARNING 379 | 380 | if not Path(options.import_path).exists(): 381 | die(f"path {options.import_path} does not exist") 382 | 383 | git_dir = None 384 | if options.commit or options.review: 385 | git_dir = validate_git_dir(options.import_path) 386 | 387 | package = update(options) 388 | 389 | if package.maintainers: 390 | print("Package maintainers:") 391 | for maintainer in package.maintainers: 392 | print( 393 | f" - {maintainer['name']}" 394 | + (f" (@{maintainer['github']})" if "github" in maintainer else ""), 395 | ) 396 | 397 | if options.build: 398 | nix_build(options) 399 | 400 | if options.run: 401 | nix_run(options) 402 | 403 | if options.shell: 404 | nix_shell(options) 405 | 406 | if not git_dir: 407 | git_dir = find_git_root(options.import_path) 408 | 409 | changes_detected = not git_dir or git_has_diff(git_dir, package) 410 | 411 | if not changes_detected: 412 | info("No changes detected, skipping remaining steps") 413 | return 414 | 415 | if options.test: 416 | nix_test(options, package) 417 | 418 | if options.review: 419 | if options.flake: 420 | print("--review is unsupported with --flake") 421 | else: 422 | nixpkgs_review() 423 | 424 | if options.format: 425 | run(["nixfmt", package.filename], stdout=None) 426 | 427 | if options.commit: 428 | assert git_dir is not None 429 | if package.changelog: 430 | # If we have a changelog we will re-eval the package in case it has changed 431 | package.changelog = eval_attr(options).changelog 432 | git_commit(git_dir, package) 433 | 434 | if options.print_commit_message: 435 | print_commit_message(package) 436 | 437 | if options.write_commit_message is not None: 438 | write_commit_message(options.write_commit_message, package) 439 | 440 | 441 | if __name__ == "__main__": 442 | main() 443 | -------------------------------------------------------------------------------- /nix_update/errors.py: -------------------------------------------------------------------------------- 1 | class UpdateError(Exception): 2 | pass 3 | 4 | 5 | class VersionError(UpdateError): 6 | pass 7 | -------------------------------------------------------------------------------- /nix_update/eval.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from dataclasses import InitVar, dataclass, field 4 | from textwrap import dedent, indent 5 | from typing import Any, Literal 6 | from urllib.parse import ParseResult, urlparse 7 | 8 | from .errors import UpdateError 9 | from .options import Options 10 | from .utils import run 11 | from .version.version import Version, VersionPreference 12 | 13 | 14 | @dataclass 15 | class Position: 16 | file: str 17 | line: int 18 | column: int 19 | 20 | 21 | class CargoLock: 22 | pass 23 | 24 | 25 | class NoCargoLock(CargoLock): 26 | pass 27 | 28 | 29 | class CargoLockInSource(CargoLock): 30 | def __init__(self, path: str) -> None: 31 | self.path = path 32 | 33 | 34 | class CargoLockInStore(CargoLock): 35 | pass 36 | 37 | 38 | @dataclass 39 | class Package: 40 | attribute: str 41 | import_path: InitVar[str] 42 | name: str 43 | pname: str 44 | old_version: str 45 | filename: str 46 | line: int 47 | urls: list[str] | None 48 | url: str | None 49 | src_homepage: str | None 50 | changelog: str | None 51 | maintainers: list[dict[str, str]] | None 52 | rev: str | None 53 | tag: str | None 54 | hash: str | None 55 | go_modules: str | None 56 | go_modules_old: str | None 57 | cargo_deps: str | None 58 | cargo_vendor_deps: str | None 59 | npm_deps: str | None 60 | pnpm_deps: str | None 61 | yarn_deps: str | None 62 | yarn_deps_old: str | None 63 | composer_deps: str | None 64 | composer_deps_old: str | None 65 | maven_deps: str | None 66 | mix_deps: str | None 67 | has_nuget_deps: bool 68 | tests: list[str] 69 | has_update_script: bool 70 | 71 | raw_version_position: InitVar[dict[str, Any] | None] 72 | raw_cargo_lock: InitVar[Literal[False] | str | None] 73 | 74 | parsed_url: ParseResult | None = None 75 | new_version: Version | None = None 76 | version_position: Position | None = field(init=False) 77 | cargo_lock: CargoLock = field(init=False) 78 | diff_url: str | None = None 79 | 80 | def __post_init__( 81 | self, 82 | import_path: str, 83 | raw_version_position: dict[str, Any] | None, 84 | raw_cargo_lock: Literal[False] | str | None, 85 | ) -> None: 86 | url = self.url or (self.urls[0] if self.urls else None) 87 | if url: 88 | self.parsed_url = urlparse(url) 89 | if raw_version_position is None: 90 | self.version_position = None 91 | else: 92 | self.version_position = Position(**raw_version_position) 93 | if self.filename: 94 | self.version_position.file = self.filename 95 | 96 | if raw_cargo_lock is None: 97 | self.cargo_lock = NoCargoLock() 98 | elif raw_cargo_lock is False or not os.path.realpath(raw_cargo_lock).startswith( 99 | import_path, 100 | ): 101 | self.cargo_lock = CargoLockInStore() 102 | else: 103 | self.cargo_lock = CargoLockInSource(raw_cargo_lock) 104 | 105 | 106 | def eval_expression( 107 | escaped_import_path: str, 108 | attr: str, 109 | flake: bool, 110 | system: str | None, 111 | override_filename: str | None, 112 | ) -> str: 113 | system = f'"{system}"' if system else "builtins.currentSystem" 114 | 115 | if flake: 116 | sanitize_position = ( 117 | f""" 118 | sanitizePosition = {{ file, ... }}@pos: 119 | assert substring 0 outPathLen file != outPath 120 | -> throw "${{file}} is not in ${{outPath}}"; 121 | pos // {{ file = {escaped_import_path} + substring outPathLen (stringLength file - outPathLen) file; }}; 122 | """ 123 | if override_filename is None 124 | else """ 125 | sanitizePosition = x: x; 126 | """ 127 | ).strip() 128 | 129 | let_bindings = f""" 130 | inherit (builtins) getFlake stringLength substring; 131 | currentSystem = {system}; 132 | flake = getFlake {escaped_import_path}; 133 | pkg = flake.packages.${{currentSystem}}.{attr} or flake.{attr}; 134 | inherit (flake) outPath; 135 | outPathLen = stringLength outPath; 136 | {sanitize_position} 137 | """ 138 | else: 139 | let_bindings = f""" 140 | pkgs = import {escaped_import_path}; 141 | args = builtins.functionArgs pkgs; 142 | inputs = (if args ? system then {{ system = {system}; }} else {{}}) // 143 | (if args ? overlays then {{ overlays = [ ]; }} else {{}}); 144 | pkg = (pkgs inputs).{attr}; 145 | sanitizePosition = x: x; 146 | """ 147 | 148 | has_update_script = "pkg.passthru.updateScript or null != null" 149 | 150 | return f""" 151 | let 152 | {indent(dedent(let_bindings), " ")} 153 | positionFromMeta = pkg: let 154 | parts = builtins.match "(.*):([0-9]+)" pkg.meta.position; 155 | in {{ 156 | file = builtins.elemAt parts 0; 157 | line = builtins.fromJSON (builtins.elemAt parts 1); 158 | }}; 159 | 160 | raw_version_position = sanitizePosition (builtins.unsafeGetAttrPos "version" pkg); 161 | 162 | position = if pkg ? isRubyGem then 163 | raw_version_position 164 | else if pkg ? isPhpExtension then 165 | raw_version_position 166 | else if (builtins.unsafeGetAttrPos "src" pkg) != null then 167 | sanitizePosition (builtins.unsafeGetAttrPos "src" pkg) 168 | else 169 | sanitizePosition (positionFromMeta pkg); 170 | in {{ 171 | name = pkg.name; 172 | pname = pkg.pname; 173 | old_version = pkg.version or (builtins.parseDrvName pkg.name).version; 174 | inherit raw_version_position; 175 | filename = position.file; 176 | line = position.line; 177 | urls = pkg.src.urls or null; 178 | url = pkg.src.url or null; 179 | rev = pkg.src.rev or null; 180 | tag = pkg.src.tag or null; 181 | hash = pkg.src.outputHash or null; 182 | go_modules = pkg.goModules.outputHash or null; 183 | go_modules_old = pkg.go-modules.outputHash or null; 184 | cargo_deps = pkg.cargoDeps.outputHash or null; 185 | cargo_vendor_deps = pkg.cargoDeps.vendorStaging.outputHash or null; 186 | raw_cargo_lock = 187 | if pkg ? cargoDeps.lockFile then 188 | let 189 | inherit (pkg.cargoDeps) lockFile; 190 | res = builtins.tryEval (sanitizePosition {{ 191 | file = toString lockFile; 192 | }}); 193 | in 194 | if res.success then res.value.file else false 195 | else 196 | null; 197 | composer_deps = pkg.composerVendor.outputHash or null; 198 | composer_deps_old = pkg.composerRepository.outputHash or null; 199 | npm_deps = pkg.npmDeps.outputHash or null; 200 | pnpm_deps = pkg.pnpmDeps.outputHash or null; 201 | yarn_deps = pkg.yarnOfflineCache.outputHash or null; 202 | yarn_deps_old = pkg.offlineCache.outputHash or null; 203 | maven_deps = pkg.fetchedMavenDeps.outputHash or null; 204 | has_nuget_deps = pkg ? nugetDeps; 205 | mix_deps = pkg.mixFodDeps.outputHash or null; 206 | tests = builtins.attrNames (pkg.passthru.tests or {{}}); 207 | has_update_script = {has_update_script}; 208 | src_homepage = pkg.src.meta.homepage or null; 209 | changelog = pkg.meta.changelog or null; 210 | maintainers = pkg.meta.maintainers or null; 211 | }}""" 212 | 213 | 214 | def eval_attr(opts: Options) -> Package: 215 | expr = eval_expression( 216 | opts.escaped_import_path, 217 | opts.escaped_attribute, 218 | opts.flake, 219 | opts.system, 220 | opts.override_filename, 221 | ) 222 | cmd = ["nix", "eval", "--json", "--impure", "--expr", expr, *opts.extra_flags] 223 | res = run(cmd) 224 | out = json.loads(res.stdout) 225 | if opts.override_filename is not None: 226 | out["filename"] = opts.override_filename 227 | if opts.url is not None: 228 | out["url"] = opts.url 229 | package = Package(attribute=opts.attribute, import_path=opts.import_path, **out) 230 | if opts.version_preference != VersionPreference.SKIP and package.old_version == "": 231 | msg = f"Nix's builtins.parseDrvName could not parse the version from {package.name}" 232 | raise UpdateError(msg) 233 | 234 | return package 235 | -------------------------------------------------------------------------------- /nix_update/git.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from .utils import run 4 | 5 | 6 | def old_version_from_diff(diff: str, linenumber: int, new_version: str) -> str | None: 7 | current_line = 0 8 | old_str = None 9 | new_str = None 10 | regex = re.compile(r"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@$") 11 | for line in diff.split("\n"): 12 | match = regex.match(line) 13 | if match: 14 | current_line = int(match.group(3)) 15 | elif line.startswith("~"): 16 | current_line += 1 17 | if current_line > linenumber: 18 | return None 19 | elif linenumber == current_line and line.startswith("-"): 20 | old_str = line[1:] 21 | elif linenumber == current_line and line.startswith("+"): 22 | if new_version not in line: 23 | old_str = None 24 | else: 25 | new_str = line[1:] 26 | break 27 | if not new_str or not old_str: 28 | return None 29 | idx = new_str.index(new_version) 30 | prefix = new_str[:idx] 31 | suffix = new_str[idx + len(new_version) :] 32 | return old_str.lstrip(prefix).rstrip(suffix) 33 | 34 | 35 | def old_version_from_git( 36 | filename: str, 37 | linenumber: int, 38 | new_version: str, 39 | ) -> str | None: 40 | proc = run( 41 | ["git", "diff", "--color=never", "--word-diff=porcelain", "--", filename], 42 | ) 43 | assert proc.stdout is not None 44 | if len(proc.stdout) == 0: 45 | return None 46 | return old_version_from_diff(proc.stdout, linenumber, new_version) 47 | -------------------------------------------------------------------------------- /nix_update/options.py: -------------------------------------------------------------------------------- 1 | import json 2 | from dataclasses import dataclass, field 3 | from pathlib import Path 4 | 5 | from .version.version import VersionPreference 6 | 7 | 8 | @dataclass 9 | class Options: 10 | attribute: str 11 | quiet: bool = False 12 | flake: bool = False 13 | version: str = "stable" 14 | version_preference: VersionPreference = VersionPreference.STABLE 15 | version_regex: str = "(.*)" 16 | import_path: str = str(Path.cwd()) 17 | subpackages: list[str] | None = None 18 | override_filename: str | None = None 19 | url: str | None = None 20 | commit: bool = False 21 | use_update_script: bool = False 22 | update_script_args: list[str] = field(default_factory=list) 23 | print_commit_message: bool = False 24 | write_commit_message: str | None = None 25 | shell: bool = False 26 | run: bool = False 27 | build: bool = False 28 | test: bool = False 29 | review: bool = False 30 | format: bool = False 31 | system: str | None = None 32 | generate_lockfile: bool = False 33 | lockfile_metadata_path: str = "." 34 | src_only: bool = False 35 | extra_flags: list[str] = field(default_factory=list) 36 | 37 | def __post_init__(self) -> None: 38 | self.escaped_attribute = ".".join(map(json.dumps, self.attribute.split("."))) 39 | self.escaped_import_path = json.dumps(self.import_path) 40 | -------------------------------------------------------------------------------- /nix_update/update.py: -------------------------------------------------------------------------------- 1 | import fileinput 2 | import json 3 | import re 4 | import shutil 5 | import subprocess 6 | import sys 7 | import tempfile 8 | import textwrap 9 | import tomllib 10 | from collections.abc import Iterator 11 | from concurrent.futures import ThreadPoolExecutor 12 | from contextlib import contextmanager 13 | from copy import deepcopy 14 | from pathlib import Path 15 | 16 | from .errors import UpdateError 17 | from .eval import CargoLockInSource, CargoLockInStore, Package, eval_attr 18 | from .git import old_version_from_git 19 | from .options import Options 20 | from .utils import info, run 21 | from .version import fetch_latest_version 22 | from .version.gitlab import GITLAB_API 23 | from .version.version import Version, VersionPreference 24 | 25 | 26 | def replace_version(package: Package) -> bool: 27 | assert package.new_version is not None 28 | old_rev_tag = package.rev or package.tag 29 | old_version = package.old_version 30 | new_version = package.new_version.number 31 | new_version = new_version.removeprefix("v") 32 | 33 | changed = old_version != new_version or ( 34 | package.new_version.rev is not None and package.new_version.rev != old_rev_tag 35 | ) 36 | 37 | if changed: 38 | info(f"Update {old_version} -> {new_version} in {package.filename}") 39 | version_string_in_version_declaration = False 40 | if package.version_position is not None: 41 | with Path(package.filename).open() as f: 42 | for i, line in enumerate(f, 1): 43 | if package.version_position.line == i: 44 | version_string_in_version_declaration = old_version in line 45 | break 46 | with fileinput.FileInput(package.filename, inplace=True) as f: 47 | for i, original_line in enumerate(f, 1): 48 | modified_line = original_line 49 | if old_rev_tag is not None and package.new_version.rev: 50 | modified_line = modified_line.replace( 51 | old_rev_tag, 52 | package.new_version.rev, 53 | ) 54 | if not version_string_in_version_declaration or ( 55 | package.version_position is not None 56 | and package.version_position.line == i 57 | ): 58 | modified_line = modified_line.replace( 59 | f'"{old_version}"', 60 | f'"{new_version}"', 61 | ) 62 | print(modified_line, end="") 63 | else: 64 | info(f"Not updating version, already {old_version}") 65 | 66 | return changed 67 | 68 | 69 | def to_sri(hashstr: str) -> str: 70 | if "-" in hashstr: 71 | return hashstr 72 | length = len(hashstr) 73 | if length == 32: 74 | prefix = "md5:" 75 | elif length == 40: 76 | # could be also base32 == 32, but we ignore this case and hope no one is using it 77 | prefix = "sha1:" 78 | elif length in (64, 52): 79 | prefix = "sha256:" 80 | elif length in (103, 128): 81 | prefix = "sha512:" 82 | else: 83 | return hashstr 84 | 85 | cmd = [ 86 | "nix", 87 | "--extra-experimental-features", 88 | "nix-command", 89 | "hash", 90 | "to-sri", 91 | f"{prefix}{hashstr}", 92 | ] 93 | proc = run(cmd) 94 | return proc.stdout.rstrip("\n") 95 | 96 | 97 | def replace_hash(filename: str, current: str, target: str) -> None: 98 | normalized_hash = to_sri(target) 99 | if to_sri(current) != normalized_hash: 100 | with fileinput.FileInput(filename, inplace=True) as f: 101 | for original_line in f: 102 | modified_line = original_line.replace(current, normalized_hash) 103 | print(modified_line, end="") 104 | 105 | 106 | def get_package(opts: Options) -> str: 107 | return ( 108 | f"(let flake = builtins.getFlake {opts.escaped_import_path}; in flake.packages.${{builtins.currentSystem}}.{opts.escaped_attribute} or flake.{opts.escaped_attribute})" 109 | if opts.flake 110 | else f"(import {opts.escaped_import_path} {disable_check_meta(opts)}).{opts.escaped_attribute}" 111 | ) 112 | 113 | 114 | def nix_prefetch(opts: Options, attr: str) -> str: 115 | expr = f"{get_package(opts)}.{attr}" 116 | 117 | extra_env: dict[str, str] = {} 118 | tempdir: tempfile.TemporaryDirectory[str] | None = None 119 | stderr = "" 120 | if extra_env.get("XDG_RUNTIME_DIR") is None: 121 | tempdir = tempfile.TemporaryDirectory() 122 | extra_env["XDG_RUNTIME_DIR"] = tempdir.name 123 | try: 124 | res = run( 125 | [ 126 | "nix-build", 127 | "--expr", 128 | f'let src = {expr}; in (src.overrideAttrs or (f: src // f src)) (_: {{ outputHash = ""; outputHashAlgo = "sha256"; }})', 129 | *opts.extra_flags, 130 | ], 131 | extra_env=extra_env, 132 | stderr=subprocess.PIPE, 133 | check=False, 134 | ) 135 | stderr = res.stderr.strip() 136 | # got: xxx 137 | # expected 'xxx' but got 'xxx' 138 | regex = re.compile(r".*got(:|\s)\s*'?([^']*)('|$)") 139 | got = "" 140 | for line in stderr.split("\n"): 141 | if match := regex.fullmatch(line): 142 | got = match[2] 143 | break 144 | finally: 145 | if tempdir: 146 | tempdir.cleanup() 147 | 148 | if got == "": 149 | print(stderr, file=sys.stderr) 150 | msg = f"failed to retrieve hash when trying to update {opts.attribute}.{attr}" 151 | raise UpdateError(msg) 152 | return got 153 | 154 | 155 | def disable_check_meta(opts: Options) -> str: 156 | return f'(if (builtins.hasAttr "config" (builtins.functionArgs (import {opts.escaped_import_path}))) then {{ config.checkMeta = false; overlays = []; }} else {{ }})' 157 | 158 | 159 | def git_prefetch(x: tuple[str, tuple[str, str]]) -> tuple[str, str]: 160 | rev, (key, url) = x 161 | res = run(["nix-prefetch-git", url, rev, "--fetch-submodules"]) 162 | return key, to_sri(json.loads(res.stdout)["sha256"]) 163 | 164 | 165 | def update_src_hash(opts: Options, filename: str, current_hash: str) -> None: 166 | target_hash = nix_prefetch(opts, "src") 167 | replace_hash(filename, current_hash, target_hash) 168 | 169 | 170 | def update_go_modules_hash(opts: Options, filename: str, current_hash: str) -> None: 171 | target_hash = nix_prefetch(opts, "goModules") 172 | replace_hash(filename, current_hash, target_hash) 173 | 174 | 175 | def update_go_modules_hash_old(opts: Options, filename: str, current_hash: str) -> None: 176 | target_hash = nix_prefetch(opts, "go-modules") 177 | replace_hash(filename, current_hash, target_hash) 178 | 179 | 180 | def update_cargo_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 181 | target_hash = nix_prefetch(opts, "cargoDeps") 182 | replace_hash(filename, current_hash, target_hash) 183 | 184 | 185 | def update_cargo_vendor_deps_hash( 186 | opts: Options, 187 | filename: str, 188 | current_hash: str, 189 | ) -> None: 190 | target_hash = nix_prefetch(opts, "cargoDeps.vendorStaging") 191 | replace_hash(filename, current_hash, target_hash) 192 | 193 | 194 | def update_cargo_lock( 195 | opts: Options, 196 | filename: str, 197 | dst: CargoLockInSource | CargoLockInStore, 198 | ) -> None: 199 | with tempfile.TemporaryDirectory() as tempdir: 200 | res = run( 201 | [ 202 | "nix", 203 | "build", 204 | "--out-link", 205 | f"{tempdir}/result", 206 | "--impure", 207 | "--print-out-paths", 208 | "--expr", 209 | f'\n{get_package(opts)}.overrideAttrs (old: {{\n cargoDeps = null;\n postUnpack = \'\'\n cp -r "$sourceRoot/${{old.cargoRoot or "."}}/Cargo.lock" $out\n exit\n \'\';\n outputs = [ "out" ];\n separateDebugInfo = false;\n}})\n', 210 | *opts.extra_flags, 211 | ], 212 | ) 213 | src = Path(res.stdout.strip()) 214 | if not src.is_file(): 215 | return 216 | 217 | with Path(src).open("rb") as f: 218 | if isinstance(dst, CargoLockInSource): 219 | with Path(dst.path).open("wb") as fdst: 220 | shutil.copyfileobj(f, fdst) 221 | f.seek(0) 222 | 223 | hashes = {} 224 | lock = tomllib.load(f) 225 | regex = re.compile(r"git\+([^?]+)(\?(rev|tag|branch)=.*)?#(.*)") 226 | git_deps = {} 227 | for pkg in lock["package"]: 228 | if (source := pkg.get("source")) and (match := regex.fullmatch(source)): 229 | rev = match[4] 230 | if rev not in git_deps: 231 | git_deps[rev] = f"{pkg['name']}-{pkg['version']}", match[1] 232 | 233 | hashes.update( 234 | dict(ThreadPoolExecutor().map(git_prefetch, git_deps.items())), 235 | ) 236 | 237 | with fileinput.FileInput(filename, inplace=True) as f: 238 | short = re.compile(r"(\s*)cargoLock\.lockFile\s*=\s*(.+)\s*;\s*") 239 | expanded = re.compile(r"(\s*)lockFile\s*=\s*(.+)\s*;\s*") 240 | 241 | for line in f: 242 | if match := short.fullmatch(line): 243 | indent = match[1] 244 | path = match[2] 245 | print(f"{indent}cargoLock = {{") 246 | print(f"{indent} lockFile = {path};") 247 | print_hashes(hashes, f"{indent} ") 248 | print(f"{indent}}};") 249 | for remaining_line in f: 250 | print(remaining_line, end="") 251 | return 252 | if match := expanded.fullmatch(line): 253 | indent = match[1] 254 | path = match[2] 255 | print(line, end="") 256 | print_hashes(hashes, indent) 257 | brace = 0 258 | for next_line in f: 259 | for c in next_line: 260 | if c == "{": 261 | brace -= 1 262 | if c == "}": 263 | brace += 1 264 | if brace == 1: 265 | print(next_line, end="") 266 | for final_line in f: 267 | print(final_line, end="") 268 | return 269 | else: 270 | print(line, end="") 271 | 272 | 273 | def generate_lockfile(opts: Options, filename: str, lockfile_type: str) -> None: 274 | if lockfile_type == "cargo": 275 | cmd = [ 276 | "generate-lockfile", 277 | "--manifest-path", 278 | f"{opts.lockfile_metadata_path}/Cargo.toml", 279 | ] 280 | bin_name = "cargo" 281 | lockfile_name = "Cargo.lock" 282 | extra_nix_override = """ 283 | cargoDeps = null; 284 | cargoVendorDir = "."; 285 | """ 286 | elif lockfile_type == "npm": 287 | cmd = [ 288 | "install", 289 | "--package-lock-only", 290 | "--prefix", 291 | opts.lockfile_metadata_path, 292 | ] 293 | bin_name = "npm" 294 | lockfile_name = "package-lock.json" 295 | extra_nix_override = """ 296 | npmDeps = null; 297 | npmDepsHash = null; 298 | """ 299 | 300 | @contextmanager 301 | def disable_copystat() -> Iterator[None]: 302 | _orig = shutil.copystat 303 | shutil.copystat = lambda *_args, **_kwargs: None 304 | try: 305 | yield 306 | finally: 307 | shutil.copystat = _orig 308 | 309 | get_src_and_bin = textwrap.dedent( 310 | f""" 311 | {get_package(opts)}.overrideAttrs (old: {{ 312 | {extra_nix_override} 313 | postUnpack = '' 314 | cp -pr --reflink=auto -- $sourceRoot $out 315 | mkdir -p "$out/nix-support" 316 | command -v {bin_name} > $out/nix-support/{bin_name}-bin || {{ 317 | echo "no {bin_name} executable found in native build inputs" >&2 318 | exit 1 319 | }} 320 | exit 321 | ''; 322 | outputs = [ "out" ]; 323 | separateDebugInfo = false; 324 | }}) 325 | """, 326 | ) 327 | 328 | res = run( 329 | [ 330 | "nix", 331 | "build", 332 | "-L", 333 | "--no-link", 334 | "--impure", 335 | "--print-out-paths", 336 | "--expr", 337 | get_src_and_bin, 338 | *opts.extra_flags, 339 | ], 340 | ) 341 | src = Path(res.stdout.strip()) 342 | 343 | with tempfile.TemporaryDirectory() as tempdir: 344 | with disable_copystat(): 345 | shutil.copytree(src, tempdir, dirs_exist_ok=True, copy_function=shutil.copy) 346 | 347 | bin_path = (src / "nix-support" / f"{bin_name}-bin").read_text().rstrip("\n") 348 | 349 | run( 350 | [bin_path, *cmd], 351 | cwd=tempdir, 352 | ) 353 | 354 | if ( 355 | lockfile_in_subdir := Path(tempdir) 356 | / opts.lockfile_metadata_path 357 | / lockfile_name 358 | ).exists(): 359 | lockfile = lockfile_in_subdir 360 | else: 361 | lockfile = Path(tempdir) / lockfile_name 362 | 363 | shutil.copy(lockfile, Path(filename).parent / lockfile_name) 364 | 365 | 366 | def update_composer_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 367 | target_hash = nix_prefetch(opts, "composerVendor") 368 | replace_hash(filename, current_hash, target_hash) 369 | 370 | 371 | def update_composer_deps_hash_old( 372 | opts: Options, 373 | filename: str, 374 | current_hash: str, 375 | ) -> None: 376 | target_hash = nix_prefetch(opts, "composerRepository") 377 | replace_hash(filename, current_hash, target_hash) 378 | 379 | 380 | def print_hashes(hashes: dict[str, str], indent: str) -> None: 381 | if not hashes: 382 | return 383 | print(f"{indent}outputHashes = {{") 384 | for k, v in hashes.items(): 385 | print(f'{indent} "{k}" = "{v}";') 386 | print(f"{indent}}};") 387 | 388 | 389 | def update_pnpm_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 390 | target_hash = nix_prefetch(opts, "pnpmDeps") 391 | replace_hash(filename, current_hash, target_hash) 392 | 393 | 394 | def update_npm_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 395 | target_hash = nix_prefetch(opts, "npmDeps") 396 | replace_hash(filename, current_hash, target_hash) 397 | 398 | 399 | def update_yarn_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 400 | target_hash = nix_prefetch(opts, "yarnOfflineCache") 401 | replace_hash(filename, current_hash, target_hash) 402 | 403 | 404 | def update_yarn_deps_hash_old(opts: Options, filename: str, current_hash: str) -> None: 405 | target_hash = nix_prefetch(opts, "offlineCache") 406 | replace_hash(filename, current_hash, target_hash) 407 | 408 | 409 | def update_maven_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 410 | target_hash = nix_prefetch(opts, "fetchedMavenDeps") 411 | replace_hash(filename, current_hash, target_hash) 412 | 413 | 414 | def update_mix_deps_hash(opts: Options, filename: str, current_hash: str) -> None: 415 | target_hash = nix_prefetch(opts, "mixFodDeps") 416 | replace_hash(filename, current_hash, target_hash) 417 | 418 | 419 | def update_nuget_deps(opts: Options) -> None: 420 | fetch_deps_script_path = run( 421 | [ 422 | "nix-build", 423 | opts.import_path, 424 | "-A", 425 | f"{opts.attribute}.fetch-deps", 426 | "--no-out-link", 427 | ], 428 | ).stdout.strip() 429 | 430 | run([fetch_deps_script_path]) 431 | 432 | 433 | def update_version( 434 | opts: Options, 435 | package: Package, 436 | version: str, 437 | preference: VersionPreference, 438 | version_regex: str, 439 | ) -> bool: 440 | if preference == VersionPreference.FIXED: 441 | new_version = Version(version) 442 | else: 443 | if not package.parsed_url: 444 | msg = "Could not find a url in the derivations src attribute" 445 | raise UpdateError(msg) 446 | 447 | version_prefix = "" 448 | if preference != VersionPreference.BRANCH: 449 | branch = None 450 | old_rev_tag = package.rev or package.tag 451 | if old_rev_tag and old_rev_tag.endswith(package.old_version): 452 | version_prefix = old_rev_tag.removesuffix(package.old_version) 453 | elif version == "branch": 454 | # fallback 455 | branch = "HEAD" 456 | else: 457 | assert version.startswith("branch=") 458 | branch = version[7:] 459 | old_rev_tag = package.rev or package.tag 460 | new_version = fetch_latest_version( 461 | package.parsed_url, 462 | preference, 463 | version_regex, 464 | branch, 465 | old_rev_tag, 466 | version_prefix, 467 | ) 468 | package.new_version = new_version 469 | position = package.version_position 470 | if new_version.number == package.old_version and position: 471 | recovered_version = old_version_from_git( 472 | position.file, 473 | position.line, 474 | new_version.number, 475 | ) 476 | if recovered_version: 477 | package.old_version = recovered_version 478 | return False 479 | 480 | if not replace_version(package): 481 | return False 482 | 483 | if package.parsed_url: 484 | if package.parsed_url.netloc == "crates.io": 485 | parts = package.parsed_url.path.split("/") 486 | package.diff_url = ( 487 | f"https://diff.rs/{parts[4]}/{package.old_version}/{new_version.number}" 488 | ) 489 | old_rev_tag = package.tag or package.rev 490 | if package.parsed_url.netloc == "registry.npmjs.org": 491 | parts = package.parsed_url.path.split("/") 492 | package.diff_url = f"https://npmdiff.dev/{parts[1]}/{package.old_version}/{new_version.number}" 493 | elif package.parsed_url.netloc == "github.com": 494 | _, owner, repo, *_ = package.parsed_url.path.split("/") 495 | 496 | if old_rev_tag is None: 497 | # happens when using fetchurl with a github link rather than using fetchFromGitHub 498 | regex = re.compile(".*/releases/download/(.*)/.*") 499 | match = regex.match(package.parsed_url.path) 500 | if match is not None: 501 | old_rev_tag = match.group(1) 502 | 503 | new_rev_tag = new_version.tag or new_version.rev 504 | if new_rev_tag is None: 505 | # happens with fixed version preference (and possibly more situtations?) 506 | new_package = eval_attr(opts) 507 | new_rev_tag = new_package.tag or new_package.rev 508 | 509 | if new_rev_tag is None and new_package.parsed_url is not None: 510 | # happens when using fetchurl with a github link rather than using fetchFromGitHub 511 | regex = re.compile(".*/releases/download/(.*)/.*") 512 | match = regex.match(new_package.parsed_url.path) 513 | if match is not None: 514 | new_rev_tag = match.group(1) 515 | 516 | if old_rev_tag is not None and new_rev_tag is not None: 517 | package.diff_url = f"https://github.com/{owner}/{repo.removesuffix('.git')}/compare/{old_rev_tag}...{new_rev_tag}" 518 | elif package.parsed_url.netloc in ["codeberg.org", "gitea.com"]: 519 | _, owner, repo, *_ = package.parsed_url.path.split("/") 520 | package.diff_url = f"https://{package.parsed_url.netloc}/{owner}/{repo}/compare/{old_rev_tag}...{new_version.rev or new_version.number}" 521 | elif GITLAB_API.match(package.parsed_url.geturl()) and package.src_homepage: 522 | package.diff_url = f"{package.src_homepage}-/compare/{old_rev_tag}...{new_version.rev or new_version.number}" 523 | elif package.parsed_url.netloc in ["bitbucket.org", "bitbucket.io"]: 524 | _, owner, repo, *_ = package.parsed_url.path.split("/") 525 | package.diff_url = f"https://{package.parsed_url.netloc}/{owner}/{repo}/branches/compare/{new_version.rev or new_version.number}%0D{old_rev_tag}" 526 | 527 | return True 528 | 529 | 530 | def run_update_script(package: Package, opts: Options) -> None: 531 | if not opts.flake: 532 | run( 533 | [ 534 | "nix-shell", 535 | str(Path(opts.import_path) / "maintainers/scripts/update.nix"), 536 | "--argstr", 537 | "package", 538 | opts.attribute, 539 | "--argstr", 540 | "skip-prompt", 541 | "true", 542 | *opts.update_script_args, 543 | ], 544 | stdout=None, 545 | ) 546 | return 547 | 548 | update_script = run( 549 | [ 550 | "nix", 551 | "--extra-experimental-features", 552 | "flakes nix-command", 553 | "build", 554 | "--print-out-paths", 555 | "--impure", 556 | "--expr", 557 | f'with import {{}}; let pkg = {get_package(opts)}; in (pkgs.writeScript "updateScript" (builtins.toString (map builtins.toString (pkgs.lib.toList (pkg.updateScript.command or pkg.updateScript)))))', 558 | ], 559 | ).stdout.strip() 560 | 561 | run( 562 | [ 563 | "nix", 564 | "develop", 565 | "--impure", 566 | "--expr", 567 | f"with import {{}}; pkgs.mkShell {{inputsFrom = [{get_package(opts)}];}}", 568 | "--command", 569 | "bash", 570 | "-c", 571 | " ".join( 572 | [ 573 | "env", 574 | f"UPDATE_NIX_NAME={package.name}", 575 | f"UPDATE_NIX_PNAME={package.pname}", 576 | f"UPDATE_NIX_OLD_VERSION={package.old_version}", 577 | f"UPDATE_NIX_ATTR_PATH={package.attribute}", 578 | update_script, 579 | ], 580 | ), 581 | *opts.update_script_args, 582 | ], 583 | cwd=opts.import_path, 584 | ) 585 | 586 | 587 | def update(opts: Options) -> Package: 588 | package = eval_attr(opts) 589 | 590 | if package.has_update_script and opts.use_update_script: 591 | run_update_script(package, opts) 592 | new_package = eval_attr(opts) 593 | package.new_version = Version( 594 | new_package.old_version, 595 | rev=new_package.rev, 596 | tag=new_package.tag, 597 | ) 598 | 599 | return package 600 | 601 | update_hash = True 602 | 603 | if opts.version_preference != VersionPreference.SKIP: 604 | update_hash = update_version( 605 | opts, 606 | package, 607 | opts.version, 608 | opts.version_preference, 609 | opts.version_regex, 610 | ) 611 | 612 | if package.hash and update_hash: 613 | update_src_hash(opts, package.filename, package.hash) 614 | 615 | if opts.subpackages: 616 | for subpackage in opts.subpackages: 617 | info(f"Updating subpackage {subpackage}") 618 | subpackage_opts = deepcopy(opts) 619 | subpackage_opts.attribute += f".{subpackage}" 620 | # Update escaped package attribute 621 | subpackage_opts.__post_init__() 622 | subpackage_opts.subpackages = None 623 | # Do not update the version number since that's already been done 624 | subpackage_opts.version_preference = VersionPreference.SKIP 625 | update(subpackage_opts) 626 | 627 | # if no package.hash was provided we just update the other hashes unless it should be skipped 628 | if (update_hash or not package.hash) and not opts.src_only: 629 | if package.go_modules: 630 | update_go_modules_hash(opts, package.filename, package.go_modules) 631 | 632 | if package.go_modules_old: 633 | update_go_modules_hash_old(opts, package.filename, package.go_modules_old) 634 | 635 | if package.cargo_deps: 636 | update_cargo_deps_hash(opts, package.filename, package.cargo_deps) 637 | 638 | if package.cargo_vendor_deps: 639 | update_cargo_vendor_deps_hash( 640 | opts, 641 | package.filename, 642 | package.cargo_vendor_deps, 643 | ) 644 | 645 | if package.composer_deps: 646 | update_composer_deps_hash(opts, package.filename, package.composer_deps) 647 | 648 | if package.composer_deps_old: 649 | update_composer_deps_hash_old( 650 | opts, 651 | package.filename, 652 | package.composer_deps_old, 653 | ) 654 | 655 | if package.npm_deps: 656 | if opts.generate_lockfile: 657 | generate_lockfile(opts, package.filename, "npm") 658 | update_npm_deps_hash(opts, package.filename, package.npm_deps) 659 | 660 | if package.pnpm_deps: 661 | update_pnpm_deps_hash(opts, package.filename, package.pnpm_deps) 662 | 663 | if package.yarn_deps: 664 | update_yarn_deps_hash(opts, package.filename, package.yarn_deps) 665 | 666 | if package.yarn_deps_old: 667 | update_yarn_deps_hash_old(opts, package.filename, package.yarn_deps_old) 668 | 669 | if package.maven_deps: 670 | update_maven_deps_hash(opts, package.filename, package.maven_deps) 671 | 672 | if package.mix_deps: 673 | update_mix_deps_hash(opts, package.filename, package.mix_deps) 674 | 675 | if package.has_nuget_deps: 676 | update_nuget_deps(opts) 677 | 678 | if isinstance(package.cargo_lock, CargoLockInSource | CargoLockInStore): 679 | if opts.generate_lockfile: 680 | generate_lockfile(opts, package.filename, "cargo") 681 | else: 682 | update_cargo_lock(opts, package.filename, package.cargo_lock) 683 | 684 | return package 685 | -------------------------------------------------------------------------------- /nix_update/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shlex 3 | import subprocess 4 | import sys 5 | from collections.abc import Callable 6 | from pathlib import Path 7 | from typing import IO, Any 8 | 9 | HAS_TTY = sys.stdout.isatty() 10 | ROOT = Path(__file__).parent 11 | 12 | 13 | class LogLevel: 14 | INFO = 0 15 | WARNING = 1 16 | 17 | 18 | LOG_LEVEL = LogLevel.INFO 19 | 20 | 21 | def color_text(code: int, file: IO[Any] = sys.stdout) -> Callable[[str], None]: 22 | def wrapper(text: str) -> None: 23 | if LOG_LEVEL < LogLevel.INFO: 24 | return 25 | if HAS_TTY: 26 | print(f"\x1b[{code}m{text}\x1b[0m", file=file) 27 | else: 28 | print(text, file=file) 29 | 30 | return wrapper 31 | 32 | 33 | info = color_text(32) 34 | 35 | 36 | def run( 37 | command: list[str], 38 | cwd: Path | str | None = None, 39 | stdout: None | int | IO[Any] = subprocess.PIPE, 40 | stderr: None | int | IO[Any] = None, 41 | check: bool = True, 42 | extra_env: dict[str, str] | None = None, 43 | ) -> "subprocess.CompletedProcess[str]": 44 | if extra_env is None: 45 | extra_env = {} 46 | info("$ " + shlex.join(command)) 47 | env = os.environ.copy() 48 | env.update(extra_env) 49 | return subprocess.run( 50 | command, 51 | cwd=cwd, 52 | check=check, 53 | text=True, 54 | stdout=stdout, 55 | stderr=stderr, 56 | env=env, 57 | ) 58 | -------------------------------------------------------------------------------- /nix_update/version/__init__.py: -------------------------------------------------------------------------------- 1 | import re 2 | from collections.abc import Callable 3 | from functools import partial 4 | from typing import Protocol 5 | from urllib.parse import ParseResult 6 | from urllib.request import build_opener, install_opener 7 | 8 | from nix_update.errors import VersionError 9 | from nix_update.version_info import VERSION 10 | 11 | from .bitbucket import fetch_bitbucket_snapshots, fetch_bitbucket_versions 12 | from .crate import fetch_crate_versions 13 | from .gitea import fetch_gitea_snapshots, fetch_gitea_versions 14 | from .github import fetch_github_snapshots, fetch_github_versions 15 | from .gitlab import fetch_gitlab_snapshots, fetch_gitlab_versions 16 | from .npm import fetch_npm_versions 17 | from .pypi import fetch_pypi_versions 18 | from .rubygems import fetch_rubygem_versions 19 | from .savannah import fetch_savannah_versions 20 | from .sourcehut import fetch_sourcehut_snapshots, fetch_sourcehut_versions 21 | from .version import Version, VersionPreference 22 | 23 | # def find_repology_release(attr) -> str: 24 | # resp = urllib.request.urlopen(f"https://repology.org/api/v1/projects/{attr}/") 25 | # data = json.loads(resp.read()) 26 | # for name, pkg in data.items(): 27 | # for repo in pkg: 28 | # if repo["status"] == "newest": 29 | # return repo["version"] 30 | # return None 31 | 32 | opener = build_opener() 33 | opener.addheaders = [("User-Agent", f"nix-update/{VERSION}")] 34 | install_opener(opener) 35 | 36 | 37 | class SnapshotFetcher(Protocol): 38 | def __call__(self, url: ParseResult, branch: str) -> list[Version]: ... 39 | 40 | 41 | fetchers: list[Callable[[ParseResult], list[Version]]] = [ 42 | fetch_crate_versions, 43 | fetch_npm_versions, 44 | fetch_pypi_versions, 45 | fetch_github_versions, 46 | fetch_gitlab_versions, 47 | fetch_rubygem_versions, 48 | fetch_savannah_versions, 49 | fetch_sourcehut_versions, 50 | fetch_bitbucket_versions, 51 | # all entries below perform requests to check if the target url is of that type 52 | fetch_gitea_versions, 53 | ] 54 | 55 | branch_snapshots_fetchers: list[SnapshotFetcher] = [ 56 | fetch_github_snapshots, 57 | fetch_gitlab_snapshots, 58 | fetch_bitbucket_snapshots, 59 | fetch_sourcehut_snapshots, 60 | # all entries below perform requests to check if the target url is of that type 61 | fetch_gitea_snapshots, 62 | ] 63 | 64 | 65 | def extract_version(version: Version, version_regex: str) -> Version | None: 66 | pattern = re.compile(version_regex) 67 | match = re.match(pattern, version.number) 68 | if match is not None: 69 | group = match.group(1) 70 | if group is not None: 71 | return Version( 72 | group, 73 | prerelease=version.prerelease, 74 | rev=version.rev 75 | or (None if version.number == group else version.number), 76 | ) 77 | return None 78 | 79 | 80 | def is_unstable(version: Version, extracted: str) -> bool: 81 | if version.prerelease is not None: 82 | return version.prerelease 83 | pattern = "rc|alpha|beta|preview|nightly|prerelease|m[0-9]+" 84 | return re.search(pattern, extracted, re.IGNORECASE) is not None 85 | 86 | 87 | def fetch_latest_version( 88 | url: ParseResult, 89 | preference: VersionPreference, 90 | version_regex: str, 91 | branch: str | None = None, 92 | old_rev_tag: str | None = None, 93 | version_prefix: str = "", 94 | ) -> Version: 95 | unstable: list[str] = [] 96 | filtered: list[str] = [] 97 | used_fetchers = fetchers 98 | if preference == VersionPreference.BRANCH: 99 | assert branch is not None 100 | used_fetchers = [partial(f, branch=branch) for f in branch_snapshots_fetchers] 101 | for fetcher in used_fetchers: 102 | versions = fetcher(url) 103 | if versions == []: 104 | continue 105 | final = [] 106 | for version in versions: 107 | extracted = extract_version(version, version_regex) 108 | if extracted is None: 109 | filtered.append(version.number) 110 | elif preference == VersionPreference.STABLE and is_unstable( 111 | version, 112 | extracted.number, 113 | ): 114 | unstable.append(extracted.number) 115 | else: 116 | final.append(extracted) 117 | if final != []: 118 | if version_prefix != "": 119 | ver = next( 120 | ( 121 | Version( 122 | version.number.removeprefix(version_prefix), 123 | prerelease=version.prerelease, 124 | rev=version.rev or version.number, 125 | ) 126 | for version in final 127 | if version.number.startswith(version_prefix) 128 | ), 129 | None, 130 | ) 131 | 132 | if ver is not None and ver.rev != old_rev_tag: 133 | return ver 134 | 135 | return final[0] 136 | 137 | if filtered: 138 | raise VersionError( 139 | "Not version matched the regex. The following versions were found:\n" 140 | + "\n".join(filtered), 141 | ) 142 | 143 | if unstable: 144 | msg = f"Found an unstable version {unstable[0]}, which is being ignored. To update to unstable version, please use '--version=unstable'" 145 | raise VersionError(msg) 146 | 147 | msg = "Please specify the version. We can only get the latest version from codeberg/crates.io/gitea/github/gitlab/pypi/savannah/sourcehut/rubygems/npm projects right now" 148 | raise VersionError(msg) 149 | -------------------------------------------------------------------------------- /nix_update/version/bitbucket.py: -------------------------------------------------------------------------------- 1 | import json 2 | from urllib.parse import ParseResult 3 | from urllib.request import urlopen 4 | 5 | from .version import Version 6 | 7 | 8 | def fetch_bitbucket_versions(url: ParseResult) -> list[Version]: 9 | if url.netloc not in ["bitbucket.org", "bitbucket.io"]: 10 | return [] 11 | 12 | _, owner, repo, *_ = url.path.split("/") 13 | # paging controlled by pagelen parameter, by default it is 10 14 | tags_url = f"https://{url.netloc}/!api/2.0/repositories/{owner}/{repo}/refs/tags?sort=-target.date" 15 | resp = urlopen(tags_url) 16 | tags = json.loads(resp.read())["values"] 17 | return [Version(tag["name"]) for tag in tags] 18 | 19 | 20 | def fetch_bitbucket_snapshots(url: ParseResult, branch: str) -> list[Version]: 21 | if url.netloc not in ["bitbucket.org", "bitbucket.io"]: 22 | return [] 23 | 24 | _, owner, repo, *_ = url.path.split("/") 25 | # seems to ignore pagelen parameter (always returns one entry) 26 | commits_url = f'https://{url.netloc}/!api/2.0/repositories/{owner}/{repo}/refs?q=name="{branch}"' 27 | resp = urlopen(commits_url) 28 | ref = json.loads(resp.read())["values"][0]["target"] 29 | 30 | versions = fetch_bitbucket_versions(url) 31 | latest_version = versions[0].number if versions else "0" 32 | 33 | date = ref["date"][:10] # to YYYY-MM-DD 34 | return [Version(f"{latest_version}-unstable-{date}", rev=ref["hash"])] 35 | -------------------------------------------------------------------------------- /nix_update/version/crate.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.request 3 | from urllib.parse import ParseResult 4 | 5 | from nix_update.utils import info 6 | 7 | from .version import Version 8 | 9 | 10 | def fetch_crate_versions(url: ParseResult) -> list[Version]: 11 | if url.netloc != "crates.io": 12 | return [] 13 | parts = url.path.split("/") 14 | package = parts[4] 15 | crate_url = f"https://crates.io/api/v1/crates/{package}/versions" 16 | info(f"fetch {crate_url}") 17 | resp = urllib.request.urlopen(crate_url) 18 | data = json.loads(resp.read()) 19 | return [ 20 | Version(version["num"]) for version in data["versions"] if not version["yanked"] 21 | ] 22 | -------------------------------------------------------------------------------- /nix_update/version/gitea.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | from urllib.error import URLError 4 | from urllib.parse import ParseResult 5 | from urllib.request import urlopen 6 | 7 | from .version import Version 8 | 9 | KNOWN_GITEA_HOSTS = ["codeberg.org", "gitea.com", "akkoma.dev"] 10 | 11 | 12 | def is_gitea_host(host: str) -> bool: 13 | if host in KNOWN_GITEA_HOSTS: 14 | return True 15 | endpoint = f"https://{host}/api/v1/signing-key.gpg" 16 | try: 17 | resp = urlopen(endpoint) 18 | except URLError: 19 | return False 20 | else: 21 | return resp.status == 200 22 | 23 | 24 | def fetch_gitea_versions(url: ParseResult) -> list[Version]: 25 | if not is_gitea_host(url.netloc): 26 | return [] 27 | 28 | _, owner, repo, *_ = url.path.split("/") 29 | repo = re.sub(r"\.git$", "", repo) 30 | tags_url = f"https://{url.netloc}/api/v1/repos/{owner}/{repo}/tags" 31 | resp = urlopen(tags_url) 32 | tags = json.loads(resp.read()) 33 | return [Version(tag["name"]) for tag in tags] 34 | 35 | 36 | def fetch_gitea_snapshots(url: ParseResult, branch: str) -> list[Version]: 37 | if not is_gitea_host(url.netloc): 38 | return [] 39 | 40 | _, owner, repo, *_ = url.path.split("/") 41 | repo = re.sub(r"\.git$", "", repo) 42 | commits_url = f"https://{url.netloc}/api/v1/repos/{owner}/{repo}/commits?sha={branch}&limit=1&stat=false&verification=false&files=false" 43 | resp = urlopen(commits_url) 44 | commits = json.loads(resp.read()) 45 | 46 | commit = next(iter(commits), None) 47 | if commit is None: 48 | return [] 49 | 50 | versions = fetch_gitea_versions(url) 51 | latest_version = versions[0].number if versions else "0" 52 | 53 | date = commit["commit"]["committer"]["date"][:10] 54 | return [Version(f"{latest_version}-unstable-{date}", rev=commit["sha"])] 55 | -------------------------------------------------------------------------------- /nix_update/version/github.py: -------------------------------------------------------------------------------- 1 | import re 2 | import urllib.request 3 | import xml.etree.ElementTree as ET 4 | from urllib.parse import ParseResult, unquote, urlparse 5 | from xml.etree.ElementTree import Element 6 | 7 | from nix_update.errors import VersionError 8 | from nix_update.utils import info 9 | 10 | from .version import Version 11 | 12 | 13 | def version_from_entry(entry: Element) -> Version: 14 | if entry is None: 15 | msg = "No release found" 16 | raise VersionError(msg) 17 | link = entry.find("{http://www.w3.org/2005/Atom}link") 18 | assert link is not None 19 | href = link.attrib["href"] 20 | url = urlparse(href) 21 | # TODO: set pre-release flag 22 | return Version(unquote(url.path.split("/")[-1])) 23 | 24 | 25 | def fetch_github_versions(url: ParseResult) -> list[Version]: 26 | if url.netloc != "github.com": 27 | return [] 28 | parts = url.path.split("/") 29 | owner, repo = parts[1], parts[2] 30 | repo = re.sub(r"\.git$", "", repo) 31 | # TODO fallback to tags? 32 | feed_url = f"https://github.com/{owner}/{repo}/releases.atom" 33 | info(f"fetch {feed_url}") 34 | resp = urllib.request.urlopen(feed_url) 35 | tree = ET.fromstring(resp.read()) 36 | releases = tree.findall(".//{http://www.w3.org/2005/Atom}entry") 37 | return [version_from_entry(x) for x in releases] 38 | 39 | 40 | def fetch_github_snapshots(url: ParseResult, branch: str) -> list[Version]: 41 | if url.netloc != "github.com": 42 | return [] 43 | parts = url.path.split("/") 44 | owner, repo = parts[1], parts[2] 45 | repo = re.sub(r"\.git$", "", repo) 46 | feed_url = f"https://github.com/{owner}/{repo}/commits/{branch}.atom" 47 | info(f"fetch {feed_url}") 48 | resp = urllib.request.urlopen(feed_url) 49 | tree = ET.fromstring(resp.read()) 50 | commits = tree.findall(".//{http://www.w3.org/2005/Atom}entry") 51 | 52 | versions = fetch_github_versions(url) 53 | latest_version = versions[0].number if versions else "0" 54 | 55 | for entry in commits: 56 | link = entry.find("{http://www.w3.org/2005/Atom}link") 57 | assert link is not None, "cannot parse ATOM feed: missing link" 58 | 59 | updated = entry.find("{http://www.w3.org/2005/Atom}updated") 60 | assert updated is not None, "cannot parse ATOM feed: missing updated element" 61 | assert updated.text is not None, ( 62 | "cannot parse ATOM feed: updated element has no text" 63 | ) 64 | 65 | url = urlparse(link.attrib["href"]) 66 | commit = url.path.rsplit("/", maxsplit=1)[-1] 67 | date = updated.text.split("T", maxsplit=1)[0] 68 | return [Version(f"{latest_version}-unstable-{date}", rev=commit)] 69 | 70 | return [] 71 | -------------------------------------------------------------------------------- /nix_update/version/gitlab.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import urllib.request 4 | from datetime import datetime 5 | from urllib.parse import ParseResult, quote_plus 6 | 7 | from nix_update.errors import VersionError 8 | from nix_update.utils import info 9 | 10 | from .version import Version 11 | 12 | GITLAB_API = re.compile( 13 | r"http(s)?://(?P[^/]+)/api/v4/projects/(?P[^/]*)/repository/archive.tar.gz\?sha=(?P.+)", 14 | ) 15 | 16 | 17 | def fetch_gitlab_versions(url: ParseResult) -> list[Version]: 18 | match = GITLAB_API.match(url.geturl()) 19 | if not match: 20 | return [] 21 | domain = match.group("domain") 22 | project_id = match.group("project_id") 23 | gitlab_url = f"https://{domain}/api/v4/projects/{project_id}/repository/tags" 24 | info(f"fetch {gitlab_url}") 25 | resp = urllib.request.urlopen(gitlab_url) 26 | json_tags = json.loads(resp.read()) 27 | if len(json_tags) == 0: 28 | msg = "No git tags found" 29 | raise VersionError(msg) 30 | releases = [] 31 | tags = [] 32 | for tag in json_tags: 33 | name = tag["name"] 34 | assert isinstance(name, str) 35 | if tag.get("release"): 36 | # TODO: has gitlab preleases? 37 | releases.append(Version(name)) 38 | else: 39 | tags.append(Version(name)) 40 | # if no release is found, use latest tag 41 | if releases == []: 42 | return tags 43 | return releases 44 | 45 | 46 | def fetch_gitlab_snapshots(url: ParseResult, branch: str) -> list[Version]: 47 | match = GITLAB_API.match(url.geturl()) 48 | if not match: 49 | return [] 50 | domain = match.group("domain") 51 | project_id = match.group("project_id") 52 | gitlab_url = f"https://{domain}/api/v4/projects/{project_id}/repository/commits?ref_name={quote_plus(branch)}" 53 | info(f"fetch {gitlab_url}") 54 | resp = urllib.request.urlopen(gitlab_url) 55 | commits = json.load(resp) 56 | 57 | try: 58 | versions = fetch_gitlab_versions(url) 59 | except VersionError: 60 | versions = [] 61 | latest_version = versions[0].number if versions else "0" 62 | 63 | for commit in commits: 64 | commit_date = datetime.strptime( 65 | commit["committed_date"], 66 | "%Y-%m-%dT%H:%M:%S.000%z", 67 | ) 68 | commit_date -= commit_date.utcoffset() # type: ignore[operator] 69 | date = commit_date.strftime("%Y-%m-%d") 70 | return [Version(f"{latest_version}-unstable-{date}", rev=commit["id"])] 71 | return [] 72 | -------------------------------------------------------------------------------- /nix_update/version/npm.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.request 3 | from urllib.parse import ParseResult 4 | 5 | from nix_update.utils import info 6 | 7 | from .version import Version 8 | 9 | 10 | def fetch_npm_versions(url: ParseResult) -> list[Version]: 11 | if url.netloc != "registry.npmjs.org": 12 | return [] 13 | parts = url.path.split("/") 14 | package = parts[1] 15 | npm_url = f"https://registry.npmjs.org/{package}/latest" 16 | info(f"fetch {npm_url}") 17 | resp = urllib.request.urlopen(npm_url) 18 | data = json.loads(resp.read()) 19 | return [Version(data["version"])] 20 | -------------------------------------------------------------------------------- /nix_update/version/pypi.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.request 3 | from urllib.parse import ParseResult 4 | 5 | from nix_update.utils import info 6 | 7 | from .version import Version 8 | 9 | 10 | def fetch_pypi_versions(url: ParseResult) -> list[Version]: 11 | if url.netloc != "pypi": 12 | return [] 13 | parts = url.path.split("/") 14 | package = parts[2] 15 | pypi_url = f"https://pypi.org/pypi/{package}/json" 16 | info(f"fetch {pypi_url}") 17 | resp = urllib.request.urlopen(pypi_url) 18 | data = json.loads(resp.read()) 19 | version = data["info"]["version"] 20 | assert isinstance(version, str) 21 | # TODO look at info->releases instead 22 | return [Version(version)] 23 | -------------------------------------------------------------------------------- /nix_update/version/rubygems.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.request 3 | from urllib.parse import ParseResult 4 | 5 | from nix_update.errors import VersionError 6 | from nix_update.utils import info 7 | 8 | from .version import Version 9 | 10 | 11 | def fetch_rubygem_versions(url: ParseResult) -> list[Version]: 12 | if url.netloc != "rubygems.org": 13 | return [] 14 | parts = url.path.split("/") 15 | gem = parts[-1] 16 | gem_name, _ = gem.rsplit("-") 17 | versions_url = f"https://rubygems.org/api/v1/versions/{gem_name}.json" 18 | info(f"fetch {versions_url}") 19 | resp = urllib.request.urlopen(versions_url) 20 | json_versions = json.load(resp) 21 | if len(json_versions) == 0: 22 | msg = "No versions found" 23 | raise VersionError(msg) 24 | 25 | versions: list[Version] = [] 26 | for version in json_versions: 27 | number = version["number"] 28 | assert isinstance(number, str) 29 | prerelease = version["prerelease"] 30 | assert isinstance(prerelease, bool) 31 | versions.append(Version(number, prerelease=prerelease)) 32 | return versions 33 | -------------------------------------------------------------------------------- /nix_update/version/savannah.py: -------------------------------------------------------------------------------- 1 | import re 2 | import urllib.request 3 | import xml.etree.ElementTree as ET 4 | from urllib.parse import ParseResult, urljoin, urlparse 5 | from xml.etree.ElementTree import Element 6 | 7 | from nix_update.utils import info 8 | 9 | from .version import Version 10 | 11 | filename_regex = re.compile(r"-(\d+(?:\.\d+)*(?:-[^-.]+)?)\.tar\.[^.]+$") 12 | 13 | 14 | def version_from_link(a: Element, baseurl: str) -> Version | None: 15 | try: 16 | href = a.attrib["href"] 17 | except KeyError: 18 | return None 19 | url = urlparse(urljoin(baseurl, href)) 20 | m = filename_regex.search(url.path) 21 | if not m: 22 | return None 23 | return Version(m[1]) 24 | 25 | 26 | def fetch_savannah_versions(url: ParseResult) -> list[Version]: 27 | if url.scheme != "mirror" or url.netloc != "savannah": 28 | return [] 29 | pname = url.path.split("/", 2)[1] 30 | dir_url = f"https://download.savannah.nongnu.org/releases/{pname}/?C=M&O=D" 31 | info(f"fetch {dir_url}") 32 | resp = urllib.request.urlopen(dir_url) 33 | html = resp.read() 34 | 35 | # only parse tbody 36 | start = html.index(b"") 37 | end = html.index(b"", start) + 8 38 | tree = ET.fromstring(html[start:end]) 39 | 40 | versions = [] 41 | for a in tree.findall(".//a"): 42 | version = version_from_link(a, dir_url) 43 | if version: 44 | versions.append(version) 45 | return versions 46 | -------------------------------------------------------------------------------- /nix_update/version/sourcehut.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | import xml.etree.ElementTree as ET 3 | from email.utils import parsedate_to_datetime 4 | from urllib.parse import ParseResult, urlparse 5 | from xml.etree.ElementTree import Element 6 | 7 | from nix_update.errors import VersionError 8 | from nix_update.utils import info 9 | 10 | from .version import Version 11 | 12 | 13 | def version_from_entry(entry: Element) -> Version: 14 | if entry is None: 15 | msg = "No release found" 16 | raise VersionError(msg) 17 | link = entry.find("link") 18 | assert link is not None 19 | url = urlparse(str(link.text)) 20 | return Version(url.path.split("/")[-1]) 21 | 22 | 23 | def snapshot_from_entry(entry: Element, url: ParseResult) -> Version: 24 | versions = fetch_sourcehut_versions(url) 25 | latest_version = versions[0].number if versions else "0" 26 | pub_date = entry.find("pubDate") 27 | if pub_date is None: 28 | msg = "No pubDate found in atom feed {url}" 29 | raise VersionError(msg) 30 | parsed = parsedate_to_datetime(pub_date.text) 31 | if parsed is None: 32 | msg = f"Invalid pubDate format: {pub_date.text}" 33 | raise VersionError(msg) 34 | date = parsed.date() 35 | date_str = date.isoformat() 36 | node = entry.find("link") 37 | if node is None or node.text is None: 38 | msg = "No link found in atom feed {url}" 39 | raise VersionError(msg) 40 | rev = node.text.split("/")[-1] 41 | return Version(f"{latest_version}-unstable-{date_str}", rev=rev) 42 | 43 | 44 | def fetch_sourcehut_versions(url: ParseResult) -> list[Version]: 45 | if url.netloc != "git.sr.ht": 46 | return [] 47 | parts = url.path.split("/") 48 | owner, repo = parts[1], parts[2] 49 | # repo = re.sub(r"\.git$", "", repo) 50 | feed_url = f"https://git.sr.ht/{owner}/{repo}/refs/rss.xml" 51 | info(f"fetch {feed_url}") 52 | resp = urllib.request.urlopen(feed_url) 53 | tree = ET.fromstring(resp.read()) 54 | releases = tree.findall(".//item") 55 | return [version_from_entry(x) for x in releases] 56 | 57 | 58 | def fetch_sourcehut_snapshots(url: ParseResult, branch: str) -> list[Version]: 59 | if url.netloc != "git.sr.ht": 60 | return [] 61 | parts = url.path.split("/") 62 | owner, repo = parts[1], parts[2] 63 | feed_url = f"https://git.sr.ht/{owner}/{repo}/log/{branch}/rss.xml" 64 | info(f"fetch {feed_url}") 65 | resp = urllib.request.urlopen(feed_url) 66 | tree = ET.fromstring(resp.read()) 67 | latest_commit = tree.find(".//item") 68 | if latest_commit is None: 69 | msg = f"No commit found in atom feed {url}" 70 | raise VersionError(msg) 71 | return [snapshot_from_entry(latest_commit, url)] 72 | -------------------------------------------------------------------------------- /nix_update/version/version.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import StrEnum, auto 3 | 4 | 5 | @dataclass 6 | class Version: 7 | number: str 8 | prerelease: bool | None = None 9 | rev: str | None = None 10 | tag: str | None = None 11 | 12 | 13 | class VersionPreference(StrEnum): 14 | STABLE = auto() 15 | UNSTABLE = auto() 16 | FIXED = auto() 17 | SKIP = auto() 18 | BRANCH = auto() 19 | 20 | @staticmethod 21 | def from_str(version: str) -> "VersionPreference": 22 | # auto is deprecated 23 | if version in ("auto", "stable"): 24 | return VersionPreference.STABLE 25 | if version == "unstable": 26 | return VersionPreference.UNSTABLE 27 | if version == "skip": 28 | return VersionPreference.SKIP 29 | if version == "branch" or version.startswith("branch="): 30 | return VersionPreference.BRANCH 31 | return VersionPreference.FIXED 32 | -------------------------------------------------------------------------------- /nix_update/version_info.py: -------------------------------------------------------------------------------- 1 | VERSION = "1.11.0" 2 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "nix-update" 7 | description = "Swiss-knife for updating nix packages" 8 | version = "1.11.0" 9 | authors = [{ name = "Jörg Thalheim", email = "joerg@thalheim.io" }] 10 | license = { text = "MIT" } 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "Environment :: Console", 14 | "Topic :: Utilities", 15 | "Intended Audience :: Developers", 16 | ] 17 | 18 | [project.urls] 19 | Homepage = "https://github.com/Mic92/nix-update" 20 | 21 | [project.scripts] 22 | nix-update = "nix_update:main" 23 | 24 | [tool.pytest.ini_options] 25 | addopts = "-v -n auto" 26 | 27 | [tool.ruff] 28 | target-version = "py311" 29 | line-length = 88 30 | 31 | lint.select = ["ALL"] 32 | lint.ignore = [ 33 | # pydocstyle 34 | "D", 35 | # todo comments 36 | "TD", 37 | # fixmes 38 | "FIX", 39 | # Logging statement uses f-string 40 | "G004", 41 | 42 | # Use of `assert` detected 43 | "S101", 44 | # `subprocess` call: check for execution of untrusted input 45 | "S603", 46 | # Starting a process with a partial executable path 47 | "S607", 48 | # Audit URL open for permitted schemes 49 | "S310", 50 | # Using `xml` to parse untrusted data 51 | "S314", 52 | 53 | # Too many statements 54 | "PLR0915", 55 | # Too many arguments in function definition 56 | "PLR0913", 57 | "PLR0912", # Too many branches 58 | "PLR0911", # Too many return statements 59 | # $X is too complex 60 | "C901", 61 | 62 | # Found commented-out code 63 | "ERA001", 64 | 65 | # Boolean default positional argument in function definition 66 | "FBT002", 67 | # Boolean-typed positional argument in function definition 68 | "FBT001", 69 | 70 | "E501", # line too long 71 | "T201", # `print` found 72 | "PLR2004", # Magic value used in comparison 73 | ] 74 | 75 | [tool.mypy] 76 | python_version = "3.11" 77 | pretty = true 78 | warn_redundant_casts = true 79 | disallow_untyped_calls = true 80 | disallow_untyped_defs = true 81 | no_implicit_optional = true 82 | 83 | [[tool.mypy.overrides]] 84 | module = "setuptools.*" 85 | ignore_missing_imports = true 86 | 87 | [[tool.mypy.overrides]] 88 | module = "pytest.*" 89 | ignore_missing_imports = true 90 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", ":dependencyDashboard"], 4 | "nix": { 5 | "enabled": true 6 | }, 7 | "lockFileMaintenance": { "enabled": true } 8 | } 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for nix-update.""" 2 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | import sys 5 | import tempfile 6 | from collections.abc import Iterator 7 | from contextlib import contextmanager 8 | from pathlib import Path 9 | 10 | import pytest 11 | 12 | TEST_ROOT = Path(__file__).parent.resolve() 13 | sys.path.append(str(TEST_ROOT.parent)) 14 | 15 | 16 | class Helpers: 17 | @staticmethod 18 | def root() -> Path: 19 | return TEST_ROOT 20 | 21 | @staticmethod 22 | @contextmanager 23 | def testpkgs(init_git: bool = False) -> Iterator[Path]: 24 | with tempfile.TemporaryDirectory() as _tmpdirname: 25 | tmpdirname = Path(_tmpdirname) 26 | shutil.copytree( 27 | Helpers.root().joinpath("testpkgs"), 28 | tmpdirname, 29 | dirs_exist_ok=True, 30 | ) 31 | if init_git: 32 | os.environ["GIT_AUTHOR_NAME"] = "nix-update" 33 | os.environ["GIT_AUTHOR_EMAIL"] = "nix-update@example.com" 34 | os.environ["GIT_COMMITTER_NAME"] = "nix-update" 35 | os.environ["GIT_COMMITTER_EMAIL"] = "nix-update@example.com" 36 | 37 | subprocess.run(["git", "-C", tmpdirname, "init"], check=True) 38 | subprocess.run(["git", "-C", tmpdirname, "add", "--all"], check=True) 39 | subprocess.run( 40 | ["git", "-C", tmpdirname, "config", "commit.gpgsign", "false"], 41 | check=True, 42 | ) 43 | subprocess.run( 44 | ["git", "-C", tmpdirname, "commit", "-m", "first commit"], 45 | check=True, 46 | ) 47 | yield Path(tmpdirname) 48 | 49 | 50 | @pytest.fixture # type: ignore[misc] 51 | def helpers() -> type[Helpers]: 52 | return Helpers 53 | -------------------------------------------------------------------------------- /tests/consul.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pkgs/servers/consul/default.nix b/pkgs/servers/consul/default.nix 2 | index eef9054ae70..334528e7673 100644 3 | --- a/pkgs/servers/consul/default.nix 4 | +++ b/pkgs/servers/consul/default.nix 5 | @@ -2,7 +2,7 @@ 6 | 7 | ~ 8 | buildGoModule rec { 9 | ~ 10 | pname = "consul"; 11 | ~ 12 | version = 13 | -"1.8.6"; 14 | +"1.9.0"; 15 | ~ 16 | rev = "v${version}"; 17 | ~ 18 | 19 | ~ 20 | # Note: Currently only release tags are supported, because they have the Consul UI 21 | ~ 22 | -------------------------------------------------------------------------------- /tests/test_bitbucket.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | # integration test for bitbucket versions (fetch_bitbucket_versions), mostly 8 | # copied from test_gitea.py. 9 | # run directly with 'nix develop -c pytest -s ./tests/test_bitbucket.py'. 10 | def test_version(helpers: conftest.Helpers) -> None: 11 | with helpers.testpkgs(init_git=True) as path: 12 | main(["--file", str(path), "--commit", "bitbucket"]) 13 | version = subprocess.run( 14 | [ 15 | "nix", 16 | "eval", 17 | "--raw", 18 | "--extra-experimental-features", 19 | "nix-command", 20 | "-f", 21 | path, 22 | "bitbucket.version", 23 | ], 24 | check=True, 25 | text=True, 26 | stdout=subprocess.PIPE, 27 | ).stdout.strip() 28 | assert tuple(map(int, version.split("."))) > (1, 0) 29 | commit = subprocess.run( 30 | ["git", "-C", path, "log", "-1"], 31 | text=True, 32 | stdout=subprocess.PIPE, 33 | check=True, 34 | ).stdout.strip() 35 | print(commit) 36 | assert version in commit 37 | assert "bitbucket" in commit 38 | assert "/nielsenb/aniso8601/branches/compare/" in commit 39 | assert "%0Dv9.0.0" in commit 40 | 41 | 42 | # integration test for bitbucket snapshots 43 | def test_snapshot(helpers: conftest.Helpers) -> None: 44 | with helpers.testpkgs(init_git=True) as path: 45 | main( 46 | [ 47 | "--file", 48 | str(path), 49 | "--commit", 50 | "--version=branch=master", 51 | "bitbucket-snapshot", 52 | ], 53 | ) 54 | version = subprocess.run( 55 | [ 56 | "nix", 57 | "eval", 58 | "--raw", 59 | "--extra-experimental-features", 60 | "nix-command", 61 | "-f", 62 | path, 63 | "bitbucket-snapshot.version", 64 | ], 65 | check=True, 66 | text=True, 67 | stdout=subprocess.PIPE, 68 | ).stdout.strip() 69 | commit = subprocess.run( 70 | ["git", "-C", path, "log", "-1"], 71 | text=True, 72 | stdout=subprocess.PIPE, 73 | check=True, 74 | ).stdout.strip() 75 | print(commit) 76 | assert version in commit 77 | assert "bitbucket" in commit 78 | assert "/nielsenb/aniso8601/branches/compare/" in commit 79 | assert "%0D55b1b849a57341a303ae47eb67c7ecf8c283b7f8" in commit 80 | -------------------------------------------------------------------------------- /tests/test_branch.py: -------------------------------------------------------------------------------- 1 | import unittest.mock 2 | from pathlib import Path 3 | from typing import BinaryIO 4 | from urllib.parse import urlparse 5 | 6 | from nix_update.version import fetch_latest_version 7 | from nix_update.version.version import VersionPreference 8 | from tests import conftest 9 | 10 | TEST_ROOT = Path(__file__).parent.resolve() 11 | 12 | 13 | def fake_urlopen(url: str) -> BinaryIO: 14 | if url.endswith("releases.atom"): 15 | return TEST_ROOT.joinpath("test_branch_releases.atom").open("rb") 16 | return TEST_ROOT.joinpath("test_branch_commits_master.atom").open("rb") 17 | 18 | 19 | def test_branch(helpers: conftest.Helpers) -> None: 20 | del helpers 21 | with unittest.mock.patch("urllib.request.urlopen", fake_urlopen): 22 | assert ( 23 | fetch_latest_version( 24 | urlparse("https://github.com/Mic92/nix-update"), 25 | VersionPreference.BRANCH, 26 | "(.*)", 27 | "master", 28 | ).number 29 | == "1.2.0-unstable-2024-02-19" 30 | ) 31 | -------------------------------------------------------------------------------- /tests/test_branch_commits_master.atom: -------------------------------------------------------------------------------- 1 | 2 | 3 | tag:github.com,2008:/Mic92/nix-update/commits/master 4 | 5 | 6 | Recent Commits to nix-update:master 7 | 2024-02-19T14:47:50Z 8 | 9 | tag:github.com,2008:Grit::Commit/37e9b32f335b8bfafd764f49de573141f85d3416 10 | 11 | 12 | bump version 1.2.0 13 | 14 | 2024-02-19T14:47:50Z 15 | 16 | 17 | Mic92 18 | https://github.com/Mic92 19 | 20 | 21 | <pre style='white-space:pre-wrap;width:81ex'>bump version 1.2.0</pre> 22 | 23 | 24 | 25 | tag:github.com,2008:Grit::Commit/a566b9bdb17bc394cbbefdd274b54432edec6d3f 26 | 27 | 28 | print maintainers list 29 | 30 | 2024-01-25T10:38:41Z 31 | 32 | 33 | GaetanLepage 34 | https://github.com/GaetanLepage 35 | 36 | 37 | <pre style='white-space:pre-wrap;width:81ex'>print maintainers list</pre> 38 | 39 | 40 | 41 | tag:github.com,2008:Grit::Commit/6e4ee6941a8f8b3ae636fc62da06f9314e46d3d3 42 | 43 | 44 | make gitlab updater work by tag when releases are disabled 45 | 46 | 2024-01-17T14:56:30Z 47 | 48 | 49 | lilyinstarlight 50 | https://github.com/lilyinstarlight 51 | 52 | 53 | <pre style='white-space:pre-wrap;width:81ex'>make gitlab updater work by tag when releases are disabled</pre> 54 | 55 | 56 | 57 | tag:github.com,2008:Grit::Commit/ae70579d8d6736009387d8d76869c7193a08d813 58 | 59 | 60 | bump version 1.1.0 61 | 62 | 2024-01-16T11:35:20Z 63 | 64 | 65 | Mic92 66 | https://github.com/Mic92 67 | 68 | 69 | <pre style='white-space:pre-wrap;width:81ex'>bump version 1.1.0</pre> 70 | 71 | 72 | 73 | tag:github.com,2008:Grit::Commit/da997c87c73f05eddc2aec3335d4a557a8dd2ca6 74 | 75 | 76 | create-release: make sed regex more strict 77 | 78 | 2024-01-16T10:25:52Z 79 | 80 | 81 | Mic92 82 | https://github.com/Mic92 83 | 84 | 85 | <pre style='white-space:pre-wrap;width:81ex'>create-release: make sed regex more strict</pre> 86 | 87 | 88 | 89 | tag:github.com,2008:Grit::Commit/36ffca82d29b04bf285baf65ece4d11ddac42594 90 | 91 | 92 | Merge pull request #217 from Mic92/composer-support 93 | 94 | 2024-01-16T10:12:47Z 95 | 96 | 97 | Mic92 98 | https://github.com/Mic92 99 | 100 | 101 | <pre style='white-space:pre-wrap;width:81ex'>Merge pull request #217 from Mic92/composer-support 102 | 103 | Add support for updating buildComposerProject&#39;s vendorHash</pre> 104 | 105 | 106 | 107 | tag:github.com,2008:Grit::Commit/0a30179f00f5a1a6c8422281b9580138b6f83074 108 | 109 | 110 | Add support for updating buildComposerProject's vendorHash 111 | 112 | 2024-01-16T10:06:16Z 113 | 114 | 115 | gaelreyrol 116 | https://github.com/gaelreyrol 117 | 118 | 119 | <pre style='white-space:pre-wrap;width:81ex'>Add support for updating buildComposerProject&#39;s vendorHash 120 | 121 | Update tests/testpkgs/default.nix</pre> 122 | 123 | 124 | 125 | tag:github.com,2008:Grit::Commit/b80494da434277508fa968e6f1628d139b002c8f 126 | 127 | 128 | Update README.md to precise that software is available in latest stab… 129 | 130 | 2024-01-15T09:59:58Z 131 | 132 | 133 | Mic92 134 | https://github.com/Mic92 135 | 136 | 137 | <pre style='white-space:pre-wrap;width:81ex'>Update README.md to precise that software is available in latest stable channel (23.11 as of today) (#215) 138 | Update README.md 139 | 140 | Update the README to precise that the software is also in the latest stable channel, currently 23.11, shows that it has version 1.0.0 of nix-update (https://search.nixos.org/packages?channel=23.11&amp;show=nix-update&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=nix-update) 141 | 142 | * Update README.md 143 | 144 | * Update README.md</pre> 145 | 146 | 147 | 148 | tag:github.com,2008:Grit::Commit/50640aa288176dbc96346edeb8806dd44293d3e7 149 | 150 | 151 | Bump cachix/install-nix-action from 24 to 25 152 | 153 | 2024-01-15T06:33:58Z 154 | 155 | 156 | dependabot 157 | https://github.com/dependabot 158 | 159 | 160 | <pre style='white-space:pre-wrap;width:81ex'>Bump cachix/install-nix-action from 24 to 25 161 | 162 | Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 24 to 25. 163 | - [Release notes](https://github.com/cachix/install-nix-action/releases) 164 | - [Commits](https://github.com/cachix/install-nix-action/compare/v24...v25) 165 | 166 | --- 167 | updated-dependencies: 168 | - dependency-name: cachix/install-nix-action 169 | dependency-type: direct:production 170 | update-type: version-update:semver-major 171 | ... 172 | 173 | Signed-off-by: dependabot[bot] &lt;support@github.com&gt;</pre> 174 | 175 | 176 | 177 | tag:github.com,2008:Grit::Commit/4c763a7cf3770d043ef0351a40726b0f65c35eac 178 | 179 | 180 | README: document `--version=branch` option in more depth 181 | 182 | 2024-01-04T08:26:43Z 183 | 184 | 185 | Atemu 186 | https://github.com/Atemu 187 | 188 | 189 | <pre style='white-space:pre-wrap;width:81ex'>README: document `--version=branch` option in more depth</pre> 190 | 191 | 192 | 193 | tag:github.com,2008:Grit::Commit/dcdf594417903919c827a91178eac0c1f56feca5 194 | 195 | 196 | Bump cachix/install-nix-action from 23 to 24 197 | 198 | 2023-12-04T06:08:06Z 199 | 200 | 201 | dependabot 202 | https://github.com/dependabot 203 | 204 | 205 | <pre style='white-space:pre-wrap;width:81ex'>Bump cachix/install-nix-action from 23 to 24 206 | 207 | Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 23 to 24. 208 | - [Release notes](https://github.com/cachix/install-nix-action/releases) 209 | - [Commits](https://github.com/cachix/install-nix-action/compare/v23...v24) 210 | 211 | --- 212 | updated-dependencies: 213 | - dependency-name: cachix/install-nix-action 214 | dependency-type: direct:production 215 | update-type: version-update:semver-major 216 | ... 217 | 218 | Signed-off-by: dependabot[bot] &lt;support@github.com&gt;</pre> 219 | 220 | 221 | 222 | tag:github.com,2008:Grit::Commit/76c4e79c2c500e03c19abf2beb0d8b18134cf977 223 | 224 | 225 | bitbucket.org integration tests 226 | 227 | 2023-11-22T19:13:43Z 228 | 229 | 230 | EBADBEEF 231 | https://github.com/EBADBEEF 232 | 233 | 234 | <pre style='white-space:pre-wrap;width:81ex'>bitbucket.org integration tests</pre> 235 | 236 | 237 | 238 | tag:github.com,2008:Grit::Commit/a10f7c047f243a4c2076261187967246388f91ac 239 | 240 | 241 | bitbucket.org support for versions and snapshots 242 | 243 | 2023-11-22T19:13:43Z 244 | 245 | 246 | EBADBEEF 247 | https://github.com/EBADBEEF 248 | 249 | 250 | <pre style='white-space:pre-wrap;width:81ex'>bitbucket.org support for versions and snapshots</pre> 251 | 252 | 253 | 254 | tag:github.com,2008:Grit::Commit/f6f45655e7b09c9f9155d4aac1d41eab37392c28 255 | 256 | 257 | README: drop comment about black formatter 258 | 259 | 2023-11-08T07:58:55Z 260 | 261 | 262 | Mic92 263 | https://github.com/Mic92 264 | 265 | 266 | <pre style='white-space:pre-wrap;width:81ex'>README: drop comment about black formatter</pre> 267 | 268 | 269 | 270 | tag:github.com,2008:Grit::Commit/47945865f6683351c15842ed7efa990d21b975dd 271 | 272 | 273 | flake.lock: Update 274 | 275 | 2023-11-08T07:58:55Z 276 | 277 | 278 | Mic92 279 | https://github.com/Mic92 280 | 281 | 282 | <pre style='white-space:pre-wrap;width:81ex'>flake.lock: Update 283 | 284 | Flake lock file updates: 285 | 286 | • Updated input &#39;nixpkgs&#39;: 287 | &#39;github:NixOS/nixpkgs/c082856b850ec60cda9f0a0db2bc7bd8900d708c&#39; (2023-11-02) 288 | → &#39;github:NixOS/nixpkgs/ec750fd01963ab6b20ee1f0cb488754e8036d89d&#39; (2023-11-07)</pre> 289 | 290 | 291 | 292 | tag:github.com,2008:Grit::Commit/d945764f13d207ba2610d53ae2f3c7802ab8ab09 293 | 294 | 295 | nix fmt 296 | 297 | 2023-11-08T07:58:55Z 298 | 299 | 300 | Mic92 301 | https://github.com/Mic92 302 | 303 | 304 | <pre style='white-space:pre-wrap;width:81ex'>nix fmt</pre> 305 | 306 | 307 | 308 | tag:github.com,2008:Grit::Commit/6f21bf4db4256cb9d913cda8f987b138718e261c 309 | 310 | 311 | improve --help output, notably '--version' possibilities 312 | 313 | 2023-11-08T07:58:55Z 314 | 315 | 316 | teto 317 | https://github.com/teto 318 | 319 | 320 | <pre style='white-space:pre-wrap;width:81ex'>improve --help output, notably &#39;--version&#39; possibilities</pre> 321 | 322 | 323 | 324 | tag:github.com,2008:Grit::Commit/c690b76213ca791b53c55a8f6abc18bcc0049e04 325 | 326 | 327 | switch to ruff for formatting 328 | 329 | 2023-11-02T09:26:15Z 330 | 331 | 332 | Mic92 333 | https://github.com/Mic92 334 | 335 | 336 | <pre style='white-space:pre-wrap;width:81ex'>switch to ruff for formatting</pre> 337 | 338 | 339 | 340 | tag:github.com,2008:Grit::Commit/5e679528f6a181a163b7a177ce49fd9232709592 341 | 342 | 343 | flake.lock: Update 344 | 345 | 2023-11-02T09:26:15Z 346 | 347 | 348 | Mic92 349 | https://github.com/Mic92 350 | 351 | 352 | <pre style='white-space:pre-wrap;width:81ex'>flake.lock: Update 353 | 354 | Flake lock file updates: 355 | 356 | • Updated input &#39;flake-parts&#39;: 357 | &#39;github:hercules-ci/flake-parts/7f53fdb7bdc5bb237da7fefef12d099e4fd611ca&#39; (2023-09-01) 358 | → &#39;github:hercules-ci/flake-parts/8c9fa2545007b49a5db5f650ae91f227672c3877&#39; (2023-11-01) 359 | • Updated input &#39;nixpkgs&#39;: 360 | &#39;github:NixOS/nixpkgs/517501bcf14ae6ec47efd6a17dda0ca8e6d866f9&#39; (2023-09-27) 361 | → &#39;github:NixOS/nixpkgs/c082856b850ec60cda9f0a0db2bc7bd8900d708c&#39; (2023-11-02) 362 | • Updated input &#39;treefmt-nix&#39;: 363 | &#39;github:numtide/treefmt-nix/720bd006d855b08e60664e4683ccddb7a9ff614a&#39; (2023-09-27) 364 | → &#39;github:numtide/treefmt-nix/5deb8dc125a9f83b65ca86cf0c8167c46593e0b1&#39; (2023-10-27)</pre> 365 | 366 | 367 | 368 | tag:github.com,2008:Grit::Commit/957b6c4dacdc748f7c2867e26bc861903eeea70d 369 | 370 | 371 | nix-update: publish to flakestry 372 | 373 | 2023-10-24T16:10:39Z 374 | 375 | 376 | Mic92 377 | https://github.com/Mic92 378 | 379 | 380 | <pre style='white-space:pre-wrap;width:81ex'>nix-update: publish to flakestry</pre> 381 | 382 | 383 | 384 | -------------------------------------------------------------------------------- /tests/test_cargo_lock_expand.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main( 10 | ["--file", str(path), "--commit", "cargoLock.expand", "--version", "1.5.3"], 11 | ) 12 | subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "cargoLock.expand.cargoDeps", 22 | ], 23 | check=True, 24 | text=True, 25 | stdout=subprocess.PIPE, 26 | ).stdout.strip() 27 | diff = subprocess.run( 28 | ["git", "-C", path, "show"], 29 | text=True, 30 | stdout=subprocess.PIPE, 31 | check=True, 32 | ).stdout.strip() 33 | print(diff) 34 | assert "cargoLock.expand: 1.4.0 -> 1.5.3" in diff 35 | assert "Cargo.lock" in diff 36 | assert "https://github.com/Mic92/cntr/compare/1.4.0...1.5.3" in diff 37 | -------------------------------------------------------------------------------- /tests/test_cargo_lock_generate.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_simple(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main( 10 | [ 11 | "--file", 12 | str(path), 13 | "--commit", 14 | "cargoLock.generate.simple", 15 | "--version", 16 | "v0.9.8", 17 | "--generate-lockfile", 18 | ], 19 | ) 20 | subprocess.run( 21 | [ 22 | "nix", 23 | "eval", 24 | "--raw", 25 | "--extra-experimental-features", 26 | "nix-command", 27 | "-f", 28 | path, 29 | "cargoLock.generate.simple.cargoDeps", 30 | ], 31 | check=True, 32 | text=True, 33 | stdout=subprocess.PIPE, 34 | ).stdout.strip() 35 | diff = subprocess.run( 36 | ["git", "-C", path, "show"], 37 | text=True, 38 | stdout=subprocess.PIPE, 39 | check=True, 40 | ).stdout.strip() 41 | print(diff) 42 | assert ( 43 | "https://github.com/jupyter-server/pycrdt/compare/v0.9.6...v0.9.8" in diff 44 | ) 45 | 46 | 47 | def test_with_lockfile_metadata_path(helpers: conftest.Helpers) -> None: 48 | with helpers.testpkgs(init_git=True) as path: 49 | main( 50 | [ 51 | "--file", 52 | str(path), 53 | "--commit", 54 | "cargoLock.generate.with-lockfile-metadata-path", 55 | "--version", 56 | "0.12.0", 57 | "--generate-lockfile", 58 | "--lockfile-metadata-path", 59 | "python", 60 | ], 61 | ) 62 | subprocess.run( 63 | [ 64 | "nix", 65 | "eval", 66 | "--raw", 67 | "--extra-experimental-features", 68 | "nix-command", 69 | "-f", 70 | path, 71 | "cargoLock.generate.with-lockfile-metadata-path.cargoDeps", 72 | ], 73 | check=True, 74 | text=True, 75 | stdout=subprocess.PIPE, 76 | ).stdout.strip() 77 | diff = subprocess.run( 78 | ["git", "-C", path, "show"], 79 | text=True, 80 | stdout=subprocess.PIPE, 81 | check=True, 82 | ).stdout.strip() 83 | print(diff) 84 | assert ( 85 | "https://github.com/lancedb/lancedb/compare/python-v0.11.0...python-v0.12.0" 86 | in diff 87 | ) 88 | 89 | 90 | def test_with_lockfile_metadata_path_outside_workspace( 91 | helpers: conftest.Helpers, 92 | ) -> None: 93 | """A test for a project where the target Cargo.toml is outside a workspace. 94 | 95 | In this case, Cargo.lock is generated in the subdirectory where Cargo.toml is located, not in the project root. 96 | For example, https://github.com/lancedb/lance/blob/v0.16.1/Cargo.toml 97 | """ 98 | with helpers.testpkgs(init_git=True) as path: 99 | main( 100 | [ 101 | "--file", 102 | str(path), 103 | "--commit", 104 | "cargoLock.generate.with-lockfile-metadata-path-outside-workspace", 105 | "--version", 106 | "v0.16.1", 107 | "--generate-lockfile", 108 | "--lockfile-metadata-path", 109 | "python", 110 | ], 111 | ) 112 | subprocess.run( 113 | [ 114 | "nix", 115 | "eval", 116 | "--raw", 117 | "--extra-experimental-features", 118 | "nix-command", 119 | "-f", 120 | path, 121 | "cargoLock.generate.with-lockfile-metadata-path-outside-workspace.cargoDeps", 122 | ], 123 | check=True, 124 | text=True, 125 | stdout=subprocess.PIPE, 126 | ).stdout.strip() 127 | diff = subprocess.run( 128 | ["git", "-C", path, "show"], 129 | text=True, 130 | stdout=subprocess.PIPE, 131 | check=True, 132 | ).stdout.strip() 133 | print(diff) 134 | assert "https://github.com/lancedb/lance/compare/v0.15.0...v0.16.1" in diff 135 | -------------------------------------------------------------------------------- /tests/test_cargo_lock_update.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main( 10 | [ 11 | "--file", 12 | str(path), 13 | "--commit", 14 | "cargoLock.update", 15 | "--version", 16 | "0.7.3", 17 | ], 18 | ) 19 | subprocess.run( 20 | [ 21 | "nix", 22 | "eval", 23 | "--raw", 24 | "--extra-experimental-features", 25 | "nix-command", 26 | "-f", 27 | path, 28 | "cargoLock.update.cargoDeps", 29 | ], 30 | check=True, 31 | text=True, 32 | stdout=subprocess.PIPE, 33 | ).stdout.strip() 34 | diff = subprocess.run( 35 | ["git", "-C", path, "show"], 36 | text=True, 37 | stdout=subprocess.PIPE, 38 | check=True, 39 | ).stdout.strip() 40 | print(diff) 41 | assert "https://github.com/astral-sh/ruff/compare/0.7.0...0.7.3" in diff 42 | -------------------------------------------------------------------------------- /tests/test_cargo_vendor_deps.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_rust_package(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main( 10 | [ 11 | "--file", 12 | str(path), 13 | "--commit", 14 | "cargoVendorDeps.rustPackage", 15 | "--version", 16 | "0.7.3", 17 | ], 18 | ) 19 | subprocess.run( 20 | [ 21 | "nix", 22 | "eval", 23 | "--raw", 24 | "--extra-experimental-features", 25 | "nix-command", 26 | "-f", 27 | path, 28 | "cargoVendorDeps.rustPackage.cargoDeps", 29 | ], 30 | check=True, 31 | text=True, 32 | stdout=subprocess.PIPE, 33 | ).stdout.strip() 34 | 35 | diff = subprocess.run( 36 | ["git", "-C", path, "show"], 37 | text=True, 38 | stdout=subprocess.PIPE, 39 | check=True, 40 | ).stdout.strip() 41 | print(diff) 42 | assert "https://github.com/astral-sh/ruff/compare/0.7.0...0.7.3" in diff 43 | 44 | 45 | def test_non_rust_package(helpers: conftest.Helpers) -> None: 46 | with helpers.testpkgs(init_git=True) as path: 47 | main( 48 | [ 49 | "--file", 50 | str(path), 51 | "--commit", 52 | "cargoVendorDeps.nonRustPackage", 53 | "--version", 54 | "v1.3.3", 55 | ], 56 | ) 57 | subprocess.run( 58 | [ 59 | "nix", 60 | "eval", 61 | "--raw", 62 | "--extra-experimental-features", 63 | "nix-command", 64 | "-f", 65 | path, 66 | "cargoVendorDeps.nonRustPackage.cargoDeps", 67 | ], 68 | check=True, 69 | text=True, 70 | stdout=subprocess.PIPE, 71 | ).stdout.strip() 72 | 73 | diff = subprocess.run( 74 | ["git", "-C", path, "show"], 75 | text=True, 76 | stdout=subprocess.PIPE, 77 | check=True, 78 | ).stdout.strip() 79 | print(diff) 80 | assert "https://github.com/pop-os/popsicle/compare/1.3.0...1.3.3" in diff 81 | -------------------------------------------------------------------------------- /tests/test_composer.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options(attribute="composer", import_path=str(path)) 11 | update(opts) 12 | version = subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "composer.version", 22 | ], 23 | text=True, 24 | stdout=subprocess.PIPE, 25 | check=False, 26 | ).stdout.strip() 27 | assert tuple(map(int, version.split("."))) >= (11, 3, 1) 28 | -------------------------------------------------------------------------------- /tests/test_composer_old.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from nix_update.version.version import VersionPreference 6 | from tests import conftest 7 | 8 | 9 | def test_update(helpers: conftest.Helpers) -> None: 10 | with helpers.testpkgs() as path: 11 | opts = Options( 12 | attribute="composer-old", 13 | import_path=str(path), 14 | # For 0.14.0 we get inconsistent lock file errors 15 | version="0.13.1", 16 | version_preference=VersionPreference.FIXED, 17 | ) 18 | update(opts) 19 | version = subprocess.run( 20 | [ 21 | "nix", 22 | "eval", 23 | "--raw", 24 | "--extra-experimental-features", 25 | "nix-command", 26 | "-f", 27 | path, 28 | "composer-old.version", 29 | ], 30 | text=True, 31 | stdout=subprocess.PIPE, 32 | check=False, 33 | ).stdout.strip() 34 | assert tuple(map(int, version.split("."))) >= (0, 11, 1) 35 | -------------------------------------------------------------------------------- /tests/test_fetchurl_github_release.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | import pytest 5 | 6 | from nix_update import main 7 | from tests import conftest 8 | 9 | 10 | @pytest.mark.skipif( 11 | "GITHUB_TOKEN" not in os.environ, 12 | reason="No GITHUB_TOKEN environment variable set", 13 | ) 14 | def test_fixed_version_no_prefix(helpers: conftest.Helpers) -> None: 15 | with helpers.testpkgs(init_git=True) as path: 16 | main( 17 | [ 18 | "--file", 19 | str(path), 20 | "--commit", 21 | "--version", 22 | "5.1.0", 23 | "fetchurl-github-release", 24 | ], 25 | ) 26 | commit = subprocess.run( 27 | ["git", "-C", path, "log", "-1"], 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | check=True, 31 | ).stdout.strip() 32 | assert "https://github.com/vrana/adminer/compare/v5.0.5...v5.1.0" in commit 33 | 34 | 35 | @pytest.mark.skipif( 36 | "GITHUB_TOKEN" not in os.environ, 37 | reason="No GITHUB_TOKEN environment variable set", 38 | ) 39 | def test_fixed_version_yes_prefix(helpers: conftest.Helpers) -> None: 40 | with helpers.testpkgs(init_git=True) as path: 41 | main( 42 | [ 43 | "--file", 44 | str(path), 45 | "--commit", 46 | "--version", 47 | "v5.1.0", 48 | "fetchurl-github-release", 49 | ], 50 | ) 51 | commit = subprocess.run( 52 | ["git", "-C", path, "log", "-1"], 53 | text=True, 54 | stdout=subprocess.PIPE, 55 | check=True, 56 | ).stdout.strip() 57 | assert "https://github.com/vrana/adminer/compare/v5.0.5...v5.1.0" in commit 58 | 59 | 60 | @pytest.mark.skipif( 61 | "GITHUB_TOKEN" not in os.environ, 62 | reason="No GITHUB_TOKEN environment variable set", 63 | ) 64 | def test_auto_version(helpers: conftest.Helpers) -> None: 65 | with helpers.testpkgs(init_git=True) as path: 66 | main( 67 | [ 68 | "--file", 69 | str(path), 70 | "--commit", 71 | "fetchurl-github-release", 72 | ], 73 | ) 74 | commit = subprocess.run( 75 | ["git", "-C", path, "log", "-1"], 76 | text=True, 77 | stdout=subprocess.PIPE, 78 | check=True, 79 | ).stdout.strip() 80 | assert "https://github.com/vrana/adminer/compare/v5.0.5...v" in commit 81 | -------------------------------------------------------------------------------- /tests/test_flake.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main(["--file", str(path), "--flake", "--commit", "--test", "crate"]) 10 | version = subprocess.run( 11 | [ 12 | "nix", 13 | "eval", 14 | "--raw", 15 | "--extra-experimental-features", 16 | "flakes nix-command", 17 | f"{path}#crate.version", 18 | ], 19 | check=True, 20 | text=True, 21 | stdout=subprocess.PIPE, 22 | ).stdout.strip() 23 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 24 | commit = subprocess.run( 25 | ["git", "-C", path, "log", "-1"], 26 | text=True, 27 | stdout=subprocess.PIPE, 28 | check=True, 29 | ).stdout.strip() 30 | print(commit) 31 | assert f"crate: 8.0.0 -> {version}" in commit 32 | assert "https://diff.rs/fd-find/8.0.0/" in commit 33 | 34 | diff = subprocess.run( 35 | ["git", "-C", path, "show"], 36 | text=True, 37 | stdout=subprocess.PIPE, 38 | check=True, 39 | ).stdout.strip() 40 | print(diff) 41 | assert "https://diff.rs/fd-find/8.0.0/10.2.0" in diff 42 | 43 | 44 | def test_update_script(helpers: conftest.Helpers) -> None: 45 | with helpers.testpkgs(init_git=True) as path: 46 | main( 47 | [ 48 | "--file", 49 | str(path), 50 | "--flake", 51 | "--commit", 52 | "--test", 53 | "--use-update-script", 54 | "crate", 55 | ], 56 | ) 57 | version = subprocess.run( 58 | [ 59 | "nix", 60 | "eval", 61 | "--raw", 62 | "--extra-experimental-features", 63 | "flakes nix-command", 64 | f"{path}#crate.version", 65 | ], 66 | check=True, 67 | text=True, 68 | stdout=subprocess.PIPE, 69 | ).stdout.strip() 70 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 71 | commit = subprocess.run( 72 | ["git", "-C", path, "log", "-1"], 73 | text=True, 74 | stdout=subprocess.PIPE, 75 | check=True, 76 | ).stdout.strip() 77 | print(commit) 78 | assert f"crate: 8.0.0 -> {version}" in commit 79 | -------------------------------------------------------------------------------- /tests/test_git.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from nix_update.git import old_version_from_diff 4 | from tests import conftest 5 | 6 | TEST_ROOT = Path(__file__).parent.resolve() 7 | 8 | 9 | def test_worddiff(helpers: conftest.Helpers) -> None: 10 | with helpers.root().joinpath("consul.patch").open() as f: 11 | diff = f.read() 12 | s = old_version_from_diff(diff, 5, "1.9.0") 13 | assert s == "1.8.6" 14 | -------------------------------------------------------------------------------- /tests/test_gitea.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main(["--file", str(path), "--commit", "gitea"]) 10 | version = subprocess.run( 11 | [ 12 | "nix", 13 | "eval", 14 | "--raw", 15 | "--extra-experimental-features", 16 | "nix-command", 17 | "-f", 18 | path, 19 | "gitea.version", 20 | ], 21 | check=True, 22 | text=True, 23 | stdout=subprocess.PIPE, 24 | ).stdout.strip() 25 | assert int(version) >= 30 26 | commit = subprocess.run( 27 | ["git", "-C", path, "log", "-1"], 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | check=True, 31 | ).stdout.strip() 32 | print(commit) 33 | assert version in commit 34 | assert "gitea" in commit 35 | assert "https://codeberg.org/nsxiv/nsxiv/compare/v29...v" in commit 36 | -------------------------------------------------------------------------------- /tests/test_github.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | import pytest 5 | 6 | from nix_update import main 7 | from tests import conftest 8 | 9 | 10 | @pytest.mark.skipif( 11 | "GITHUB_TOKEN" not in os.environ, 12 | reason="No GITHUB_TOKEN environment variable set", 13 | ) 14 | def test_github_api(helpers: conftest.Helpers) -> None: 15 | with helpers.testpkgs(init_git=True) as path: 16 | main(["--file", str(path), "--commit", "github"]) 17 | version = subprocess.run( 18 | [ 19 | "nix", 20 | "eval", 21 | "--raw", 22 | "--extra-experimental-features", 23 | "nix-command", 24 | "-f", 25 | path, 26 | "github.version", 27 | ], 28 | check=True, 29 | text=True, 30 | stdout=subprocess.PIPE, 31 | ).stdout.strip() 32 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 33 | commit = subprocess.run( 34 | ["git", "-C", path, "log", "-1"], 35 | text=True, 36 | stdout=subprocess.PIPE, 37 | check=True, 38 | ).stdout.strip() 39 | print(commit) 40 | assert version in commit 41 | assert "github" in commit 42 | assert "https://github.com/sharkdp/fd/compare/v8.0.0...v" in commit 43 | 44 | 45 | @pytest.mark.skipif( 46 | "GITHUB_TOKEN" not in os.environ, 47 | reason="No GITHUB_TOKEN environment variable set", 48 | ) 49 | def test_github_empty_fallback(helpers: conftest.Helpers) -> None: 50 | with helpers.testpkgs(init_git=True) as path: 51 | main(["--file", str(path), "--commit", "github-no-release"]) 52 | version = subprocess.run( 53 | [ 54 | "nix", 55 | "eval", 56 | "--raw", 57 | "--extra-experimental-features", 58 | "nix-command", 59 | "-f", 60 | path, 61 | "github-no-release.version", 62 | ], 63 | check=True, 64 | text=True, 65 | stdout=subprocess.PIPE, 66 | ).stdout.strip() 67 | assert tuple(map(int, version.split("."))) >= (4, 4, 3) 68 | commit = subprocess.run( 69 | ["git", "-C", path, "log", "-1"], 70 | text=True, 71 | stdout=subprocess.PIPE, 72 | check=True, 73 | ).stdout.strip() 74 | print(commit) 75 | assert version in commit 76 | assert "github" in commit 77 | assert ( 78 | "https://github.com/ProtonVPN/proton-vpn-gtk-app/compare/v4.3.2...v" 79 | in commit 80 | ) 81 | 82 | 83 | def test_github_tag(helpers: conftest.Helpers) -> None: 84 | with helpers.testpkgs(init_git=True) as path: 85 | monkeypatch = pytest.MonkeyPatch() 86 | monkeypatch.setenv("GITHUB_TOKEN", "invalid_token") 87 | main(["--file", str(path), "--commit", "github-tag"]) 88 | version = subprocess.run( 89 | [ 90 | "nix", 91 | "eval", 92 | "--raw", 93 | "--extra-experimental-features", 94 | "nix-command", 95 | "-f", 96 | path, 97 | "github-tag.version", 98 | ], 99 | check=True, 100 | text=True, 101 | stdout=subprocess.PIPE, 102 | ).stdout.strip() 103 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 104 | commit = subprocess.run( 105 | ["git", "-C", path, "log", "-1"], 106 | text=True, 107 | stdout=subprocess.PIPE, 108 | check=True, 109 | ).stdout.strip() 110 | print(commit) 111 | assert version in commit 112 | assert "github" in commit 113 | assert "https://github.com/sharkdp/fd/compare/v8.0.0...v" in commit 114 | 115 | 116 | def test_github_feed_fallback(helpers: conftest.Helpers) -> None: 117 | with helpers.testpkgs(init_git=True) as path: 118 | monkeypatch = pytest.MonkeyPatch() 119 | monkeypatch.setenv("GITHUB_TOKEN", "invalid_token") 120 | main(["--file", str(path), "--commit", "github"]) 121 | version = subprocess.run( 122 | [ 123 | "nix", 124 | "eval", 125 | "--raw", 126 | "--extra-experimental-features", 127 | "nix-command", 128 | "-f", 129 | path, 130 | "github.version", 131 | ], 132 | check=True, 133 | text=True, 134 | stdout=subprocess.PIPE, 135 | ).stdout.strip() 136 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 137 | commit = subprocess.run( 138 | ["git", "-C", path, "log", "-1"], 139 | text=True, 140 | stdout=subprocess.PIPE, 141 | check=True, 142 | ).stdout.strip() 143 | print(commit) 144 | assert version in commit 145 | assert "github" in commit 146 | assert "https://github.com/sharkdp/fd/compare/v8.0.0...v" in commit 147 | 148 | 149 | def test_github_fetchtree(helpers: conftest.Helpers) -> None: 150 | with helpers.testpkgs(init_git=True) as path: 151 | monkeypatch = pytest.MonkeyPatch() 152 | monkeypatch.setenv("GITHUB_TOKEN", "invalid_token") 153 | main(["--file", str(path), "--commit", "github-fetchtree"]) 154 | version = subprocess.run( 155 | [ 156 | "nix", 157 | "eval", 158 | "--raw", 159 | "--extra-experimental-features", 160 | "nix-command fetch-tree", 161 | "-f", 162 | path, 163 | "github-fetchtree.version", 164 | ], 165 | check=True, 166 | text=True, 167 | stdout=subprocess.PIPE, 168 | ).stdout.strip() 169 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 170 | commit = subprocess.run( 171 | ["git", "-C", path, "log", "-1"], 172 | text=True, 173 | stdout=subprocess.PIPE, 174 | check=True, 175 | ).stdout.strip() 176 | print(commit) 177 | assert version in commit 178 | assert "github" in commit 179 | -------------------------------------------------------------------------------- /tests/test_gitlab.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main(["--file", str(path), "--commit", "gitlab"]) 10 | version = subprocess.run( 11 | [ 12 | "nix", 13 | "eval", 14 | "--raw", 15 | "--extra-experimental-features", 16 | "nix-command", 17 | "-f", 18 | path, 19 | "gitlab.version", 20 | ], 21 | check=True, 22 | text=True, 23 | stdout=subprocess.PIPE, 24 | ).stdout.strip() 25 | assert tuple(map(int, version.split("."))) >= (0, 22, 0) 26 | commit = subprocess.run( 27 | ["git", "-C", path, "log", "-1"], 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | check=True, 31 | ).stdout.strip() 32 | print(commit) 33 | assert version in commit 34 | assert "gitlab" in commit 35 | assert ( 36 | "https://gitlab.gnome.org/world/phosh/phosh/-/compare/v0.20.0...v" in commit 37 | ) 38 | -------------------------------------------------------------------------------- /tests/test_maven.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options(attribute="maven", import_path=str(path)) 11 | update(opts) 12 | version = subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "maven.version", 22 | ], 23 | text=True, 24 | stdout=subprocess.PIPE, 25 | check=False, 26 | ).stdout.strip() 27 | assert tuple(map(int, version.split("."))) > (3, 3, 0) 28 | -------------------------------------------------------------------------------- /tests/test_mix.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options(attribute="mix", import_path=str(path)) 11 | update(opts) 12 | mix_hash = subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "mix.mixFodDeps.outputHash", 22 | ], 23 | text=True, 24 | stdout=subprocess.PIPE, 25 | check=False, 26 | ).stdout.strip() 27 | assert mix_hash != "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 28 | -------------------------------------------------------------------------------- /tests/test_npm.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options(attribute="npm", import_path=str(path)) 11 | update(opts) 12 | version = subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "npm.version", 22 | ], 23 | text=True, 24 | stdout=subprocess.PIPE, 25 | check=False, 26 | ).stdout.strip() 27 | assert tuple(map(int, version.split("."))) > (10, 8, 6) 28 | -------------------------------------------------------------------------------- /tests/test_npm_lock_generate.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_update(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main( 10 | [ 11 | "--file", 12 | str(path), 13 | "--commit", 14 | "npm-lock-generate", 15 | "--version", 16 | "v2.6.0", 17 | "--generate-lockfile", 18 | ], 19 | ) 20 | npm_deps_name = subprocess.run( 21 | [ 22 | "nix", 23 | "eval", 24 | "--raw", 25 | "--extra-experimental-features", 26 | "nix-command", 27 | "-f", 28 | path, 29 | "npm-lock-generate.npmDeps.name", 30 | ], 31 | check=True, 32 | text=True, 33 | stdout=subprocess.PIPE, 34 | ).stdout.strip() 35 | diff = subprocess.run( 36 | ["git", "-C", path, "show"], 37 | text=True, 38 | stdout=subprocess.PIPE, 39 | check=True, 40 | ).stdout.strip() 41 | print(diff) 42 | assert "2.6.0" in npm_deps_name 43 | assert ( 44 | "https://github.com/olrtg/emmet-language-server/compare/v2.5.0...v2.6.0" 45 | in diff 46 | ) 47 | -------------------------------------------------------------------------------- /tests/test_npm_package.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main(["--file", str(path), "--commit", "npm-package"]) 10 | version = subprocess.run( 11 | [ 12 | "nix", 13 | "eval", 14 | "--raw", 15 | "--extra-experimental-features", 16 | "nix-command", 17 | "-f", 18 | path, 19 | "npm-package.version", 20 | ], 21 | check=True, 22 | text=True, 23 | stdout=subprocess.PIPE, 24 | ).stdout.strip() 25 | commit = subprocess.run( 26 | ["git", "-C", path, "show"], 27 | text=True, 28 | stdout=subprocess.PIPE, 29 | check=True, 30 | ).stdout.strip() 31 | print(commit) 32 | assert version in commit 33 | assert "npm" in commit 34 | assert "https://npmdiff.dev/pnpm/9.1.3/" in commit 35 | -------------------------------------------------------------------------------- /tests/test_nuget_deps_generate.py: -------------------------------------------------------------------------------- 1 | import json 2 | import subprocess 3 | 4 | from nix_update import main 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs(init_git=True) as path: 10 | main( 11 | [ 12 | "--file", 13 | str(path), 14 | "--commit", 15 | "nuget-deps-generate", 16 | "--version", 17 | "v1.1.1", 18 | ], 19 | ) 20 | 21 | nuget_deps_raw = subprocess.run( 22 | [ 23 | "nix", 24 | "eval", 25 | "--json", 26 | "--extra-experimental-features", 27 | "nix-command", 28 | "-f", 29 | path, 30 | "nuget-deps-generate.nugetDeps", 31 | ], 32 | check=True, 33 | text=True, 34 | stdout=subprocess.PIPE, 35 | ).stdout.strip() 36 | nuget_deps = json.loads(nuget_deps_raw) 37 | assert len(nuget_deps) > 0 38 | 39 | diff = subprocess.run( 40 | ["git", "-C", path, "log"], 41 | text=True, 42 | stdout=subprocess.PIPE, 43 | check=True, 44 | ).stdout.strip() 45 | print(diff) 46 | assert "https://github.com/ExOK/Celeste64/compare/v1.1.0...v1.1.1" in diff 47 | -------------------------------------------------------------------------------- /tests/test_pnpm.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options(attribute="pnpm", import_path=str(path)) 11 | update(opts) 12 | pnpm_hash = subprocess.run( 13 | [ 14 | "nix", 15 | "eval", 16 | "--raw", 17 | "--extra-experimental-features", 18 | "nix-command", 19 | "-f", 20 | path, 21 | "pnpm.pnpmDeps.outputHash", 22 | ], 23 | text=True, 24 | stdout=subprocess.PIPE, 25 | check=False, 26 | ).stdout.strip() 27 | assert pnpm_hash != "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 28 | -------------------------------------------------------------------------------- /tests/test_pypi.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update import main 4 | from tests import conftest 5 | 6 | 7 | def test_main(helpers: conftest.Helpers) -> None: 8 | with helpers.testpkgs(init_git=True) as path: 9 | main(["--file", str(path), "--commit", "pypi"]) 10 | version = subprocess.run( 11 | [ 12 | "nix", 13 | "eval", 14 | "--raw", 15 | "--extra-experimental-features", 16 | "nix-command", 17 | "-f", 18 | path, 19 | "pypi.version", 20 | ], 21 | check=True, 22 | text=True, 23 | stdout=subprocess.PIPE, 24 | ).stdout.strip() 25 | assert tuple(map(int, version.split("."))) >= (3, 0, 1) 26 | commit = subprocess.run( 27 | ["git", "-C", path, "log", "-1"], 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | check=True, 31 | ).stdout.strip() 32 | print(commit) 33 | assert f"pypi: 2.0.0 -> {version}" in commit 34 | assert ( 35 | f"https://github.com/Mic92/python-mpd2/blob/{version}/doc/changes.rst" 36 | in commit 37 | ) 38 | -------------------------------------------------------------------------------- /tests/test_savanna.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import urllib.error 3 | import urllib.request 4 | 5 | import pytest 6 | 7 | from nix_update.options import Options 8 | from nix_update.update import update 9 | from tests import conftest 10 | 11 | 12 | def test_update(helpers: conftest.Helpers) -> None: 13 | try: 14 | response = urllib.request.urlopen( 15 | "https://download.savannah.nongnu.org/releases/fileschanged/?C=M&O=D", 16 | timeout=5, 17 | ) 18 | response.read() 19 | except (urllib.error.URLError, TimeoutError, ConnectionError): 20 | # savannah's api seems to have issues lately 21 | pytest.xfail("Savana is taking too long to respond") 22 | 23 | with helpers.testpkgs() as path: 24 | opts = Options(attribute="savanna", import_path=str(path)) 25 | update(opts) 26 | version = subprocess.run( 27 | [ 28 | "nix", 29 | "eval", 30 | "--raw", 31 | "--extra-experimental-features", 32 | "nix-command", 33 | "-f", 34 | path, 35 | "savanna.version", 36 | ], 37 | text=True, 38 | stdout=subprocess.PIPE, 39 | check=False, 40 | ).stdout.strip() 41 | assert tuple(map(int, version.split("."))) >= (0, 6, 8) 42 | -------------------------------------------------------------------------------- /tests/test_sourcehut.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from datetime import date 3 | from urllib.parse import urlparse 4 | 5 | import pytest 6 | 7 | from nix_update.options import Options 8 | from nix_update.update import update 9 | from nix_update.version import VersionPreference, fetch_latest_version 10 | from tests import conftest 11 | 12 | 13 | def test_update(helpers: conftest.Helpers) -> None: 14 | with helpers.testpkgs() as path: 15 | opts = Options(attribute="sourcehut", import_path=str(path)) 16 | update(opts) 17 | version = subprocess.run( 18 | [ 19 | "nix", 20 | "eval", 21 | "--raw", 22 | "--extra-experimental-features", 23 | "nix-command", 24 | "-f", 25 | path, 26 | "sourcehut.version", 27 | ], 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | check=False, 31 | ).stdout.strip() 32 | assert tuple(map(int, version.split("."))) >= (0, 3, 6) 33 | 34 | 35 | @pytest.mark.usefixtures("helpers") 36 | def test_branch() -> None: 37 | version = fetch_latest_version( 38 | urlparse("https://git.sr.ht/~jcc/addr-book-combine"), 39 | VersionPreference.BRANCH, 40 | "(.*)", 41 | "master", 42 | ).number 43 | version_date = date.fromisoformat(version[-10:]) 44 | assert version_date >= date(2022, 12, 14) 45 | -------------------------------------------------------------------------------- /tests/test_src_only.py: -------------------------------------------------------------------------------- 1 | import json 2 | import subprocess 3 | 4 | from nix_update import main 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs(init_git=True) as path: 10 | main( 11 | [ 12 | "--file", 13 | str(path), 14 | "--commit", 15 | "nuget-deps-generate", 16 | "--version", 17 | "v1.1.1", 18 | "--src-only", 19 | ], 20 | ) 21 | 22 | nuget_deps_raw = subprocess.run( 23 | [ 24 | "nix", 25 | "eval", 26 | "--json", 27 | "--extra-experimental-features", 28 | "nix-command", 29 | "-f", 30 | path, 31 | "nuget-deps-generate.nugetDeps", 32 | ], 33 | check=True, 34 | text=True, 35 | stdout=subprocess.PIPE, 36 | ).stdout.strip() 37 | nuget_deps = json.loads(nuget_deps_raw) 38 | assert len(nuget_deps) == 0 39 | 40 | diff = subprocess.run( 41 | ["git", "-C", path, "log"], 42 | text=True, 43 | stdout=subprocess.PIPE, 44 | check=True, 45 | ).stdout.strip() 46 | print(diff) 47 | assert "https://github.com/ExOK/Celeste64/compare/v1.1.0...v1.1.1" in diff 48 | -------------------------------------------------------------------------------- /tests/test_subpackage.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from nix_update.options import Options 4 | from nix_update.update import update 5 | from tests import conftest 6 | 7 | 8 | def test_update(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs() as path: 10 | opts = Options( 11 | attribute="subpackage", 12 | subpackages=["autobrr-web"], 13 | import_path=str(path), 14 | ) 15 | update(opts) 16 | 17 | def get_attr(attr: str) -> str: 18 | return subprocess.run( 19 | [ 20 | "nix", 21 | "eval", 22 | "--raw", 23 | "--extra-experimental-features", 24 | "nix-command", 25 | "-f", 26 | path, 27 | attr, 28 | ], 29 | text=True, 30 | stdout=subprocess.PIPE, 31 | check=False, 32 | ).stdout.strip() 33 | 34 | subpackage_hash = get_attr("subpackage.autobrr-web.pnpmDeps.outputHash") 35 | assert subpackage_hash != "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 36 | 37 | src_hash = get_attr("subpackage.src.outputHash") 38 | assert src_hash != "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 39 | 40 | gomodules_hash = get_attr("subpackage.goModules.outputHash") 41 | assert gomodules_hash != "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" 42 | 43 | version = get_attr("subpackage.version") 44 | assert tuple(map(int, version.split("."))) >= (1, 53, 0) 45 | -------------------------------------------------------------------------------- /tests/test_update.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | import pytest 5 | 6 | from nix_update import main 7 | from tests import conftest 8 | 9 | 10 | @pytest.mark.skipif( 11 | "GITHUB_TOKEN" not in os.environ, 12 | reason="No GITHUB_TOKEN environment variable set", 13 | ) 14 | def test_multiple_sources(helpers: conftest.Helpers) -> None: 15 | with helpers.testpkgs(init_git=True) as path: 16 | main(["--file", str(path), "--commit", "set.fd"]) 17 | fd_version = subprocess.run( 18 | [ 19 | "nix", 20 | "eval", 21 | "--raw", 22 | "--extra-experimental-features", 23 | "nix-command", 24 | "-f", 25 | path, 26 | "set.fd.version", 27 | ], 28 | check=True, 29 | text=True, 30 | stdout=subprocess.PIPE, 31 | ).stdout.strip() 32 | skim_version = subprocess.run( 33 | [ 34 | "nix", 35 | "eval", 36 | "--raw", 37 | "--extra-experimental-features", 38 | "nix-command", 39 | "-f", 40 | path, 41 | "set.skim.version", 42 | ], 43 | check=True, 44 | text=True, 45 | stdout=subprocess.PIPE, 46 | ).stdout.strip() 47 | assert tuple(map(int, fd_version.split("."))) >= (4, 4, 3) 48 | assert tuple(map(int, skim_version.split("."))) == (0, 0, 0) 49 | commit = subprocess.run( 50 | ["git", "-C", path, "log", "-1"], 51 | text=True, 52 | stdout=subprocess.PIPE, 53 | check=True, 54 | ).stdout.strip() 55 | print(commit) 56 | assert fd_version in commit 57 | assert "sharkdp/fd/compare/v0.0.0..." in commit 58 | assert "skim" not in commit 59 | 60 | 61 | @pytest.mark.skipif( 62 | "GITHUB_TOKEN" not in os.environ, 63 | reason="No GITHUB_TOKEN environment variable set", 64 | ) 65 | def test_let_bound_version(helpers: conftest.Helpers) -> None: 66 | with helpers.testpkgs(init_git=True) as path: 67 | main(["--file", str(path), "--commit", "let-bound-version"]) 68 | version = subprocess.run( 69 | [ 70 | "nix", 71 | "eval", 72 | "--raw", 73 | "--extra-experimental-features", 74 | "nix-command", 75 | "-f", 76 | path, 77 | "let-bound-version.version", 78 | ], 79 | check=True, 80 | text=True, 81 | stdout=subprocess.PIPE, 82 | ).stdout.strip() 83 | assert tuple(map(int, version.split("."))) >= (8, 5, 2) 84 | commit = subprocess.run( 85 | ["git", "-C", path, "log", "-1"], 86 | text=True, 87 | stdout=subprocess.PIPE, 88 | check=True, 89 | ).stdout.strip() 90 | print(commit) 91 | assert version in commit 92 | assert "github" in commit 93 | assert "https://github.com/sharkdp/fd/compare/v8.0.0...v" in commit 94 | -------------------------------------------------------------------------------- /tests/test_version_regex_no_rev.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from pathlib import Path 3 | 4 | from nix_update import main 5 | from tests import conftest 6 | 7 | 8 | def test_main(helpers: conftest.Helpers) -> None: 9 | with helpers.testpkgs(init_git=True) as path: 10 | main( 11 | [ 12 | "--file", 13 | str(path), 14 | "--commit", 15 | "net-news-wire", 16 | "--version-regex", 17 | "^mac-(\\d+\\.\\d+\\.\\d+(?:b\\d+)?)$", 18 | ], 19 | ) 20 | version = get_nix_value(path, "net-news-wire.version") 21 | src = get_nix_value(path, "net-news-wire.src") 22 | commit = subprocess.run( 23 | ["git", "-C", path, "show"], 24 | text=True, 25 | stdout=subprocess.PIPE, 26 | check=True, 27 | ).stdout.strip() 28 | print(commit) 29 | assert src != "/nix/store/8k7nkbk4xbxwc6zc2bp85i8pvbvzzx6a-NetNewsWire6.1.5.zip" 30 | assert version != "6.1.5" 31 | assert version in commit 32 | assert "net-news-wire: 6.1.5 ->" in commit 33 | 34 | 35 | def get_nix_value(path: Path, key: str) -> str: 36 | return subprocess.run( 37 | [ 38 | "nix", 39 | "eval", 40 | "--raw", 41 | "--extra-experimental-features", 42 | "nix-command", 43 | "-f", 44 | path, 45 | key, 46 | ], 47 | check=True, 48 | text=True, 49 | stdout=subprocess.PIPE, 50 | ).stdout.strip() 51 | -------------------------------------------------------------------------------- /tests/testpkgs/bitbucket.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | fetchFromBitbucket, 4 | isSnapshot, 5 | }: 6 | 7 | let 8 | # Why this package? No reason, I just found a small package that uses tags 9 | # for release by grepping through nixpkgs for fetchFromBitbucket. 10 | owner = "nielsenb"; 11 | repo = "aniso8601"; 12 | # As of 2024-04-23, latest version is 9.0.1, so we will be testing that it 13 | # finds a version greater than 9.0.0. The rev from 2021-03-02 is an untagged 14 | # commit. 15 | version = if (isSnapshot) then "0.16-unstable-2022-10-01" else "9.0.0"; 16 | rev = if (isSnapshot) then "55b1b849a57341a303ae47eb67c7ecf8c283b7f8" else "v9.0.0"; 17 | in 18 | stdenv.mkDerivation rec { 19 | pname = repo; 20 | inherit version; 21 | src = fetchFromBitbucket { 22 | inherit 23 | owner 24 | repo 25 | version 26 | rev 27 | ; 28 | # dont care about hash 29 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-expand/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "addr2line" 5 | version = "0.13.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "adler" 13 | version = "0.2.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "argparse" 18 | version = "0.2.2" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | 21 | [[package]] 22 | name = "autocfg" 23 | version = "1.0.1" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.53" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "addr2line 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "object 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", 37 | ] 38 | 39 | [[package]] 40 | name = "bitflags" 41 | version = "1.2.1" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | 44 | [[package]] 45 | name = "cc" 46 | version = "1.0.61" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | 49 | [[package]] 50 | name = "cfg-if" 51 | version = "0.1.10" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | 54 | [[package]] 55 | name = "cfg-if" 56 | version = "1.0.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | 59 | [[package]] 60 | name = "cloudabi" 61 | version = "0.1.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | dependencies = [ 64 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "cntr" 69 | version = "1.3.0" 70 | dependencies = [ 71 | "argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "cntr-fuse 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "concurrent-hashmap 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "cpuprofiler 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "nix 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "parking_lot 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 79 | ] 80 | 81 | [[package]] 82 | name = "cntr-fuse" 83 | version = "0.4.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | dependencies = [ 86 | "cntr-fuse-abi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "cntr-fuse-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 88 | "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "cntr-fuse-abi" 94 | version = "0.4.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | 97 | [[package]] 98 | name = "cntr-fuse-sys" 99 | version = "0.4.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 103 | ] 104 | 105 | [[package]] 106 | name = "concurrent-hashmap" 107 | version = "0.2.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | dependencies = [ 110 | "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 111 | ] 112 | 113 | [[package]] 114 | name = "cpuprofiler" 115 | version = "0.0.4" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | dependencies = [ 118 | "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "error-chain" 125 | version = "0.12.4" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | dependencies = [ 128 | "backtrace 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "gimli" 134 | version = "0.22.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | 137 | [[package]] 138 | name = "instant" 139 | version = "0.1.8" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 143 | ] 144 | 145 | [[package]] 146 | name = "lazy_static" 147 | version = "1.4.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | 150 | [[package]] 151 | name = "libc" 152 | version = "0.2.80" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | 155 | [[package]] 156 | name = "lock_api" 157 | version = "0.4.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 161 | ] 162 | 163 | [[package]] 164 | name = "log" 165 | version = "0.4.11" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | dependencies = [ 168 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 169 | ] 170 | 171 | [[package]] 172 | name = "miniz_oxide" 173 | version = "0.4.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | dependencies = [ 176 | "adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 177 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 178 | ] 179 | 180 | [[package]] 181 | name = "nix" 182 | version = "0.19.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | dependencies = [ 185 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", 189 | ] 190 | 191 | [[package]] 192 | name = "object" 193 | version = "0.21.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | 196 | [[package]] 197 | name = "parking_lot" 198 | version = "0.11.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | dependencies = [ 201 | "instant 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "lock_api 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 204 | ] 205 | 206 | [[package]] 207 | name = "parking_lot_core" 208 | version = "0.8.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | dependencies = [ 211 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "instant 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 214 | "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "smallvec 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "pkg-config" 222 | version = "0.3.19" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | 225 | [[package]] 226 | name = "redox_syscall" 227 | version = "0.1.57" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | 230 | [[package]] 231 | name = "rustc-demangle" 232 | version = "0.1.18" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | 235 | [[package]] 236 | name = "scopeguard" 237 | version = "1.1.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | 240 | [[package]] 241 | name = "smallvec" 242 | version = "1.4.2" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | 245 | [[package]] 246 | name = "spin" 247 | version = "0.4.10" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | 250 | [[package]] 251 | name = "version_check" 252 | version = "0.9.2" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | 255 | [[package]] 256 | name = "winapi" 257 | version = "0.3.9" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | dependencies = [ 260 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "winapi-i686-pc-windows-gnu" 266 | version = "0.4.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | 269 | [[package]] 270 | name = "winapi-x86_64-pc-windows-gnu" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | 274 | [metadata] 275 | "checksum addr2line 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" 276 | "checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 277 | "checksum argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f8ebf5827e4ac4fd5946560e6a99776ea73b596d80898f357007317a7141e47" 278 | "checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 279 | "checksum backtrace 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)" = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e" 280 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 281 | "checksum cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)" = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" 282 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 283 | "checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 284 | "checksum cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" 285 | "checksum cntr-fuse 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18aade3cdc06f81ad887ceda79055d7e3d755871f69c95f7d294efc38833f5c0" 286 | "checksum cntr-fuse-abi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34c51ee584968575df4aaa084964d9d43f2f2945bb2c0f043035e7c8e0da82dc" 287 | "checksum cntr-fuse-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ae3892895a649be4477b275deeca4a4f5e64f31c7986eb3dab81d6cfa88491a" 288 | "checksum concurrent-hashmap 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9b497d3441e4e31ef52273f344aacf0a24d28a353837451a45df9b3e9cf7d44a" 289 | "checksum cpuprofiler 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "43f8479dbcfd2bbaa0c0c26779b913052b375981cdf533091f2127ea3d42e52b" 290 | "checksum error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 291 | "checksum gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" 292 | "checksum instant 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" 293 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 294 | "checksum libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" 295 | "checksum lock_api 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 296 | "checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 297 | "checksum miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 298 | "checksum nix 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85db2feff6bf70ebc3a4793191517d5f0331100a2f10f9bf93b5e5214f32b7b7" 299 | "checksum object 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" 300 | "checksum parking_lot 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 301 | "checksum parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" 302 | "checksum pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 303 | "checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 304 | "checksum rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" 305 | "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 306 | "checksum smallvec 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 307 | "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" 308 | "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 309 | "checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 310 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 311 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 312 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-expand/default.nix: -------------------------------------------------------------------------------- 1 | { rustPlatform, fetchFromGitHub }: 2 | 3 | rustPlatform.buildRustPackage rec { 4 | pname = "cntr"; 5 | version = "1.4.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "Mic92"; 9 | repo = "cntr"; 10 | rev = version; 11 | hash = "sha256-rIDAPtDMth/5S+zwTk1tN5BQzFdv7Qq3yNe9wBGnrkk="; 12 | }; 13 | 14 | cargoLock.lockFile = ./Cargo.lock; 15 | } 16 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/simple/Cargo.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mic92/nix-update/6338957f33c54bb1b1f3393d4c1b45f22dbf5b17/tests/testpkgs/cargo-lock-generate/simple/Cargo.lock -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/simple/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | python3Packages, 3 | rustPlatform, 4 | fetchFromGitHub, 5 | }: 6 | 7 | python3Packages.buildPythonPackage rec { 8 | pname = "pycrdt"; 9 | version = "0.9.6"; 10 | 11 | src = fetchFromGitHub { 12 | owner = "jupyter-server"; 13 | repo = "pycrdt"; 14 | rev = "v${version}"; 15 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 16 | }; 17 | 18 | nativeBuildInputs = [ 19 | rustPlatform.cargoSetupHook 20 | rustPlatform.maturinBuildHook 21 | ]; 22 | 23 | cargoDeps = rustPlatform.importCargoLock { lockFile = ./Cargo.lock; }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path-outside-workspace/Cargo.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mic92/nix-update/6338957f33c54bb1b1f3393d4c1b45f22dbf5b17/tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path-outside-workspace/Cargo.lock -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path-outside-workspace/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | python3Packages, 3 | rustPlatform, 4 | fetchFromGitHub, 5 | }: 6 | 7 | python3Packages.buildPythonPackage rec { 8 | pname = "pylance"; 9 | version = "0.15.0"; 10 | 11 | src = fetchFromGitHub { 12 | owner = "lancedb"; 13 | repo = "lance"; 14 | rev = "v${version}"; 15 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 16 | }; 17 | 18 | nativeBuildInputs = [ 19 | rustPlatform.cargoSetupHook 20 | rustPlatform.maturinBuildHook 21 | ]; 22 | 23 | cargoDeps = rustPlatform.importCargoLock { lockFile = ./Cargo.lock; }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path/Cargo.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mic92/nix-update/6338957f33c54bb1b1f3393d4c1b45f22dbf5b17/tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path/Cargo.lock -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-generate/with-lockfile-metadata-path/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | python3Packages, 3 | rustPlatform, 4 | fetchFromGitHub, 5 | }: 6 | 7 | python3Packages.buildPythonPackage rec { 8 | pname = "lancedb"; 9 | version = "0.11.0"; 10 | 11 | src = fetchFromGitHub { 12 | owner = "lancedb"; 13 | repo = "lancedb"; 14 | rev = "python-v${version}"; 15 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 16 | }; 17 | 18 | nativeBuildInputs = [ 19 | rustPlatform.cargoSetupHook 20 | rustPlatform.maturinBuildHook 21 | ]; 22 | 23 | cargoDeps = rustPlatform.importCargoLock { lockFile = ./Cargo.lock; }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-lock-update/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | rustPlatform, 3 | fetchFromGitHub, 4 | }: 5 | rustPlatform.buildRustPackage rec { 6 | pname = "ruff"; 7 | version = "0.7.0"; 8 | 9 | src = fetchFromGitHub { 10 | owner = "astral-sh"; 11 | repo = pname; 12 | rev = version; 13 | hash = "sha256-+8JKzKKWPQEanU2mh8p5sRjnoU6DawTQQi43qRXVXIg="; 14 | }; 15 | 16 | cargoLock = { 17 | lockFile = src + "/Cargo.lock"; 18 | outputHashes = { 19 | "lsp-types-0.95.1" = "sha256-8Oh299exWXVi6A39pALOISNfp8XBya8z+KT/Z7suRxQ="; 20 | "salsa-0.18.0" = "sha256-vuLgeaqIL8U+5PUHJaGdovHFapAMGGQ9nPAMJJnxz/o="; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-vendor-deps/non-rust-package.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | rustPlatform, 4 | fetchFromGitHub, 5 | }: 6 | stdenv.mkDerivation rec { 7 | pname = "popsicle"; 8 | version = "1.3.0"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "pop-os"; 12 | repo = "popsicle"; 13 | rev = version; 14 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }; 16 | 17 | nativeBuildInputs = [ 18 | rustPlatform.bindgenHook 19 | rustPlatform.cargoSetupHook 20 | ]; 21 | 22 | cargoDeps = rustPlatform.fetchCargoVendor { 23 | inherit pname version src; 24 | hash = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /tests/testpkgs/cargo-vendor-deps/rust-package.nix: -------------------------------------------------------------------------------- 1 | { 2 | rustPlatform, 3 | fetchFromGitHub, 4 | }: 5 | rustPlatform.buildRustPackage rec { 6 | pname = "ruff"; 7 | version = "0.7.0"; 8 | 9 | src = fetchFromGitHub { 10 | owner = "astral-sh"; 11 | repo = pname; 12 | rev = version; 13 | hash = "sha256-+8JKzKKWPQEanU2mh8p5sRjnoU6DawTQQi43qRXVXIg="; 14 | }; 15 | 16 | useFetchCargoVendor = true; 17 | cargoHash = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; 18 | } 19 | -------------------------------------------------------------------------------- /tests/testpkgs/composer-old.nix: -------------------------------------------------------------------------------- 1 | { 2 | fetchFromGitHub, 3 | php, 4 | }: 5 | 6 | php.buildComposerProject (finalAttrs: { 7 | pname = "castor"; 8 | version = "0.10.0"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "jolicode"; 12 | repo = "castor"; 13 | rev = "v${finalAttrs.version}"; 14 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }; 16 | 17 | vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testpkgs/composer.nix: -------------------------------------------------------------------------------- 1 | { fetchFromGitHub, php }: 2 | 3 | php.buildComposerProject2 (finalAttrs: { 4 | pname = "phpunit"; 5 | version = "11.3.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "sebastianbergmann"; 9 | repo = "phpunit"; 10 | rev = finalAttrs.version; 11 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | 14 | vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }) 16 | -------------------------------------------------------------------------------- /tests/testpkgs/crate.nix: -------------------------------------------------------------------------------- 1 | { 2 | rustPlatform, 3 | fetchCrate, 4 | hello, 5 | nix-update-script, 6 | }: 7 | rustPlatform.buildRustPackage rec { 8 | pname = "fd-find"; 9 | version = "8.0.0"; 10 | 11 | src = fetchCrate { 12 | inherit pname version; 13 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 14 | }; 15 | 16 | cargoHash = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; 17 | 18 | passthru.tests = { 19 | foo = hello; 20 | bar = hello; 21 | }; 22 | passthru.updateScript = nix-update-script { 23 | attrPath = "crate"; 24 | extraArgs = [ 25 | "--flake" 26 | ]; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /tests/testpkgs/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { }, 3 | }: 4 | { 5 | bitbucket = pkgs.callPackage ./bitbucket.nix { isSnapshot = false; }; 6 | bitbucket-snapshot = pkgs.callPackage ./bitbucket.nix { isSnapshot = true; }; 7 | cargoLock.expand = pkgs.callPackage ./cargo-lock-expand { }; 8 | cargoLock.generate.simple = pkgs.callPackage ./cargo-lock-generate/simple { }; 9 | cargoLock.generate.with-lockfile-metadata-path = 10 | pkgs.callPackage ./cargo-lock-generate/with-lockfile-metadata-path 11 | { }; 12 | cargoLock.generate.with-lockfile-metadata-path-outside-workspace = 13 | pkgs.callPackage ./cargo-lock-generate/with-lockfile-metadata-path-outside-workspace 14 | { }; 15 | cargoLock.update = pkgs.callPackage ./cargo-lock-update { }; 16 | composer = pkgs.callPackage ./composer.nix { }; 17 | cargoVendorDeps.nonRustPackage = pkgs.callPackage ./cargo-vendor-deps/non-rust-package.nix { }; 18 | cargoVendorDeps.rustPackage = pkgs.callPackage ./cargo-vendor-deps/rust-package.nix { }; 19 | composer-old = pkgs.callPackage ./composer-old.nix { }; 20 | crate = pkgs.callPackage ./crate.nix { }; 21 | fetchurl-github-release = pkgs.callPackage ./fetchurl-github-release.nix { }; 22 | nuget-deps-generate = pkgs.callPackage ./nuget-deps-generate { }; 23 | gitea = pkgs.callPackage ./gitea.nix { }; 24 | github = pkgs.callPackage ./github.nix { }; 25 | github-no-release = pkgs.callPackage ./github-no-release.nix { }; 26 | github-tag = pkgs.callPackage ./github-tag.nix { }; 27 | github-fetchtree = pkgs.callPackage ./github-fetchtree.nix { }; 28 | gitlab = pkgs.callPackage ./gitlab.nix { }; 29 | pypi = pkgs.python3.pkgs.callPackage ./pypi.nix { }; 30 | sourcehut = pkgs.python3.pkgs.callPackage ./sourcehut.nix { }; 31 | sourcehut-snapshot = pkgs.python3.pkgs.callPackage ./sourcehut-snapshot.nix { }; 32 | savanna = pkgs.python3.pkgs.callPackage ./savanna.nix { }; 33 | net-news-wire = pkgs.callPackage ./net-news-wire.nix { }; 34 | npm = pkgs.callPackage ./npm.nix { }; 35 | npm-package = pkgs.callPackage ./npm-package.nix { }; 36 | npm-lock-generate = pkgs.callPackage ./npm-lock-generate { }; 37 | pnpm = pkgs.callPackage ./pnpm.nix { }; 38 | maven = pkgs.callPackage ./maven.nix { }; 39 | mix = pkgs.callPackage ./mix.nix { }; 40 | set = pkgs.callPackage ./set.nix { }; 41 | let-bound-version = pkgs.callPackage ./let-bound-version.nix { }; 42 | subpackage = pkgs.callPackage ./subpackage.nix { }; 43 | } 44 | -------------------------------------------------------------------------------- /tests/testpkgs/fetchurl-github-release.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | fetchurl, 4 | }: 5 | 6 | stdenv.mkDerivation (finalAttrs: { 7 | version = "5.0.5"; 8 | pname = "adminer"; 9 | 10 | src = fetchurl { 11 | url = "https://github.com/vrana/adminer/releases/download/v${finalAttrs.version}/adminer-${finalAttrs.version}.zip"; 12 | hash = "sha256-7VAy9bE9dUZpkKtRMUa/boA6NlfZ7tBT/2x1POtazoM="; 13 | }; 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testpkgs/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | outputs = 3 | { 4 | self, 5 | nixpkgs, 6 | }: 7 | { 8 | packages = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: { 9 | crate = nixpkgs.legacyPackages.${system}.callPackage (self + "/crate.nix") { }; 10 | }); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /tests/testpkgs/gitea.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitea }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "nsxiv"; 5 | version = "29"; 6 | 7 | src = fetchFromGitea { 8 | domain = "codeberg.org"; 9 | owner = "nsxiv"; 10 | repo = "nsxiv"; 11 | rev = "v${version}"; 12 | hash = "sha256-swzTdQ6ow1At4bKRORqz6fb0Ej92yU9rlI/OgcinPu4="; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/testpkgs/github-fetchtree.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | 3 | let 4 | fetchFromInternalGitHub = 5 | { 6 | githubBase, 7 | owner, 8 | repo, 9 | hash, 10 | tag ? null, 11 | rev ? null, 12 | }: 13 | stdenv.mkDerivation (finalAttrs: rec { 14 | # fetchFromGitHub derivation name is always "source" 15 | name = "source"; 16 | 17 | revWithTag = if tag != null then "refs/tags/${tag}" else rev; 18 | url = "https://${githubBase}/${owner}/${repo}/tarball/${revWithTag}"; 19 | src = builtins.fetchTree { 20 | type = "tarball"; 21 | url = url; 22 | 23 | # use hash of final derivation attributes, once it will get overwritten on the outer derivation 24 | narHash = finalAttrs.outputHash; 25 | }; 26 | 27 | dontUnpack = true; 28 | buildPhase = '' 29 | cp -a ${src}/ $out/ 30 | ''; 31 | 32 | # fixed output derivation 33 | outputHashAlgo = "sha256"; 34 | outputHashMode = "recursive"; 35 | outputHash = hash; 36 | }); 37 | in 38 | stdenv.mkDerivation rec { 39 | pname = "fd"; 40 | version = "8.0.0"; 41 | 42 | src = fetchFromInternalGitHub { 43 | githubBase = "github.com"; 44 | owner = "sharkdp"; 45 | repo = pname; 46 | rev = "v${version}"; 47 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /tests/testpkgs/github-no-release.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "proton-vpn"; 5 | version = "4.3.2"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "ProtonVPN"; 9 | repo = "${pname}-gtk-app"; 10 | rev = "v${version}"; 11 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /tests/testpkgs/github-tag.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "fd"; 5 | version = "8.0.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "sharkdp"; 9 | repo = pname; 10 | tag = "v${version}"; 11 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /tests/testpkgs/github.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "fd"; 5 | version = "8.0.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "sharkdp"; 9 | repo = pname; 10 | rev = "v${version}"; 11 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /tests/testpkgs/gitlab.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitLab }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "phosh"; 5 | version = "0.20.0"; 6 | 7 | src = fetchFromGitLab { 8 | domain = "gitlab.gnome.org"; 9 | group = "world"; 10 | owner = "phosh"; 11 | repo = pname; 12 | rev = "v${version}"; 13 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /tests/testpkgs/let-bound-version.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | 3 | let 4 | version = "8.0.0"; 5 | in 6 | stdenv.mkDerivation rec { 7 | pname = "fd"; 8 | inherit version; 9 | 10 | src = fetchFromGitHub { 11 | owner = "sharkdp"; 12 | repo = pname; 13 | rev = "v${version}"; 14 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /tests/testpkgs/maven.nix: -------------------------------------------------------------------------------- 1 | { maven, fetchFromGitHub }: 2 | 3 | maven.buildMavenPackage rec { 4 | pname = "mariadb-connector-java"; 5 | version = "3.3.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "mariadb-corporation"; 9 | repo = "mariadb-connector-j"; 10 | rev = "refs/tags/${version}"; 11 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | 14 | mvnHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | mvnParameters = "-DskipTests"; 16 | } 17 | -------------------------------------------------------------------------------- /tests/testpkgs/mix.nix: -------------------------------------------------------------------------------- 1 | { beamPackages, fetchFromGitHub }: 2 | 3 | beamPackages.mixRelease rec { 4 | pname = "credo-language-server"; 5 | version = "0.2.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "elixir-tools"; 9 | repo = "credo-language-server"; 10 | rev = "refs/tags/v${version}"; 11 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | 14 | mixFodDeps = beamPackages.fetchMixDeps { 15 | inherit pname version src; 16 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /tests/testpkgs/net-news-wire.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenvNoCC, 3 | fetchurl, 4 | }: 5 | 6 | stdenvNoCC.mkDerivation rec { 7 | pname = "net-news-wire"; 8 | version = "6.1.5"; 9 | 10 | src = fetchurl { 11 | url = "https://github.com/Ranchero-Software/NetNewsWire/releases/download/mac-${version}/NetNewsWire${version}.zip"; 12 | hash = "sha256-92hsVSEpa661qhebeSd5lxt8MtIJRn7YZyKlMs0vle0="; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/testpkgs/npm-lock-generate/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildNpmPackage, 3 | fetchFromGitHub, 4 | }: 5 | 6 | buildNpmPackage rec { 7 | pname = "emmet-language-server"; 8 | version = "2.5.0"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "olrtg"; 12 | repo = "emmet-language-server"; 13 | rev = "v${version}"; 14 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }; 16 | 17 | npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 18 | 19 | postPatch = '' 20 | cp ${./package-lock.json} ./package-lock.json 21 | ''; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/testpkgs/npm-lock-generate/package-lock.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mic92/nix-update/6338957f33c54bb1b1f3393d4c1b45f22dbf5b17/tests/testpkgs/npm-lock-generate/package-lock.json -------------------------------------------------------------------------------- /tests/testpkgs/npm-package.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchurl }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "pnpm"; 5 | version = "9.1.3"; 6 | 7 | src = fetchurl { 8 | url = "https://registry.npmjs.org/pnpm/-/pnpm-${version}.tgz"; 9 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /tests/testpkgs/npm.nix: -------------------------------------------------------------------------------- 1 | { fetchFromGitHub, buildNpmPackage }: 2 | 3 | buildNpmPackage rec { 4 | pname = "jellyfin-web"; 5 | version = "10.8.6"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "jellyfin"; 9 | repo = "jellyfin-web"; 10 | rev = "v${version}"; 11 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | 14 | npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | } 16 | -------------------------------------------------------------------------------- /tests/testpkgs/nuget-deps-generate/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildDotnetModule, 3 | fetchFromGitHub, 4 | }: 5 | 6 | buildDotnetModule rec { 7 | pname = "celeste64"; 8 | version = "1.1.0"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "ExOK"; 12 | repo = "Celeste64"; 13 | tag = "v${version}"; 14 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 15 | }; 16 | 17 | nugetDeps = ./deps.json; 18 | } 19 | -------------------------------------------------------------------------------- /tests/testpkgs/nuget-deps-generate/deps.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /tests/testpkgs/pnpm.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildNpmPackage, 3 | fetchFromGitHub, 4 | pnpm_9, 5 | }: 6 | 7 | buildNpmPackage rec { 8 | pname = "flood"; 9 | version = "4.9.2"; 10 | 11 | src = fetchFromGitHub { 12 | owner = "jesec"; 13 | repo = "flood"; 14 | rev = "v${version}"; 15 | hash = "sha256-sIwXx9DA+vRW4pf6jyqcsla0khh8fdpvVTZ5pLrUhhc="; 16 | }; 17 | 18 | npmConfigHook = pnpm_9.configHook; 19 | npmDeps = pnpmDeps; 20 | pnpmDeps = pnpm_9.fetchDeps { 21 | inherit pname version src; 22 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/testpkgs/pypi.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildPythonPackage, 3 | fetchPypi, 4 | twisted, 5 | mock, 6 | pytestCheckHook, 7 | }: 8 | 9 | buildPythonPackage rec { 10 | pname = "python-mpd2"; 11 | version = "2.0.0"; 12 | 13 | src = fetchPypi { 14 | inherit pname version; 15 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 16 | }; 17 | 18 | checkInputs = [ 19 | pytestCheckHook 20 | twisted 21 | mock 22 | ]; 23 | 24 | pytestFlagsArray = [ "mpd/tests.py" ]; 25 | 26 | meta.changelog = "https://github.com/Mic92/python-mpd2/blob/${version}/doc/changes.rst"; 27 | } 28 | -------------------------------------------------------------------------------- /tests/testpkgs/savanna.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | fetchurl, 4 | gamin, 5 | }: 6 | 7 | stdenv.mkDerivation rec { 8 | pname = "fileschanged"; 9 | version = "0.6.8"; 10 | 11 | src = fetchurl { 12 | url = "mirror://savannah/fileschanged/fileschanged-${version}.tar.gz"; 13 | sha256 = "0ajc9h023vzpnlqqjli4wbvs0q36nr5p9msc3wzbic8rk687qcxc"; 14 | }; 15 | 16 | buildInputs = [ gamin ]; 17 | 18 | doCheck = true; 19 | } 20 | -------------------------------------------------------------------------------- /tests/testpkgs/set.nix: -------------------------------------------------------------------------------- 1 | { stdenv, fetchFromGitHub }: 2 | { 3 | fd = stdenv.mkDerivation rec { 4 | pname = "fd"; 5 | version = "0.0.0"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "sharkdp"; 9 | repo = pname; 10 | rev = "v${version}"; 11 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 12 | }; 13 | }; 14 | 15 | skim = stdenv.mkDerivation rec { 16 | pname = "skim"; 17 | version = "0.0.0"; 18 | 19 | src = fetchFromGitHub { 20 | owner = "skim-rs"; 21 | repo = pname; 22 | rev = "v${version}"; 23 | sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /tests/testpkgs/sourcehut-snapshot.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildGoModule, 3 | fetchFromSourcehut, 4 | }: 5 | buildGoModule { 6 | pname = "addr-book-combine"; 7 | version = "0-unstable-2022-12-08"; 8 | 9 | src = fetchFromSourcehut { 10 | owner = "~jcc"; 11 | repo = "addr-book-combine"; 12 | rev = "c3f3c7022837c7a93c0f6034c56ee0c73e7b76ba"; 13 | hash = "sha256-SENur3p5LxMNnjo/+qiVdrEs+i+rI1PT1wYYdLLqWrg="; 14 | }; 15 | 16 | vendorHash = null; 17 | } 18 | -------------------------------------------------------------------------------- /tests/testpkgs/sourcehut.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildGoModule, 3 | fetchFromSourcehut, 4 | lib, 5 | jq, 6 | installShellFiles, 7 | makeWrapper, 8 | scdoc, 9 | }: 10 | 11 | buildGoModule rec { 12 | pname = "ijq"; 13 | version = "0.3.5"; 14 | 15 | src = fetchFromSourcehut { 16 | owner = "~gpanders"; 17 | repo = pname; 18 | rev = "v${version}"; 19 | hash = "sha256-0xLmjidPxjSkYmLI4lWieT2rswZsWBY/IUXFOrUFAMo="; 20 | }; 21 | 22 | vendorHash = "sha256-7UuQXnQdlUMC0ZIgHydQ5bZMB5XrE7dhx5+1NI+zFkM="; 23 | 24 | nativeBuildInputs = [ 25 | installShellFiles 26 | makeWrapper 27 | scdoc 28 | ]; 29 | 30 | ldflags = [ 31 | "-s" 32 | "-w" 33 | "-X main.Version=${version}" 34 | ]; 35 | 36 | postBuild = '' 37 | scdoc < ijq.1.scd > ijq.1 38 | installManPage ijq.1 39 | ''; 40 | 41 | postInstall = '' 42 | wrapProgram "$out/bin/ijq" \ 43 | --prefix PATH : "${lib.makeBinPath [ jq ]}" 44 | ''; 45 | } 46 | -------------------------------------------------------------------------------- /tests/testpkgs/subpackage.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildGoModule, 3 | fetchFromGitHub, 4 | stdenvNoCC, 5 | nodejs, 6 | pnpm_9, 7 | typescript, 8 | }: 9 | 10 | let 11 | pname = "autobrr"; 12 | version = "1.53.0"; 13 | src = fetchFromGitHub { 14 | owner = "autobrr"; 15 | repo = "autobrr"; 16 | rev = "v${version}"; 17 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 18 | }; 19 | 20 | autobrr-web = stdenvNoCC.mkDerivation { 21 | pname = "${pname}-web"; 22 | inherit src version; 23 | 24 | nativeBuildInputs = [ 25 | nodejs 26 | pnpm_9.configHook 27 | typescript 28 | ]; 29 | 30 | sourceRoot = "${src.name}/web"; 31 | 32 | pnpmDeps = pnpm_9.fetchDeps { 33 | inherit (autobrr-web) 34 | pname 35 | version 36 | src 37 | sourceRoot 38 | ; 39 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 40 | }; 41 | 42 | postBuild = '' 43 | pnpm run build 44 | ''; 45 | 46 | installPhase = '' 47 | cp -r dist $out 48 | ''; 49 | }; 50 | in 51 | buildGoModule rec { 52 | inherit 53 | autobrr-web 54 | pname 55 | version 56 | src 57 | ; 58 | 59 | vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 60 | 61 | preBuild = '' 62 | cp -r ${autobrr-web}/* web/dist 63 | ''; 64 | } 65 | -------------------------------------------------------------------------------- /treefmt.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | imports = [ inputs.treefmt-nix.flakeModule ]; 4 | 5 | perSystem = 6 | { pkgs, ... }: 7 | { 8 | treefmt = { 9 | flakeCheck = pkgs.hostPlatform.system != "riscv64-linux"; 10 | 11 | # Used to find the project root 12 | projectRootFile = "flake.lock"; 13 | 14 | programs.deno.enable = pkgs.hostPlatform.system != "x86_64-darwin"; 15 | programs.mypy.enable = true; 16 | 17 | programs.yamlfmt.enable = true; 18 | 19 | programs.nixfmt.enable = true; 20 | programs.deadnix.enable = true; 21 | programs.ruff.format = true; 22 | programs.ruff.check = true; 23 | 24 | programs.shellcheck.enable = true; 25 | programs.shfmt.enable = true; 26 | settings.formatter.shfmt.includes = [ "*.envrc" ]; 27 | }; 28 | }; 29 | } 30 | --------------------------------------------------------------------------------