#!/usr/bin/env just --justfile # support custom local justfile recipies #set fallback # git worktrees #import? 'justfile.extra' # git branches / jujitsu # makes variables accesible as $1 $2 $@, useful for escaping variadics set positional-arguments := true export invokedir := invocation_directory() export epoch := `date +%s` # default interactive settings export GUM_FILTER_HEIGHT := "15" export FZF_DEFAULT_OPTS := "--height 15 --cycle --bind 'ctrl-a:toggle-all' " + env('FZF_DEFAULT_OPTS', "") # deal with concurrency export GIT := `command -v git-wait >/dev/null && echo "git-wait" || echo "git"` # trigger a direnv reload when entering $SHELL export DIRENV_WATCHES := "" # try and avoid surprises via local nixpkgs overrides export NIXPKGS_CONFIG := "" #export NIX_PATH := "" # in case 'nixpkgs-overlay' is set here export NIX_PATH := `echo "${NIX_PATH_ORIG:-"${NIX_PATH:-}"}"` # in case 'nixpkgs-overlay' is set here XARGS_NL := "-d'\\n' --no-run-if-empty" NIX_EVAL_OPTS := "--log-format raw --option warn-dirty false" # required programs: git nix nom gum fzf tee nix-update jq fd xe sd @_default: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep -vxE 'commit|fixup|list|build'))" @commit *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^commit-'))" "$@" @fixup *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^fixup-'))" "$@" @list *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^list-'))" "$@" @instantiate *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^instantiate-'))" "$@" @build *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^build-'))" "$@" @bump *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^bump-'))" "$@" @get *args: cd "$invokedir"; just "$(gum filter $(just --summary --unsorted | tr ' ' '\n' | grep '^get-'))" "$@" # === info helpers === list-packages +$cut_args="-c1-": _packages_json #!/usr/bin/env -S bash -euo pipefail cachefile=/tmp/nixpkgs-packages.json.tsv if ! [[ -s "$cachefile" && "$cachefile" -nt packages.json ]]; then jq "$cachefile" #| sed -e "s#\t$(realpath master)/#\t#" \ fi cut <"$cachefile" "$@" list-packages-fname-filtered *fnames: #!/usr/bin/env -S bash -euo pipefail declare -a fnames=("$@") if [[ "${#fnames[@]}" -gt 0 ]]; then printf "%s\n" "${fnames[@]}" | just _list_packages_fname_filtered elif [[ -t 0 ]]; then fd . pkgs -enix -tf | fzf --reverse --multi | just _list_packages_fname_filtered else echo >&2 No args and no tty, reading from stdin... just _list_packages_fname_filtered fi @_list_packages_fname_filtered: xargs {{XARGS_NL}} -n 2000 just __list_packages_fname_filtered \ | tr '0123456789' '9876543210' | sort -u | tr '0123456789' '9876543210' @__list_packages_fname_filtered +fnames: just list-packages | grep --fixed-strings "$(printf "\t%s\t\n" "$@")" \ | grep -v $'\t'pkgs/top-level/ \ | grep -v $'\t'pkgs/applications/editors/vim/plugins/ \ | grep -v $'\t'pkgs/applications/editors/vim/plugins/ \ | grep -v $'\t'pkgs/applications/editors/vscode/extensions/default.nix \ | grep -v $'\t'pkgs/build-support/cc-wrapper/default.nix \ | grep -v $'\t'pkgs/build-support/trivial-builders/default.nix \ | grep -v $'\t'pkgs/desktops/gnome/extensions/buildGnomeExtension.nix \ | grep -v $'\t'pkgs/development/compilers/chicken/5/default.nix \ | grep -v $'\t'pkgs/development/haskell-modules/hackage-packages.nix \ | grep -v $'\t'pkgs/development/lisp-modules/imported.nix \ | grep -v $'\t'pkgs/development/lua-modules/generated-packages.nix \ | grep -v $'\t'pkgs/development/python-modules/mypy-boto3/default.nix \ | grep -v $'\t'pkgs/development/python-modules/types-aiobotocore-packages/default.nix \ | grep -v $'\t'pkgs/os-specific/linux/nvidia-x11/generic.nix \ | grep -v $'\t'pkgs/servers/home-assistant/default.nix \ | grep -v $'\t'pkgs/tools/package-management/akku/default.nix \ | grep -v $'\t'pkgs/tools/typesetting/tex/texlive/build-texlive-package.nix list-packages-attrpath-filtered *attrpaths: #!/usr/bin/env -S bash -euo pipefail declare -a attrpaths=("$@") if [[ "${#attrpaths[@]}" -gt 0 ]]; then printf "%s\n" "${attrpaths[@]}" | just _list_packages_attrpath_filtered elif [[ -t 0 ]]; then fd . pkgs -enix -tf | fzf --reverse --multi | just _list_packages_attrpath_filtered else echo >&2 No args and no tty, reading from stdin... just _list_packages_attrpath_filtered fi @_list_packages_attrpath_filtered: tr '0123456789' '9876543210' | sort -u | tr '0123456789' '9876543210' \ | xargs {{XARGS_NL}} -n 2000 just __list_packages_attrpath_filtered __list_packages_attrpath_filtered +attrpaths: #!/usr/bin/env -S bash -euo pipefail declare -a attrpaths=("$@") attrpaths=("${attrpaths[@]/"."/"\."}") attrpaths=("${attrpaths[@]/"*"/"\*"}") attrpaths=("${attrpaths[@]/"+"/"\+"}") attrpaths=("${attrpaths[@]/"$"/"\$"}") attrpaths=("${attrpaths[@]/"("/"\("}") attrpaths=("${attrpaths[@]/")"/"\)"}") attrpaths=("${attrpaths[@]/#/"^"}") attrpaths=("${attrpaths[@]/%/$'\t'}") # printf >&1 "'%s'\n" "${attrpaths[@]}" just list-packages | grep "$(printf "%s\n" "${attrpaths[@]}")" list-packages-by-maintainer *github_handles: _packages_json #!/usr/bin/env -S bash -euo pipefail declare -a maintainers=("$@") [[ "${#maintainers[@]}" -gt 0 ]] || readarray -td $'\n' maintainers < <( just _some_maintainers ) [[ "${#maintainers[@]}" -gt 0 ]] #grep &2 "ERROR: Not in repo root"; false; } [[ -n "$ripgrep_args" ]] || { echo >&2 "ERROR: No ripgrep args"; false; } rg "$@" pkgs/ -tnix -l | just _list_packages_fname_filtered [no-cd] @list-nix-located-packages +$nix_locate_args: [[ -e .git && -e flake.nix ]] || { echo >&2 "ERROR: Not in repo root"; false; } [[ -n "$nix_locate_args" ]] || { echo >&2 "ERROR: No nix-locate args"; false; } nix-locate --top-level --regex "$@" | cut -d' ' -f1 | sd '\.out$' '' | sort -u | just _list_packages_attrpath_filtered [no-cd] @list-dirty-packages: test -n "$($GIT diff HEAD --name-only)" || { echo >&2 "No unstaged dirty files found!"; false; } $GIT diff HEAD --name-only | just _list_packages_fname_filtered [no-cd] @list-touched-packages-since $rev=`cd "$invokedir"; just _a_commit`: test -n "$rev" || { echo >&2 "you must pick a revision to compare against!"; false; } $GIT diff "$rev" --name-only | just _list_packages_fname_filtered [no-cd] @list-unmerged-packages: $GIT fetch upstream ||: $GIT log HEAD --not --remotes --oneline | cut -d' ' -f2- | cut -d: -f1 | grep -vE '(^treewide|[, ])' | sort -u # === build helpers === @build-packages-fname-filtered *fnames: cd "$invokedir"; just list-packages-fname-filtered "$@" | cut -f1 | xargs {{XARGS_NL}} just build-packages @build-ripgrepped-packages $ripgrep_arg $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-ripgrepped-packages "$ripgrep_arg" | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just build-packages @build-nix-located-packages $nix_locate_arg $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-nix-located-packages "$nix_locate_arg" | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just build-packages @build-dirty-packages $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-dirty-packages | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just build-packages @instantiate-dirty-packages $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-dirty-packages | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just instantiate-packages @build-touched-packages-since $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-touched-packages-since | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just build-packages @instantiate-touched-packages-since $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-touched-packages-since | cut -f1 | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just instantiate-packages @build-unmerged-packages $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-unmerged-packages | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just build-packages @instantiate-unmerged-packages $attr_prefix="" $attr_suffix="" +$extra_args="": [[ -n "$extra_args" ]] && export _JUST_NIX_INSTANTIATE_ARGS="$(shift; shift; printf " %q" "$@")"; \ cd "$invokedir"; just list-unmerged-packages | xargs {{XARGS_NL}} printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%}\n" | xargs {{XARGS_NL}} just instantiate-packages # # _JUST_NIX_INSTANTIATE_ARGS="--system darwin" # TODO: remove, use for unfree or cross intead [no-cd] instantiate-packages +$attrpaths="": #!/usr/bin/env -S bash -euo pipefail test -n "${attrpaths:-}" || set -- $(just _some_packages) attrpaths=("$@") export NIXPKGS_ALLOW_UNFREE=1 if gum confirm "Allow insecure?"; then export NIXPKGS_ALLOW_INSECURE=1 fi readarray -td $'\n' systems < <( just _some_systems ) for system in "${systems[@]}"; do just _instantiate_packages "$system" "${attrpaths[@]}" done [no-cd] instantiate-packages-extra-suffixes +$attrpaths="": #!/usr/bin/env -S bash -euo pipefail test -n "${attrpaths:-}" || set -- $(just _some_packages) attrpaths=("$@") [[ "${#attrpaths[@]}" -ne 0 ]] suffixes=( "%s" "%s.src" "%s.patches" "%s.passthru.tests" "%s.cargoDeps" ) readarray -td $'\n' selected_suffixes < <( # gum choose --no-limit "${suffixes[@]}" printf "%s\n" "${suffixes[@]}" | fzf --layout=reverse --multi ) [[ "${#selected_suffixes[@]}" -ne 0 ]] readarray -td $'\n' selected_attrpaths < <( for suffix in "${selected_suffixes[@]}"; do printf "$suffix\n" "${attrpaths[@]}" done ) just instantiate-packages "${selected_attrpaths[@]}" [no-cd] _instantiate_packages $system +$attrpaths: #!/usr/bin/env -S bash -euo pipefail # slower than normal nix-instantiate, but handles bad attrpaths, TODO: parallel eval? # supports envvar _JUST_NIX_INSTANTIATE_CACHE # supports envvar _JUST_NIX_INSTANTIATE_ARGS declare -a attrpaths=("${@:2}") if [[ -d "${_JUST_NIX_INSTANTIATE_CACHE:-}" ]]; then mkdir -p "$_JUST_NIX_INSTANTIATE_CACHE"/{roots,paths} worker() { local attrpath="$1" if [[ -s "$_JUST_NIX_INSTANTIATE_CACHE/paths/$attrpath.drv" ]]; then cat "$_JUST_NIX_INSTANTIATE_CACHE/paths/$attrpath.drv" else NIXPKGS_ALLOW_UNFREE=1 nice -n5 nix-instantiate . -A "$attrpath" \ --system "$system" \ ${_JUST_NIX_INSTANTIATE_ARGS:-} \ --add-root "$_JUST_NIX_INSTANTIATE_CACHE/roots/$attrpath.drv" \ | tee "$_JUST_NIX_INSTANTIATE_CACHE/paths/$attrpath.drv" fi } printf "%s\n" "${attrpaths[@]}" | grep . | \ xe -j0 -s "$(declare -f worker); worker \"\$@\"" else printf "%s\n" "${attrpaths[@]}" | grep . | \ xe -j0 -s 'NIXPKGS_ALLOW_UNFREE=1 nix-instantiate . -A "$1" --system "$system" '"${_JUST_NIX_INSTANTIATE_ARGS:-}"'' fi [no-cd] profile *args: #!/usr/bin/env -S bash -euo pipefail declare -a args=("$@") if [[ "${#args[@]}" -eq 0 ]]; then args=(. -A "$(just _a_package)") fi set -x NIX_SHOW_STATS=1 NIX_COUNT_CALLS=1 nix-instantiate "${args[@]}" # _JUST_NIX_BUILD_ARGS='--check' # _JUST_NIX_BUILD_ARGS='-j0 --builders "@/etc/nix/machines; ssh://pederbs@heid.idi.ntnu.no x86_64-linux - 24 5 big-parallel"' [no-cd] build-packages *$attrpaths: #!/usr/bin/env -S bash -euo pipefail [[ "$#" -gt 0 ]] || set -- $(just _some_packages) [[ "$#" -gt 0 ]] export JUST_SYSTEM="$(just _a_system)" [[ -n "$JUST_SYSTEM" ]] export NIXPKGS_ALLOW_UNFREE=1 if gum confirm "Allow insecure?"; then export NIXPKGS_ALLOW_INSECURE=1 fi if [[ -z "${_JUST_NIX_BUILD_ARGS:-}" ]]; then _JUST_NIX_BUILD_ARGS="$(gum choose --no-limit -- "-j0" "--check" | tr '\n' ' ')" export _JUST_NIX_BUILD_ARGS fi just _build_packages results "$@" [no-cd] _build_packages $outdir +$attrpaths: just __build_packages "$(just _a_system)" "$@" [no-cd] __build_packages $system $outdir +$attrpaths: #!/usr/bin/env -S bash -euo pipefail # hint: supports _JUST_NIX_BUILD_ARGS # hint: supports _JUST_NIX_INSTANTIATE_ARGS test -z "${_JUST_NIX_BUILD_ARGS:-}" || echo >&2 "_JUST_NIX_BUILD_ARGS = $_JUST_NIX_BUILD_ARGS" test -z "${_JUST_NIX_INSTANTIATE_ARGS:-}" || echo >&2 "_JUST_NIX_INSTANTIATE_ARGS = $_JUST_NIX_INSTANTIATE_ARGS" test -d "$outdir" && { rm -rf "$outdir"/.gcroot rm -rf "$outdir"/.views fd . "$outdir" --type l -X rm -v fd \\\.log$ "$outdir" --type f -X rm -v } declare -a attrpaths=("${@:3}") mkdir -p "$outdir/.gcroot" mkdir -p "$outdir/.views/"{succeeded,failed} export _JUST_NIX_INSTANTIATE_CACHE=$(mktemp -d) export NOTFOUNDDIR="$(mktemp -d)"; touch "$NOTFOUNDDIR"/no-eval # set -x just _instantiate_packages "$system" "${attrpaths[@]}" | tee >(command cat >&2) \ | while IFS=! read path output; do printf "%s\n" "$(realpath "$path")${output:+!"$output"}"; done \ | xargs {{XARGS_NL}} nom-build --keep-going --no-out-link ${_JUST_NIX_BUILD_ARGS:-} ||: # | eval xargs nom-build --keep-going --no-out-link ${_JUST_NIX_BUILD_ARGS:-} ||: worker() { local attrpath="$1" local dst="$outdir/$attrpath" local dst2a="$outdir/.views/succeeded/$attrpath" local dst2b="$outdir/.views/failed/$attrpath" local gcroot="$outdir/.gcroot/$attrpath" # workaround since .drv!dev will always append "-dev" to --out-path local drvpath=$( just _instantiate_packages "$system" "$attrpath" ) set -x if [[ -n "$drvpath" ]]; then local inStoreDrvPath # eww, why doesn't the !output syntax work for out of store symlinks? if [[ "$drvpath" =~ '!' ]]; then inStoreDrvPath="$(realpath "$(cut -d! -f1 <<<"$drvpath")")!$(cut -d! -f2 <<<"$drvpath")" else inStoreDrvPath="$(realpath "$drvpath")" fi nice -n5 nix-build "$inStoreDrvPath" -j0 --option builders "" --option substitute false --out-link "$gcroot" >&/dev/null ||: if [[ "$drvpath" =~ '!' ]]; then # :( gcroot="$gcroot-$(cut -d! -f2 <<<"$drvpath")" fi if [[ -e "$gcroot" ]]; then ln -s "$(readlink "$gcroot")" $dst fi fi # local outpaths=$( nix-store -q --outputs "$drvpath" ) # doesn't gcroot, some outputs may not be pulled from builders if [[ -L "$dst" ]]; then nice -n5 nix log $(readlink "$dst") > "$dst".log 2>/dev/null ||: # nice -n5 nix log --option substituters "" $(readlink "$dst") > "$dst".log 2>/dev/null ||: ln -sr "$dst" "$dst2a" ln -sr "$dst".log "$dst2a".log else if [[ -z "$drvpath" ]]; then ln -s "$NOTFOUNDDIR"/no-eval "$dst" ln -s "$NOTFOUNDDIR"/no-eval "$dst2b" nice -n5 nix-instantiate . -A "$attrpath" >& "$dst.log" # a third time... ln -sr "$dst".log "$dst2b".log else ln -s /build-failure-"$attrpath" "$dst" ln -s /build-failure-"$attrpath" "$dst2b" nice -n5 nix log --option substituters "" ".#$attrpath" --system "$system" ${_JUST_NIX_INSTANTIATE_ARGS:-} > "$dst".log 2>/dev/null ||: ln -sr "$dst".log "$dst2b".log fi fi } printf "%s\n" "${attrpaths[@]}" | xe -j0 -s "set -euo pipefail; $(declare -f worker); worker \"\$@\"" ||: # echo -e "#!/usr/bin/env bash\ntest -f flake.nix || cd ..; env _JUST_NIX_INSTANTIATE_ARGS=\"\$*\" just __build_packages "$system" \"$outdir\"" $attrpaths > "$outdir"/_rerun.sh cat <<-EOF >"$outdir"/_rerun.sh #!/usr/bin/env bash while [[ ! -f flake.nix ]]; do cd .. done set -v _JUST_NIX_INSTANTIATE_ARGS="\$*" just __build_packages "\${1:-"$system"}" $(printf " %q" "$outdir" "${attrpaths[@]}") EOF chmod +x "$outdir"/_rerun.sh fd . "$outdir" -l # TODO: use in 'bump' [no-cd] build-package-sources *packages: #!/usr/bin/env -S bash -euo pipefail packages=("$@") [[ "${#packages[@]}" -gt 0 ]] || readarray -td $'\n' packages < <( just _some_packages ) just _build_package_sources "$(just _a_system)" "${packages[@]}" [no-cd] _build_package_sources $system +packages: #!/usr/bin/env -S bash -euo pipefail -x [[ -e .git && -f flake.nix ]] || { printf >&2 "%s\n" "ERROR: not in repo root!"; false; } declare packages=("${@:2}") # TODO: allow selecting different names than result-src and results-src-writeable just __build_packages "$system" results-src "${packages[@]/%/.src}" [[ -d results-src-writeable ]] && rm -rf results-src-writeable ||: mkdir -p results-src-writeable for result in results-src/* ; do [[ -L "$result" ]] || continue result_writeable=results-src-writeable/"$(basename "$result")" mkdir -p "$result_writeable" if [[ -d "$(readlink "$result")" ]]; then rsync -rxL --chmod=ugo+w "$result/" "$result_writeable/" ||: #chmod -R +w "$result_writeable/" elif [[ -f "$(readlink "$result")" ]]; then atool "$(readlink "$result")" --extract-to="$result_writeable/" ||: if [[ $(ls --almost-all "$result_writeable" | wc -l ) -eq 1 ]] && test -d "$result_writeable"/*; then mv "$result_writeable"{,-old} mv "$result_writeable"-old/* "$result_writeable" rmdir "$result_writeable"-old fi fi ( # todo: should i eval nix and query for upstream remote and ref? cd "$result_writeable" $GIT init $GIT add . $GIT commit -m "Base" >/dev/null ) ||: done { # printf "#!/usr/bin/env -S nom-build\n" printf "#!/usr/bin/env -S nom build -f\n" printf "let pkgs = import ./. { }; in {\n" for package in "${packages[@]}"; do # printf " # ./build.nix -A %q" "$package" printf " # ./build.nix %q\n" "$package" printf " %s = pkgs.%s.overrideAttrs { src = ./results-src-writeable/%s.src; };\n" "$package" "$package" "$package" done printf "}\n" # printf "# ./build.nix$(printf " -A %q" "${packages[@]}")\n" printf "# ./build.nix$(printf " %q" "${packages[@]}")\n" printf "# ./build.nix\n" } >build.nix chmod +x build.nix # TODO: redesign or drop @build-pr-packages $pr=`just _a_pr` $merge=`gum choose --header="which refs/pull//xxx ?" merge head` $attrpaths=`just _some_packages` $args=`gum choose --no-limit -- -j0 "--system "{aarch64,x86_64}-{darwin,linux} --rebuild`: printf "github:nixos/nixpkgs/refs/pull/"$(just _sanitize_pr_url "$pr")"/$merge#%s\n" $attrpaths | xargs {{XARGS_NL}} echo + nom build $args (printf "github:nixos/nixpkgs/refs/pull/"$(just _sanitize_pr_url "$pr")"/$merge#%s\n" $attrpaths | xargs {{XARGS_NL}} nom build $args) ||: printf "github:nixos/nixpkgs/refs/pull/"$(just _sanitize_pr_url "$pr")"/$merge#%s\n" $attrpaths | xargs {{XARGS_NL}} echo + nom build $args # === commit helpers === [no-cd] @commit-staged *$message: test -n "$($GIT diff HEAD --name-only --staged)" || ! echo >&2 "No files are staged!" || false $GIT diff HEAD --name-only --staged | sort -u | xe nixfmt --check $GIT -c commit.template=<(git diff HEAD --name-only --staged | just _list_packages_fname_filtered | cut -f1 | just _attrpaths_2_aliases | xargs {{XARGS_NL}} printf "%s: ${*//%/%%}\n") commit && git show [no-cd] commit-dirty-packages +$message=`gum input --placeholder="commit message, (attrpath: this message)"`: #!/usr/bin/env -S bash -euo pipefail [[ -n "$message" ]] if ! $GIT ls-files --modified | sort -u | xe -j0 nixfmt --check; then if gum confirm "Run 'just format-files-dirty'?"; then just format-files-dirty else false fi fi just list-dirty-packages | while read attrpath position row; do [[ -f "$position" ]] || continue $GIT diff -s --exit-code "$position" || ( set -x $GIT add "$position" $GIT commit -m "$(just _attrpaths_2_aliases <<<"$attrpath"): $*" ) done [no-cd] fixup-commit: #!/usr/bin/env -S bash -euo pipefail # TODO: list commits first test -n "$($GIT diff --name-only --staged)" || { $GIT ls-files --modified --others --exclude-standard | grep -v ^results- | fzf --layout=reverse --multi | xe $GIT add --patch } test -n "$($GIT diff --name-only --staged)" || { echo >&2 Nothing is staged! false } just fixup-commit-staged [no-cd] fixup-commit-staged commit=`cd "$invokedir"; just _a_commit`: #!/usr/bin/env -S bash -euo pipefail test -n "{{ commit }}" # just exit if no selection was made test -n "$($GIT diff --name-only --staged)" || { echo >&2 Nothing is staged!; false; } $GIT diff --staged echo; gum format "# ========= into ========="; echo $GIT log {{ commit }} -n1 gum confirm $GIT commit --fixup={{ commit }} GIT_SEQUENCE_EDITOR=true $GIT rebase -i --autostash --autosquash "$($GIT log -n1 --pretty=%P {{ commit }})" rebase-commits commit=`cd "$invokedir"; just _a_commit`: cd "$invokedir"; $GIT rebase -i --autostash --autosquash "$($GIT log -n1 --pretty=%P {{ commit }})" [no-cd] bisect-eval $attrpath=`just _a_package` *_: #!/usr/bin/env -S bash -euo pipefail tmpfile=$(mktemp) hello printf "#!/usr/bin/env -S bash -euxo pipefail\nnix-instantiate --json --strict --eval --system %q . -A %q" "$(just _a_system)" "$attrpath" > "$tmpfile" chmod +x "$tmpfile" just bisect-script "$tmpfile" "${@:2}" [no-cd] bisect-build $attrpath=`just _a_package` *_: #!/usr/bin/env -S bash -euo pipefail tmpfile=$(mktemp) # printf "#!/usr/bin/env -S bash -euxo pipefail\nnix-build --no-out-link $(gum choose -- "" "-j0") --system %q . -A %q" "$(just _a_system)" "$attrpath" > "$tmpfile" printf "#!/usr/bin/env -S bash -euxo pipefail\nnom-build --no-out-link $(gum choose -- "" "-j0") --system %q . -A %q" "$(just _a_system)" "$attrpath" > "$tmpfile" chmod +x "$tmpfile" just bisect-script "$tmpfile" "${@:2}" [no-cd] bisect-script $repro_script $bad_commit=`cd "$invokedir"; HEADER="Bad commit" just _a_commit` $good_commit=`cd "$invokedir"; HEADER="Good commit" just _a_commit`: #!/usr/bin/env -S bash -euo pipefail [[ -n "$repro_script" ]] [[ -n "$good_commit" ]] [[ -n "$bad_commit" ]] [[ -x "$repro_script" ]] || ! echo >&2 "ERROR: '$repro_script' is not an executable file!" || false declare -a args=() declare -a possible_args=( "--first-parent Follow only the first parent commit upon seeing a merge commit." "--no-checkout Do not checkout the new working tree, instead just update BISECT_HEAD " ) readarray -td $'\n' args < <(gum choose --no-limit -- "${possible_args[@]}" | cut -d' ' -f1) (set -x $GIT bisect start "${args[@]}" "$bad_commit" "$good_commit" -- $GIT bisect run "$repro_script" ) echo >&2 "Continue with '$GIT bisect run \"$repro_script\"'" echo >&2 "Conclude with 'git bisect reset'" # === pr helper === [no-cd] push-new-pr: #!/usr/bin/env -S bash -euo pipefail -x # TODO: verify "origin" is a nixpkgs fork and not NixOS/nixpkgs if gum confirm --default=no "Test merging into upstream branches?"; then just test-merge || gum confirm --default=no "Continue?" fi # TODO: can i retrieve what the upstream tracking base is for the current branch/worktree? declare base base="$(just _a_upstream_release_branch "Base?" | cut -d/ -f2-)" [[ -n "$base" ]] declare title title="$(just _a_commit_title ||:)" if [[ "$base" != "master" && "$base" != "staging" && "$base" != staging-next ]]; then title="[$base] $title" fi title="$(gum input --placeholder="foobar: did a thing" --value="$title" ||:)" $GIT -c push.autoSetupRemote=true push origin if [[ -n "$title" ]]; then gh pr create --base="$base" --title="$title" else gh pr create --base="$base" fi if gum confirm "Enqueue nixpkgs-review?"; then pr_number="$(gh pr list --author @me | column -s$'\t' -t | gum choose | cut -d' ' -f1)" [[ -n "$pr_number" ]] just enqueue-nixpkgs-review "$pr_number" fi # === speed worktrees === bump-new-worktree *packages: #!/usr/bin/env -S bash -euo pipefail declare -a packages=("$@") [[ "${#packages[@]}" -gt 0 ]] || readarray -td $'\n' packages <<<"$( just _some_packages )" [[ "${#packages[@]}" -gt 0 ]] export JUST_SYSTEM="$(just _a_system)" gum confirm --default=no "Check versions on all branches?" && just get-versions "${packages[0]}" ||: # TODO: timeout? worktree=$(just _new_worktree bump "$(printf "%s\n" "${packages[0]}" | rev | cut -d. -f1 | rev)") [[ -n "$worktree" ]] [[ -d "$worktree" ]] cd "$worktree" just bump-here "${packages[@]}" NIXPKGS_ALLOW_UNFREE=1 exec just _enter_interactive_shell [no-cd] bump-here *packages: #!/usr/bin/env -S bash -euo pipefail [[ -e .git && flake.nix ]] declare -a packages=("$@") [[ "${#packages[@]}" -gt 0 ]] || readarray -td $'\n' packages <<<"$( just _some_packages )" [[ "${#packages[@]}" -gt 0 ]] declare -a package_aliases=() readarray -td $'\n' package_aliases <<<"$( printf "%s\n" "${packages[@]}" | just _attrpaths_2_aliases )" [[ "${#package_aliases[@]}" -gt 0 ]] || package_aliases=("${#packages[@]}") [[ "${#package_aliases[@]}" -gt 0 ]] system="$(just _a_system)" config1=( env NIXPKGS_ALLOW_UNFREE=1 NIXPKGS_ALLOW_INSECURE=1 ) config2=( "${config1[@]}" NIXPKGS_ALLOW_BROKEN=1 ) printf "%s.src\n" "${package_aliases[@]}" | "${config2[@]}" xargs {{XARGS_NL}} just __build_packages "$system" results-src-old ||: # TODO: unpack? for package in "${package_aliases[@]}"; do if nix eval {{NIX_EVAL_OPTS}} --file default.nix "$package".passthru.updateScript >&/dev/null; then HEAD=$($GIT rev-parse HEAD) "${config2[@]}" nix-update "$package" --use-update-script --update-script-args "--argstr skip-prompt true" --commit if [[ $($GIT rev-parse HEAD) = "$HEAD" ]]; then echo "no commit found, trying without the updateScript..." $GIT restore . "${config2[@]}" nix-update "$package" --commit || true fi else "${config2[@]}" nix-update "$package" --commit || true fi done set +e printf "%s.src\n" "${package_aliases[@]}" | "${config2[@]}" xargs {{XARGS_NL}} just __build_packages "$system" results-src-new # TODO: unpack? "${config1[@]}" just __build_packages "$system" results "${packages[@]}" # TODO: filter non-existing tests: printf "%s.tests\n" "${packages[@]}" | "${config1[@]}" _JUST_NIX_BUILD_ARGS="-j1" xargs {{XARGS_NL}} just __build_packages "$system" results-tests printf "HEAD^%s " $(seq 0 $(( "${#package_aliases[@]}" - 1 ))) | xe $GIT show fd . -l results results-tests --max-depth 1 # test -L result && fd . result --type x --color=always | tee update-executables.txt # $GIT show --name-only --pretty="" | xe statix fix $GIT diff printf "\n" printf "delta results-src-{old,new}/%s.src/.\n" "${package_aliases[@]}" printf "\n" printf > delta-cmds.sh '#!/usr/bin/env bash\n' printf >>delta-cmds.sh 'tail "$0" -n+3 | bat --style plain -l bash; exit 1\n' printf >>delta-cmds.sh "delta results-src-{old,new}/%s.src/.\n" "${package_aliases[@]}" fix *packages: #!/usr/bin/env -S bash -euo pipefail declare packages=("$@") [[ "${#packages[@]}" -gt 0 ]] || packages=($(just _some_packages)) [[ "${#packages[@]}" -gt 0 ]] || { printf >&2 "%s\n" "ERROR: no packages chosen..."; false; } export JUST_SYSTEM="$(just _a_system)" just open-package-urls "${packages[0]}" ||: cd "$(just _new_worktree fix "$(echo "${packages[0]}" | rev | cut -d. -f1 | rev)")" just _fix "${packages[@]}" ||: if gum confirm "Try to bump?" --default=no; then rm -rf results-src just bump-here "${packages[@]}" fi exec just _enter_interactive_shell [no-cd] _fix +packages: #!/usr/bin/env -S bash -euo pipefail [[ -e .git && -f flake.nix ]] || { printf >&2 "%s\n" "ERROR: not in repo root!"; false; } declare packages=("$@") export JUST_SYSTEM="$(just _a_system)" just build-package-sources "${packages[@]}" NIXPKGS_ALLOW_UNFREE=1 just build-packages "${packages[@]}" ||: if command -v bat >/dev/null; then bat build.nix --style header-filename else cat build.nix fi pr $number=`just _a_pr`: #!/usr/bin/env -S bash -euo pipefail number=$(just _sanitize_pr_url "$number") # TODO: support --onto do_rebase() { local upstream_branch="$1" # "upstream/master" local remote="$(cut <<<"$upstream_branch" -d'/' -f1)" local branch="$(cut <<<"$upstream_branch" -d'/' -f2)" # TODO: gitignore ./checkout-upstream-branch.sh printf "git checkout --autostash %q\n" "$(git rev-parse --abbrev-ref HEAD)" > checkout-upstream-branch.sh (set -x; $GIT checkout -b pr-"$number"-rebased-"$remote"-"$branch-{{ epoch }}" ||: $GIT pull --rebase "$remote" "$branch" || $GIT rebase --abort ) || echo >&2 "retcode: $?" ||: } if [[ -d prs/pr-"$number" ]]; then cd prs/pr-"$number" if gum confirm --default=no "Force pull?"; then upstream_branch=$( just _a_upstream_release_branch "Rebase?" "" ) [[ ! -e ./checkout-upstream-branch.sh ]] || rm ./checkout-upstream-branch.sh (set -x; gh pr checkout "$number" --force) [[ -z "$upstream_branch" ]] || do_rebase "$upstream_branch" fi else upstream_branch=$( just _a_upstream_release_branch "Rebase?" "" ) (cd master; $GIT worktree prune # (set -x; $GIT worktree add -b just-nixpkgs-pr-"$number"-tmp ../prs/pr-"$number") declare pull_json pull_json="$(gh api repos/NixOS/nixpkgs/pulls/382957)" git fetch upstream "$(jq <<<"$pull_json" '.base.ref' -r)" (set -x; $GIT worktree add ../prs/pr-"$number" "$(jq <<<"$pull_json" '.base.sha' -r)") ) cd prs/pr-"$number" (set -x; gh pr checkout "$number") git branch -D just-nixpkgs-pr-"$number"-tmp # TODO: set remote tracking branch with a git or http link to the contributors fork, if not already in `git remotes` if [[ -n "$upstream_branch" ]]; then remote="$(cut <<<"$upstream_branch" -d'/' -f1)" branch="$(cut <<<"$upstream_branch" -d'/' -f2)" # TODO: gitignore ./checkout-upstream-branch.sh printf "#!/usr/bin/env -S bash -euo pipefail\ngit checkout --autostash %q\n" "$(git rev-parse --abbrev-ref HEAD)" > checkout-upstream-branch.sh chmod +x checkout-upstream-branch.sh (set -x; $GIT checkout -b pr-"$number"-rebased-"$remote"-"$branch" ||: $GIT pull --rebase "$remote" "$branch" || $GIT rebase --abort ) || echo >&2 "retcode: $?" ||: fi fi exec just _enter_interactive_shell @pr-comment-diff: cd "$invokedir"; just _pr_post_diff comment @pr-review-diff: cd "$invokedir"; just _pr_post_diff "review $(gum choose --ordered -- --comment --request-changes --approve)" @_pr_post_diff mode: test "prs" = "$(cd "$invokedir" ; $GIT rev-parse --show-toplevel | rev | cut -d/ -f2 | rev)" test -n "$(cd "$invokedir"; $GIT diff --staged)" || { echo no changes staged; cd "$invokedir"; $GIT add --patch; true; } test -n "$(cd "$invokedir"; $GIT diff --staged)" || { echo no changes staged; false; } cd "$invokedir"; \ NUMBER="$($GIT rev-parse --show-toplevel | rev | cut -d/ -f1 | rev | cut -d- -f2)"; \ FNAME="$(mktemp --suffix=.md)"; \ echo "NUMBER=$NUMBER" "FNAME=$FNAME"; \ >>"$FNAME" echo; \ >>"$FNAME" echo '
'; \ >>"$FNAME" echo ':clipboard: proposed patch'; \ >>"$FNAME" echo; \ >>"$FNAME" echo '```patch'; \ >>"$FNAME" $GIT diff --staged; \ >>"$FNAME" echo '```'; \ >>"$FNAME" echo; \ >>"$FNAME" echo '
'; \ >>"$FNAME" echo; \ >>"$FNAME" echo '_How to apply (pick either):_'; \ >>"$FNAME" echo; \ >>"$FNAME" echo 'a. `curl -L "'"$(git diff --staged | gh gist create -f pr-$NUMBER.patch -)"'/raw" | git apply`'; \ >>"$FNAME" echo 'b. copy the patch above, run `git apply`, paste (typically `ctrl-shift-v`), press enter, then end the input with `ctrl-d`'; \ "$EDITOR" "$FNAME"; \ test -s "$FNAME" && bat --language markdown --style plain --pager never "$FNAME" && gum confirm && gh pr {{ mode }} "$NUMBER" --body-file "$FNAME"; \ rm -v "$FNAME" # TODO: git nomad? remote-branch $remote_ref=`just _a_remote_branch origin | cut -d/ -f2-`: #!/usr/bin/env -S bash -euo pipefail branch_dir=$(cut <<<"$remote_ref" -d- -f1) branch_base=$(cut <<<"$remote_ref" -d- -f2-) cd master $GIT worktree add --track ../"$branch_dir/$branch_base" -b "$remote_ref" origin/"$remote_ref" cd ../"$branch_dir/$branch_base" exec just _enter_interactive_shell # === worktrees === # TODO: onto release-xx.yy doesn't work # TODO: python-updates checkout-commit-in-worktree $commit=`just _a_commit`: #!/usr/bin/env -S bash -euo pipefail [[ -n "${commit:-}" ]] if [[ -d tmp/commit-"$commit" ]]; then echo >&2 "Commit already checked out, cd'ing there..." else ( cd master $GIT worktree prune # TODO: fetch the commit from upstream if it doesn't exist? # TODO: support github /commit/ urls? $GIT worktree add ../tmp/commit-"$commit" "$commit" ) fi cd tmp/commit-"$commit" exec just _enter_interactive_shell @new-worktree *args: cd "$(just _new_worktree "$@")"; exec just _enter_interactive_shell @_a_wt_type: gum choose feat fix init bump doc migrate backport drop tmp @_a_wt_name: gum input --placeholder="Branch name?" _new_worktree $type=`just _a_wt_type` $name=`just _a_wt_name` $base=`just _a_upstream_release_branch "Base?"` $onto=`just _a_upstream_release_branch "Onto?" ""`: #!/usr/bin/env -S bash -euo pipefail [[ -n "$type" ]] [[ -n "$name" ]] name="$(printf "%s" "$name" | tr '[:space:]/' -- | tr -s -)" just _mk_worktree 1>&2 \ "$type-$name-{{ epoch }}" \ "$type/$name-{{ epoch }}" \ "$base" \ "$onto" echo "$type/$name-{{ epoch }}" _mk_worktree $name $dir $base="upstream/master" $onto="": #!/usr/bin/env -S bash -euo pipefail -x [[ -n "$name" ]] [[ -n "$dir" ]] [[ -n "$base" ]] remote="$(cut -d/ -f1 <<<"$base")" branch="$(cut -d/ -f2- <<<"$base")" # TODO: use upstream/master/? cd master $GIT worktree prune $GIT fetch "$remote" "$branch" #$GIT branch "$name" "$branch" --no-track $GIT branch "$name" "$base" --no-track $GIT worktree add ../"$dir" "$name" cd ../"$dir" # TODO: use this to determine base instead of using rebase-onto: baserev="$(git merge-base "$base" "$onto")" $GIT pull --rebase "$remote" "$branch" [[ -z "$onto" ]] || just rebase-onto HEAD "$onto" # TODO: resolve the shared ancestor rev and make the worktree with it as base instead? cherry-pick-commits-here *commits: #!/usr/bin/env -S bash -euo pipefail -x declare -a commits=("$@") [[ "${#commits[@]}" -gt 0 ]] || readarray -td $'\n' commits <<<"$( cd "$invokedir"; GIT_LOG_ALL=1 just _some_commits | tac )" [[ "${#commits[@]}" -gt 0 && -n "${commits[*]}" ]] if gum confirm 'Use -x? Will append a line that says "(cherry picked from commit ...)"'; then commits=(-x "${commits[@]}") fi git cherry-pick "${commits[@]}" cherry-pick-commits-into-new-worktree *commits: #!/usr/bin/env -S bash -euo pipefail declare -a commits=("$@") [[ "${#commits[@]}" -gt 0 ]] || readarray -td $'\n' commits <<<"$( cd "$invokedir"; just _some_commits | tac )" [[ "${#commits[@]}" -gt 0 && -n "${commits[*]}" ]] proposed_name="$( git -C "$invokedir" show --oneline --no-patch "${commits[0]}" | cut -d' ' -f2- | cut -d: -f1 | rev | cut -d. -f1 | rev )" if gum confirm 'Use -x? Will append a line that says "(cherry picked from commit ...)"'; then commits=(-x "${commits[@]}") fi worktree="$(just _new_worktree "$(just _a_wt_type)" "$(gum input --placeholder="Branch name?" --value="$proposed_name")")" [[ -n "$worktree" ]] cd "$worktree" git cherry-pick "${commits[@]}" || echo >&2 "ERROR: Cherry pick failed!" exec just _enter_interactive_shell pop-commits-to-new-worktree $commits=`cd "$invokedir"; just _some_commits`: #!/usr/bin/env -S bash -euo pipefail test -n "$commits" || { echo >&2 "no commits were selected!"; false;} # we make two nasty assumption here: # * the last commit passed in is also the topologically oldest one in the git history # * the revs in the interactive rebase are just as short or longer than those returned by 'just _some_commits' type="$(just _a_wt_type)" name="$( cd "$invokedir"; git show --oneline --no-patch "$(head -n1 <<<"$commits")" | cut -d' ' -f2- | cut -d: -f1 | rev | cut -d. -f1 | rev )" cd "$( just _new_worktree "$type" "$(gum input --placeholder="Branch name?" --value="$name")" )" if (tac <<<"$commits" | xargs {{XARGS_NL}} git cherry-pick); then ( cd "$invokedir" EDITOR="sed -i $(xargs {{XARGS_NL}} <<<"$commits" printf " -e 's/^pick %s/#/g'")" \ git rebase "$(tail -n1 <<<"$commits")"^^ -i ) git log -n "$(wc -l <<<"$commits" )" exec just _enter_interactive_shell else echo >&2 "The commits have not been popped." git status echo >&2 "The commits have not been popped." exec just _enter_interactive_shell fi [no-cd] @_rebase_onto $onto: [[ -n "$onto" ]] $GIT fetch upstream "$onto" $GIT rebase --onto upstream/"$onto"...HEAD upstream/master --autostash [no-cd] rebase-onto $base=`just _a_upstream_release_branch "Base?" HEAD` $onto=`just _a_upstream_release_branch "Onto?"`: #!/usr/bin/env -S bash -euo pipefail [[ -n "$onto" ]] if [[ "$base" != HEAD ]] ; then remote="$(cut -d/ -f1 <<<"$base")" branch="$(cut -d/ -f2- <<<"$base")" (set -x; $GIT fetch "${remote:-upstream}" "$branch") fi if [[ "$onto" != HEAD ]] ; then remote="$(cut -d/ -f1 <<<"$onto")" branch="$(cut -d/ -f2- <<<"$onto")" (set -x; $GIT fetch "${remote:-upstream}" "$branch") fi # $GIT rebase --onto upstream/"$onto"...HEAD upstream/master --autostash $GIT rebase --onto "$onto"..."$base" $base --autostash # fix periodic merge conflicts fix-periodic-merge-conflict $from=`just _a_upstream_release_branch "From?"` $to=`just _a_upstream_release_branch "To?"`: cd "$invokedir"; $GIT fetch upstream "$from" cd "$invokedir"; $GIT fetch upstream "$to" cd "$invokedir"; $GIT worktree add -b fix-merge-conflict-"$from"-to-"$to"-{{ epoch }} ../fix/merge-conflict-"$from"-to-"$to"-{{ epoch }} upstream/"$to" cd fix/merge-conflict-"$from"-to-"$to"-{{ epoch }}; $GIT merge upstream/"$from" || true cd fix/merge-conflict-"$from"-to-"$to"-{{ epoch }}; $GIT status @echo "Cheatsheet:" @echo " $GIT ls-files --modified | sort -u | xe -N0 $GIT reset" @echo " $GIT commit" @echo " $GIT push upstream HEAD:$to" cd fix/merge-conflict-"$from"-to-"$to"-{{ epoch }}; exec just _enter_interactive_shell # === setup === # setup or update master and staging++ setup: #!/usr/bin/env -S bash -euo pipefail if [[ ! -e master/.git ]]; then gh_user=$(gum input \ --header "git clone git@github.com:< username >/nixpkgs.git" \ --value="$( if command -v gh >/dev/null && gh auth status >&/dev/null; then gh api user --jq .login else whoami fi )" ) $GIT clone "git@github.com:${gh_user}/nixpkgs.git" master [[ -e master/.git ]] fi cd master if ! $GIT remote get-url upstream >&/dev/null; then (set -x; $GIT remote add upstream "https://github.com/NixOS/nixpkgs.git") fi # TODO: check the mtime on the HEAD file instead? if [[ $(( "$epoch" - "$($GIT log --oneline --format="%ct" HEAD -n1)" )) -gt $(( 60*60*24 )) ]]; then (set -x; $GIT pull --rebase --autostash upstream master >&2) fi add_upstream() ( local branch="$1" if [[ ! -d ../upstream/"$branch" ]]; then set -x $GIT fetch upstream "$branch" $GIT worktree add ../upstream/"$branch" -B "upstream-$branch" upstream/"$branch" else cd ../upstream/"$branch" if [[ $(( "$epoch" - "$($GIT log --oneline --format="%ct" HEAD -n1)" )) -gt $(( 60*60*24 )) ]]; then set -x $GIT pull --rebase --autostash upstream "$branch" >&2 fi fi ) declare -a branches=() readarray -td $'\n' branches < <( just _list_upstream_release_branches "" "" ) # TODO: clean old release branches for branch in "${branches[@]}"; do add_upstream "$branch" done clean: #!/usr/bin/env -S bash -euo pipefail cleanlist="$(just _mk_clean_list)" || exit 1 #gum choose --no-limit --height="$(($(wc -l <<<"$cleanlist") + 5))" <<<"$cleanlist" | bash -x - gum choose --no-limit <<<"$cleanlist" | bash -x - _mk_clean_list: #!/usr/bin/env -S bash -euo pipefail # set -x ( cd master # TODO: assert "upstream" present? just _list_upstream_release_branches "" | xargs {{XARGS_NL}} $GIT fetch upstream ) porcelain2jsonl() { uniq | ( printf "[" while read key data; do if test -z "$key"; then printf "]\n[" else printf '{"%s": "%s"}, ' "$key" "$data" fi done echo "]" ) | sed -e 's/, ]/]/g' | grep -v '^\[\]$' | jq add -c } worktrees="$( cd master; git worktree list --porcelain | porcelain2jsonl )" #cat <<<"$worktrees" ( cd master just _list_upstream_release_branches | xe -j0 $GIT branch --merged ||: ) | sort -u | while IFS= read line; do branch="$(cut <<<"$line" -c3-)" if [[ "$branch" = "master" ]] \ || grep <<<"$branch" "$(just _list_upstream_release_branches '^' '$')" --quiet \ || grep <<<"$branch" "$(just _list_upstream_release_branches '^upstream-' '$')" --quiet; then continue fi #jq <<<"$worktrees" '.branch' worktree="$( jq <<<"$worktrees" 'select(.branch == "refs/heads/'"$branch"'")' -c | head -n1 )" #echo "$line" #echo "$branch" #echo "$worktree" if [[ -n "$worktree" ]]; then path="$(jq <<<"$worktree" .worktree -r )" if [[ -d "$path" ]]; then if [[ "$(realpath -m --relative-to=. "$path" | cut -d/ -f1)" = "upstream" ]]; then continue fi if ( cd "$path" ! $GIT diff HEAD --quiet --exit-code >&/dev/null || $GIT ls-files --others --exclude-standard | grep -q / # ignore toplevel dirty files ) ; then continue fi if [[ "$(basename "$path")" != "tmp" ]] \ && [[ "$(basename "$path")" != "scratch" ]] then printf "rm -rf %q\n" "$path" fi fi fi printf "%s\n" "( cd master && $GIT worktree prune && git branch -D \"$branch\" )" done #printf "%s\n" "( cd master && git worktree prune && git prune )" # slow printf "%s\n" "( cd master && git worktree prune )" printf "%s\n" "rmdir */ --ignore-fail-on-non-empty" # === get/is/test === #get-maintainers-for-packages *attrpaths: _maintainers_tsv get-maintainers-for-packages *attrpaths: _packages_json #!/usr/bin/env -S bash -euo pipefail declare -a attrpaths=("$@") [[ "${#attrpaths[@]}" -gt 0 ]] || readarray -td $'\n' attrpaths < <( just _some_packages ) [[ "${#attrpaths[@]}" -gt 0 ]] #grep &2 "ERROR: no target branches chosen" || false retval=0 for branch in "${branches[@]}"; do echo >&2 "Checking $branch..." if ! $GIT merge-tree "$branch" "$rev" >/dev/null; then echo >&2 "❌ MERGE-CONFLICT with $branch" retval=1 [[ -t 1 ]] || break # short circuit if only retcode matters fi done if [[ "$retval" -eq 0 ]]; then echo >&2 "✅ No conflicts!" fi exit "$retval" [no-cd] test-merge-in-new-branch $upstream=`just _a_upstream_release_branch "Select upstream branch"`: #!/usr/bin/env -S bash -euo pipefail [[ -n "$upstream" ]] || ! echo >&2 "ERROR: No branch selected" oldref="$(git branch --show-current ||:)" [[ -n "$oldref" ]] || oldref="$(GIT rev-parse HEAD)" [[ -n "$oldref" ]] || ! echo >&2 "ERROR: Unable to determine current ref" echo >checkout-original-rev.sh "git checkout $oldref" chmod +x checkout-original-rev.sh remote=$(cut <<<"$upstream" -d'/' -f1) branch=$(cut <<<"$upstream" -d'/' -f2) set -x git checkout -b test-merge-"$oldref"-into-"$remote"-"$branch" git pull "$remote" "$branch" --rebase open-package-urls $package=`just _a_package`: #!/usr/bin/env -S bash -euo pipefail fpkg="$(jq -rn --arg x "$package" '$x | @uri')" bpkg="$(jq -rn --arg x "$(rev <<<"$package" | cut -d. -f1 | rev)" '$x | @uri')" # homepage="$(nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix "$package".meta.homepage --raw ||:)" srcpage="$(nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix "$package".src.meta.homepage --raw ||:)" position="$({ nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix "$package".meta.position --raw ||: ; } | rev | cut -d: -f2- | rev | sd -F "$PWD/master/" "")" declare -a urls=() urls+=("https://hydra.nixos.org/job/nixos/trunk-combined/nixpkgs.${fpkg}.x86_64-linux") urls+=("https://github.com/NixOS/nixpkgs/pulls?q=is%3Apr+in%3Atitle+${bpkg}") urls+=("https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+in%3Atitle+${bpkg}") [[ -z "$position" ]] || urls+=(https://github.com/NixOS/nixpkgs/blame/master/"$position") [[ -z "$position" ]] || urls+=(https://github.com/NixOS/nixpkgs/commits/master/"$position") # [[ -z "$homepage" ]] || urls+=("$homepage") [[ -z "$srcpage" ]] || urls+=("$srcpage") urls+=("https://search.nixos.org/packages?channel=unstable&query=${fpkg}") printf "%s\n" "" "${urls[@]}" | fzf --reverse --multi | { grep . ||:; } | xe xdg-open enqueue-nixpkgs-review *$prs: #!/usr/bin/env -S bash -euo pipefail if ! command -v pueue >/dev/null || ! pueue status >&/dev/null; then echo >&2 "ERROR: pueue not available!" exit 1 fi declare -a prs=("$@") if [[ "${#prs[@]}" -eq 0 ]]; then readarray -td $'\n' prs < <( just _some_prs ) [[ "${#prs[@]}" -ne 0 ]] fi declare -a all_systems=( x86_64-linux aarch64-linux aarch64-darwin x86_64-darwin ) declare -a systems=() readarray -td $'\n' systems < <( printf "%s\n" "${all_systems[@]}" | fzf --sync --layout=reverse --multi ) declare -a all_configs=( '' # 'crossSystem = "aarch64-linux";' # TODO: doesn't work # 'crossSystem = "aarch64-unknown-linux-gnu";' # TODO: doesn't work 'allowInsecurePredicate = x: true;' 'cudaSupport = true;' 'rocmSupport = true;' # 'allowUnfree = true;' # default in nixpkgs-review 'allowUnfree = false;' 'allowBroken = true;' # 'allowUnsupportedSystem = true;' ) declare -a configs=() readarray -td $'\n' "configs" < <( printf "%s\n" "${all_configs[@]}" | fzf --sync --layout=reverse --multi ) cd master # required for nixpkgs-review prefix="$( gum choose \ $'' \ $'bolle\t~/repos/nixpkgs/master/' \ $'eple\t~/repos/nixpkgs/master/' \ $'garp\t~/repos/nixpkgs/master/' )" gum confirm for pr in $prs; do declare -a cmd # nixpkgs-review cmd=( nixpkgs-review pr "$pr" --no-shell --post-result --systems "${systems[*]}" ) if [[ "${#configs[@]}" -gt 0 ]]; then cmd+=(--extra-nixpkgs-config "{ ${configs[*]} }") fi # TODO: --checkout commit # TODO: --eval local # pueue cmd=( pueue add --escape "${cmd[@]}" ) # ssh if [[ -n "$prefix" ]]; then cmd=( ssh "$(cut <<<"$prefix" -f1)" "cd $(cut <<<"$prefix" -f2) && systemd-run --scope --user nohup $(printf " %q" "${cmd[@]}" )" ) fi (set -x; "${cmd[@]}" ) done # === format helpers === [no-cd] format-files-dirty: #!/usr/bin/env -S bash -euo pipefail git ls-files --modified | sort -u | grep . | xe -j0 nixfmt [no-cd] format-files-touched-by-commit *commits: #!/usr/bin/env -S bash -euo pipefail declare -a commits=("$@") [[ "${#commits[@]}" -gt 0 ]] || readarray -td $'\n' commits < <( just _some_commits ) [[ "${#commits[@]}" -gt 0 ]] printf "%s\n" "${commits[@]}" | xe -s 'git log --pretty=format: --name-only "$1"^.."$1"' | sort -u | grep . | xe nixfmt # === internal helpers === [no-cd] mk-packages-json $fname="packages.json" $rootdir=`cd "$invokedir"; git rev-parse --show-toplevel` $outpaths=`just _gum_bool "Eval outpaths?" --default=no` $system=`just _a_system` $allow_aliases=`just _gum_bool --default=no "Allow aliases?"`: #!/usr/bin/env -S bash -euo pipefail extra_args=() if [[ -n "$outpaths" && "$outpaths" != false ]]; then extra_args+=(--drv-path --out-path) fi # keeping this in ram requires 220mb extra memory # TODO: use a tmpdir instead? packages_json="$( set -x time nix-env "$fname" _packages_json: #!/usr/bin/env -S bash -euo pipefail { if [[ ! -s packages.json || $(( "$epoch" - "$(stat -c %Y packages.json )" )) -gt $(( 60*60*24*7 )) ]]; then set -x [[ -d upstream/master/ ]] || just setup if ! $GIT -C upstream/master/ diff HEAD --exit-code --quiet 1>&2; then echo >&2 "ERROR: $(realpath upstream/master) is dirty!" false fi git -C upstream/master/ pull upstream master --rebase --autostash 1>&2 just mk-packages-json "packages.json" "./upstream/master" false "$(just _this_system)" false fi } /dev/null # HACK just list-packages | cut -f1 | fzf --sync --layout=reverse | grep . _some_packages: _packages_json #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_PACKAGE:-}" ]] && { cat <<<"$JUST_PACKAGE" ; exit 0; } ||: #gum spin --show-output just list-packages | cut -f1 | fzf --sync --layout=reverse --multi | grep . just list-packages >/dev/null just list-packages | cut -f1 | fzf --sync --layout=reverse --multi | grep . @_attrpaths_2_aliases: sed -E 's/^python3[0-9]+Packages\./python3Packages./g' \ | tr '0123456789' '9876543210' | sort -u | tr '0123456789' '9876543210' @_this_system: nix config show system _a_system: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_SYSTEM:-}" ]] && { head -n1 <<<"$JUST_SYSTEM"; exit 0; } ||: # nix eval --file upstream/master/default.nix lib.systems.flakeExposed --json | jq .[] -r | xargs {{XARGS_NL}} gum choose --selected "$(just _this_system)" systems="$(nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix lib.systems.flakeExposed --json)" system="$(just _this_system)" # system="aarch64-darwin" jq <<<"$systems" .[] -r | fzf --sync --layout=reverse $( jq <<<"$systems" --arg system "$system" -r \ 'map(. == $system) | index(true) | if . == null then "" else @sh "--bind load:pos(\(. + 1))" end' ) _some_systems: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_SYSTEM:-}" ]] && { cat -n1 <<<"$JUST_SYSTEM"; exit 0; } ||: # nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix lib.systems.flakeExposed --json | jq .[] -r | xargs {{XARGS_NL}} gum choose --no-limit --selected "$(just _this_system)" systems="$(nix eval {{NIX_EVAL_OPTS}} --file upstream/master/default.nix lib.systems.flakeExposed --json)" system="$(just _this_system)" # system="aarch64-darwin" jq <<<"$systems" .[] -r | fzf --sync --layout=reverse --multi $( jq <<<"$systems" --arg system "$system" -r \ 'map(. == $system) | index(true) | if . == null then "" else @sh "--bind load:pos(\(. + 1))" end' ) _a_maintainer: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_MAINTAINER:-}" ]] && { head -n1 <<<"$JUST_MAINTAINER"; exit 0; } ||: nix eval {{NIX_EVAL_OPTS}} --file upstream/master/maintainers/maintainer-list.nix \ --apply 'x: builtins.attrValues (builtins.mapAttrs (k: v: let user = v.github or k; name = v.name or null; in [user (if name != user then name else null) (v.email or null)]) x)' --json \ | jq '.[]|@tsv' -r \ | sort --ignore-case \ | column -t -s$'\t' \ | fzf --sync --layout=reverse \ | cut -d' ' -f1 _some_maintainers: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_MAINTAINER:-}" ]] && { cat <<<"$JUST_MAINTAINER"; exit 0; } ||: nix eval {{NIX_EVAL_OPTS}} --file upstream/master/maintainers/maintainer-list.nix \ --apply 'x: builtins.attrValues (builtins.mapAttrs (k: v: let user = v.github or k; name = v.name or null; in [user (if name != user then name else null) (v.email or null)]) x)' --json \ | jq '.[]|@tsv' -r \ | sort --ignore-case \ | column -t -s$'\t' \ | fzf --sync --layout=reverse --multi \ | cut -d' ' -f1 _a_committer: _committers_jsonl #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_COMMITTERS:-}" ]] && { head -n1 <<<"$JUST_COMMITTERS"; exit 0; } ||: nix eval {{NIX_EVAL_OPTS}} --file upstream/master/maintainers/maintainer-list.nix \ --apply 'x: builtins.attrValues (builtins.mapAttrs (k: v: let user = v.github or k; name = v.name or null; in [user (if name != user then name else null) (v.email or null)]) x)' --json \ | jq --argjson committers "$(jq <.direnv/nixpkgs-committers.jsonl -s 'map(.login)' -c)" '.[] | .[0] as $github| select([$committers[]|.==$github]|any) | @tsv' -r \ | sort --ignore-case \ | column -t -s$'\t' \ | fzf --sync --layout=reverse \ | cut -d' ' -f1 _some_committers: _committers_jsonl #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_COMMITTERS:-}" ]] && { cat <<<"$JUST_COMMITTERS"; exit 0; } ||: nix eval {{NIX_EVAL_OPTS}} --file upstream/master/maintainers/maintainer-list.nix \ --apply 'x: builtins.attrValues (builtins.mapAttrs (k: v: let user = v.github or k; name = v.name or null; in [user (if name != user then name else null) (v.email or null)]) x)' --json \ | jq --argjson committers "$(jq <.direnv/nixpkgs-committers.jsonl -s 'map(.login)' -c)" '.[] | .[0] as $github| select([$committers[]|.==$github]|any) | @tsv' -r \ | sort --ignore-case \ | column -t -s$'\t' \ | fzf --sync --layout=reverse --multi \ | cut -d' ' -f1 _committers_jsonl: #!/usr/bin/env -S bash -euo pipefail # TODO: this requires 'gh login'? cachefile=.direnv/nixpkgs-committers.jsonl if [[ ! -s "$cachefile" || $(( "$epoch" - "$(stat -c %Y "$cachefile" )" )) -gt $(( 60*60*24*7*4 )) ]]; then [[ ! -f "${cachefile}.staging" ]] || rm -v "${cachefile}.staging" let page=0 ||: while ((page++)) resp="$( gh api -H 'X-GitHub-Api-Version: 2022-11-28' "/orgs/nixos/teams/nixpkgs-committers/members?per_page=100&page=${page}" | jq '.[]' -c )" cat <<<"$resp" >>"${cachefile}.staging" [[ "$(wc -l <<<"$resp" )" -ge 100 ]] do true; done sort <"${cachefile}.staging" -u --ignore-case > "$cachefile" rm "${cachefile}.staging" fi _a_pr: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_PR:-}" ]] && { head -n1 <<<"$JUST_PR"; exit 0; } ||: cd upstream/master gh pr list --limit 1000 --json 'number,title' --state open | jq '.[]|"\(.number) - \(.title)"' -r | gum filter --placeholder "Pick PR..." --height 15 | cut -d' ' -f1 _some_prs: #!/usr/bin/env -S bash -euo pipefail [[ -n "${JUST_PR:-}" ]] && { cat <<<"$JUST_PR"; exit 0; } ||: cd upstream/master gh pr list --limit 1000 --json 'number,title' --state open | jq '.[]|"\(.number) - \(.title)"' -r | gum filter --placeholder "Pick PR..." --height 15 --no-limit | cut -d' ' -f1 @_list_upstream_release_branches $prefix="upstream/" $suffix="": printf "${prefix//%/%%}%s${suffix//%/%%}\n" \ master \ staging \ staging-next \ python-updates \ release-25.05 \ staging-25.05 \ staging-next-25.05 @_a_upstream_release_branch $header *extra_branches: gum filter --header="$header" "${@:2}" $(just _list_upstream_release_branches "${prefix:-upstream/}" "${suffix:-}") # --ordered @_some_upstream_release_branches $header *extra_branches: gum filter --header="$header" "${@:2}" $(just _list_upstream_release_branches "${prefix:-upstream/}" "${suffix:-}") --no-limit # --ordered @_sanitize_pr_url $number: echo "$number" | tr '[:upper:]' '[:lower:]' | sed -Ee 's|https?://github.com/nixos/nixpkgs/pull/([0-9]+).*|\1|' @_a_remote_branch $remote="origin": cd master; $GIT fetch "$remote" cd master; $GIT for-each-ref --sort=committerdate refs/remotes --format='%(committerdate:short) %(refname:short)' | sort -r | grep " $remote/" | gum filter --height=15 --no-sort | cut -d' ' -f2- [no-exit-message] _gum_bool *args: gum confirm "$@" && echo true || echo false [no-cd] [no-exit-message] _enter_interactive_shell *_: #!/usr/bin/env -S bash -euo pipefail if [[ -t 0 && -z "${SSH_CLIENT:-}" && -z "${SSH_TTY:-}" ]]; then # report new working dir to terminal so that tab are spawned relative # based on ghostty:src/shell-integration/bash/ghostty.bash if [[ -n "$GHOSTTY_RESOURCES_DIR" ]]; then _ghostty_last_reported_cwd="$PWD" builtin printf "\e]7;kitty-shell-cwd://%s%s\a" "$HOSTNAME" "$PWD" fi fi # TODO: skip if no tty? # TODO: -l ? exec "$SHELL" "$@" # TODO: somehow report the old PWD here, will require i rethink the use of [no-cd] and maybe instead accept the new working dir via an argument # maintenance @_fmt_justfile: just --unstable --fmt #sd "\n+(@_[a-zA-Z_-]+:)" "\n" justfile