1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
#!/usr/bin/env node
import {
getRequiredEnvVars,
getStdout,
LogTraceable,
LogMetricTraceable,
Metric,
TraceUtil,
Command,
} from '@emprespresso/pengueno';
import type { BuildDockerImageJob, BuildDockerImageJobProps } from '@emprespresso/ci_model';
import { Bitwarden, type LoginItem } from '@emprespresso/ci_worker';
const eitherJob = getRequiredEnvVars([
'registry',
'namespace',
'repository',
'imageTag',
'context',
'dockerfile',
'buildTarget',
]).mapRight(
(baseArgs) =>
<BuildDockerImageJob>{
type: 'build_docker_image.js',
arguments: baseArgs,
},
);
const eitherVault = Bitwarden.getConfigFromEnvironment().mapRight((config) => new Bitwarden(config));
const buildImageMetric = Metric.fromName('dockerImage.build').asResult();
const loginMetric = Metric.fromName('dockerRegistry.login').asResult();
const _logJob = LogTraceable.of(eitherJob).flatMap((tEitherJob) => {
const trace = tEitherJob.get().fold(
() => 'NO_BUILD_TARGET',
({ arguments: { buildTarget } }) => buildTarget,
);
return tEitherJob.traceScope(() => `build_docker_image.${trace}`);
});
await LogMetricTraceable.ofLogTraceable(_logJob)
.flatMap(TraceUtil.withMetricTrace(buildImageMetric))
.flatMap(TraceUtil.withMetricTrace(loginMetric))
.peek((tEitherJob) => tEitherJob.trace.trace('starting docker image build job! (⑅˘꒳˘)'))
.map((tEitherJob) =>
tEitherJob.get().flatMapAsync((job) =>
eitherVault.flatMapAsync(async (vault) => {
const eitherKey = await vault.unlock(tEitherJob);
return eitherKey.mapRight((key) => ({ job, key, vault }));
}),
),
)
.map(async (tEitherJobVault) => {
tEitherJobVault.trace.trace('logging into the wegistwy uwu~');
const eitherJobVault = await tEitherJobVault.get();
const eitherDockerRegistryLoginItem = await eitherJobVault.flatMapAsync(({ job, key, vault }) =>
vault
.fetchSecret<LoginItem>(tEitherJobVault, key, job.arguments.registry)
.finally(() => vault.lock(tEitherJobVault, key)),
);
return eitherDockerRegistryLoginItem.flatMapAsync(({ login }) =>
eitherJobVault.flatMapAsync(async ({ job }) => {
const loginCommand = getDockerLoginCommand(login.username, job.arguments.registry);
const eitherLoggedIn = await tEitherJobVault
.move(loginCommand)
.map((tLoginCmd) => getStdout(tLoginCmd, { env: { REGISTRY_PASSWORD: login.password } }))
.get();
return eitherLoggedIn.moveRight(job);
}),
);
})
.peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(loginMetric)))
.map(async (tEitherWithAuthdRegistryBuildJob) => {
const eitherWithAuthdRegistryBuildJob = await tEitherWithAuthdRegistryBuildJob.get();
tEitherWithAuthdRegistryBuildJob.trace.trace('finally building the image~ (◕ᴗ◕✿)');
const eitherBuiltImage = await eitherWithAuthdRegistryBuildJob.flatMapAsync((job) =>
tEitherWithAuthdRegistryBuildJob
.move(getBuildCommand(job.arguments))
.map((tBuildCmd) =>
getStdout(tBuildCmd, {
env: {},
clearEnv: true,
}),
)
.get(),
);
return eitherBuiltImage.flatMap((buildOutput) =>
eitherWithAuthdRegistryBuildJob.mapRight((job) => ({ buildOutput, job })),
);
})
.flatMapAsync(TraceUtil.promiseify(TraceUtil.traceResultingEither(buildImageMetric)))
.peek(
TraceUtil.promiseify((tBuilt) =>
tBuilt.get().fold(
(err) => tBuilt.trace.trace(`oh nyoo we couldn't buiwd the img :(( ${err}`),
(ok) => tBuilt.trace.traceScope('buildOutput').trace(ok.buildOutput),
),
),
)
.map(async (tEitherWithBuiltImage) => {
const eitherWithBuiltImage = await tEitherWithBuiltImage.get();
return eitherWithBuiltImage
.mapRight(({ job }) => tEitherWithBuiltImage.move(getPushCommand(job.arguments.imageTag)))
.flatMapAsync((tPushCommand) => getStdout(tPushCommand));
})
.get();
function getDockerLoginCommand(username: string, registry: string): Command {
return `docker login --username ${username} --password $REGISTRY_PASSWORD ${registry}`.split(' ');
}
function getBuildCommand({ buildTarget, imageTag, dockerfile, context }: BuildDockerImageJobProps): Command {
return ['docker', 'build', '--target', buildTarget, '-t', imageTag, '-f', dockerfile, context];
}
function getPushCommand(tag: string): Command {
return ['docker', 'push', tag];
}
|