import { GetSecretValueCommand, SecretsManagerClient, UpdateSecretCommand } from '@aws-sdk/client-secrets-manager'; export interface StepFunctionLambdaInput { readonly owner: string; readonly repo: string; readonly runnerName: string; readonly installationId?: number; } /** * Error for problems that can only be fixed by changing the configuration, either in CDK or on GitHub. They all share * one error name so the step function immediately shows this isn't capacity or a flaky API, and the message says what * to fix. We still retry them, so a configuration fixed within the 24 hours GitHub queues a job still gets a runner. * * @internal */ export class RunnerConfigurationError extends Error { constructor(msg: string) { super(msg); this.name = 'RunnerConfigurationError'; Object.setPrototypeOf(this, RunnerConfigurationError.prototype); } } const sm = new SecretsManagerClient(); export async function getSecretValue(arn: string | undefined) { if (!arn) { throw new Error('Missing secret ARN. Check the Lambda configuration and required environment variables.'); } const secret = await sm.send(new GetSecretValueCommand({ SecretId: arn })); if (!secret.SecretString) { throw new Error('Secrets Manager getSecretValue returned no SecretString. This often indicates that the secret was stored as binary data (SecretBinary) instead of a string. Ensure the secret is stored in SecretString or update the code to handle SecretBinary.'); } return secret.SecretString; } export async function getSecretJsonValue(arn: string | undefined) { return JSON.parse(await getSecretValue(arn)); } export async function updateSecretValue(arn: string | undefined, value: string) { if (!arn) { throw new Error('Missing secret ARN. Check the Lambda configuration and required environment variables.'); } await sm.send(new UpdateSecretCommand({ SecretId: arn, SecretString: value })); } export async function customResourceRespond(event: AWSLambda.CloudFormationCustomResourceEvent, responseStatus: string, reason: string, physicalResourceId: string, data: any) { const responseBody = JSON.stringify({ Status: responseStatus, Reason: reason, PhysicalResourceId: physicalResourceId, StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, NoEcho: false, Data: data, }); console.log({ notice: 'Responding to CloudFormation custom resource', status: responseStatus, reason, physicalResourceId, responseBody, }); // eslint-disable-next-line @typescript-eslint/no-require-imports const parsedUrl = require('url').parse(event.ResponseURL); const requestOptions = { hostname: parsedUrl.hostname, path: parsedUrl.path, method: 'PUT', headers: { 'content-type': '', 'content-length': responseBody.length, }, }; return new Promise((resolve, reject) => { try { // eslint-disable-next-line @typescript-eslint/no-require-imports const request = require('https').request(requestOptions, resolve); request.on('error', reject); request.write(responseBody); request.end(); } catch (e) { reject(e); } }); }