@pnp/cli-microsoft365 5.1.0-beta.c27e2b5 → 5.1.0-beta.c518929
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/Dockerfile +7 -4
- package/dist/Command.js +3 -0
- package/dist/cli/Cli.js +13 -0
- package/dist/m365/spfx/commands/spfx-doctor.js +6 -0
- package/dist/m365/spo/commands/app/app-teamspackage-download.js +180 -0
- package/dist/m365/spo/commands.js +1 -0
- package/docs/docs/cmd/spfx/spfx-doctor.md +4 -1
- package/docs/docs/cmd/spo/app/app-teamspackage-download.md +56 -0
- package/package.json +1 -1
package/.eslintrc.js
CHANGED
package/Dockerfile
CHANGED
|
@@ -21,7 +21,9 @@ RUN apk add --no-cache \
|
|
|
21
21
|
shadow \
|
|
22
22
|
bash-completion \
|
|
23
23
|
nodejs \
|
|
24
|
-
npm
|
|
24
|
+
npm \
|
|
25
|
+
python3 \
|
|
26
|
+
py3-pip
|
|
25
27
|
|
|
26
28
|
RUN adduser --system cli-microsoft365
|
|
27
29
|
USER cli-microsoft365
|
|
@@ -31,11 +33,10 @@ WORKDIR /home/cli-microsoft365
|
|
|
31
33
|
ENV 0="/bin/bash" \
|
|
32
34
|
SHELL="bash" \
|
|
33
35
|
NPM_CONFIG_PREFIX=/home/cli-microsoft365/.npm-global \
|
|
34
|
-
PATH=$PATH:/home/cli-microsoft365/.npm-global/bin \
|
|
36
|
+
PATH=$PATH:/home/cli-microsoft365/.npm-global/bin:/home/cli-microsoft365/.local/bin \
|
|
35
37
|
CLIMICROSOFT365_ENV="docker"
|
|
36
38
|
|
|
37
|
-
RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin" >> ~/.bash_profile' \
|
|
38
|
-
&& bash -c 'echo "export NPM_CONFIG_PREFIX=/home/cli-microsoft365/.npm-global" >> ~/.bash_profile' \
|
|
39
|
+
RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin:/home/.local/bin" >> ~/.bash_profile' \
|
|
39
40
|
&& bash -c 'echo "export CLIMICROSOFT365_ENV=\"docker\"" >> ~/.bash_profile' \
|
|
40
41
|
&& bash -c 'npm i -g @pnp/cli-microsoft365@${CLI_VERSION} --production --quiet --no-progress' \
|
|
41
42
|
&& bash -c 'echo "source /etc/profile.d/bash_completion.sh" >> ~/.bash_profile' \
|
|
@@ -43,4 +44,6 @@ RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin" >>
|
|
|
43
44
|
&& bash -c 'm365 cli completion sh setup' \
|
|
44
45
|
&& pwsh -c 'm365 cli completion pwsh setup --profile $profile'
|
|
45
46
|
|
|
47
|
+
RUN pip install jmespath-terminal
|
|
48
|
+
|
|
46
49
|
CMD [ "bash", "-l" ]
|
package/dist/Command.js
CHANGED
package/dist/cli/Cli.js
CHANGED
|
@@ -163,6 +163,19 @@ class Cli {
|
|
|
163
163
|
if (optionsWithoutShorts.options.output === undefined) {
|
|
164
164
|
optionsWithoutShorts.options.output = this.getSettingWithDefaultValue(settingsNames_1.settingsNames.output, undefined);
|
|
165
165
|
}
|
|
166
|
+
// validate options sets defined by the command
|
|
167
|
+
const optionsSets = this.commandToExecute.command.optionSets();
|
|
168
|
+
const argsOptions = Object.keys(this.optionsFromArgs.options);
|
|
169
|
+
const cmdArgs = this.optionsFromArgs;
|
|
170
|
+
optionsSets === null || optionsSets === void 0 ? void 0 : optionsSets.forEach((optionSet) => {
|
|
171
|
+
const commonOptions = argsOptions.filter(opt => optionSet.includes(opt));
|
|
172
|
+
if (commonOptions.length === 0) {
|
|
173
|
+
return this.closeWithError(`Specify one of the following options: ${optionSet.map(opt => opt).join(', ')}.`, cmdArgs, true);
|
|
174
|
+
}
|
|
175
|
+
if (commonOptions.length > 1) {
|
|
176
|
+
return this.closeWithError(`Specify one of the following options: ${optionSet.map(opt => opt).join(', ')}, but not multiple.`, cmdArgs, true);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
166
179
|
const validationResult = this.commandToExecute.command.validate(optionsWithoutShorts);
|
|
167
180
|
if (typeof validationResult === 'string') {
|
|
168
181
|
return this.closeWithError(validationResult, optionsWithoutShorts, true);
|
|
@@ -481,6 +481,9 @@ class SpfxDoctorCommand extends AnonymousCommand_1.default {
|
|
|
481
481
|
return 'Verifies environment configuration for using the specific version of the SharePoint Framework';
|
|
482
482
|
}
|
|
483
483
|
commandAction(logger, args, cb) {
|
|
484
|
+
if (!args.options.output) {
|
|
485
|
+
args.options.output = 'text';
|
|
486
|
+
}
|
|
484
487
|
logger.log(' ');
|
|
485
488
|
logger.log('CLI for Microsoft 365 SharePoint Framework doctor');
|
|
486
489
|
logger.log('Verifying configuration of your system for working with the SharePoint Framework');
|
|
@@ -758,6 +761,9 @@ class SpfxDoctorCommand extends AnonymousCommand_1.default {
|
|
|
758
761
|
return `${args.options.env} is not a valid SharePoint version. Valid versions are sp2016, sp2019 or spo`;
|
|
759
762
|
}
|
|
760
763
|
}
|
|
764
|
+
if (args.options.output && args.options.output !== 'text') {
|
|
765
|
+
return `The output option only accepts the type 'text'`;
|
|
766
|
+
}
|
|
761
767
|
return true;
|
|
762
768
|
}
|
|
763
769
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const request_1 = require("../../../../request");
|
|
6
|
+
const urlUtil_1 = require("../../../../utils/urlUtil");
|
|
7
|
+
const spo_1 = require("../../../../utils/spo");
|
|
8
|
+
const validation_1 = require("../../../../utils/validation");
|
|
9
|
+
const commands_1 = require("../../commands");
|
|
10
|
+
const SpoAppBaseCommand_1 = require("./SpoAppBaseCommand");
|
|
11
|
+
class SpoAppTeamsPackageDownloadCommand extends SpoAppBaseCommand_1.SpoAppBaseCommand {
|
|
12
|
+
get name() {
|
|
13
|
+
return commands_1.default.APP_TEAMSPACKAGE_DOWNLOAD;
|
|
14
|
+
}
|
|
15
|
+
get description() {
|
|
16
|
+
return 'Downloads Teams app package for an SPFx solution deployed to tenant app catalog';
|
|
17
|
+
}
|
|
18
|
+
getTelemetryProperties(args) {
|
|
19
|
+
const telemetryProps = super.getTelemetryProperties(args);
|
|
20
|
+
telemetryProps.appCatalogUrl = typeof args.options.appCatalogUrl !== 'undefined';
|
|
21
|
+
telemetryProps.appItemUniqueId = typeof args.options.appItemUniqueId !== 'undefined';
|
|
22
|
+
telemetryProps.appItemId = typeof args.options.appItemId !== 'undefined';
|
|
23
|
+
telemetryProps.appName = typeof args.options.appName !== 'undefined';
|
|
24
|
+
telemetryProps.fileName = typeof args.options.fileName !== 'undefined';
|
|
25
|
+
return telemetryProps;
|
|
26
|
+
}
|
|
27
|
+
commandAction(logger, args, cb) {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
this.appCatalogUrl = args.options.appCatalogUrl;
|
|
30
|
+
const appInfo = {
|
|
31
|
+
id: (_a = args.options.appItemId) !== null && _a !== void 0 ? _a : undefined,
|
|
32
|
+
packageFileName: (_b = args.options.fileName) !== null && _b !== void 0 ? _b : undefined
|
|
33
|
+
};
|
|
34
|
+
if (this.debug) {
|
|
35
|
+
logger.logToStderr(`appInfo: ${JSON.stringify(appInfo)}`);
|
|
36
|
+
}
|
|
37
|
+
this
|
|
38
|
+
.ensureAppInfo(logger, args, appInfo)
|
|
39
|
+
.then(_ => {
|
|
40
|
+
if (this.debug) {
|
|
41
|
+
logger.logToStderr(`ensureAppInfo: ${JSON.stringify(appInfo)}`);
|
|
42
|
+
}
|
|
43
|
+
return this.loadAppCatalogUrl(logger, args);
|
|
44
|
+
})
|
|
45
|
+
.then(_ => {
|
|
46
|
+
const requestOptions = {
|
|
47
|
+
url: `${this.appCatalogUrl}/_api/web/tenantappcatalog/downloadteamssolution(${appInfo.id})/$value`,
|
|
48
|
+
headers: {
|
|
49
|
+
accept: 'application/json;odata=nometadata'
|
|
50
|
+
},
|
|
51
|
+
responseType: 'stream'
|
|
52
|
+
};
|
|
53
|
+
return request_1.default.get(requestOptions);
|
|
54
|
+
})
|
|
55
|
+
.then((file) => {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const writer = fs.createWriteStream(appInfo.packageFileName);
|
|
58
|
+
file.data.pipe(writer);
|
|
59
|
+
writer.on('error', err => {
|
|
60
|
+
reject(err);
|
|
61
|
+
});
|
|
62
|
+
writer.on('close', () => {
|
|
63
|
+
resolve(appInfo.packageFileName);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
})
|
|
67
|
+
.then((file) => {
|
|
68
|
+
if (this.verbose) {
|
|
69
|
+
logger.logToStderr(`Package saved to ${file}`);
|
|
70
|
+
}
|
|
71
|
+
cb();
|
|
72
|
+
}, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
73
|
+
}
|
|
74
|
+
ensureAppInfo(logger, args, appInfo) {
|
|
75
|
+
if (appInfo.id && appInfo.packageFileName) {
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
if (args.options.appName && !appInfo.packageFileName) {
|
|
79
|
+
appInfo.packageFileName = this.getPackageNameFromFileName(args.options.appName);
|
|
80
|
+
}
|
|
81
|
+
return this
|
|
82
|
+
.loadAppCatalogUrl(logger, args)
|
|
83
|
+
.then(_ => {
|
|
84
|
+
const appCatalogListName = 'AppCatalog';
|
|
85
|
+
const serverRelativeAppCatalogListUrl = `${urlUtil_1.urlUtil.getServerRelativeSiteUrl(this.appCatalogUrl)}/${appCatalogListName}`;
|
|
86
|
+
let url = `${this.appCatalogUrl}/_api/web/`;
|
|
87
|
+
if (args.options.appItemUniqueId) {
|
|
88
|
+
url += `GetList('${serverRelativeAppCatalogListUrl}')/GetItemByUniqueId('${args.options.appItemUniqueId}')?$expand=File&$select=Id,File/Name`;
|
|
89
|
+
}
|
|
90
|
+
else if (args.options.appItemId) {
|
|
91
|
+
url += `GetList('${serverRelativeAppCatalogListUrl}')/GetItemById(${args.options.appItemId})?$expand=File&$select=File/Name`;
|
|
92
|
+
}
|
|
93
|
+
else if (args.options.appName) {
|
|
94
|
+
url += `getfolderbyserverrelativeurl('${appCatalogListName}')/files('${encodeURIComponent(args.options.appName)}')/ListItemAllFields?$select=Id`;
|
|
95
|
+
}
|
|
96
|
+
const requestOptions = {
|
|
97
|
+
url,
|
|
98
|
+
headers: {
|
|
99
|
+
accept: 'application/json;odata=nometadata'
|
|
100
|
+
},
|
|
101
|
+
responseType: 'json'
|
|
102
|
+
};
|
|
103
|
+
return request_1.default.get(requestOptions);
|
|
104
|
+
})
|
|
105
|
+
.then(res => {
|
|
106
|
+
if (args.options.appItemUniqueId) {
|
|
107
|
+
appInfo.id = parseInt(res.Id);
|
|
108
|
+
if (!appInfo.packageFileName) {
|
|
109
|
+
appInfo.packageFileName = this.getPackageNameFromFileName(res.File.Name);
|
|
110
|
+
}
|
|
111
|
+
return Promise.resolve();
|
|
112
|
+
}
|
|
113
|
+
if (args.options.appItemId) {
|
|
114
|
+
if (!appInfo.packageFileName) {
|
|
115
|
+
appInfo.packageFileName = this.getPackageNameFromFileName(res.File.Name);
|
|
116
|
+
}
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
}
|
|
119
|
+
// if (args.options.appName)
|
|
120
|
+
// skipped 'if' clause to provide a default code branch
|
|
121
|
+
appInfo.id = parseInt(res.Id);
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
getPackageNameFromFileName(fileName) {
|
|
126
|
+
return `${path.basename(fileName, path.extname(fileName))}.zip`;
|
|
127
|
+
}
|
|
128
|
+
loadAppCatalogUrl(logger, args) {
|
|
129
|
+
if (this.appCatalogUrl) {
|
|
130
|
+
return Promise.resolve();
|
|
131
|
+
}
|
|
132
|
+
return spo_1.spo
|
|
133
|
+
.getSpoUrl(logger, this.debug)
|
|
134
|
+
.then(spoUrl => this.getAppCatalogSiteUrl(logger, spoUrl, args))
|
|
135
|
+
.then(appCatalogUrl => {
|
|
136
|
+
this.appCatalogUrl = appCatalogUrl;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
options() {
|
|
140
|
+
const options = [
|
|
141
|
+
{ option: '--appItemId [appItemId]' },
|
|
142
|
+
{ option: '--appItemUniqueId [appItemUniqueId]' },
|
|
143
|
+
{ option: '--appName [appName]' },
|
|
144
|
+
{ option: '--fileName [fileName]' },
|
|
145
|
+
{ option: '-u, --appCatalogUrl [appCatalogUrl]' }
|
|
146
|
+
];
|
|
147
|
+
const parentOptions = super.options();
|
|
148
|
+
return options.concat(parentOptions);
|
|
149
|
+
}
|
|
150
|
+
validate(args) {
|
|
151
|
+
if (!args.options.appItemUniqueId &&
|
|
152
|
+
!args.options.appItemId &&
|
|
153
|
+
!args.options.appName) {
|
|
154
|
+
return `Specify appItemUniqueId, appItemId or appName`;
|
|
155
|
+
}
|
|
156
|
+
if ((args.options.appItemUniqueId && args.options.appItemId) ||
|
|
157
|
+
(args.options.appItemUniqueId && args.options.appName) ||
|
|
158
|
+
(args.options.appItemId && args.options.appName)) {
|
|
159
|
+
return `Specify appItemUniqueId, appItemId or appName but not multiple`;
|
|
160
|
+
}
|
|
161
|
+
if (args.options.appItemUniqueId &&
|
|
162
|
+
!validation_1.validation.isValidGuid(args.options.appItemUniqueId)) {
|
|
163
|
+
return `${args.options.appItemUniqueId} is not a valid GUID`;
|
|
164
|
+
}
|
|
165
|
+
if (args.options.appItemId &&
|
|
166
|
+
isNaN(args.options.appItemId)) {
|
|
167
|
+
return `${args.options.appItemId} is not a number`;
|
|
168
|
+
}
|
|
169
|
+
if (args.options.fileName &&
|
|
170
|
+
fs.existsSync(args.options.fileName)) {
|
|
171
|
+
return `File ${args.options.fileName} already exists`;
|
|
172
|
+
}
|
|
173
|
+
if (args.options.appCatalogUrl) {
|
|
174
|
+
return validation_1.validation.isValidSharePointUrl(args.options.appCatalogUrl);
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
module.exports = new SpoAppTeamsPackageDownloadCommand();
|
|
180
|
+
//# sourceMappingURL=app-teamspackage-download.js.map
|
|
@@ -10,6 +10,7 @@ exports.default = {
|
|
|
10
10
|
APP_LIST: `${prefix} app list`,
|
|
11
11
|
APP_REMOVE: `${prefix} app remove`,
|
|
12
12
|
APP_RETRACT: `${prefix} app retract`,
|
|
13
|
+
APP_TEAMSPACKAGE_DOWNLOAD: `${prefix} app teamspackage download`,
|
|
13
14
|
APP_UNINSTALL: `${prefix} app uninstall`,
|
|
14
15
|
APP_UPGRADE: `${prefix} app upgrade`,
|
|
15
16
|
APPPAGE_ADD: `${prefix} apppage add`,
|
|
@@ -20,7 +20,7 @@ m365 spfx doctor [options]
|
|
|
20
20
|
: JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
|
|
21
21
|
|
|
22
22
|
`-o, --output [output]`
|
|
23
|
-
: Output type. `
|
|
23
|
+
: Output type. `text`. Default `text`
|
|
24
24
|
|
|
25
25
|
`--verbose`
|
|
26
26
|
: Runs command with verbose logging
|
|
@@ -43,6 +43,9 @@ If you miss any required tools or use a version that doesn't meet the SharePoint
|
|
|
43
43
|
|
|
44
44
|
Next to verifying the readiness of your environment to use a particular version of the SharePoint Framework, you can also check if the version of the SharePoint Framework that you use is compatible with the specific version of SharePoint. Supported versions are `sp2016`, `sp2019` and `spo`.
|
|
45
45
|
|
|
46
|
+
!!! important
|
|
47
|
+
This command supports only text output.
|
|
48
|
+
|
|
46
49
|
## Examples
|
|
47
50
|
|
|
48
51
|
Verify if your environment meets the requirements to work with the SharePoint Framework
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# spo app teamspackage download
|
|
2
|
+
|
|
3
|
+
Downloads Teams app package for an SPFx solution deployed to tenant app catalog
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
m365 spo app teamspackage download [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Options
|
|
12
|
+
|
|
13
|
+
`--appItemUniqueId [appItemUniqueId]`
|
|
14
|
+
: The unique ID of the SPFx app to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
|
|
15
|
+
|
|
16
|
+
`--appItemId [appItemId]`
|
|
17
|
+
: The ID of the list item behind the SPFx app to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
|
|
18
|
+
|
|
19
|
+
`--appName [appName]`
|
|
20
|
+
: The name of the sppkg file to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
|
|
21
|
+
|
|
22
|
+
`--fileName [fileName]`
|
|
23
|
+
: Name of the file to save the package to. If not specified will use the name of the sppkg file with a `.zip` extension
|
|
24
|
+
|
|
25
|
+
`-u, --appCatalogUrl [appCatalogUrl]`
|
|
26
|
+
: URL of the tenant app catalog. If not specified, the command will try to autodiscover it
|
|
27
|
+
|
|
28
|
+
--8<-- "docs/cmd/_global.md"
|
|
29
|
+
|
|
30
|
+
## Remarks
|
|
31
|
+
|
|
32
|
+
Download the Teams app package for an SPFx solution works only for solutions deployed to the tenant app catalog.
|
|
33
|
+
|
|
34
|
+
If you try to download Teams app package for an SPFx solution that doesn't support deployment to Teams, you'll get the _Request failed with status code 404_ error.
|
|
35
|
+
|
|
36
|
+
For maximum performance, specify the URL of the tenant app catalog, the item ID (`appItemId`) of the SPFx package for which you want to download the Teams app package and the name of the file where you want to save the downloaded package to (`fileName`).
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
Downloads the Teams app package for the SPFx solution deployed to the tenant app catalog with the ID `1` to a file with .zip extension named after the .sppkg file:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
m365 spo app teamspackage download --appItemId 1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Downloads the Teams app package for the SPFx solution deployed to the tenant app catalog with the unique item ID `335a5612-3e85-462d-9d5b-c014b5abeac5` to the specified file:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
m365 spo app teamspackage download --appItemUniqueId 335a5612-3e85-462d-9d5b-c014b5abeac5 --fileName my-app.zip
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Downloads the Teams app package for the SPFx solution deployed to the specified tenant app catalog:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
m365 spo app teamspackage download --appName my-app.sppkg --appCatalogUrl https://contoso.sharepoint.com/sites/appcatalog
|
|
56
|
+
```
|
package/package.json
CHANGED