mirror of
https://github.com/prefix-dev/setup-pixi
synced 2026-10-08 15:04:08 +03:00
Co-authored-by: pavelzw <29506042+pavelzw@users.noreply.github.com>
236 lines
7.7 KiB
TypeScript
236 lines
7.7 KiB
TypeScript
import fs from 'fs/promises'
|
|
import os from 'os'
|
|
import path from 'path'
|
|
import { exit } from 'process'
|
|
import * as core from '@actions/core'
|
|
import { downloadTool } from '@actions/tool-cache'
|
|
import { parse, stringify } from 'smol-toml'
|
|
import type { PixiSource } from './options'
|
|
import { options } from './options'
|
|
import { execute, pixiCmd, renderPixiUrl } from './util'
|
|
import { tryRestoreGlobalCache, tryRestoreProjectCache, saveGlobalCache, saveProjectCache } from './cache'
|
|
import { activateEnvironment } from './activate'
|
|
|
|
const downloadPixi = async (source: PixiSource) => {
|
|
const url = renderPixiUrl(source.urlTemplate, source.version)
|
|
await core.group('Downloading Pixi', async () => {
|
|
core.debug('Installing pixi')
|
|
core.debug(`Downloading pixi from ${url}`)
|
|
core.debug(`Using headers: ${JSON.stringify(source.headers)}`)
|
|
await fs.mkdir(path.dirname(options.pixiBinPath), { recursive: true })
|
|
await downloadTool(url, options.pixiBinPath, undefined, source.headers)
|
|
await fs.chmod(options.pixiBinPath, 0o755)
|
|
core.info(`Pixi installed to ${options.pixiBinPath}`)
|
|
})
|
|
}
|
|
|
|
const writePixiConfig = async () => {
|
|
const { configuration } = options
|
|
if (!configuration) {
|
|
core.debug('Skipping pixi config setup.')
|
|
return
|
|
}
|
|
|
|
await core.group('Writing pixi configuration', async () => {
|
|
const configDir = path.join(os.homedir(), '.pixi')
|
|
const configPath = path.join(configDir, 'config.toml')
|
|
|
|
// Validate TOML before writing
|
|
let parsedNewConfig: Record<string, unknown>
|
|
try {
|
|
parsedNewConfig = parse(configuration)
|
|
core.debug(`Parsed configuration: ${JSON.stringify(parsedNewConfig)}`)
|
|
} catch (error) {
|
|
throw new Error(`Invalid TOML configuration: ${error instanceof Error ? error.message : String(error)}`)
|
|
}
|
|
|
|
await fs.mkdir(configDir, { recursive: true })
|
|
|
|
// Check if config file already exists and throw error if it does
|
|
try {
|
|
await fs.access(configPath)
|
|
throw new Error(
|
|
`Configuration file already exists at ${configPath}. ` +
|
|
`Please remove the existing file or do not use the configuration input.`
|
|
)
|
|
} catch (error) {
|
|
// If file doesn't exist (ENOENT), continue with writing
|
|
if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
throw error
|
|
}
|
|
core.debug(`No existing configuration found at ${configPath}, creating new file`)
|
|
}
|
|
|
|
core.debug(`Writing pixi configuration to ${configPath}`)
|
|
const tomlContent = stringify(parsedNewConfig)
|
|
await fs.writeFile(configPath, tomlContent, 'utf-8')
|
|
core.info(`Pixi configuration written to ${configPath}`)
|
|
})
|
|
}
|
|
|
|
const pixiLogin = async () => {
|
|
const auth = options.auth
|
|
if (!auth) {
|
|
core.debug('Skipping pixi login.')
|
|
return
|
|
}
|
|
core.debug(`auth keys: ${Object.keys(auth).toString()}`)
|
|
await core.group('Logging in to private channel', async () => {
|
|
// tokens get censored in the logs as long as they are a github secret
|
|
if ('token' in auth) {
|
|
core.debug(`Logging in to ${auth.host} with token`)
|
|
await execute(pixiCmd(`auth login --token ${auth.token} ${auth.host}`, false))
|
|
} else if ('username' in auth) {
|
|
core.debug(`Logging in to ${auth.host} with username and password`)
|
|
await execute(pixiCmd(`auth login --username ${auth.username} --password ${auth.password} ${auth.host}`, false))
|
|
} else if ('s3AccessKeyId' in auth) {
|
|
core.debug(`Logging in to ${auth.host} with s3 credentials`)
|
|
const command = auth.s3SessionToken
|
|
? `auth login --s3-access-key-id ${auth.s3AccessKeyId} --s3-secret-access-key ${auth.s3SecretAccessKey} --s3-session-token ${auth.s3SessionToken} ${auth.host}`
|
|
: `auth login --s3-access-key-id ${auth.s3AccessKeyId} --s3-secret-access-key ${auth.s3SecretAccessKey} ${auth.host}`
|
|
await execute(pixiCmd(command, false))
|
|
} else if ('condaToken' in auth) {
|
|
core.debug(`Logging in to ${auth.host} with conda token`)
|
|
await execute(pixiCmd(`auth login --conda-token ${auth.condaToken} ${auth.host}`, false))
|
|
}
|
|
})
|
|
}
|
|
|
|
const addPixiToPath = () => {
|
|
core.addPath(path.dirname(options.pixiBinPath))
|
|
}
|
|
|
|
const pixiGlobalInstall = async () => {
|
|
const { globalEnvironments } = options
|
|
if (!globalEnvironments) {
|
|
core.debug('Skipping pixi global install.')
|
|
return
|
|
}
|
|
|
|
await tryRestoreGlobalCache()
|
|
|
|
core.debug('Installing global environments')
|
|
for (const env of globalEnvironments) {
|
|
const command = `global install ${env}`
|
|
await core.group(`pixi ${command}`, () => execute(pixiCmd(command, false)))
|
|
}
|
|
|
|
await saveGlobalCache()
|
|
}
|
|
|
|
const pixiInstall = async () => {
|
|
if (!options.runInstall) {
|
|
core.debug('Skipping pixi install.')
|
|
return
|
|
}
|
|
|
|
await tryRestoreProjectCache()
|
|
|
|
const environments = options.environments ?? [undefined]
|
|
for (const environment of environments) {
|
|
core.debug(`Installing environment ${environment ?? 'default'}`)
|
|
let command = `install`
|
|
if (environment) {
|
|
command += ` -e ${environment}`
|
|
}
|
|
if (options.frozen) {
|
|
command += ' --frozen'
|
|
}
|
|
if (options.locked) {
|
|
command += ' --locked'
|
|
}
|
|
if (options.pypiKeyringProvider) {
|
|
command += ` --pypi-keyring-provider ${options.pypiKeyringProvider}`
|
|
}
|
|
await core.group(`pixi ${command}`, () => execute(pixiCmd(command)))
|
|
}
|
|
|
|
await saveProjectCache()
|
|
}
|
|
|
|
const generateList = async () => {
|
|
if (!options.runInstall) {
|
|
core.debug('Skipping pixi list.')
|
|
return
|
|
}
|
|
if (
|
|
'version' in options.pixiSource &&
|
|
options.pixiSource.version !== 'latest' &&
|
|
options.pixiSource.version < 'v0.13.0'
|
|
) {
|
|
core.warning(
|
|
'pixi list is not supported for pixi versions < `v0.13.0`. Please set `pixi-version` to `v0.13.0` or later.'
|
|
)
|
|
return
|
|
}
|
|
let command = 'list'
|
|
if (
|
|
'version' in options.pixiSource &&
|
|
options.pixiSource.version !== 'latest' &&
|
|
options.pixiSource.version < 'v0.14.0'
|
|
) {
|
|
if (options.frozen) core.warning('pixi versions < `v0.14.0` do not support the --frozen option for pixi list.')
|
|
if (options.locked) core.warning('pixi versions < `v0.14.0` do not support the --locked option for pixi list.')
|
|
} else {
|
|
command = `${command}${options.frozen ? ' --frozen' : ''}${options.locked ? ' --locked' : ''}`
|
|
}
|
|
if (options.environments) {
|
|
for (const environment of options.environments) {
|
|
core.debug(`Listing environment ${environment}`)
|
|
const cmd = `${command} -e ${environment}`
|
|
await core.group(`pixi ${cmd}`, () => execute(pixiCmd(cmd)))
|
|
}
|
|
} else {
|
|
await core.group(`pixi ${command}`, () => execute(pixiCmd(command)))
|
|
}
|
|
}
|
|
|
|
const generateInfo = async () => {
|
|
await core.group('pixi info', () => execute(pixiCmd('info')))
|
|
}
|
|
|
|
const activateEnv = async (environment: string) => {
|
|
await core.group('Activate environment', () => activateEnvironment(environment))
|
|
}
|
|
|
|
const run = async () => {
|
|
core.debug(`process.env.HOME: ${process.env.HOME ?? '-'}`)
|
|
core.debug(`os.homedir(): ${os.homedir()}`)
|
|
if (options.downloadPixi) {
|
|
await downloadPixi(options.pixiSource)
|
|
}
|
|
addPixiToPath()
|
|
await writePixiConfig()
|
|
await pixiLogin()
|
|
await pixiGlobalInstall()
|
|
await generateInfo()
|
|
await pixiInstall()
|
|
await generateList()
|
|
if (options.activatedEnvironment) {
|
|
await activateEnv(options.activatedEnvironment)
|
|
}
|
|
}
|
|
|
|
const main = async () => {
|
|
try {
|
|
await run()
|
|
// workaround for https://github.com/actions/toolkit/issues/1578
|
|
exit(0)
|
|
} catch (error: unknown) {
|
|
if (core.isDebug()) {
|
|
throw error
|
|
}
|
|
if (error instanceof Error) {
|
|
core.setFailed(error.message)
|
|
exit(1)
|
|
} else if (typeof error === 'string') {
|
|
core.setFailed(error)
|
|
exit(1)
|
|
} else {
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
void main()
|