-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Add workflow to run validation pipelines from a PR directly #81836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
7b06609
GH Workflow Performance Validation (#81544)
akhera99 83844e1
make workflow run once
akhera99 57194bd
Revert "make workflow run once"
akhera99 112cffb
Merge branch 'dev/ankitakhera/test_gh_workflow'
akhera99 d0cc734
better logging
akhera99 36cd833
undo logging changes
akhera99 bcc7160
add environment
akhera99 dbc2095
change location for pipeline
akhera99 bcaf338
combine dnceng pipeline into workflow
akhera99 fb16394
require commitsha is user not a contriributor
akhera99 099a745
poll for pipeline result
akhera99 ac2ede7
remove polling for now
akhera99 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| name: PR Validation | ||
|
|
||
| on: | ||
| issue_comment: | ||
| types: [created] | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| id-token: write # Required to fetch an OIDC token for Azure authentication | ||
|
|
||
| jobs: | ||
| validate-and-trigger: | ||
| name: Validate and Trigger Azure Pipeline | ||
| if: | | ||
| github.event.issue.pull_request && | ||
| (contains(github.event.comment.body, '/dart') || contains(github.event.comment.body, '/pr-val')) | ||
| runs-on: ubuntu-latest | ||
| environment: pr_val | ||
|
|
||
| steps: | ||
| - name: Check if user has write access | ||
| id: check-access | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| username: context.actor | ||
| }); | ||
|
|
||
| const hasWriteAccess = ['admin', 'write', 'maintain'].includes(permission.permission); | ||
| console.log(`User ${context.actor} has permission: ${permission.permission}`); | ||
| core.setOutput('has-access', hasWriteAccess); | ||
| return hasWriteAccess; | ||
|
|
||
| - name: Check Microsoft org membership | ||
| id: check-org | ||
| if: steps.check-access.outputs.has-access == 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| try { | ||
| await github.rest.orgs.checkMembershipForUser({ | ||
| org: 'microsoft', | ||
| username: context.actor | ||
| }); | ||
| console.log(`User ${context.actor} is a member of Microsoft org`); | ||
| core.setOutput('is-member', 'true'); | ||
| return true; | ||
| } catch (error) { | ||
| console.log(`User ${context.actor} is not a member of Microsoft org`); | ||
| core.setOutput('is-member', 'false'); | ||
| return false; | ||
| } | ||
|
|
||
| - name: Parse commit hash from comment | ||
| id: parse-commit | ||
| if: steps.check-access.outputs.has-access != 'true' || steps.check-org.outputs.is-member != 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const commentBody = context.payload.comment.body; | ||
| // Extract commit hash after /dart or /pr-val | ||
| const match = commentBody.match(/\/(dart|pr-val)\s+([a-f0-9]{7,40})/i); | ||
|
|
||
| if (!match || !match[2]) { | ||
| console.log('No commit hash found in comment'); | ||
| core.setOutput('has-commit', 'false'); | ||
| return false; | ||
| } | ||
|
|
||
| const commitHash = match[2]; | ||
| console.log(`Extracted commit hash: ${commitHash}`); | ||
| core.setOutput('has-commit', 'true'); | ||
| core.setOutput('commit-hash', commitHash); | ||
| return true; | ||
|
|
||
| - name: Comment on missing commit hash | ||
| if: | | ||
| (steps.check-access.outputs.has-access != 'true' || steps.check-org.outputs.is-member != 'true') && | ||
| steps.parse-commit.outputs.has-commit != 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: 'You do not have permission to trigger this workflow without specifying a commit hash. Please use the format `/dart <commit-hash>` or `/pr-val <commit-hash>.' | ||
| }); | ||
|
|
||
| - name: Exit if unauthorized without commit hash | ||
| if: | | ||
| (steps.check-access.outputs.has-access != 'true' || steps.check-org.outputs.is-member != 'true') && | ||
| steps.parse-commit.outputs.has-commit != 'true' | ||
| run: exit 1 | ||
|
|
||
| - name: Get PR details | ||
| id: pr-details | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const { data: pr } = await github.rest.pulls.get({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| pull_number: context.issue.number | ||
| }); | ||
|
|
||
| core.setOutput('ref', pr.head.ref); | ||
| core.setOutput('repo', pr.head.repo.full_name); | ||
| console.log(`PR #${context.issue.number}: ${pr.head.repo.full_name}@${pr.head.ref} (${pr.head.sha})`); | ||
|
|
||
| - name: Determine commit SHA to use | ||
| id: commit-sha | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const parseCommitOutput = '${{ steps.parse-commit.outputs.has-commit }}'; | ||
| const providedCommit = '${{ steps.parse-commit.outputs.commit-hash }}'; | ||
|
|
||
| let commitSha; | ||
| if (parseCommitOutput === 'true' && providedCommit) { | ||
| // Use the commit hash provided in the comment | ||
| commitSha = providedCommit; | ||
| console.log(`Using commit hash from comment: ${commitSha}`); | ||
| } else { | ||
| // Use the PR head SHA for privileged users | ||
| const { data: pr } = await github.rest.pulls.get({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| pull_number: context.issue.number | ||
| }); | ||
| commitSha = pr.head.sha; | ||
| console.log(`Using PR head SHA: ${commitSha}`); | ||
| } | ||
|
|
||
| core.setOutput('sha', commitSha); | ||
|
|
||
| - name: Validate commit exists in PR | ||
| if: steps.parse-commit.outputs.has-commit == 'true' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const commitSha = '${{ steps.commit-sha.outputs.sha }}'; | ||
| const { data: commits } = await github.rest.pulls.listCommits({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| pull_number: context.issue.number | ||
| }); | ||
|
|
||
| const commitExists = commits.some(commit => commit.sha.startsWith(commitSha)); | ||
|
|
||
| if (!commitExists) { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: `The specified commit hash \`${commitSha}\` was not found in this PR. Please ensure you are using a valid commit hash from this PR.` | ||
| }); | ||
| core.setFailed(`Commit ${commitSha} not found in PR`); | ||
| } else { | ||
| console.log(`Validated commit ${commitSha} exists in PR`); | ||
| } | ||
|
|
||
| - name: Azure Login with OpenID Connect | ||
| uses: azure/login@v2 | ||
| with: | ||
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Determine validation type and pipeline ID | ||
| id: validation-type | ||
| run: | | ||
| COMMENT_BODY="${{ github.event.comment.body }}" | ||
| if echo "$COMMENT_BODY" | grep -q "/dart"; then | ||
| echo "type=dart" >> $GITHUB_OUTPUT | ||
| echo "pipeline-id=15324" >> $GITHUB_OUTPUT | ||
| elif echo "$COMMENT_BODY" | grep -q "/pr-val"; then | ||
| echo "type=pr-val" >> $GITHUB_OUTPUT | ||
| echo "pipeline-id=8972" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Trigger Pipeline | ||
| id: trigger-pipeline | ||
| run: | | ||
| # Get Azure DevOps access token | ||
| AZDO_TOKEN=$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) | ||
|
|
||
| VALIDATION_TYPE="${{ steps.validation-type.outputs.type }}" | ||
| PIPELINE_ID="${{ steps.validation-type.outputs.pipeline-id }}" | ||
| DEVDIV_ORG="devdiv" | ||
| DEVDIV_PROJECT="DevDiv" | ||
|
|
||
| echo "Triggering DevDiv $VALIDATION_TYPE pipeline (ID: $PIPELINE_ID)..." | ||
|
|
||
| # Build request body based on validation type | ||
| if [ "$VALIDATION_TYPE" = "dart" ]; then | ||
| # DART pipeline uses prNumber and sha parameters | ||
| REQUEST_BODY=$(cat <<EOF | ||
| { | ||
| "templateParameters": { | ||
| "prNumber": "${{ github.event.issue.number }}", | ||
| "sha": "${{ steps.commit-sha.outputs.sha }}" | ||
| } | ||
| } | ||
| EOF | ||
| ) | ||
| else | ||
| # PR-Val pipeline uses PRNumber, CommitSHA, and SourceBranch parameters | ||
| REQUEST_BODY=$(cat <<EOF | ||
| { | ||
| "templateParameters": { | ||
| "PRNumber": ${{ github.event.issue.number }}, | ||
| "CommitSHA": "${{ steps.commit-sha.outputs.sha }}", | ||
| "SourceBranch": "${{ steps.pr-details.outputs.ref }}" | ||
| } | ||
| } | ||
| EOF | ||
| ) | ||
| fi | ||
|
|
||
| echo "Request body: $REQUEST_BODY" | ||
|
|
||
| # Trigger the pipeline | ||
| RESPONSE=$(curl -X POST \ | ||
| -H "Content-Type: application/json" \ | ||
| -H "Authorization: Bearer $AZDO_TOKEN" \ | ||
| -d "$REQUEST_BODY" \ | ||
| "https://dev.azure.com/$DEVDIV_ORG/$DEVDIV_PROJECT/_apis/pipelines/$PIPELINE_ID/runs?api-version=7.0") | ||
|
|
||
| echo "Response: $RESPONSE" | ||
|
|
||
| # Extract pipeline run information | ||
| BUILD_ID=$(echo $RESPONSE | jq -r '.id // empty') | ||
|
|
||
| if [ -z "$BUILD_ID" ]; then | ||
| echo "Failed to trigger pipeline" | ||
| echo "Error details: $(echo $RESPONSE | jq -r '.message // "Unknown error"')" | ||
| exit 1 | ||
| fi | ||
|
|
||
| WEB_URL="https://dev.azure.com/$DEVDIV_ORG/$DEVDIV_PROJECT/_build/results?buildId=$BUILD_ID" | ||
| echo "pipeline-url=$WEB_URL" >> $GITHUB_OUTPUT | ||
| echo "build-id=$BUILD_ID" >> $GITHUB_OUTPUT | ||
| echo "Successfully triggered pipeline: $WEB_URL" | ||
|
|
||
| - name: Comment pipeline link | ||
| if: success() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: `Pipeline triggered by @${context.actor}\n\n[View Pipeline Run](${{ steps.trigger-pipeline.outputs.pipeline-url }})\n\n**Parameters:**\n- Validation Type: \`${{ steps.validation-type.outputs.type }}\`\n- Pipeline ID: \`${{ steps.validation-type.outputs.pipeline-id }}\`\n- PR Number: \`${{ github.event.issue.number }}\`\n- Commit SHA: \`${{ steps.commit-sha.outputs.sha }}\`\n- Source Branch: \`${{ steps.pr-details.outputs.ref }}\`\n- Build ID: \`${{ steps.trigger-pipeline.outputs.build-id }}\`` | ||
| }); | ||
|
|
||
| - name: Comment on failure | ||
| if: failure() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: 'Failed to trigger the pipeline. Please check the workflow logs for details.' | ||
| }); | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.