-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a BaseCommand with telemetry code inside instead of each co…
…mmand defining it manually
- Loading branch information
1 parent
4766683
commit eecfd74
Showing
52 changed files
with
730 additions
and
891 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { Command, Flags, Interfaces } from '@oclif/core'; | ||
import { ExitError } from '@oclif/core/lib/errors'; | ||
import { Span, SpanStatusCode, trace, Tracer } from '@opentelemetry/api'; | ||
|
||
import { SB_TELEMETRY_DISABLED } from './config/env'; | ||
import { traceExporter } from './config/telemetry'; | ||
|
||
const formatAttrs = (obj: { [k: string]: string } = {}, prefix = '') => { | ||
return Object.fromEntries( | ||
Object.keys(obj).map((key: string) => { | ||
return [[prefix, key].join('.'), obj[key]]; | ||
}) | ||
); | ||
}; | ||
|
||
export type Flags<T extends typeof Command> = Interfaces.InferredFlags< | ||
(typeof BaseCommand)['baseFlags'] & T['flags'] | ||
>; | ||
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>; | ||
|
||
export abstract class BaseCommand<T extends typeof Command> extends Command { | ||
protected tracer: Tracer | null = null; | ||
protected span: Span | null = null; | ||
|
||
static baseFlags = {}; | ||
|
||
protected flags!: Flags<T>; | ||
protected args!: Args<T>; | ||
|
||
public async init(): Promise<void> { | ||
await super.init(); | ||
const { args, flags } = await this.parse({ | ||
flags: this.ctor.flags, | ||
baseFlags: (super.ctor as typeof BaseCommand).baseFlags, | ||
args: this.ctor.args, | ||
strict: this.ctor.strict, | ||
}); | ||
this.flags = flags as Flags<T>; | ||
this.args = args as Args<T>; | ||
|
||
if (!SB_TELEMETRY_DISABLED) { | ||
this.printTelemetryInfo(); | ||
this.tracer = trace.getTracer('command', this.config.version); | ||
|
||
this.span = this.tracer.startSpan(`command.${this.ctor.id}`, { | ||
attributes: { | ||
...formatAttrs(flags, 'flags'), | ||
...formatAttrs(args, 'args'), | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
protected async catch(err: Error & { exitCode?: number }): Promise<any> { | ||
if (!SB_TELEMETRY_DISABLED) { | ||
if (!(err instanceof ExitError) || err.oclif.exit !== 0) { | ||
this.span?.addEvent('Command error'); | ||
this.span?.recordException(err); | ||
this.span?.setStatus({ code: SpanStatusCode.ERROR }); | ||
} | ||
} | ||
return super.catch(err); | ||
} | ||
|
||
protected async finally(_: Error | undefined): Promise<any> { | ||
if (!SB_TELEMETRY_DISABLED) { | ||
this.span?.addEvent('Command finished'); | ||
this.span?.end(); | ||
|
||
// Need to wait en event loop for the internal promise in exporter to be visible | ||
await new Promise((resolve) => setTimeout(() => resolve(true))); | ||
// wait for the exporter to send data | ||
await traceExporter.forceFlush(); | ||
} | ||
return super.finally(_); | ||
} | ||
|
||
protected printTelemetryInfo(): void { | ||
console.log({SB_TELEMETRY_DISABLED}) | ||
if (!SB_TELEMETRY_DISABLED) { | ||
this.log(`\x1b[2m | ||
------ Notice ------ | ||
This CLI collects various anonymous events, warnings, and errors to improve the CLI tool and enhance your user experience. | ||
Read more: [Your GitHub or documentation link here] | ||
If you want to opt out of telemetry, you can set the environment variable SB_TELEMETRY_DISABLED to 1 in your shell. | ||
For example: | ||
export SB_TELEMETRY_DISABLED=1 | ||
\x1b[0m`); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,13 @@ | ||
import { Command } from '@oclif/core'; | ||
import { trace } from '@opentelemetry/api'; | ||
|
||
import { initConfig } from '../../config/init'; | ||
import { BaseCommand } from '../../baseCommand'; | ||
|
||
const tracer = trace.getTracer('aws'); | ||
export default class GetEnv extends Command { | ||
export default class GetEnv extends BaseCommand<typeof GetEnv> { | ||
static description = 'Get currently selected ENV stage'; | ||
|
||
static examples = [`$ <%= config.bin %> <%= command.id %>`]; | ||
|
||
async run(): Promise<void> { | ||
return tracer.startActiveSpan('get-env', async (span) => { | ||
const { envStage } = await initConfig(this, {}); | ||
this.log(envStage); | ||
span.end(); | ||
}); | ||
const { envStage } = await initConfig(this, {}); | ||
this.log(envStage); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,17 @@ | ||
import { Command } from '@oclif/core'; | ||
import { trace } from '@opentelemetry/api'; | ||
|
||
import { initConfig } from '../../config/init'; | ||
import { runCommand } from '../../lib/runCommand'; | ||
import { assertAwsVaultInstalled } from '../../lib/awsVault'; | ||
import { BaseCommand } from '../../baseCommand'; | ||
|
||
const tracer = trace.getTracer('aws'); | ||
export default class AwsLogin extends Command { | ||
export default class AwsLogin extends BaseCommand<typeof AwsLogin> { | ||
static description = 'Get currently selected ENV stage'; | ||
|
||
static examples = [`$ <%= config.bin %> <%= command.id %>`]; | ||
|
||
async run(): Promise<void> { | ||
return tracer.startActiveSpan('login', async (span) => { | ||
await initConfig(this, { requireAws: true }); | ||
await assertAwsVaultInstalled(); | ||
await initConfig(this, { requireAws: true }); | ||
await assertAwsVaultInstalled(); | ||
|
||
await runCommand('aws-vault', ['login']); | ||
span.end(); | ||
}); | ||
await runCommand('aws-vault', ['login']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,36 @@ | ||
import { Command } from '@oclif/core'; | ||
import { trace } from '@opentelemetry/api'; | ||
|
||
import { initConfig } from '../../config/init'; | ||
import { runCommand } from '../../lib/runCommand'; | ||
import { assertDockerIsRunning, dockerHubLogin } from '../../lib/docker'; | ||
import { BaseCommand } from '../../baseCommand'; | ||
|
||
const tracer = trace.getTracer('backend'); | ||
|
||
export default class BackendBlack extends Command { | ||
export default class BackendBlack extends BaseCommand<typeof BackendBlack> { | ||
static description = 'Run black inside backend docker container'; | ||
|
||
static examples = [`$ <%= config.bin %> <%= command.id %>`]; | ||
|
||
async run(): Promise<void> { | ||
return tracer.startActiveSpan('black', async (span) => { | ||
const { rootPath } = await initConfig(this, { | ||
requireLocalEnvStage: true, | ||
}); | ||
await assertDockerIsRunning(); | ||
await dockerHubLogin(); | ||
|
||
await runCommand( | ||
'docker', | ||
[ | ||
'compose', | ||
'run', | ||
'--rm', | ||
'-T', | ||
'--no-deps', | ||
'backend', | ||
'black', | ||
'--config=pyproject.toml', | ||
'.', | ||
], | ||
{ | ||
cwd: rootPath, | ||
} | ||
); | ||
|
||
span.end(); | ||
const { rootPath } = await initConfig(this, { | ||
requireLocalEnvStage: true, | ||
}); | ||
await assertDockerIsRunning(); | ||
await dockerHubLogin(); | ||
|
||
await runCommand( | ||
'docker', | ||
[ | ||
'compose', | ||
'run', | ||
'--rm', | ||
'-T', | ||
'--no-deps', | ||
'backend', | ||
'black', | ||
'--config=pyproject.toml', | ||
'.', | ||
], | ||
{ | ||
cwd: rootPath, | ||
} | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,19 @@ | ||
import { Command } from '@oclif/core'; | ||
import { trace } from '@opentelemetry/api'; | ||
|
||
import { initConfig } from '../../config/init'; | ||
import { runCommand } from '../../lib/runCommand'; | ||
import { dockerHubLogin } from '../../lib/docker'; | ||
import { BaseCommand } from '../../baseCommand'; | ||
|
||
const tracer = trace.getTracer('backend'); | ||
|
||
export default class BackendBuild extends Command { | ||
export default class BackendBuildDocs extends BaseCommand< | ||
typeof BackendBuildDocs | ||
> { | ||
static description = 'Build backend docs and put results into docs package'; | ||
|
||
static examples = [`$ <%= config.bin %> <%= command.id %>`]; | ||
|
||
async run(): Promise<void> { | ||
return tracer.startActiveSpan('build-docs', async (span) => { | ||
await initConfig(this, {}); | ||
await dockerHubLogin(); | ||
await initConfig(this, {}); | ||
await dockerHubLogin(); | ||
|
||
await runCommand('pnpm', ['nx', 'run', 'backend:build-docs']); | ||
span.end(); | ||
}); | ||
await runCommand('pnpm', ['nx', 'run', 'backend:build-docs']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,29 @@ | ||
import { Command } from '@oclif/core'; | ||
import { color } from '@oclif/color'; | ||
import { trace } from '@opentelemetry/api'; | ||
|
||
import { initConfig } from '../../config/init'; | ||
import { runCommand } from '../../lib/runCommand'; | ||
import { dockerHubLogin } from '../../lib/docker'; | ||
import { BaseCommand } from '../../baseCommand'; | ||
|
||
const tracer = trace.getTracer('backend'); | ||
export default class BackendBuild extends Command { | ||
export default class BackendBuild extends BaseCommand<typeof BackendBuild> { | ||
static description = 'Build backend docker image and upload it to AWS ECR'; | ||
|
||
static examples = [`$ <%= config.bin %> <%= command.id %>`]; | ||
|
||
async run(): Promise<void> { | ||
return tracer.startActiveSpan('build', async (span) => { | ||
const { envStage, version, awsRegion, awsAccountId } = await initConfig( | ||
this, | ||
{ requireAws: true } | ||
); | ||
await dockerHubLogin(); | ||
const { envStage, version, awsRegion, awsAccountId } = await initConfig( | ||
this, | ||
{ requireAws: true } | ||
); | ||
await dockerHubLogin(); | ||
|
||
this.log(`Building backend: | ||
this.log(`Building backend: | ||
envStage: ${color.green(envStage)} | ||
version: ${color.green(version)} | ||
AWS account: ${color.green(awsAccountId)} | ||
AWS region: ${color.green(awsRegion)} | ||
`); | ||
|
||
await runCommand('pnpm', ['nx', 'run', 'backend:build']); | ||
span.end(); | ||
}); | ||
await runCommand('pnpm', ['nx', 'run', 'backend:build']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.