1
0

Amplify (Gen 1) で Lambda から Amplify の Branch を追加する

Posted at

やりたいこと

Amplify のアプリを組織管理用と各組織用の 2 種類作成して、組織管理用のアプリから組織を追加したときに、各組織用 Amplify のブランチを作成するということをやりたい。

image.png

前提

  • 各組織用 Amplify のソースコードは CodeCommit に push 済み
  • 各組織用 Amplify も一度は push 済みで Build できることを確認済み

Lambda でやること

  1. Amplify の BackendEnviroment 作成
    • CreateBackendEnvironmentCommand
      • 定義ができるだけ。実体は CodeCommit の Branch 作成時に作成される
      • Gen 2 では要らなくなるかも
  2. Amplify の Branch 作成
    • CreateBranchCommand
      • これも定義ができるだけ。実体は CodeCommit の Branch 作成時に作成
  3. CodeCommit の Branch 作成
  4. Amplify Job の待ち合わせ
    • GetJobCommand
      • 定期的に実行して、環境ができたか確認する

作成に時間がかかる場合は Step Functions にすれば良いです。

コード

環境変数として以下を定義します。

  • PROJECT_REPOSITORY: CodeCommit に作成した Amplify の Repository 名
  • AMPLIFY_APP_ID: 作成する Amplify の AppId
import {
  AmplifyClient,
  ListJobsCommand,
  type ListJobsCommandOutput,
  CreateBranchCommand as CreateAmplifyBranchCommand,
  type CreateBranchCommandOutput as CreateAmplifyBranchCommandOutput,
  CreateBackendEnvironmentCommand,
  type CreateBackendEnvironmentCommandOutput,
  type GetJobCommandOutput,
  GetJobCommand,
} from '@aws-sdk/client-amplify';
import {
  CodeCommitClient,
  GetBranchCommand as GetCodecommitBranchCommand,
  type GetBranchCommandOutput as GetCodecommitBranchCommandOutput,
  CreateBranchCommand as CreateCodecommitBranchCommand,
  type CreateBranchCommandOutput as CreateCodecommitBranchCommandOutput,
} from '@aws-sdk/client-codecommit';
import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';

import { type Handler, type Context } from 'aws-lambda';
 B
type Event = {
  typeName: string;
  fieldName: string;
  arguments: {
    srcBranchName: string;  // ベースとなる CodeCommit のBranch 名
    newBranchName: string;  // 新しく作る Branch 名
  };
};

type Output = {
  jobId?: string;      // Amplify 作成 Job の ID
  jobStatus?: string;  // Amplify 作成 Job の Status
};

const amplifyClient = new AmplifyClient();
const codecommitClient = new CodeCommitClient();

/* Amplify BackendEnviroment 作成 */
const CreateBackendEnvironment = async (
  appId: string,
  environmentName: string,
): Promise<CreateBackendEnvironmentCommandOutput> => {
  const command = new CreateBackendEnvironmentCommand({
    appId,
    environmentName,
  });

  return await amplifyClient.send(command);
};

/* Amplify Branch 作成 */
const CreateAmplifyBranch = async (
  appId: string,
  branchName: string,
  backendEnvironmentArn: string,
): Promise<CreateAmplifyBranchCommandOutput> => {
  const command = new CreateAmplifyBranchCommand({
    appId,
    backendEnvironmentArn,
    branchName,
  });

  return await amplifyClient.send(command);
};

/* Amplify Job 一覧取得 */
const ListJobs = async (
  appId: string,
  branchName: string,
): Promise<ListJobsCommandOutput> => {
  const command = new ListJobsCommand({
    appId,
    branchName,
  });

  return await amplifyClient.send(command);
};

/* Amplify Job 情報を取得 */
const getJob = async (
  appId: string,
  branchName: string,
  jobId: string,
): Promise<GetJobCommandOutput> => {
  const command = new GetJobCommand({
    appId,
    branchName,
    jobId,
  });
  
  return await amplifyClient.send(command);
};

/* CodeCommit Branch 取得 */
const GetCodecommitBranch = async (
  repositoryName: string,
  branchName: string,
): Promise<GetCodecommitBranchCommandOutput> => {
  const command = new GetCodecommitBranchCommand({
    repositoryName,
    branchName,
  });

  return await codecommitClient.send(command);
};

/* CodeCommit Branch 作成 */
const CreateCodecommitBranch = async (
  repositoryName: string,
  branchName: string,
  commitId: string,
): Promise<CreateCodecommitBranchCommandOutput> => {
  const command = new CreateCodecommitBranchCommand({
    repositoryName,
    branchName,
    commitId,
  });

  return await codecommitClient.send(command);
};

/* 待ち */
const sleep = async (ms: number) => {
  return await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
};

export const handler: Handler = async (
  event: Event,
  context: Context,
): Promise<Output | undefined> => {
  console.log(`EVENT: ${JSON.stringify(event)}`);

  const repositoryName = process.env.PROJECT_REPOSITORY;
  const appId = process.env.AMPLIFY_APP_ID;
  if (repositoryName == null) {
    throw new Error('PROJECT_REPOSITORY undefined.');
  }
  if (appId == null) {
    throw new Error('AMPLIFY_APP_ID undefined.');
  }

  /* CodeCommit 元 Branch Commit ID 取得 */
  const codecommitBranch = await GetCodecommitBranch(
    repositoryName,
    event.arguments.srcBranchName,
  );
  console.log(codecommitBranch);
  if (codecommitBranch?.branch?.commitId == null) {
    throw new Error(`Not found CodeCommit branch ${event.arguments.srcBranchName}.`);
  }

  /* Amplify Backend Environment 作成 */
  const createBackendEnvironmentResponse = await CreateBackendEnvironment(
    appId,
    event.arguments.newBranchName,
  );
  console.log(createBackendEnvironmentResponse);

  const backendEnvironmentArn =
    createBackendEnvironmentResponse?.backendEnvironment?.backendEnvironmentArn;

  if (backendEnvironmentArn == null) {
    throw new Error(`Faild to create Amplify BackendEnvironment ${event.arguments.newBranchName}.`);
  }

  /* Amplify Branch 作成 */
  const createAmplifyBranchResponse = await CreateAmplifyBranch(
    appId,
    event.arguments.newBranchName,
    backendEnvironmentArn,
  );
  console.log(createAmplifyBranchResponse);

  /* CodeCommit Branch 作成 */
  const createCodecommitBranchResponse = await CreateCodecommitBranch(
    repositoryName,
    event.arguments.newBranchName,
    codecommitBranch.branch.commitId,
  );
  console.log(createCodecommitBranchResponse);

  /* Amplify 構築 Job ができるのを待ち合わせる */
  let jobId;
  while (jobId == null) {
    await sleep(1_000);
    const listJobsResponse = await ListJobs(appId, event.arguments.newBranchName);
    console.log(listJobsResponse);

    jobId = listJobsResponse.jobSummaries?.[0].jobId;
  }

  /* Amplify 構築終了を待ち合わせる */
  let jobStatus;
  while (jobStatus == null || ['PENDING', 'PROVISIONING', 'RUNNING'].includes(jobStatus)) {
  
    /* Lambda の残り時間が 10 秒切ったら諦める */
    if (context.getRemainingTimeInMillis() < 10_000) {
      console.log('Remaining time: ', context.getRemainingTimeInMillis());

      break;
    }

    await sleep(1_000);
    const getJobResponse = await getJob(
      appId,
      event.arguments.newBranchName,
      jobId,
    );
    console.log(getJobResponse);
    
    jobStatus = getJobResponse?.job?.summary?.status;
  }

  // 作成失敗
  if (jobStatus !== 'SUCCEED') {
    throw new Error(`Faild to create Amplify Branch ${event.arguments.newBranchName}.`);
  }

  return {
    jobId,
    jobStatus,
  };
};

Amplify Branch の削除

削除の時は、DeleteBranchCommandDeleteBackendCommand を使えばできるはず。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0