You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置AWS CodePipeline后Bitbucket未显示分支/构建状态,缺什么?

问题:Bitbucket无法显示AWS CodePipeline分支及构建状态,缺少哪些配置?

我通过以下AWS CDK代码实现了连接Bitbucket的AWS CodePipeline,期望在Bitbucket平台上显示分支及构建状态,但尝试后未成功。请问还缺少哪些必要配置?

import { CfnOutput, Duration, RemovalPolicy } from 'aws-cdk-lib'
import * as codebuild from 'aws-cdk-lib/aws-codebuild'
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline'
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions'
import * as events from 'aws-cdk-lib/aws-events'
import * as targets from 'aws-cdk-lib/aws-events-targets'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as lambda from 'aws-cdk-lib/aws-lambda'
import * as s3 from 'aws-cdk-lib/aws-s3'
import { Construct } from 'constructs'
import { StackContext, use } from 'sst/constructs'
import { BITBUCKET_BRANCH, BITBUCKET_OWNER, BITBUCKET_REPO } from './config'
import { Connections } from './connections'

export interface BasePipelineProps {
  app: StackContext['app']
  stack: StackContext['stack']
  baseName: string
  pullRequests?: boolean
  branch?: string
  triggerOnPush?: boolean
}

export class BasePipeline extends Construct {
  pipeline: codepipeline.Pipeline
  buildRole: iam.Role
  sourceOutput: codepipeline.Artifact
  sourceAction: codepipeline_actions.CodeStarConnectionsSourceAction
  pipelineRole: iam.Role
  constructor(scope: Construct, id: string, props: BasePipelineProps) {
    super(scope, id)
    const { app, stack, triggerOnPush = true } = props
    const baseName = app.logicalPrefixedName(props.baseName)
    this.sourceOutput = new codepipeline.Artifact(`${baseName}SourceOutput`)
    const branch = props.branch ?? BITBUCKET_BRANCH

    this.buildRole = new iam.Role(stack, `${baseName}CodeBuildRole`, {
      assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
    })

    const { connectionArn, connectionPolicy } = use(Connections)

    this.buildRole.addToPolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
          'logs:CreateLogGroup',
          'logs:CreateLogStream',
          'logs:PutLogEvents',
          's3:GetBucketAcl',
          's3:GetBucketLocation',
          's3:GetObject',
          's3:GetObjectVersion',
          's3:PutObject',
          'sts:AssumeRole',
        ],
        resources: ['*'],
      }),
    )

    // Grant permissions to the build role
    this.buildRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'))

    // Create Pipeline role
    this.pipelineRole = new iam.Role(stack, `${baseName}CodePipelineRole`, {
      assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
    })

    this.pipelineRole.addToPolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
          'logs:CreateLogGroup',
          'logs:CreateLogStream',
          'logs:PutLogEvents',
          's3:GetObject',
          's3:GetObjectVersion',
          's3:GetBucketVersioning',
          's3:PutObject',
          'codebuild:BatchGetBuilds',
          'codebuild:StartBuild',
          'codebuild:StopBuild',
          'codestar-connections:UseConnection',
          'codestar-connections:GetConnection',
          'codestar-connections:PassConnection',
        ],
        resources: ['*'],
      }),
    )

    this.pipelineRole.addManagedPolicy(connectionPolicy)

    this.sourceAction = new codepipeline_actions.CodeStarConnectionsSourceAction({
      actionName: 'Source',
      owner: BITBUCKET_OWNER,
      repo: BITBUCKET_REPO,
      branch: branch,
      output: this.sourceOutput,
      connectionArn: connectionArn,
      triggerOnPush,
      variablesNamespace: 'pipeVariables',
      codeBuildCloneOutput: true,
      role: this.pipelineRole,
    })

    // create the artifact bucket
    const artifactBucket = new s3.Bucket(stack, `${baseName}ArtifactBucket`, {
      versioned: true,
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    })

    // Create Pipeline
    this.pipeline = new codepipeline.Pipeline(stack, `${baseName}Pipeline`, {
      pipelineType: codepipeline.PipelineType.V2,
      pipelineName: `${baseName}-pipeline`,
      crossAccountKeys: true,
      restartExecutionOnUpdate: true,
      executionMode: codepipeline.ExecutionMode.PARALLEL,
      role: this.pipelineRole,
      artifactBucket,
      ...(props.pullRequests && {
        triggers: [
          {
            providerType: codepipeline.ProviderType.CODE_STAR_SOURCE_CONNECTION,
            gitConfiguration: {
              sourceAction: this.sourceAction,
              pullRequestFilter: [
                {
                  events: [codepipeline.GitPullRequestEvent.OPEN, codepipeline.GitPullRequestEvent.UPDATED],
                  branchesIncludes: [branch],
                },
              ],
            },
          },
        ],
      }),
    })

    // Create Lint Project
    const lintProject = new codebuild.PipelineProject(stack, `${baseName}LintProject`, {
      buildSpec: codebuild.BuildSpec.fromObject({
        version: '0.2',
        phases: {
          install: {
            'runtime-versions': {
              nodejs: '20',
            },
            commands: ['npm install -g pnpm', 'pnpm install --store-dir=/root/.pnpm-store'],
          },
          build: {
            commands: ['pnpm lint'],
          },
        },
        cache: {
          paths: ['node_modules/**/*', '**/node_modules/**/*', 'web/.next/cache/**/*', '/root/.pnpm-store/**/*'],
        },
      }),
      environment: {
        buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
        privileged: true,
        environmentVariables: {
          DOCKER: { value: 'true' },
          NODE_OPTIONS: { value: '--max_old_space_size=8192' },
        },
      },
      role: this.buildRole,
    })

    this.pipeline.addStage({
      stageName: 'Source',
      actions: [this.sourceAction],
    })

    this.pipeline.addStage({
      stageName: 'Lint',
      actions: [
        new codepipeline_actions.CodeBuildAction({
          actionName: 'Lint',
          project: lintProject,
          input: this.sourceOutput,
          role: this.pipelineRole,
        }),
      ],
    })

    new CfnOutput(stack, `${baseName}PipelineURL`, {
      value: `https://${stack.region}.console.aws.amazon.com/codepipeline/home?region=${stack.region}#/view/${this.pipeline.pipelineName}`,
      description: 'Pipeline URL',
    })
  }
}

缺失的必要配置

AWS CodePipeline不会自动向Bitbucket推送构建状态,需要额外配置状态同步机制,同时补充Bitbucket侧的权限设置:

1. 配置EventBridge+Lambda同步Pipeline状态到Bitbucket

Bitbucket的提交状态需要通过其API主动更新,你需要:

  • 创建EventBridge规则,捕获CodePipeline的执行状态变化事件
  • 编写Lambda函数,接收事件后调用Bitbucket API更新对应提交的状态
  • 给Lambda配置必要的权限和环境变量

代码补充示例

在BasePipeline类的构造函数末尾添加以下代码:

// 1. 创建EventBridge规则,监听Pipeline执行状态变化
const pipelineStateRule = new events.Rule(stack, `${baseName}PipelineStateRule`, {
  eventPattern: {
    source: ['aws.codepipeline'],
    detailType: ['CodePipeline Pipeline Execution State Change'],
    detail: {
      pipeline: [this.pipeline.pipelineName],
    },
  },
});

// 2. 创建Lambda函数,处理Bitbucket状态更新
const bitbucketStatusLambda = new lambda.Function(stack, `${baseName}BitbucketStatusLambda`, {
  runtime: lambda.Runtime.NODEJS_20_X,
  handler: 'index.handler',
  code: lambda.Code.fromInline(`
    const axios = require('axios');
    exports.handler = async (event) => {
      const executionStatus = event.detail.state;
      const commitId = event.detail.executionTrigger?.commitId;
      const bitbucketToken = process.env.BITBUCKET_TOKEN;
      const repoOwner = process.env.BITBUCKET_OWNER;
      const repoName = process.env.BITBUCKET_REPO;
      const region = process.env.AWS_REGION;
      const pipelineName = event.detail.pipeline;

      if (!commitId || !bitbucketToken) return;

      // 映射AWS状态到Bitbucket状态值
      const bitbucketState = {
        SUCCEEDED: 'SUCCESSFUL',
        FAILED: 'FAILED',
        STARTED: 'INPROGRESS',
        RESUMED: 'INPROGRESS',
        CANCELED: 'STOPPED'
      }[executionStatus] || 'INPROGRESS';

      try {
        await axios.post(
          \`https://api.bitbucket.org/2.0/repositories/\${repoOwner}/\${repoName}/commit/\${commitId}/statuses/build\`,
          {
            state: bitbucketState,
            key: 'aws-codepipeline',
            name: 'AWS CodePipeline',
            url: \`https://\${region}.console.aws.amazon.com/codepipeline/home?region=\${region}#/view/\${pipelineName}\`,
            description: \`Pipeline执行状态:\${executionStatus}\`,
          },
          {
            headers: { Authorization: \`Bearer \${bitbucketToken}\` },
          }
        );
      } catch (err) {
        console.error('更新Bitbucket状态失败:', err.response?.data || err.message);
      }
    };
  `),
  environment: {
    BITBUCKET_TOKEN: '你的Bitbucket个人访问令牌', // 建议用AWS Secrets Manager存储,不要硬编码
    BITBUCKET_OWNER: BITBUCKET_OWNER,
    BITBUCKET_REPO: BITBUCKET_REPO,
    AWS_REGION: stack.region,
  },
});

// 3. 给Lambda添加读取CodePipeline执行信息的权限
bitbucketStatusLambda.addToRolePolicy(new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  actions: ['codepipeline:GetPipelineExecution'],
  resources: [this.pipeline.pipelineArn],
}));

// 4. 将Lambda设置为EventBridge规则的目标
pipelineStateRule.addTarget(new targets.LambdaFunction(bitbucketStatusLambda));

2. Bitbucket侧配置个人访问令牌(PAT)

在Bitbucket账户中创建一个PAT,需要勾选Repository > Write权限(用于更新提交状态),然后将该令牌安全存储(比如AWS Secrets Manager),再在Lambda的环境变量中引用。

3. 确认CodeStar Connection权限

在AWS控制台的CodeStar Connections中,确认连接已成功授权Bitbucket仓库,并且连接状态为"已连接"。

4. (可选)Pull Request状态同步

如果需要在Bitbucket PR中显示构建状态,需要确保:

  • Pipeline的PR触发器配置正确(你的代码中已包含pullRequests相关配置)
  • Lambda函数中处理PR相关的执行事件,从事件中提取PR对应的提交ID进行状态更新

内容的提问来源于stack exchange,提问作者PlayMa256

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:45:54