亚马逊AWS官方博客
新功能 – 使用 Step Functions 和 AWS CodeBuild 构建持续集成工作流程
自动化软件构建过程是采用开发运营最佳实践的重要一步。为了帮助您实现目的,我们构建了 AWS CodeBuild,该服务是一项完全托管的持续集成服务,可编译源代码、运行测试并生成可供部署使用的程序包。
然而,客户的构建过程中可能存在很多自定义选项,我们已经看到有开发人员花费时间构建自己的自定义工作流程,以协调其软件构建所需的不同活动。例如,您可能会在需要部署快速修复时想要运行或不运行某些测试,或跳过代码的静态分析。 根据您的单元测试结果,您可能想要采取不同的错误,或通过 SNS 接收通知。
为了简化此过程,我们今天推出了全新的与 CodeBuild 的 AWS Step Functions 服务集成。现在,在执行状态机的过程中,您可以开始或停止构建、获取构建报告摘要并删除过去的构建执行记录。
用此方式,您可以定义自己的工作流程驱动型构建过程,并且手动或自动触发它。例如,您可以:
- 使用 Amazon EventBridge 规则定期(适用于每晚构建)或在发生某些情况时(例如,AWS CodeCommit 存储库收到 pull 请求)开始构建工作流程。
- 构建一个可以由 GitHub 等服务使用 Amazon API Gateway(通过直接与状态机集成或通过在开始工作流程前检查输入有效负载有效性的 AWS Lambda 函数)调用的 Webhook。
- 自定义 AWS CodePipeline 操作的行为,如本博文所述。
此次集成后,您可以使用 Step Functions 全部功能来自动执行您的软件构建。例如,您可以使用 Parallel 状态为版本的独立组件构建并行版本。您可以从代码库中所有的分支列表中使用 Map 状态为每个分支运行一组步骤(自动化构建、单元测试和集成测试)。 您还可以在同一个工作流程中利用其他 Step Functions 服务集成。 例如,您可以发送一条消息到 SQS 队列来跟踪您的活动,或者启动您刚使用 Amazon ECS 和 AWS 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 映像的命令。
最后一个工作流程是自动化以下所有步骤:
工作流程及其步骤可以根据您的要求轻松自定义。例如,通过几项更改,您便可以调整 buildspec 文件,以将映像推送到 Docker Hub。
现已推出
 CodeBuild 服务集成现已在提供 Step Functions 和 CodeBuild 服务的所有商业和 GovCloud 区域推出。有关区域可用性,请参阅 AWS 区域表。 有关更多信息,请查看文档。
正如 AWS 无服务器精英 Gojko Adzic 在 AWS 开发运营博客中提到的那样,CodeBuild 还可以用于执行管理任务。与 Step Functions 的集成开启了一系列新的可能性。
欢迎与我分享您打算将这项新服务集成用于何处!
– Danilo


