Skip to content

Commit

Permalink
Merge branch 'main' into release-v0.0.64
Browse files Browse the repository at this point in the history
  • Loading branch information
royra authored May 28, 2024
2 parents fd67f1f + eb4c58b commit a4c7661
Show file tree
Hide file tree
Showing 39 changed files with 1,852 additions and 1,451 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/awesome-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: '20.x'
- name: Check awesome-list linter
# https://github.com/sindresorhus/awesome/blob/main/pull_request_template.md
run: npx --yes awesome-lint ./README.md
2 changes: 1 addition & 1 deletion .github/workflows/check-package-mismatches.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: '20.x'
- name: Check mismatched dependencies in workspace
run: npx --yes [email protected] check-mismatches
2 changes: 1 addition & 1 deletion .github/workflows/gh-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
cache: yarn

- run: yarn
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
cache: yarn

- run: yarn
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/npm-publish-canary.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
cache: yarn

- run: yarn
- run: yarn build

- name: Set NPM token
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
cache: yarn

- run: yarn
Expand All @@ -31,7 +31,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
cache: yarn

- run: yarn
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ oclif.manifest.json

packages/*/dist
packages/*/tsconfig.tsbuildinfo

.history
10 changes: 8 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
"eslint.packageManager": "yarn",
"eslint.lintTask.enable": true,
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
}
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
Expand All @@ -29,5 +32,8 @@
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
},
"files.exclude": {
".history/**": true,
},
}
3 changes: 2 additions & 1 deletion packages/cli-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export { HookName, HookFunc, HooksListeners, Hooks } from './lib/hooks.js'
export { PluginContext, PluginInitContext } from './lib/plugins/context.js'
export { errorToJson } from './lib/errors.js'
export {
composeFlags, pluginFlags, envIdFlags, tunnelServerFlags, urlFlags, buildFlags, tableFlags, parseBuildFlags,
composeFlags, pluginFlags, envIdFlags, tunnelServerFlags, parseTunnelServerFlags,
urlFlags, buildFlags, tableFlags, parseBuildFlags,
} from './lib/common-flags/index.js'
export { formatFlagsToArgs, parseFlags, ParsedFlags } from './lib/flags.js'
export { initHook } from './hooks/init/load-plugins.js'
Expand Down
22 changes: 6 additions & 16 deletions packages/cli-common/src/lib/common-flags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EOL } from 'os'
import { DEFAULT_PLUGINS } from '../plugins/default-plugins.js'

export * from './build-flags.js'
export * from './tunnel-server-flags.js'

export const tableFlags = mapValues(ux.table.flags(), f => ({ ...f, helpGroup: 'OUTPUT' })) as ReturnType<typeof ux.table['flags']>

Expand Down Expand Up @@ -69,22 +70,6 @@ export const envIdFlags = {
...projectFlag,
} as const

export const tunnelServerFlags = {
'tunnel-url': Flags.string({
summary: 'Tunnel url, specify ssh://hostname[:port] or ssh+tls://hostname[:port]',
char: 't',
default: 'ssh+tls://livecycle.run' ?? process.env.PREVIEW_TUNNEL_OVERRIDE,
}),
'tls-hostname': Flags.string({
summary: 'Override TLS server name when tunneling via HTTPS',
required: false,
}),
'insecure-skip-verify': Flags.boolean({
summary: 'Skip TLS or SSH certificate verification',
default: false,
}),
} as const

export const urlFlags = {
'include-access-credentials': Flags.boolean({
summary: 'Include access credentials for basic auth for each service URL',
Expand All @@ -109,4 +94,9 @@ export const urlFlags = {
summary: 'Timeout for fetching URLs request in milliseconds',
default: 2500,
}),
wait: Flags.boolean({
description: 'Wait for all tunnels to be ready',
default: true,
allowNo: true,
}),
} as const
24 changes: 24 additions & 0 deletions packages/cli-common/src/lib/common-flags/tunnel-server-flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Flags } from '@oclif/core'
import { InferredFlags } from '@oclif/core/lib/interfaces'

export const tunnelServerFlags = {
'tunnel-url': Flags.string({
summary: 'Tunnel url, specify ssh://hostname[:port] or ssh+tls://hostname[:port]',
char: 't',
default: 'ssh+tls://livecycle.run' ?? process.env.PREVIEW_TUNNEL_OVERRIDE,
}),
'tls-hostname': Flags.string({
summary: 'Override TLS server name when tunneling via HTTPS',
required: false,
}),
'insecure-skip-verify': Flags.boolean({
summary: 'Skip TLS or SSH certificate verification',
default: false,
}),
} as const

export const parseTunnelServerFlags = (flags: Omit<InferredFlags<typeof tunnelServerFlags>, 'json'>) => ({
url: flags['tunnel-url'],
tlsServerName: flags['tls-hostname'],
insecureSkipVerify: flags['insecure-skip-verify'],
})
6 changes: 3 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@
"topicSeparator": " ",
"update": {
"node": {
"version": "18.19.0",
"version": "20.13.1",
"options": [
"--no-warnings=ExperimentalWarning",
"--disable-warning=ExperimentalWarning",
"--enable-source-maps"
]
},
Expand All @@ -123,4 +123,4 @@
"preview"
],
"types": "dist/index.d.ts"
}
}
37 changes: 37 additions & 0 deletions packages/cli/src/commands/env-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ux } from '@oclif/core'
import { findEnvId } from '@preevy/core'
import ProfileCommand from '../profile-command.js'
import { envIdFlags } from '../common-flags.js'

// eslint-disable-next-line no-use-before-define
export default class EnvId extends ProfileCommand<typeof EnvId> {
static description = 'Show the Preevy environment ID for the current Compose project'

static flags = {
...envIdFlags,
}

static enableJsonFlag = true

static args = {}

async run(): Promise<unknown> {
const log = this.logger
const { flags, args } = this

const envId = await findEnvId({
userSpecifiedEnvId: flags.id,
userSpecifiedProjectName: flags.project,
userModel: () => this.ensureUserModel(),
log,
explanationLogLevel: 'debug',
})

if (this.jsonEnabled()) {
return envId
}

ux.log(envId)
return undefined
}
}
144 changes: 144 additions & 0 deletions packages/cli/src/commands/env/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Flags, ux } from '@oclif/core'
import { envIdFlags, parseTunnelServerFlags, text, tunnelServerFlags } from '@preevy/cli-common'
import { TunnelOpts, addBaseComposeTunnelAgentService, findComposeTunnelAgentUrl, findEnvId, getTunnelNamesToServicePorts, getUserCredentials, jwtGenerator, profileStore, queryEnvMetadata, readMetadata } from '@preevy/core'
import { tunnelNameResolver } from '@preevy/common'
import { inspect } from 'util'
import DriverCommand from '../../driver-command.js'
import { connectToTunnelServerSsh } from '../../tunnel-server-client.js'

type MetadataSource = 'agent' | 'driver'
type UnknownMetadata = Record<string, unknown>

// eslint-disable-next-line no-use-before-define
export default class EnvMetadataCommand extends DriverCommand<typeof EnvMetadataCommand> {
static description = 'Show metadata for a preview environment'
static enableJsonFlag = true

static flags = {
...envIdFlags,
...tunnelServerFlags,
source: Flags.custom<'driver' | 'agent'>({
summary: 'Show metadata from the driver, the agent, or the driver if the agent is not available',
default: ['agent', 'driver'],
multiple: true,
delimiter: ',',
multipleNonGreedy: true,
})(),
'fetch-timeout': Flags.integer({
default: 2500,
summary: 'Timeout for fetching metadata from the agent in milliseconds',
}),
} as const

async getComposeTunnelAgentUrl(
envId: string,
tunnelOpts: TunnelOpts,
tunnelingKey: string | Buffer,
) {
const expectedTunnels = getTunnelNamesToServicePorts(
addBaseComposeTunnelAgentService({ name: '' }),
tunnelNameResolver({ envId }),
)

const { client: tunnelServerSshClient } = await connectToTunnelServerSsh({
tunnelOpts,
profileStore: profileStore(this.store),
tunnelingKey,
log: this.logger,
})

try {
const expectedTunnelUrls = await tunnelServerSshClient.execTunnelUrl(Object.keys(expectedTunnels))

const expectedServiceUrls = Object.entries(expectedTunnels)
.map(([tunnel, { name, port }]) => ({ name, port, url: expectedTunnelUrls[tunnel] }))

return findComposeTunnelAgentUrl(expectedServiceUrls)
} finally {
void tunnelServerSshClient.end()
}
}

#envId: string | undefined
async envId() {
if (!this.#envId) {
const { flags } = this
this.#envId = await findEnvId({
userSpecifiedEnvId: flags.id,
userSpecifiedProjectName: flags.project,
userModel: () => this.ensureUserModel(),
log: this.logger,
})
}
return this.#envId
}

async getMetadataFromDriver() {
return await this.withConnection(await this.envId(), readMetadata)
}

async getMetadataFromAgent() {
const pStore = profileStore(this.store).ref
const tunnelingKey = await pStore.tunnelingKey()
const composeTunnelServiceUrl = await this.getComposeTunnelAgentUrl(
await this.envId(),
parseTunnelServerFlags(this.flags),
tunnelingKey,
)
const credentials = await getUserCredentials(jwtGenerator(tunnelingKey))
// eslint-disable-next-line @typescript-eslint/return-await
return await queryEnvMetadata({
composeTunnelServiceUrl,
credentials,
fetchTimeout: this.flags['fetch-timeout'],
retryOpts: { retries: 2 },
})
}

metadataFactories: Record<MetadataSource, () => Promise<UnknownMetadata>> = {
driver: this.getMetadataFromDriver.bind(this),
agent: this.getMetadataFromAgent.bind(this),
}

async getMetatdata() {
const { flags: { source: sources } } = this
const errors: { source: MetadataSource; error: unknown }[] = []
for (const source of sources) {
try {
this.logger.debug(`Fetching metadata from ${source}`)
return {
// eslint-disable-next-line no-await-in-loop
metadata: await this.metadataFactories[source](),
errors,
source,
}
} catch (err) {
errors.push({ source, error: err })
}
}

return { errors }
}

async run(): Promise<unknown> {
const { metadata, source: metadataSource, errors } = await this.getMetatdata()

if (!metadata) {
throw new Error(`Could not get metadata: ${inspect(errors)}`)
}

if (errors.length) {
for (const { source: errorSource, error } of errors) {
this.logger.warn(`Error fetching metadata from ${errorSource}: ${error}`)
}
}

if (this.jsonEnabled()) {
return { ...metadata, _source: metadataSource }
}

this.logger.info(`Metadata from ${text.code(metadataSource)}`)
this.logger.info(inspect(metadata, { depth: null, colors: text.supportsColor !== false }))
return undefined
}
}
8 changes: 2 additions & 6 deletions packages/cli/src/commands/proxy/connect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Args, Flags } from '@oclif/core'
import { jwkThumbprint, commands, profileStore, withSpinner, SshConnection, machineId, validateEnvId, normalizeEnvId, EnvId } from '@preevy/core'
import { tableFlags, text, tunnelServerFlags, urlFlags } from '@preevy/cli-common'
import { parseTunnelServerFlags, tableFlags, text, tunnelServerFlags, urlFlags } from '@preevy/cli-common'
import { inspect } from 'util'
import { formatPublicKey } from '@preevy/common'
import { spawn } from 'child_process'
Expand Down Expand Up @@ -58,11 +58,7 @@ export default class Connect extends ProfileCommand<typeof Connect> {
const pStoreRef = pStore.ref

const tunnelingKey = await pStoreRef.tunnelingKey()
const tunnelOpts = {
url: flags['tunnel-url'],
tlsServerName: flags['tls-hostname'],
insecureSkipVerify: flags['insecure-skip-verify'],
}
const tunnelOpts = parseTunnelServerFlags(flags)
const composeProject = args['compose-project']
let envId:EnvId
if (flags.id) {
Expand Down
Loading

0 comments on commit a4c7661

Please sign in to comment.