Skip to content

Commit 1d91636

Browse files
committed
refactoring bundle topics and descriptions
1 parent 7e31076 commit 1d91636

4 files changed

Lines changed: 89 additions & 26 deletions

File tree

packages/b2c-cli/package.json

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,40 +100,37 @@
100100
"description": "WebDAV file operations (ls, get, put, rm, zip, unzip)"
101101
},
102102
"mrt": {
103-
"description": "Manage Managed Runtime projects and deployments"
103+
"description": "Manage Managed Runtime (MRT) projects, environments, bundles, and deployments"
104104
},
105105
"mrt:env": {
106-
"description": "Manage MRT environments"
106+
"description": "Create, update, delete, and configure MRT environments (targets)"
107107
},
108108
"mrt:env:var": {
109-
"description": "Manage environment variables"
109+
"description": "Set and delete environment variables for MRT environments"
110110
},
111111
"mrt:env:redirect": {
112-
"description": "Manage URL redirects"
112+
"description": "Configure URL redirects and clone redirect rules between environments"
113113
},
114114
"mrt:org": {
115-
"description": "Manage MRT organizations"
115+
"description": "List organizations and view B2C Commerce instance connections"
116116
},
117117
"mrt:project": {
118-
"description": "Manage MRT projects"
118+
"description": "Create, update, delete, and configure MRT projects"
119119
},
120120
"mrt:project:member": {
121-
"description": "Manage project members"
121+
"description": "Add, remove, and update project member roles and permissions"
122122
},
123123
"mrt:project:notification": {
124-
"description": "Manage deployment notifications"
124+
"description": "Configure email notifications for deployment events (start, success, failure)"
125125
},
126126
"mrt:env:access-control": {
127-
"description": "Manage access control headers"
127+
"description": "Configure access control headers for environment security"
128128
},
129129
"mrt:bundle": {
130-
"description": "Manage and deploy bundles"
130+
"description": "Push builds, deploy bundles, view deployment history, and download artifacts"
131131
},
132132
"mrt:user": {
133-
"description": "Manage user profile and settings"
134-
},
135-
"mrt:b2c": {
136-
"description": "Manage B2C Commerce integrations"
133+
"description": "View profile, reset API keys, and configure email preferences"
137134
},
138135
"ods": {
139136
"description": "Manage On-Demand Sandboxes"

packages/b2c-cli/src/commands/mrt/bundle/download.ts

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
* SPDX-License-Identifier: Apache-2
44
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
55
*/
6-
import {Args} from '@oclif/core';
6+
import * as fs from 'node:fs';
7+
import * as path from 'node:path';
8+
import {pipeline} from 'node:stream/promises';
9+
import {Args, Flags} from '@oclif/core';
710
import {MrtCommand} from '@salesforce/b2c-tooling-sdk/cli';
811
import {downloadBundle, type DownloadBundleResult} from '@salesforce/b2c-tooling-sdk/operations/mrt';
912
import {t} from '../../../i18n/index.js';
1013

14+
type BundleDownloadResult = DownloadBundleResult & {filePath?: string};
15+
1116
/**
12-
* Get a presigned download URL for a bundle.
17+
* Download a bundle artifact from Managed Runtime.
1318
*/
1419
export default class MrtBundleDownload extends MrtCommand<typeof MrtBundleDownload> {
1520
static args = {
@@ -19,23 +24,30 @@ export default class MrtBundleDownload extends MrtCommand<typeof MrtBundleDownlo
1924
}),
2025
};
2126

22-
static description = t(
23-
'commands.mrt.bundle.download.description',
24-
'Get a presigned download URL for a Managed Runtime bundle',
25-
);
27+
static description = t('commands.mrt.bundle.download.description', 'Download a Managed Runtime bundle artifact');
2628

2729
static enableJsonFlag = true;
2830

2931
static examples = [
3032
'<%= config.bin %> <%= command.id %> 12345 --project my-storefront',
33+
'<%= config.bin %> <%= command.id %> 12345 -p my-storefront -o ./artifacts/my-bundle.tgz',
34+
'<%= config.bin %> <%= command.id %> 12345 -p my-storefront --url-only',
3135
'<%= config.bin %> <%= command.id %> 12345 -p my-storefront --json',
3236
];
3337

3438
static flags = {
3539
...MrtCommand.baseFlags,
40+
output: Flags.string({
41+
char: 'o',
42+
description: 'Output file path (default: bundle-{bundleId}.tgz)',
43+
}),
44+
'url-only': Flags.boolean({
45+
description: 'Only output the download URL without downloading',
46+
default: false,
47+
}),
3648
};
3749

38-
async run(): Promise<DownloadBundleResult> {
50+
async run(): Promise<BundleDownloadResult> {
3951
this.requireMrtCredentials();
4052

4153
const {bundleId} = this.args;
@@ -47,7 +59,14 @@ export default class MrtBundleDownload extends MrtCommand<typeof MrtBundleDownlo
4759
);
4860
}
4961

50-
this.log(t('commands.mrt.bundle.download.fetching', 'Getting download URL for bundle {{bundleId}}...', {bundleId}));
62+
const urlOnly = this.flags['url-only'];
63+
64+
this.log(
65+
t('commands.mrt.bundle.download.fetching', 'Fetching download URL for bundle {{bundleId}} from {{project}}...', {
66+
bundleId,
67+
project,
68+
}),
69+
);
5170

5271
try {
5372
const result = await downloadBundle(
@@ -59,19 +78,66 @@ export default class MrtBundleDownload extends MrtCommand<typeof MrtBundleDownlo
5978
this.getMrtAuth(),
6079
);
6180

81+
// If url-only flag or JSON mode, just return the URL
82+
if (urlOnly) {
83+
if (!this.jsonEnabled()) {
84+
this.log(
85+
t('commands.mrt.bundle.download.urlOnly', 'Download URL (valid for 1 hour):\n{{downloadUrl}}', {
86+
downloadUrl: result.downloadUrl,
87+
}),
88+
);
89+
}
90+
return result;
91+
}
92+
93+
// Download the file
94+
const outputPath = this.flags.output ?? `bundle-${bundleId}.tgz`;
95+
const absolutePath = path.resolve(outputPath);
96+
97+
this.log(
98+
t('commands.mrt.bundle.download.downloading', 'Downloading bundle {{bundleId}} to {{filePath}}...', {
99+
bundleId,
100+
filePath: outputPath,
101+
}),
102+
);
103+
104+
const response = await fetch(result.downloadUrl);
105+
if (!response.ok) {
106+
this.error(
107+
t('commands.mrt.bundle.download.httpError', 'Failed to download bundle: HTTP {{status}}', {
108+
status: response.status,
109+
}),
110+
);
111+
}
112+
113+
if (!response.body) {
114+
this.error(t('commands.mrt.bundle.download.noBody', 'Failed to download bundle: empty response'));
115+
}
116+
117+
// Ensure directory exists
118+
const dir = path.dirname(absolutePath);
119+
if (dir !== '.') {
120+
fs.mkdirSync(dir, {recursive: true});
121+
}
122+
123+
// Stream the response to file
124+
const fileStream = fs.createWriteStream(absolutePath);
125+
await pipeline(response.body, fileStream);
126+
62127
if (!this.jsonEnabled()) {
63128
this.log(
64-
t('commands.mrt.bundle.download.success', 'Download URL (valid for 1 hour):\n{{downloadUrl}}', {
65-
downloadUrl: result.downloadUrl,
129+
t('commands.mrt.bundle.download.success', 'Bundle {{bundleId}} downloaded to {{filePath}}', {
130+
bundleId,
131+
filePath: outputPath,
66132
}),
67133
);
68134
}
69135

70-
return result;
136+
return {...result, filePath: absolutePath};
71137
} catch (error) {
72138
if (error instanceof Error) {
73139
this.error(
74-
t('commands.mrt.bundle.download.failed', 'Failed to get download URL: {{message}}', {message: error.message}),
140+
t('commands.mrt.bundle.download.failed', 'Failed to download bundle: {{message}}', {message: error.message}),
75141
);
76142
}
77143
throw error;
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)