Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add --project-directory flag #411

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 6 additions & 3 deletions packages/cli-common/src/hooks/init/load-plugins.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path'
import { Hook as OclifHook, Command, Flags } from '@oclif/core'
import { Parser } from '@oclif/core/lib/parser/parse.js'
import { BooleanFlag, Config, Topic } from '@oclif/core/lib/interfaces'
Expand Down Expand Up @@ -41,9 +42,11 @@ export const initHook: OclifHook<'init'> = async function hook(args) {
argv,
} as const).parse()

const composeFiles = await resolveComposeFiles({
const { files: composeFiles, projectDirectory } = await resolveComposeFiles({
userSpecifiedFiles: flags.file,
userSpecifiedSystemFiles: flags['system-compose-file'],
userSpecifiedProjectDirectory: flags['project-directory'],
cwd: process.cwd(),
})

const userModelOrError = composeFiles.length
Expand All @@ -53,7 +56,7 @@ export const initHook: OclifHook<'init'> = async function hook(args) {
async () => await localComposeClient({
composeFiles,
projectName: flags.project,
projectDirectory: process.cwd(),
projectDirectory,
}).getModelOrError(),
{
text: `Loading compose file${composeFiles.length > 1 ? 's' : ''}: ${composeFiles.join(', ')}`,
Expand All @@ -76,7 +79,7 @@ export const initHook: OclifHook<'init'> = async function hook(args) {
(config as InternalConfig).loadTopics({ commands, topics })

Object.assign(config, {
composeFiles,
composeFiles: { files: composeFiles, projectDirectory },
initialUserModel: userModelOrError,
preevyConfig,
preevyHooks: hooksFromPlugins(loadedPlugins.map(p => p.initResults)),
Expand Down
4 changes: 4 additions & 0 deletions packages/cli-common/src/lib/common-flags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export const composeFlags = {
default: [],
helpGroup: 'GLOBAL',
}),
'project-directory': Flags.string({
required: false,
summary: 'Alternate working directory (default: the path of the first specified Compose file)',
}),
...projectFlag,
} as const

Expand Down
4 changes: 2 additions & 2 deletions packages/cli-common/src/lib/plugins/model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FlagProps } from '@oclif/core/lib/interfaces/parser.js'
import { Topic } from '@oclif/core/lib/interfaces'
import { Command } from '@oclif/core'
import { ComposeModel, config as coreConfig } from '@preevy/core'
import { ComposeFiles, ComposeModel, config as coreConfig } from '@preevy/core'
import { PluginInitContext } from './context.js'
import { HookFuncs, HooksListeners } from '../hooks.js'
import PreevyConfig = coreConfig.PreevyConfig
Expand All @@ -25,7 +25,7 @@ export type PluginModule = {

declare module '@oclif/core/lib/config/config.js' {
export interface Config {
composeFiles: string[]
composeFiles: ComposeFiles
initialUserModel: ComposeModel | Error
preevyHooks: HooksListeners
preevyConfig: PreevyConfig
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export default class Logs extends DriverCommand<typeof Logs> {
const compose = localComposeClient({
composeFiles: Buffer.from(yaml.stringify(addBaseComposeTunnelAgentService(userModel))),
projectName: flags.project,
projectDirectory: process.cwd(),
projectDirectory: this.config.composeFiles.projectDirectory,
})

await using dockerContext = await dockerEnvContext({ connection, log })
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/commands/up.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default class Up extends MachineCreationDriverCommand<typeof Up> {
...tunnelServerFlags,
...buildFlags,
'skip-volume': Flags.string({
description: 'Additional volume glob patterns to skip copying',
description: 'Additional volume glob patterns to skip copying (relative to project directory)',
multiple: true,
multipleNonGreedy: true,
default: [],
Expand Down Expand Up @@ -199,7 +199,6 @@ export default class Up extends MachineCreationDriverCommand<typeof Up> {
dataDir: this.config.dataDir,
sshTunnelPrivateKey: tunnelingKey,
allowedSshHostKeys: hostKey,
cwd: process.cwd(),
skipUnchangedFiles: flags['skip-unchanged-files'],
version: this.config.version,
buildSpec,
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const buildCommand = async ({
log,
composeModel,
projectLocalDataDir,
cwd,
buildSpec,
machineDockerPlatform,
env,
Expand All @@ -23,7 +22,6 @@ const buildCommand = async ({
log: Logger
composeModel: ComposeModel
projectLocalDataDir: string
cwd: string
buildSpec: BuildSpec
machineDockerPlatform: string
env?: Record<string, string>
Expand All @@ -47,7 +45,7 @@ const buildCommand = async ({
]

log.info(`Running: docker ${dockerArgs.join(' ')}`)
const { elapsedTimeSec } = await measureTime(() => childProcessPromise(spawn('docker', dockerArgs, { stdio: 'inherit', cwd, env })))
const { elapsedTimeSec } = await measureTime(() => childProcessPromise(spawn('docker', dockerArgs, { stdio: 'inherit', env })))
telemetryEmitter().capture('build success', {
elapsed_sec: elapsedTimeSec,
has_registry: Boolean(buildSpec.registry),
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/commands/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MachineStatusCommand, ScriptInjection } from '@preevy/common'
import path from 'path'
import { rimraf } from 'rimraf'
import { TunnelOpts } from '../ssh/index.js'
import { ComposeModel, remoteComposeModel } from '../compose/index.js'
import { ComposeFiles, ComposeModel, remoteComposeModel } from '../compose/index.js'
import { createCopiedFileInDataDir } from '../remote-files.js'
import { Logger } from '../log.js'
import { EnvId } from '../env-id.js'
Expand All @@ -21,7 +21,6 @@ const composeModel = async ({
dataDir,
allowedSshHostKeys: hostKey,
sshTunnelPrivateKey,
cwd,
version,
envId,
expectedServiceUrls,
Expand All @@ -35,13 +34,12 @@ const composeModel = async ({
userSpecifiedProjectName: string | undefined
userSpecifiedServices: string[]
volumeSkipList: string[]
composeFiles: string[]
composeFiles: ComposeFiles
log: Logger
dataDir: string
scriptInjections?: Record<string, ScriptInjection>
sshTunnelPrivateKey: string | Buffer
allowedSshHostKeys: Buffer
cwd: string
version: string
envId: EnvId
expectedServiceUrls: { name: string; port: number; url: string }[]
Expand All @@ -60,7 +58,6 @@ const composeModel = async ({
volumeSkipList,
composeFiles,
log,
cwd,
expectedServiceUrls,
projectName,
modelFilter,
Expand Down
9 changes: 2 additions & 7 deletions packages/core/src/commands/up.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MachineStatusCommand, ScriptInjection } from '@preevy/common'
import yaml from 'yaml'
import { TunnelOpts } from '../ssh/index.js'
import { ComposeModel, composeModelFilename, localComposeClient } from '../compose/index.js'
import { ComposeFiles, ComposeModel, composeModelFilename, localComposeClient } from '../compose/index.js'
import { dockerEnvContext } from '../docker.js'
import { MachineConnection } from '../driver/index.js'
import { remoteProjectDir } from '../remote-files.js'
Expand Down Expand Up @@ -54,7 +54,6 @@ const up = async ({
dataDir,
allowedSshHostKeys,
sshTunnelPrivateKey,
cwd,
skipUnchangedFiles,
version,
envId,
Expand All @@ -72,13 +71,12 @@ const up = async ({
userSpecifiedProjectName: string | undefined
userSpecifiedServices: string[]
volumeSkipList: string[]
composeFiles: string[]
composeFiles: ComposeFiles
log: Logger
dataDir: string
scriptInjections?: Record<string, ScriptInjection>
sshTunnelPrivateKey: string | Buffer
allowedSshHostKeys: Buffer
cwd: string
skipUnchangedFiles: boolean
version: string
envId: EnvId
Expand All @@ -100,7 +98,6 @@ const up = async ({
log,
machineStatusCommand,
userAndGroup,
cwd,
tunnelOpts,
userSpecifiedProjectName,
userSpecifiedServices,
Expand All @@ -126,7 +123,6 @@ const up = async ({
composeModel = (await buildCommand({
log,
buildSpec,
cwd,
composeModel,
projectLocalDataDir,
machineDockerPlatform: dockerPlatform,
Expand All @@ -151,7 +147,6 @@ const up = async ({

const compose = localComposeClient({
composeFiles: [composeFilePath.local],
projectDirectory: cwd,
})

const composeArgs = [
Expand Down
19 changes: 14 additions & 5 deletions packages/core/src/compose/files.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import fs from 'fs'
import path from 'path'
import { ComposeFiles } from './model.js'

const DEFAULT_BASE_FILES = ['compose', 'docker-compose']
const DEFAULT_OVERRIDE_FILES = DEFAULT_BASE_FILES.map(f => `${f}.override`)
Expand Down Expand Up @@ -46,11 +48,18 @@ const findDefaultFiles = async () => (await oneYamlFileArray(DEFAULT_BASE_FILES,
const findDefaultSystemFiles = async () => (await oneYamlFileArray(DEFAULT_SYSTEM_FILES, 'default system Compose')) ?? []

export const resolveComposeFiles = async (
{ userSpecifiedFiles, userSpecifiedSystemFiles }: {
{ userSpecifiedFiles, userSpecifiedSystemFiles, userSpecifiedProjectDirectory, cwd }: {
userSpecifiedFiles: string[]
userSpecifiedSystemFiles: string[]
userSpecifiedProjectDirectory: string
cwd: string
},
): Promise<string[]> => [
...(userSpecifiedSystemFiles.length ? userSpecifiedSystemFiles : await findDefaultSystemFiles()),
...(userSpecifiedFiles.length ? userSpecifiedFiles : await findDefaultFiles()),
]
): Promise<ComposeFiles> => {
const files = (userSpecifiedFiles.length ? userSpecifiedFiles : await findDefaultFiles())
const systemFiles = (userSpecifiedSystemFiles.length ? userSpecifiedSystemFiles : await findDefaultSystemFiles())

return {
files: [...systemFiles, ...files],
projectDirectory: path.resolve(userSpecifiedProjectDirectory ?? files.length ? path.dirname(files[0]) : cwd),
}
}
5 changes: 5 additions & 0 deletions packages/core/src/compose/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ export type ComposeModel = {
}

export const composeModelFilename = 'docker-compose.yaml'

export type ComposeFiles = {
files: string[]
projectDirectory: string
}
24 changes: 11 additions & 13 deletions packages/core/src/compose/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import yaml from 'yaml'
import path from 'path'
import { mapValues } from 'lodash-es'
import { MMRegExp, makeRe } from 'minimatch'
import { asyncMap, asyncToArray } from 'iter-tools-es'
import { asyncMap, asyncToArray, compose } from 'iter-tools-es'
import { COMPOSE_TUNNEL_AGENT_SERVICE_NAME, MachineStatusCommand, ScriptInjection, formatPublicKey } from '@preevy/common'
import { MachineConnection } from '../driver/index.js'
import { ComposeModel, ComposeSecretOrConfig, composeModelFilename } from './model.js'
import { ComposeFiles, ComposeModel, ComposeSecretOrConfig, composeModelFilename } from './model.js'
import { REMOTE_DIR_BASE, remoteProjectDir } from '../remote-files.js'
import { TunnelOpts } from '../ssh/index.js'
import { addComposeTunnelAgentService } from '../compose-tunnel-agent-client.js'
Expand Down Expand Up @@ -42,9 +42,9 @@ const toPosix = (x:string) => x.split(path.sep).join(path.posix.sep)
export type SkippedVolume = { service: string; source: string; matchingRule: string }

const fixModelForRemote = async (
{ skipServices = [], cwd, remoteBaseDir, volumeSkipList = defaultVolumeSkipList }: {
{ skipServices = [], projectDirectory, remoteBaseDir, volumeSkipList = defaultVolumeSkipList }: {
skipServices?: string[]
cwd: string
projectDirectory: string
remoteBaseDir: string
volumeSkipList: string[]
},
Expand All @@ -55,7 +55,7 @@ const fixModelForRemote = async (
skippedVolumes: SkippedVolume[]
}> => {
const volumeSkipRes = volumeSkipList
.map(s => makeRe(path.resolve(cwd, s)))
.map(s => makeRe(path.resolve(projectDirectory, s)))
.map((r, i) => {
if (!r) {
throw new Error(`Invalid glob pattern in volumeSkipList: "${volumeSkipList[i]}"`)
Expand All @@ -70,7 +70,7 @@ const fixModelForRemote = async (
if (!path.isAbsolute(absolutePath)) {
throw new Error(`expected absolute path: "${absolutePath}"`)
}
const relativePath = toPosix(path.relative(cwd, absolutePath))
const relativePath = toPosix(path.relative(projectDirectory, absolutePath))

return relativePath.startsWith('..')
? path.posix.join('absolute', absolutePath)
Expand Down Expand Up @@ -166,7 +166,6 @@ export const remoteComposeModel = async ({
volumeSkipList,
composeFiles,
log,
cwd,
expectedServiceUrls,
projectName,
agentSettings,
Expand All @@ -176,33 +175,32 @@ export const remoteComposeModel = async ({
userSpecifiedProjectName: string | undefined
userSpecifiedServices: string[]
volumeSkipList: string[]
composeFiles: string[]
composeFiles: ComposeFiles
log: Logger
cwd: string
expectedServiceUrls: { name: string; port: number; url: string }[]
projectName: string
agentSettings?: AgentSettings
modelFilter: (userModel: ComposeModel) => Promise<ComposeModel>
}) => {
const remoteDir = remoteProjectDir(projectName)

log.debug(`Using compose files: ${composeFiles.join(', ')}`)
log.debug(`Using compose files: ${composeFiles.files.join(', ')} and project directory "${composeFiles.projectDirectory}"`)

const linkEnvVars = serviceLinkEnvVars(expectedServiceUrls)

const composeClientWithInjectedArgs = localComposeClient({
composeFiles,
composeFiles: composeFiles.files,
env: linkEnvVars,
projectName: userSpecifiedProjectName,
projectDirectory: cwd,
projectDirectory: composeFiles.projectDirectory,
})

const services = userSpecifiedServices.length
? [...userSpecifiedServices].concat(COMPOSE_TUNNEL_AGENT_SERVICE_NAME)
: []

const { model: fixedModel, filesToCopy, skippedVolumes } = await fixModelForRemote(
{ cwd, remoteBaseDir: remoteDir, volumeSkipList },
{ projectDirectory: composeFiles.projectDirectory, remoteBaseDir: remoteDir, volumeSkipList },
await modelFilter(await composeClientWithInjectedArgs.getModel(services)),
)

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export {
localComposeClient, ComposeModel, resolveComposeFiles, getExposedTcpServicePorts,
fetchRemoteUserModel as remoteUserModel, NoComposeFilesError,
addScriptInjectionsToServices as addScriptInjectionsToModel,
ComposeFiles,
defaultVolumeSkipList,
} from './compose/index.js'
export { withSpinner } from './spinner.js'
Expand Down
Loading