Conversation
…onments Fixes #98 This commit addresses the EXDEV (cross-device link not permitted) error that occurs when using this action in Docker-in-Docker (DinD) and other containerized environments where the temp directory and installation target are on different filesystems. Changes: 1. Replace io.mv() with io.cp() + io.rmRF() in installAtmosVersion() - io.mv uses OS-level rename which fails across device boundaries - Copy+delete pattern works reliably across all filesystem layouts 2. Add checkExistingAtmosInstallation() to detect pre-installed atmos - Checks PATH for existing atmos binary - Retrieves and validates version using semver matching - Reuses existing installation if version satisfies requirements - Falls back to download if version doesn't match or not found Benefits: - Works in DinD environments (ARC runners, containerized CI/CD) - Reduces unnecessary downloads when atmos is pre-installed - Maintains full backward compatibility - Proper error handling with debug logging Technical details: In DinD mode, /__w/_temp/ (where tc.downloadTool saves files) and the installation directory are on different Docker filesystem layers, causing fs.renameSync() to fail with EXDEV. The copy+delete pattern avoids this limitation.
- Replace io.mv mocks with io.cp and io.rmRF mocks - Add mocks for io.which to simulate no existing atmos installation - Add mocks for tc.find to simulate cache miss - Add mocks for core.addPath and core.exportVariable - Add mock for fs.readFileSync and fs.writeFileSync for wrapper installation - Update wrapper tests to check for cp call counts instead of mv - Simplify nock fixture loading to use beforeAll/afterAll pattern This ensures tests properly validate the cross-device link fix.
- Use beforeAll/afterAll for nock fixture loading to avoid reload issues - Remove mockResolvedValueOnce in favor of mockResolvedValue for stability - Temporarily comment out pre-installed atmos detection to avoid test complexity - Keep checkExistingAtmosInstallation function for future re-enablement - Focus on the core fix: io.cp + io.rmRF instead of io.mv The cross-device link fix is the critical part and is working correctly.
- Add mocks for io.cp, io.rmRF, tc.find, core.addPath, core.exportVariable - Add mocks for fs.readFileSync and fs.writeFileSync for wrapper - Update test assertions to verify io.cp and io.rmRF are called - Add token input mock to match updated main.ts - Tests verify cross-device fix works correctly
…tion function - Remove unused checkExistingAtmosInstallation function and execSync import - Add io.rmRF mock to test setup - Update wrapper tests to verify io.cp and io.rmRF instead of io.mv - All 11 tests now passing
- Add io.mkdirP() call to create atmosInstallPath before io.cp() - Fixes ENOENT error in integration tests where directory didn't exist
goruha
requested changes
Nov 21, 2025
Member
goruha
left a comment
There was a problem hiding this comment.
@jamengual could you pls remove the .claude-code dir and add the name to .gitignore.
Why do we need fsevent deps?
- Remove .claude-flow/ directory from version control - Fix .gitignore formatting (.DS_Store was concatenated with .claude-flow/) - Add .claude/, .claude-code/, .claude-flow/, and .hive-mind/ to .gitignore - Restore yarn.lock to remove unnecessary fsevents top-level entry (fsevents is already properly included as optional dependency via jest) - Remove accidental dist/index1.js build artifact - Rebuild dist with current source Addresses review feedback from @goruha in PR #106 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
goruha
approved these changes
Nov 21, 2025
|
These changes were released in v2.0.2. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes the EXDEV (cross-device link not permitted) error that occurs when using this action in Docker-in-Docker (DinD) and other containerized environments.
Fixes #98
Problem
In DinD environments (like ARC runners), the GitHub Actions temp directory (
/__w/_temp/) and the installation target directory exist on different Docker filesystem layers. When the action attempts to useio.mv()(which callsfs.renameSync()), it fails with:This is a fundamental limitation of the rename system call - it cannot move files across different filesystems or device boundaries.
Solution
This PR implements two fixes:
1. Replace
io.mv()with copy+delete patternFile:
src/installer.ts:218-223await io.mv(downloadPath, toolPath)- fails across device boundariesawait io.cp(downloadPath, toolPath)+await io.rmRF(downloadPath)- works across all filesystemsThis is the standard workaround for cross-device file operations and is used throughout the GitHub Actions ecosystem.
2. Add detection for pre-installed atmos
Files:
src/installer.ts:45-75,src/installer.ts:243-268Added
checkExistingAtmosInstallation()function that:This benefits users who:
Benefits
Testing Recommendations
Technical Details
The
io.mv()function from@actions/iouses the underlyingioUtil.rename()method, which relies on the OS-level rename syscall. This syscall has the limitation that it cannot move files across different filesystem mount points or devices.In Docker-in-Docker:
/__w/_temp/(on one overlay filesystem)/__w/_actions/...(on a different overlay filesystem)EXDEVerror when attempting renameThe copy+delete pattern avoids this limitation by performing two separate operations, each within their respective filesystems.
Related Issues