AWS CodePipelineとAWS CodeArtifact でソースコードのパッケージ化と共有をやってみる

記事タイトルとURLをコピーする

こんにちは。
アプリケーションサービス部、DevOps担当の兼安です。
今回は、AWS CodeArtifact のお話です。
システムを開発していると、重複コードは共通化を図りたくなります。
この時、マイクロサービスアーキテクチャを採用していると、重複コードは避けたいけれどサービスが分散しているため、共通化が難しいということが起こります。
この問題を軽減するために、コードをパッケージ化して共有する方法があるので、今回はその方法を紹介します。

コードをパッケージ化して共有するイメージ

本記事の対象者

本記事は、マイクロサービスアーキテクチャなど、システムが複数のサブシステムに分かれている場合を想定して記述しています。
また、ソースコードのパッケージ化とアップロードの部分にCI/CDを使用しています。
したがって、以下のような前提知識をお持ちの方を対象としています。

  • マイクロサービスアーキテクチャの概要を理解している
  • Pythonのパッケージ管理ツールである uv を使用したことがある
  • AWS CodePipelineの概要を理解している

本記事の検証環境

  • macOS Sequoia 15.5
  • Python 3.13.0
  • uv 0.7.8

AWS CodeArtifact とは

AWS CodeArtifact(以降、CodeArtifactと記述)は、AWS が提供するプライベートなパッケージリポジトリサービスです。
CodeArtifact を使用すると、Python、Java、JavaScript、.Net などのプログラミング言語で作成されたパッケージを管理できます。

aws.amazon.com

例えば、Project A で作成した Python のコードをパッケージ化してCodeArtifactにアップロードすると、Project Bでそのパッケージをダウンロードして使用することができます。
CodeArtifactへのアップロードはコマンドで行うことができるので、CI/CDパイプラインでソースコードがアップされたら、そのコードをパッケージ化してCodeArtifactにアップロードするということも可能です。

CodeArtifact のドメインとリポジトリ

CodeArtifactには、ドメインとリポジトリという概念があります。
CodeArtifactを使う場合は、まずドメインを作成し、そのドメインの中にリポジトリを作成します。
ドメインは複数作ることができ、さらにそのドメインの中に複数のリポジトリを作成することができます。
ドメインは、AWSアカウントの中で一意である必要があります。

graph TD
    Root[CodeArtifact]
    DomainA[DomainA]
    DomainB[DomainB]
    RepoA1[RepositoryA1]
    RepoA2[RepositoryA2]
    RepoB1[RepositoryB1]
    RepoB2[RepositoryB2]
    
    Root --> DomainA
    Root --> DomainB
    DomainA --> RepoA1
    DomainA --> RepoA2
    DomainB --> RepoB1
    DomainB --> RepoB2

CodeArtifact のアップロードとダウンロード

本記事ではPythonとそのパッケージ公開ユーティリティtwineを使用して、CodeArtifactにパッケージをアップロードします。
ダウンロードとインストールにuvを使用します。

どちらも、CodeArtifactにログインしてから実行する必要があります。

CI/CDパイプラインでCodeArtifactにパッケージをアップロードする

今回は、CodeArtifactにパッケージをアップロードするためのCI/CDパイプラインを構築します。
ソースコードをAmazon S3にアップロードすると、CodePipelineがトリガーされてCodeBuildが実行されます。
CodeBuildでは、コマンドでソースコードをパッケージ化し、CodeArtifactにアップロードします。

CodeArtifactへパッケージを登録するCI/CDパイプラインの構成図

CodeArtifactへパッケージを登録するCI/CDパイプラインのフローチャート

CodeArtifactのドメインとリポジトリの作成

CodeArtifactのドメインとリポジトリは、事前に作成しておきます。

CodeArtifactのドメイン

CodeArtifactのリポジトリ

ドメインにはドメイン所有者、リポジトリにはリポジトリ管理者が設定できます。
自分のAWSアカウントの中で、CodeArtifactのドメインを作成した場合は、そのAWSアカウントがドメイン所有者・リポジトリ管理者になります。

ソースコードをパッケージにするための設定

パッケージにするためのソースコードは、フォルダ内にpyproject.tomlを作ってパッケージ名を指定しておきます。

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "<パッケージ名>"
version = "1.0.0"
description = "A sample Python package for CodeArtifact demonstration"
readme = "README.md"
以下省略

パッケージ化とCodeArtifactへのアップロードコマンドの設定

パッケージ化してCodeArtifactにアップロードする部分は、コマンド一式をbuildspec.ymlに記述します。

version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.13
    commands:
      - echo "=== Installing build dependencies ==="
      - pip install --upgrade pip
      - pip install build twine
      - echo "Build dependencies installed successfully"

  pre_build:
    commands:
      - echo "=== Configuring CodeArtifact authentication ==="
      - echo "Domain:" $CODEARTIFACT_DOMAIN
      - echo "Repository:" $CODEARTIFACT_REPOSITORY
      - echo "Region:" $AWS_REGION
      - echo "Getting AWS account ID..."
      - export AWS_ACCOUNT_ID=$(aws sts get-caller-identity \
          --query Account \
          --output text)
      - echo "AWS Account ID:" $AWS_ACCOUNT_ID
      - echo "Getting CodeArtifact authorization token..."
      - |
        export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
          --domain $CODEARTIFACT_DOMAIN \
          --domain-owner $AWS_ACCOUNT_ID \
          --query authorizationToken \
          --output text)
      - echo "Getting CodeArtifact repository endpoint..."
      - |
        export CODEARTIFACT_REPOSITORY_URL=$(aws codeartifact get-repository-endpoint \
          --domain $CODEARTIFACT_DOMAIN \
          --domain-owner $AWS_ACCOUNT_ID \
          --repository $CODEARTIFACT_REPOSITORY \
          --format pypi \
          --query repositoryEndpoint \
          --output text)
      - echo "CodeArtifact repository URL:" $CODEARTIFACT_REPOSITORY_URL
      - echo "CodeArtifact authentication configured successfully"

  build:
    commands:
      - echo "=== Building Python package ==="
      - echo "Current directory:" $(pwd)
      - echo "Directory contents:"
      - ls -la
      - echo "Running python -m build..."
      - python -m build
      - echo "Build completed. Checking generated artifacts:"
      - ls -la dist/
      - echo "Package built successfully"

  post_build:
    commands:
      - echo "=== Uploading package to CodeArtifact ==="
      - echo "Uploading package using twine..."
      - |
        twine upload \
          --repository-url $CODEARTIFACT_REPOSITORY_URL \
          --username aws \
          --password $CODEARTIFACT_AUTH_TOKEN \
          dist/*
      - echo "Package uploaded to CodeArtifact successfully"
      - echo "=== Build and upload process completed ==="

artifacts:
  files:
    - dist/*
  name: python-package-artifacts

コマンドを実行するCodeBuildの設定

CodeBuildのビルドプロジェクトでbuildspec.ymlを使用するように設定します。
上述のbuildspec.ymlは、CodeArtifactのドメインとリポジトリを環境変数から取得するようにしています。
したがって、CodeBuildのビルドプロジェクトで環境変数として設定します。

ドメインとリポジトリはビルドプロジェクトの環境変数で設定しておく

buildspec.ymlの各フェーズで行っていること

フェーズ 処理内容
pre_build CodeArtifact のログイントークンとリポジトリエンドポイントの取得
build Python パッケージのビルド処理
post_build twine を使用して CodeArtifact にパッケージをアップロード

twineは、Pythonのパッケージをアップロードするためのツールで、CodeArtifactへのアップロードもサポートしています。

pypi.org

この設定のCodePipelineとCodeBuildを作ってから、S3にソースコードをアップロードすると、CodePipelineがトリガーされてCodeBuildが実行されます。
CodeBuildでは、buildspec.ymlに記述したコマンドが実行されてCodeArtifactにパッケージがアップロードされます。
アップロードしたパッケージは、CodeArtifactのリポジトリで確認できます。

アップロードされたパッケージ

CodeArtifactからパッケージをダウンロードしてインストールする

CodeArtifactにアップロードしたパッケージをインストールしてみましょう。
Pythonのuvの場合、以下の手順でインストールします。

  1. CodeArtifactにログイン
  2. uvでインデックスURLを指定
  3. uv pip installコマンドで直接インストール、もしくは、pyproject.tomlに当該パッケージを依存関係として追加して、uv syncを実行

uvのインデックスURLとは、Pythonパッケージをインストールする際に参照するサーバーのURLのことです。
インデックスURLの形式に従い、CodeArtifactのリポジトリのURLを設定します。

CodeArtifactへのログインとインデックスURLの設定

CodeArtifactへのログインとインデックスURLの設定までのコマンドは以下の通りです。

# 環境変数を設定
export CODEARTIFACT_DOMAIN=<ドメイン名>
export CODEARTIFACT_REPOSITORY=<リポジトリ名>
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity \
  --query Account \
  --output text)

# CodeArtifact にログイン
aws codeartifact login \
  --tool pip \
  --domain $CODEARTIFACT_DOMAIN \
  --repository $CODEARTIFACT_REPOSITORY

# uv用の環境変数を設定
export UV_INDEX_URL="https://aws:$(aws codeartifact get-authorization-token \
  --domain $CODEARTIFACT_DOMAIN \
  --domain-owner $AWS_ACCOUNT_ID \
  --query authorizationToken \
  --output text)@$CODEARTIFACT_DOMAIN-$AWS_ACCOUNT_ID.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/$CODEARTIFACT_REPOSITORY/simple/"
export UV_EXTRA_INDEX_URL="https://pypi.org/simple/"

環境変数UV_INDEX_URLUV_EXTRA_INDEX_URLでインデックスURLを設定しています。
このように設定することで、uvはCodeArtifactのリポジトリからパッケージをダウンロードできるようになります。
UV_INDEX_URLUV_EXTRA_INDEX_URLの2つの環境変数を設定しているのは、CodeArtifactのリポジトリにパッケージがない場合に、PyPIからパッケージをダウンロードさせるためです。

インデックスURLにpypisimpleというキーワードが含まれていますが、これらはPyPIのSimple APIの仕様に従ったものです。
詳しくは下記のページを参照してください。

packaging.python.org

docs.aws.amazon.com

uv pip installコマンドで直接インストール

上述のコマンドでCodeArtifactへのログインとインデックスURLの設定を行ったら、コマンドでパッケージをインストールすることができます。

uv pip install <パッケージ名>

uvを使っているのに、pyproject.tomlを使わずに直接インストールする方を先に書いているのは、次項で説明します。

pyproject.tomlに当該パッケージを依存関係として追加して、uv syncを実行

CodeArtifactへのログインとインデックスURLの設定を行っていれば、pyproject.tomlに当該パッケージを依存関係として追加して、uv syncを実行することもできます。
pyproject.tomlの例は以下の通りです。

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "consumer-example"
version = "0.1.0"
description = "Example consumer application for CodeArtifact sample package"
readme = "README.md"
requires-python = ">=3.13"

省略

dependencies = [
    "<パッケージ名>",
    "click>=8.0.0",
]

省略

pyproject.tomlに依存関係を追加したら、以下のコマンドでパッケージをインストールします。

uv sync

この方法のデメリットは、以下のようにuv.lockファイルにインデックスURLが書き込まれることです。
インデックスURLはCodeArtifactのリポジトリのURLを指定する必要があります。
CodeArtifactのリポジトリのURLは、AWSアカウントごとに異なるため、uv.lockファイルを共有すると、他の人が同じURLでインストールできなくなる可能性があります。
ここは、チーム内でどのような形でパッケージを運用するか相談が必要でしょう。

[[package]]
name = "<パッケージ名>"
version = "<バージョン>"
source = { registry = "https://<ドメイン名>-<AWSアカウントNo>.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/<リポジトリ名>/simple/" }
dependencies = [
    { name = "requests" },
]
sdist = { url = "https://<リポジトリ名>-<AWS>.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/<リポジトリ名>/simple/<パッケージ名>/<バージョン>/<パッケージ名>-<バージョン>.tar.gz", hash = "sha256:0c6bef09401583866bf7f604d0da25af7b9f1a32af7a5536a6b0a8e4930a7fb2" }
wheels = [
    { url = "https://<ドメイン名>-<AWSアカウント>.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/<リポジトリ名>/simple/<パッケージ名>/<バージョン>/<パッケージ名>-<バージョン>-py3-none-any.whl", hash = "sha256:d0f87557a8e7f8163f961016f6fa5d4ca71099c966dc582754690acf2bd2ed9d" },
]

まとめ

CodeArtifactを使用することで、複数のプロジェクト間でコードをパッケージ化して共有することができます。
マイクロサービスアーキテクチャや、複数のサブシステムを持つシステムにおけるコードの共通化の問題を軽減することができます。

CodeArtifactにパッケージをアップロードするとき、またはインストールするときは、ログインを行う必要があります。
ログイン後にリポジトリのURLを設定することで、パッケージ管理ツールを通じてパッケージのインストールが可能になります。
ただし、インストール方法によっては、ファイルにAWSアカウント番号を含んだURLが記録されることがあるため、インストール方法には注意が必要です。

兼安 聡(執筆記事の一覧)

アプリケーションサービス部 DS3課所属
2025 Japan AWS Top Engineers (AI/ML Data Engineer)
2025 Japan AWS All Certifications Engineers
2025 AWS Community Builders
Certified ScrumMaster
PMP
広島在住です。今日も明日も修行中です。