diff options
author | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-13 09:50:15 -0700 |
---|---|---|
committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-13 09:50:15 -0700 |
commit | 2543ac8b11af11f034836591046cdb52911f9403 (patch) | |
tree | d2fa475348c9867aab2994e9914895a57db88d75 | |
parent | e49fda41176d025a671802be76c219d66167276f (diff) | |
download | ci-2543ac8b11af11f034836591046cdb52911f9403.tar.gz ci-2543ac8b11af11f034836591046cdb52911f9403.zip |
snapshot
-rw-r--r--[-rwxr-xr-x] | hooks/mod.ts | 274 | ||||
-rw-r--r-- | hooks/queuer.ts | 21 | ||||
-rwxr-xr-x | hooks/server.ts | 272 | ||||
-rw-r--r-- | utils/either.ts | 29 | ||||
-rw-r--r-- | utils/isObject.ts | 2 | ||||
-rw-r--r-- | utils/mod.ts | 1 | ||||
-rw-r--r-- | utils/run.ts | 11 | ||||
-rw-r--r-- | utils/trace.ts | 5 | ||||
-rw-r--r-- | utils/validate_identifier.ts | 12 | ||||
-rwxr-xr-x | worker/scripts/build_image | 13 |
10 files changed, 345 insertions, 295 deletions
diff --git a/hooks/mod.ts b/hooks/mod.ts index 9df7d67..738003c 100755..100644 --- a/hooks/mod.ts +++ b/hooks/mod.ts @@ -1,272 +1,2 @@ -#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run - -import { - getRequiredEnv, - getStdout, - invalidExecutionEntriesOf, - Traceable, - ITraceable, - ITraceableLogger, - IEither, - Either, -} from "@liz-ci/utils"; -import type { Job } from "@liz-ci/model"; - -const SERVER_CONFIG = { - host: "0.0.0.0", - port: 9000, -}; -interface IHealthCheckActivity { - healthCheck(req: Traceable<Request>): Traceable<Promise<Response>>; -} - -class HealthCheckActivity implements IHealthCheckActivity { - public healthCheck( - req: Traceable<Request>, - ) { - return req.map(async ({ logger }) => { - try { - getRequiredEnv("LAMINAR_HOST"); - await getStdout(["laminarc", "show-jobs"]); - const msg = `think im healthy!! (✿˘◡˘) ready to do work~\n`; - logger.log(msg); - return new Response( - msg, - { status: 200 }, - ); - } catch (error) { - logger.error(error); - return new Response( - "oh no, i need to eat more vegetables (。•́︿•̀。)...\n", - { status: 500 }, - ); - } - }); - } -} - -const aPost = (req: Traceable<Request>): IEither<Response, Request> => { - const {item: request, logger} = req; - const {method} = request; - if (method !== "POST") { - const msg = "that's not how you pet me (⋟﹏⋞) try post instead~"; - logger.warn(msg); - return { err: new Response(msg + "\n", {status: 405}) }; - } - return {ok: request}; -}; - -type JsonTransformer<JsonT, R> = (json: Traceable<JsonT>) => Either<string, R>; -const aJson = <BodyT, JsonT = unknown>(jsonTransformer: JsonTransformer<JsonT, BodyT>) => - (r: Traceable<Request>) => r - .map(async ({item: request, logger}): Promise<Either<string, JsonT>> => { - try { - return {ok: <JsonT>(await request.json())}; - } catch (e) { - const err = "seems to be invalid JSON (>//<) can you fix?"; - logger.warn(err); - return { err }; - } - }) - .flatMapAsync(TraceableImpl.promiseify((t): Traceable<Either<string, BodyT>> => { - const {item: {err, ok: json}} = t; - if (err) return t.map(() => ({ err })); - return t.map(() => json!).map(jsonTransformer); - })) - - -interface IJobHookActivity { - processHook(req: Traceable<Request>): Traceable<Promise<Response>>; -} -type GetJobRequest = { jobType: string; args: unknown }; -class JobHookActivityImpl implements IJobHookActivity { - constructor(private readonly queuer: IJobQueuer) {} - - private getJob( - { logger, item }: Traceable<unknown>, - ): Either<string, Job> { - const isObject = (o: unknown): o is Record<string, unknown> => - typeof o === "object" && !Array.isArray(o) && !!o; - if (!isObject(item) || !isObject(item.arguments)|| typeof item.type !== "string") { - const err = "seems like a pwetty mawfomed job \\(-.-)/"; - logger.warn(err) - return { err }; - } - - const ok = { type: item.type, arguments: item.arguments }; - const invalidArgEntries = invalidExecutionEntriesOf({type: ok.type, ...ok.arguments}); - if (invalidArgEntries.length > 0) { - const err = "your reqwest seems invawid (´。﹏。`) can you fix? uwu\n" + invalidArgEntries; - logger.warn(err); - return { err }; - } - - return { ok: <Job>ok }; - } - - public processHook(r: Traceable<Request>) { - return r.map(aPost) - .map((t) => { - either((err) => ({ err }), (request) => ) - const {item: {ok: request, err}} = t; - if (err) return <Traceable<{ err: Response }>> t; - return t.map(() => request!).map(aJson(this.getJob)); - }); -// flatMapAsync(aJsonPost(this.getJob)) -// .map(TraceableImpl.promiseify((g) => { -// if (jobRequest) { -// return g.map(() => jobRequest) -// .map(this.getJob) -// .map( -// ({ item: { ok: jobRequest, err } }) => { -// if (err) return { err: new Response(err, { status: 400 }) }; -// return { ok: jobRequest }; -// }, -// ); -// } -// return g.map(() => ({ ok: undefined, err })); -// })) -// .map(TraceableImpl.promiseify(({ item: t }) => { -// const { item: { ok: job, err } } = t; -// if (err) return t.map(() => Promise.resolve(err)); -// -// return t.map(() => job!) -// .map(this.queuer.queue) -// .map(TraceableImpl.promiseify(({ item, logger }) => { -// if (item.ok) { -// return new Response(item.ok, { status: 200 }); -// } -// logger.error(item.err); -// return new Response("i messed up D:\n", { status: 500 }); -// })); -// })); -// } -} - -class LizCIServerImpl implements ILizCIServer { - constructor( - private readonly healthCheckActivity: IHealthCheckActivity, - private readonly jobHookActivity: IJobHookActivity, - ) {} - - private route( - req: Traceable<Request & { pathname: string }>, - ): Traceable<Promise<Response>> { - return req.flatMap((req) => { - const { logger, item: { method, pathname } } = req; - if (pathname === "/health") { - return this.healthCheckActivity.healthCheck(req); - } - return this.jobHookActivity.processHook(req); - }); - } - - public async serve(req: Request): Promise<Response> { - const traceId = crypto.randomUUID(); - const { pathname } = new URL(req.url); - const traceSupplier = () => `[${traceId} <- ${req.method}'d @ ${pathname}]`; - return TraceableImpl.from(req) - .bimap(({ item: req }) => [{ req, pathname }, traceSupplier]) - .flatMap(this.route) - .map(({ item, logger }) => - item.catch((e) => { - const errorMessage = `oh noes! something went wrong (ಥ_ಥ) so sowwy!`; - logger.error(errorMessage, e); - return new Response(`${errorMessage}\n`, { status: 500 }); - }) - ) - .item; - } -} - -class JobQueue { - private readonly logger: PrefixLogger; - private readonly url: URL; - private readonly pathname: string; - - constructor(private readonly request: Request, private readonly) { - this.url = new URL(request.url); - this.pathname = this.url.pathname; - this.logger = this.createLogger(); - } - - /** - * Creates a logger with request-specific context - */ - - /** - * Performs health checks on dependent services - */ - private async performHealthCheck(): Promise<void> { - } - - /** - * Handles health check requests - */ - private async handleHealthCheck(): Promise<Response> { - try { - await this.performHealthCheck(); - } catch (error) { - } - } - - /** - * Queues a job in the laminar system - */ - private async queueJob(jobName: string, args: JobRequest): Promise<Response> { - } - - /** - * Validates job request parameters - */ - private validateJobRequest( - jobName: string, - args: unknown, - ): { valid: boolean; response?: Response } { - } - - /** - * Main method to handle the request - */ - public async handle(): Promise<Response> { - this.logger.log("go! :DDD"); - - // Handle health check requests - if (this.pathname === "/health") { - return this.handleHealthCheck(); - } - - // Validate HTTP method - if (this.request.method !== "POST") { - } - - // Extract job name from path - - if (!validation.valid) { - return validation.response!; - } - - // Queue the job - return this.queueJob(jobName, requestBody as JobRequest); - } - - /** - * Handles the entire request lifecycle, including error handling - */ - public async processRequest(): Promise<Response> { - try { - return await this.handle(); - } catch (error) { - } finally { - this.logger.log("allll done!"); - } - } -} - -/** - * Entry point - starts the server - */ -Deno.serve(SERVER_CONFIG, async (request: Request) => { - const handler = new RequestHandler(request); - return handler.processRequest(); -}); +export * from "./queuer.ts"; +export * from "./server.ts"; diff --git a/hooks/queuer.ts b/hooks/queuer.ts index 1461809..d2987ca 100644 --- a/hooks/queuer.ts +++ b/hooks/queuer.ts @@ -1,20 +1,22 @@ -import type { IEither, ITraceable, ITraceableLogger } from "@liz-ci/utils"; +import { getStdout, type IEither, type Traceable } from "@liz-ci/utils"; import type { Job } from "@liz-ci/model"; type QueuePosition = string; export class QueueError extends Error {} export interface IJobQueuer<TJob> { - queue: <L extends ITraceableLogger<L>>( - job: ITraceable<TJob, L>, + queue: ( + job: TJob, ) => Promise<IEither<QueueError, QueuePosition>>; } -export class LaminarJobQueuer implements IJobQueuer<Job> { +export class LaminarJobQueuer implements IJobQueuer<Traceable<Job>> { constructor( private readonly queuePositionPrefix: string, ) {} - public async queue({ item: job, logger }: Traceable<Job>) { + public async queue(j: Traceable<Job>) { + const { item: job, logger: _logger } = j; + const logger = _logger.addTracer(() => `[LaminarJobQueuer.queue.${job}]`); const laminarCommand = [ "laminarc", "queue", @@ -27,12 +29,7 @@ export class LaminarJobQueuer implements IJobQueuer<Job> { laminarCommand, ); - return (await getStdout(laminarCommand)).mapBoth( - (e) => { - const err = `we bwoke oh noesss D:`; - logger.error(err, e); - return Either.left<QueueError, QueuePosition>(e); - }, + return (await getStdout(j.map(() => laminarCommand))).mapRight( (stdout) => { logger.log(stdout); @@ -40,7 +37,7 @@ export class LaminarJobQueuer implements IJobQueuer<Job> { const jobUrl = `${this.queuePositionPrefix}/jobs/${jobName}/${jobId}`; logger.log(`all queued up and weady to go~ (˘ω˘) => ${jobUrl}\n`); - return Either.right<QueueError, QueuePosition>(jobUrl); + return jobUrl; }, ); } diff --git a/hooks/server.ts b/hooks/server.ts new file mode 100755 index 0000000..000c391 --- /dev/null +++ b/hooks/server.ts @@ -0,0 +1,272 @@ +#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run + +import { + Either, + getRequiredEnv, + getStdout, + IEither, + isObject, + Traceable, + validateExecutionEntries, +} from "@liz-ci/utils"; +import { IJobQueuer } from "./mod.ts"; +import type { Job } from "@liz-ci/model"; + +const SERVER_CONFIG = { + host: "0.0.0.0", + port: 9000, +}; +interface IHealthCheckActivity<R> { + healthCheck(req: R): Promise<Response>; +} + +class HealthCheckActivity implements IHealthCheckActivity<Traceable<Request>> { + public async healthCheck( + req: Traceable<Request>, + ) { + return await req.bimap(Traceable.withClassTrace(this)) + .map(async (r) => { + const { logger } = r; + try { + getRequiredEnv("LAMINAR_HOST"); + await getStdout(r.map(() => ["laminarc", "show-jobs"])); + const msg = `think im healthy!! (✿˘◡˘) ready to do work~\n`; + logger.log(msg); + return new Response( + msg, + { status: 200 }, + ); + } catch (error) { + logger.error(error); + return new Response( + "oh no, i need to eat more vegetables (。•́︿•̀。)...\n", + { status: 500 }, + ); + } + }).item; + } +} + +const aPost = (req: Traceable<Request>): IEither<Response, Request> => { + const { item: request, logger: _logger } = req; + const logger = _logger.addTracer(() => "[aPost]"); + + if (request.method !== "POST") { + const msg = "that's not how you pet me (⋟﹏⋞) try post instead~"; + logger.warn(msg); + return Either.left(new Response(msg + "\n", { status: 405 })); + } + return Either.right(request); +}; + +type JsonTransformer<JsonT, R> = (json: Traceable<JsonT>) => Either<string, R>; +const aJson = + <BodyT, JsonT = unknown>(jsonTransformer: JsonTransformer<JsonT, BodyT>) => + async (r: Traceable<Request>): Promise<Either<string, BodyT>> => { + const { item: request, logger } = r; + try { + return Either.right<string, JsonT>(await request.json()) + .mapRight(r.move) + .flatMap(jsonTransformer); + } catch (_e) { + const err = "seems to be invalid JSON (>//<) can you fix?"; + logger.warn(err); + return Either.left(err); + } + }; + +interface IJobHookActivity<R> { + processHook(req: R): Promise<Response>; +} +type GetJobRequest = { jobType: string; args: unknown }; +class JobHookActivityImpl implements IJobHookActivity<Traceable<Request>> { + constructor(private readonly queuer: IJobQueuer<Traceable<Job>>) {} + + private getJob<JsonT>( + u: Traceable<JsonT>, + ): Either<string, Job> { + const { logger: _logger, item } = u; + const logger = _logger.addTracer(() => "[getJob]"); + const couldBeJsonJob = isObject(item) && "arguments" in item && + "type" in item && item; + const couldBeArguments = couldBeJsonJob && isObject(item.arguments) && + item.arguments; + if (!couldBeJsonJob) { + const err = "seems like a pwetty mawfomed job \\(-.-)/"; + logger.warn(err); + return Either.left(err); + } + + return validateExecutionEntries({ + type: item.type, + ...couldBeArguments, + }).mapBoth((err) => { + const e = "your reqwest seems invawid (´。﹏。`) can you fix? uwu\n" + + err.toString(); + logger.warn(e); + return e; + }, (_ok) => <Job> item); + } + + public async processHook(r: Traceable<Request>) { + return await r.bimap(Traceable.withClassTrace(this)).map(aPost) + .map(aJson(this.getJob)); + } + // flatMapAsync(aJsonPost(this.getJob)) + // .map(TraceableImpl.promiseify((g) => { + // if (jobRequest) { + // return g.map(() => jobRequest) + // .map(this.getJob) + // .map( + // ({ item: { ok: jobRequest, err } }) => { + // if (err) return { err: new Response(err, { status: 400 }) }; + // return { ok: jobRequest }; + // }, + // ); + // } + // return g.map(() => ({ ok: undefined, err })); + // })) + // .map(TraceableImpl.promiseify(({ item: t }) => { + // const { item: { ok: job, err } } = t; + // if (err) return t.map(() => Promise.resolve(err)); + // + // return t.map(() => job!) + // .map(this.queuer.queue) + // .map(TraceableImpl.promiseify(({ item, logger }) => { + // if (item.ok) { + // return new Response(item.ok, { status: 200 }); + // } + // logger.error(item.err); + // return new Response("i messed up D:\n", { status: 500 }); + // })); + // })); + // } +} + +class LizCIServerImpl implements ILizCIServer { + constructor( + private readonly healthCheckActivity: IHealthCheckActivity, + private readonly jobHookActivity: IJobHookActivity, + ) {} + + private route( + req: Traceable<Request & { pathname: string }>, + ): Traceable<Promise<Response>> { + return req.flatMap((req) => { + const { logger, item: { method, pathname } } = req; + if (pathname === "/health") { + return this.healthCheckActivity.healthCheck(req); + } + return this.jobHookActivity.processHook(req); + }); + } + + public async serve(req: Request): Promise<Response> { + const traceId = crypto.randomUUID(); + const { pathname } = new URL(req.url); + const traceSupplier = () => `[${traceId} <- ${req.method}'d @ ${pathname}]`; + return TraceableImpl.from(req) + .bimap(({ item: req }) => [{ req, pathname }, traceSupplier]) + .flatMap(this.route) + .map(({ item, logger }) => + item.catch((e) => { + const errorMessage = `oh noes! something went wrong (ಥ_ಥ) so sowwy!`; + logger.error(errorMessage, e); + return new Response(`${errorMessage}\n`, { status: 500 }); + }) + ) + .item; + } +} + +class JobQueue { + private readonly logger: PrefixLogger; + private readonly url: URL; + private readonly pathname: string; + + constructor(private readonly request: Request, private readonly) { + this.url = new URL(request.url); + this.pathname = this.url.pathname; + this.logger = this.createLogger(); + } + + /** + * Creates a logger with request-specific context + */ + + /** + * Performs health checks on dependent services + */ + private async performHealthCheck(): Promise<void> { + } + + /** + * Handles health check requests + */ + private async handleHealthCheck(): Promise<Response> { + try { + await this.performHealthCheck(); + } catch (error) { + } + } + + /** + * Queues a job in the laminar system + */ + private async queueJob(jobName: string, args: JobRequest): Promise<Response> { + } + + /** + * Validates job request parameters + */ + private validateJobRequest( + jobName: string, + args: unknown, + ): { valid: boolean; response?: Response } { + } + + /** + * Main method to handle the request + */ + public async handle(): Promise<Response> { + this.logger.log("go! :DDD"); + + // Handle health check requests + if (this.pathname === "/health") { + return this.handleHealthCheck(); + } + + // Validate HTTP method + if (this.request.method !== "POST") { + } + + // Extract job name from path + + if (!validation.valid) { + return validation.response!; + } + + // Queue the job + return this.queueJob(jobName, requestBody as JobRequest); + } + + /** + * Handles the entire request lifecycle, including error handling + */ + public async processRequest(): Promise<Response> { + try { + return await this.handle(); + } catch (error) { + } finally { + this.logger.log("allll done!"); + } + } +} + +/** + * Entry point - starts the server + */ +Deno.serve(SERVER_CONFIG, async (request: Request) => { + const handler = new RequestHandler(request); + return handler.processRequest(); +}); diff --git a/utils/either.ts b/utils/either.ts index d21c796..10e4f43 100644 --- a/utils/either.ts +++ b/utils/either.ts @@ -5,14 +5,37 @@ export interface IEither<E, T> { errBranch: (e: E) => Ee, okBranch: (o: T) => Tt, ) => IEither<Ee, Tt>; + mapRight: <Tt>(mapper: (t: T) => Tt) => Either<E, Tt>; + mapLeft: <Ee>(mapper: (e: E) => Ee) => Either<Ee, T>; + flatMap: <Ee extends E, Tt>( + mapper: (e: T) => Either<Ee, Tt>, + ) => Either<Ee, Tt>; } export class Either<E, T> implements IEither<E, T> { private constructor(readonly err?: E, readonly ok?: T) {} - public mapBoth<Ee, Tt>(errBranch: (e: E) => Ee, okBranch: (t: T) => Tt) { - if (this.err) return new Either<Ee, Tt>(errBranch(this.err)); - return new Either<Ee, Tt>(undefined, okBranch(this.ok!)); + public mapBoth<Ee, Tt>( + errBranch: (e: E) => Ee, + okBranch: (t: T) => Tt, + ): Either<Ee, Tt> { + if (this.err) return Either.left(errBranch(this.err)); + return Either.right(okBranch(this.ok!)); + } + + public flatMap<Ee extends E, Tt>(mapper: (t: T) => Either<Ee, Tt>) { + if (this.ok) return mapper(this.ok); + return this; + } + + public mapRight<Tt>(mapper: (t: T) => Tt): Either<E, Tt> { + if (this.ok) return Either.right(mapper(this.ok)); + return Either.left(this.err!); + } + + public mapLeft<Ee>(mapper: (e: E) => Ee): Either<Ee, T> { + if (this.err) return Either.left(mapper(this.err)); + return Either.right(this.ok!); } static left<E, T>(e: E) { diff --git a/utils/isObject.ts b/utils/isObject.ts new file mode 100644 index 0000000..73f7f80 --- /dev/null +++ b/utils/isObject.ts @@ -0,0 +1,2 @@ +export const isObject = (o: unknown): o is object => + typeof o === "object" && !Array.isArray(o) && !!o; diff --git a/utils/mod.ts b/utils/mod.ts index 53ea173..d8cb526 100644 --- a/utils/mod.ts +++ b/utils/mod.ts @@ -1,3 +1,4 @@ +export * from "./isObject.ts"; export * from "./trace.ts"; export * from "./either.ts"; export * from "./env.ts"; diff --git a/utils/run.ts b/utils/run.ts index 06e7d9f..9093863 100644 --- a/utils/run.ts +++ b/utils/run.ts @@ -1,10 +1,13 @@ -import { Either } from "./mod.ts"; +import { Either, type Traceable } from "./mod.ts"; export class ProcessError extends Error {} export const getStdout = async ( - cmd: string[] | string, + { item: cmd, logger: _logger }: Traceable<string[] | string>, options: Deno.CommandOptions = {}, ): Promise<Either<ProcessError, string>> => { + const logger = _logger.addTracer(() => "[getStdout]"); + + logger.log(`:> im gonna run this command!`, cmd); const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; const command = new Deno.Command(exec, { args, @@ -19,12 +22,16 @@ export const getStdout = async ( const stderrText = new TextDecoder().decode(stderr); if (code !== 0) { + logger.error(`i weceived an exit code of ${code} i wanna zeroooo :<`); return Either.left<ProcessError, string>( new ProcessError(`command failed\n${stderrText}`), ); } + + logger.log("yay! i got code 0 :3", cmd); return Either.right<ProcessError, string>(stdoutText); } catch (e) { + logger.error(`o.o wat`, e); if (e instanceof Error) { return Either.left<ProcessError, string>(e); } diff --git a/utils/trace.ts b/utils/trace.ts index 373f37e..1a5e51d 100644 --- a/utils/trace.ts +++ b/utils/trace.ts @@ -31,6 +31,7 @@ export interface ITraceable<T, L extends ITraceableLogger<L>> { flatMapAsync<U>( mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>, ): ITraceable<Promise<U>, L>; + move<Tt>(t: Tt): ITraceable<Tt, L>; } export class TraceableLogger implements ITraceableLogger<TraceableLogger> { @@ -96,6 +97,10 @@ class TraceableImpl< return this; } + public move<Tt>(t: Tt) { + return this.map(() => t); + } + public bimap<U>(mapper: ITraceableMapper<T, L, ITraceableTuple<U>>) { const [item, trace] = mapper(this); return new TraceableImpl(item, this.logger.addTracer(trace)); diff --git a/utils/validate_identifier.ts b/utils/validate_identifier.ts index e2a1dc5..ec8b77b 100644 --- a/utils/validate_identifier.ts +++ b/utils/validate_identifier.ts @@ -1,11 +1,17 @@ +import { Either } from "./mod.ts"; + export const validateIdentifier = (token: string) => { return (/^[a-zA-Z0-9_\-:. \/]+$/).test(token) && !token.includes(".."); }; -export const invalidExecutionEntriesOf = ( +// ensure {@param obj} is a Record<string, string> with stuff that won't +// have the potential for shell injection, just to be super safe. +export const validateExecutionEntries = ( obj: Record<string, unknown>, -): Array<[string, unknown]> => { - return Object.entries(obj).filter((e) => +): Either<Array<[string, unknown]>, Record<string, string>> => { + const invalidEntries = Object.entries(obj).filter((e) => !e.every((x) => typeof x === "string" && validateIdentifier(x)) ); + if (invalidEntries.length > 0) return Either.left(invalidEntries); + return Either.right(<Record<string, string>> obj); }; diff --git a/worker/scripts/build_image b/worker/scripts/build_image index a4dcdf4..6618af6 100755 --- a/worker/scripts/build_image +++ b/worker/scripts/build_image @@ -27,7 +27,9 @@ const logger = loggerWithPrefix(() => ); const run = async () => { - logger.log("starting docker image build job~ (⑅˘꒳˘) let's make something cute!"); + logger.log( + "starting docker image build job~ (⑅˘꒳˘) let's make something cute!", + ); const bitwardenSession = new BitwardenSession(); const { username: registryUsername, password: registryPassword } = @@ -44,9 +46,14 @@ const run = async () => { "--username", registryUsername, "--password", - registryPassword, + "$DOCKER_PASSWORD", args.registry, ], + { + env: { + DOCKER_PASSWORD: registryPassword, + }, + }, ); const tag = @@ -77,7 +84,7 @@ const run = async () => { "push", tag, ]; - logger.log(`sending image to registry~ (>ᴗ<)`, pushCmd); + logger.log(`sending image to wegistwy~ (>ᴗ<)`, pushCmd); await getStdout(pushCmd); }; |