Skip to content

Commit a830ef9

Browse files
committed
devops project commands
1 parent 87bb9e9 commit a830ef9

14 files changed

Lines changed: 1838 additions & 1238 deletions

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2024, Salesforce.com, Inc.
1+
Copyright (c) 2026, Salesforce.com, Inc.
22
All rights reserved.
33

44
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

messages/devops.project.list.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# summary
2+
3+
List all DevOps Center projects in a Salesforce org.
4+
5+
# description
6+
7+
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.
8+
9+
# flags.target-org.summary
10+
11+
Username or alias of the DevOps Center org.
12+
13+
# examples
14+
15+
- List all DevOps Center projects in an org.
16+
17+
<%= config.bin %> <%= command.id %> --target-org my-devops-org
18+
19+
- List projects using a username.
20+
21+
<%= config.bin %> <%= command.id %> --target-org devops-center@example.com
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# summary
2+
3+
Create a new work item in a DevOps Center project.
4+
5+
# description
6+
7+
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.
8+
9+
# flags.target-org.summary
10+
11+
Username or alias of the DevOps Center org.
12+
13+
# flags.project-id.summary
14+
15+
ID of the DevOps Center project to create the work item in.
16+
17+
# flags.subject.summary
18+
19+
Subject (title) of the new work item.
20+
21+
# flags.description.summary
22+
23+
Description of the new work item.
24+
25+
# examples
26+
27+
- Create a work item with a subject.
28+
29+
<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --subject "Fix login bug"
30+
31+
- Create a work item with a subject and description.
32+
33+
<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --subject "Add dark mode" --description "Implement dark mode toggle in settings page"

messages/devops.work-item.list.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# summary
2+
3+
List all work items for a DevOps Center project.
4+
5+
# description
6+
7+
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`.
8+
9+
# flags.target-org.summary
10+
11+
Username or alias of the DevOps Center org.
12+
13+
# flags.project-id.summary
14+
15+
ID of the DevOps Center project to list work items for.
16+
17+
# examples
18+
19+
- List work items for a specific project.
20+
21+
<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001
22+
23+
- List work items with JSON output.
24+
25+
<%= config.bin %> <%= command.id %> --target-org my-devops-org --project-id 0Hn000000000001 --json

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@
7474
}
7575
}
7676
}
77+
},
78+
"devops": {
79+
"description": "Commands for DevOps and Application Lifecycle Management.",
80+
"subtopics": {
81+
"project": {
82+
"description": "Commands for managing DevOps Center projects."
83+
},
84+
"work-item": {
85+
"description": "Commands for managing DevOps Center work items."
86+
}
87+
}
7788
}
7889
}
7990
},
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Messages, Org } from '@salesforce/core';
9+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
10+
import { ux } from '@oclif/core';
11+
12+
Messages.importMessagesDirectory(__dirname);
13+
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.project.list');
14+
15+
export type DevopsProject = {
16+
Id: string;
17+
Name: string;
18+
Description: string | null;
19+
};
20+
21+
export type DevopsProjectListResult = {
22+
projects: DevopsProject[];
23+
};
24+
25+
export default class DevopsProjectList extends SfCommand<DevopsProjectListResult> {
26+
public static readonly summary = messages.getMessage('summary');
27+
public static readonly description = messages.getMessage('description');
28+
public static readonly examples = messages.getMessages('examples');
29+
30+
public static readonly flags = {
31+
'target-org': Flags.requiredOrg({
32+
summary: messages.getMessage('flags.target-org.summary'),
33+
char: 'o',
34+
required: true,
35+
}),
36+
};
37+
38+
public async run(): Promise<DevopsProjectListResult> {
39+
const { flags } = await this.parse(DevopsProjectList);
40+
const org: Org = flags['target-org'];
41+
const connection = org.getConnection('65.0');
42+
43+
let projects: DevopsProject[];
44+
try {
45+
const query = 'SELECT Id, Name, Description FROM DevopsProject';
46+
const result = await connection.query<DevopsProject>(query);
47+
projects = result.records ?? [];
48+
} catch (error: unknown) {
49+
const errMsg = error instanceof Error ? error.message : String(error);
50+
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
51+
this.error(
52+
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
53+
);
54+
}
55+
throw error;
56+
}
57+
58+
if (projects.length === 0) {
59+
this.log('No DevOps Center projects found in this org.');
60+
} else {
61+
ux.styledHeader('DevOps Center Projects');
62+
ux.table(projects, {
63+
Id: { header: 'Id' },
64+
Name: { header: 'Name' },
65+
Description: { header: 'Description' },
66+
});
67+
}
68+
69+
return { projects };
70+
}
71+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Messages, Org } from '@salesforce/core';
9+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
10+
import { createWorkItem, CreateWorkItemResult } from '../../../utils/createWorkItem';
11+
12+
Messages.importMessagesDirectory(__dirname);
13+
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.work-item.create');
14+
15+
export default class DevopsWorkItemCreate extends SfCommand<CreateWorkItemResult> {
16+
public static readonly summary = messages.getMessage('summary');
17+
public static readonly description = messages.getMessage('description');
18+
public static readonly examples = messages.getMessages('examples');
19+
20+
public static readonly flags = {
21+
'target-org': Flags.requiredOrg({
22+
summary: messages.getMessage('flags.target-org.summary'),
23+
char: 'o',
24+
required: true,
25+
}),
26+
'project-id': Flags.string({
27+
summary: messages.getMessage('flags.project-id.summary'),
28+
char: 'p',
29+
required: true,
30+
}),
31+
subject: Flags.string({
32+
summary: messages.getMessage('flags.subject.summary'),
33+
char: 's',
34+
required: true,
35+
}),
36+
description: Flags.string({
37+
summary: messages.getMessage('flags.description.summary'),
38+
char: 'd',
39+
}),
40+
};
41+
42+
public async run(): Promise<CreateWorkItemResult> {
43+
const { flags } = await this.parse(DevopsWorkItemCreate);
44+
const org: Org = flags['target-org'];
45+
const connection = org.getConnection('65.0');
46+
47+
let result: CreateWorkItemResult;
48+
try {
49+
result = await createWorkItem({
50+
connection,
51+
projectId: flags['project-id'],
52+
subject: flags['subject'],
53+
description: flags['description'] ?? '',
54+
});
55+
} catch (error: unknown) {
56+
const errMsg = error instanceof Error ? error.message : String(error);
57+
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
58+
this.error(
59+
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
60+
);
61+
}
62+
throw error;
63+
}
64+
65+
if (result.success) {
66+
this.log(`Successfully created work item: ${result.workItemName ?? result.workItemId}`);
67+
this.log(` ID: ${result.workItemId}`);
68+
this.log(` Subject: ${result.subject}`);
69+
} else {
70+
this.error(`Failed to create work item: ${result.error}`);
71+
}
72+
73+
return result;
74+
}
75+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Messages, Org } from '@salesforce/core';
9+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
10+
import { ux } from '@oclif/core';
11+
import { fetchWorkItems } from '../../../utils/workItems';
12+
import { WorkItem } from '../../../utils/types';
13+
14+
Messages.importMessagesDirectory(__dirname);
15+
const messages = Messages.loadMessages('@salesforce/plugin-devops-center', 'devops.work-item.list');
16+
17+
export type DevopsWorkItemListResult = {
18+
workItems: WorkItem[];
19+
};
20+
21+
export default class DevopsWorkItemList extends SfCommand<DevopsWorkItemListResult> {
22+
public static readonly summary = messages.getMessage('summary');
23+
public static readonly description = messages.getMessage('description');
24+
public static readonly examples = messages.getMessages('examples');
25+
26+
public static readonly flags = {
27+
'target-org': Flags.requiredOrg({
28+
summary: messages.getMessage('flags.target-org.summary'),
29+
char: 'o',
30+
required: true,
31+
}),
32+
'project-id': Flags.string({
33+
summary: messages.getMessage('flags.project-id.summary'),
34+
char: 'p',
35+
required: true,
36+
}),
37+
};
38+
39+
public async run(): Promise<DevopsWorkItemListResult> {
40+
const { flags } = await this.parse(DevopsWorkItemList);
41+
const org: Org = flags['target-org'];
42+
const projectId: string = flags['project-id'];
43+
const connection = org.getConnection('65.0');
44+
45+
let workItems: WorkItem[];
46+
try {
47+
workItems = await fetchWorkItems(connection, projectId);
48+
} catch (error: unknown) {
49+
const errMsg = error instanceof Error ? error.message : String(error);
50+
if (errMsg.includes('sObject type') && errMsg.includes('is not supported')) {
51+
this.error(
52+
'DevOps Center is not enabled in this org. Enable DevOps Center in Setup before using this command.'
53+
);
54+
}
55+
throw error;
56+
}
57+
58+
if (workItems.length === 0) {
59+
this.log('No work items found for this project.');
60+
} else {
61+
const tableData = workItems.map((wi) => ({
62+
Name: wi.name,
63+
Subject: wi.subject ?? '',
64+
Status: wi.status,
65+
Branch: wi.WorkItemBranch ?? '',
66+
'Target Branch': wi.TargetBranch ?? '',
67+
}));
68+
69+
ux.styledHeader('DevOps Center Work Items');
70+
ux.table(tableData, {
71+
Name: { header: 'Name' },
72+
Subject: { header: 'Subject' },
73+
Status: { header: 'Status' },
74+
Branch: { header: 'Branch' },
75+
'Target Branch': { header: 'Target Branch' },
76+
});
77+
}
78+
79+
return { workItems };
80+
}
81+
}

0 commit comments

Comments
 (0)