如何在 AWS 上自动部署带有子模块的 git 个存储库?

How to auto deploying git repositories with submodules on AWS?

我的 git 存储库中有一个子模块,我的目录结构如下,

app
  -- folder1
  -- folder2
  -- submodule @5855

我已经使用自动部署服务在 AWS 上部署了我的代码。 现在,在服务器上,我在父目录中有代码,但子模块目录是空的。

Q1) 如何获取子模块中的数据。我在服务器上的存储库不是 git 存储库。我是否需要先将其转换为 git 存储库,然后再将其转换为 运行 submodule 命令才能获取它?

Q2) 我怎样才能自动部署子模块?

谢谢

编辑:Codebuild 现在有一个“子模块”标志 https://docs.aws.amazon.com/codebuild/latest/APIReference/API_GitSubmodulesConfig.html

这是对我有用的方法

我们将重新初始化 git 存储库,然后在部署的构建阶段触发子模块克隆,本质上是修补以支持 codepipeline / codebuild

中的子模块
  • 为您的 github 帐户生成一个新的 SSH 密钥,如果使用一个组织,您可能需要创建一个部署用户
  • 使用 aws ssm put-parameter --name build_ssh_key --type String --value "$(cat id_rsa)" 将此 ssh 密钥存储在您的 aws 参数存储中,理想情况下使用 SecureString 而不是 String 但我遵循的指南只是使用字符串,所以我不确定命令行是否需要任何额外的参数
  • 进入 IAM 并授予您的 CodePipeline 用户对您的参数库的读取权限,我刚刚授予对 SSM 的读取权限

然后让你的 buildspec.yml 看起来像下面这样:

version: 0.2

env:
  parameter-store:
    build_ssh_key: "build_ssh_key"

phases:
  install:
    commands:
      - mkdir -p ~/.ssh
      - echo "$build_ssh_key" > ~/.ssh/id_rsa
      - chmod 600 ~/.ssh/id_rsa
      - ssh-keygen -F github.com || ssh-keyscan github.com >>~/.ssh/known_hosts
      - git config --global url."git@github.com:".insteadOf "https://github.com/"
      - git init
      - git remote add origin <Your Repo url here using the git protocol>
      - git fetch
      - git checkout -t origin/master
      - git submodule init
      - git submodule update --recursive
  build:
    commands:
      - echo '...replace with real build commands...'

artifacts:
  files:
    - '**/*'

我自己 运行 解决了这个问题,多亏了 @matt-bucci 的精彩建议,我才能够想出一个看似可靠的解决方案。

我的具体用例略有不同 - 我正在使用 Lambda 层来减少 lambda 冗余,但仍需要将这些层作为子模块包含在 Lambda 函数存储库中,以便 CodeBuild 可以构建和测试 PR。我也在使用 CodePipeline 来协助持续交付 - 所以我需要一个可以单独使用 CodePipeline 和 CodeBuild 的系统

  1. 我创建了一个新的 SSH 密钥供 these instructions 之后的 "machine user" 使用。在这种情况下,我使用的是机器用户,因此不需要为 每个 项目生成新的 ssh 密钥,也不需要为多个私有子模块提供潜在支持

  2. 我将私钥作为 SecureString 存储在 AWS Parameter Store 中。这实际上并没有改变 CodeBuild 中的任何内容,因为它足够聪明,只知道如何解密密钥

  3. 我给了 "codebuild" 角色 AWS 托管 属性:AmazonSSMReadOnlyAccess - 允许 CodeBuild 访问私钥

  4. 我制作了我的 buildspec.yml 文件,使用了@matt-bucci 建议的一堆命令,以及一些新命令

# This example buildspec will enable submodules for CodeBuild projects that are both 
# triggered directly and via CodePipeline
#
# This buildspec is designed with help from Stack Overflow: 
# 
version: 0.2  # Always use version 2
env:
  variables:
    # The remote origin that will be used if building through CodePipeline
    remote_origin: "git@github.com:your/gitUri"
  parameter-store:
    # The SSH RSA Key used by our machine user
    ssh_key: "ssh_key_name_goes_here"
phases:
  install:
    commands:
      # Add the "machine user's" ssh key and activate it - this allows us to get private (sub) repositories
      - mkdir -p ~/.ssh                   # Ensure the .ssh directory exists
      - echo "$ssh_key" > ~/.ssh/ssh_key  # Save the machine user's private key
      - chmod 600 ~/.ssh/ssh_key          # Adjust the private key permissions (avoids a critical error)
      - eval "$(ssh-agent -s)"            # Initialize the ssh agent
      - ssh-add ~/.ssh/ssh_key            # Add the machine user's key to the ssh "keychain"
      # SSH Credentials have been set up. Check for a .git directory to determine if we need to set up our git package
      - |
        if [ ! -d ".git" ]; then
          git init                                              # Initialize Git
          git remote add origin "$remote_origin"                # Add the remote origin so we can fetch
          git fetch                                             # Get all the things
          git checkout -f "$CODEBUILD_RESOLVED_SOURCE_VERSION"  # Checkout the specific commit we are building
        fi
      # Now that setup is complete, get submodules
      - git submodule init
      - git submodule update --recursive
      # Additional install steps... (npm install, etc)
  build:
    commands:
      # Build commands...
artifacts:
  files:
    # Artifact Definitions...

此安装脚本执行三个独立的步骤

  1. 它安装并启用用于访问私有存储库的 ssh 私钥

  2. 它确定是否有 .git 文件夹 - 如果没有,则脚本将初始化 git 并检查 exact 正在构建的提交。 注意: 根据 AWS 文档,$CODEBUILD_RESOLVED_SOURCE_VERSION envar 不会 gu运行teed 出现在 CodePipeline 构建中.但是,我没有看到这个失败

  3. 终于真正拿到子模块了

显然,这不是解决此问题的好方法。但是,考虑到 CodePipeline 的(不必要的)限制,这是我能想到的最好的方法。这个过程的一个副作用是 "Source" CodePipeline 阶段完全没有价值,因为我们只是覆盖了存档的源文件——它只用于监听对存储库的更改

两年多来一直要求更好的功能: https://forums.aws.amazon.com/thread.jspa?threadID=248267

2019 年 1 月 23 日编辑

我意识到(艰难地)我之前的回复不支持 CodePipeline 构建,只能直接通过 CodeBuild 构建 运行。当 CodeBuild 响应 GitHub Webhook 时,它将克隆整个 GitHub 存储库,包括 .git 文件夹

但是,当使用 CodePipeline 时,"Source" 操作将克隆存储库,检查适当的 b运行ch,然后生成原始文件 而没有 .git 文件夹。这意味着我们必须初始化 github 存储库才能访问子模块

虽然@MattBucci 的回答有效,但它有一个警告,你只能拉一个特定的分支,而不是子模块正在使用的特定提交。

为了处理使用子模块时很可能出现的这种情况,需要做很多事情:

1) 创建一个 git pre-commit 挂钩,内容如下:

#!/bin/bash

#   This file is used in post-commit hook
#   if .commit exists you know a commit has just taken place but a post-commit hasn't run yet
#
touch .commit

如果您已有一个,可以在开头添加该行。

2) 使用以下内容创建一个 git post-commit 挂钩:

#!/bin/bash


DIR=$(git rev-parse --show-toplevel);

if [[ -e $DIR/.commit ]]; then
    echo "Generating submodule integrity file"
    rm .commit

    SUBMODULE_TRACKING_FILE=$DIR/.submodule-hash
    MODULE_DIR=module
    #   Get submodule hash, this will be used by AWS Code Build to pull the correct version.
    #   AWS Code Build does not support git submodules at the moment
    #   https://forums.aws.amazon.com/thread.jspa?messageID=764680#764680
    git ls-tree $(git symbolic-ref --short HEAD) $MODULE_DIR/ | awk '{ print  }' > $SUBMODULE_TRACKING_FILE

    git add $SUBMODULE_TRACKING_FILE
    git commit --amend -C HEAD --no-verify
fi

exit 0

这个钩子会将当前的提交哈希放入.submodule-hash文件,这个文件需要提交到版本控制

3) 转到您的 AWS Code 构建项目

Developer Tools > CodeBuild > Build projects > YOUR_PROJECT > Edit Environment

添加一个名为:GIT_KEY 的环境变量,其值将是 ssh 密钥 base 64 编码。 (不能换行,否则不行)

您可以转换它 online,或者使用任何工具或编程语言。

4) 在您的 buildspec.yml 上添加一个 pre_build 脚本。

version: 0.2

phases:
  pre_build:
    commands:
      - bash build/aws-pre-build.sh
...

5) 使用以下内容创建 build/aws-pre-build.sh:

#!/bin/bash

set -e

#   Get root path
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )"

MODULE_HASH=$(cat $DIR/.submodule-hash);
GIT_HOST=bitbucket.org
MODULE_DIR=module
REPO=user/repo.git


if [[ ! -d ~/.ssh ]]; then
    mkdir ~/.ssh
fi

if [[ ! -f ~/.ssh/known_hosts ]]; then
    touch ~/.ssh/known_hosts
fi

#   Base64 decode private key, and save it to ~/.ssh/git
echo "- Adding git private key"

echo $GIT_KEY | base64 -d > ~/.ssh/git

#   Add correct permissions to key
chmod 600 ~/.ssh/git

#   Add $GIT_HOST to ssh config
echo "- Adding ssh config file"

cat > ~/.ssh/config <<_EOF_
Host $GIT_HOST
    User git
    IdentityFile ~/.ssh/git
    IdentitiesOnly yes
_EOF_

#   Check if host is present in known_hosts
echo "- Checking $GIT_HOST in known_hosts"

if ! ssh-keygen -F $GIT_HOST > /dev/null; then
    echo "- Adding $GIT_HOST to known hosts"
    ssh-keyscan -t rsa $GIT_HOST >> ~/.ssh/known_hosts
fi

#   AWS Code build does not send submodules, remove the empty folder
rm -rf $MODULE_DIR

# Clone submodule in the right folder
git clone git@$GIT_HOST:$REPO $MODULE_DIR

# cd to submodule
cd $DIR/$MODULE_DIR

# Checkout the right commit
echo "- Checking out $MODULE_HASH"

git checkout $MODULE_HASH


额外内容

如果您在进入 AWS Code Build 之前有一个额外的步骤,比如 bitbucket 管道或类似的东西,您可以检查实际的 git 子模块哈希是否与生成文件中的哈希相匹配:.submodule-hash.

如果不匹配,这意味着谁曾经推动过,没有 git 钩子。

#!/bin/bash

$MODULE_DIR=module

echo "- Checking submodules integrity"

SUBMODULE_TRACKING_FILE=.submodule-hash


#   Check submodule hash, this will be used by AWS Code Build to pull the correct version.
#   AWS Code Build does not support git submodules at the moment
#   https://forums.aws.amazon.com/thread.jspa?messageID=764680#764680

#   Git submodule actual hash
SUBMODULE_HASH=$(git ls-tree $(git symbolic-ref --short HEAD) $MODULE_DIR/ | awk '{ print  }')

if [[ ! -e $SUBMODULE_TRACKING_FILE ]]; then

    echo "ERROR: $SUBMODULE_TRACKING_FILE file not found."
    submoduleError

    exit 1
fi

#   Custom submodule hash - The is used by AWS Code Build
SUBMODULE_TRACKING_FILE_HASH=$(cat $SUBMODULE_TRACKING_FILE)

if [[ "$SUBMODULE_TRACKING_FILE_HASH" != "$SUBMODULE_HASH"  ]]; then

    echo "ERROR: $SUBMODULE_TRACKING_FILE file content does not match submodule hash: $SUBMODULE_HASH"

    echo -e "\tYou should have pre-commit && post-commit hook enabled or update $SUBMODULE_TRACKING_FILE manually:"
    echo -e "\tcmd: git ls-tree $(git symbolic-ref --short HEAD) $MODULE_DIR/ | awk '{ print $3 }' > $SUBMODULE_TRACKING_FILE"

    exit 1
fi

注意:您还可以在 AWS Code Build 之前在管道上创建该文件,创建提交,标记它并推送它,以便 AWS Code Build 管道开始。

git ls-tree $(git symbolic-ref --short HEAD) module/ | awk '{ print $3 }' > .submodule-hash

如果您使用 CodeCommit 作为存储库,则不需要 SSH。使用 AWS CLI Credential Helper 并通过 https 克隆。

git config --global credential.helper '!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true
git clone https://git-codecommit.[region].amazonaws.com/v1/repos/[repo]

很久没发现子模块的问题了。但是 aws 无法修复它。因此 codepipeline 无法将 .git 目录发送到 codebuild。所以我们必须发明新的双臂,我的解决方法是 buildspec.yml 预构建命令

rm -rf $PWD/*
git clone --depth 1 https://<REPO NAME>  -b develop .
git submodule update --init --recursive

AWS 快点,因为我们的团队正在考虑搬回 github。

借鉴@MattBucci 和@JoshuaEvans 的概念。这是我们所做的。我们无法在管道中安装 git,因为 ssh 存在严格的 access/security 问题。所以我们最终改为通过 HTTP 执行此操作。

  1. 我创建了一个新的 Github 个人访问令牌
  2. 我将 PAT 作为 安全字符串。这实际上并没有改变任何东西 CodeBuild,因为它足够聪明,只知道如何解密 键
  3. 我给了 "codebuild" 角色来读取 Secrets manager
  4. 我使用一堆命令创建了我的 buildspec.yml 文件 @matt-bucci 和@JoshuaEvans 以及一些新建议
version: 0.2
env:
    variables:
      token: " token "
    secrets-manager:
      personalAccessToken: $personalAccessTokenPath
phases:
    install:
      runtime-versions:
        nodejs: 12
      commands:
          - cd <to the folder where you want the submodules>
          - wget --header="Authorization:$token$personalAccessToken" --content-disposition  https://github.com/uri/zipball/$branch/
          - unzip project*.zip -d project-folder
          - rm -rf project*.zip
          - cd project-folder
          - mv project*/* .
          - rm -rf project*
          - cd <back to your base folder where the buildspec is>
    pre_build:
      commands:
        - xxx
    build:
      commands:
        - xxx
    post_build:
      commands:
        - xxx
artifacts:
    files:
      - '**/*'
    base-directory: dist

希望对您有所帮助!

我在 AWS CodeBuild 上遇到了同样的问题。我勾选 Use Git submodules 如下图更新我的 submodule.

当我 运行 构建时出现以下错误,

CLIENT_ERROR: Submodule error error creating SSH agent: "SSH agent requested but SSH_AUTH_SOCK not-specified" for primary source and source version refs/heads/dev

所以我用谷歌搜索了上面的错误,并从 AWS 论坛得到了这个 DOWNLOAD_SOURCE Fails with Git submodules 帖子。他们提到,

The submodules must be configured as https and not ssh.

我认为这没有用,有人将 submodule 设置为 ssh 会发生什么。我也做了同样的事情,这是我的 .gitmodules 文件。

[submodule "common"]
    path = common
    url = git@bitbucket.org:organization_id/common.git

真的不想改成https。然后我发现了这个Working with Git Submodules in CodePipeline article from medium. I would like to visualize what I did to solve this issue and there was an error that didn't mention in that article。让我们以更安全的方式来做到这一点。


首先转到 AWS Key Management Service (KMS) 并转到 Customer managed keys 部分并单击 Create key 创建密钥。

  1. 单击 Symmetric 并单击 Next

  1. 输入任何名称(例如:- bitbucket-credentials)以创建 Alias 并单击 Next

  1. 很可能您已经有一个 AWS Role 来配置任何 AWS 上的开发人员工具,所以在我的例子中,我创建了一个 AWS Role 调用 ecsCodeBuildRole for AWS CodeBuild 并为其输入 Define key administrative permissions 然后单击 Next

  1. 接下来为您的 AWS Role 输入 Define key usage permissions 然后点击 Next

  1. 最后回顾一下您到目前为止所做的,然后单击 Finish 创建 CMK

  1. 您可以像下面这样查看。


所以 AWS Key Management Service (KMS) 部分已完成,现在转到 AWS Systems Manager 并找到 Parameter Store 部分。单击 Create parameter

  1. 只要将它命名为id_rsa并像下面一样放置相同的东西。

  1. 对于 value 部分,只需在您的终端中使用 运行 cat ~/.ssh/id_rsa 命令,您将获得如下输出。将其添加到 value 部分。
-----BEGIN RSA PRIVATE KEY-----
qdjbXp+42VTnccC7pxOZcofomfwGXPWuqcv99sQEPtToODvGIxWoooJUpb6qMIWY
1zccEuwAhmqcPvpsJyWhcctZB/wWglNvViZcOYjrQ8HBUBKJT8pF
-----END RSA PRIVATE KEY-----
  1. 创建另一个参数并将其命名为id_rsa.pub。按照与上述相同的步骤进行操作。

  2. 对于 value 部分,只需在您的终端中使用 运行 cat ~/.ssh/id_rsa.pub 命令,您将获得如下输出。将其添加到 value 部分。

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGtf8jjkogWxRGNGjJlOZ1G+pWExgDOdA5wVML6TMTT2YtvhPJD60nPx5TfA8zRzGWubwrPp40SPAhSs5wiAAg38HlS4pz9X wasdkiller@wasdkiller

作为我的研究(也许我错了,请纠正我)我没有任何其他方法可以将 ssh 凭据传递给 AWS CodeBuild 而不需要这么大的努力。所以我像这样手动更改了 buildspec.yml 文件。

version: 0.2

env:
  parameter-store:
    ssh_key: id_rsa
    ssh_pub: id_rsa.pub

phases:
  install:
    commands:
      - mkdir -p ~/.ssh
      - echo "$ssh_key" > ~/.ssh/id_rsa   
      - echo "$ssh_pub" > ~/.ssh/id_rsa.pub
      - chmod 600 ~/.ssh/id_rsa
      - eval "$(ssh-agent -s)"
      - git submodule update --init --recursive

当您继续时,您肯定会遇到以下错误,

Decrypted Variables Error: AccessDeniedException: User: arn:aws:sts::organization_id:assumed-role/ecsCodeBuildRole/AWSCodeBuild-12896abb-bdcf-4cfc-a12b-bcf30d6e96ab is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:ap-southeast-2:organization_id:parameter/wasd status code: 400, request id: 23b94bc2-961e-4d86-9b73-d16e3bda357c

它会要求您提供 ssm:GetParameters 权限,只需附加 AmazonSSMReadOnlyAccess 策略或使用 ssm:GetParameters 权限手动创建策略并将其附加到您的 AWS Role,它会解决这个问题。

一整天都在苦思冥想之后,我找到了一个简单的解决方案(用于代码管道),不需要在构建规范 中处理任何 SSH 密钥。我正在使用 Bitbucket,但我认为这适用于其他提供商。我也在通过 https 克隆我的子模块,我不确定这是否是必需的。

  1. 配置您的源以对存储库进行完整克隆。这将传递您需要的 git 元数据。

  2. 配置您的构建角色以添加 customer-managed UseConnection 权限,使您的构建操作能够访问您为源配置的凭据。此处来自 AWS 的文档: https://docs.aws.amazon.com/codepipeline/latest/userguide/troubleshooting.html#codebuild-role-connections

  3. 设置您的环境以包含 git-credential-helper: yes 并克隆 buildspec.yml:

    中的子模块

就是这样!子模块将可用于构建,而无需为您要使用的每个子模块做一堆关键配置。

如果最终对人们有用,这可能是对文档的一个很好的补充。