Skip to content

Conversation

@omus
Copy link
Contributor

@omus omus commented May 27, 2025

Follow up to #49. We need to be able to specify the location on the host system where the bind directory is located which will be used for injecting/extracting cache data. Without this change users can easily run into issues when using absolute paths for their id:

No cache map provided. Trying to parse the Dockerfile to find the cache mount instructions...
Cache map parsed from Dockerfile: {"/var/cache/apt":{"id":"/var/cache/apt","target":"/var/cache-target"},"/var/lib/apt":{"id":"/var/lib/apt","target":"/var/cache-target"}}
Error: EACCES: permission denied, open '/var/cache/apt/buildstamp'
    at async open (node:internal/fs/promises:639:25)
    at async Object.writeFile (node:internal/fs/promises:1216:14)
    at async $bd1d73aff0732146$var$injectCache (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7152:5)
    at async $bd1d73aff0732146$export$38c65e9f06d3d433 (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7199:72)
    at async $bec5d2ddaaf4a876$var$main (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7306:9)
    at async file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7310:5 {
  errno: -13,
  code: 'EACCES',
  syscall: 'open',
  path: '/var/cache/apt/buildstamp'
}
Error: EACCES: permission denied, open '/var/cache/apt/buildstamp'
    at async open (node:internal/fs/promises:639:25)
    at async Object.writeFile (node:internal/fs/promises:1216:14)
    at async $bd1d73aff0732146$var$injectCache (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7152:5)
    at async $bd1d73aff0732146$export$38c65e9f06d3d433 (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7199:72)
    at async $bec5d2ddaaf4a876$var$main (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7306:9)
    at async file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7310:5

src/opts.ts Outdated
const target = "/var/cache-target";

cacheMap[id] = {
cacheMap[`${bindRoot}/${id}`] = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A breaking change because of this. I could update this to allow bindRoot to be null to restore the original behavior. However, the only scenario where the original behavior could work reliably is when the ids were all explicitly set which would make their source be the working directory. We could avoid breakage by making the default cache-dir be . if we want a mostly breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

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

I really like the approach of maintaining backward compatibility by defaulting cache-dir to ..

This way, we avoid having to release a major version with breaking changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll update this to avoid having a breaking change

Copy link
Contributor Author

@omus omus May 30, 2025

Choose a reason for hiding this comment

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

Changes are now fully backwards compatible. I didn't go with the cache-dir="." approach as that was still breaking for cache mount ids with absolute paths

@omus omus marked this pull request as ready for review May 27, 2025 19:55
@omus
Copy link
Contributor Author

omus commented May 27, 2025

@bennesp did you want to give this a review?

Copy link
Contributor

@bennesp bennesp left a comment

Choose a reason for hiding this comment

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

I understand the problem you’re addressing and I really appreciate the solution you’ve proposed.

At the same time, I would prefer if the change could be implemented in a way that avoids breaking compatibility, so it doesn’t break any existing workflows.

That said, since I’m not the repository owner, this is just my opinion, feel free to challenge it 😄

with:
images: Build

- name: Cache
Copy link
Contributor

Choose a reason for hiding this comment

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

Switching this example from two caches to just one sounds a bit confusing.

In my use cases it’s important to use two different caches because they have different lifecycles: one gets reused more often, while the other is invalidated more frequently.

So it would be nice to keep an example showing how to handle both.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you elaborate more on what you'd like to see? The updated example uses one GHA cache for all of the cache mounts just like the original example did. The only difference here is that the cache-map is generated from the Dockerfile and that the locations of the cache mounts on disk are ./cache-mount//var/cache/apt and ./cache-mount//var/lib/apt instead of ./var-cache-apt and ./var-lib-apt.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh I think I got confused by the two paths we had previously, but indeed the cache is just one.

To be sure I got it right, please follow this scenario:

  1. A Dockerfile with two caches: --mount=type=cache,id=var-cache-apt and --mount=type=cache,id=var-lib-apt
  2. A workflow like the following:
      - name: Cache
       uses: actions/cache@v3
        id: cache
        with:
          path: |
            var-cache-apt
            var-lib-apt
          key: cache-${{ hashFiles('.github/workflows/test/Dockerfile') }}

      - name: Restore Docker cache mounts
        uses: reproducible-containers/[email protected]

Given the two points above, I would expect the "Restore" step to work correctly, by populating the builder cache with the content of ./var-cache-apt and ./var-lib-apt.

Is it correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given the Dockerfile you mentioned the example you provided is correct given that a cache-map is also provided. An example with that included would be:

      - name: Cache
        uses: actions/cache@v3
        id: cache
        with:
          path: |
            var-cache-apt
            var-lib-apt
          key: cache-${{ hashFiles('.github/workflows/test/Dockerfile') }}

      - name: Restore Docker cache mounts
        uses: reproducible-containers/[email protected]
        with:
          cache-map: |
            {
              "var-cache-apt": "/var/cache/apt",
              "var-lib-apt": "/var/lib/apt"
            }

Alternatively these equivalent examples would also work with that same Dockerfile with variations to the cache-map:

Extracting the cache-map from the Dockerfile
      - name: Cache
        uses: actions/cache@v3
        id: cache
        with:
          path: |
            /var/cache/apt
            /var/lib/apt
          key: cache-${{ hashFiles('.github/workflows/test/Dockerfile') }}

      - name: Restore Docker cache mounts
        uses: reproducible-containers/[email protected]
        with:
          dockerfile: Dockerfile

Note: This example would only be possible if additional permissions are granted in GHA. I tried this and ran into this problem:

No cache map provided. Trying to parse the Dockerfile to find the cache mount instructions...
Cache map parsed from Dockerfile: {"/var/cache/apt":{"id":"/var/cache/apt","target":"/var/cache-target"},"/var/lib/apt":{"id":"/var/lib/apt","target":"/var/cache-target"}}
Error: EACCES: permission denied, open '/var/cache/apt/buildstamp'
    at async open (node:internal/fs/promises:639:25)
    at async Object.writeFile (node:internal/fs/promises:1216:14)
    at async $bd1d73aff0732146$var$injectCache (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7152:5)
    at async $bd1d73aff0732146$export$38c65e9f06d3d433 (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7199:72)
    at async $bec5d2ddaaf4a876$var$main (file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7306:9)
    at async file:///home/runner/work/_actions/reproducible-containers/buildkit-cache-dance/653a570f730e3b9460adc576db523788ba59a0d7/dist/index.js:7310:5 {
  errno: -13,
  code: 'EACCES',
  syscall: 'open',
  path: '/var/cache/apt/buildstamp'
}
Extracting the cache-map from the Dockerfile and use cache-dir
      - name: Cache
        uses: actions/cache@v3
        id: cache
        with:
          path: |
            cache-mount/var/cache/apt
            cache-mount/var/lib/apt
          key: cache-${{ hashFiles('.github/workflows/test/Dockerfile') }}

      - name: Restore Docker cache mounts
        uses: reproducible-containers/[email protected]
        with:
          dockerfile: Dockerfile
          cache-dir: cache-mount

This would generate the following cache-map:

{
  "cache-mount//var/cache/apt": {
    "id":"/var/cache/apt",
    "target":"/var/cache-target"
  },
  "cache-mount//var/lib/apt":{
    "id":"/var/lib/apt",
    "target":"/var/cache-target"
  }
}

This example is equivalent to what I currently am proposing just a little more verbose for the path in actions/cache.

src/opts.ts Outdated
const target = "/var/cache-target";

cacheMap[id] = {
cacheMap[`${bindRoot}/${id}`] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

I really like the approach of maintaining backward compatibility by defaulting cache-dir to ..

This way, we avoid having to release a major version with breaking changes.

@omus omus requested a review from bennesp May 30, 2025 18:46
"var-lib-apt": "/var/lib/apt"
}
skip-extraction: ${{ steps.cache.outputs.cache-hit }}
```
Copy link
Member

Choose a reason for hiding this comment

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

How is this change relevant to cache-dir?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I updated the primary example of how to use this action above to demonstrate how to use dockerfile/cache-dir I moved the snippet from the original example here. Doing this was necessary as this sentence no longer made sense since there was no example using a "single string":

Optionally, instead of a single string for the target you can provide an object with additional options that should be passed to --mount=type=cache in the values cache-map JSON. The target path must be present in the object as a property.

@AkihiroSuda
Copy link
Member

AkihiroSuda commented May 31, 2025

Please squash commits that are related to the cache-dir.
(Changes irrelevant to cache-dir should be separate commits, or ideally separate PRs)

@omus
Copy link
Contributor Author

omus commented Jun 2, 2025

Please squash commits that are related to the cache-dir. (Changes irrelevant to cache-dir should be separate commits, or ideally separate PRs)

Done. I've kept the README change as a separate commit as it appears you may want that removed from this PR entirely

@omus omus requested a review from AkihiroSuda June 2, 2025 13:48
@omus
Copy link
Contributor Author

omus commented Jun 13, 2025

@AkihiroSuda can you give this another review?

@AkihiroSuda
Copy link
Member

Will review this week, sorry for the delay

Copy link
Member

@AkihiroSuda AkihiroSuda left a comment

Choose a reason for hiding this comment

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

Thanks, sorry for the delay

@AkihiroSuda AkihiroSuda merged commit 5b81f4d into reproducible-containers:main Jun 20, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants