Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Lock files #4550

Closed
wants to merge 78 commits into from
Closed
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
3be0c5a
Add FromJSON instance for Repo
psibi Jan 8, 2019
889059c
Add corresponding test for fromJSON instance
psibi Jan 8, 2019
7abafb5
Add FromJSON for PackageMetadata
psibi Jan 8, 2019
d10af00
Add relevant test
psibi Jan 8, 2019
d1d9138
Commit typechecking code
psibi Jan 10, 2019
1ec1a64
Modify types and parsing logic
psibi Jan 11, 2019
d20749b
The parsing logic works now
psibi Jan 11, 2019
e0ee3ba
Move function to the Types module and simplify tests
psibi Jan 11, 2019
3bbc3d1
Add multiple repo parsing
psibi Jan 11, 2019
71668fc
Add Monad instance for Unrseolved type
psibi Jan 11, 2019
b8e56cf
Commit work
psibi Jan 17, 2019
bad1443
Commit work
psibi Jan 17, 2019
d099cc4
Merge remote-tracking branch 'origin/master' into lock-files
psibi Jan 30, 2019
a9a8268
Add test case for RPLIArchive
psibi Jan 30, 2019
da47898
Add parsing logic for archive
psibi Jan 30, 2019
fb3446c
Add test for multiple RPLIArchive
psibi Jan 30, 2019
c868f6a
Change the parsing logic for individual objects
psibi Jan 30, 2019
446b147
Fix compile errors
psibi Jan 31, 2019
c76b1f6
Commit work
psibi Feb 3, 2019
325c7d0
Merge remote-tracking branch 'origin/master' into lock-files
psibi Feb 11, 2019
e4cd0ab
Add toJSON instance for PL
psibi Feb 12, 2019
79659e9
Cleanup logic
psibi Feb 12, 2019
7c4d1b5
Fix logic of freeze
psibi Feb 14, 2019
8958031
Implement parseRPL function
psibi Feb 14, 2019
624ffaf
Implement resolveLockfile function
psibi Feb 15, 2019
cd9b7a9
Update test
psibi Feb 15, 2019
b2553cb
Cleanup Freeze module
psibi Feb 15, 2019
4ea05bf
Add loadLockfile
psibi Feb 15, 2019
c05fc59
Cleanup freeze module
psibi Feb 15, 2019
dad2b16
Fill remaining function
psibi Feb 15, 2019
d211d2a
Cleanup
psibi Feb 15, 2019
536929c
Add an optimization step
psibi Feb 15, 2019
c0467f5
Add initial lock file module
psibi Feb 15, 2019
1a6ada2
Add logDebug
psibi Feb 15, 2019
4060499
Cleanup
psibi Feb 15, 2019
70db768
Cleanup
psibi Feb 15, 2019
6d05164
Cleanup
psibi Feb 15, 2019
d6cba4d
Fix style
psibi Feb 17, 2019
47573a0
Add parseRSL function
psibi Feb 18, 2019
da27d94
Add parsing logic for resolver also
psibi Feb 18, 2019
6533f97
Add logic for reusing snapshot information from lock file
psibi Feb 18, 2019
6a5c179
Fix tests
psibi Feb 18, 2019
fd6f0fb
Add resolveSnapshotFile function
psibi Feb 18, 2019
ed4e800
Add more utility functions
psibi Feb 19, 2019
c99ecf5
Add logic for generating lock file for custom snapshot
psibi Feb 19, 2019
f688564
Add other helper functions
psibi Feb 19, 2019
016b16d
Add test
psibi Feb 19, 2019
86ff18c
Fix minor bug related to snapshot lock file
psibi Feb 19, 2019
25e3427
Add lock support for custom snapshot.
psibi Feb 19, 2019
8e11c8a
Add Friendly recommendation for stack.yaml from now on
psibi Feb 19, 2019
fac02b0
Use Immutable types for custom snapshot
psibi Feb 19, 2019
acf2e04
Add some logic to find out the exact changes
psibi Feb 19, 2019
84940c2
Add info messages.
psibi Feb 19, 2019
6d8cc54
Fix lock file reading bug
psibi Feb 27, 2019
378f3c3
Minor refactor
psibi Feb 28, 2019
d755d25
Restructure from pantry to stack.lock
psibi Feb 28, 2019
1afa890
Make it actually use of cahed ds.
psibi Feb 28, 2019
f168499
Address some of Kirill's comments
psibi Feb 28, 2019
d463b76
Some cleanup for changing the locations
psibi Feb 28, 2019
f6559db
Fix Pantry tests
psibi Feb 28, 2019
a7e6c25
Basic unit test works for lock file
psibi Feb 28, 2019
a419587
Move lock related parsing test to stack
psibi Mar 1, 2019
d284f10
Change to Map data type for better efficiency
psibi Mar 1, 2019
1805828
Cleanup
psibi Mar 1, 2019
ed5e41d
Do renaming
psibi Mar 1, 2019
27f3a88
Fix test for the Map data structure
psibi Mar 1, 2019
28921f5
Change the type for parsing from snapshot lock file
psibi Mar 1, 2019
89da1ef
Switch to map data structure for cached data
psibi Mar 1, 2019
10ad675
Add Display instance for RawPackageLocation
psibi Mar 1, 2019
02efbec
Cleanup, make fields strict
psibi Mar 1, 2019
6e0be06
Fix compile error
psibi Mar 1, 2019
6e05cd0
Fix test
psibi Mar 1, 2019
246ff8e
Remove rootDir parameter as it's not required
psibi Mar 1, 2019
5819514
Don't new string-quote anymore
psibi Mar 1, 2019
c662491
Fix pedantic warnings
psibi Mar 1, 2019
281ed27
Some hlint fixes
psibi Mar 1, 2019
6149c75
Remove BangPatterns
psibi Mar 1, 2019
72b1867
Fix windows build issue
psibi Mar 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ library:
- Stack.Image
- Stack.Init
- Stack.Ls
- Stack.Lock
- Stack.New
- Stack.Nix
- Stack.Options.BenchParser
Expand Down Expand Up @@ -301,6 +302,7 @@ tests:
- hspec
- stack
- smallcheck
- raw-strings-qq
stack-integration-test:
main: IntegrationSpec.hs
source-dirs:
Expand Down
4 changes: 0 additions & 4 deletions src/Stack/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ build msetLocalFiles mbuildLk = do
boptsCli <- view $ envConfigL.to envConfigBuildOptsCLI
baseConfigOpts <- mkBaseConfigOpts boptsCli
plan <- constructPlan baseConfigOpts localDumpPkgs loadPackage sourceMap installedMap (boptsCLIInitialBuildSteps boptsCli)

allowLocals <- view $ configL.to configAllowLocals
unless allowLocals $ case justLocals plan of
[] -> return ()
Expand All @@ -107,14 +106,11 @@ build msetLocalFiles mbuildLk = do
(Just lk,True) -> do logDebug "All installs are local; releasing snapshot lock early."
liftIO $ unlockFile lk
_ -> return ()

checkCabalVersion
warnAboutSplitObjs bopts
warnIfExecutablesWithSameNameCouldBeOverwritten locals plan

when (boptsPreFetch bopts) $
preFetch plan

if boptsCLIDryrun boptsCli
then printPlan plan
else executePlan boptsCli baseConfigOpts locals
Expand Down
6 changes: 3 additions & 3 deletions src/Stack/Build/ConstructPlan.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,7 @@ pprintExceptions exceptions stackYaml stackRoot parentMap wanted' prunedGlobalDe
, line
]

extras :: Map PackageName (Version, BlobKey)
extras = Map.unions $ map getExtras exceptions'
getExtras DependencyCycleDetected{} = Map.empty
getExtras UnknownPackage{} = Map.empty
Expand All @@ -1028,9 +1029,8 @@ pprintExceptions exceptions stackYaml stackRoot parentMap wanted' prunedGlobalDe
go (name, (_range, Just (version,cabalHash), DependencyMismatch{})) =
Map.singleton name (version, cabalHash)
go _ = Map.empty
pprintExtra (name, (version, BlobKey cabalHash cabalSize)) =
let cfInfo = CFIHash cabalHash (Just cabalSize)
packageIdRev = PackageIdentifierRevision name version cfInfo
pprintExtra (name, (version, BlobKey _ _)) =
let packageIdRev = PackageIdentifierRevision name version CFILatest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explain this change? Why do we use CFILatest when we have a particular blob key?

Copy link
Member Author

@psibi psibi Mar 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is for the suggestions part. Without this, if a package is not in the snapshot - The stack build tool will recommend it to be added like this:

  * Recommended action: try adding the following to your extra-deps in /home/sibi/github/tldr-hs2/stack.yaml:

string-quote-0.0.1@sha256:7d91a0ba1be44b2443497c92f2f027cd4580453b893f8b5ebf061e1d85befaf3

With the above change, it will recommend it like this:

  * Recommended action: try adding the following to your extra-deps in /home/sibi/github/tldr-hs2/stack.yaml:

string-quote-0.0.1

The reason I made that change was because of Michael's comment in the issue:

... the original stack.yaml can be even more lightweight than it is today, e.g. including the @rev:0 information there will no longer be necessary at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why not change the suggestion properly instead? This change above complicates the logic a bit an gives no hints about reasons

in fromString $ T.unpack $ utf8BuilderToText $ RIO.display packageIdRev

allNotInBuildPlan = Set.fromList $ concatMap toNotInBuildPlan exceptions'
Expand Down
58 changes: 52 additions & 6 deletions src/Stack/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ import System.PosixCompat.Files (fileOwner, getFileStatus)
import System.PosixCompat.User (getEffectiveUserID)
import RIO.PrettyPrint
import RIO.Process
import Stack.Lock
( LockFile(..)
, generatePackageLockFile
, generateSnapshotLayerLockFile
, isLockFileOutdated
, loadPackageLockFile
, loadSnapshotLayerLockFile
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need such a large multiline import list? Why not just everything from Stack.Lock? Also it doesn't look great when not aligned with other imports in the module


-- | If deprecated path exists, use it and print a warning.
-- Otherwise, return the new path.
Expand Down Expand Up @@ -513,6 +521,18 @@ loadConfig :: HasRunner env
loadConfig configArgs mresolver mstackYaml inner =
loadProjectConfig mstackYaml >>= \x -> loadConfigMaybeProject configArgs mresolver x inner

cachedCompletePackageLocation :: (HasPantryConfig env, HasLogFunc env, HasProcessContext env) => Map RawPackageLocation PackageLocation
-> RawPackageLocation
-> RIO env PackageLocation
cachedCompletePackageLocation cachePackages rp@(RPLImmutable rpli) = do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor naming suggestion: what about s/cachePackages/cachedLocations/?

let item = Map.lookup rp cachePackages
case item of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if case Map.lookup rp cachePackages of? That extra variable item doesn't add in terms of readability, just adds an extra line

Nothing -> do
pl <- completePackageLocation rpli
pure $ PLImmutable pl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe in 1 line as PLImmutable <$> completePackageLocation rpli?

Just pl -> pure pl
cachedCompletePackageLocation _ (RPLMutable rplm) = pure $ PLMutable rplm

-- | Load the build configuration, adds build-specific values to config loaded by @loadConfig@.
-- values.
loadBuildConfig :: LocalConfigStatus (Project, Path Abs File, ConfigMonoid)
Expand Down Expand Up @@ -587,8 +607,37 @@ loadBuildConfig mproject maresolver mcompiler = do
{ projectResolver = fromMaybe (projectResolver project') mresolver
}

resolver <- completeSnapshotLocation $ projectResolver project
(snapshot, _completed) <- loadAndCompleteSnapshot resolver
lockFileOutdated <- isLockFileOutdated stackYamlFP
if lockFileOutdated
then do
logDebug "Lock file is outdated or isn't present"
generatePackageLockFile stackYamlFP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in generatePackageLockFile you complete locations when it's needed, why can't we just reuse that information and not to go through FS to get it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and not to go through FS

We go through the FS to read the lock file to see if we can avoid doing completePackageLocation and rather reuse the information from the lock file. Is there a reason you don't want to go through FS ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no reasons not to reuse already completed locations without going through FS

else logDebug "Lock file is upto date"

lockFile <- liftIO $ addFileExtension "lock" stackYamlFP
(cachedPackageLock, origResolver, compResolver) <- liftIO $ do
lf <- loadPackageLockFile lockFile
return (lfPackageLocations lf, lfoResolver lf, lfcResolver lf)


resolver <- if projectResolver project == origResolver
then pure compResolver
else completeSnapshotLocation $ projectResolver project

case resolver of
SLFilePath path -> do
outdated <- isLockFileOutdated (resolvedAbsolute path)
when outdated (generateSnapshotLayerLockFile resolver stackYamlFP)
_ -> return ()

cachedSnapshotLock <- case resolver of
SLFilePath path -> do
let sf = resolvedAbsolute path
slf <- liftIO $ addFileExtension "lock" sf
liftIO $ loadSnapshotLayerLockFile slf (parent stackYamlFP)
_ -> pure Map.empty

(snapshot, _completed) <- loadAndCompleteSnapshot resolver cachedSnapshotLock

extraPackageDBs <- mapM resolveDir' (projectExtraPackageDBs project)

Expand All @@ -600,11 +649,8 @@ loadBuildConfig mproject maresolver mcompiler = do
pp <- mkProjectPackage YesPrintWarnings resolved (boptsHaddock bopts)
pure (cpName $ ppCommon pp, pp)

let completeLocation (RPLMutable m) = pure $ PLMutable m
completeLocation (RPLImmutable im) = PLImmutable <$> completePackageLocation im

deps0 <- forM (projectDependencies project) $ \rpl -> do
pl <- completeLocation rpl
pl <- cachedCompletePackageLocation cachedPackageLock rpl
dp <- additionalDepPackage (shouldHaddockDeps bopts) pl
pure (cpName $ dpCommon dp, dp)

Expand Down
113 changes: 60 additions & 53 deletions src/Stack/Freeze.hs
Original file line number Diff line number Diff line change
@@ -1,77 +1,84 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Stack.Freeze
( freeze
, FreezeOpts (..)
, FreezeMode (..)
, FreezeOpts(..)
, FreezeMode(..)
) where

import Data.Aeson ((.=), object)
import Data.Aeson ((.=), object)
import qualified Data.Yaml as Yaml
import RIO.Process
import qualified RIO.ByteString as B
import Stack.Prelude
import Stack.Types.Config
import RIO.Process
import Stack.Prelude
import Stack.Types.Config

data FreezeMode = FreezeProject | FreezeSnapshot
data FreezeMode
= FreezeProject
| FreezeSnapshot

newtype FreezeOpts = FreezeOpts
{ freezeMode :: FreezeMode
}

freeze :: HasEnvConfig env => FreezeOpts -> RIO env ()
freeze (FreezeOpts mode) = do
mproject <- view $ configL.to configMaybeProject
case mproject of
Just (p, _) -> doFreeze p mode
Nothing -> logWarn "No project was found: nothing to freeze"
mproject <- view $ configL . to configMaybeProject
case mproject of
Just (p, _) -> doFreeze p mode
Nothing -> logWarn "No project was found: nothing to freeze"

completeFullPackageLocation ::
(HasPantryConfig env, HasLogFunc env, HasProcessContext env)
=> RawPackageLocation
-> RIO env PackageLocation
completeFullPackageLocation (RPLImmutable rpli) = do
pl <- completePackageLocation rpli
pure $ PLImmutable pl
completeFullPackageLocation (RPLMutable rplm) = pure $ PLMutable rplm

doFreeze ::
(HasProcessContext env, HasLogFunc env, HasPantryConfig env)
( HasProcessContext env
, HasLogFunc env
, HasPantryConfig env
, HasEnvConfig env
)
=> Project
-> FreezeMode
-> RIO env ()
doFreeze p FreezeProject = do
let deps = projectDependencies p
resolver = projectResolver p
completePackageLocation' pl =
case pl of
RPLImmutable pli -> PLImmutable <$> completePackageLocation pli
RPLMutable m -> pure $ PLMutable m
resolver' <- completeSnapshotLocation resolver
deps' <- mapM completePackageLocation' deps
let rawCompleted = map toRawPL deps'
rawResolver = toRawSL resolver'
if rawCompleted == deps && rawResolver == resolver
then
logInfo "No freezing is required for this project"
else do
logInfo "# Fields not mentioned below do not need to be updated"

if rawResolver == resolver
then logInfo "# No update to resolver is needed"
else do
logInfo "# Frozen version of resolver"
B.putStr $ Yaml.encode $ object ["resolver" .= rawResolver]

if rawCompleted == deps
then logInfo "# No update to extra-deps is needed"
else do
logInfo "# Frozen version of extra-deps"
B.putStr $ Yaml.encode $ object ["extra-deps" .= rawCompleted]

let deps :: [RawPackageLocation] = projectDependencies p
resolver :: RawSnapshotLocation = projectResolver p
resolver' :: SnapshotLocation <- completeSnapshotLocation resolver
deps' :: [PackageLocation] <- mapM completeFullPackageLocation deps
let rawCompleted = map toRawPL deps'
rawResolver = toRawSL resolver'
if rawCompleted == deps && rawResolver == resolver
then logInfo "No freezing is required for this project"
else do
logInfo "# Fields not mentioned below do not need to be updated"
if rawResolver == resolver
then logInfo "# No update to resolver is needed"
else do
logInfo "# Frozen version of resolver"
B.putStr $ Yaml.encode $ object ["resolver" .= rawResolver]
if rawCompleted == deps
then logInfo "# No update to extra-deps is needed"
else do
logInfo "# Frozen version of extra-deps"
B.putStr $
Yaml.encode $ object ["extra-deps" .= rawCompleted]
doFreeze p FreezeSnapshot = do
resolver <- completeSnapshotLocation $ projectResolver p
result <- loadSnapshotLayer resolver
case result of
Left _wc ->
logInfo "No freezing is required for compiler resolver"
Right (snap, _) -> do
snap' <- completeSnapshotLayer snap
let rawCompleted = toRawSnapshotLayer snap'
if rawCompleted == snap
then
logInfo "No freezing is required for the snapshot of this project"
else
liftIO $ B.putStr $ Yaml.encode snap'
resolver <- completeSnapshotLocation $ projectResolver p
result <- loadSnapshotLayer resolver
case result of
Left _wc -> logInfo "No freezing is required for compiler resolver"
Right (snap, _) -> do
snap' <- completeSnapshotLayer snap
let rawCompleted = toRawSnapshotLayer snap'
if rawCompleted == snap
then logInfo
"No freezing is required for the snapshot of this project"
else liftIO $ B.putStr $ Yaml.encode snap'
Loading