Files
just-nixpkgs/justfile

738 lines
32 KiB
Makefile

#!/usr/bin/env just --justfile
# makes variables accesible as $1 $2 $@, useful for escaping variadics
set positional-arguments := true
export invokedir := invocation_directory()
export epoch := `date +%s`
export GUM_FILTER_HEIGHT := "15"
export GIT := `command -v git-wait >/dev/null && echo "git-wait" || echo "git"`
# will trigger a direnv reload when we enter $SHELL
export DIRENV_WATCHES := ""
# please no surprises for now
export NIXPKGS_CONFIG := ""
export NIX_PATH := `echo "${NIX_PATH_ORIG:-"${NIX_PATH:-}"}"`
# required programs: git nix nom gum fzf tee nix-update
@_default:
cd "$invokedir"; just "$(gum filter $(just --summary --unsorted))"
# === 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
cut <"$cachefile" "$@"
else
jq <packages.json 'to_entries[] | select(.value.meta.position==null|not) | [.key, .value.meta.position] | @tsv' -r \
| tr '0123456789' '9876543210' | sort | tr '0123456789' '9876543210' \
| sed -e "s#\t$(realpath master)/#\t#" \
| sed -e 's#:\([0-9]*\)$#\t\1#' \
| grep . >"$cachefile"
fi
@_list_packages_fname_filtered:
xargs -d $'\n' -n 2000 just __list_packages_fname_filtered
@__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:
xargs -d $'\n' -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[@]}")"
[no-cd]
@list-ripgrepped-packages +$ripgrep_args:
[[ -e .git && -e flake.nix ]] || { echo >&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
@list-dirty-packages:
test -n "$($GIT diff HEAD --name-only)" || { echo >&2 "No unstaged dirty files found!"; false; }
cd "$invokedir"; $GIT diff HEAD --name-only | just _list_packages_fname_filtered
@list-touched-packages-since $rev=`cd "$invokedir"; just _a_commit`:
test -n "$rev" || { echo >&2 "you must pick a revision to compare against!"; false; }
cd "$invokedir"; $GIT diff "$rev" --name-only | just _list_packages_fname_filtered
@list-unmerged-packages:
cd "$invokedir"; $GIT fetch upstream ||:
cd "$invokedir"; $GIT log HEAD --not --remotes --oneline | cut -d' ' -f2- | cut -d: -f1 | grep -vE '(^treewide|[, ])' | sort -u
# === build helpers ===
@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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs 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 printf "${attr_prefix//%/%%}"'%s'"${attr_suffix//%/%%} " | xargs just instantiate-packages
#
[no-cd]
instantiate-packages +$attrpaths="":
#!/usr/bin/env -S bash -euo pipefail
# slower, but resitant to bad attrpaths
test -n "${attrpaths:-}" || set -- $(just _some_packages)
if [[ -d "${_INSTANTIATE_CACHE:-}" ]]; then
mkdir -p "$_INSTANTIATE_CACHE"/{roots,paths}
worker() {
local attrpath="$1"
if [[ -s "$_INSTANTIATE_CACHE/paths/$attrpath" ]]; then
cat "$_INSTANTIATE_CACHE/paths/$attrpath"
else
NIXPKGS_ALLOW_UNFREE=1 nix-instantiate . -A "$attrpath" \
${_JUST_NIX_INSTANTIATE_ARGS:-} \
--add-root "$_INSTANTIATE_CACHE/roots/$attrpath" \
| tee "$_INSTANTIATE_CACHE/paths/$attrpath"
fi
}
printf "%s\n" "$@" | grep . | \
xe -j0 -s "$(declare -f worker); worker \"\$@\""
else
printf "%s\n" "$@" | grep . | \
xe -j0 -s 'NIXPKGS_ALLOW_UNFREE=1 nix-instantiate . -A "$1" '"${_JUST_NIX_INSTANTIATE_ARGS:-}"''
fi
# _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"'
# _JUST_NIX_INSTANTIATE_ARGS='--system darwin'
@build-packages +$attrpaths="":
test -n "${attrpaths:-}" || set -- $(just _some_packages); \
cd "$invokedir"; just _build_packages results "$@"
[no-cd]
_build_packages $outdir +$attrpaths:
#!/usr/bin/env -S bash -euo pipefail -x
# 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" && {
fd . "$outdir" --type l -X rm -v
fd \\\.log$ "$outdir" --type f -X rm -v
}
mkdir -p "$outdir"
export _INSTANTIATE_CACHE=$(mktemp -d)
export NOTFOUNDDIR="$(mktemp -d)"; touch "$NOTFOUNDDIR"/no-eval
shift
just instantiate-packages "$@" | tee >(command cat >&2) \
| eval xargs nom-build --keep-going --no-out-link ${_JUST_NIX_BUILD_ARGS:-} ||:
worker() {
set -x
local attrpath="$1"
local dst="$outdir/$attrpath"
local drvpath=$( just instantiate-packages "$attrpath" )
# NIXPKGS_ALLOW_UNFREE=1 nix-build . -A "$attrpath" ${_JUST_NIX_INSTANTIATE_ARGS:-} -j0 --option builders "" --option substitute false -o "$dst" >&/dev/null
if [[ -n "$drvpath" ]]; then
NIXPKGS_ALLOW_UNFREE=1 nix-build "$drvpath" -j0 --option builders "" --option substitute false -o "$dst" >&/dev/null
fi
# local outpaths=$( nix-store -q --outputs "$drvpath" ) # doesn't gcroot, some outputs may not be pulled from builders
if [[ -L "$dst" ]]; then
nix log $(readlink "$dst") > "$dst".log 2>/dev/null ||:
else
if [[ -z "$drvpath" ]]; then
ln -s "$NOTFOUNDDIR"/no-eval "$dst"
else
ln -s /build-failure-"$attrpath" "$dst"
nix log ".#$attrpath" ${_JUST_NIX_INSTANTIATE_ARGS:-} > "$dst".log 2>/dev/null ||:
fi
fi
}
printf "%s\n" "$@" | xe -j0 -s "$(declare -f worker); worker \"\$@\""
# echo -e "#!/usr/bin/env bash\ntest -f flake.nix || cd ..; env _JUST_NIX_INSTANTIATE_ARGS=\"\$*\" just _build_packages \"$outdir\"" $attrpaths > "$outdir"/_rerun.sh
cat <<-EOF >"$outdir"/_rerun.sh
#!/usr/bin/env bash
while [[ ! -f flake.nix ]]; do
cd ..
done
_JUST_NIX_INSTANTIATE_ARGS="\$*" just _build_packages "$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 -x
[[ -e .git && -f flake.nix ]] || { printf >&2 "%s\n" "ERROR: not in repo root!"; false; }
declare packages=("$@")
# TODO: allow selecting different names than result-src and results-src-writeable
just _build_packages 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 "$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
@build-pr-packages $pr=`just _a_pr` $merge=`gum choose --header="which refs/pull/<id>/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" $attrpaths | xargs echo + nom build $args
-(printf "github:nixos/nixpkgs/refs/pull/"$(just _sanitize_pr_url "$pr")"/$merge#%s" $attrpaths | xargs nom build $args) || true
-printf "github:nixos/nixpkgs/refs/pull/"$(just _sanitize_pr_url "$pr")"/$merge#%s" $attrpaths | xargs echo + nom build $args
# === commit helpers ===
#@commit-with-pkgname:
# cd "$invokedir"; test -n "$(git diff HEAD --name-only --staged)" || { echo >&2 "No files are staged!"; false; }
# cd "$invokedir"; $GIT -c commit.template=<(echo "$(just list-packages | grep --fixed-strings "$(git diff HEAD --name-only --staged)" | cut -f1 | head -n1): ") commit
[no-cd]
commit-dirty-packages +$message=`gum input --placeholder="commit message, (attrpath: this message)"`:
#!/usr/bin/env -S bash -euo pipefail
[[ -n "$message" ]]
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 "$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 --height 15 |
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 }})"
# === speed worktrees ===
bump *packages:
#!/usr/bin/env -S bash -euo pipefail -x
declare -a packages=("$@")
[[ -n "${packages[*]}" ]] || packages=($(just _some_packages))
worktree=$(just _new_worktree bump "$(printf "%s\n" "${packages[0]}" | rev | cut -d. -f1 | rev)")
[[ -n "worktree" ]]
cd "$worktree"
just _bump "${packages[@]}"
NIXPKGS_ALLOW_UNFREE=1 "$SHELL"
[no-cd]
_bump +$packages:
#!/usr/bin/env -S bash -euo pipefail -x
[[ -e .git && flake.nix ]]
printf "%s.src\n" "$@" | xargs just _build_packages results-src-old ||: # TODO: unpack?
config=(env
NIXPKGS_ALLOW_UNFREE=1
NIXPKGS_ALLOW_BROKEN=1
NIXPKGS_ALLOW_INSECURE=1
)
for package in "$@"; do
if nix eval -f . "$package".passthru.updateScript >&/dev/null; then
HEAD=$($GIT rev-parse HEAD)
"${config[@]}" 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 .
"${config[@]}" nix-update "$package" --commit || true
fi
else
"${config[@]}" nix-update "$package" --commit || true
fi
done
set +e
printf "%s.src\n" "$@" | xargs just _build_packages results-src-new # TODO: unpack?
just _build_packages results "$@"
# TODO: filter non-existing tests:
printf "%s.tests\n" "$@" | env _JUST_NIX_BUILD_ARGS="-j1" xargs just _build_packages results-tests
printf "HEAD^%s " $(seq 0 $(($#-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 "delta results-src-{old,new}/%s.src/.\n" "$@"
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; }
cd "$(just _new_worktree fix "$(echo "${packages[0]}" | rev | cut -d. -f1 | rev)")"
just _fix "${packages[@]}" ||:
$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=("$@")
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
echo "./results/_rerun.sh --system $(nix config show system)"
pr $number=`just _a_pr`:
#!/usr/bin/env -S bash -euo pipefail
number=$(just _sanitize_pr_url "$number")
if [[ -d prs/pr-"$number" ]]; then
cd prs/pr-"$number"
elif test -d prs/pr-"$number"-*; then
cd prs/pr-"$number"-*
else
(cd master;
$GIT worktree prune
$GIT worktree add ../prs/pr-"$number"
)
cd prs/pr-"$number"
gh pr checkout "$number"
fi
$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 '<details>'; \
>>"$FNAME" echo '<summary>:clipboard: proposed patch</summary>'; \
>>"$FNAME" echo; \
>>"$FNAME" echo '```patch'; \
>>"$FNAME" $GIT diff --staged; \
>>"$FNAME" echo '```'; \
>>"$FNAME" echo; \
>>"$FNAME" echo '</details>'; \
>>"$FNAME" echo; \
>>"$FNAME" echo '_How to apply (pick one):_'; \
>>"$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"
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"
$SHELL
# === worktrees ===
# TODO: onto release-xx.yy doesn't work
# TODO: python-updates
@new-worktree *args:
cd "$(just _new_worktree "$@")"; $SHELL
@_a_wt_type:
gum choose feat fix init bump doc migrate
@_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" ]]
remote="$(cut -d/ -f1 <<<"$base")"
branch="$(cut -d/ -f2- <<<"$base")"
cd master
$GIT worktree prune
$GIT fetch "$remote" "$branch"
$GIT branch "$name" "$branch" --no-track
$GIT worktree add ../"$dir" "$name"
cd ../"$dir"
$GIT pull "$remote" "$branch"
[[ -z "$onto" ]] || just rebase-onto HEAD "$onto"
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 a nasty assumption here: the last commit passed in is also the topologically oldest one in the git history
# we also assume the revs in the interactive rebase are just as short or longer as 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 git cherry-pick); then
(
cd "$invokedir"
EDITOR="sed -i $(xargs <<<"$commits" printf " -e 's/^pick %s/#/g'")" \
git rebase "$(tail -n1 <<<"$commits")"^^ -i
)
git log -n "$(wc -l <<<"$commits" )"
$SHELL
else
echo >&2 "The commits have not been popped."
git status
echo >&2 "The commits have not been popped."
$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 }}; $SHELL
# === setup ===
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
$GIT remote add upstream "https://github.com/NixOS/nixpkgs.git"
fi
add_upstream() {
local branch="$1"
if [[ ! -d ../upstream/"$branch" ]]; then
$GIT fetch upstream "$branch"
$GIT worktree add ../upstream/"$branch" -B "$branch" upstream/"$branch"
fi
}
add_upstream staging
add_upstream staging-next
add_upstream staging-24.11
add_upstream staging-next-24.11
# TODO: test
#upstreams-fetch:
# cd master; $GIT fetch upstream master
# cd master; $GIT fetch upstream staging
# cd master; $GIT fetch upstream staging-next
# cd master; $GIT fetch upstream release-24.05
# cd master; $GIT fetch upstream staging-24.05
# cd master; $GIT fetch upstream release-23.11
# cd master; $GIT fetch upstream staging-23.11
# cd master; $GIT fetch upstream python-updates
# === helpers ===
# TODO stat the file and redo if too old, or tie it to the master checkout commit
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 -
_mk_clean_list:
#!/usr/bin/env -S bash -euo pipefail
# set -x
(
cd master
# TODO: assert "upstream" present?
just _upstream_release_branches "" | xargs -d'\n' $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 _upstream_release_branches | xe -j0 $GIT branch --merged ||:
) | sort -u |
while IFS= read line; do
branch="$(cut <<<"$line" -c3-)"
if [[ "$branch" = "scratch" ]] \
|| grep <<<"$branch" "$(just _upstream_release_branches '^' '$')" --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 (
cd "$path"
! $GIT diff HEAD --quiet --exit-code >&/dev/null ||
$GIT ls-files --others --exclude-standard |
grep -qEv '^(update-executables\.txt|results(-.*)?|build\.nix|(asd|do|foo|test|spismeg|packages)[2-9]?\.(sh|txt|json)|diff\.patch|patch\.diff|(packages(_?[0-9]+))\.json|[^/]*\.log)$'
) ; 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"
_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
if ! $GIT -C master/ diff HEAD --exit-code --quiet 1>&2; then
echo >&2 "ERROR: $(realpath master) is dirty!"
fi
git -C master/ pull upstream master 1>&2
time nix-env > packages.json \
--extra-experimental-features no-url-literals \
--option system x86_64-linux \
-f ./master \
-qaP \
--json \
--meta \
--show-trace \
--no-allow-import-from-derivation \
--arg config '{ allowAliases = false; }'
fi
@_a_commit *extra_revs:
# TODO: check if in a nixpkgs git repo
{ \
test $# -eq 0 || printf "%s\n" "$@"; \
cd "$invokedir"; $GIT log --oneline -n800; \
} | gum filter --placeholder "Pick commit..." --height 10 | cut -d' ' -f1
@_some_commits:
# TODO: check if in a git repo
#cd "$invokedir"; $GIT log --oneline -n800 | gum filter --placeholder "Pick commit..." --height 10 --no-limit | cut -d' ' -f1
cd "$invokedir"; $GIT log --oneline -n800 | fzf --layout=reverse --multi --height 15 | cut -d' ' -f1
@_a_package: _packages_json
#gum spin --show-output just list-packages | cut -f1 | fzf --sync --layout=reverse --height 15 | grep .
just list-packages >/dev/null
just list-packages | cut -f1 | fzf --sync --layout=reverse --height 15 | grep .
@_some_packages: _packages_json
#gum spin --show-output just list-packages | cut -f1 | fzf --sync --layout=reverse --height 15 --multi | grep .
just list-packages >/dev/null
just list-packages | cut -f1 | fzf --sync --layout=reverse --height 15 --multi | grep .
@_a_pr:
cd master; gh pr list --limit 1000 --json 'number,title' --state open | jq '.[]|"\(.number) - \(.title)"' -r | gum filter --placeholder "Pick PR..." --height 10 | cut -d' ' -f1
@_upstream_release_branches $prefix="upstream/" $suffix="":
printf "${prefix//%/%%}%s${suffix//%/%%}\n" \
master \
staging \
staging-next \
python-updates \
release-24.11 \
staging-24.11 \
staging-next-24.11
@_a_upstream_release_branch $header *extra_branches:
shift; gum choose --header="$header" "$@" $(just _upstream_release_branches "${prefix:-upstream/}")
# --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-
@_fmt:
just --unstable --fmt
#sd "\n+(@_[a-zA-Z_-]+:)" "\n" justfile