-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add example for Haskell Stack #173
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
Conversation
|
Looks like there's a conflict eh. |
|
@fosskers, rebased off |
examples.md
Outdated
| name: Cache .stack-work | ||
| with: | ||
| path: .stack-work | ||
| key: ${{ runner.os }}-stack-work-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}-${{ '**/*.hs' }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.hs are source files. Is it a good or bad idea to create hash over all sources?
With the restore keys other hashes should still match. But will this create lot's of caches and lead to issues?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually yes, I'd disagree with that as well. In my own post on the matter I recommend just considering the hash of the stack.yaml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting :) What about package.yaml? All dependencies are listed there (which seems to be the equivalent to package.json or pom.xml). My stack.yaml(.lock) mainly contains the resolver. With restore-keys it'll fall back to use a cache that is defined by the stack.yaml. Would you advice against using package.yaml (with hpack)?
Btw. I wanted to ask for feedback in my own PR, but mixed those two up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fosskers, your post describes caching the ~/.stack folder, and I definitely agree having hashes in the key related to source files doesn't make any sense for that. The example I'm adding with this PR has a cache for both the global ~/.stack folder, and the project's .stack-work folder.
For the global cache (~/.stack), I agree with @andys8 that both stack.yaml and package.yaml should be included in the key, since changes to either will effect that cache.
For the project cache (.stack-work), I'm pretty sure we want to include all source files in the key since changes to those files are the main thing that change the contents of that cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps there's a discussion to be had though about whether the example should include a cache for .stack-work.
I definitely find it helpful to include in my workflows, especially for large projects. I'm very keen for my workflows to run as quickly as possible, and not caching .stack-work means the workflow will always have the recompile the project from scratch.
If we are going to cache .stack-work then having the key include a hash of all the source files seems like the right call, since the contents of the cache will likely change if any of those files change. If we only include hashes for the .yaml configuration files, then the cache will only update when those files change, which is much less often than changes to the source files. As a result, the cache will become stale pretty quickly, and workflow runtimes will increase, since more of the project will need to be recompiled on each workflow run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just looked through my open PR and realized this was still pending. Any thoughts on my comments?
|
@malob Looks good. I also have the same concern as @fosskers . Why do we need to include all source files in the key? If you could point to haskell docs which say that we need to compulsorily do that and we can't just use package.yaml and stack.yaml, that'll be great. I can review this further after getting that info. |
|
@vsvipul, thanks for bumping this :)
The Stack User Guide, is the best official documentation I could quickly find, but unfortunately it doesn't go into much depth about what the project specific working directory (which defaults the None of this is compulsory, i.e., nothing will break/fail if the cache of the So my overall take here is something like (mostly restating what I said in my previous comments):
It may very well be the case that some folks won't want to cache Maybe a concrete example would be helpful? Here's a small Haskell project of mine: https://github.com/malob/prefmanager. The first use of the Lines 106 to 112 in 44222d2
This cache will contain the GHC compiler required to build the project, as well as built versions of the external dependencies of the project (and some other stuff). The contents of this directory will only change if I make changes to the For example:
(I think we all agree about the above, but I'm just including it for completeness.) The second use of the Lines 113 to 119 in 44222d2
This is the working directory that Stack uses when building my project, and it contains build artifacts for the project. I just built the ❯ tree .stack-work/
.stack-work/
├── dist
│ └── aarch64-osx-nix
│ └── Cabal-3.2.1.0
│ ├── build
│ │ ├── Defaults
│ │ │ ├── Pretty.dyn_hi
│ │ │ ├── Pretty.dyn_o
│ │ │ ├── Pretty.hi
│ │ │ ├── Pretty.o
│ │ │ ├── Types.dyn_hi
│ │ │ ├── Types.dyn_o
│ │ │ ├── Types.hi
│ │ │ └── Types.o
│ │ ├── Defaults.dyn_hi
│ │ ├── Defaults.dyn_o
│ │ ├── Defaults.hi
│ │ ├── Defaults.o
│ │ ├── Paths_prefmanager.dyn_hi
│ │ ├── Paths_prefmanager.dyn_o
│ │ ├── Paths_prefmanager.hi
│ │ ├── Paths_prefmanager.o
│ │ ├── Prelude.dyn_hi
│ │ ├── Prelude.dyn_o
│ │ ├── Prelude.hi
│ │ ├── Prelude.o
│ │ ├── autogen
│ │ │ ├── Paths_prefmanager.hs
│ │ │ └── cabal_macros.h
│ │ ├── libHSprefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD-ghc8.10.7.dylib
│ │ ├── libHSprefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD.a
│ │ └── prefmanager
│ │ ├── autogen
│ │ │ ├── Paths_prefmanager.hs
│ │ │ └── cabal_macros.h
│ │ ├── prefmanager
│ │ └── prefmanager-tmp
│ │ ├── Main.hi
│ │ ├── Main.o
│ │ ├── Paths_prefmanager.hi
│ │ └── Paths_prefmanager.o
│ ├── build-lock
│ ├── package.conf.inplace
│ │ ├── package.cache
│ │ ├── package.cache.lock
│ │ └── prefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD.conf
│ ├── setup-config
│ ├── stack-build-caches
│ │ └── 22db420d881b88e3a9fab458cd4f7448ded2894604a37ea67e13cbd8a3d2f2b6
│ │ ├── exe-prefmanager
│ │ └── lib
│ ├── stack-cabal-mod
│ └── stack-setup-config-mod
├── install
│ └── aarch64-osx-nix
│ └── 22db420d881b88e3a9fab458cd4f7448ded2894604a37ea67e13cbd8a3d2f2b6
│ └── 8.10.7
│ ├── bin
│ │ └── prefmanager
│ ├── doc
│ │ └── prefmanager-0.1.0.0
│ │ └── LICENSE
│ ├── lib
│ │ └── aarch64-osx-ghc-8.10.7
│ │ ├── libHSprefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD-ghc8.10.7.dylib
│ │ └── prefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD
│ │ ├── Defaults
│ │ │ ├── Pretty.dyn_hi
│ │ │ ├── Pretty.hi
│ │ │ ├── Types.dyn_hi
│ │ │ └── Types.hi
│ │ ├── Defaults.dyn_hi
│ │ ├── Defaults.hi
│ │ ├── Paths_prefmanager.dyn_hi
│ │ ├── Paths_prefmanager.hi
│ │ ├── Prelude.dyn_hi
│ │ ├── Prelude.hi
│ │ └── libHSprefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD.a
│ └── pkgdb
│ ├── package.cache
│ ├── package.cache.lock
│ └── prefmanager-0.1.0.0-1iWRVnWMmZMJ686FXZvjSD.conf
├── stack.sqlite3
└── stack.sqlite3.pantry-write-lockIf I make changes to However, the contents of this directory will also change whenever I change a source file in my project. In this project, no other source files depend on For example, I just added a new function to So let's say I setup this project on GitHub with a workflow that included the two If I then pushed a new commit that only contained changes to the If I then pushed another commit that only made a change to However, if the In the above example, that really won't be a big deal since this is a very small project, and so rebuilding the whole project takes very little time, but for much larger projects this can have a much bigger impact. Imagine a project with dozens or hundreds of source files. If someone pushed a commit that included a change to a source file that required most or all of the project to be rebuilt, then a later commit made a change to (or added) say a test (that nothing else in the project depended on), if the |
vsvipul
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@malob Wow. Thank you for such a detailed explanation. Now I clearly understand it. The changes look good. Will go ahead and merge. Thanks for contributing.
🎉
|
@malob I didn't see the target branch and this ended up getting merged to master. We use "main" branch as the default branch now. Can you raise a new PR against that with same changes so I can merge? Thanks. |
No description provided.