Skip to content

Commit

Permalink
feat: commands to delete scratch orgs and sandboxes
Browse files Browse the repository at this point in the history
* feat: delete scratch org and sboxes

* chore: snapshot

* chore: hi, schema

* refactor: specifc return type for sbox

* chore: schema one more time

* refactor: target-org

* refactor: consolidate flags and confirmation-prompt

* refactor: use new Flags from plugins-core

* chore: bump dependencies

* chore: merge deps

* chore: bump deps for latest flag

* test: fix typo

* test: nuts without setting envs

* fix: summary, not description

* Update src/commands/env/delete/sandbox.ts

Co-authored-by: Rodrigo Espinosa de los Monteros <[email protected]>

* refactor: move confirm to sfCommand

* test: ut fixes from env:create sbox branch

* chore: dependency bumps

* ci: tsconfig to build oclif/core from top-level

* chore: bump plugins-core

* test: copy open test from Pete's branch

Co-authored-by: Rodrigo Espinosa de los Monteros <[email protected]>
  • Loading branch information
mshanemc and RodEsp authored Mar 17, 2022
1 parent 7c3890f commit 27e460d
Show file tree
Hide file tree
Showing 16 changed files with 913 additions and 372 deletions.
21 changes: 18 additions & 3 deletions command-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
[
{
"command": "env:delete:sandbox",
"plugin": "@salesforce/plugin-env",
"flags": ["json", "no-prompt", "target-org"],
"alias": []
},
{
"command": "env:delete:scratch",
"plugin": "@salesforce/plugin-env",
"flags": ["json", "no-prompt", "target-org"],
"alias": []
},
{
"command": "env:display",
"plugin": "@salesforce/plugin-env",
"flags": ["json", "target-env"]
"flags": ["json", "target-env"],
"alias": []
},
{
"command": "env:list",
"plugin": "@salesforce/plugin-env",
"flags": ["all", "columns", "csv", "filter", "json", "no-header", "no-truncate", "output", "sort"]
"flags": ["all", "columns", "csv", "filter", "json", "no-header", "no-truncate", "output", "sort"],
"alias": []
},
{
"command": "env:open",
"plugin": "@salesforce/plugin-env",
"flags": ["browser", "json", "path", "target-env", "url-only"]
"flags": ["browser", "json", "path", "target-env", "url-only"],
"alias": []
}
]
37 changes: 37 additions & 0 deletions messages/delete_sandbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# summary

Delete a sandbox.

# description

Specify an environment with either the username you used when you logged into the environment with "sf login", or the alias you gave the environment when you created it. Run "sf env list" to view all your environments and their aliases.

# examples

- Delete a sandbox with alias my-sandbox:

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

- Specify a username instead of an alias:

<%= config.bin %> <%= command.id %> --target-org=[email protected]

- Delete the org without prompting to confirm :

<%= config.bin %> <%= command.id %> --target-org=my-sandbox --no-prompt

# flags.target-org.summary

Environment alias or login user.

# flags.no-prompt.summary

Do not prompt the user to confirm the deletion

# prompt.confirm

Delete sandbox with name: %s? Are you sure?

# success

Successfully marked sandbox %s for deletion
41 changes: 41 additions & 0 deletions messages/delete_scratch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# summary

Delete a scratch org.

# description

Specify an environment with either the username you used when you logged into the environment with "sf login", or the alias you gave the environment when you created it. Run "sf env list" to view all your environments and their aliases.

# examples

- Delete a scratch org with alias my-scratch-org:

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

- Specify a username instead of an alias:

<%= config.bin %> <%= command.id %> --target-org=[email protected]

- Delete the org without prompting to confirm :

<%= config.bin %> <%= command.id %> --target-org=my-scratch-org --no-prompt

# flags.target-org.summary

Org alias or login user.

# flags.no-prompt.summary

Do not prompt the user to confirm the deletion

# prompt.confirm

Enqueue scratch org with name: %s for deletion? Are you sure?

# success

Successfully marked scratch org %s for deletion.

# success.Idempotent

Successfully deleted scratch org %s.
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
"author": "Salesforce",
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@oclif/core": "^1.3.4",
"@salesforce/core": "3.7.7",
"@salesforce/sf-plugins-core": "^1.2.1",
"@oclif/core": "^1.6.0",
"@salesforce/core": "^3.8.0",
"@salesforce/sf-plugins-core": "^1.8.0",
"change-case": "^4.1.2",
"open": "^8.4.0",
"tslib": "^2"
},
"devDependencies": {
"@oclif/plugin-command-snapshot": "^3.1.3",
"@oclif/test": "^1.2.9",
"@oclif/test": "^2.1.0",
"@salesforce/cli-plugins-testkit": "^1.5.12",
"@salesforce/dev-config": "^2.1.3",
"@salesforce/dev-scripts": "^1.0.4",
"@salesforce/plugin-command-reference": "^2.1.1",
"@salesforce/dev-config": "^3.0.0",
"@salesforce/dev-scripts": "^2.0.1",
"@salesforce/plugin-command-reference": "^2.2.1",
"@salesforce/plugin-config": "^2.3.0",
"@salesforce/plugin-functions": "^1.5.0",
"@salesforce/plugin-functions": "^1.7.0",
"@salesforce/prettier-config": "^0.0.2",
"@salesforce/ts-sinon": "1.3.21",
"@types/shelljs": "^0.8.11",
Expand All @@ -39,15 +39,15 @@
"eslint-plugin-prettier": "^3.4.1",
"husky": "^7.0.4",
"lint-staged": "^11.2.6",
"mocha": "^8.4.0",
"mocha": "^9.1.3",
"nyc": "^15.1.0",
"oclif": "^2.4.4",
"oclif": "^2.6.0",
"prettier": "^2.5.1",
"pretty-quick": "^3.1.3",
"shelljs": "^0.8.5",
"shx": "0.3.4",
"sinon": "10.0.1",
"ts-node": "^10.5.0",
"ts-node": "^10.7.0",
"typescript": "^4.5.5"
},
"config": {
Expand Down
19 changes: 19 additions & 0 deletions schemas/env-delete-sandbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/SandboxDeleteResponse",
"definitions": {
"SandboxDeleteResponse": {
"type": "object",
"properties": {
"orgId": {
"type": "string"
},
"username": {
"type": "string"
}
},
"required": ["orgId", "username"],
"additionalProperties": false
}
}
}
19 changes: 19 additions & 0 deletions schemas/env-delete-scratch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/ScratchDeleteResponse",
"definitions": {
"ScratchDeleteResponse": {
"type": "object",
"properties": {
"orgId": {
"type": "string"
},
"username": {
"type": "string"
}
},
"required": ["orgId", "username"],
"additionalProperties": false
}
}
}
50 changes: 50 additions & 0 deletions src/commands/env/delete/sandbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, 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 } from '@salesforce/core';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-env', 'delete_sandbox');

export interface SandboxDeleteResponse {
orgId: string;
username: string;
}
export default class EnvDeleteSandbox extends SfCommand<SandboxDeleteResponse> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static flags = {
'target-org': Flags.requiredOrg({
summary: messages.getMessage('flags.target-org.summary'),
}),
'no-prompt': Flags.boolean({
char: 'p',
summary: messages.getMessage('flags.no-prompt.summary'),
}),
};

public async run(): Promise<SandboxDeleteResponse> {
const { flags } = await this.parse(EnvDeleteSandbox);
const org = flags['target-org'];

if (flags['no-prompt'] || (await this.confirm(messages.getMessage('prompt.confirm', [org.getUsername()])))) {
try {
await org.delete();
this.log(messages.getMessage('success', [org.getUsername()]));
} catch (e) {
if (e instanceof Error && e.name === 'SandboxNotFound') {
this.log(messages.getMessage('success.Idempotent', [org.getUsername()]));
} else {
throw e;
}
}

return { username: org.getUsername(), orgId: org.getOrgId() };
}
}
}
52 changes: 52 additions & 0 deletions src/commands/env/delete/scratch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, 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 } from '@salesforce/core';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-env', 'delete_scratch');

export interface ScratchDeleteResponse {
orgId: string;
username: string;
}

export default class EnvDeleteScratch extends SfCommand<ScratchDeleteResponse> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static flags = {
'target-org': Flags.requiredOrg({
summary: messages.getMessage('flags.target-org.summary'),
}),
'no-prompt': Flags.boolean({
char: 'p',
summary: messages.getMessage('flags.no-prompt.summary'),
}),
};

public async run(): Promise<ScratchDeleteResponse> {
const { flags } = await this.parse(EnvDeleteScratch);
const org = flags['target-org'];

if (flags['no-prompt'] || (await this.confirm(messages.getMessage('prompt.confirm', [org.getUsername()])))) {
try {
await org.delete();
this.log(messages.getMessage('success', [org.getUsername()]));
} catch (e) {
if (e instanceof Error && e.name === 'ScratchOrgNotFound') {
this.log(messages.getMessage('success.Idempotent', [org.getUsername()]));
} else {
throw e;
}
}

return { username: org.getUsername(), orgId: org.getOrgId() };
}
}
}
63 changes: 63 additions & 0 deletions test/commands/env/delete/scratch.nut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, 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 * as fs from 'fs';
import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit';
import { expect } from 'chai';
import { ScratchDeleteResponse } from '../../../../src/commands/env/delete/scratch';

describe('env delete scratch NUTs', () => {
const scratchOrgAlias = 'scratch-org';
let session: TestSession;
let scratchUsernames: string[];
before(async () => {
session = await TestSession.create({
project: {
name: 'testProject',
},
setupCommands: [
`sfdx force:org:create -f config/project-scratch-def.json -d 1 -a ${scratchOrgAlias}`,
'sfdx force:org:create -f config/project-scratch-def.json -d 1',
'sfdx force:org:create -f config/project-scratch-def.json -d 1 -s',
],
});
scratchUsernames = (session.setup as Array<{ result: { username: string } }>).map((setup) => setup.result.username);
});

after(async () => {
// clean restores sinon, but will throw when it tries to delete the alread-deleted orgs.
// so catch that and delete the dir manually
try {
session?.clean();
} catch {
await fs.promises.rmdir(session.dir, { recursive: true });
}
});

it('should see default username in help', () => {
const output = execCmd<ScratchDeleteResponse>('env delete scratch --help', { ensureExitCode: 0 }).shellOutput;
expect(output).to.include(scratchUsernames[2]);
});

it('should delete the 1st scratch org by alias', () => {
const command = `env delete scratch --target-org ${scratchOrgAlias} --no-prompt --json`;
const output = execCmd<ScratchDeleteResponse>(command, { ensureExitCode: 0 }).jsonOutput.result;
expect(output.username).to.equal(scratchUsernames[0]);
});

it('should delete the 2nd scratch org by username', () => {
const command = `env delete scratch --target-org ${scratchUsernames[1]} --no-prompt --json`;
const output = execCmd<ScratchDeleteResponse>(command, { ensureExitCode: 0 }).jsonOutput.result;
expect(output.username).to.equal(scratchUsernames[1]);
});

it('should delete the 3rd scratch org because it is the default', () => {
const command = 'env delete scratch --no-prompt --json';
const output = execCmd<ScratchDeleteResponse>(command, { ensureExitCode: 0 }).jsonOutput.result;
expect(output.username).to.equal(scratchUsernames[2]);
});
});
Loading

0 comments on commit 27e460d

Please sign in to comment.