亚马逊AWS官方博客

新功能 – 使用 Step Functions 和 AWS CodeBuild 构建持续集成工作流程

自动化软件构建过程是采用开发运营最佳实践的重要一步。为了帮助您实现目的,我们构建了 AWS CodeBuild,该服务是一项完全托管的持续集成服务,可编译源代码、运行测试并生成可供部署使用的程序包。

然而,客户的构建过程中可能存在很多自定义选项,我们已经看到有开发人员花费时间构建自己的自定义工作流程,以协调其软件构建所需的不同活动。例如,您可能会在需要部署快速修复时想要运行或不运行某些测试,或跳过代码的静态分析。 根据您的单元测试结果,您可能想要采取不同的错误,或通过 SNS 接收通知。

为了简化此过程,我们今天推出了全新的与 CodeBuildAWS Step Functions 服务集成。现在,在执行状态机的过程中,您可以开始停止构建、获取构建报告摘要删除过去的构建执行记录

用此方式,您可以定义自己的工作流程驱动型构建过程,并且手动或自动触发它。例如,您可以:

此次集成后,您可以使用 Step Functions 全部功能来自动执行您的软件构建。例如,您可以使用 Parallel 状态为版本的独立组件构建并行版本。您可以从代码库中所有的分支列表中使用 Map 状态为每个分支运行一组步骤(自动化构建、单元测试和集成测试)。 您还可以在同一个工作流程中利用其他 Step Functions 服务集成。 例如,您可以发送一条消息到 SQS 队列来跟踪您的活动,或者启动您刚使用 Amazon ECSAWS Fargate 构建的容器化应用程序。

Step Functions 用于工作流程驱动型构建过程
我正在开发一个 Java Web 应用程序。为确保它在我添加新功能时正常运行,我使用 JUnit Jupiter 编写了几个测试。我希望这些测试在构建过程后运行,但希望它们不要一直运行,因为测试可能会减慢一些快速迭代的速度。当我运行测试时,我希望使用 CodeBuild 存储并查看我的测试报告。最后,我希望在测试运行及测试成功时通过 SNS 主题接收通知。

我在 CodeCommit 中创建了一个存储库,并且包含了 CodeBuild 的两个 buildspec 文件:

  • buildspec.yml 是默认文件,它使用 Apache Maven 运行构建和测试,然后将测试结果存储为报告。
version: 0.2
phases:
  build:
    commands:
      - mvn package
artifacts:
  files:
    - target/binary-converter-1.0-SNAPSHOT.jar
reports:
  SurefireReports:
    files:
      - '**/*'
    base-directory: 'target/surefire-reports'
  • buildspec-notests.yml 仅执行构建,不执行测试。
version: 0.2
phases:
  build:
    commands:
      - mvn package -DskipTests
artifacts:
  files:
    - target/binary-converter-1.0-SNAPSHOT.jar

为了设置 CodeBuild 项目和 Step Functions 状态机来自动执行构建,我使用了 AWS CloudFormation 与以下模板:

AWSTemplateFormatVersion: 2010-09-09
描述:用于获取 AWS CodeBuild 测试报告结果通知的 AWS Step Functions 示例项目
Resources:
  CodeBuildStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt [ CodeBuildExecutionRole, Arn ]
      DefinitionString:
        !Sub
          - |-
            {
              "Comment": "An example of using CodeBuild to run (or not run) tests, get test results and send a notification.",
              "StartAt": "Run Tests?",
              "States": {
                "Run Tests?": {
                  "Type": "Choice",
                  "Choices": [
                    {
                      "Variable": "$.tests",
                      "BooleanEquals": false,
                      "Next": "Trigger CodeBuild Build Without Tests"
                    }
                  ],
                  "Default": "Trigger CodeBuild Build With Tests"
                },
                "Trigger CodeBuild Build With Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:startBuild.sync",
                  "Parameters": {
                    "ProjectName": "${projectName}"
                  },
                  "Next": "Get Test Results"
                },
                "Trigger CodeBuild Build Without Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:startBuild.sync",
                  "Parameters": {
                    "ProjectName": "${projectName}",
                    "BuildspecOverride": "buildspec-notests.yml"
                  },
                  "Next": "Notify No Tests"
                },
                "Get Test Results": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:batchGetReports",
                  "Parameters": {
                    "ReportArns.$": "$.Build.ReportArns"
                  },
                  "Next": "All Tests Passed?"
                },
                "All Tests Passed?": {
                  "Type": "Choice",
                  "Choices": [
                    {
                      "Variable": "$.Reports[0].Status",
                      "StringEquals": "SUCCEEDED",
                      "Next": "Notify Success"
                    }
                  ],
                  "Default": "Notify Failure"
                },
                "Notify Success": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build tests succeeded",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                },
                "Notify Failure": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build tests failed",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                },
                "Notify No Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build without tests",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                }
              }
            }
          - {snsTopicArn: !Ref SNSTopic, projectName: !Ref CodeBuildProject}
  SNSTopic:
    Type: AWS::SNS::Topic
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      ServiceRole: !Ref CodeBuildServiceRole
      Artifacts:
        Type: NO_ARTIFACTS
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:2.0
      Source:
        Type: CODECOMMIT
        Location: https://git-codecommit.us-east-1.amazonaws.com/v1/repos/binary-converter
  CodeBuildExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRole"
            Principal:
              Service: states.amazonaws.com
      Path: "/"
      Policies:
        - PolicyName: CodeBuildExecutionRolePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sns:Publish"
                Resource:
                  - !Ref SNSTopic
              - Effect: Allow
                Action:
                  - "codebuild:StartBuild"
                  - "codebuild:StopBuild"
                  - "codebuild:BatchGetBuilds"
                  - "codebuild:BatchGetReports"
                Resource: "*"
              - Effect: Allow
                Action:
                  - "events:PutTargets"
                  - "events:PutRule"
                  - "events:DescribeRule"
                Resource:
                  - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventForCodeBuildStartBuildRule"
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRole"
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
      Path: /
      Policies:
        - PolicyName: CodeBuildServiceRolePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - "logs:CreateLogGroup"
                - "logs:CreateLogStream"
                - "logs:PutLogEvents"
                - "codebuild:CreateReportGroup"
                - "codebuild:CreateReport"
                - "codebuild:UpdateReport"
                - "codebuild:BatchPutTestCases"
                - "codecommit:GitPull"
                Resource: "*"
Outputs:
  StateMachineArn:
    Value: !Ref CodeBuildStateMachine
  ExecutionInput:
    Description: Sample input to StartExecution.
    Value:
      >
        {}

CloudFormation 堆栈创建后,状态机定义中有两个 CodeBuild 任务:

  • 第一个是 CodeBuild 任务,它使用同步集成 (startBuild.sync) 自动等待构建终止后再进行下一步:
"Trigger CodeBuild Build With Tests": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "CodeBuildProject-HaVamwTeX8kM"
  },
  "Next": "Get Test Results"
}
  • 第二个是 CodeBuild 任务,它使用 BuildspecOverride 参数用未运行测试的文件覆盖构建所用的默认 buildspec 文件:
"Trigger CodeBuild Build Without Tests": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "CodeBuildProject-HaVamwTeX8kM",
    "BuildspecOverride": "buildspec-notests.yml"
  },
  "Next": "Notify No Tests"
},

第一步是选择查看状态机执行的输入,以决定是否运行测试。例如,要运行测试,我可以提供输入:

{
  "tests": true
}

这一步是执行运行测试的可视化工作流程,所有测试均通过了。

我将 "tests" 的值更改为 false,并开始在另一个分支上进行的新执行。

这一次,buildspec 没有执行测试,我收到没有运行测试的通知。

在 GitHub 或 CodeCommit 上的活动后自动开始此工作流程时,我可以查看特定模式的上一次提交消息,并相应自定义构建流程。例如,如果提交消息中包含 [skip tests] 字符串,我可以跳过测试。同样地,在生产环境中,如果提交中包含 [skip static analysis] 消息,我可以跳过代码静态分析,为紧急更改提供更快的集成。

扩展容器化应用程序的工作流程
将应用程序分配到不同环境的一种很好的做法是,将它们打包为 Docker 映像。用此方法,我还可以添加一个步骤到构建工作流程中,并在 Amazon ECS 任务(在 AWS Fargate 上运行)中为质量保证 (QA) 团队启动容器化应用程序。

首先,我在 ECR 中添加一个映像存储库,并为 CodeBuild 项目所用的服务角色添加上传到 ECR 的权限,如此处所述

然后,在代码库中,我按照此示例添加:

  • Dockerfile,以使用软件构建来准确 Docker 容器,并启动应用程序。
  • buildspec-docker.yml 文件,使用创建和上传 Docker 映像的命令。

最后一个工作流程是自动化以下所有步骤:

  1. 从源代码中构建软件。
  2. 创建 Docker 映像。
  3. 将 Docker 映像上传到 ECR。
  4. ECSFargate 上开始 QA 环境。
  5. 发送一条表示 QA 环境已准备好的 SNS 通知。

工作流程及其步骤可以根据您的要求轻松自定义。例如,通过几项更改,您便可以调整 buildspec 文件,以将映像推送到 Docker Hub

现已推出
CodeBuild 服务集成现已在提供 Step Functions 和 CodeBuild 服务的所有商业和 GovCloud 区域推出。有关区域可用性,请参阅 AWS 区域表。 有关更多信息,请查看文档

正如 AWS 无服务器精英 Gojko Adzic 在 AWS 开发运营博客中提到的那样,CodeBuild 还可以用于执行管理任务。与 Step Functions 的集成开启了一系列新的可能性。

欢迎与我分享您打算将这项新服务集成用于何处!

Danilo