Skip to content

Instantly share code, notes, and snippets.

@aymanbagabas
Last active February 10, 2025 03:41
Show Gist options
  • Save aymanbagabas/96579c4c62f9e20066001a11cdd682fa to your computer and use it in GitHub Desktop.
Save aymanbagabas/96579c4c62f9e20066001a11cdd682fa to your computer and use it in GitHub Desktop.
Find all versions of a package that were available in a channel and the revision you can download it from using https://github.com/lazamar/nix-package-versions
#!/usr/bin/env bash
# Check if gum is installed
if ! command -v gum &>/dev/null; then
echo "Error: gum is not installed. Please install it first."
exit 1
fi
# Default values
CHANNEL="nixpkgs-unstable"
FORMAT=""
# Show help function
show_help() {
echo "Usage: $0 [-c channel] [-f format] package" >&2
echo >&2
echo "Options:" >&2
echo " -h Show this help message" >&2
echo " -c <channel> Specify the Nix channel (default: nixpkgs-unstable)" >&2
echo " -f <format> Output format (env|shell|profile|in-shell)" >&2
echo >&2
echo "Examples:" >&2
echo " $0 python # Search python in nixpkgs-unstable" >&2
echo " $0 -c nixos-23.05 python # Search python in nixos-23.05" >&2
echo " $0 -f shell python # Get nix shell command for python" >&2
exit 1
}
# Parse command line arguments
while getopts "c:f:h" opt; do
case $opt in
c)
CHANNEL="$OPTARG"
;;
f)
FORMAT="$OPTARG"
;;
h)
show_help
;;
\?)
echo "Invalid option: -$OPTARG" >&2
show_help
;;
esac
done
# Shift off the options and optional --
shift $((OPTIND - 1))
# Get package name from argument or gum input if in TTY
PACKAGE=${1}
if [ -z "$PACKAGE" ]; then
if [ -t 0 ]; then # Check if running in a TTY
PACKAGE=$(gum input --placeholder "Enter package name" --header "Which package do you want to search?")
fi
if [ -z "$PACKAGE" ]; then
show_help
fi
fi
URL="https://lazamar.co.uk/nix-versions/?channel=${CHANNEL}&package=${PACKAGE}"
# Fetch data
echo "Fetching data for package '${PACKAGE}' from channel '${CHANNEL}'..." >&2
HTML=$(curl -sL "$URL")
# Extract the table data
DATA=$(echo "$HTML" | grep -Eo '<tbody>.*</tbody>' || echo "")
if [ -z "$DATA" ]; then
echo "Error: No data found for package '${PACKAGE}' in channel '${CHANNEL}'" >&2
exit 1
fi
# Process the data into different formats
process_data() {
echo "$DATA" | grep -o '<tr[^>]*>.*</tr>' | sed 's/<tr[^<]*>/\n/g' | while read -r line; do
data=$(echo "$line" | grep -o '<td>[^<]*</td>' | sed 's/<td>\([^<]*\)<\/td>/\1/')
package=$(echo "$data" | sed -n '1p')
version=$(echo "$data" | sed -n '2p')
revision=$(echo "$line" | grep -o '<a[^<]*</a>' | sed 's/<a[^>]*>\([^<]*\)<\/a>/\1/' | sed -n '1p')
date=$(echo -n "$data" | sed -n '3p')
if [ "$package" != "$PACKAGE" ]; then
continue
fi
printf "%s %s %s\n" "$version" "$revision" "$date"
done
}
# Build data array
DATA=$(process_data | sort -k3,3r -k1,1r)
# Let user filter the versions sorted by date and without the revision in the output
CHOICE=$(echo "$DATA" | gum filter --height=20 --reverse --header "Select version" || echo "")
if [ -z "$CHOICE" ]; then
echo "Error: No version selected" >&2
exit 1
fi
VERSION=$(echo "$CHOICE" | cut -d' ' -f1)
REVISION=$(echo "$CHOICE" | cut -d' ' -f2)
TEMPLATE_ENV=$(
cat <<EOF
nix-env -iA $PACKAGE -f https://github.com/NixOS/nixpkgs/archive/$REVISION.tar.gz
EOF
)
TEMPLATE_IN_SHELL=$(
cat <<EOF
nix-shell -p $PACKAGE -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/$REVISION.tar.gz
EOF
)
TEMPLATE_SHELL=$(
cat <<EOF
nix shell github:nixos/nixpkgs/$REVISION#$PACKAGE
EOF
)
TEMPLATE_PROFILE=$(
cat <<EOF
nix profile install nixpkgs/$REVISION#$PACKAGE
EOF
)
TEMPLATE=$(
cat <<EOF
# Install $PACKAGE with nix-env.
\`\`\`sh
$TEMPLATE_ENV
\`\`\`
# Use $PACKAGE in a nix-shell.
\`\`\`sh
$TEMPLATE_IN_SHELL
\`\`\`
# Use $PACKAGE with nix shell.
\`\`\`sh
$TEMPLATE_SHELL
\`\`\`
# Install $PACKAGE with nix profile.
\`\`\`sh
$TEMPLATE_PROFILE
\`\`\`
# Use $PACKAGE in a nix script via tarball
\`\`\`nix
let
pkgs = import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/$REVISION.tar.gz";
}) {};
myPkg = pkgs.$PACKAGE;
in
...
\`\`\`
# Use $PACKAGE in a nix script via Git
\`\`\`nix
let
pkgs = import (builtins.fetchGit {
# Descriptive name to make the store path easier to identify
name = "my-old-revision";
url = "https://github.com/NixOS/nixpkgs/";
ref = "refs/heads/$CHANNEL";
rev = "$REVISION";
}) {};
myPkg = pkgs.$PACKAGE;
in
...
\`\`\`
EOF
)
INFO="
For more information, see the [Installation Instructions](https://lazamar.co.uk/nix-versions/?package=$PACKAGE&version=$VERSION&fullName=$PACKAGE-$VERSION&keyName=$PACKAGE&revision=$REVISION&channel=$CHANNEL)
"
# Format the output if format is not specified
case "$FORMAT" in
'env')
echo "$TEMPLATE_ENV"
;;
'shell')
echo "$TEMPLATE_SHELL"
;;
'profile')
echo "$TEMPLATE_PROFILE"
;;
'in-shell')
echo "$TEMPLATE_IN_SHELL"
;;
*)
gum format "$TEMPLATE$INFO"
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment