-
Notifications
You must be signed in to change notification settings - Fork 841
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
Blessed recipe how to use stack on github actions, in particular caching? #5754
Comments
I do not know that it is blessed, but v3 of However that example - and the CI currently used in this repository - seem to me to assume that operating system is Unix-like. The default On Unix-like operating systems, Stack stores GHC and other tools in a I will raise a separate issue for the lack of Windows caching in the current CI on this repository. |
Thanks for the pointer!
I think this example is insufficient, because it does not show how to correctly place these cache action into a bigger workflow. E.g. how do these interact with I am particularly interested in a blessed scheme that would avoid CI breakages coming from upstream ( |
Can you elaborate on the |
Ok, but then restoring a cached
This cannot be meant literally, otherwise there was no purpose in running |
As I understand it, there is no substantive purpose in running |
Ah, this is very interesting to know. Thanks for the pointer! |
Excellent question @andreasabel. I've done quite a few CI/CD pipelines for haskell, on CircleCI, BitBucket Pipelines, GitHub Actions... even Jenkins. I do have comments on this topic.
Agreed 💯 — these samples are "starters" at best. Let's just... let me just criticize the first sample lines we're shown at the link — I can see 4 issues here right away: - uses: actions/cache@v3
name: Cache ~/.stack
with:
path: ~/.stack
key: ${{ runner.os }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}
restore-keys: |
${{ runner.os }}-stack-global- Issue 1: pick apart ~/.stackNo, you don't want to cache the entire Caching Caching Caching Issue 2: hash the lockfile
Why would you want to invalidate any cache by insignificant changes in stack.yaml? Whitespace changes, comments, package list regrouping or reordering — does not invalidate any of pre-compiled artifacts. Stack will happily reuse those, if you allow it to, saving pipeline time & CPU credits. Specifically for the cache of compiled dependency packages (see below) — hash of the lockfile is invalidation trigger of the correct granularity. I want it to change exactly whenever the dependency forest changes. Non-insignificant modifications in project's Issue 3: don't hash the cabal-file
Say you're compiling package Now. Compiled modules/bins/tests of acme-app will all go under its local Issue 4: no manual overrideThere's a particularly gnarly type of issue in caching CI pipelines, once you start optimizing them. Cache bloat. Variations and imperfections in the setup — e.g. caching too much, not invalidating correctly, re-caching anew no-longer necessary parts of outdated cache — will sometimes cause issues very difficult to pinpoint. There won't be related "recent changes" in git. For all you know, the pipeline worked "perfectly" just last year — but gradually, developers have become increasingly stern in their complaints of slow CI. You go check — and voila, the store-restore steps dominate pipeline duration, dwarfing actual compile... because there're tens of gigs in the caches. Trust me, debugging these isn't impossible. But hell it is tedious. Will easily consume days of work. On that background — I tend to always include a manual override style of control in my pipelines, on the top-level of most cache-invalidation-key structures. Examples below. This is your "flush the cache now" button. Remember, CI runs in cloud... Some day, you or your successor will love having it. Bonus:
|
Thanks for this detailed description, @ulidtko ! I am trying to put this into practice now. Let me raise some doubts about the key for the dependencies (snapshots):
This key only accounts for change in the resolver and other changes in I think this key should have another component in the end that hashes the build plan. |
Hey @andreasabel, glad to get feedback. Yup, I see, good point! Also I definitely remember seeing this happening in practice too. Good to finally realize why 😅 Appending cabal-file hash to the key of deps-cache indeed is a way to "solve" this (feels conceptually wrong, but will work), and not without its own drawbacks... Same for the build-plan from I simply didn't find anything better than stack's lockfile as "the perfect" value to hook cache invalidation onto. Perhaps an SQL query against Minutia like this is the ultimate reason I always have that ¹ rebuilding just the handful missing deps is still much faster than full recompile of the typical ~hundreds of deps |
Is there a blessed documentation how to do caching with
stack
builds on GitHub Actions? If not, could we have one?In particular, I wonder how to correctly cache stack builds from one run of the CI to another. The resources I queried recommend to restore the whole stack root (
.stack/
). I wonder whether this would overwrite parts that shouldn't be overwritten, in particular ifstack
was updated upstream in between. Note that forcabal
, only the subdirectory.cabal/store
is cached, not all of.cabal/
.For example, is the following workflow correct?
stack update
stack build --dry-run
, generating lock file.stack/
if stack version and lock file have not changed in comparison with last run of CIstack build
Context:
The text was updated successfully, but these errors were encountered: