다음을 통해 공유


GitHub Actions를 사용하여 파이프라인 확장

이 문서는 GitHub Actions 및 Power Automate 클라우드 흐름을 사용하여 Power Platform에서 파이프라인을 확장하는 방법을 보여줍니다. 파이프라인 배포가 제출되면 클라우드 흐름이 GitHub 워크플로를 트리거하여 아티팩트의 소스 코드를 다운로드하고 압축을 풀고 GitHub 분기에 커밋합니다.

워크플로 세부 정보

워크플로는 workflow_dispatch 이벤트를 통해 트리거됩니다. 워크플로는 ubuntu-latest에서 실행되며 GitHub 리포지토리 분기에 대한 변경 사항을 커밋할 수 있는 contents: write 권한을 포함합니다.

워크플로는 다음 단계로 구성됩니다.

  1. actions/checkout@v3: 리포지토리를 체크아웃합니다.
  2. create new branch if specified: 입력에 target_branch가 지정된 경우 새 분기를 생성합니다.
  3. download solution from artifact: 파이프라인에서 생성된 아티팩트에서 솔루션을 다운로드합니다.
  4. unpack solution: 솔루션의 압축을 풉니다.
  5. commit changes: 기존 또는 새 브랜치에 대한 변경 사항을 커밋합니다.
  6. push to branch: 커밋된 변경 사항을 소스 분기로 푸시합니다.

워크플로 입력

다음 워크플로 입력은 필수 또는 선택 사항입니다.

  • artifact_url (필수): 파이프라인에서 생성된 아티팩트에 대한 Microsoft Dataverse 행(레코드) ID의 URL입니다.
  • solution_name (필수): Dataverse 환경의 솔루션 이름입니다.
  • source_branch (필수) : 솔루션 커밋을 위한 분기입니다.
  • target_branch (선택 사항): 솔루션 커밋을 위해 생성할 분기입니다. 지정하지 않으면 source_branch가 사용됩니다.
  • commit_message (필수): 커밋을 위해 제공할 메시지입니다.

워크플로 암호

Dataverse 및 Microsoft Entra ID(AD)에 구성된 애플리케이션 사용자를 사용하여 Dataverse에 연결하려면 다음 암호가 필요합니다. GitHub 리포지토리 설정에서 이러한 암호를 구성합니다.

  • CLIENT_ID: 등록된 Microsoft Entra 애플리케이션의 클라이언트 ID입니다.
  • TENANT_ID: Microsoft Entra 애플리케이션과 연결된 Microsoft Entra 디렉터리의 테넌트 ID입니다.
  • CLIENT_SECRET: 등록된 Microsoft Entra 애플리케이션의 클라이언트 암호입니다.

자세한 내용은 암호화된 비밀 만들기 및 사용하기애플리케이션 사용자 만들기를 참고하세요.

워크플로 코드

아래 목록은 GitHub Actions 워크플로 코드입니다.

name: Download, unpack and commit the solution to git
run-name: Getting ${{ github.event.inputs.solution_name }} from pipelines host environment and committing
on:
  workflow_dispatch:
    inputs:
      artifact_url:
        description: "The url of the Dataverse record ID for the artifact created by the pipelines (Example: https://[your-env].crm.dynamics.com/api/data/v9.0/deploymentartifacts([your-artifact-id])/artifactfile/$value)."
        required: true
      solution_name:
        description: "Name of the Solution in Dataverse environment"
        required: true
      user_name: 
        description: "User name for the commit"
        required: true
      source_branch:
        description: "Branch for the solution commit"
        required: true
      target_branch:
        description: "Branch to create for the solution commit"
        required: false
      commit_message:
        description: "Message to provide for the commit"
        required: true
permissions:
  contents: write
jobs:
  export-unpack-commit:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
        with:
            ref: ${{ github.event.inputs.source_branch }}

      # Commit changes to the existing or new branch
      - name: create new branch if specified
        shell: pwsh
        run: |
            if('${{ github.event.inputs.target_branch }}' -ne '') {
                git checkout -b ${{ github.event.inputs.target_branch }} ${{ github.event.inputs.source_branch }}
            }

      # Export the solution from the artifact created by pipelines
      - name: download solution from artifact
        env:
            CLIENT_ID: ${{secrets.CLIENT_ID}}   
            TENANT_ID: ${{secrets.TENANT_ID}}   
            CLIENT_SECRET: ${{secrets.CLIENT_SECRET}}
        shell: pwsh
        run: |
            $aadHost = "login.microsoftonline.com"
            $url = "${{ github.event.inputs.artifact_url }}"
            $options = [System.StringSplitOptions]::RemoveEmptyEntries
            $dataverseHost = $url.Split("://", $options)[1].Split("/")[0]

            $body = @{client_id = $env:CLIENT_ID; client_secret = $env:CLIENT_SECRET; grant_type = "client_credentials"; scope = "https://$dataverseHost/.default"; }
            $OAuthReq = Invoke-RestMethod -Method Post -Uri "https://$aadHost/$env:TENANT_ID/oauth2/v2.0/token" -Body $body
            $spnToken = $OAuthReq.access_token
            $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
            $headers.Add("Authorization", "Bearer $spnToken")
            $headers.Add("Content-Type", "application/json")

            # Download the managed solution
            $response = Invoke-RestMethod "${{ github.event.inputs.artifact_url }}" -Method 'GET' -Headers $headers
            $bytes = [Convert]::FromBase64String($response.value)
            [IO.File]::WriteAllBytes("${{ github.event.inputs.solution_name }}_managed.zip", $bytes)

            # Download the unmanaged solution (for now we will need to use string manipulation to get the unmanaged solution URL, until the API provides this value)
            $unmanaged_artifact_url = "${{ github.event.inputs.artifact_url }}".Replace("artifactfile", "artifactfileunmanaged")
            $response = Invoke-RestMethod "$unmanaged_artifact_url" -Method 'GET' -Headers $headers
            $bytes = [Convert]::FromBase64String($response.value)
            [IO.File]::WriteAllBytes("${{ github.event.inputs.solution_name }}.zip", $bytes)

      # Unpack the solution
      - name: unpack solution
        uses: microsoft/powerplatform-actions/unpack-solution@v0
        with:
          solution-file: "${{ github.event.inputs.solution_name }}.zip"
          solution-folder: "${{ github.event.repository.name }}"
          solution-type: 'Both'
          process-canvas-apps: false
          overwrite-files: true

      # Commit changes to the existing or new branch
      - name: commit changes
        shell: pwsh
        run: |
          rm -rf ${{ github.event.inputs.solution_name }}.zip
          rm -rf ${{ github.event.inputs.solution_name }}_managed.zip
          git config user.name ${{ github.event.inputs.user_name }}
          git pull 
          git add --all
          git commit -am "${{ github.event.inputs.commit_message }}" --allow-empty

      # Push the committed changes to the source branch
      - name: push to branch
        shell: pwsh
        run: |
          if('${{ github.event.inputs.target_branch }}' -ne '') {
              git push origin ${{ github.event.inputs.target_branch }}
          } else {
              git push origin ${{ github.event.inputs.source_branch }}
          }

참고

솔루션 아티팩트를 다운로드하는 데 사용되는 Dataverse 웹 API의 최대 파일 크기 제한은 16MB입니다.

Power Automate 흐름 예

GitHub 워크플로를 호출하기 위해 Dataverse에서 배포 요청이 있을 때 트리거되는 Power Automate 흐름을 만들 수 있습니다. 필요한 입력을 GitHub 워크플로에 전달하도록 흐름을 구성할 수 있습니다. Power Automate 흐름을 만드는 방법에 대한 자세한 내용은 흐름 만들기를 참고하세요.

흐름 세부 정보

OnDeploymentRequested 작업이 Dataverse에서 실행될 때 흐름이 트리거됩니다. 흐름은 HTTP 커넥터를 호출하여 GitHub 워크플로를 트리거합니다. 흐름은 필요한 입력을 GitHub 워크플로에 전달합니다. 요청 본문에 다음 입력을 포함합니다.

  • artifact_url: 파이프라인에서 생성된 Dataverse 솔루션 아티팩트의 URL입니다.
  • solution_name: Dataverse 환경의 솔루션 이름입니다.
  • user_name: 커밋을 위한 사용자 이름입니다.
  • source_branch: 솔루션 커밋을 위한 소스 분기입니다.
  • target_branch: 솔루션 커밋을 위해 생성할 분기입니다.
  • commit_message: 커밋을 위해 제공할 메시지입니다.

artifact_url, solution_nameuser_name에 전달된 값은 파이프라인을 트리거한 작업의 출력에서 가져옵니다. commit_message는 Dataverse의 배포 단계 실행 행에서 가져옵니다.

  • artifact_url: @{triggerOutputs()?['body/OutputParameters/ArtifactFileDownloadLink']}
  • solution_name: @{triggerOutputs()?['body/OutputParameters/ArtifactName']}
  • user_name: @{triggerOutputs()?['body/OutputParameters/DeployAsUser']}
  • commit_message: @{outputs('Retrieve_the_Deployment_Stage_Run')?['body/deploymentnotes']}

흐름은 또한 개인용 액세스 토큰(PAT)를 사용하여 GitHub에 인증합니다. GitHub 개인 액세스 토큰을 만드는 방법에 대한 자세한 내용은 개인용 액세스 토큰 만들기를 참조하세요. PAT는 HTTP 요청의 Authorization 헤더로 전달됩니다.

흐름에서 다음 값을 업데이트합니다.

  • [GitHub Personal Access Token] - GitHub 개인용 액세스 토큰으로 바꿉니다.
  • [GitHub Organization] - GitHub 조직 이름으로 바꿉니다.
  • [GitHub Repository] - GitHub 리포지토리 이름으로 바꿉니다.
  • [GitHub Workflow YAML File] - GitHub 워크플로 YAML 파일 이름으로 바꿉니다.
  • [Source Branch] - Git 분기로 교체하여 솔루션을 커밋합니다.
  • [Target Branch] - 솔루션 커밋을 위해 만들 Git 분기로 바꿉니다. Target Branch는 선택 사항입니다. 대상 분기를 지정하지 않으면 솔루션이 Source Branch에 커밋됩니다.

HTTP 커넥터를 사용하여 연결된 배포 단계를 검색하고 GitHub 워크플로를 실행하고 호출하는 단계와 함께 OnDeploymentRequested 트리거를 보여 주는 Power Automate 흐름

다음 단계

Power Platform의 파이프라인 실행

참조 항목

GitHub Actions용 빠른 시작
Power Platform의 파이프라인 확장
클라우드 흐름이란?