Skip to content

Commit cd15ac2

Browse files
gavinbarronCopilot
andauthored
ci: use GitHub App identity instead of PAT for Agents-M365Copilot (#1442)
* ci: use GitHub App identity instead of PAT for Agents-M365Copilot Pivots the Agents-M365Copilot generation flow off the PAT-backed `microsoftkiota` service connection and onto the existing `microsoft-graph-devx-bot` GitHub App identity. - Add opt-in `useGitHubAppAuth` param to language-generation-kiota.yml. When true, the public repo is cloned anonymously and the push remote is switched to a GitHub App installation token before pushing. SDK repos (param defaults false) are unaffected. - Add scripts/set-app-token-push-url.ps1 to set the origin push URL to the App token without logging it (fetch stays anonymous). - Remove the Agents-M365Copilot repo resource (microsoftkiota endpoint) and its SDL-exclude entry; set useGitHubAppAuth: true on the 6 Agents jobs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 74cd916 commit cd15ac2

3 files changed

Lines changed: 98 additions & 15 deletions

File tree

.azure-pipelines/generation-pipeline.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ resources:
9797
type: github
9898
endpoint: microsoftgraph (22)
9999
name: microsoftgraph/microsoft-graph-docs
100-
- repository: Agents-M365Copilot
101-
type: github
102-
endpoint: microsoftkiota
103-
name: microsoft/Agents-M365Copilot
104100
- repository: 1ESPipelineTemplates
105101
type: git
106102
name: 1ESPipelineTemplates/1ESPipelineTemplates
@@ -190,7 +186,6 @@ extends:
190186
- repository: msgraph-beta-sdk-python
191187
- repository: msgraph-metadata
192188
- repository: microsoft-graph-docs
193-
- repository: Agents-M365Copilot
194189
- repository: 1ESPipelineTemplates
195190
pool:
196191
name: Azure-Pipelines-1ESPT-ExDShared
@@ -963,6 +958,7 @@ extends:
963958
targetClassName: "BaseAgentsM365CopilotServiceClient"
964959
targetNamespace: "Microsoft.Agents.M365Copilot"
965960
commitMessagePrefix: "feat(generation): update request builders and models for dotnet v1"
961+
useGitHubAppAuth: true
966962
customArguments: "-i '**/copilot/**'" # include only copilot paths
967963
cleanMetadataFolder: $(cleanOpenAPIFolderV1)
968964
pathExclusionArguments: ''
@@ -1009,6 +1005,7 @@ extends:
10091005
targetClassName: "BaseAgentsM365CopilotBetaServiceClient"
10101006
targetNamespace: "Microsoft.Agents.M365Copilot.Beta"
10111007
commitMessagePrefix: "feat(generation): update request builders and models for dotnet beta"
1008+
useGitHubAppAuth: true
10121009
customArguments: "-i '**/copilot/**'" # include only copilot paths
10131010
cleanMetadataFolder: $(cleanOpenAPIFolderBeta)
10141011
pathExclusionArguments: ''
@@ -1057,6 +1054,7 @@ extends:
10571054
customArguments: "-i '**/copilot/**'" # include only copilot paths
10581055
cleanMetadataFolder: $(cleanOpenAPIFolderV1)
10591056
commitMessagePrefix: "feat(generation): update request builders and models for python v1"
1057+
useGitHubAppAuth: true
10601058
pathExclusionArguments: ''
10611059
languageSpecificSteps:
10621060
- template: /.azure-pipelines/generation-templates/python.yml@self
@@ -1103,6 +1101,7 @@ extends:
11031101
customArguments: "-i '**/copilot/**'" # include only copilot paths
11041102
cleanMetadataFolder: $(cleanOpenAPIFolderBeta)
11051103
commitMessagePrefix: "feat(generation): update request builders and models for python beta"
1104+
useGitHubAppAuth: true
11061105
pathExclusionArguments: ''
11071106
languageSpecificSteps:
11081107
- template: /.azure-pipelines/generation-templates/python.yml@self
@@ -1145,6 +1144,7 @@ extends:
11451144
baseBranchName: 'main'
11461145
branchName: 'ccs-typescript/$(v1Branch)'
11471146
commitMessagePrefix: "feat(generation): update request builders and models for typescript v1"
1147+
useGitHubAppAuth: true
11481148
targetClassName: "BaseAgentsM365CopilotServiceClient"
11491149
targetNamespace: "github.com/microsoft/Agents-M365Copilot/typescript/"
11501150
customArguments: "-i '**/copilot/**'"
@@ -1191,6 +1191,7 @@ extends:
11911191
baseBranchName: 'main'
11921192
branchName: 'ccs-typescript/$(betaBranch)'
11931193
commitMessagePrefix: "feat(generation): update request builders and models for typescript beta"
1194+
useGitHubAppAuth: true
11941195
targetClassName: "BaseAgentsM365CopilotBetaServiceClient"
11951196
targetNamespace: "github.com/microsoft/Agents-M365Copilot/typescript/"
11961197
customArguments: "-i '**/copilot/**'"

.azure-pipelines/generation-templates/language-generation-kiota.yml

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,25 @@ parameters:
5656
type: string
5757
default: "-e '/copilot' -e '/copilot/**'"
5858

59+
# When true, the target repository is read anonymously (public repo) and the generated
60+
# files are pushed using a GitHub App installation token instead of a PAT-backed service
61+
# connection. PR creation is App-based regardless. Defaults to false to preserve the
62+
# existing service connection based behaviour for the microsoftgraph SDK repos.
63+
- name: useGitHubAppAuth
64+
type: boolean
65+
default: false
66+
5967
steps:
6068
- template: /.azure-pipelines/generation-templates/set-up-for-generation-kiota.yml@self
6169
parameters:
6270
cleanMetadataFolder: ${{ parameters.cleanMetadataFolder }}
6371

64-
- checkout: ${{ parameters.repoName }}
65-
displayName: 'checkout ${{ parameters.repoName }}'
66-
fetchDepth: 1
67-
persistCredentials: true
72+
# Default path: check out the target repo via its service connection.
73+
${{ if eq(parameters.useGitHubAppAuth, false) }}:
74+
- checkout: ${{ parameters.repoName }}
75+
displayName: 'checkout ${{ parameters.repoName }}'
76+
fetchDepth: 1
77+
persistCredentials: true
6878

6979
# need this for the shared scripts (maybe we should move them to msgraph-metadata)
7080
# no need for recursive, just scripts from the main repo
@@ -73,6 +83,16 @@ steps:
7383
fetchDepth: 1
7484
persistCredentials: true
7585

86+
# App-auth path: the target repo is public, so clone it anonymously (no PAT). The push
87+
# remote is switched to a GitHub App token in a later step before the push happens.
88+
${{ if eq(parameters.useGitHubAppAuth, true) }}:
89+
- pwsh: |
90+
$repoDir = "$(Build.SourcesDirectory)/${{ parameters.repoName }}"
91+
if (Test-Path $repoDir) { Remove-Item $repoDir -Recurse -Force }
92+
git clone --depth 1 "https://github.com/${{ parameters.orgName }}/${{ parameters.repoName }}.git" "$repoDir"
93+
if ($LASTEXITCODE -ne 0) { throw "Failed to clone ${{ parameters.orgName }}/${{ parameters.repoName }}" }
94+
displayName: 'Clone ${{ parameters.repoName }} (anonymous, public repo)'
95+
7696
- pwsh: '$(scriptsDirectory)/checkout-custom-base-branch.ps1'
7797
displayName: 'Checking out custom base branch ${{ parameters.baseBranchName }}'
7898
env:
@@ -103,6 +123,25 @@ steps:
103123

104124
- ${{ parameters.languageSpecificSteps }}
105125

126+
# App-auth path: fetch the App secrets and switch the push remote to the App token
127+
# BEFORE pushing.
128+
${{ if eq(parameters.useGitHubAppAuth, true) }}:
129+
- task: AzureKeyVault@2
130+
displayName: "Azure Key Vault: Get Secrets"
131+
inputs:
132+
azureSubscription: "Federated AKV Managed Identity Connection"
133+
KeyVaultName: akv-prod-eastus
134+
SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey"
135+
136+
- pwsh: '$(scriptsDirectory)/set-app-token-push-url.ps1'
137+
displayName: 'Git: use GitHub App token for push'
138+
env:
139+
GhAppId: $(microsoft-graph-devx-bot-appid)
140+
GhAppKey: $(microsoft-graph-devx-bot-privatekey)
141+
RepoName: '${{ parameters.orgName }}/${{ parameters.repoName }}'
142+
ScriptsDirectory: $(scriptsDirectory)
143+
workingDirectory: ${{ parameters.repoName }}
144+
106145
- pwsh: '$(scriptsDirectory)/git-push-files.ps1'
107146
displayName: 'Git: push generated files'
108147
env:
@@ -112,12 +151,15 @@ steps:
112151
CommitMessagePrefix: ${{ parameters.commitMessagePrefix }}
113152
workingDirectory: ${{ parameters.repoName }}
114153

115-
- task: AzureKeyVault@2
116-
displayName: "Azure Key Vault: Get Secrets"
117-
inputs:
118-
azureSubscription: "Federated AKV Managed Identity Connection"
119-
KeyVaultName: akv-prod-eastus
120-
SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey"
154+
# Default path: secrets are fetched after the push, just before PR creation. On the
155+
# App-auth path the secrets were already fetched above and persist for the PR step.
156+
${{ if eq(parameters.useGitHubAppAuth, false) }}:
157+
- task: AzureKeyVault@2
158+
displayName: "Azure Key Vault: Get Secrets"
159+
inputs:
160+
azureSubscription: "Federated AKV Managed Identity Connection"
161+
KeyVaultName: akv-prod-eastus
162+
SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey"
121163

122164
- pwsh: '$(scriptsDirectory)/create-pull-request.ps1'
123165
displayName: 'Create Pull Request for the generated build for ${{ parameters.repoName }}'

scripts/set-app-token-push-url.ps1

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# Configure the 'origin' push URL of an already-cloned repository to authenticate with a
5+
# GitHub App installation token instead of a PAT. The fetch URL is left untouched so reads
6+
# stay anonymous for public repositories.
7+
#
8+
# Required environment variables:
9+
# GhAppId - GitHub App client ID (from AKV)
10+
# GhAppKey - GitHub App private key (from AKV)
11+
# RepoName - Target repo in "owner/repo" format (e.g. "microsoft/Agents-M365Copilot")
12+
# ScriptsDirectory - Path to the scripts folder containing Generate-Github-Token.ps1
13+
# Runs from the repository working directory (the cloned repo).
14+
15+
[CmdletBinding()]
16+
param ()
17+
18+
$ErrorActionPreference = "Stop"
19+
20+
if ([string]::IsNullOrWhiteSpace($env:GhAppId)) { throw "GhAppId is required" }
21+
if ([string]::IsNullOrWhiteSpace($env:GhAppKey)) { throw "GhAppKey is required" }
22+
if ([string]::IsNullOrWhiteSpace($env:RepoName)) { throw "RepoName is required" }
23+
if ([string]::IsNullOrWhiteSpace($env:ScriptsDirectory)){ throw "ScriptsDirectory is required" }
24+
25+
# The installed application is required to have contents:write on the target repository.
26+
$tokenGenerationScript = Join-Path $env:ScriptsDirectory "Generate-Github-Token.ps1"
27+
$token = & $tokenGenerationScript -AppClientId $env:GhAppId -AppPrivateKeyContents $env:GhAppKey -Repository $env:RepoName
28+
if ([string]::IsNullOrWhiteSpace($token)) {
29+
throw "Failed to generate GitHub App installation token (empty result)"
30+
}
31+
32+
# Mask the token so it is never surfaced in pipeline logs.
33+
Write-Host "##vso[task.setsecret]$token"
34+
35+
# Set the push URL only; leave the (anonymous) fetch URL alone.
36+
$pushUrl = "https://x-access-token:$token@github.com/$($env:RepoName).git"
37+
git remote set-url --push origin $pushUrl
38+
if ($LASTEXITCODE -ne 0) { throw "Failed to set push URL for origin" }
39+
40+
Write-Host "Configured origin push URL to use the GitHub App installation token." -ForegroundColor Green

0 commit comments

Comments
 (0)