🏭
CodeBuildのローカルビルド環境変数展開: eval不使用版
下記記事の続き: eval不使用版
TL;DR
- 環境変数ファイルをマウント&読み込みさせたコンテナを起動し、キーと値のペアをbase64エンコード
- local_build.shでbase64デコードした環境変数をファイルに書き出してcustomer-specific.ymlへ追記する
イメージ図
事前準備
-
環境変数エンコード環境作成
- エンコードスクリプト
parse_envs.pyimport os from base64 import b64encode from pathlib import Path envs: dict[str, str] = {} [ [ envs.update({line: os.environ.get(line, "")}) for line in file.read_text(encoding="utf-8").split("\n") if line in os.environ.keys() ] for file in Path("/envs").glob("**/*.env") ] ( print( b64encode( "\n".join([f"{key}={value}" for key, value in envs.items()]).encode() ).decode() ) if 0 < len(envs.keys()) else None )
-
コンテナイメージ用Dockerfile
DockerfileFROM python:3.13.0a3-slim-bookworm COPY ./parse_envs.py / ENTRYPOINT [ "/usr/local/bin/python" ] CMD [ "/parse_envs.py" ]
-
イメージ作成
docker build -t parse-envs:latest .
-
冒頭記事のmy-local-builds:latestまでは作成しておく
スクリプト修正
- codebuild_build.shの修正
※冒頭記事の同ファイルの修正はrevertしておく。
codebuild_build.sh
@@ -46,6 +46,8 @@ function usage {
echo " -m Used to mount the source directory to the customer build container directly."
echo " -d Used to run the build container in docker privileged mode."
echo " -e FILE Used to specify a file containing environment variables."
+ echo " -n FILE Used to specify a file containing names of environment variable for expansion."
+ echo " -x FILE Used to specify a file containing environment variables source for expansion."
echo " (-e) File format expectations:"
echo " * Each line is in VAR=VAL format"
echo " * Lines beginning with # are processed as comments and ignored"
@@ -61,7 +63,7 @@ awsconfig_flag=false
mount_src_dir_flag=false
docker_privileged_mode_flag=false
-while getopts "cmdi:a:r:s:b:e:l:p:h" opt; do
+while getopts "cmdi:a:r:s:b:e:n:x:l:p:h" opt; do
case $opt in
i ) image_flag=true; image_name=$OPTARG;;
a ) artifact_flag=true; artifact_dir=$OPTARG;;
@@ -72,6 +74,8 @@ while getopts "cmdi:a:r:s:b:e:l:p:h" opt; do
d ) docker_privileged_mode_flag=true;;
s ) source_dirs+=("$OPTARG");;
e ) environment_variable_file=$OPTARG;;
+ n ) names_of_expand_environment_variable_file=$OPTARG;;
+ x ) expand_environment_variable_file=$OPTARG;;
l ) local_agent_image=$OPTARG;;
p ) aws_profile=$OPTARG;;
h ) usage; exit;;
@@ -182,6 +186,22 @@ else
docker_command+=" -e \"INITIATOR=$USER\""
fi
+if [ -f "$names_of_expand_environment_variable_file" ]
+then
+ customer_envs="$(bash -c "if [ -f "$expand_environment_variable_file" ]; \
+ then source $expand_environment_variable_file; \
+ fi && \
+ docker run \
+ --rm \
+ --env-file=${names_of_expand_environment_variable_file} \
+ -v ${names_of_expand_environment_variable_file}:/envs/.env \
+ parse-envs:latest")"
+ if [ -n "$customer_envs" ]
+ then
+ docker_command+=" -e CUSTOMER_ENVIRONMENTS=\"${customer_envs}\""
+ fi
+fi
+
if [ -n "$local_agent_image" ]
then
docker_command+=" $local_agent_image
実行方法
ビルドコンテナに渡したい環境変数名リストファイルをnames.env
、変数値の上書き・展開が必要な場合の変数値リストファイルをvalues.env
(オプション)とする。
変数値リストファイルはexportをつける。
names.env
ENV1
ENV2
values.env
export ENV1=$(pwd)
export ENV2="pwd: ${ENV2}"
codebuild_build.shを以下のように実行する。
command
./codebuild_build.sh \
-i public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:5.0-24.02.08 \
-s ./src \
-a ./artifacts \
-n ./names.env
-x values.env
※-nは相対パスの場合./をつけないと正しくマウントできない。
実行結果
agent_1 | [Container] ****/**/** **:**:** Running command echo ${ENV1}
agent_1 | /root/local_builds
agent_1 |
agent_1 | [Container] ****/**/** **:**:** Running command echo ${ENV2}
agent_1 | pwd: /root/local_builds
agent_1 |
agent_1 | [Container] ****/**/** **:**:** Phase complete: BUILD State: SUCCEEDED
詳細
codebuild_build.sh内で環境変数を評価するためevalを実行することは実行中シェルの環境を変更させてしまうため安全とは言えない。
そこで別シェルで環境変数を読み込み、agentに環境変数値を渡すためにコンテナを用いて環境変数のペアをbase64でエンコードする。(シェル側で解析すればシェル内で完結させることも不可能ではないが、自前の解析処理を記述するよりもpythonの標準ライブラリを用いたほうが間違いもなく処理はシンプルにもなるためpythonコンテナ内で処理させている)
このようにすることで、ホスト側の環境から完全に隔離された状態で環境変数を展開した結果を取得することができる。
構造としては複雑であまりスマートとは言えないが、確実かつ安全に処理することはできているはずである。
codebuild_build.sh
customer_envs="$(bash -c "if [ -f "$expand_environment_variable_file" ]; \
then source $expand_environment_variable_file; \
fi && \
docker run \
--rm \
--env-file=${names_of_expand_environment_variable_file} \
-v ${names_of_expand_environment_variable_file}:/envs/.env \
parse-envs:latest")"
parse_envs.py
[
[
envs.update({line: os.environ.get(line, "")})
for line in file.read_text(encoding="utf-8").split("\n")
if line in os.environ.keys()
]
for file in Path("/envs").glob("**/*.env")
]
・・・
b64encode(
"\n".join([f"{key}={value}" for key, value in envs.items()]).encode()
).decode()
local_build.shでのデコードは冒頭記事と同じ仕組みを使用。
Discussion