Skip to content

Commit 49d16ad

Browse files
authored
Merge pull request #1014 from microsoftgraph/feat/adding-typespec-reference-generation-pipeline
Adding Typespec reference library generation pipeline created
2 parents dc1d05a + 4651815 commit 49d16ad

File tree

5 files changed

+508
-0
lines changed

5 files changed

+508
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
2+
# contains an end to end validation pipeline using C# compilation tests for staging beta metadata
3+
trigger:
4+
branches:
5+
include:
6+
- master
7+
paths:
8+
include:
9+
- schemas/*.csdl
10+
pr: none
11+
12+
resources:
13+
repositories:
14+
- repository: typespec-msgraph
15+
type: github
16+
endpoint: microsoftgraph (22)
17+
name: microsoftgraph/typespec-msgraph
18+
ref: main
19+
- repository: typespec-msgraph-reference
20+
type: git
21+
name: typespec-msgraph-reference
22+
variables:
23+
BuildConfiguration: 'Release'
24+
scriptsDirectory: '$(Build.SourcesDirectory)\.azure-pipelines\scripts'
25+
26+
extends:
27+
template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
28+
parameters:
29+
pool:
30+
name: Azure-Pipelines-1ESPT-ExDShared
31+
vmImage: windows-latest
32+
stages:
33+
- stage: build_reference_tool
34+
dependsOn: []
35+
jobs:
36+
- job: buildReferenceTool
37+
displayName: "Build Reference Lib Artifact"
38+
templateContext:
39+
outputs:
40+
- output: pipelineArtifact
41+
targetPath: '$(Build.ArtifactStagingDirectory)'
42+
artifactName: typespec-reference-tool
43+
steps:
44+
- task: UseDotNet@2
45+
displayName: 'Use .NET 10'
46+
inputs:
47+
version: 10.x
48+
- checkout: typespec-msgraph-reference
49+
displayName: checkout typespec-msgraph-reference
50+
fetchDepth: 1
51+
# Install the nuget tool
52+
- task: NuGetToolInstaller@1
53+
displayName: 'Install Nuget dependency manager'
54+
inputs:
55+
versionSpec: '>=5.2.0'
56+
checkLatest: true
57+
58+
- task: NuGetAuthenticate@1
59+
60+
# Restore dependencies
61+
- task: DotNetCoreCLI@2
62+
displayName: 'Restore dependencies'
63+
inputs:
64+
command: restore
65+
projects: '$(Build.SourcesDirectory)\typespec-msgraph-reference\typespec-msgraph-reference.sln'
66+
67+
# Build the solution
68+
- task: DotNetCoreCLI@2
69+
displayName: 'Build solution'
70+
inputs:
71+
command: build
72+
projects: '$(Build.SourcesDirectory)\typespec-msgraph-reference\typespec-msgraph-reference.sln'
73+
arguments: '--configuration $(BuildConfiguration) --no-incremental'
74+
- task: CopyFiles@2
75+
inputs:
76+
sourceFolder: '$(Build.SourcesDirectory)/typespec-msgraph-reference/bin/$(BuildConfiguration)/net10.0'
77+
contents: '**/*'
78+
targetFolder: '$(Build.ArtifactStagingDirectory)'
79+
displayName: Copy Typespec Reference executable
80+
- stage: generate_reference_lib
81+
dependsOn: [build_reference_tool]
82+
jobs:
83+
- job: generateLibraryFiles
84+
displayName: "Generating reference library"
85+
templateContext:
86+
inputs:
87+
- input: pipelineArtifact
88+
buildType: 'current'
89+
artifactName: 'typespec-reference-tool'
90+
targetPath: '$(Build.SourcesDirectory)/typespec-reference-tool'
91+
outputs:
92+
- output: pipelineArtifact
93+
targetPath: '$(Build.ArtifactStagingDirectory)/lib'
94+
artifactName: referenceLibrary
95+
steps:
96+
- checkout: self # Add this to checkout msgraph-metadata repo
97+
displayName: checkout msgraph-metadata
98+
fetchDepth: 1
99+
- pwsh: '$(Build.SourcesDirectory)/.azure-pipelines/scripts/process-all-schemas.ps1 -ExecPath $(Build.SourcesDirectory)/typespec-reference-tool/typespec-msgraph-reference.exe'
100+
displayName: "Process all the valid schemas files"
101+
- task: CopyFiles@2
102+
inputs:
103+
sourceFolder: '$(Build.SourcesDirectory)/generated-lib/'
104+
contents: '**/*'
105+
targetFolder: '$(Build.ArtifactStagingDirectory)/lib'
106+
- stage: update_reference_lib
107+
dependsOn: [generate_reference_lib]
108+
jobs:
109+
- job: update_reference_lib
110+
displayName: "Create PR to update the typespec-msgraph reference lib"
111+
templateContext:
112+
inputs:
113+
- input: pipelineArtifact
114+
buildType: 'current'
115+
artifactName: 'referenceLibrary'
116+
targetPath: '$(Build.SourcesDirectory)/tmp_lib'
117+
steps:
118+
- checkout: typespec-msgraph
119+
displayName: checkout typespec-msgraph
120+
fetchDepth: 1
121+
persistCredentials: true
122+
- pwsh: |
123+
git config --global user.email "GraphTooling@service.microsoft.com"
124+
git config --global user.name "Microsoft Graph DevX Tooling"
125+
displayName: 'Git: set user config'
126+
# Copy files from the tmp folder to the checkout repo
127+
- task: CopyFiles@2
128+
inputs:
129+
sourceFolder: '$(Build.SourcesDirectory)/tmp_lib'
130+
contents: '**/*'
131+
targetFolder: '$(Build.SourcesDirectory)/typespec-msgraph/packages/typespec-reference-library/lib'
132+
overwrite: true
133+
displayName: Copy OpenAPI files to local typespec-msgraph repo
134+
# Push changes to Typespec msgraph repo
135+
- pwsh: '$(scriptsDirectory)/git-push-reference-files.ps1'
136+
displayName: Publish new generated files to msgraph-metadata repo
137+
env:
138+
PublishChanges: True
139+
workingDirectory: '$(Build.SourcesDirectory)/typespec-msgraph'
140+
enabled: true
141+
142+
# Create PR
143+
- task: AzureKeyVault@2
144+
displayName: "Azure Key Vault: Get Secrets"
145+
inputs:
146+
azureSubscription: "Federated AKV Managed Identity Connection"
147+
KeyVaultName: akv-prod-eastus
148+
SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey"
149+
150+
- pwsh: '$(scriptsDirectory)/create-pull-request.ps1'
151+
displayName: 'Create Pull Request for the generated OpenAPI files for msgraph-metadata'
152+
env:
153+
BaseBranch: main
154+
GeneratePullRequest: true
155+
GhAppId: $(microsoft-graph-devx-bot-appid)
156+
GhAppKey: $(microsoft-graph-devx-bot-privatekey)
157+
OverrideSkipCI: false
158+
RepoName: 'microsoftgraph/typespec-msgraph'
159+
ScriptsDirectory: $(scriptsDirectory)
160+
# Version is intentionally left empty for OpenAPI PRs as versioning is not applicable in this context.
161+
Version: ''
162+
workingDirectory: '$(Build.SourcesDirectory)/typespec-msgraph'
163+
164+
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
[CmdletBinding()]
2+
param (
3+
[Parameter(Mandatory = $true)]
4+
[string]
5+
$AppClientId,
6+
[Parameter(Mandatory = $true)]
7+
[string]
8+
$AppPrivateKeyContents,
9+
[Parameter(Mandatory = $true)]
10+
[ValidatePattern('^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$', ErrorMessage = "Repository must be in the format 'owner/repo' (e.g. 'octocat/hello-world')")]
11+
[string]
12+
$Repository
13+
)
14+
15+
$ErrorActionPreference = "Stop"
16+
17+
function Generate-AppToken {
18+
param (
19+
[string]
20+
$ClientId,
21+
[string]
22+
$PrivateKeyContents
23+
)
24+
25+
$header = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
26+
alg = "RS256"
27+
typ = "JWT"
28+
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
29+
30+
$payload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
31+
iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
32+
exp = [System.DateTimeOffset]::UtcNow.AddMinutes(1).ToUnixTimeSeconds()
33+
iss = $ClientId
34+
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
35+
36+
$rsa = [System.Security.Cryptography.RSA]::Create()
37+
$rsa.ImportFromPem($PrivateKeyContents)
38+
39+
$signature = [Convert]::ToBase64String($rsa.SignData([System.Text.Encoding]::UTF8.GetBytes("$header.$payload"), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
40+
$jwt = "$header.$payload.$signature"
41+
42+
return $jwt
43+
}
44+
45+
function Generate-InstallationToken {
46+
param (
47+
[string]
48+
$AppToken,
49+
[string]
50+
$InstallationId,
51+
[string]
52+
$Repository
53+
)
54+
55+
$uri = "https://api.github.com/app/installations/$InstallationId/access_tokens"
56+
$headers = @{
57+
Authorization = "Bearer $AppToken"
58+
Accept = "application/vnd.github+json"
59+
"X-GitHub-Api-Version" = "2022-11-28"
60+
}
61+
62+
$body = @{
63+
repositories = @($Repository)
64+
}
65+
66+
$response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body (ConvertTo-Json -InputObject $body -Compress -Depth 10)
67+
68+
return $response.token
69+
}
70+
71+
72+
function Get-OrganizationInstallationId {
73+
param (
74+
[string]
75+
$AppToken,
76+
[string]
77+
$Organization
78+
)
79+
80+
$uri = "https://api.github.com/orgs/$Organization/installation"
81+
$headers = @{
82+
Authorization = "Bearer $AppToken"
83+
Accept = "application/vnd.github+json"
84+
"X-GitHub-Api-Version" = "2022-11-28"
85+
}
86+
87+
try {
88+
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers
89+
90+
return $response.id
91+
}
92+
catch [Microsoft.PowerShell.Commands.HttpResponseException] {
93+
if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
94+
return $null
95+
}
96+
97+
throw
98+
}
99+
}
100+
101+
function Get-RepositoryInstallationId {
102+
param (
103+
[string]
104+
$AppToken,
105+
[string]
106+
$Repository
107+
)
108+
109+
$uri = "https://api.github.com/repos/$Repository/installation"
110+
$headers = @{
111+
Authorization = "Bearer $AppToken"
112+
Accept = "application/vnd.github+json"
113+
"X-GitHub-Api-Version" = "2022-11-28"
114+
}
115+
116+
try {
117+
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers
118+
119+
return $response.id
120+
}
121+
catch [Microsoft.PowerShell.Commands.HttpResponseException] {
122+
if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
123+
return $null
124+
}
125+
126+
throw
127+
}
128+
}
129+
130+
function Get-UserInstallationId {
131+
param (
132+
[string]
133+
$AppToken,
134+
[string]
135+
$Username
136+
)
137+
138+
$uri = "https://api.github.com/users/$Username/installation"
139+
$headers = @{
140+
Authorization = "Bearer $AppToken"
141+
Accept = "application/vnd.github+json"
142+
"X-GitHub-Api-Version" = "2022-11-28"
143+
}
144+
145+
try {
146+
$response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers
147+
148+
return $response.id
149+
}
150+
catch [Microsoft.PowerShell.Commands.HttpResponseException] {
151+
if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::UnprocessableContent -or $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
152+
return $null
153+
}
154+
155+
throw
156+
}
157+
}
158+
159+
function Get-InstallationId {
160+
param (
161+
[string]
162+
$AppToken,
163+
[string]
164+
$Owner,
165+
[string]
166+
$Repo
167+
)
168+
169+
$orgInstallationId = Get-OrganizationInstallationId -AppToken $AppToken -Organization $Owner
170+
171+
if ($null -eq $orgInstallationId) {
172+
$repoInstallationId = Get-RepositoryInstallationId -AppToken $AppToken -Repository "$Owner/$Repo"
173+
}
174+
else {
175+
return $orgInstallationId
176+
}
177+
178+
if ($null -eq $repoInstallationId) {
179+
$userInstallationId = Get-UserInstallationId -AppToken $AppToken -Username $Owner
180+
}
181+
else {
182+
return $repoInstallationId
183+
}
184+
185+
if ($null -eq $userInstallationId) {
186+
throw "Installation not found for repository '$Repo'"
187+
}
188+
else {
189+
return $userInstallationId
190+
}
191+
}
192+
193+
$owner, $repo = $Repository -split '/'
194+
195+
$AppToken = Generate-AppToken -ClientId $AppClientId -PrivateKeyContents $AppPrivateKeyContents
196+
197+
$InstallationId = Get-InstallationId -AppToken $AppToken -Owner $owner -Repo $repo
198+
199+
$InstallationToken = Generate-InstallationToken -AppToken $AppToken -InstallationId $InstallationId -Repository $repo
200+
201+
Write-Output $InstallationToken
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
if (($env:GeneratePullRequest -eq $False)) { # Skip CI if manually running this pipeline.
3+
Write-Host "Skipping pull request creation due this repository being disabled"
4+
return;
5+
}
6+
7+
$title = "Generated $env:Version typespec reference files"
8+
9+
10+
$body = ":bangbang:**_Important_**:bangbang: <br> Check for unexpected deletions or changes in this PR and ensure relevant CI checks are passing. <br><br> **Note:** This pull request was automatically created by Azure pipelines."
11+
12+
# The installed application is required to have the following permissions: read/write on pull requests/
13+
$tokenGenerationScript = "$env:ScriptsDirectory\Generate-Github-Token.ps1"
14+
$env:GITHUB_TOKEN = & $tokenGenerationScript -AppClientId $env:GhAppId -AppPrivateKeyContents $env:GhAppKey -Repository $env:RepoName
15+
Write-Host "Fetched Github Token for PR generation and set as environment variable."
16+
17+
# No need to specify reviewers as code owners should be added automatically.
18+
if (![string]::IsNullOrEmpty($env:BaseBranch)) {
19+
gh pr create -t $title -b $body -B $env:BaseBranch
20+
} else {
21+
gh pr create -t $title -b $body
22+
}
23+
24+
Write-Host "Pull Request Created successfully."

0 commit comments

Comments
 (0)