やりたいこと
Amplify のアプリを組織管理用と各組織用の 2 種類作成して、組織管理用のアプリから組織を追加したときに、各組織用 Amplify のブランチを作成するということをやりたい。
前提
- 各組織用 Amplify のソースコードは CodeCommit に push 済み
- 各組織用 Amplify も一度は push 済みで Build できることを確認済み
Lambda でやること
- Amplify の BackendEnviroment 作成
-
CreateBackendEnvironmentCommand
- 定義ができるだけ。実体は CodeCommit の Branch 作成時に作成される
- Gen 2 では要らなくなるかも
-
CreateBackendEnvironmentCommand
- Amplify の Branch 作成
-
CreateBranchCommand
- これも定義ができるだけ。実体は CodeCommit の Branch 作成時に作成
-
CreateBranchCommand
- CodeCommit の Branch 作成
-
CreateBranchCommand
- デプロイが実行されて Amplify 環境ができていく
-
CreateBranchCommand
- Amplify Job の待ち合わせ
-
GetJobCommand
- 定期的に実行して、環境ができたか確認する
-
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 の削除
削除の時は、DeleteBranchCommand と DeleteBackendCommand を使えばできるはず。