Skip to content

Commit 8bf8202

Browse files
committed
Implement working directory setup
1 parent 82d477f commit 8bf8202

File tree

8 files changed

+100
-41
lines changed

8 files changed

+100
-41
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
run: |
4848
set -euo pipefail
4949
latest_version="$(jq -r '.version' package.json)"
50-
count_expected=21
50+
count_expected=23
5151
count_actual="$(grep -c "setup-pixi@v$latest_version" README.md || true)"
5252
if [ "$count_actual" -ne "$count_expected" ]; then
5353
echo "::error file=README.md::Expected $count_expected mentions of \`setup-pixi@v$latest_version\` in README.md, but found $count_actual."

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,27 @@ This can be overwritten by setting the `manifest-path` input argument.
418418
manifest-path: pyproject.toml
419419
```
420420

421+
### Working directory for monorepos
422+
423+
If you're working with a monorepo where your pixi project is in a subdirectory, you can use the `working-directory` input to specify where pixi should look for manifest files (`pixi.toml` or `pyproject.toml`).
424+
425+
```yml
426+
- uses: prefix-dev/[email protected]
427+
with:
428+
working-directory: ./packages/my-project
429+
```
430+
431+
This will make pixi look for `pixi.toml` or `pyproject.toml` in the `./packages/my-project` directory instead of the repository root. All pixi commands will be executed from this working directory.
432+
433+
You can combine `working-directory` with `manifest-path` if needed:
434+
435+
```yml
436+
- uses: prefix-dev/[email protected]
437+
with:
438+
working-directory: ./packages/my-project
439+
manifest-path: custom-pixi.toml
440+
```
441+
421442
### Only install pixi
422443

423444
If you only want to install pixi and not install the current project, you can use the `run-install` option.

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ inputs:
1818
One of `q`, `default`, `v`, `vv`, or `vvv`.
1919
manifest-path:
2020
description: Path to the manifest file (i.e., `pixi.toml`) to use for the pixi CLI. Defaults to `pixi.toml`.
21+
working-directory:
22+
description: |
23+
Working directory to use for pixi commands. If specified, pixi will look for manifest files (pixi.toml or pyproject.toml)
24+
in this directory instead of the repository root. Useful for monorepos where pixi projects are in subdirectories.
2125
run-install:
2226
description: Whether to run `pixi install` after installing pixi. Defaults to `true`.
2327
environments:

dist/index.js

Lines changed: 23 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/post.js

Lines changed: 17 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cache.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ export const generateProjectCacheKey = async (cacheKeyPrefix: string) => {
3232
core.debug(`lockfilePathSha: ${lockfilePathSha}`)
3333
const environments = sha256(options.environments?.join(' ') ?? '')
3434
core.debug(`environments: ${environments}`)
35-
// since the lockfile path is not necessarily absolute, we need to include the cwd in the cache key
36-
const cwdSha = sha256(process.cwd())
35+
// since the lockfile path is not necessarily absolute, we need to include the working directory in the cache key
36+
const cwdSha = sha256(options.workingDirectory)
3737
core.debug(`cwdSha: ${cwdSha}`)
3838
const sha = sha256(lockfileSha + environments + pixiSha + lockfilePathSha + cwdSha)
3939
core.debug(`sha: ${sha}`)

src/options.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Inputs = Readonly<{
1515
pixiUrlHeaders?: NodeJS.Dict<string>
1616
logLevel?: LogLevel
1717
manifestPath?: string
18+
workingDirectory?: string
1819
runInstall?: boolean
1920
environments?: string[]
2021
activateEnvironment?: string
@@ -80,6 +81,7 @@ export type Options = Readonly<{
8081
downloadPixi: boolean
8182
logLevel: LogLevel
8283
manifestPath: string
84+
workingDirectory: string
8385
pixiLockFile: string
8486
runInstall: boolean
8587
environments?: string[]
@@ -281,29 +283,41 @@ const inferOptions = (inputs: Inputs): Options => {
281283
inputs.pixiBinPath
282284
)
283285
const logLevel = inputs.logLevel ?? (core.isDebug() ? 'vv' : 'default')
284-
// infer manifest path from inputs or default to pixi.toml or pyproject.toml depending on what is present in the repo.
285-
let manifestPath = pixiPath // default
286+
287+
// Determine the working directory - resolve to absolute path if provided
288+
const workingDirectory = inputs.workingDirectory ? path.resolve(untildify(inputs.workingDirectory)) : process.cwd()
289+
core.debug(`Working directory: ${workingDirectory}`)
290+
291+
// infer manifest path from inputs or default to pixi.toml or pyproject.toml depending on what is present in the working directory.
292+
const pixiPathInWorkingDir = path.join(workingDirectory, pixiPath)
293+
const pyprojectPathInWorkingDir = path.join(workingDirectory, pyprojectPath)
294+
295+
let manifestPath = pixiPathInWorkingDir // default
286296
if (inputs.manifestPath) {
287-
manifestPath = path.resolve(untildify(inputs.manifestPath))
297+
// If manifest path is provided, resolve it relative to working directory if it's not absolute
298+
manifestPath = path.isAbsolute(inputs.manifestPath)
299+
? path.resolve(untildify(inputs.manifestPath))
300+
: path.resolve(workingDirectory, untildify(inputs.manifestPath))
288301
} else {
289-
if (existsSync(pixiPath)) {
290-
manifestPath = pixiPath
291-
} else if (existsSync(pyprojectPath)) {
302+
if (existsSync(pixiPathInWorkingDir)) {
303+
manifestPath = pixiPathInWorkingDir
304+
core.debug(`Found pixi.toml at: ${manifestPath}`)
305+
} else if (existsSync(pyprojectPathInWorkingDir)) {
292306
try {
293-
const fileContent = readFileSync(pyprojectPath, 'utf-8')
307+
const fileContent = readFileSync(pyprojectPathInWorkingDir, 'utf-8')
294308
const parsedContent: Record<string, unknown> = parse(fileContent)
295309

296310
// Test if the tool.pixi table is present in the pyproject.toml file, if so, use it as the manifest file.
297311
if (parsedContent.tool && typeof parsedContent.tool === 'object' && 'pixi' in parsedContent.tool) {
298-
core.debug(`The tool.pixi table found, using ${pyprojectPath} as manifest file.`)
299-
manifestPath = pyprojectPath
312+
core.debug(`The tool.pixi table found, using ${pyprojectPathInWorkingDir} as manifest file.`)
313+
manifestPath = pyprojectPathInWorkingDir
300314
}
301315
} catch (error) {
302-
core.error(`Error while trying to read ${pyprojectPath} file.`)
316+
core.error(`Error while trying to read ${pyprojectPathInWorkingDir} file.`)
303317
core.error(error as Error)
304318
}
305319
} else if (runInstall) {
306-
core.warning(`Could not find any manifest file. Defaulting to ${pixiPath}.`)
320+
core.warning(`Could not find any manifest file in ${workingDirectory}. Defaulting to ${pixiPathInWorkingDir}.`)
307321
}
308322
}
309323

@@ -372,6 +386,7 @@ const inferOptions = (inputs: Inputs): Options => {
372386
downloadPixi,
373387
logLevel,
374388
manifestPath,
389+
workingDirectory,
375390
pixiLockFile,
376391
runInstall,
377392
environments: inputs.environments,
@@ -410,6 +425,7 @@ const getOptions = () => {
410425
'log-level must be one of `q`, `default`, `v`, `vv`, `vvv`.'
411426
),
412427
manifestPath: parseOrUndefined('manifest-path', z.string()),
428+
workingDirectory: parseOrUndefined('working-directory', z.string()),
413429
runInstall: parseOrUndefinedJSON('run-install', z.boolean()),
414430
environments: parseOrUndefinedList('environments', z.string()),
415431
activateEnvironment: parseOrUndefined('activate-environment', z.string()),

src/util.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,16 @@ export const execute = (cmd: string[]) => {
8282
core.debug(`Executing: \`${cmd.toString()}\``)
8383
// needs escaping if cmd[0] contains spaces
8484
// https://github.com/prefix-dev/setup-pixi/issues/184#issuecomment-2765724843
85-
return exec(`"${cmd[0]}"`, cmd.slice(1))
85+
return exec(`"${cmd[0]}"`, cmd.slice(1), { cwd: options.workingDirectory })
8686
}
8787

88-
export const executeGetOutput = (cmd: string[], options?: ExecOptions) => {
88+
export const executeGetOutput = (cmd: string[], execOptions?: ExecOptions) => {
8989
core.debug(`Executing: \`${cmd.toString()}\``)
9090
// needs escaping if cmd[0] contains spaces
9191
// https://github.com/prefix-dev/setup-pixi/issues/184#issuecomment-2765724843
92-
return getExecOutput(`"${cmd[0]}"`, cmd.slice(1), options)
92+
const defaultOptions = { cwd: options.workingDirectory }
93+
const mergedOptions = execOptions ? { ...defaultOptions, ...execOptions } : defaultOptions
94+
return getExecOutput(`"${cmd[0]}"`, cmd.slice(1), mergedOptions)
9395
}
9496

9597
export const pixiCmd = (command: string, withManifestPath = true) => {

0 commit comments

Comments
 (0)