Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
モノレポのGitHub(Enterprise)からCodePipelineを呼び出す小ネタ
Search
hmatsu47
PRO
February 23, 2022
Technology
280
0
Share
モノレポのGitHub(Enterprise)からCodePipelineを呼び出す小ネタ
JAWS-UG 浜松 AWS 勉強会 2022#2 2022/2/25
hmatsu47
PRO
February 23, 2022
More Decks by hmatsu47
See All by hmatsu47
名古屋城とデータセンター
hmatsu47
PRO
0
20
IPv6 に関する話
hmatsu47
PRO
0
17
さいきんの光ファイバーの話
hmatsu47
PRO
0
43
低いほうのレイヤを見てみる話
hmatsu47
PRO
0
20
IPv6 VPC の実装パターンをいくつか
hmatsu47
PRO
0
40
光ファイバーと IPv6 絡みの話
hmatsu47
PRO
0
50
AWS で試して学ぶ IPv6
hmatsu47
PRO
0
47
今年の MySQL/HeatWave ネタ登壇振り返り
hmatsu47
PRO
0
41
今年の DB ネタ登壇振り返り
hmatsu47
PRO
0
37
Other Decks in Technology
See All in Technology
TypeScriptはどのようにどこまで推論できるのか ─ とにかく as は禁止で
ypresto
1
290
TypeScriptで実現する既存APIを活用したリモートMCPサーバー構築 / TSKaigi 2026
soarteclab
1
180
障害対応のRunbookは作った、でも本当に動くの? AWS FIS で EKS の AZ 障害を再現してみた
tk3fftk
0
120
[4] Power BI Deep Dive [2026-05]
ohata_bi
0
110
サプライチェーン攻撃への備えについて考えている #湘なんか
stefafafan
2
2.2k
その英語学習、AWSで代替できませんか?
suzutatsu
1
190
AIコーディングエージェントの活用で、コードは静かに肥大化した
yosukeshinoda
1
140
10サービス以上のメール到達率改善を地道に継続的に進めている話 / Continue to improve email delivery rates across multiple services
yamaguchitk333
6
2.3k
コーポレートサイトのアクセシビリティ改善とJIS準拠への実践
lycorptech_jp
PRO
2
110
R&D 祭 2024 アニメエフェクト作成の効率化
olmdrd
PRO
0
110
Claude Code で使える DuckDB Skills を試してみた / DuckDB Skills and Claude Code
masahirokawahara
1
2k
SDDで⾒える、AIコーディングの"内訳"
lycorptech_jp
PRO
0
150
Featured
See All Featured
Done Done
chrislema
186
16k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
350
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
180
Scaling GitHub
holman
464
140k
Unsuck your backbone
ammeep
672
58k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
260
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
73
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
800
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.1k
KATA
mclloyd
PRO
35
15k
Transcript
モノレポの GitHub (Enterprise) から CodePipeline を呼び出す小ネタ JAWS-UG 浜松 AWS 勉強会 2022#2 2022/2/25
まつひさ(hmatsu47)
自己紹介 松久裕保(@hmatsu47) • https://qiita.com/hmatsu47 • 現在のステータス: ◦ 名古屋で Web インフラのお守り係をしています
◦ Aurora MySQL v1(5.6)の EoL が発表されたのでアップ開始 ▪ https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aur ora.MySQL56.EOL.html • v1 → v3 移行を画策中 2
本日の小ネタ • こちら↓の Lambda 関数を正しく動くよう実装 ◦ GitHub モノレポを AWS CodePipeline
と統合して、プロジェクト固有の CI/CD パイプラインを実行する(Amazon Web Services ブログ) • Zenn で記事化済み ◦ https://zenn.dev/hmatsu47/articles/73c624fb5730dd • 参考にした記事 ◦ Backlogの課題にGitHubのコミットを連携する方法(ponsuke_tarou’s blog) 3
モノレポの課題 • 開発プロジェクト(プロダクト・サービス)別にビルド →デプロイするのに手間が掛かる ◦ 必要がなくても全プロジェクトをビルドパイプラインが走る • そこで提示されたのが前掲の記事 ◦ GitHub
モノレポを AWS CodePipeline と統合して、プロジェクト固有の CI/CD パイプラインを実行する(Amazon Web Services ブログ) ◦ ただし動作に問題がある 4
何が問題? • 対象ブランチの指定がない ◦ どこのブランチに push してもパイプラインが実行されてしまう • コードの変更以外の操作まで拾う ◦
誤動作の可能性がある • 複数フォルダにまたがる push でも、1 本のパイプライン しか実行されない 5
修正後は • 対象ブランチの指定が可能 • パイプライン実行対象のフォルダを指定可能 • 複数フォルダにまたがる push で複数のパイプラインを並 列呼び出し可能
• 例外的に全パイプラインを並列呼び出しするフォルダの 指定が可能 6
設定の流れ(詳細は前掲の Zenn 記事を参照) 1. Secrets Manager にシークレットを保存 2. Lambda 関数を作成
3. API Gateway を作成し、Lambda 関数を統合 4. IAM Role(Lambda 実行用)にポリシーを追加 5. GitHub (Enterprise) で Webhook を設定 6. CodePipeline を設定(変更) 7
1. Secrets Manager にシークレットを保存 • GitHub → (API Gateway →)Lambda
認証時に使用 ◦ パスワードジェネレータなどで生成 ◦ Secrets Manager で「その他のシークレットのタイプ」を選択 ▪ キー : GHE_SECRETS ▪ 値 : 生成したシークレットの値 ▪ 任意の名前を付けて保存 8
2. Lambda 関数を作成 • GitHub Webhook からのリクエストを受けて、対象の CodePipeline を呼び出す ◦
コードと↓の環境変数を登録 ▪ 呼び出すパイプライン名のサフィックス : job_name_suffix • 「【フォルダ名】+【サフィックス】」の CodePipeline を呼び出します ▪ シークレット名(先ほど保存したもの) : secrets_name ▪ パイプライン実行対象のブランチ名 :trigger_branch • 「refs/heads/【ブランチ名】」の形で指定 9
2. Lambda 関数を作成 import json import hmac, hashlib import boto3
import base64 import ast, re import os from botocore.exceptions import ClientError def lambda_handler(event, context): body = event['body'] if is_correct_signature(event['headers']['x-hub-signature'], body): print('認証成功') project_names = [] job_name_suffix = os.environ['job_name_suffix'] body_json = json.loads(body) ref = body_json['ref'] if ref == os.environ['trigger_branch'] and len(body_json['commits']) > 0: # 指定ブランチへのコミットの場合だけ処理 added_files = body_json['commits'][0]['added'] removed_files = body_json['commits'][0]['removed'] modified_files = body_json['commits'][0]['modified'] + added_files + removed_files print('added / removed / modified : {}'.format(modified_files)) # どのプロジェクトのビルドを行うかファイルパスから判断 includes = ['project1', 'project2', 'project3'] pipelines_count_max = len(includes) common = ['common'] 10
2. Lambda 関数を作成 for file_path in modified_files: pos = file_path.find('/')
if pos > 0: # パスにフォルダを含む→プロジェクト名を確認 project_name = file_path[:pos] if common.count(project_name) > 0: # 共有プロジェクト名であれば全て呼び出しパイプラインに含める project_names = includes break if project_names.count(project_name) == 0: # 対象プロジェクト初検出→呼び出しパイプラインに含める project_names.append(project_name) if len(project_names) == pipelines_count_max: # すべてのプロジェクトを検出→ループを抜ける break # 対象プロジェクトをビルドするパイプラインを呼び出す print('projects : {}'.format(project_names)) if len(project_names) > 0: for project_name in project_names: return_code = start_code_pipeline('{}{}'.format(project_name, job_name_suffix)) print(return_code) return { 'statusCode': 200, 'body': json.dumps('Modified project in repo: {}'.format(project_names)) } 11
2. Lambda 関数を作成 def get_secrets_manager_dict(secret_name: str) -> dict: """Secrets Managerからシークレットのセットを辞書型で取得する"""
secrets_dict = {} if not secret_name: print('シークレットの名前未設定') else: session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name='ap-northeast-1' ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: print('シークレット取得失敗:シークレットの名前={}'.format(secret_name)) print(e.response['Error']) else: if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] else: secret = base64.b64decode(get_secret_value_response['SecretBinary']) secrets_dict = ast.literal_eval(secret) return secrets_dict 12
2. Lambda 関数を作成 def get_secrets_manager_key_value(secret_name: str, secret_key: str) -> str:
"""AWS Secrets Managerからシークレットキーの値を取得する.""" value = '' secrets_dict = get_secrets_manager_dict(secret_name) if secrets_dict: if secret_key in secrets_dict: # secrets_dictが設定されていてsecret_keyがキーとして存在する場合 value = secrets_dict[secret_key] else: print('シークレットキーの値取得失敗:シークレットの名前={}、シークレットキー={}'.format(secret_name, secret_key)) return value def is_correct_signature(signature: str, body: dict) -> bool: """GitHubから送られてきた情報をHMAC認証する.""" if signature and body: # GitHubのWebhookに設定したSecretをSecrets Managerから取得する secret = get_secrets_manager_key_value(os.environ['secrets_name'], 'GHE_SECRETS') if secret: secret_bytes = bytes(secret, 'utf-8') body_bytes = bytes(body, 'utf-8') # Secretから16進数ダイジェストを作成する signedBody = "sha1=" + hmac.new(secret_bytes, body_bytes, hashlib.sha1).hexdigest() return signature == signedBody else: return False 13
2. Lambda 関数を作成 def start_code_pipeline(pipelineName): client = codepipeline_client() response =
client.start_pipeline_execution(name=pipelineName) return True cpclient = None def codepipeline_client(): global cpclient if not cpclient: cpclient = boto3.client('codepipeline') return cpclient 14 • こちらで公開中 ◦ https://github.com/hmatsu47/github-monorepo-codepipeline
3. API Gateway を作成し、Lambda 関数を統合 • HTTP の API Gateway
を作成 ◦ 先ほど作成した Lambda 関数を統合 ◦ 任意の API 名を指定 ◦ ルートのメソッドは POST に限定 ◦ ステージ名「$default」のまま自動デプロイ指定で作成 15
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow",
"Action": [ "secretsmanager:GetResourcePolicy", "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret", "secretsmanager:ListSecretVersionIds" ], "Resource": "【シークレットのARN】" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "secretsmanager:GetRandomPassword", "secretsmanager:ListSecrets" ], "Resource": "*" } ] } 4. IAM Role(Lambda 実行用)にポリシーを追加 16
5. GitHub (Enterprise) で Webhook を設定 • ↓を指定して作成 ◦ Payload
URL :API Gateway の URL ▪ https://XXX.execute-api.ap-northeast-1.amazonaws.com/【リソースパス】 ◦ Content type :application/json ◦ Secret :生成したシークレット 17
6. CodePipeline を設定(変更) • Source ステージで↓のチェックを外す ◦ 検出オプションを変更する ▪ ソースコードの変更時にパイプラインを開始する
18
やってみた感想など • 意外と面倒 ◦ 情報があまり出回っていない ◦ 本当に大変なのは GitHub Webhooks 〜
Lambda よりも CodePipeline 側 ◦ GitHub Actions でやったほうが… • 事情で GitHub Actions が使えない場合の代替策 19