lsupg Progress
I went ahead and changed the lsupg Nix component to calculate upgrade items as described in Nix Container Declarative Package Management.
Bug Fix
When I initially implemented the Nix component, I was unsure of how
to parse package names and versions from strings in
{{ name }}-{{ version }}
format. As a tentative
implementation, I split on the dash character and considered all parts
from the first one that starts with a digit as the version.
parseNameAndVersion :: BS.ByteString -> (BS.ByteString, BS.ByteString)
parseNameAndVersion= bimap (BS8.intercalate "-") (BS8.intercalate "-")
. break (maybe True (isDigit . fst) . BS8.uncons)
. BS8.split '-'
Since then, I found two places in the manuals where the correct behavior is specified!
In the Nix manual, the parseDrvName
function is described as follows:
Split the string
s
into a package name and version. The package name is everything up to but not including the first dash followed by a digit, and the version is everything following that dash. The result is returned in a set{ name, version }
. Thus,builtins.parseDrvName "nix-0.12pre12876"
returns{ name = "nix"; version = "0.12pre12876"; }
.
In the Nixpkgs manual, the coding conventions section on package naming specifies:
The version part of the
name
attribute must start with a digit (following a dash) — e.g.,"hello-0.3.1rc2"
.
I did not revisit my tentative implementation until today, and I see that it was close but not correct! When splitting on the dash character, the first part is always part of the name, even if it begins with a digit. My corrected implementation, with a different API to match the new code, is as follows:
parseLine :: BS.ByteString -> Either String (Text, Text)
=
parseLine line maybe (Left $ "error parsing nix line: " ++ TTC.toS line) Right $ do
-- the first part is a name part even if it begins with a digit
<- uncons $ BS8.split '-' line
(namePart, parts) let (nameParts, versionParts) =
break (maybe True (isDigit . fst) . BS8.uncons) parts
. not $ null versionParts
guard let joinParts = TTC.toT . BS8.intercalate "-"
return (joinParts (namePart : nameParts), joinParts versionParts)
I included a package name starting with a digit (0ad
) in
the unit tests.
Stack Docker Images
I use the Docker functionality of Stack to build the static executable in an Alpine Linux container. The command automatically pulls and uses a fpco/stack-build image that is tagged with the LTS. This fails when using LTS 18, and I confirmed on Docker Hub that no LTS 18 images have been pushed.
Investigating, it seems that images are now pushed to the commercialhaskell/stackage repository instead! Perhaps I missed a Stack release? Checking the stack repository, I confirmed that I am running the latest tagged release, made over a year ago. I imagine that the next release will fix the issue, but I need to work around it in the meantime.
Consulting the Docker
integration documentation, I learned how to configure my
stack.yaml
to point to the new repository. Setting it to
commercialhaskell/stackage
did not work because
the images are no longer tagged the same way! Instead of having separate
images for each point release, there is now a single image per LTS. The
following configuration works:
resolver: lts-18.1
packages:
- .
extra-deps:
- ttc-1.1.0.1
docker:
enable: false
repo: "commercialhaskell/stackage:lts18"
Note that I explicitly disable Docker functionality because I only
use Docker to build the static executable. The build command in my
Makefile
enables Docker, overriding the default in the
configuration:
stack build --flag lsupg:static --docker
Demonstration
I created a Docker image to demonstrate the change.
Dockerfile
:
FROM nixos/nix:latest
COPY packages.nix /etc/nix/packages.nix
COPY test.nix /tmp/test.nix
RUN nix-env -if "/tmp/test.nix"
packages.nix
:
with import <nixpkgs> {}; [
nix
python3
]
test.nix
:
let
pkgs = import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/20.09.tar.gz";
}) {};
in pkgs.python3
Running the nix
component using the released version
produces the following output:
[lsupg] $ lsupg --docker extremais/lsupg-test-nix:latest nix
nix python3 3.8.5 3.10.0a5
nix nix 2.3.11 2.3.12
Running the nix
component using a build with the change
produces the following output:
[lsupg] build$ ./lsupg --docker extremais/lsupg-test-nix:latest nix
nix nix 2.3.11 2.3.12
nix python3 3.8.5 3.8.9
The new build is able to correctly determine available upgrades based on the attribute paths.