azure - 在 Azure DevOps 中删除源分支时如何删除 Azure Static Web App 分支预览环境?

标签 azure azure-devops azure-pipelines azure-static-web-app

背景

我正在使用Azure DevOps用于托管我的 Web 应用程序的源并将应用程序构建/部署到 Azure Static Web App .

我正在使用静态 Web 应用程序的“分支预览环境”,如下所示 ( source ):

steps:
  ...
  - task: AzureStaticWebApp@0
    inputs:
      ...
      production_branch: 'main'

到目前为止效果很好。例如,如果我使用分支“dev”,则会创建相应的分支环境。

问题

在删除为其创建的 Azure 静态 Web 应用分支预览环境后,如何自动删除它?

使用 Azure cli?

到目前为止我发现的唯一方法是使用 Azure CLI - 但如何实现自动化?

az staticwebapp environment delete --name my-static-app \
  --environment-name an-env-name --subscription my-sub

最佳答案

我通过创建一个由 main 分支触发的单独管道解决了这个问题。该管道会删除所有没有开放拉取请求的部署。

这是管道,基本上只是调用负责清理的节点脚本:

name: Cleanup static web apps

trigger:
  - main

# Add the following variables into devops:
# - DEVOPS_PAT: your personal access token for DevOps
# - AZURE_SUBSCRIPTION: the subscription in azure under which your swa lives
variables:
  NPM_CONFIG_CACHE: $(Pipeline.Workspace)/.npm
  DEVOPS_ORG_URL: "https://dev.azure.com/feedm3"
  DEVOPS_PROJECT: "azure-playground"
  AZURE_STATIC_WEBAPP_NAME: "react-app"

jobs:
- job: cleanup_preview_environments_job
  displayName: Cleanup
  pool:
    vmImage: ubuntu-latest
  steps:
  - task: Cache@2
    inputs:
      key: 'npm | "$(Agent.OS)" | package-lock.json'
      restoreKeys: |
        npm | "$(Agent.OS)"
      path: $(NPM_CONFIG_CACHE)
    displayName: "Cache npm"

  - script: |
      npm ci
    displayName: "Install dependencies"
  - task: AzureCLI@2
    inputs:
      azureSubscription: "test-service-connection-name"
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: |
        npm run ci:cleanup-deployments
    displayName: "Cleanup outdated deployments"

这是删除部署的实际脚本:

import { getPersonalAccessTokenHandler, WebApi } from "azure-devops-node-api";
import { exec as callbackExec } from 'child_process';
import { promisify } from 'util';

const exec = promisify(callbackExec);

const DEVOPS_ORG_URL = process.env["DEVOPS_ORG_URL"] as string;
const DEVOPS_PROJECT = process.env["DEVOPS_PROJECT"] as string;
const DEVOPS_PAT = process.env["DEVOPS_PAT"] as string;
const AZURE_SUBSCRIPTION = process.env["AZURE_SUBSCRIPTION"] as string;
const AZURE_STATIC_WEBAPP_NAME = process.env["AZURE_STATIC_WEBAPP_NAME"] as string;

const ALWAYS_DEPLOYED_BRANCHES = ['main'];

const REPO_ID = process.env['BUILD_REPOSITORY_ID'] as string;

const getAllStaticWebAppDeployments = async (): Promise<{ name: string; sourceBranch: string, hostname: string }[]> => {
  const { stdout, stderr } = await exec(`az staticwebapp environment list --name ${AZURE_STATIC_WEBAPP_NAME} --subscription ${AZURE_SUBSCRIPTION}`);
  if (stderr) {
    console.error('Command failed!', stderr);
    throw new Error(stderr);
  }

  return JSON.parse(stdout);
}

const run = async () => {
  console.log(`Cleanup outdated deployments ${{REPO_ID, DEVOPS_PROJECT, AZURE_STATIC_WEBAPP_NAME}}...`)

  const webAppDeployments = await getAllStaticWebAppDeployments();

  // post comment
  const authHandler = getPersonalAccessTokenHandler(DEVOPS_PAT);
  const connection = new WebApi(DEVOPS_ORG_URL, authHandler);

  await connection.connect();

  const gitApi = await connection.getGitApi(`${DEVOPS_ORG_URL}/${DEVOPS_PROJECT}`);

  // status 1 is active (PullRequestStatus type)
  const activePullRequests = await gitApi.getPullRequests(REPO_ID, { status: 1 });
  const activePullRequestBranches = activePullRequests.map(pr => pr.sourceRefName).filter(Boolean).map(fullBranchName => fullBranchName!.split('/')[2]);

  // main deployment should always be alive
  activePullRequestBranches.push(...ALWAYS_DEPLOYED_BRANCHES);

  const outdatedDeployments = webAppDeployments.filter(deployment => {
    return !activePullRequestBranches.includes(deployment.sourceBranch);
  })
  console.log('Deployments to delete:', outdatedDeployments);

  for await (const deployment of outdatedDeployments) {
    const deploymentName = deployment.name;
    console.log(`Deleting deployment ${deploymentName}...`);

    /**
     * Deletion works, but ends with an irrelevant error.
     */
    try {
      const { stderr } = await exec(`az staticwebapp environment delete --name ${AZURE_STATIC_WEBAPP_NAME} --subscription ${AZURE_SUBSCRIPTION}  --environment-name ${deploymentName} --yes`);
      if (stderr) {
        console.error('Could not delete deployment ', deploymentName);
      } else {
        console.log('Deleted deployment ', deploymentName);
      }
    } catch (e) {
      console.log('Deleted deployment ', deploymentName);
    }
  }

  console.log('Outdated deployments cleared!')
}

await run();

完整的存储库可以在这里找到:https://github.com/feedm3/learning-azure-swa-devops

关于azure - 在 Azure DevOps 中删除源分支时如何删除 Azure Static Web App 分支预览环境?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74069643/

相关文章:

Azure DNS 转发器无法使用公共(public)资源

azure - 如何检查arm模板中是否存在资源

azure - Azure Synapse 中的外部表列不允许使用 NOT NULL

azure-devops - 如何在不进行部署的情况下调试和开发Azure DevOps扩展?

azure-devops - 如何在自定义 azure devops 插件中添加自定义摘要选项卡,当实验主题和多阶段管道处于开启模式时?

Azure PAM 未收到来自 ‎"Microsoft Graph data connect‎"数据工厂管道执行的特权访问请求

Azure Build Pipeline 无法发布项目

python - 树莓派中无法导入Azure ServiceBus

c# - 如何从 azure OAuth 2.0 token (v2) 端点获取/生成访问 token ?

azure-devops - 在azure devops中触发具有特定分支名称(模式)的作业