Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
"flags": ["api-version", "flags-dir", "json", "pipeline-id", "stage-id", "target-org"],
"plugin": "@salesforce/plugin-devops-center"
},
{
"alias": [],
"command": "devops:pipeline:stage:update",
"flagAliases": [],
"flagChars": ["n", "o", "s"],
"flags": ["api-version", "flags-dir", "json", "name", "stage-id", "target-org"],
"plugin": "@salesforce/plugin-devops-center"
},
{
"alias": [],
"command": "devops:pipeline:update",
Expand Down Expand Up @@ -110,6 +118,7 @@
"deploy-all",
"flags-dir",
"json",
"skip-validation",
"stage-id",
"target-org",
"target-stage-id",
Expand Down Expand Up @@ -137,6 +146,22 @@
],
"plugin": "@salesforce/plugin-devops-center"
},
{
"alias": [],
"command": "devops:promotion:validate",
"flagAliases": [],
"flagChars": ["i", "o", "t"],
"flags": [
"api-version",
"check-combine-details",
"flags-dir",
"json",
"target-org",
"target-stage-id",
"work-item-id"
],
"plugin": "@salesforce/plugin-devops-center"
},
{
"alias": [],
"command": "devops:request:status",
Expand Down
25 changes: 25 additions & 0 deletions messages/devops.pipeline.stage.update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# summary

Update a DevOps Center pipeline stage.

# description

Updates the name of a pipeline stage.

# flags.stage-id.summary

ID of the pipeline stage to update.

# flags.name.summary

New name for the pipeline stage.

# examples

- Rename a pipeline stage.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --stage-id 1QV000000000001 --name "Integration"

# error.StageNotFound

Stage "%s" not found. Check the stage ID and try again.
16 changes: 16 additions & 0 deletions messages/devops.promote.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ ID of the source pipeline stage whose approved work items will be promoted. Mutu

<%= config.bin %> <%= command.id %> --target-org my-devops-org --stage-id 1QVxx0000000001 --target-stage-id 1QVxx0000000002 --deploy-all

# flags.skip-validation.summary

Skip pre-promote validation. By default, promotion is validated before proceeding to prevent promoting work items without an associated PR. Use this flag to bypass validation.

# error.NoModeFlag

Provide either --work-item-id to promote specific work items or --stage-id to promote all approved work items from a stage.
Expand All @@ -56,6 +60,18 @@ Provide either --work-item-id to promote specific work items or --stage-id to pr

No work items found to promote from the source stage. Make sure there are approved work items before promoting.

# error.ValidationRequestFailed

Failed to run pre-promote validation: %s

# error.ValidationFailedCombineRequired

Promotion blocked: work items must be combined before promoting. Run the combine command with the following details:

# error.ValidationFailed

Pre-promote validation failed (%s): %s

# error.PromoteFailed

Failed to promote: %s
41 changes: 41 additions & 0 deletions messages/devops.promotion.validate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# summary

Validate work item promotion for a pipeline stage.

# description

Validates whether the specified work items can be promoted to the target pipeline stage. Checks for VCS and object permission errors before a promotion is attempted. Use --check-combine-details to also check for shared components that require combine resolution.

# flags.target-stage-id.summary

ID of the target pipeline stage to validate promotion to.

# flags.work-item-id.summary

ID of a work item to validate for promotion. Specify multiple times for multiple work items.

# flags.check-combine-details.summary

Check for shared components requiring combine resolution. Use this when custom promotion with the combine feature is enabled.

# examples

- Validate promotion of a work item to a target stage.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --target-stage-id 1QV000000000001 --work-item-id 1fk000000000001

- Validate promotion of multiple work items with combine details check.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --target-stage-id 1QV000000000001 --work-item-id 1fk000000000001 --work-item-id 1fk000000000002 --check-combine-details

# error.NoPipeline

No pipeline found for work item "%s". Ensure the project has an associated pipeline.

# error.ValidationFailed

Validation failed (%s): %s

# error.ValidationRequestFailed

Validation request failed: %s
22 changes: 22 additions & 0 deletions schemas/devops-pipeline-stage-update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/PipelineStageUpdateResult",
"definitions": {
"PipelineStageUpdateResult": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"stageId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": ["success", "stageId", "name"],
"additionalProperties": false
}
}
}
14 changes: 14 additions & 0 deletions schemas/devops-promote.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,24 @@
"items": {
"type": "string"
}
},
"combineDetails": {
"anyOf": [
{
"$ref": "#/definitions/CombineDetails"
},
{
"type": "null"
}
]
}
},
"required": ["requestId", "status", "message", "promotedWorkitemIds"],
"additionalProperties": false
},
"CombineDetails": {
"type": "object",
"additionalProperties": {}
}
}
}
36 changes: 36 additions & 0 deletions schemas/devops-promotion-validate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/PromotionValidateResult",
"definitions": {
"PromotionValidateResult": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"errorType": {
"type": ["string", "null"]
},
"errorDetails": {
"type": ["string", "null"]
},
"combineDetails": {
"anyOf": [
{
"$ref": "#/definitions/CombineDetails"
},
{
"type": "null"
}
]
}
},
"required": ["success", "errorType", "errorDetails", "combineDetails"],
"additionalProperties": false
},
"CombineDetails": {
"type": "object",
"additionalProperties": {}
}
}
}
79 changes: 79 additions & 0 deletions src/commands/devops/pipeline/stage/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Messages, Org } from '@salesforce/core';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { validateSalesforceId } from '../../../../utils/soqlUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.pipeline.stage.update');
const commonErrorMessages = Messages.loadMessages('@salesforce/plugin-devops-center', 'commonErrors');

export type PipelineStageUpdateResult = {
success: boolean;
stageId: string;
name: string;
};

export default class DevopsPipelineStageUpdate extends SfCommand<PipelineStageUpdateResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');

public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
'stage-id': Flags.salesforceId({
summary: messages.getMessage('flags.stage-id.summary'),
required: true,
char: 's',
}),
name: Flags.string({
summary: messages.getMessage('flags.name.summary'),
required: true,
char: 'n',
}),
};

public async run(): Promise<PipelineStageUpdateResult> {
const { flags } = await this.parse(DevopsPipelineStageUpdate);
const org: Org = flags['target-org'];
const connection = org.getConnection(flags['api-version']);
const stageId = flags['stage-id'];
const name = flags['name'];

validateSalesforceId(stageId, 'stage');

try {
await connection.sobject('DevopsPipelineStage').update({ Id: stageId, Name: name });
} catch (error: unknown) {
const errMsg = error instanceof Error ? error.message : String(error);
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
this.error(commonErrorMessages.getMessage('error.DevopsCenterNotEnabled'));
}
if (errMsg.toLowerCase().includes('not found') || errMsg.includes('entity is deleted')) {
this.error(messages.getMessage('error.StageNotFound', [stageId]));
}
throw error;
}

this.log(`Successfully updated stage "${name}".`);
this.log(` Stage ID: ${stageId}`);
this.log(` Name: ${name}`);

return { success: true, stageId, name };
}
}
Loading
Loading