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
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2024, Salesforce.com, Inc.
Copyright (c) 2026, Salesforce.com, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Expand Down
21 changes: 21 additions & 0 deletions messages/devops.project.list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# summary

List all DevOps Center projects in a Salesforce org.

# description

Lists all DevOps Center projects available in the specified org by querying the DevopsProject object. Returns project Id, Name, and Description for each project found.

# flags.target-org.summary

Username or alias of the DevOps Center org.

# examples

- List all DevOps Center projects in an org.

<%= config.bin %> <%= command.id %> --target-org my-devops-org

- List projects using a username.

<%= config.bin %> <%= command.id %> --target-org devops-center@example.com
33 changes: 33 additions & 0 deletions messages/devops.work-item.create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# summary

Create a new work item in a DevOps Center project.

# description

Creates a new DevOps Center work item in the specified project using the Connect API. Requires a project ID (obtainable from `sf devops project list`) and a subject for the work item.

# flags.target-org.summary

Username or alias of the DevOps Center org.

# flags.project-id.summary

ID of the DevOps Center project to create the work item in.

# flags.subject.summary

Subject (title) of the new work item.

# flags.description.summary

Description of the new work item.

# examples

- Create a work item with a subject.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --subject "Fix login bug"

- Create a work item with a subject and description.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --subject "Add dark mode" --description "Implement dark mode toggle in settings page"
25 changes: 25 additions & 0 deletions messages/devops.work-item.list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# summary

List all work items for a DevOps Center project.

# description

Lists work items from a Salesforce DevOps Center project. Each work item includes branch, environment, and repository details needed for checkout and promotion. Requires a project ID which can be obtained from `sf devops project list`.

# flags.target-org.summary

Username or alias of the DevOps Center org.

# flags.project-id.summary

ID of the DevOps Center project to list work items for.

# examples

- List work items for a specific project.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001

- List work items with JSON output.

<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --json
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@
}
}
}
},
"devops": {
"description": "Commands for DevOps and Application Lifecycle Management.",
"subtopics": {
"project": {
"description": "Commands for managing DevOps Center projects."
},
"work-item": {
"description": "Commands for managing DevOps Center work items."
}
}
}
}
},
Expand Down
71 changes: 71 additions & 0 deletions src/commands/devops/project/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2025, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Messages, Org } from '@salesforce/core';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { ux } from '@oclif/core';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.project.list');

export type DevopsProject = {
Id: string;
Name: string;
Description: string | null;
};

export type DevopsProjectListResult = {
projects: DevopsProject[];
};

export default class DevopsProjectList extends SfCommand<DevopsProjectListResult> {
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({
summary: messages.getMessage('flags.target-org.summary'),
char: 'o',
required: true,
}),
};

public async run(): Promise<DevopsProjectListResult> {
const { flags } = await this.parse(DevopsProjectList);
const org: Org = flags['target-org'];
const connection = org.getConnection('65.0');

let projects: DevopsProject[];
try {
const query = 'SELECT Id, Name, Description FROM DevopsProject';
const result = await connection.query<DevopsProject>(query);
projects = result.records ?? [];
} catch (error: unknown) {
const errMsg = error instanceof Error ? error.message : String(error);
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
this.error(
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
);
}
throw error;
}

if (projects.length === 0) {
this.log('No DevOps Center projects found in this org.');
} else {
ux.styledHeader('DevOps Center Projects');
ux.table(projects, {
Id: { header: 'Id' },
Name: { header: 'Name' },
Description: { header: 'Description' },
});
}

return { projects };
}
}
75 changes: 75 additions & 0 deletions src/commands/devops/work-item/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2025, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

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

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.work-item.create');

export default class DevopsWorkItemCreate extends SfCommand<CreateWorkItemResult> {
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({
summary: messages.getMessage('flags.target-org.summary'),
char: 'o',
required: true,
}),
'project-id': Flags.string({
summary: messages.getMessage('flags.project-id.summary'),
char: 'p',
required: true,
}),
subject: Flags.string({
summary: messages.getMessage('flags.subject.summary'),
char: 's',
required: true,
}),
description: Flags.string({
summary: messages.getMessage('flags.description.summary'),
char: 'd',
}),
};

public async run(): Promise<CreateWorkItemResult> {
const { flags } = await this.parse(DevopsWorkItemCreate);
const org: Org = flags['target-org'];
const connection = org.getConnection('65.0');

let result: CreateWorkItemResult;
try {
result = await createWorkItem({
connection,
projectId: flags['project-id'],
subject: flags['subject'],
description: flags['description'] ?? '',
});
} catch (error: unknown) {
const errMsg = error instanceof Error ? error.message : String(error);
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
this.error(
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
);
}
throw error;
}

if (result.success) {
this.log(`Successfully created work item: ${result.workItemName ?? result.workItemId}`);
this.log(` ID: ${result.workItemId}`);
this.log(` Subject: ${result.subject}`);
} else {
this.error(`Failed to create work item: ${result.error}`);
}

return result;
}
}
81 changes: 81 additions & 0 deletions src/commands/devops/work-item/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2025, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Messages, Org } from '@salesforce/core';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { ux } from '@oclif/core';
import { fetchWorkItems } from '../../../utils/workItems';
import { WorkItem } from '../../../utils/types';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.work-item.list');

export type DevopsWorkItemListResult = {
workItems: WorkItem[];
};

export default class DevopsWorkItemList extends SfCommand<DevopsWorkItemListResult> {
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({
summary: messages.getMessage('flags.target-org.summary'),
char: 'o',
required: true,
}),
'project-id': Flags.string({
summary: messages.getMessage('flags.project-id.summary'),
char: 'p',
required: true,
}),
};

public async run(): Promise<DevopsWorkItemListResult> {
const { flags } = await this.parse(DevopsWorkItemList);
const org: Org = flags['target-org'];
const projectId: string = flags['project-id'];
const connection = org.getConnection('65.0');

let workItems: WorkItem[];
try {
workItems = await fetchWorkItems(connection, projectId);
} catch (error: unknown) {
const errMsg = error instanceof Error ? error.message : String(error);
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
this.error(
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
);
}
throw error;
}

if (workItems.length === 0) {
this.log('No work items found for this project.');
} else {
const tableData = workItems.map((wi) => ({
Name: wi.name,
Subject: wi.subject ?? '',
Status: wi.status,
Branch: wi.WorkItemBranch ?? '',
'Target Branch': wi.TargetBranch ?? '',
}));

ux.styledHeader('DevOps Center Work Items');
ux.table(tableData, {
Name: { header: 'Name' },
Subject: { header: 'Subject' },
Status: { header: 'Status' },
Branch: { header: 'Branch' },
'Target Branch': { header: 'Target Branch' },
});
}

return { workItems };
}
}
4 changes: 2 additions & 2 deletions src/common/outputService/aorOutputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type AorOutputFlags = {
*
* @author JuanStenghele-sf
*/
export interface AorOutputService extends OutputService {
export type AorOutputService = OutputService & {
/**
* Prints the status of the given aor
*/
Expand All @@ -44,7 +44,7 @@ export interface AorOutputService extends OutputService {
* Sets the aor id
*/
getStatus(): AsyncOperationStatus | undefined;
}
};

/**
* Abstract class that implements AorOutputService interface
Expand Down
4 changes: 2 additions & 2 deletions src/common/outputService/deploymentResultOutputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { AbstractOutputService, OutputFlags, OutputService } from './outputServi
/**
* Service interface for printing the output of a deployment result.
*/
export interface DeploymentResultOutputService extends OutputService {
export type DeploymentResultOutputService = OutputService & {
/**
* Prints the deployment result.
*/
printDeploymentResult(): void;
}
};

export abstract class AbstractDeploymentResultOutputService<T extends OutputFlags>
extends AbstractOutputService<T>
Expand Down
4 changes: 2 additions & 2 deletions src/common/outputService/outputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export type OutputFlags = {
*
* @author JuanStenghele-sf
*/
export interface OutputService {
export type OutputService = {
/**
* Prints a summary of the operation being done
*/
printOpSummary(): void;
}
};

/**
* Abstract class that implements OutputService interface
Expand Down
2 changes: 1 addition & 1 deletion src/common/outputService/promoteOutputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type PromoteOutputFlags = {
*
* @author JuanStenghele-sf
*/
export interface PromoteOutputService extends ResumeOutputService {}
export type PromoteOutputService = ResumeOutputService;

/**
* Abstract class that implements PromoteOutputService interface
Expand Down
Loading
Loading