Skip to content

Commit 20d725b

Browse files
authored
Prioritise less global repos and avoid local urls (#17)
1 parent 791834f commit 20d725b

19 files changed

+447
-91
lines changed

.github/workflows/cabaltest.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ jobs:
1111
with:
1212
nix_path: nixpkgs=channel:nixos-unstable
1313
- run: nix develop
14-
- run: cabal v2-update
15-
- run: cabal v2-test
14+
- run: nix develop --command cabal v2-update
15+
- run: nix develop --command cabal v2-test --enable-tests

shellify.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ library
5757
extra >=1.7.13 && <1.9,
5858
HStringTemplate >=0.8.8 && <0.9,
5959
mtl >=2.2.2 && <2.4,
60+
parsec >=3.1.17.0 && <3.2,
6061
shake >=0.19.7 && <0.20,
6162
unordered-containers >=0.2.19.1 && <0.3
6263

src/Constants.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Options
2626
the versions of dependencies are kept for reproducibility and so that
2727
shells are cached to load faster.
2828

29+
--allow-local-pinned-registries-to-be-prioritized
30+
Pinned local repoisitory URLs are usually taken last when looking for URLs for
31+
generated flake.nix files. This is usually desired. If you do however want
32+
to see these pinned entries in the flake file as specified in your registry,
33+
then set this flag.
34+
2935
--version
3036
Show the version number
3137
|]

src/Options.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ data Options = Options {
2929
packages :: Packages
3030
, command :: Maybe Text
3131
, generateFlake :: Bool
32+
, prioritiseLocalPinnedSystem :: Bool
3233
}
3334

3435
data OptionsParser = OptionsParser [Text] -- remainingOptions
@@ -59,6 +60,7 @@ options progName args =
5960
oldStyleOption opt = baseOption opt
6061
newStyleOption "-p" = returnError "-p not supported with new style commands"
6162
newStyleOption "--packages" = returnError "--packages not supported with new style commands"
63+
newStyleOption "--allow-local-pinned-registries-to-be-prioritized" = transformOptionsWith setPrioritiseLocalPinnedSystem
6264
newStyleOption arg | isSwitch arg = baseOption arg
6365
| otherwise = transformOptionsWith $ appendPackages [arg]
6466
baseOption :: Text -> [Text] -> OptionsParser
@@ -82,6 +84,7 @@ options progName args =
8284
appendPackages ps opts = opts{packages=ps ++ packages opts}
8385
setCommand cmd opts = opts{command=Just cmd}
8486
setFlakeGeneration opts = opts{generateFlake=True}
87+
setPrioritiseLocalPinnedSystem opts = opts {prioritiseLocalPinnedSystem=True}
8588
returnError errorText remaining = OptionsParser remaining $ Left errorText
8689

8790
consumePackageArgs :: [Text] -> (Packages, [Text])
@@ -99,7 +102,7 @@ hasShellArg (hd:tl) | isSwitch hd = hasShellArg tl
99102
isSwitch = isPrefixOf "-"
100103

101104
instance Default Options where
102-
def = Options [] Nothing False
105+
def = Options [] Nothing False False
103106

104107
instance Eq Options where
105108
a == b = isEqual command

src/TemplateGeneration.hs

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
module TemplateGeneration (generateShellDotNixText, generateFlakeText, getRegistryDB) where
22

3-
import Prelude hiding (lines)
4-
53
import Constants
64
import FlakeTemplate
75
import Options
86
import ShellifyTemplate
97

8+
import Data.Bifunctor (bimap)
109
import Data.Bool (bool)
11-
import Data.List (find, sort)
12-
import Data.List.Extra ((!?))
13-
import Data.Maybe (catMaybes, fromMaybe)
10+
import Data.List (find, sort, sortBy, sortOn)
11+
import Data.Maybe (fromMaybe)
1412
import Data.Set (fromList, toList)
15-
import Data.Text (isInfixOf, isPrefixOf, lines, pack, splitOn, Text())
13+
import Data.Text (Text(), isInfixOf, isPrefixOf, pack, splitOn, unpack)
1614
import Development.Shake.Command (cmd, Exit(Exit), Stderr(Stderr), Stdout(Stdout))
1715
import System.Exit (ExitCode (ExitSuccess))
16+
import Text.ParserCombinators.Parsec (Parser, char, endBy, eof, many1, noneOf, parse, string, (<|>))
1817
import Text.StringTemplate (newSTMP, render, setAttribute)
1918

2019
generateFlakeText :: Text -> Options -> Maybe Text
21-
generateFlakeText db Options{packages=packages, generateFlake=shouldGenerateFlake} =
20+
generateFlakeText db Options{packages=packages, generateFlake=shouldGenerateFlake, prioritiseLocalPinnedSystem=prioritiseLocalPinnedSystem} =
2221
bool
2322
Nothing
2423
(Just $ render
@@ -28,15 +27,15 @@ generateFlakeText db Options{packages=packages, generateFlake=shouldGenerateFlak
2827
$ setAttribute "shell_args" shellArgs
2928
$ newSTMP flakeTemplate)
3029
shouldGenerateFlake
31-
where repos = uniq $ getPackageRepo <$> sort packages
30+
where repos = getPackageRepoWrapper packages
3231
repoVars = getPackageRepoVarName <$> repos
3332
repoInputs = repoInput <$> repos
3433
repoInputLine repoName url = repoName <> ".url = \"" <> url <> "\";"
3534
repoInput repoName = repoInputLine repoName .
3635
either
37-
(error "Unexpected output from nix registry call: " <>)
36+
(error . ("Unexpected output from nix registry call: " <>))
3837
(fromMaybe "PLEASE ENTER input here")
39-
. findFlakeRepoUrl db $ repoName
38+
. findFlakeRepoUrl prioritiseLocalPinnedSystem db $ repoName
4039
pkgsVar = (<> "Pkgs")
4140
pkgsVars = pkgsVar <$> repos
4241
pkgsDecls = (\repo -> pkgsDecl (pkgsVar repo) repo) <$> repos
@@ -52,9 +51,12 @@ generateShellDotNixText Options{packages=packages, command=command} =
5251
command
5352
$ newSTMP shellifyTemplate
5453
where pkgs = generateBuildInput <$> sort packages
55-
parameters = uniq $ generateParameters <$> sort packages
54+
parameters = generateParametersWrapper packages
5655
generateBuildInput input = (toImportVar . getPackageRepo) input <> "." <> getPackageName input
5756

57+
getPackageRepoWrapper :: [Package] -> [Text]
58+
getPackageRepoWrapper = uniq . ("nixpkgs" :) . fmap getPackageRepo . sort
59+
5860
getPackageRepo input | "#" `isInfixOf` input
5961
= head $ splitOn "#" input
6062
| otherwise
@@ -73,6 +75,9 @@ toImportVar var | var == "nixpkgs"
7375
getPackageRepoVarName "nixpkgs" = "pkgs"
7476
getPackageRepoVarName a = a
7577

78+
generateParametersWrapper :: [Package] -> [Text]
79+
generateParametersWrapper = uniq . ("pkgs ? import <nixpkgs> {}" :) . fmap generateParameters . sort
80+
7681
generateParameters :: Package -> Text
7782
generateParameters package | "#" `isInfixOf` package
7883
&& not ("nixpkgs#" `isPrefixOf` package)
@@ -90,23 +95,40 @@ getRegistryDB =
9095
(Right $ pack out)
9196
(ex == ExitSuccess)
9297

93-
findFlakeRepoUrl :: Text -> Text -> Either Text (Maybe Text)
94-
findFlakeRepoUrl haystack needle =
95-
fmap repoUrl . find ((needle ==) . repoName) . catMaybes <$> mapM getFlakeRepo (lines haystack)
98+
findFlakeRepoUrl :: Bool -> Text -> Text -> Either String (Maybe Text)
99+
findFlakeRepoUrl prioritiseLocalPinnedSystem haystack needle =
100+
bimap ((<>) "Error processing nix registry list output: " . show)
101+
(fmap repoUrl . find ((needle ==) . repoName)
102+
. (if prioritiseLocalPinnedSystem then sortOn repoType else sortBy compareRepoEntries))
103+
$ parse parseRepos "" . unpack $ haystack
104+
105+
compareRepoEntries repoA repoB
106+
| repoHasLocalPinning repoA && not (repoHasLocalPinning repoB) = GT
107+
| repoHasLocalPinning repoB && not (repoHasLocalPinning repoA) = LT
108+
| otherwise = repoType repoA `compare` repoType repoB
109+
where repoHasLocalPinning = isPrefixOf "path:" . repoUrl
110+
111+
data RepoType = User | System | Global
112+
deriving (Eq, Ord)
96113

97114
data FlakeRepo = FlakeRepo {
98115
repoName :: Text
99116
, repoUrl :: Text
117+
, repoType :: RepoType
100118
}
101119

102-
getFlakeRepo :: Text -> Either Text (Maybe FlakeRepo)
103-
getFlakeRepo line = let expectedField = maybe (Left "unexepected nix registry command format")
104-
Right
105-
. (!?) (splitOn " " line)
106-
urlField = expectedField 2
107-
splitRepoField = splitOn ":" <$> expectedField 1
108-
potentialFlakeName ["flake", b] = Just b
109-
potentialFlakeName _ = Nothing
110-
f x y = (`FlakeRepo` y) <$> potentialFlakeName x
111-
in f <$> splitRepoField <*> urlField
120+
parseRepos :: Parser [FlakeRepo]
121+
parseRepos = do res <- endBy parseLine (char '\n')
122+
eof
123+
return res
124+
where parseLine = do repoType <- parseRepoType
125+
char ' '
126+
flakeName <- string "flake:" >> parseParam
127+
char ' '
128+
repoUrl <- parseParam
129+
return $ FlakeRepo (pack flakeName) (pack repoUrl) repoType
130+
parseParam = many1 (noneOf " \n")
131+
parseRepoType = (string "global" >> return Global)
132+
<|> (string "system" >> return System)
133+
<|> (string "user" >> return User)
112134

0 commit comments

Comments
 (0)