@appsemble/cli 0.27.7 → 0.27.9
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/README.md +25 -7
- package/commands/app/index.js +2 -0
- package/commands/app/patch.d.ts +22 -0
- package/commands/app/patch.js +115 -0
- package/commands/app/publish.js +4 -1
- package/commands/app/update.js +3 -0
- package/commands/serve.js +1 -1
- package/lib/app.d.ts +12 -5
- package/lib/app.js +120 -72
- package/package.json +5 -5
- package/templates/apps/empty/app-definition.yaml +2 -2
- package/templates/blocks/mini-jsx/package.json +1 -1
- package/templates/blocks/preact/package.json +2 -2
- package/templates/blocks/vanilla/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble CLI
|
|
2
2
|
|
|
3
3
|
> Manage apps and blocks from the command line.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/cli)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.27.9)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -157,6 +157,24 @@ Similarly, to update an app using an app directory, run the following command:
|
|
|
157
157
|
appsemble app update --id <app-id> <path/to/updated app>.
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
If you want to update settings for an app, run the following command:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
appsemble app patch --id <app-id>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Use `--help` flag with the above command to view the supported arguments.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
**🛈NOTE**
|
|
171
|
+
|
|
172
|
+
> The `app patch` command supports updating the `locked` property of an app, locked property is
|
|
173
|
+
> updated before other properties. If you set the locked property to `fullLock`, any other changes
|
|
174
|
+
> won’t be applied unless you are using `--force` flag.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
160
178
|
To delete an app using the CLI use the following command:
|
|
161
179
|
|
|
162
180
|
```sh
|
|
@@ -169,10 +187,10 @@ To export an app as a zip file, run the following:
|
|
|
169
187
|
appsemble app export --id <app-id>
|
|
170
188
|
```
|
|
171
189
|
|
|
172
|
-
Supported arguments for this
|
|
173
|
-
whether to include the resources in the exported file, the same can be applied to assets and
|
|
174
|
-
the path of the folder where you want to put your downloaded file. The default path is
|
|
175
|
-
resources and assets are by default `false`.
|
|
190
|
+
Supported arguments for this command are `resources`, `assets` and `path`. Resources is used to
|
|
191
|
+
specify whether to include the resources in the exported file, the same can be applied to assets and
|
|
192
|
+
path is the path of the folder where you want to put your downloaded file. The default path is
|
|
193
|
+
`./apps`, and resources and assets are by default `false`.
|
|
176
194
|
|
|
177
195
|
To import an app from a zip file, use the following command:
|
|
178
196
|
|
|
@@ -305,5 +323,5 @@ appsemble run-cronjobs --interval 30
|
|
|
305
323
|
|
|
306
324
|
## License
|
|
307
325
|
|
|
308
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.
|
|
326
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.9/LICENSE.md) ©
|
|
309
327
|
[Appsemble](https://appsemble.com)
|
package/commands/app/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as remove from './delete.js';
|
|
|
3
3
|
import * as exportApp from './export.js';
|
|
4
4
|
import * as extractMessages from './extract-messages.js';
|
|
5
5
|
import * as importApp from './import.js';
|
|
6
|
+
import * as patch from './patch.js';
|
|
6
7
|
import * as publish from './publish.js';
|
|
7
8
|
import * as update from './update.js';
|
|
8
9
|
export { noop as handler } from '@appsemble/utils';
|
|
@@ -16,6 +17,7 @@ export function builder(yargs) {
|
|
|
16
17
|
.command(exportApp)
|
|
17
18
|
.command(extractMessages)
|
|
18
19
|
.command(importApp)
|
|
20
|
+
.command(patch)
|
|
19
21
|
.command(update)
|
|
20
22
|
.demandCommand(1);
|
|
21
23
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type AppLock, type AppVisibility } from '@appsemble/types';
|
|
2
|
+
import { type Argv } from 'yargs';
|
|
3
|
+
import { type BaseArguments } from '../../types.js';
|
|
4
|
+
interface PatchAppArguments extends BaseArguments {
|
|
5
|
+
id: number;
|
|
6
|
+
iconBackground?: string;
|
|
7
|
+
force?: boolean;
|
|
8
|
+
path?: string;
|
|
9
|
+
visibility?: AppVisibility;
|
|
10
|
+
showAppDefinition?: boolean;
|
|
11
|
+
template?: boolean;
|
|
12
|
+
demoMode?: boolean;
|
|
13
|
+
locked?: AppLock;
|
|
14
|
+
showAppsembleOAuth2Login?: boolean;
|
|
15
|
+
showAppsembleLogin?: boolean;
|
|
16
|
+
enableSelfRegistration?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare const command = "patch";
|
|
19
|
+
export declare const description = "Change the settings of an app without having the entire source.";
|
|
20
|
+
export declare function builder(yargs: Argv): Argv<any>;
|
|
21
|
+
export declare function handler({ clientCredentials, id, remote, ...args }: PatchAppArguments): Promise<void>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { authenticate, logger } from '@appsemble/node-utils';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import FormData from 'form-data';
|
|
4
|
+
export const command = 'patch';
|
|
5
|
+
export const description = 'Change the settings of an app without having the entire source.';
|
|
6
|
+
export function builder(yargs) {
|
|
7
|
+
return yargs
|
|
8
|
+
.option('id', {
|
|
9
|
+
description: 'Id of the app to patch.',
|
|
10
|
+
demandOption: true,
|
|
11
|
+
})
|
|
12
|
+
.option('iconBackground', {
|
|
13
|
+
description: 'The background color to use for the icon in opaque contexts.',
|
|
14
|
+
})
|
|
15
|
+
.option('path', {
|
|
16
|
+
description: 'Path to be used to determine the URL path of the app',
|
|
17
|
+
})
|
|
18
|
+
.option('visibility', {
|
|
19
|
+
description: 'Visibility of the app in the public app store.',
|
|
20
|
+
choices: ['public', 'unlisted', 'private'],
|
|
21
|
+
})
|
|
22
|
+
.option('showAppDefinition', {
|
|
23
|
+
description: 'Whether to expose the app definition in Appsemble studio.',
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
})
|
|
26
|
+
.option('template', {
|
|
27
|
+
description: 'Whether to mark the app as template',
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
})
|
|
30
|
+
.option('demoMode', {
|
|
31
|
+
description: 'Whether the app should be used in demo mode',
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
})
|
|
34
|
+
.option('locked', {
|
|
35
|
+
description: 'Change the value of AppLock for your app.',
|
|
36
|
+
type: 'string',
|
|
37
|
+
choices: ['fullLock', 'studioLock', 'unlocked'],
|
|
38
|
+
})
|
|
39
|
+
.option('showAppsembleOAuth2Login', {
|
|
40
|
+
description: 'Whether the Appsemble OAuth2 login method should be shown',
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
})
|
|
43
|
+
.option('showAppsembleLogin', {
|
|
44
|
+
description: 'Whether the Appsemble password login method should be shown',
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
})
|
|
47
|
+
.option('enableSelfRegistration', {
|
|
48
|
+
description: 'Whether new users should be able to register themselves',
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
})
|
|
51
|
+
.option('force', {
|
|
52
|
+
description: 'Whether the lock property should be ignored',
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export async function handler({ clientCredentials, id, remote, ...args }) {
|
|
57
|
+
await authenticate(remote, 'apps:write', clientCredentials);
|
|
58
|
+
const formData = new FormData();
|
|
59
|
+
if (args.path) {
|
|
60
|
+
logger.info(`Setting app path to ${args.path}`);
|
|
61
|
+
formData.append('path', args.path);
|
|
62
|
+
}
|
|
63
|
+
if (args.force) {
|
|
64
|
+
formData.append('force', String(args.force));
|
|
65
|
+
}
|
|
66
|
+
if (args.demoMode) {
|
|
67
|
+
logger.info(`Setting app demo mode to ${args.demoMode}`);
|
|
68
|
+
formData.append('demoMode', String(args.demoMode));
|
|
69
|
+
}
|
|
70
|
+
if (args.template) {
|
|
71
|
+
logger.info(`Setting template to ${args.template}`);
|
|
72
|
+
formData.append('template', String(args.template));
|
|
73
|
+
}
|
|
74
|
+
if (args.visibility) {
|
|
75
|
+
logger.info(`Setting app visibility to ${args.visibility}`);
|
|
76
|
+
formData.append('visibility', args.visibility);
|
|
77
|
+
}
|
|
78
|
+
if (args.iconBackground) {
|
|
79
|
+
logger.info(`Setting app icon background to ${args.iconBackground}`);
|
|
80
|
+
formData.append('iconBackground', args.iconBackground);
|
|
81
|
+
}
|
|
82
|
+
if (args.showAppDefinition) {
|
|
83
|
+
logger.info(`Setting showAppDefinition to ${args.showAppDefinition}`);
|
|
84
|
+
formData.append('showAppDefinition', String(args.showAppDefinition));
|
|
85
|
+
}
|
|
86
|
+
if (args.showAppsembleLogin) {
|
|
87
|
+
logger.info(`Setting showAppsembleLogin to ${args.showAppsembleLogin}`);
|
|
88
|
+
formData.append('showAppsembleLogin', String(args.showAppsembleLogin));
|
|
89
|
+
}
|
|
90
|
+
if (args.showAppsembleOAuth2Login) {
|
|
91
|
+
logger.info(`Setting showAppsembleOAuth2Login to ${args.showAppsembleOAuth2Login}`);
|
|
92
|
+
formData.append('showAppsembleOAuth2Login', String(args.showAppsembleOAuth2Login));
|
|
93
|
+
}
|
|
94
|
+
if (args.enableSelfRegistration) {
|
|
95
|
+
logger.info(`Setting enableSelfRegistration to ${args.enableSelfRegistration}`);
|
|
96
|
+
formData.append('enableSelfRegistration', String(args.enableSelfRegistration));
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
if (args.locked) {
|
|
100
|
+
await axios.post(`api/apps/${id}/lock`, {
|
|
101
|
+
locked: args.locked,
|
|
102
|
+
});
|
|
103
|
+
logger.info(`Successfully set app lock value for app with id ${id} to ${args.locked}`);
|
|
104
|
+
if (!formData.getLengthSync()) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
await axios.patch(`/api/apps/${id}`, formData);
|
|
109
|
+
logger.info(`Successfully updated app with id ${id}`);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
logger.error(error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=patch.js.map
|
package/commands/app/publish.js
CHANGED
|
@@ -14,7 +14,7 @@ export function builder(yargs) {
|
|
|
14
14
|
describe: 'If specified, use the specified context from .appsemblerc.yaml',
|
|
15
15
|
})
|
|
16
16
|
.option('organization', {
|
|
17
|
-
describe: 'The organization ID the app should be published
|
|
17
|
+
describe: 'The organization ID the app should be published under.',
|
|
18
18
|
})
|
|
19
19
|
.option('icon', {
|
|
20
20
|
describe: 'The icon to upload. By default "icon.png" in the app directory is used.',
|
|
@@ -27,6 +27,9 @@ export function builder(yargs) {
|
|
|
27
27
|
.option('maskable-icon', {
|
|
28
28
|
describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
|
|
29
29
|
coerce: coerceFile,
|
|
30
|
+
})
|
|
31
|
+
.option('variant', {
|
|
32
|
+
describe: 'The shared variant to use instead.',
|
|
30
33
|
})
|
|
31
34
|
.option('visibility', {
|
|
32
35
|
describe: 'Visibility of the app in the public app store.',
|
package/commands/app/update.js
CHANGED
|
@@ -29,6 +29,9 @@ export function builder(yargs) {
|
|
|
29
29
|
.option('maskable-icon', {
|
|
30
30
|
describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
|
|
31
31
|
coerce: coerceFile,
|
|
32
|
+
})
|
|
33
|
+
.option('variant', {
|
|
34
|
+
describe: 'The shared variant to use instead.',
|
|
32
35
|
})
|
|
33
36
|
.option('visibility', {
|
|
34
37
|
describe: 'Visibility of the app in the public app store.',
|
package/commands/serve.js
CHANGED
|
@@ -56,7 +56,7 @@ export function builder(yargs) {
|
|
|
56
56
|
export async function handler(argv) {
|
|
57
57
|
setArgv(argv);
|
|
58
58
|
const appPath = join(process.cwd(), argv.path);
|
|
59
|
-
const [, ,
|
|
59
|
+
const [, , appsembleApp] = await traverseAppDirectory(appPath, new FormData());
|
|
60
60
|
const appRoles = appsembleApp.definition.roles;
|
|
61
61
|
const passedUserRole = argv['user-role'];
|
|
62
62
|
if (passedUserRole && !(appRoles === null || appRoles === void 0 ? void 0 : appRoles.includes(passedUserRole))) {
|
package/lib/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
3
|
import { type ReadStream } from 'node:fs';
|
|
4
|
-
import { type App, type
|
|
4
|
+
import { type App, type AppsembleRC, type AppVisibility } from '@appsemble/types';
|
|
5
5
|
import FormData from 'form-data';
|
|
6
6
|
interface PublishAppParams {
|
|
7
7
|
/**
|
|
@@ -20,6 +20,10 @@ interface PublishAppParams {
|
|
|
20
20
|
* The path in which the App YAML is located.
|
|
21
21
|
*/
|
|
22
22
|
path: string;
|
|
23
|
+
/**
|
|
24
|
+
* The shared variant to use instead.
|
|
25
|
+
*/
|
|
26
|
+
variant?: string;
|
|
23
27
|
/**
|
|
24
28
|
* Visibility of the app in the public app store.
|
|
25
29
|
*/
|
|
@@ -103,6 +107,10 @@ interface UpdateAppParams {
|
|
|
103
107
|
* The path in which the App YAML is located.
|
|
104
108
|
*/
|
|
105
109
|
path: string;
|
|
110
|
+
/**
|
|
111
|
+
* The shared variant to use instead.
|
|
112
|
+
*/
|
|
113
|
+
variant?: string;
|
|
106
114
|
/**
|
|
107
115
|
* Visibility of the app in the public app store.
|
|
108
116
|
*/
|
|
@@ -164,11 +172,10 @@ interface UpdateAppParams {
|
|
|
164
172
|
* Traverses an app directory and appends the files it finds to the given FormData object.
|
|
165
173
|
*
|
|
166
174
|
* @param path The path of the app directory to traverse.
|
|
167
|
-
* @param context The Context to use from `.appsemblerc.yaml`.
|
|
168
175
|
* @param formData The FormData object to append the results into..
|
|
169
176
|
* @returns The context from `.appsemblerc.yaml` if a match was found.
|
|
170
177
|
*/
|
|
171
|
-
export declare function traverseAppDirectory(path: string,
|
|
178
|
+
export declare function traverseAppDirectory(path: string, formData: FormData): Promise<[AppsembleRC, string, App]>;
|
|
172
179
|
/**
|
|
173
180
|
* Export an app as a zip file.
|
|
174
181
|
*
|
|
@@ -203,13 +210,13 @@ export declare function writeAppMessages(path: string, languages: string[], veri
|
|
|
203
210
|
*
|
|
204
211
|
* @param argv The command line options used for updating the app.
|
|
205
212
|
*/
|
|
206
|
-
export declare function updateApp({
|
|
213
|
+
export declare function updateApp({ clientCredentials, context, force, path, ...options }: UpdateAppParams): Promise<void>;
|
|
207
214
|
/**
|
|
208
215
|
* Publish a new app.
|
|
209
216
|
*
|
|
210
217
|
* @param options The options to use for publishing an app.
|
|
211
218
|
*/
|
|
212
|
-
export declare function publishApp({
|
|
219
|
+
export declare function publishApp({ clientCredentials, context, dryRun, modifyContext, path, ...options }: PublishAppParams): Promise<void>;
|
|
213
220
|
/**
|
|
214
221
|
* Resolve the app’s ID and remote based on the app context.
|
|
215
222
|
*
|
package/lib/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createReadStream, createWriteStream, existsSync } from 'node:fs';
|
|
1
|
+
import { cpSync, createReadStream, createWriteStream, existsSync } from 'node:fs';
|
|
2
2
|
import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
3
3
|
import { basename, dirname, join, parse, relative, resolve } from 'node:path';
|
|
4
4
|
import { inspect } from 'node:util';
|
|
@@ -20,21 +20,25 @@ import { publishResourcesRecursively } from './resource.js';
|
|
|
20
20
|
* Traverses an app directory and appends the files it finds to the given FormData object.
|
|
21
21
|
*
|
|
22
22
|
* @param path The path of the app directory to traverse.
|
|
23
|
-
* @param context The Context to use from `.appsemblerc.yaml`.
|
|
24
23
|
* @param formData The FormData object to append the results into..
|
|
25
24
|
* @returns The context from `.appsemblerc.yaml` if a match was found.
|
|
26
25
|
*/
|
|
27
|
-
export async function traverseAppDirectory(path,
|
|
28
|
-
let discoveredContext;
|
|
26
|
+
export async function traverseAppDirectory(path, formData) {
|
|
29
27
|
let rc;
|
|
30
|
-
let iconPath;
|
|
31
|
-
let maskableIconPath;
|
|
32
28
|
let yaml;
|
|
33
29
|
let controllerPath;
|
|
34
30
|
let controllerBuildConfig;
|
|
35
31
|
let controllerBuildResult;
|
|
36
32
|
let controllerCode;
|
|
37
33
|
let controllerImplementations;
|
|
34
|
+
let supportedLanguages = new Set();
|
|
35
|
+
try {
|
|
36
|
+
const languageFiles = await readdir(join(path, 'i18n'));
|
|
37
|
+
supportedLanguages = new Set(languageFiles.map((lang) => lang.split('.json')[0].toLowerCase()));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
logger.warn(`Could not read ${join(path, 'i18n')}. No supported languages found.`);
|
|
41
|
+
}
|
|
38
42
|
const gatheredData = {
|
|
39
43
|
screenshotUrls: [],
|
|
40
44
|
};
|
|
@@ -48,10 +52,6 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
48
52
|
formData.append('iconBackground', rc.iconBackground);
|
|
49
53
|
gatheredData.iconBackground = rc.iconBackground;
|
|
50
54
|
}
|
|
51
|
-
if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
|
|
52
|
-
discoveredContext = rc.context[context];
|
|
53
|
-
logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
|
|
54
|
-
}
|
|
55
55
|
break;
|
|
56
56
|
case 'app-definition.yaml': {
|
|
57
57
|
logger.info(`Using app definition from ${filepath}`);
|
|
@@ -66,11 +66,9 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
66
66
|
}
|
|
67
67
|
case 'icon.png':
|
|
68
68
|
case 'icon.svg':
|
|
69
|
-
iconPath = filepath;
|
|
70
69
|
gatheredData.iconUrl = filepath;
|
|
71
70
|
return;
|
|
72
71
|
case 'maskable-icon.png':
|
|
73
|
-
maskableIconPath = filepath;
|
|
74
72
|
return;
|
|
75
73
|
case 'readme.md':
|
|
76
74
|
logger.info(`Including longDescription from ${filepath}`);
|
|
@@ -79,13 +77,28 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
79
77
|
return;
|
|
80
78
|
case 'screenshots':
|
|
81
79
|
return opendirSafe(filepath, (screenshotPath, screenshotStat) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
if (screenshotStat.isDirectory() &&
|
|
81
|
+
!['screenshots', ...supportedLanguages].includes(screenshotStat.name)) {
|
|
82
|
+
logger.warn(`Unsupported directory name at ${screenshotPath}. Skipping...`);
|
|
85
83
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
if (screenshotStat.isFile()) {
|
|
85
|
+
const screenshotDirectoryPath = dirname(screenshotPath);
|
|
86
|
+
const screenshotDirectoryName = basename(screenshotDirectoryPath);
|
|
87
|
+
if (!['screenshots', ...supportedLanguages].includes(screenshotDirectoryName)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const screenshotName = basename(screenshotPath);
|
|
91
|
+
const language = supportedLanguages.has(screenshotDirectoryName)
|
|
92
|
+
? screenshotDirectoryName
|
|
93
|
+
: 'unspecified';
|
|
94
|
+
const tmpFilePath = join(screenshotDirectoryPath, `${language}-${screenshotName}`);
|
|
95
|
+
cpSync(screenshotPath, tmpFilePath);
|
|
96
|
+
logger.info(`Adding screenshot ${tmpFilePath} 🖼️`);
|
|
97
|
+
formData.append('screenshots', createReadStream(tmpFilePath));
|
|
98
|
+
gatheredData.screenshotUrls.push(basename(screenshotPath));
|
|
99
|
+
rm(tmpFilePath);
|
|
100
|
+
}
|
|
101
|
+
}, { allowMissing: true, recursive: true });
|
|
89
102
|
case 'theme':
|
|
90
103
|
return opendirSafe(filepath, async (themeDir, themeStat) => {
|
|
91
104
|
const name = themeStat.name.toLowerCase();
|
|
@@ -117,6 +130,34 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
117
130
|
if (yaml === undefined) {
|
|
118
131
|
throw new AppsembleError('No app definition found');
|
|
119
132
|
}
|
|
133
|
+
return [rc, yaml, gatheredData];
|
|
134
|
+
}
|
|
135
|
+
async function retrieveContext(path, context) {
|
|
136
|
+
let rc;
|
|
137
|
+
let discoveredContext;
|
|
138
|
+
let iconPath;
|
|
139
|
+
let maskableIconPath;
|
|
140
|
+
await opendirSafe(path, async (filepath, filestat) => {
|
|
141
|
+
switch (filestat.name.toLowerCase()) {
|
|
142
|
+
case '.appsemblerc.yaml':
|
|
143
|
+
logger.info(`Reading app settings from ${filepath}`);
|
|
144
|
+
[rc] = await readData(filepath);
|
|
145
|
+
if (context && has(rc === null || rc === void 0 ? void 0 : rc.context, context)) {
|
|
146
|
+
discoveredContext = rc.context[context];
|
|
147
|
+
logger.verbose(`Using context: ${inspect(discoveredContext, { colors: true })}`);
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case 'icon.png':
|
|
151
|
+
case 'icon.svg':
|
|
152
|
+
iconPath = filepath;
|
|
153
|
+
return;
|
|
154
|
+
case 'maskable-icon.png':
|
|
155
|
+
maskableIconPath = filepath;
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
120
161
|
discoveredContext || (discoveredContext = {});
|
|
121
162
|
discoveredContext.icon = discoveredContext.icon
|
|
122
163
|
? resolve(path, discoveredContext.icon)
|
|
@@ -124,7 +165,7 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
124
165
|
discoveredContext.maskableIcon = discoveredContext.maskableIcon
|
|
125
166
|
? resolve(path, discoveredContext.maskableIcon)
|
|
126
167
|
: maskableIconPath;
|
|
127
|
-
return
|
|
168
|
+
return discoveredContext;
|
|
128
169
|
}
|
|
129
170
|
/**
|
|
130
171
|
* Export an app as a zip file.
|
|
@@ -412,38 +453,45 @@ This block version is not used in the app`);
|
|
|
412
453
|
*
|
|
413
454
|
* @param argv The command line options used for updating the app.
|
|
414
455
|
*/
|
|
415
|
-
export async function updateApp({
|
|
416
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
456
|
+
export async function updateApp({ clientCredentials, context, force, path, ...options }) {
|
|
457
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
417
458
|
const file = await stat(path);
|
|
418
459
|
const formData = new FormData();
|
|
419
|
-
|
|
460
|
+
const appsembleContext = await retrieveContext(path, context);
|
|
461
|
+
let yaml;
|
|
462
|
+
let filename = relative(process.cwd(), path);
|
|
463
|
+
const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
|
|
420
464
|
let appVariantPath = path;
|
|
421
|
-
if (existsSync(join(path, 'variants',
|
|
422
|
-
await applyAppVariant(path,
|
|
423
|
-
appVariantPath = join(dirname(path), `${basename(path)}-${
|
|
465
|
+
if (variant && existsSync(join(path, 'variants', variant))) {
|
|
466
|
+
await applyAppVariant(path, variant);
|
|
467
|
+
appVariantPath = join(dirname(path), `${basename(path)}-${variant}`);
|
|
424
468
|
}
|
|
425
469
|
else {
|
|
426
|
-
logger.warn(`App variant ${
|
|
470
|
+
logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
|
|
427
471
|
}
|
|
428
472
|
if (file.isFile()) {
|
|
429
473
|
const [, data] = await readData(appVariantPath);
|
|
474
|
+
yaml = data;
|
|
430
475
|
formData.append('yaml', data);
|
|
431
476
|
}
|
|
432
477
|
else {
|
|
433
|
-
[
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
const
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
const
|
|
478
|
+
[, yaml] = await traverseAppDirectory(appVariantPath, formData);
|
|
479
|
+
filename = join(filename, 'app-definition.yaml');
|
|
480
|
+
}
|
|
481
|
+
const remote = (_c = appsembleContext.remote) !== null && _c !== void 0 ? _c : options.remote;
|
|
482
|
+
const id = (_d = appsembleContext.id) !== null && _d !== void 0 ? _d : options.id;
|
|
483
|
+
const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
|
|
484
|
+
const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
|
|
485
|
+
const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
|
|
486
|
+
const visibility = (_l = appsembleContext.visibility) !== null && _l !== void 0 ? _l : options.visibility;
|
|
487
|
+
const iconBackground = (_m = appsembleContext.iconBackground) !== null && _m !== void 0 ? _m : options.iconBackground;
|
|
488
|
+
const icon = (_o = options.icon) !== null && _o !== void 0 ? _o : appsembleContext.icon;
|
|
489
|
+
const maskableIcon = (_p = options.maskableIcon) !== null && _p !== void 0 ? _p : appsembleContext.maskableIcon;
|
|
490
|
+
const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
|
|
491
|
+
const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
|
|
492
|
+
const googleAnalyticsId = (_s = appsembleContext.googleAnalyticsId) !== null && _s !== void 0 ? _s : options.googleAnalyticsId;
|
|
493
|
+
const resources = (_t = appsembleContext.resources) !== null && _t !== void 0 ? _t : options.resources;
|
|
494
|
+
const assets = (_u = appsembleContext.assets) !== null && _u !== void 0 ? _u : options.assets;
|
|
447
495
|
const { appLock } = appsembleContext;
|
|
448
496
|
logger.info(`App id: ${id}`);
|
|
449
497
|
logger.verbose(`App remote: ${remote}`);
|
|
@@ -461,12 +509,12 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
|
|
|
461
509
|
formData.append('iconBackground', iconBackground);
|
|
462
510
|
if (icon) {
|
|
463
511
|
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
464
|
-
logger.info(`Using icon from ${(
|
|
512
|
+
logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
|
|
465
513
|
formData.append('icon', realIcon);
|
|
466
514
|
}
|
|
467
515
|
if (maskableIcon) {
|
|
468
516
|
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
469
|
-
logger.info(`Using maskable icon from ${(
|
|
517
|
+
logger.info(`Using maskable icon from ${(_w = realIcon.path) !== null && _w !== void 0 ? _w : 'stdin'}`);
|
|
470
518
|
formData.append('maskableIcon', realIcon);
|
|
471
519
|
}
|
|
472
520
|
if (sentryDsn) {
|
|
@@ -499,19 +547,16 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
|
|
|
499
547
|
}
|
|
500
548
|
let data;
|
|
501
549
|
try {
|
|
502
|
-
data = await axios
|
|
503
|
-
.patch(`/api/apps/${id}`, formData, { baseURL: remote })
|
|
504
|
-
.then((r) => r.data);
|
|
550
|
+
({ data } = await axios.patch(`/api/apps/${id}`, formData, { baseURL: remote }));
|
|
505
551
|
}
|
|
506
552
|
catch (error) {
|
|
507
553
|
if (!axios.isAxiosError(error)) {
|
|
508
554
|
throw error;
|
|
509
555
|
}
|
|
510
|
-
if (((
|
|
556
|
+
if (((_y = (_x = error.response) === null || _x === void 0 ? void 0 : _x.data) === null || _y === void 0 ? void 0 : _y.message) !== 'App validation failed') {
|
|
511
557
|
throw error;
|
|
512
558
|
}
|
|
513
|
-
|
|
514
|
-
process.exit(1);
|
|
559
|
+
throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
|
|
515
560
|
}
|
|
516
561
|
if (file.isDirectory()) {
|
|
517
562
|
// After uploading the app, upload block styles and messages if they are available
|
|
@@ -539,21 +584,22 @@ export async function updateApp({ assets, clientCredentials, context, force, pat
|
|
|
539
584
|
*
|
|
540
585
|
* @param options The options to use for publishing an app.
|
|
541
586
|
*/
|
|
542
|
-
export async function publishApp({
|
|
543
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
587
|
+
export async function publishApp({ clientCredentials, context, dryRun, modifyContext, path, ...options }) {
|
|
588
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
544
589
|
const file = await stat(path);
|
|
545
590
|
const formData = new FormData();
|
|
546
|
-
|
|
591
|
+
const appsembleContext = await retrieveContext(path, context);
|
|
547
592
|
let rc;
|
|
548
593
|
let yaml;
|
|
549
594
|
let filename = relative(process.cwd(), path);
|
|
595
|
+
const variant = (_b = (_a = appsembleContext.variant) !== null && _a !== void 0 ? _a : options.variant) !== null && _b !== void 0 ? _b : context;
|
|
550
596
|
let appVariantPath = path;
|
|
551
|
-
if (existsSync(join(path, 'variants',
|
|
552
|
-
await applyAppVariant(path,
|
|
553
|
-
appVariantPath = join(dirname(path), `${basename(path)}-${
|
|
597
|
+
if (variant && existsSync(join(path, 'variants', variant))) {
|
|
598
|
+
await applyAppVariant(path, variant);
|
|
599
|
+
appVariantPath = join(dirname(path), `${basename(path)}-${variant}`);
|
|
554
600
|
}
|
|
555
601
|
else {
|
|
556
|
-
logger.warn(`App variant ${
|
|
602
|
+
logger.warn(`App variant ${variant} is not defined in ${join(path, 'variants')}. Using default app.`);
|
|
557
603
|
}
|
|
558
604
|
if (file.isFile()) {
|
|
559
605
|
// Assuming file is App YAML
|
|
@@ -562,21 +608,23 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
|
|
|
562
608
|
formData.append('yaml', data);
|
|
563
609
|
}
|
|
564
610
|
else {
|
|
565
|
-
[
|
|
611
|
+
[rc, yaml] = await traverseAppDirectory(appVariantPath, formData);
|
|
566
612
|
filename = join(filename, 'app-definition.yaml');
|
|
567
613
|
}
|
|
568
|
-
const remote = (
|
|
569
|
-
const organizationId = (
|
|
570
|
-
const template = (
|
|
571
|
-
const assetsClonable = (
|
|
572
|
-
const demoMode = (
|
|
573
|
-
const visibility = (
|
|
574
|
-
const iconBackground = (
|
|
575
|
-
const icon = (
|
|
576
|
-
const maskableIcon = (
|
|
577
|
-
const sentryDsn = (
|
|
578
|
-
const sentryEnvironment = (
|
|
579
|
-
const googleAnalyticsId = (
|
|
614
|
+
const remote = (_c = appsembleContext.remote) !== null && _c !== void 0 ? _c : options.remote;
|
|
615
|
+
const organizationId = (_d = appsembleContext.organization) !== null && _d !== void 0 ? _d : options.organization;
|
|
616
|
+
const template = (_f = (_e = appsembleContext.template) !== null && _e !== void 0 ? _e : options.template) !== null && _f !== void 0 ? _f : false;
|
|
617
|
+
const assetsClonable = (_h = (_g = appsembleContext.assetsClonable) !== null && _g !== void 0 ? _g : options.assetsClonable) !== null && _h !== void 0 ? _h : false;
|
|
618
|
+
const demoMode = (_k = (_j = appsembleContext.demoMode) !== null && _j !== void 0 ? _j : options.demoMode) !== null && _k !== void 0 ? _k : false;
|
|
619
|
+
const visibility = (_l = appsembleContext.visibility) !== null && _l !== void 0 ? _l : options.visibility;
|
|
620
|
+
const iconBackground = (_m = appsembleContext.iconBackground) !== null && _m !== void 0 ? _m : options.iconBackground;
|
|
621
|
+
const icon = (_o = options.icon) !== null && _o !== void 0 ? _o : appsembleContext.icon;
|
|
622
|
+
const maskableIcon = (_p = options.maskableIcon) !== null && _p !== void 0 ? _p : appsembleContext.maskableIcon;
|
|
623
|
+
const sentryDsn = (_q = appsembleContext.sentryDsn) !== null && _q !== void 0 ? _q : options.sentryDsn;
|
|
624
|
+
const sentryEnvironment = (_r = appsembleContext.sentryEnvironment) !== null && _r !== void 0 ? _r : options.sentryEnvironment;
|
|
625
|
+
const googleAnalyticsId = (_s = appsembleContext.googleAnalyticsId) !== null && _s !== void 0 ? _s : options.googleAnalyticsId;
|
|
626
|
+
const assets = (_t = appsembleContext.assets) !== null && _t !== void 0 ? _t : options.assets;
|
|
627
|
+
const resources = (_u = appsembleContext.resources) !== null && _u !== void 0 ? _u : options.resources;
|
|
580
628
|
const appLock = appsembleContext.appLock || 'unlocked';
|
|
581
629
|
logger.verbose(`App remote: ${remote}`);
|
|
582
630
|
logger.verbose(`App organization: ${organizationId}`);
|
|
@@ -594,12 +642,12 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
|
|
|
594
642
|
formData.append('locked', appLock);
|
|
595
643
|
if (icon) {
|
|
596
644
|
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
597
|
-
logger.info(`Using icon from ${(
|
|
645
|
+
logger.info(`Using icon from ${(_v = realIcon.path) !== null && _v !== void 0 ? _v : 'stdin'}`);
|
|
598
646
|
formData.append('icon', realIcon);
|
|
599
647
|
}
|
|
600
648
|
if (maskableIcon) {
|
|
601
649
|
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
602
|
-
logger.info(`Using maskable icon from ${(
|
|
650
|
+
logger.info(`Using maskable icon from ${(_w = realIcon.path) !== null && _w !== void 0 ? _w : 'stdin'}`);
|
|
603
651
|
formData.append('maskableIcon', realIcon);
|
|
604
652
|
}
|
|
605
653
|
if (sentryDsn) {
|
|
@@ -629,7 +677,7 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
|
|
|
629
677
|
if (!axios.isAxiosError(error)) {
|
|
630
678
|
throw error;
|
|
631
679
|
}
|
|
632
|
-
if (((
|
|
680
|
+
if (((_y = (_x = error.response) === null || _x === void 0 ? void 0 : _x.data) === null || _y === void 0 ? void 0 : _y.message) !== 'App validation failed') {
|
|
633
681
|
throw error;
|
|
634
682
|
}
|
|
635
683
|
throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
|
|
@@ -660,7 +708,7 @@ export async function publishApp({ assets, clientCredentials, context, dryRun, m
|
|
|
660
708
|
logger.info(`Successfully published app ${data.definition.name}! 🙌`);
|
|
661
709
|
logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
|
|
662
710
|
logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
|
|
663
|
-
const rcCollections = rc.context[context].collections;
|
|
711
|
+
const rcCollections = (_z = rc.context[context]) === null || _z === void 0 ? void 0 : _z.collections;
|
|
664
712
|
if (Array.isArray(rcCollections)) {
|
|
665
713
|
for (const collectionId of rcCollections) {
|
|
666
714
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/cli",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.9",
|
|
4
4
|
"description": "The CLI for developing with Appsemble apps and blocks",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"test": "vitest"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@appsemble/node-utils": "0.27.
|
|
46
|
-
"@appsemble/types": "0.27.
|
|
47
|
-
"@appsemble/utils": "0.27.
|
|
45
|
+
"@appsemble/node-utils": "0.27.9",
|
|
46
|
+
"@appsemble/types": "0.27.9",
|
|
47
|
+
"@appsemble/utils": "0.27.9",
|
|
48
48
|
"@fortawesome/fontawesome-common-types": "^6.0.0",
|
|
49
49
|
"@koa/cors": "^4.0.0",
|
|
50
50
|
"@odata/parser": "^0.2.0",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"yargs": "^17.0.0"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@appsemble/types": "0.27.
|
|
90
|
+
"@appsemble/types": "0.27.9",
|
|
91
91
|
"@types/concat-stream": "^2.0.0",
|
|
92
92
|
"@types/inquirer": "^9.0.0",
|
|
93
93
|
"@types/normalize-path": "^3.0.0",
|
|
@@ -6,7 +6,7 @@ pages:
|
|
|
6
6
|
- name: Example Page A
|
|
7
7
|
blocks:
|
|
8
8
|
- type: action-button
|
|
9
|
-
version: 0.27.
|
|
9
|
+
version: 0.27.9
|
|
10
10
|
parameters:
|
|
11
11
|
icon: arrow-right
|
|
12
12
|
actions:
|
|
@@ -17,7 +17,7 @@ pages:
|
|
|
17
17
|
- name: Example Page B
|
|
18
18
|
blocks:
|
|
19
19
|
- type: action-button
|
|
20
|
-
version: 0.27.
|
|
20
|
+
version: 0.27.9
|
|
21
21
|
parameters:
|
|
22
22
|
icon: arrow-left
|
|
23
23
|
actions:
|