Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
5db1cf9
Enhance reading from .python-version (#787)
krystof-k May 21, 2025
5fa0ee6
Bump @actions/tool-cache from 2.0.1 to 2.0.2 (#1095)
dependabot[bot] Jun 18, 2025
e9c40fb
Add support for `pip-version` (#1129)
v-priyagupta108 Jun 20, 2025
1264885
Enhance cache-dependency-path handling to support files outside the w…
v-aparnajyothi-y Jun 25, 2025
532b046
Add Architecture-Specific PATH Management for Python with --user Flag…
v-aparnajyothi-y Jul 3, 2025
88ffd4d
Include python version in PyPy python-version output (#1110)
cdce8p Jul 21, 2025
3c6f142
update documentation (#1156)
v-priya-kinthali Jul 23, 2025
36da51d
Add version parsing from Pipfile (#1067)
aradkdj Jul 24, 2025
03bb615
Bump idna from 2.9 to 3.7 in /__tests__/data (#843)
dependabot[bot] Jul 29, 2025
fbeb884
Bump form-data to fix critical vulnerabilities #182 & #183 (#1163)
v-aparnajyothi-y Jul 30, 2025
9322b3c
Upgrade setuptools to 78.1.1 to fix path traversal vulnerability in P…
v-aparnajyothi-y Aug 4, 2025
f62a0e2
Change missing cache directory error to warning (#1182)
v-aparnajyothi-y Aug 26, 2025
5b668cf
Bump actions/checkout from 4 to 5 (#1181)
dependabot[bot] Aug 26, 2025
65b0712
Clarify pythonLocation behavior for PyPy and GraalPy in environment v…
v-aparnajyothi-y Aug 26, 2025
3d1e2d2
Revert "Enhance cache-dependency-path handling to support files outsi…
v-aparnajyothi-y Aug 28, 2025
e797f83
Upgrade to node 24 (#1164)
salmanmkc Sep 4, 2025
4267e28
Bump urllib3 from 1.26.19 to 2.5.0 in /__tests__/data and document br…
dependabot[bot] Sep 18, 2025
2e3e4b1
Add support for pip-install input (#1201)
v-gowridurgad Sep 26, 2025
18566f8
Improve wording and "fix example" (remove 3.13) on testing against pr…
yarikoptic Oct 14, 2025
bba65e5
Bump typescript from 5.4.2 to 5.9.3 and update docs/advanced-usage.md…
dependabot[bot] Oct 20, 2025
cfd55ca
graalpy: add graalpy early-access and windows builds (#880)
timfel Oct 22, 2025
443da59
Bump actions/publish-action from 0.3.0 to 0.4.0 & Documentation updat…
dependabot[bot] Nov 12, 2025
97aeb3e
Bump requests from 2.32.2 to 2.32.4 in /__tests__/data (#1130)
dependabot[bot] Nov 13, 2025
bfc4944
Bump prettier from 3.5.3 to 3.6.2 (#1234)
dependabot[bot] Nov 18, 2025
83679a8
Bump @types/node from 24.1.0 to 24.9.1 and update macos-13 to macos-1…
dependabot[bot] Nov 24, 2025
4f41a90
Bump urllib3 from 2.5.0 to 2.6.0 in /__tests__/data (#1253)
dependabot[bot] Dec 30, 2025
bfe8cc5
Upgrade @actions dependencies to Node 24 compatible versions (#1259)
salmanmkc Jan 20, 2026
a309ff8
Bump urllib3 from 2.6.0 to 2.6.3 in /__tests__/data (#1264)
dependabot[bot] Jan 22, 2026
28f2168
Bump minimatch from 3.1.2 to 3.1.5 (#1281)
dependabot[bot] Mar 9, 2026
c8813ba
Upgrade @actions dependencies and update licenses (#1303)
Copilot Apr 15, 2026
fa8bde1
Bump @actions/cache to 5.1.0, log cache write denied
jasongin Jun 18, 2026
6f4b74b
Strict equality
jasongin Jun 18, 2026
dc6eab6
Update dist
jasongin Jun 18, 2026
0cb1a84
Add RHEL support and include Linux distro in cache keys (#1323)
v-priyagupta108 Jun 18, 2026
6731c2b
Resolve high-severity audit issues
jasongin Jun 19, 2026
8f639b1
Merge pull request #1324 from jasongin/update-actions-cache-5.1.0
Link- Jun 23, 2026
d2b357a
Update dependency versions and test workflow configuration (#1322)
v-HarithaVattikuti Jun 23, 2026
1d18d7a
Update advanced-usage.md (#811)
Dunky-Z Jun 23, 2026
ece7cb0
Fix pip cache error handling on Windows. (#1040)
v-priyagupta108 Jun 23, 2026
0903b46
Bump certifi from 2020.6.20 to 2024.7.4 in /__tests__/data (#1328)
dependabot[bot] Jul 10, 2026
6849080
remove EOL Python versions and Bumps numpy text fixture (#1333)
v-priya-kinthali Jul 13, 2026
c709277
Annotation code fix (#1335)
v-lmvysakh Jul 13, 2026
54baeea
Validate and retry manifest fetch to prevent silent failures (#1332)
v-priyagupta108 Jul 14, 2026
f8cf429
Migrate to ESM and upgrade dependencies (#1330)
v-priyagupta108 Jul 15, 2026
0f3a009
Remove the pip-install input (#1336)
v-gowridurgad Jul 16, 2026
4ab7e95
Merge pull request #1337 from actions/philip-gai/bump-actions-cache-6…
philip-gai Jul 16, 2026
5fda3b9
Pin SHA commits and update docs with latest versions (#1338)
v-HarithaVattikuti Jul 20, 2026
8549b9f
fix: resolve npm audit high severity vulnerabilities (#1347)
v-HarithaVattikuti Aug 3, 2026
9191ea1
fix: resolve npm audit high severity vulnerabilities (#1350)
v-HarithaVattikuti Aug 18, 2026
66fcabf
Bump actions/checkout from 7.0.0 to 7.0.1 (#1345)
dependabot[bot] Aug 19, 2026
751276e
Updated error message (#1351)
v-lmvysakh Aug 20, 2026
337b072
feat: Add `mirror` and `mirror-token` inputs for custom Python distri…
luhenry Sep 8, 2026
ad3497a
Bump browserslist from 4.28.2 to 4.28.9 (#1353)
dependabot[bot] Sep 9, 2026
7aeabeb
Bump @typescript-eslint/parser from 8.62.0 to 8.70.0 (#1343)
dependabot[bot] Sep 21, 2026
af9bcc7
Bump eslint-plugin-jest from 29.15.2 to 29.16.6 (#1342)
dependabot[bot] Sep 21, 2026
06da627
Bump @vercel/ncc from 0.44.0 to 0.45.0 (#1357)
dependabot[bot] Sep 24, 2026
e1be392
Bump @types/semver from 7.7.1 to 7.8.0 (#1359)
dependabot[bot] Oct 12, 2026
6609657
Bump undici from 6.28.0 to 6.29.0 (#1364)
dependabot[bot] Oct 12, 2026
3892123
Bump ts-jest from 29.4.11 to 29.4.14 (#1361)
dependabot[bot] Oct 12, 2026
0e40890
Bump brace-expansion from 5.0.9 to 5.0.12 (#1365)
dependabot[bot] Oct 14, 2026
ff73b24
Bump prettier from 3.8.4 to 3.9.9 and package.json from 7.0.0 to 7.1.…
dependabot[bot] Oct 14, 2026
ec52d0a
Bump @typescript-eslint/eslint-plugin from 8.62.0 to 8.71.1 (#1369)
dependabot[bot] Oct 16, 2026
6d4fd22
Cache upgrade to 6.3.0 (#1370)
v-HarithaVattikuti Oct 22, 2026
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
Prev Previous commit
Next Next commit
Enhance cache-dependency-path handling to support files outside the w…
…orkspace root (actions#1128)

* ehnace cache dependency path handling

* logic update

* npm run format-check

* update cacheDependencies tests to cover resolved paths and copy edge cases

* check failure fix

* depricate-windows-2019

* refactored the code

* Check failure fix
  • Loading branch information
v-aparnajyothi-y authored Jun 25, 2025
commit 12648859835f68b273febdd9aab9972bbb624d8c
1 change: 0 additions & 1 deletion .github/workflows/test-pypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ jobs:
- macos-13
- macos-14
- macos-15
- windows-2019
- windows-2022
- windows-2025
- ubuntu-22.04
Expand Down
149 changes: 149 additions & 0 deletions __tests__/setup-python.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import * as core from '@actions/core';
import * as fs from 'fs';
import * as path from 'path';
import {cacheDependencies} from '../src/setup-python';
import {getCacheDistributor} from '../src/cache-distributions/cache-factory';

jest.mock('fs', () => {
const actualFs = jest.requireActual('fs');
return {
...actualFs,
promises: {
access: jest.fn(),
mkdir: jest.fn(),
copyFile: jest.fn(),
writeFile: jest.fn(),
appendFile: jest.fn()
}
};
});
jest.mock('@actions/core');
jest.mock('../src/cache-distributions/cache-factory');

const mockedFsPromises = fs.promises as jest.Mocked<typeof fs.promises>;
const mockedCore = core as jest.Mocked<typeof core>;
const mockedGetCacheDistributor = getCacheDistributor as jest.Mock;

describe('cacheDependencies', () => {
const mockRestoreCache = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
process.env.GITHUB_ACTION_PATH = '/github/action';
process.env.GITHUB_WORKSPACE = '/github/workspace';

mockedCore.getInput.mockReturnValue('nested/deps.lock');

// Simulate file exists by resolving access without error
mockedFsPromises.access.mockImplementation(async p => {
const pathStr = typeof p === 'string' ? p : p.toString();
if (pathStr === '/github/action/nested/deps.lock') {
return Promise.resolve();
}
// Simulate directory doesn't exist to test mkdir
if (pathStr === path.dirname('/github/workspace/nested/deps.lock')) {
return Promise.reject(new Error('no dir'));
}
return Promise.resolve();
});

// Simulate mkdir success
mockedFsPromises.mkdir.mockResolvedValue(undefined);

// Simulate copyFile success
mockedFsPromises.copyFile.mockResolvedValue(undefined);

mockedGetCacheDistributor.mockReturnValue({restoreCache: mockRestoreCache});
});

it('copies the dependency file and resolves the path with directory structure', async () => {
await cacheDependencies('pip', '3.12');

const sourcePath = path.resolve('/github/action', 'nested/deps.lock');
const targetPath = path.resolve('/github/workspace', 'nested/deps.lock');

expect(mockedFsPromises.access).toHaveBeenCalledWith(
sourcePath,
fs.constants.F_OK
);
expect(mockedFsPromises.mkdir).toHaveBeenCalledWith(
path.dirname(targetPath),
{
recursive: true
}
);
expect(mockedFsPromises.copyFile).toHaveBeenCalledWith(
sourcePath,
targetPath
);
expect(mockedCore.info).toHaveBeenCalledWith(
`Copied ${sourcePath} to ${targetPath}`
);
expect(mockedCore.info).toHaveBeenCalledWith(
`Resolved cache-dependency-path: nested/deps.lock`
);
expect(mockRestoreCache).toHaveBeenCalled();
});

it('warns if the dependency file does not exist', async () => {
// Simulate file does not exist by rejecting access
mockedFsPromises.access.mockRejectedValue(new Error('file not found'));

await cacheDependencies('pip', '3.12');

expect(mockedCore.warning).toHaveBeenCalledWith(
expect.stringContaining('does not exist')
);
expect(mockedFsPromises.copyFile).not.toHaveBeenCalled();
expect(mockRestoreCache).toHaveBeenCalled();
});

it('warns if file copy fails', async () => {
// Simulate copyFile failure
mockedFsPromises.copyFile.mockRejectedValue(new Error('copy failed'));

await cacheDependencies('pip', '3.12');

expect(mockedCore.warning).toHaveBeenCalledWith(
expect.stringContaining('Failed to copy file')
);
expect(mockRestoreCache).toHaveBeenCalled();
});

it('skips path logic if no input is provided', async () => {
mockedCore.getInput.mockReturnValue('');

await cacheDependencies('pip', '3.12');

expect(mockedFsPromises.copyFile).not.toHaveBeenCalled();
expect(mockedCore.warning).not.toHaveBeenCalled();
expect(mockRestoreCache).toHaveBeenCalled();
});

it('does not copy if dependency file is already inside the workspace but still sets resolved path', async () => {
// Simulate cacheDependencyPath inside workspace
mockedCore.getInput.mockReturnValue('deps.lock');

// Override sourcePath and targetPath to be equal
const actionPath = '/github/workspace'; // same path for action and workspace
process.env.GITHUB_ACTION_PATH = actionPath;
process.env.GITHUB_WORKSPACE = actionPath;

// access resolves to simulate file exists
mockedFsPromises.access.mockResolvedValue();

await cacheDependencies('pip', '3.12');

const sourcePath = path.resolve(actionPath, 'deps.lock');
const targetPath = sourcePath; // same path

expect(mockedFsPromises.copyFile).not.toHaveBeenCalled();
expect(mockedCore.info).toHaveBeenCalledWith(
`Dependency file is already inside the workspace: ${sourcePath}`
);
expect(mockedCore.info).toHaveBeenCalledWith(
`Resolved cache-dependency-path: deps.lock`
);
expect(mockRestoreCache).toHaveBeenCalled();
});
});
43 changes: 42 additions & 1 deletion dist/setup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96844,6 +96844,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.cacheDependencies = void 0;
const core = __importStar(__nccwpck_require__(7484));
const finder = __importStar(__nccwpck_require__(6843));
const finderPyPy = __importStar(__nccwpck_require__(2625));
Expand All @@ -96862,10 +96863,50 @@ function isGraalPyVersion(versionSpec) {
function cacheDependencies(cache, pythonVersion) {
return __awaiter(this, void 0, void 0, function* () {
const cacheDependencyPath = core.getInput('cache-dependency-path') || undefined;
const cacheDistributor = (0, cache_factory_1.getCacheDistributor)(cache, pythonVersion, cacheDependencyPath);
let resolvedDependencyPath = undefined;
if (cacheDependencyPath) {
const actionPath = process.env.GITHUB_ACTION_PATH || '';
const workspace = process.env.GITHUB_WORKSPACE || process.cwd();
const sourcePath = path.resolve(actionPath, cacheDependencyPath);
const relativePath = path.relative(actionPath, sourcePath);
const targetPath = path.resolve(workspace, relativePath);
try {
const sourceExists = yield fs_1.default.promises
.access(sourcePath, fs_1.default.constants.F_OK)
.then(() => true)
.catch(() => false);
if (!sourceExists) {
core.warning(`The resolved cache-dependency-path does not exist: ${sourcePath}`);
}
else {
if (sourcePath !== targetPath) {
const targetDir = path.dirname(targetPath);
// Create target directory if it doesn't exist
yield fs_1.default.promises.mkdir(targetDir, { recursive: true });
// Copy file asynchronously
yield fs_1.default.promises.copyFile(sourcePath, targetPath);
core.info(`Copied ${sourcePath} to ${targetPath}`);
}
else {
core.info(`Dependency file is already inside the workspace: ${sourcePath}`);
}
resolvedDependencyPath = path
.relative(workspace, targetPath)
.replace(/\\/g, '/');
core.info(`Resolved cache-dependency-path: ${resolvedDependencyPath}`);
}
}
catch (error) {
core.warning(`Failed to copy file from ${sourcePath} to ${targetPath}: ${error}`);
}
}
// Pass resolvedDependencyPath if available, else fallback to original input
const dependencyPathForCache = resolvedDependencyPath !== null && resolvedDependencyPath !== void 0 ? resolvedDependencyPath : cacheDependencyPath;
const cacheDistributor = (0, cache_factory_1.getCacheDistributor)(cache, pythonVersion, dependencyPathForCache);
yield cacheDistributor.restoreCache();
});
}
exports.cacheDependencies = cacheDependencies;
function resolveVersionInputFromDefaultFile() {
const couples = [
['.python-version', utils_1.getVersionsInputFromPlainFile]
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ steps:
- run: pip install -e .
# Or pip install -e '.[test]' to install test dependencies
```

Note: cache-dependency-path supports files located outside the workspace root by copying them into the workspace to enable proper caching.
# Outputs and environment variables

## Outputs
Expand Down
53 changes: 51 additions & 2 deletions src/setup-python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,62 @@ function isGraalPyVersion(versionSpec: string) {
return versionSpec.startsWith('graalpy');
}

async function cacheDependencies(cache: string, pythonVersion: string) {
export async function cacheDependencies(cache: string, pythonVersion: string) {
const cacheDependencyPath =
core.getInput('cache-dependency-path') || undefined;
let resolvedDependencyPath: string | undefined = undefined;

if (cacheDependencyPath) {
const actionPath = process.env.GITHUB_ACTION_PATH || '';
const workspace = process.env.GITHUB_WORKSPACE || process.cwd();

const sourcePath = path.resolve(actionPath, cacheDependencyPath);
const relativePath = path.relative(actionPath, sourcePath);
const targetPath = path.resolve(workspace, relativePath);

try {
const sourceExists = await fs.promises
.access(sourcePath, fs.constants.F_OK)
.then(() => true)
.catch(() => false);

if (!sourceExists) {
core.warning(
`The resolved cache-dependency-path does not exist: ${sourcePath}`
);
} else {
if (sourcePath !== targetPath) {
const targetDir = path.dirname(targetPath);
// Create target directory if it doesn't exist
await fs.promises.mkdir(targetDir, {recursive: true});
// Copy file asynchronously
await fs.promises.copyFile(sourcePath, targetPath);
core.info(`Copied ${sourcePath} to ${targetPath}`);
} else {
core.info(
`Dependency file is already inside the workspace: ${sourcePath}`
);
}

resolvedDependencyPath = path
.relative(workspace, targetPath)
.replace(/\\/g, '/');
core.info(`Resolved cache-dependency-path: ${resolvedDependencyPath}`);
}
} catch (error) {
core.warning(
`Failed to copy file from ${sourcePath} to ${targetPath}: ${error}`
);
}
}

// Pass resolvedDependencyPath if available, else fallback to original input
const dependencyPathForCache = resolvedDependencyPath ?? cacheDependencyPath;

const cacheDistributor = getCacheDistributor(
cache,
pythonVersion,
cacheDependencyPath
dependencyPathForCache
);
await cacheDistributor.restoreCache();
}
Expand Down