The ado2gh rewire-pipeline command fails with HTTP 400 (Bad Request) on Classic (Designer) pipelines because the rewiring logic unconditionally sets settingsSourceType = 2 (YAML definitions) in the payload sent back to the ADO Build Definitions API. Classic pipelines use a Designer process (process.type = 1) and don't have YAML definitions, so the ADO API rejects the update with a 400.
Root Cause
In AdoPipelineTriggerService.cs line 232, the BuildPipelinePayload method hardcodes:
payload["settingsSourceType"] = 2;
settingsSourceType = 1 means "pipeline settings are defined in the UI" (classic/designer pipelines) -process.type = 1
settingsSourceType = 2 means "pipeline settings come from a YAML file" (YAML pipelines) - process.type = 2
For classic pipelines, there is no YAML file - the build steps are stored as designer JSON inside ADO. Setting settingsSourceType = 2 tells ADO to look for a YAML file that doesn't exist, resulting in a 400 Bad Request.
Note: the loop above this line already copies the original settingsSourceType from the pipeline definition into the payload, but line 232 unconditionally overwrites it.
Reproduction
- Create a classic (designer) pipeline in Azure DevOps
- Run:
gh ado2gh rewire-pipeline --ado-org <org> --ado-team-project <project> --ado-pipeline <classic-pipeline-name> --github-org <org> --github-repo <repo> --service-connection-id <id>
- Observe:
[WARNING] HTTP error retrieving pipeline <id> in <org>/<project>: Response status code does not indicate success: 400 (Bad Request).. Skipping pipeline rewiring.
YAML pipelines in the same project succeed.
Customer Impact
One customer has 38k pipelines, of which 14k are classic pipelines. All classic pipelines fail to rewire.
CLI version: v1.26.0 (also likely affects v1.27.0 since the code is unchanged).
Suggested Fix
- Preserve the original
settingsSourceType from the pipeline definition instead of overriding it:
// Preserve original settingsSourceType (classic=1, YAML=2)
payload["settingsSourceType"] ??= 2;
This keeps 1 for classic pipelines and defaults to 2 only if no value was present.
- The
process.type field is already present in the pipeline definition response from _apis/build/definitions/{pipelineId}. The fix is to:
- Read
process.type from the fetched definition data
- Set
settingsSourceType = 1 (UI/Designer) for Classic pipelines and settingsSourceType = 2 (YAML) for YAML pipelines
Similarly, the RestorePipelineToAdoRepo method in AdoApi.cs hardcodes settingsSourceType = 1, which should also be reviewed.
The
ado2gh rewire-pipelinecommand fails withHTTP 400 (Bad Request)on Classic (Designer) pipelines because the rewiring logic unconditionally setssettingsSourceType = 2(YAMLdefinitions) in the payload sent back to the ADO Build Definitions API. Classic pipelines use a Designer process (process.type = 1) and don't haveYAMLdefinitions, so the ADO API rejects the update with a 400.Root Cause
In
AdoPipelineTriggerService.csline 232, theBuildPipelinePayloadmethod hardcodes:settingsSourceType = 1means "pipeline settings are defined in the UI" (classic/designer pipelines) -process.type = 1settingsSourceType = 2means "pipeline settings come from a YAML file" (YAML pipelines) -process.type = 2For classic pipelines, there is no YAML file - the build steps are stored as designer JSON inside ADO. Setting
settingsSourceType = 2tells ADO to look for a YAML file that doesn't exist, resulting in a 400 Bad Request.Note: the loop above this line already copies the original
settingsSourceTypefrom the pipeline definition into the payload, but line 232 unconditionally overwrites it.Reproduction
gh ado2gh rewire-pipeline --ado-org <org> --ado-team-project <project> --ado-pipeline <classic-pipeline-name> --github-org <org> --github-repo <repo> --service-connection-id <id>[WARNING] HTTP error retrieving pipeline <id> in <org>/<project>: Response status code does not indicate success: 400 (Bad Request).. Skipping pipeline rewiring.YAML pipelines in the same project succeed.
Customer Impact
One customer has 38k pipelines, of which 14k are classic pipelines. All classic pipelines fail to rewire.
CLI version: v1.26.0 (also likely affects v1.27.0 since the code is unchanged).
Suggested Fix
settingsSourceTypefrom the pipeline definition instead of overriding it:This keeps
1for classic pipelines and defaults to2only if no value was present.process.typefield is already present in the pipeline definition response from_apis/build/definitions/{pipelineId}. The fix is to:process.typefrom the fetched definition datasettingsSourceType = 1(UI/Designer) for Classic pipelines andsettingsSourceType = 2(YAML) for YAML pipelinesSimilarly, the
RestorePipelineToAdoRepomethod inAdoApi.cshardcodessettingsSourceType = 1, which should also be reviewed.