はじめに
- CodeArtifact上のMavenプライベートリポジトリに対して、GitHubへのPushにより、JARファイルの公開が可能なパイプラインを今回構築しました。
- アーキテクチャの紹介とインフラコードの紹介を行います。
構築したもの
- インフラ構成(AWS CDK)とアプリケーションコード(Gradleプロジェクト)をセットでGitHub上のリポジトリで保持しています。
- リポジトリはこちら
- リポジトリの
main
ブランチにソースコードがプッシュされると、パイプラインが動いてJARがCodeArtifact上のリポジトリにpublish
されます。 - 今回構築している、CodeArtifact・Codestar connections・CodePipeline・CodeBuildの4つのリソースにより、上記の機能が実現されています。
CodeArtifact
フルマネージドのアーティファクトリポジトリサービスである AWS CodeArtifact を使用すると、組織はアプリケーション開発に使用するソフトウェアパッケージを安全に保存および共有できます。CodeArtifactは、NuGet CLI、Maven、Gradle、npm、yarn、pip、twine などの一般的なビルドツールやパッケージマネージャーで使用できます。
- 普段だとローカルのPCで保持するようなパッケージ(npmやMavenなど)を、共有のパッケージリポジトリとして公開する事が可能です。
- リポジトリのエンドポイントを指定する事で、リポジトリに対してパッケージの公開や、リポジトリ内のパッケージの利用が可能です。
- ドメインと呼ばれるグループの中に、パッケージを保持するリポジトリ作成する形です。
- 使用例として、チーム開発や個人開発を想定し、プライベートなJarライブラリとしての利用する事が可能です。
今回構築したCodeArtifactのリポジトリ
-
yoyoyo-pg
ドメインの中にgradle-publish-sample
リポジトリを用意しています。 - 今回構築しているJAR公開のパイプラインを通して、こちらのリポジトリにJARが公開されます。
Codestar connections
デベロッパーツールコンソールの接続昨日を使用して、AWS CodePipeline などの AWS リソースを外部コードリポジトリに接続します。
・・・
例えば、CodePipeline で接続を追加して、サードパーティーのコードリポジトリでコードが変更されたときにパイプラインをトリガーできるようになります。
- サードパーティーのコードリポジトリとの接続設定を行う事が出来ます。
- コードリポジトリ内の変更があった際に、連携しているCodePipelineをトリガー出来るようになります。
今回構築した接続設定
- 今回は
yoyoyo-pg-connection
という名前で接続設定を作成しています。 - こちらの接続設定を用いて、GitHubリポジトリ上のソースコードをCodePipelineで取得しています。
- GitHubの場合は、一度接続設定を作成した後に「GitHub側」で許可の操作をする必要があります。
- この操作については後の構築手順で解説します。
CodePipeline
AWS CodePipeline は、ソフトウェアをリリースするために必要なステップのモデル化、視覚化、および自動化に使用できる継続的な配信サービスです。
- ソースの取得、プロジェクトのビルドやテスト、実行環境へのデプロイを取りまとめる事が可能です。
- 今回利用するCodeBuildや、他にもCodeDeployなどAWSマネージドのサービスと連携する事が可能です。
今回構築したパイプライン
- GitHubからのソース取得を
Source
ステージで行い、CodeBuildの呼び出しをBuild
ステージで行います。 -
Source
ステージ- 先程の接続設定を用いて、GitHubからソースコードの取得を行います。
-
Build
ステージ- 後述するCodeBuildを呼び出し、Gradleプロジェクトとして構成されているソースコードのビルドとJARの公開処理を行います。
CodeBuild
AWS CodeBuild はクラウドで動作する、完全マネージド型のビルドサービスです。CodeBuild はソースコードをコンパイルし、単体テストを実行して、すぐにデプロイできるアーティファクトを生成します。
- サーバーの用意が必要なく、オンデマンドかつビルドスクリプトさえ用意出来ればすぐに使える点が利点とされています。
-
Buildspec
を用意する事で、そこに記載のあるコマンドをCodeBuild上で実行してくれます。
今回構築したビルド処理
- CodeBuildとしては主に以下二つの処理をしています。
- CodeArtifactのトークンを取得
- その後CodeArtifactに対してJARを公開
-
Buildspec
の内容は以下です。
version: 0.2
phases:
install:
runtime-versions:
java: corretto17
pre_build:
on-failure: ABORT
commands:
- export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain yoyoyo-pg --domain-owner $AWS_ACCOUNT_ID --region ap-northeast-1 --query authorizationToken --output text`
- export CODEARTIFACT_REPO_URL=$CODEARTIFACT_REPO_URL
build:
on-failure: ABORT
commands:
- cd $CODEBUILD_SRC_DIR/gradle-sample
- gradle publishAllPublicationsToMavenRepository
- CodeBuildの他の構築内容については後述します。
CDKリポジトリの紹介
上記構成についての初期構築、初期パイプラインの起動時の手順を紹介する前に、今回用意したCDKコードの紹介を進めていきます。
ディレクトリの構成
- 先にも紹介した通り、インフラ構成(AWS CDK)とアプリケーションコード(Gradleプロジェクト)をセットでGitHub上のリポジトリで保持しています。
- 通常のAWS CDKのプロジェクトに加え、
gradle-sample
配下がGradleプロジェクトとなっております。
CDKのスタックとコンストラクトの構成
-
GradlePublishSampleStack
の中で、今回構築する4つのリソースをカスタムコンストラクトとして用意しています。
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { GradlePublishSampleStack } from '../lib/gradle-publish-sample-stack';
const app = new cdk.App();
new GradlePublishSampleStack(app, 'GradlePublishSampleStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
- カスタムコンストラクトは
CodeStar
、CodeArtifact
、CodeBuild
、CodePipeline
の4つです。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Codebuild } from './codebuild';
import { CodePipeline } from './codepipeline';
import { CodeArtifact } from './codeartifact';
import { CodeStar } from './codestar';
export class GradlePublishSampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id);
// Connection
const codeStar = new CodeStar(this, 'CodeStar', {})
// CodeArtifact
new CodeArtifact(this, 'CodeArtifact', {});
// CodeBuild
const codeBuild = new Codebuild(this, 'CodeBuild', {});
// CodePipeline
new CodePipeline(this, 'CodePipeline', {
buildProject: codeBuild.buildProject,
CfnConnection : codeStar.CfnConnection
});
}
}
各コンストラクトの記載
codestar.ts
- L1コンストラクト
CfnConnection
を利用し、接続設定を作成しています。 - 接続設定としてCodePipelineで利用する為、
CfnConnection
として外部から参照できるようにしています。
// 外部からの参照用リソース
readonly CfnConnection : CfnConnection
...
this.CfnConnection = codeStarConnection;
import { StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { CfnConnection } from 'aws-cdk-lib/aws-codestarconnections';
/** CodeStar */
export class CodeStar extends Construct {
// 外部からの参照用リソース
readonly CfnConnection : CfnConnection
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id);
// Resources
const codeStarConnection = new CfnConnection(this, 'Connection', {
connectionName: 'yoyoyo-pg-connection',
providerType: 'GitHub',
});
this.CfnConnection = codeStarConnection;
}
}
codeartifact.ts
- L1コンストラクト
CfnDomain
とCfnRepository
を利用し、CodeArtifactのドメインとリポジトリを定義しています。
import * as codeartifact from 'aws-cdk-lib/aws-codeartifact';
import { Construct } from "constructs";
import { StackProps } from 'aws-cdk-lib';
/** CodeArtifact */
export class CodeArtifact extends Construct {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id);
// CodeArtifact domain
const codeArtifactDomain = new codeartifact.CfnDomain(this, 'Domain', {domainName: 'yoyoyo-pg'});
// Maven Repo
const MavenPrivateRepo = new codeartifact.CfnRepository(this, 'GradleSample', {
repositoryName: 'gradle-publish-sample',
description: 'gradle publish repository',
domainName: codeArtifactDomain.attrName,
});
}
}
codebuild.ts
- L2コンストラクト
PipelineProject
を利用し、CodeBuildのビルドプロジェクトを定義しています。 - CodeArtifactに対するトークン発行と公開も行う為、実行IAMロールに対する権限付与も
attachInlinePolicy
で定義しています。
buildRole.attachInlinePolicy(codeArtifactAccessPolicy);
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import { Construct } from "constructs";
import { IProject } from 'aws-cdk-lib/aws-codebuild';
import { Policy, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { buildSpecObject } from './buildspec';
import { StackProps } from 'aws-cdk-lib';
/** Codebuild */
export class Codebuild extends Construct {
// 外部からの参照用リソース
readonly buildProject : IProject
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id);
// Role
const buildRole = new Role(this, 'Role', { assumedBy: new ServicePrincipal('codebuild.amazonaws.com') });
// BuildProject
const buildProject = new codebuild.PipelineProject(this, 'CodeBuild', {
buildSpec: codebuild.BuildSpec.fromObjectToYaml(buildSpecObject),
role: buildRole,
environment: {
buildImage: codebuild.LinuxBuildImage.fromCodeBuildImageId('aws/codebuild/amazonlinux2-x86_64-standard:4.0'),
privileged: false,
environmentVariables: {
AWS_ACCOUNT_ID: {
value: process.env.CDK_DEFAULT_ACCOUNT
},
CODEARTIFACT_REPO_URL: {
value: `https://yoyoyo-pg-${process.env.CDK_DEFAULT_ACCOUNT}.d.codeartifact.ap-northeast-1.amazonaws.com/maven/gradle-publish-sample/`
}
}
},
description: 'gradle-sample'
});
// CodeArtifactAccessPolicy
const codeArtifactAccessPolicy = new Policy(this, 'CodeArtifactAccessPolicy', {
policyName: 'codeArtifactAccessPolicy',
statements: [
new PolicyStatement({
actions: [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoin",
"codeartifact:ReadFromRepository",
"codeartifact:PublishPackageVersion",
"codeartifact:PutPackageMetadata",
],
resources: ["*"]
}),
new PolicyStatement({
actions: ["sts:GetServiceBearerToken"],
resources: ["*"],
conditions: {
'StringEquals': {
'sts:AWSServiceName': 'codeartifact.amazonaws.com',
},
},
})
]
});
buildRole.attachInlinePolicy(codeArtifactAccessPolicy);
this.buildProject = buildProject;
}
}
codepipeline.ts
- L2コンストラクト
Pipeline
を利用し、接続設定を利用する事でGitHubからのソース取得を可能としているSource
ステージと、CodeBuildを呼び出す為のBuild
ステージを定義しています。 -
props.CfnConnection.attrConnectionArn
という形で接続設定のArnを取得する事で、ソースコード上にAWSアカウントIDをべた書きしなくて良いようにしています。
import { Construct } from "constructs";
import { CfnConnection } from 'aws-cdk-lib/aws-codestarconnections';
import { CodeStarConnectionsSourceAction, CodeBuildAction, CodeBuildActionType } from 'aws-cdk-lib/aws-codepipeline-actions';
import { IProject } from 'aws-cdk-lib/aws-codebuild';
import { Artifact, Pipeline } from "aws-cdk-lib/aws-codepipeline";
/** 入力インターフェース */
export interface constructProps {
buildProject : IProject;
CfnConnection : CfnConnection
}
/** Codepipeline */
export class CodePipeline extends Construct {
constructor(scope: Construct, id: string, props: constructProps) {
super(scope, id);
// アーティファクト定義
const sourceOutput = new Artifact(); // SourceAction
const buildOutput = new Artifact(); // BuildAction
// CodePipeline
new Pipeline(this, 'Pipeline', {
crossAccountKeys: false,
stages: [
{
stageName: 'Source',
actions: [
new CodeStarConnectionsSourceAction({
actionName: 'Source',
owner: 'yoyoyo-pg',
repo: 'gradle-publish-sample',
branch: 'main',
output: sourceOutput,
connectionArn: props.CfnConnection.attrConnectionArn,
triggerOnPush: true,
}),
],
},
{
stageName: 'Build',
actions: [
new CodeBuildAction({
actionName: 'Build',
input: sourceOutput,
project: props.buildProject,
type: CodeBuildActionType.BUILD,
outputs: [buildOutput]
}),
],
}
]
});
}
}
Gradleプロジェクト内の設定内容
Gradleプロジェクトのディレクトリ
-
lib
配下のbuild.gradle
が、Gradleでのビルドに関する主なファイルとなっています。 -
lib/src
配下にJavaクラスファイルが記載されています。
CodeBuild側からの呼び出し内容
- CodeBuildの
Buildspec
では、以下の形でディレクトリgradle-sample
へと移動し、JAR公開のコマンドpublishAllPublicationsToMavenRepository
を実行しています。
commands:
- cd $CODEBUILD_SRC_DIR/gradle-sample
- gradle publishAllPublicationsToMavenRepository
build.gradleの設定
CodeArtifactへの公開処理が行われる際の設定として用いられるbuild.gradle
について紹介します。
-
publications
では、Mavenリポジトリに対する公開処理なので、groupId
、artifactId
、version
を指定しています。- この内容で公開されるので、逆にこのJARを利用する場合にも同じ指定をする必要があります。
-
repositories
では、対象のCodeArtifactのURLとトークンを指定しています。- どちらもCodeBuild上から値を取得できるような工夫をしています。
publishing {
// Used publishToMavenLocal and publish to CodeArtifact
publications {
maven(MavenPublication) {
groupId = 'com.sample.yoyoyo-pg'
artifactId = 'jarSample'
version = '1.0.0-SNAPSHOT'
from components.java
}
}
// Used only publish to CodeArtifact
repositories {
maven {
url System.env.CODEARTIFACT_REPO_URL
credentials {
username "aws"
password System.env.CODEARTIFACT_AUTH_TOKEN
}
}
}
}
リソースの構築とJARの公開手順
いよいよ上記リポジトリを用いたリソースの構築に入ります。
リポジトリの詳細については、GitHub上のパブリックリポジトリとして公開しています。
CDKのスタックデプロイ
- CDKのスタックデプロイとして、
cdk deploy GradlePublishSampleStack
を実行します。
PS C:\Users\git\gradle-publish-sample> cdk deploy GradlePublishSampleStack
✨ Synthesis time: 4.75s
(中略)
✅ GradlePublishSampleStack
✨ Deployment time: 67.98s
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXX:stack/GradlePublishSampleStack/7f4f0d80-c9a8-11ee-bc49-0abe591c2227
✨ Total time: 72.73s
- 以下スタックが構築されます。
- この時点で、上記で紹介した一通りのAWSリソースが構築されている事が分かります。
GitHubへの接続設定の完了
- 接続設定はGitHub側での許可が必要なため、
cdk deploy
後に「接続中」の表示となります。
- 「接続中」の接続を選択し、「保留中の接続を更新」をクリックします。
- 別ウィンドウが出てくるので、そちらで接続をします。
- 「新しいアプリをインストールする」を選択した場合は、以下の様なGitHub側の画面から、接続許可するリポジトリを選択します。
- ここまで来たら、接続には成功です。
JARの公開
- 接続設定が済んだ時点で、CodeArtifactへのJAR公開のパイプラインは完成です。
- 後は
main
ブランチに対してコミットをするか、手動でパイプラインを動かすと、自動的にCodeArtifactに対してJARが公開されるようになります。
- パイプラインが起動するとBuildステージまで成功し、CodeArtifact上にも
JarSample
パッケージとしてJARが反映されている事が分かります。
参考文献
CodeArtifactユーザーガイド - GradleでCodeArtifactを使用する
Qiita - GradleでMavenローカルリポジトリにpublishをする