@appsemble/cli 0.25.2 → 0.27.0
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 +7 -6
- package/commands/app/create.js +1 -0
- package/commands/app/export.d.ts +2 -1
- package/commands/app/export.js +7 -4
- package/commands/app/publish.js +4 -5
- package/commands/app/update.d.ts +3 -1
- package/commands/app/update.js +14 -3
- package/commands/asset/publish.d.ts +2 -1
- package/commands/asset/publish.js +6 -1
- package/commands/resource/publish.d.ts +2 -1
- package/commands/resource/publish.js +7 -2
- package/commands/serve.js +1 -1
- package/lib/app.d.ts +16 -5
- package/lib/app.js +179 -113
- package/lib/asset.d.ts +5 -1
- package/lib/asset.js +3 -2
- package/lib/block.d.ts +1 -1
- package/lib/block.js +10 -4
- package/lib/resource.d.ts +10 -2
- package/lib/resource.js +8 -5
- package/package.json +5 -5
- package/server/options/index.d.ts +0 -1
- package/server/options/index.js +0 -1
- package/server/options/options.js +1 -2
- 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/server/options/checkSeededResources.d.ts +0 -1
- package/server/options/checkSeededResources.js +0 -4
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.0)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -169,9 +169,10 @@ To export an app as a zip file, run the following:
|
|
|
169
169
|
appsemble app export --id <app-id>
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
Supported arguments for this app are `resources` and `path`. Resources is used to specify
|
|
173
|
-
include the resources in the exported file
|
|
174
|
-
your downloaded file.
|
|
172
|
+
Supported arguments for this app are `resources`, `assets` and `path`. Resources is used to specify
|
|
173
|
+
whether to include the resources in the exported file, the same can be applied to assets and path is
|
|
174
|
+
the path of the folder where you want to put your downloaded file. The default path is `./apps`, and
|
|
175
|
+
resources and assets are by default `false`.
|
|
175
176
|
|
|
176
177
|
To import an app from a zip file, use the following command:
|
|
177
178
|
|
|
@@ -304,5 +305,5 @@ appsemble run-cronjobs --interval 30
|
|
|
304
305
|
|
|
305
306
|
## License
|
|
306
307
|
|
|
307
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.
|
|
308
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.27.0/LICENSE.md) ©
|
|
308
309
|
[Appsemble](https://appsemble.com)
|
package/commands/app/create.js
CHANGED
package/commands/app/export.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { type Argv } from 'yargs';
|
|
2
2
|
import { type BaseArguments } from '../../types.js';
|
|
3
3
|
interface ExportAppArgs extends BaseArguments {
|
|
4
|
+
assets: boolean;
|
|
4
5
|
id: number;
|
|
5
6
|
resources: boolean;
|
|
6
7
|
path?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare const command = "export";
|
|
9
|
-
export declare const description = "Export an app as a zip file using app id.\n App definition, styles and messages are exported by default, to export resources, make sure you have suitable permissions and use the '--resources' flag.";
|
|
10
|
+
export declare const description = "Export an app as a zip file using app id.\n App definition, styles, icon and messages are exported by default, to export resources, make sure you have suitable permissions and use the '--resources' flag.\n Similarly for assets use the --assets flag to include assets in the export.";
|
|
10
11
|
export declare function builder(yargs: Argv): Argv<any>;
|
|
11
12
|
export declare function handler(args: ExportAppArgs): Promise<void>;
|
|
12
13
|
export {};
|
package/commands/app/export.js
CHANGED
|
@@ -3,7 +3,8 @@ import { authenticate, logger } from '@appsemble/node-utils';
|
|
|
3
3
|
import { exportAppAsZip } from '../../lib/app.js';
|
|
4
4
|
export const command = 'export';
|
|
5
5
|
export const description = `Export an app as a zip file using app id.
|
|
6
|
-
App definition, styles and messages are exported by default, to export resources, make sure you have suitable permissions and use the '--resources' flag
|
|
6
|
+
App definition, styles, icon and messages are exported by default, to export resources, make sure you have suitable permissions and use the '--resources' flag.
|
|
7
|
+
Similarly for assets use the --assets flag to include assets in the export.`;
|
|
7
8
|
export function builder(yargs) {
|
|
8
9
|
return yargs
|
|
9
10
|
.option('id', {
|
|
@@ -12,18 +13,20 @@ export function builder(yargs) {
|
|
|
12
13
|
})
|
|
13
14
|
.option('resources', {
|
|
14
15
|
describe: 'Boolean representing whether the resources should be exported or not',
|
|
15
|
-
|
|
16
|
+
})
|
|
17
|
+
.option('assets', {
|
|
18
|
+
describe: 'Boolean representing whether assets should be exported.',
|
|
16
19
|
})
|
|
17
20
|
.option('path', {
|
|
18
21
|
describe: 'Path of the folder where you want to put your exported file.',
|
|
19
22
|
});
|
|
20
23
|
}
|
|
21
24
|
export async function handler(args) {
|
|
22
|
-
const { clientCredentials, id: appId, path: outputDirectory, remote, resources } = args;
|
|
25
|
+
const { assets = false, clientCredentials, id: appId, path: outputDirectory, remote, resources = false, } = args;
|
|
23
26
|
const defaultOutputDirectory = join(process.cwd(), 'apps');
|
|
24
27
|
await authenticate(remote, 'apps:export', clientCredentials);
|
|
25
28
|
logger.info(`Exporting app with id: ${appId}`);
|
|
26
|
-
await exportAppAsZip(appId, resources, outputDirectory || defaultOutputDirectory, remote);
|
|
29
|
+
await exportAppAsZip(appId, assets, resources, outputDirectory || defaultOutputDirectory, remote);
|
|
27
30
|
logger.info('Export completed successfully');
|
|
28
31
|
}
|
|
29
32
|
//# sourceMappingURL=export.js.map
|
package/commands/app/publish.js
CHANGED
|
@@ -40,26 +40,25 @@ export function builder(yargs) {
|
|
|
40
40
|
.option('demo-mode', {
|
|
41
41
|
describe: 'Whether the app should be used in demo mode.',
|
|
42
42
|
type: 'boolean',
|
|
43
|
-
})
|
|
44
|
-
.option('seed', {
|
|
45
|
-
describe: 'Whether to enable seeding for an app or not.',
|
|
46
|
-
implies: ['demo-mode'],
|
|
47
43
|
})
|
|
48
44
|
.option('dry-run', {
|
|
49
45
|
describe: 'Whether the API should be called to run without actually creating the app.',
|
|
50
46
|
type: 'boolean',
|
|
51
47
|
})
|
|
52
48
|
.option('resources', {
|
|
53
|
-
describe: 'Whether the resources from the `resources` directory should be created after publishing the app. The names of
|
|
49
|
+
describe: 'Whether the resources from the `resources` directory should be created after publishing the app. The names of sub-directories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
|
|
54
50
|
type: 'boolean',
|
|
55
51
|
})
|
|
56
52
|
.option('assets', {
|
|
57
53
|
describe: 'Whether the assets from the `assets` directory should be created after publishing the app.',
|
|
58
54
|
type: 'boolean',
|
|
55
|
+
default: false,
|
|
59
56
|
})
|
|
60
57
|
.option('assets-clonable', {
|
|
61
58
|
describe: 'Whether published assets should be clonable. Ignored if assets equals false.',
|
|
62
59
|
type: 'boolean',
|
|
60
|
+
default: false,
|
|
61
|
+
implies: 'assets',
|
|
63
62
|
})
|
|
64
63
|
.option('modify-context', {
|
|
65
64
|
describe: 'If the app context is specified, modify it for the current context to include the id of the published app.',
|
package/commands/app/update.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ interface UpdateAppArguments extends BaseArguments {
|
|
|
13
13
|
id: number;
|
|
14
14
|
template: boolean;
|
|
15
15
|
demoMode: boolean;
|
|
16
|
-
|
|
16
|
+
resources: boolean;
|
|
17
|
+
assets: boolean;
|
|
18
|
+
assetsClonable: boolean;
|
|
17
19
|
force: boolean;
|
|
18
20
|
visibility: AppVisibility;
|
|
19
21
|
sentryDsn: string;
|
package/commands/app/update.js
CHANGED
|
@@ -44,9 +44,20 @@ export function builder(yargs) {
|
|
|
44
44
|
describe: 'Whether the app should be used in demo mode.',
|
|
45
45
|
type: 'boolean',
|
|
46
46
|
})
|
|
47
|
-
.option('
|
|
48
|
-
describe: 'Whether
|
|
49
|
-
|
|
47
|
+
.option('resources', {
|
|
48
|
+
describe: 'Whether the resources from the `resources` directory should replace the seed resources of the app being updated. The names of sub-directories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
})
|
|
51
|
+
.option('assets', {
|
|
52
|
+
describe: 'Whether the assets from the `assets` directory should replace the seed assets of the app being updated.',
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
default: false,
|
|
55
|
+
})
|
|
56
|
+
.option('assets-clonable', {
|
|
57
|
+
describe: 'Whether published assets should be clonable. Ignored if assets equals false.',
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
default: false,
|
|
60
|
+
implies: 'assets',
|
|
50
61
|
})
|
|
51
62
|
.option('force', {
|
|
52
63
|
describe: 'Whether the lock property should be ignored.',
|
|
@@ -8,9 +8,10 @@ interface PublishAssetArguments extends BaseArguments {
|
|
|
8
8
|
context: string;
|
|
9
9
|
app: string;
|
|
10
10
|
clonable: boolean;
|
|
11
|
+
seed: boolean;
|
|
11
12
|
}
|
|
12
13
|
export declare const command = "publish <paths...>";
|
|
13
14
|
export declare const description = "Publish assets for an existing app.";
|
|
14
15
|
export declare function builder(yargs: Argv): Argv<any>;
|
|
15
|
-
export declare function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, useFileName, }: PublishAssetArguments): Promise<void>;
|
|
16
|
+
export declare function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, seed, useFileName, }: PublishAssetArguments): Promise<void>;
|
|
16
17
|
export {};
|
|
@@ -35,9 +35,13 @@ export function builder(yargs) {
|
|
|
35
35
|
type: 'boolean',
|
|
36
36
|
default: false,
|
|
37
37
|
describe: 'If true, all published assets will be clonable',
|
|
38
|
+
})
|
|
39
|
+
.option('seed', {
|
|
40
|
+
describe: 'If true, published resources will be used as seed',
|
|
41
|
+
default: false,
|
|
38
42
|
});
|
|
39
43
|
}
|
|
40
|
-
export async function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, useFileName, }) {
|
|
44
|
+
export async function handler({ app, appId, clientCredentials, clonable, context, name, paths, remote, seed, useFileName, }) {
|
|
41
45
|
const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
|
|
42
46
|
await authenticate(resolvedRemote, 'assets:write', clientCredentials);
|
|
43
47
|
const normalizedPaths = paths.map((path) => normalizePath(path));
|
|
@@ -52,6 +56,7 @@ export async function handler({ app, appId, clientCredentials, clonable, context
|
|
|
52
56
|
name: name || (useFileName ? parse(path).name : null),
|
|
53
57
|
appId: resolvedAppId,
|
|
54
58
|
path,
|
|
59
|
+
seed,
|
|
55
60
|
remote: resolvedRemote,
|
|
56
61
|
clonable,
|
|
57
62
|
});
|
|
@@ -6,9 +6,10 @@ interface PublishResourceArguments extends BaseArguments {
|
|
|
6
6
|
appId: number;
|
|
7
7
|
context: string;
|
|
8
8
|
app: string;
|
|
9
|
+
seed: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare const command = "publish <resource-name> <paths...>";
|
|
11
12
|
export declare const description = "Publish resources based on a specified JSON file or directory.";
|
|
12
13
|
export declare function builder(yargs: Argv): Argv<any>;
|
|
13
|
-
export declare function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, }: PublishResourceArguments): Promise<void>;
|
|
14
|
+
export declare function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, seed, }: PublishResourceArguments): Promise<void>;
|
|
14
15
|
export {};
|
|
@@ -27,9 +27,13 @@ export function builder(yargs) {
|
|
|
27
27
|
.option('context', {
|
|
28
28
|
describe: 'If specified, use the specified context from .appsemblerc.yaml',
|
|
29
29
|
demandOption: 'app',
|
|
30
|
+
})
|
|
31
|
+
.option('seed', {
|
|
32
|
+
describe: 'If true, published resources will be used as seed',
|
|
33
|
+
default: false,
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
|
-
export async function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, }) {
|
|
36
|
+
export async function handler({ app, appId, clientCredentials, context, paths, remote, resourceName, seed, }) {
|
|
33
37
|
const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
|
|
34
38
|
await authenticate(resolvedRemote, 'resources:write', clientCredentials);
|
|
35
39
|
const normalizedPaths = paths.map((path) => normalizePath(path));
|
|
@@ -43,8 +47,9 @@ export async function handler({ app, appId, clientCredentials, context, paths, r
|
|
|
43
47
|
await publishResource({
|
|
44
48
|
type: resourceName,
|
|
45
49
|
appId: resolvedAppId,
|
|
46
|
-
path,
|
|
47
50
|
remote: resolvedRemote,
|
|
51
|
+
path,
|
|
52
|
+
seed,
|
|
48
53
|
});
|
|
49
54
|
}
|
|
50
55
|
}
|
package/commands/serve.js
CHANGED
|
@@ -120,7 +120,7 @@ export async function handler(argv) {
|
|
|
120
120
|
};
|
|
121
121
|
}));
|
|
122
122
|
const localBlocksPromises = localBlocksConfigs.map(async (blockConfig) => {
|
|
123
|
-
await buildBlock(blockConfig);
|
|
123
|
+
await buildBlock(blockConfig, 'development');
|
|
124
124
|
const [, blockImplementations] = await makeProjectPayload(blockConfig);
|
|
125
125
|
return {
|
|
126
126
|
...blockImplementations,
|
package/lib/app.d.ts
CHANGED
|
@@ -120,9 +120,17 @@ interface UpdateAppParams {
|
|
|
120
120
|
*/
|
|
121
121
|
demoMode: boolean;
|
|
122
122
|
/**
|
|
123
|
-
* Whether the
|
|
123
|
+
* Whether resources from the `resources` directory should be published after publishing the app.
|
|
124
124
|
*/
|
|
125
|
-
|
|
125
|
+
resources: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Whether assets from the `assets` directory should be published after publishing the app.
|
|
128
|
+
*/
|
|
129
|
+
assets: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Whether published assets should be clonable. Ignored if `assets` equals false.
|
|
132
|
+
*/
|
|
133
|
+
assetsClonable: boolean;
|
|
126
134
|
/**
|
|
127
135
|
* Whether the locked property should be ignored.
|
|
128
136
|
*/
|
|
@@ -165,11 +173,12 @@ export declare function traverseAppDirectory(path: string, context: string, form
|
|
|
165
173
|
* Export an app as a zip file.
|
|
166
174
|
*
|
|
167
175
|
* @param appId Id of the app to be exported.
|
|
176
|
+
* @param assets Boolean representing whether to include assets in the exported zip file.
|
|
168
177
|
* @param resources Boolean representing whether to include resources in the zip file.
|
|
169
178
|
* @param path Path of the folder where you want to put your downloaded file.
|
|
170
179
|
* @param remote The remote to fetch the app from.
|
|
171
180
|
*/
|
|
172
|
-
export declare function exportAppAsZip(appId: number, resources: boolean, path: string, remote: string): Promise<void>;
|
|
181
|
+
export declare function exportAppAsZip(appId: number, assets: boolean, resources: boolean, path: string, remote: string): Promise<void>;
|
|
173
182
|
/**
|
|
174
183
|
* Upload messages for an app.
|
|
175
184
|
*
|
|
@@ -179,6 +188,8 @@ export declare function exportAppAsZip(appId: number, resources: boolean, path:
|
|
|
179
188
|
* @param force Whether or not to force the update for locked apps.
|
|
180
189
|
*/
|
|
181
190
|
export declare function uploadMessages(path: string, appId: number, remote: string, force: boolean): Promise<void>;
|
|
191
|
+
export declare function publishSeedResources(path: string, app: App, remote: string): Promise<void>;
|
|
192
|
+
export declare function publishSeedAssets(path: string, app: App, remote: string, assetsClonable: boolean): Promise<void>;
|
|
182
193
|
/**
|
|
183
194
|
* @param path The path to the app directory.
|
|
184
195
|
* @param languages A list of languages for which translations should be added in addition to the
|
|
@@ -192,13 +203,13 @@ export declare function writeAppMessages(path: string, languages: string[], veri
|
|
|
192
203
|
*
|
|
193
204
|
* @param argv The command line options used for updating the app.
|
|
194
205
|
*/
|
|
195
|
-
export declare function updateApp({ clientCredentials, context, force, path, ...options }: UpdateAppParams): Promise<void>;
|
|
206
|
+
export declare function updateApp({ assets, clientCredentials, context, force, path, resources, ...options }: UpdateAppParams): Promise<void>;
|
|
196
207
|
/**
|
|
197
208
|
* Publish a new app.
|
|
198
209
|
*
|
|
199
210
|
* @param options The options to use for publishing an app.
|
|
200
211
|
*/
|
|
201
|
-
export declare function publishApp({ assets,
|
|
212
|
+
export declare function publishApp({ assets, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }: PublishAppParams): Promise<void>;
|
|
202
213
|
/**
|
|
203
214
|
* Resolve the app’s ID and remote based on the app context.
|
|
204
215
|
*
|
package/lib/app.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { createReadStream, createWriteStream, existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readdir, readFile, stat } from 'node:fs/promises';
|
|
3
|
-
import { basename, join, parse, relative, resolve } from 'node:path';
|
|
2
|
+
import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, join, parse, relative, resolve } from 'node:path';
|
|
4
4
|
import { inspect } from 'node:util';
|
|
5
|
-
import { AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
|
|
5
|
+
import { applyAppVariant, AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
|
|
6
6
|
import { extractAppMessages, has, normalizeBlockName } from '@appsemble/utils';
|
|
7
7
|
import axios from 'axios';
|
|
8
8
|
import fg from 'fast-glob';
|
|
9
9
|
import FormData from 'form-data';
|
|
10
10
|
import normalizePath from 'normalize-path';
|
|
11
|
-
import { parse as parseYaml } from 'yaml';
|
|
12
11
|
import { publishAsset } from './asset.js';
|
|
13
12
|
import { traverseBlockThemes } from './block.js';
|
|
14
13
|
import { coerceRemote } from './coercers.js';
|
|
@@ -131,14 +130,15 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
131
130
|
* Export an app as a zip file.
|
|
132
131
|
*
|
|
133
132
|
* @param appId Id of the app to be exported.
|
|
133
|
+
* @param assets Boolean representing whether to include assets in the exported zip file.
|
|
134
134
|
* @param resources Boolean representing whether to include resources in the zip file.
|
|
135
135
|
* @param path Path of the folder where you want to put your downloaded file.
|
|
136
136
|
* @param remote The remote to fetch the app from.
|
|
137
137
|
*/
|
|
138
|
-
export async function exportAppAsZip(appId, resources, path, remote) {
|
|
138
|
+
export async function exportAppAsZip(appId, assets, resources, path, remote) {
|
|
139
139
|
const app = await axios.get(`/api/apps/${appId}`);
|
|
140
140
|
const { name } = app.data.definition;
|
|
141
|
-
const response = await axios.get(`/api/apps/${appId}/export?resources=${resources}`, {
|
|
141
|
+
const response = await axios.get(`/api/apps/${appId}/export?resources=${resources}&assets=${assets}`, {
|
|
142
142
|
baseURL: remote,
|
|
143
143
|
responseType: 'stream',
|
|
144
144
|
});
|
|
@@ -175,6 +175,88 @@ export async function uploadMessages(path, appId, remote, force) {
|
|
|
175
175
|
logger.info(`Successfully uploaded messages for language “${language.language}” 🎉`);
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
+
export async function publishSeedResources(path, app, remote) {
|
|
179
|
+
var _a, _b;
|
|
180
|
+
const resourcesPath = join(path, 'resources');
|
|
181
|
+
logger.info(`Publishing seed resources from ${resourcesPath}`);
|
|
182
|
+
if (existsSync(resourcesPath)) {
|
|
183
|
+
logger.info(`Deleting existing seed resources from app ${app.id}`);
|
|
184
|
+
await axios.delete(`/api/apps/${app.id}/seed-resources`);
|
|
185
|
+
try {
|
|
186
|
+
const resourceFiles = await readdir(resourcesPath, { withFileTypes: true });
|
|
187
|
+
const resourcesToPublish = [];
|
|
188
|
+
const publishedResourcesIds = {};
|
|
189
|
+
for (const resource of resourceFiles) {
|
|
190
|
+
if (resource.isFile()) {
|
|
191
|
+
const { name } = parse(resource.name);
|
|
192
|
+
resourcesToPublish.push({
|
|
193
|
+
appId: app.id,
|
|
194
|
+
path: join(resourcesPath, resource.name),
|
|
195
|
+
definition: (_a = app.definition.resources) === null || _a === void 0 ? void 0 : _a[name],
|
|
196
|
+
type: name,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else if (resource.isDirectory()) {
|
|
200
|
+
const subDirectoryResources = await readdir(join(resourcesPath, resource.name), {
|
|
201
|
+
withFileTypes: true,
|
|
202
|
+
});
|
|
203
|
+
for (const subResource of subDirectoryResources.filter((s) => s.isFile())) {
|
|
204
|
+
resourcesToPublish.push({
|
|
205
|
+
appId: app.id,
|
|
206
|
+
path: join(resourcesPath, resource.name, subResource.name),
|
|
207
|
+
definition: (_b = app.definition.resources) === null || _b === void 0 ? void 0 : _b[resource.name],
|
|
208
|
+
type: resource.name,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
await publishResourcesRecursively({
|
|
214
|
+
seed: true,
|
|
215
|
+
remote,
|
|
216
|
+
resourcesToPublish,
|
|
217
|
+
publishedResourcesIds,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
logger.error('Something went wrong when publishing seed resources:');
|
|
222
|
+
logger.error(error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
logger.warn(`Missing resources directory in ${path}. Skipping...`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
export async function publishSeedAssets(path, app, remote, assetsClonable) {
|
|
230
|
+
const assetsPath = join(path, 'assets');
|
|
231
|
+
logger.info(`Publishing seed assets from ${assetsPath}`);
|
|
232
|
+
if (existsSync(assetsPath)) {
|
|
233
|
+
logger.info(`Deleting existing seed assets from app ${app.id}`);
|
|
234
|
+
await axios.delete(`/api/apps/${app.id}/seed-assets`);
|
|
235
|
+
const assetFiles = await readdir(assetsPath);
|
|
236
|
+
const normalizedPaths = assetFiles.map((assetFile) => normalizePath(join(assetsPath, assetFile)));
|
|
237
|
+
const files = await fg(normalizedPaths, { absolute: true, onlyFiles: true });
|
|
238
|
+
try {
|
|
239
|
+
logger.info(`Publishing ${files.length} asset(s)`);
|
|
240
|
+
for (const assetFilePath of files) {
|
|
241
|
+
await publishAsset({
|
|
242
|
+
name: parse(assetFilePath).name,
|
|
243
|
+
appId: app.id,
|
|
244
|
+
path: assetFilePath,
|
|
245
|
+
remote,
|
|
246
|
+
seed: true,
|
|
247
|
+
clonable: assetsClonable,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
logger.error('Something went wrong when creating assets:');
|
|
253
|
+
logger.error(error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
logger.warn(`Missing assets directory in ${path}. Skipping...`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
178
260
|
/**
|
|
179
261
|
* @param path The path to the app directory.
|
|
180
262
|
* @param languages A list of languages for which translations should be added in addition to the
|
|
@@ -330,32 +412,38 @@ This block version is not used in the app`);
|
|
|
330
412
|
*
|
|
331
413
|
* @param argv The command line options used for updating the app.
|
|
332
414
|
*/
|
|
333
|
-
export async function updateApp({ clientCredentials, context, force, path, ...options }) {
|
|
334
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u
|
|
415
|
+
export async function updateApp({ assets, clientCredentials, context, force, path, resources, ...options }) {
|
|
416
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
335
417
|
const file = await stat(path);
|
|
336
418
|
const formData = new FormData();
|
|
337
419
|
let appsembleContext;
|
|
420
|
+
let appVariantPath = path;
|
|
421
|
+
if (existsSync(join(path, 'variants', context))) {
|
|
422
|
+
await applyAppVariant(path, context);
|
|
423
|
+
appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
logger.warn(`App variant ${context} is not defined. Using default app.`);
|
|
427
|
+
}
|
|
338
428
|
if (file.isFile()) {
|
|
339
|
-
const [, data] = await readData(
|
|
429
|
+
const [, data] = await readData(appVariantPath);
|
|
340
430
|
formData.append('yaml', data);
|
|
341
431
|
}
|
|
342
432
|
else {
|
|
343
|
-
[appsembleContext] = await traverseAppDirectory(
|
|
433
|
+
[appsembleContext] = await traverseAppDirectory(appVariantPath, context, formData);
|
|
344
434
|
}
|
|
345
435
|
const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
|
|
346
436
|
const id = (_b = appsembleContext.id) !== null && _b !== void 0 ? _b : options.id;
|
|
347
437
|
const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
const sentryEnvironment = (_q = appsembleContext.sentryEnvironment) !== null && _q !== void 0 ? _q : options.sentryEnvironment;
|
|
358
|
-
const googleAnalyticsId = (_r = appsembleContext.googleAnalyticsId) !== null && _r !== void 0 ? _r : options.googleAnalyticsId;
|
|
438
|
+
const assetsClonable = (_f = (_e = appsembleContext.assetsClonable) !== null && _e !== void 0 ? _e : options.assetsClonable) !== null && _f !== void 0 ? _f : false;
|
|
439
|
+
const demoMode = (_h = (_g = appsembleContext.demoMode) !== null && _g !== void 0 ? _g : options.demoMode) !== null && _h !== void 0 ? _h : false;
|
|
440
|
+
const visibility = (_j = appsembleContext.visibility) !== null && _j !== void 0 ? _j : options.visibility;
|
|
441
|
+
const iconBackground = (_k = appsembleContext.iconBackground) !== null && _k !== void 0 ? _k : options.iconBackground;
|
|
442
|
+
const icon = (_l = options.icon) !== null && _l !== void 0 ? _l : appsembleContext.icon;
|
|
443
|
+
const maskableIcon = (_m = options.maskableIcon) !== null && _m !== void 0 ? _m : appsembleContext.maskableIcon;
|
|
444
|
+
const sentryDsn = (_o = appsembleContext.sentryDsn) !== null && _o !== void 0 ? _o : options.sentryDsn;
|
|
445
|
+
const sentryEnvironment = (_p = appsembleContext.sentryEnvironment) !== null && _p !== void 0 ? _p : options.sentryEnvironment;
|
|
446
|
+
const googleAnalyticsId = (_q = appsembleContext.googleAnalyticsId) !== null && _q !== void 0 ? _q : options.googleAnalyticsId;
|
|
359
447
|
const { appLock } = appsembleContext;
|
|
360
448
|
logger.info(`App id: ${id}`);
|
|
361
449
|
logger.verbose(`App remote: ${remote}`);
|
|
@@ -369,17 +457,16 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
|
|
|
369
457
|
formData.append('force', String(force));
|
|
370
458
|
formData.append('template', String(template));
|
|
371
459
|
formData.append('demoMode', String(demoMode));
|
|
372
|
-
formData.append('seed', String(seed));
|
|
373
460
|
formData.append('visibility', visibility);
|
|
374
461
|
formData.append('iconBackground', iconBackground);
|
|
375
462
|
if (icon) {
|
|
376
463
|
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
377
|
-
logger.info(`Using icon from ${(
|
|
464
|
+
logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
|
|
378
465
|
formData.append('icon', realIcon);
|
|
379
466
|
}
|
|
380
467
|
if (maskableIcon) {
|
|
381
468
|
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
382
|
-
logger.info(`Using maskable icon from ${(
|
|
469
|
+
logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
|
|
383
470
|
formData.append('maskableIcon', realIcon);
|
|
384
471
|
}
|
|
385
472
|
if (sentryDsn) {
|
|
@@ -393,7 +480,11 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
|
|
|
393
480
|
logger.info('Using Google Analytics');
|
|
394
481
|
formData.append('googleAnalyticsID', googleAnalyticsId);
|
|
395
482
|
}
|
|
396
|
-
|
|
483
|
+
let authScope = 'apps:write';
|
|
484
|
+
if (resources) {
|
|
485
|
+
authScope += ' resources:write';
|
|
486
|
+
}
|
|
487
|
+
await authenticate(remote, authScope, clientCredentials);
|
|
397
488
|
if (appLock) {
|
|
398
489
|
logger.info(`Setting AppLock to ${appLock}`);
|
|
399
490
|
try {
|
|
@@ -416,7 +507,7 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
|
|
|
416
507
|
if (!axios.isAxiosError(error)) {
|
|
417
508
|
throw error;
|
|
418
509
|
}
|
|
419
|
-
if (((
|
|
510
|
+
if (((_u = (_t = error.response) === null || _t === void 0 ? void 0 : _t.data) === null || _u === void 0 ? void 0 : _u.message) !== 'App validation failed') {
|
|
420
511
|
throw error;
|
|
421
512
|
}
|
|
422
513
|
logger.error(error.response.data);
|
|
@@ -424,51 +515,68 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
|
|
|
424
515
|
}
|
|
425
516
|
if (file.isDirectory()) {
|
|
426
517
|
// After uploading the app, upload block styles and messages if they are available
|
|
427
|
-
await traverseBlockThemes(
|
|
428
|
-
await uploadMessages(
|
|
518
|
+
await traverseBlockThemes(appVariantPath, data.id, remote, force);
|
|
519
|
+
await uploadMessages(appVariantPath, data.id, remote, force);
|
|
520
|
+
// After updating the app, publish seed resources and assets if they are available
|
|
521
|
+
if (assets && (data.locked !== 'fullLock' || force)) {
|
|
522
|
+
await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
|
|
523
|
+
}
|
|
524
|
+
if (resources && (data.locked !== 'fullLock' || force)) {
|
|
525
|
+
await publishSeedResources(appVariantPath, data, remote);
|
|
526
|
+
}
|
|
429
527
|
}
|
|
430
528
|
const { host, protocol } = new URL(remote);
|
|
431
529
|
logger.info(`Successfully updated app ${data.definition.name}! 🙌`);
|
|
432
530
|
logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
|
|
433
531
|
logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
|
|
532
|
+
if (appVariantPath !== path) {
|
|
533
|
+
await rm(appVariantPath, { recursive: true });
|
|
534
|
+
logger.info(`Removed ${appVariantPath}`);
|
|
535
|
+
}
|
|
434
536
|
}
|
|
435
537
|
/**
|
|
436
538
|
* Publish a new app.
|
|
437
539
|
*
|
|
438
540
|
* @param options The options to use for publishing an app.
|
|
439
541
|
*/
|
|
440
|
-
export async function publishApp({ assets,
|
|
441
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u
|
|
542
|
+
export async function publishApp({ assets, clientCredentials, context, dryRun, modifyContext, path, resources, ...options }) {
|
|
543
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
442
544
|
const file = await stat(path);
|
|
443
545
|
const formData = new FormData();
|
|
444
546
|
let appsembleContext;
|
|
445
547
|
let rc;
|
|
446
548
|
let yaml;
|
|
447
549
|
let filename = relative(process.cwd(), path);
|
|
550
|
+
let appVariantPath = path;
|
|
551
|
+
if (existsSync(join(path, 'variants', context))) {
|
|
552
|
+
await applyAppVariant(path, context);
|
|
553
|
+
appVariantPath = join(dirname(path), `${basename(path)}-${context}`);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
logger.warn(`App variant ${context} is not defined. Using default app.`);
|
|
557
|
+
}
|
|
448
558
|
if (file.isFile()) {
|
|
449
559
|
// Assuming file is App YAML
|
|
450
|
-
const [, data] = await readData(
|
|
560
|
+
const [, data] = await readData(appVariantPath);
|
|
451
561
|
yaml = data;
|
|
452
562
|
formData.append('yaml', data);
|
|
453
563
|
}
|
|
454
564
|
else {
|
|
455
|
-
[appsembleContext, rc, yaml] = await traverseAppDirectory(
|
|
565
|
+
[appsembleContext, rc, yaml] = await traverseAppDirectory(appVariantPath, context, formData);
|
|
456
566
|
filename = join(filename, 'app-definition.yaml');
|
|
457
567
|
}
|
|
458
568
|
const remote = (_a = appsembleContext.remote) !== null && _a !== void 0 ? _a : options.remote;
|
|
459
569
|
const organizationId = (_b = appsembleContext.organization) !== null && _b !== void 0 ? _b : options.organization;
|
|
460
570
|
const template = (_d = (_c = appsembleContext.template) !== null && _c !== void 0 ? _c : options.template) !== null && _d !== void 0 ? _d : false;
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
const
|
|
469
|
-
const
|
|
470
|
-
const sentryEnvironment = (_q = appsembleContext.sentryEnvironment) !== null && _q !== void 0 ? _q : options.sentryEnvironment;
|
|
471
|
-
const googleAnalyticsId = (_r = appsembleContext.googleAnalyticsId) !== null && _r !== void 0 ? _r : options.googleAnalyticsId;
|
|
571
|
+
const assetsClonable = (_f = (_e = appsembleContext.assetsClonable) !== null && _e !== void 0 ? _e : options.assetsClonable) !== null && _f !== void 0 ? _f : false;
|
|
572
|
+
const demoMode = (_h = (_g = appsembleContext.demoMode) !== null && _g !== void 0 ? _g : options.demoMode) !== null && _h !== void 0 ? _h : false;
|
|
573
|
+
const visibility = (_j = appsembleContext.visibility) !== null && _j !== void 0 ? _j : options.visibility;
|
|
574
|
+
const iconBackground = (_k = appsembleContext.iconBackground) !== null && _k !== void 0 ? _k : options.iconBackground;
|
|
575
|
+
const icon = (_l = options.icon) !== null && _l !== void 0 ? _l : appsembleContext.icon;
|
|
576
|
+
const maskableIcon = (_m = options.maskableIcon) !== null && _m !== void 0 ? _m : appsembleContext.maskableIcon;
|
|
577
|
+
const sentryDsn = (_o = appsembleContext.sentryDsn) !== null && _o !== void 0 ? _o : options.sentryDsn;
|
|
578
|
+
const sentryEnvironment = (_p = appsembleContext.sentryEnvironment) !== null && _p !== void 0 ? _p : options.sentryEnvironment;
|
|
579
|
+
const googleAnalyticsId = (_q = appsembleContext.googleAnalyticsId) !== null && _q !== void 0 ? _q : options.googleAnalyticsId;
|
|
472
580
|
const appLock = appsembleContext.appLock || 'unlocked';
|
|
473
581
|
logger.verbose(`App remote: ${remote}`);
|
|
474
582
|
logger.verbose(`App organization: ${organizationId}`);
|
|
@@ -481,18 +589,17 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
|
|
|
481
589
|
formData.append('OrganizationId', organizationId);
|
|
482
590
|
formData.append('template', String(template));
|
|
483
591
|
formData.append('demoMode', String(demoMode));
|
|
484
|
-
formData.append('seed', String(seed));
|
|
485
592
|
formData.append('visibility', visibility);
|
|
486
593
|
formData.append('iconBackground', iconBackground);
|
|
487
594
|
formData.append('locked', appLock);
|
|
488
595
|
if (icon) {
|
|
489
596
|
const realIcon = typeof icon === 'string' ? createReadStream(icon) : icon;
|
|
490
|
-
logger.info(`Using icon from ${(
|
|
597
|
+
logger.info(`Using icon from ${(_r = realIcon.path) !== null && _r !== void 0 ? _r : 'stdin'}`);
|
|
491
598
|
formData.append('icon', realIcon);
|
|
492
599
|
}
|
|
493
600
|
if (maskableIcon) {
|
|
494
601
|
const realIcon = typeof maskableIcon === 'string' ? createReadStream(maskableIcon) : maskableIcon;
|
|
495
|
-
logger.info(`Using maskable icon from ${(
|
|
602
|
+
logger.info(`Using maskable icon from ${(_s = realIcon.path) !== null && _s !== void 0 ? _s : 'stdin'}`);
|
|
496
603
|
formData.append('maskableIcon', realIcon);
|
|
497
604
|
}
|
|
498
605
|
if (sentryDsn) {
|
|
@@ -522,7 +629,7 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
|
|
|
522
629
|
if (!axios.isAxiosError(error)) {
|
|
523
630
|
throw error;
|
|
524
631
|
}
|
|
525
|
-
if (((
|
|
632
|
+
if (((_u = (_t = error.response) === null || _t === void 0 ? void 0 : _t.data) === null || _u === void 0 ? void 0 : _u.message) !== 'App validation failed') {
|
|
526
633
|
throw error;
|
|
527
634
|
}
|
|
528
635
|
throw new AppsembleError(printAxiosError(filename, yaml, error.response.data.data.errors));
|
|
@@ -533,83 +640,42 @@ export async function publishApp({ assets, assetsClonable, clientCredentials, co
|
|
|
533
640
|
return;
|
|
534
641
|
}
|
|
535
642
|
if (file.isDirectory() && !dryRun) {
|
|
536
|
-
// After uploading the app, upload block styles
|
|
537
|
-
await traverseBlockThemes(
|
|
538
|
-
await uploadMessages(
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const normalizedPaths = assetFiles.map((assetFile) => normalizePath(join(assetsPath, assetFile)));
|
|
543
|
-
const files = await fg(normalizedPaths, { absolute: true, onlyFiles: true });
|
|
544
|
-
try {
|
|
545
|
-
logger.info(`Publishing ${files.length} asset(s)`);
|
|
546
|
-
for (const assetFilePath of files) {
|
|
547
|
-
await publishAsset({
|
|
548
|
-
name: parse(assetFilePath).name,
|
|
549
|
-
appId: data.id,
|
|
550
|
-
path: assetFilePath,
|
|
551
|
-
remote,
|
|
552
|
-
clonable: assetsClonable,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
catch (error) {
|
|
557
|
-
logger.error('Something went wrong when creating assets:');
|
|
558
|
-
logger.error(error);
|
|
559
|
-
}
|
|
643
|
+
// After uploading the app, upload block styles, messages if they are available
|
|
644
|
+
await traverseBlockThemes(appVariantPath, data.id, remote, false);
|
|
645
|
+
await uploadMessages(appVariantPath, data.id, remote, false);
|
|
646
|
+
// After uploading the app, publish seed resources and assets if they are available
|
|
647
|
+
if (assets) {
|
|
648
|
+
await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
|
|
560
649
|
}
|
|
561
|
-
if (resources
|
|
562
|
-
|
|
563
|
-
const resourcePath = join(path, 'resources');
|
|
564
|
-
try {
|
|
565
|
-
const resourceFiles = await readdir(resourcePath, { withFileTypes: true });
|
|
566
|
-
const resourcesToPublish = [];
|
|
567
|
-
const publishedResourcesIds = {};
|
|
568
|
-
for (const resource of resourceFiles) {
|
|
569
|
-
if (resource.isFile()) {
|
|
570
|
-
const { name } = parse(resource.name);
|
|
571
|
-
resourcesToPublish.push({
|
|
572
|
-
appId: data.id,
|
|
573
|
-
path: join(resourcePath, resource.name),
|
|
574
|
-
definition: appDefinition.resources[name],
|
|
575
|
-
type: name,
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
else if (resource.isDirectory()) {
|
|
579
|
-
const subDirectoryResources = await readdir(join(resourcePath, resource.name), {
|
|
580
|
-
withFileTypes: true,
|
|
581
|
-
});
|
|
582
|
-
for (const subResource of subDirectoryResources.filter((s) => s.isFile())) {
|
|
583
|
-
resourcesToPublish.push({
|
|
584
|
-
appId: data.id,
|
|
585
|
-
path: join(resourcePath, resource.name, subResource.name),
|
|
586
|
-
definition: appDefinition.resources[resource.name],
|
|
587
|
-
type: resource.name,
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
await publishResourcesRecursively({
|
|
593
|
-
remote,
|
|
594
|
-
resourcesToPublish,
|
|
595
|
-
publishedResourcesIds,
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
catch (error) {
|
|
599
|
-
logger.error('Something went wrong when creating resources:');
|
|
600
|
-
logger.error(error);
|
|
601
|
-
}
|
|
650
|
+
if (resources) {
|
|
651
|
+
await publishSeedResources(appVariantPath, data, remote);
|
|
602
652
|
}
|
|
603
653
|
}
|
|
604
654
|
if (modifyContext && appsembleContext && context && !dryRun) {
|
|
605
655
|
rc.context[context].id = data.id;
|
|
606
|
-
await writeData(join(
|
|
656
|
+
await writeData(join(appVariantPath, '.appsemblerc.yaml'), rc, { sort: false });
|
|
607
657
|
logger.info(`Updated .appsemblerc: Set context.${context}.id to ${data.id}`);
|
|
608
658
|
}
|
|
609
659
|
const { host, protocol } = new URL(remote);
|
|
610
660
|
logger.info(`Successfully published app ${data.definition.name}! 🙌`);
|
|
611
661
|
logger.info(`App URL: ${protocol}//${data.path}.${data.OrganizationId}.${host}`);
|
|
612
662
|
logger.info(`App store page: ${new URL(`/apps/${data.id}`, remote)}`);
|
|
663
|
+
const rcCollections = rc.context[context].collections;
|
|
664
|
+
if (Array.isArray(rcCollections)) {
|
|
665
|
+
for (const collectionId of rcCollections) {
|
|
666
|
+
await axios.post(`/api/appCollections/${collectionId}/apps`, { AppId: data.id }, {
|
|
667
|
+
baseURL: remote,
|
|
668
|
+
});
|
|
669
|
+
logger.info(`Successfully added app to collection ${collectionId}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (appVariantPath !== path) {
|
|
673
|
+
const [defaultAppRc] = await readData(join(path, '.appsemblerc.yaml'));
|
|
674
|
+
defaultAppRc.context[context] = rc.context[context];
|
|
675
|
+
await writeData(join(path, '.appsemblerc.yaml'), defaultAppRc, { sort: false });
|
|
676
|
+
await rm(appVariantPath, { recursive: true });
|
|
677
|
+
logger.info(`Removed ${appVariantPath}`);
|
|
678
|
+
}
|
|
613
679
|
}
|
|
614
680
|
/**
|
|
615
681
|
* Resolve the app’s ID and remote based on the app context.
|
package/lib/asset.d.ts
CHANGED
|
@@ -16,10 +16,14 @@ interface PublishAssetParams {
|
|
|
16
16
|
* The remote server to publish the asset on.
|
|
17
17
|
*/
|
|
18
18
|
remote: string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the published asset should be used as seed.
|
|
21
|
+
*/
|
|
22
|
+
seed: boolean;
|
|
19
23
|
/**
|
|
20
24
|
* Whether the asset should be clonable.
|
|
21
25
|
*/
|
|
22
26
|
clonable: boolean;
|
|
23
27
|
}
|
|
24
|
-
export declare function publishAsset({ appId, clonable, name, path, remote, }: PublishAssetParams): Promise<void>;
|
|
28
|
+
export declare function publishAsset({ appId, clonable, name, path, remote, seed, }: PublishAssetParams): Promise<void>;
|
|
25
29
|
export {};
|
package/lib/asset.js
CHANGED
|
@@ -3,7 +3,7 @@ import { logger } from '@appsemble/node-utils';
|
|
|
3
3
|
import { normalize } from '@appsemble/utils';
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
import FormData from 'form-data';
|
|
6
|
-
export async function publishAsset({ appId, clonable, name, path, remote, }) {
|
|
6
|
+
export async function publishAsset({ appId, clonable, name, path, remote, seed, }) {
|
|
7
7
|
const formData = new FormData();
|
|
8
8
|
const file = createReadStream(path);
|
|
9
9
|
formData.append('file', file);
|
|
@@ -11,7 +11,8 @@ export async function publishAsset({ appId, clonable, name, path, remote, }) {
|
|
|
11
11
|
if (name) {
|
|
12
12
|
formData.append('name', normalize(name));
|
|
13
13
|
}
|
|
14
|
-
const
|
|
14
|
+
const endpoint = `/api/apps/${appId}/${seed ? 'seed-' : ''}assets`;
|
|
15
|
+
const { data: { id }, } = await axios.post(endpoint, formData, { baseURL: remote });
|
|
15
16
|
logger.info(`Published asset ${id}${name ? ` with name ${normalize(name)}` : ''}`);
|
|
16
17
|
}
|
|
17
18
|
//# sourceMappingURL=asset.js.map
|
package/lib/block.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { type Stats } from 'webpack';
|
|
|
12
12
|
* @param buildConfig The config of the block to build.
|
|
13
13
|
* @returns The Webpack stats object.
|
|
14
14
|
*/
|
|
15
|
-
export declare function buildBlock(buildConfig: ProjectBuildConfig): Promise<Stats>;
|
|
15
|
+
export declare function buildBlock(buildConfig: ProjectBuildConfig, env?: 'development' | 'production'): Promise<Stats>;
|
|
16
16
|
interface DeleteBlockVersionParams {
|
|
17
17
|
organization: string;
|
|
18
18
|
blockName: string;
|
package/lib/block.js
CHANGED
|
@@ -19,8 +19,8 @@ import { processCss } from './processCss.js';
|
|
|
19
19
|
* @param buildConfig The config of the block to build.
|
|
20
20
|
* @returns The Webpack stats object.
|
|
21
21
|
*/
|
|
22
|
-
export async function buildBlock(buildConfig) {
|
|
23
|
-
const conf = await getProjectWebpackConfig(buildConfig, 'production', join(buildConfig.dir, buildConfig.output));
|
|
22
|
+
export async function buildBlock(buildConfig, env) {
|
|
23
|
+
const conf = await getProjectWebpackConfig(buildConfig, env || 'production', join(buildConfig.dir, buildConfig.output));
|
|
24
24
|
if (existsSync(conf.output.path)) {
|
|
25
25
|
logger.warn(`Removing ${conf.output.path}`);
|
|
26
26
|
await rm(conf.output.path, { force: true, recursive: true });
|
|
@@ -28,7 +28,7 @@ export async function buildBlock(buildConfig) {
|
|
|
28
28
|
logger.info(`Building ${buildConfig.name}@${buildConfig.version} 🔨`);
|
|
29
29
|
const compiler = webpack(conf);
|
|
30
30
|
return new Promise((resolve, reject) => {
|
|
31
|
-
|
|
31
|
+
const callback = (err, stats) => {
|
|
32
32
|
if (err) {
|
|
33
33
|
reject(err);
|
|
34
34
|
}
|
|
@@ -39,7 +39,13 @@ export async function buildBlock(buildConfig) {
|
|
|
39
39
|
logger.verbose(stats.toString({ colors: true }));
|
|
40
40
|
resolve(stats);
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
};
|
|
43
|
+
if (env === 'development') {
|
|
44
|
+
compiler.watch({ ignored: /node_modules/ }, callback);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
compiler.run(callback);
|
|
48
|
+
}
|
|
43
49
|
});
|
|
44
50
|
}
|
|
45
51
|
export async function deleteBlock({ blockName, blockVersion, organization, remote, }) {
|
package/lib/resource.d.ts
CHANGED
|
@@ -34,6 +34,10 @@ interface PublishResourceParams {
|
|
|
34
34
|
* The remote server to publish the resource on.
|
|
35
35
|
*/
|
|
36
36
|
remote: string;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the published resource should be used as seed.
|
|
39
|
+
*/
|
|
40
|
+
seed: boolean;
|
|
37
41
|
/**
|
|
38
42
|
* The definition of the resource.
|
|
39
43
|
*/
|
|
@@ -54,6 +58,10 @@ interface PublishResourcesRecursivelyParams {
|
|
|
54
58
|
* The remote server to publish the resource on.
|
|
55
59
|
*/
|
|
56
60
|
remote: string;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the published resources should be used as seed.
|
|
63
|
+
*/
|
|
64
|
+
seed: boolean;
|
|
57
65
|
/**
|
|
58
66
|
* All resources that need to be published.
|
|
59
67
|
*/
|
|
@@ -63,7 +71,7 @@ interface PublishResourcesRecursivelyParams {
|
|
|
63
71
|
*/
|
|
64
72
|
publishedResourcesIds: Record<string, number[]>;
|
|
65
73
|
}
|
|
66
|
-
export declare function publishResource({ appId, definition, path, publishedResourcesIds, remote, type, }: PublishResourceParams): Promise<number[]>;
|
|
67
|
-
export declare function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, }: PublishResourcesRecursivelyParams): Promise<Record<string, number[]>>;
|
|
74
|
+
export declare function publishResource({ appId, definition, path, publishedResourcesIds, remote, seed, type, }: PublishResourceParams): Promise<number[]>;
|
|
75
|
+
export declare function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, seed, }: PublishResourcesRecursivelyParams): Promise<Record<string, number[]>>;
|
|
68
76
|
export declare function updateResource({ appId, path, remote, resourceName, }: UpdateResourceParams): Promise<void>;
|
|
69
77
|
export {};
|
package/lib/resource.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { AppsembleError, logger, readData } from '@appsemble/node-utils';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
-
export async function publishResource({ appId, definition, path, publishedResourcesIds = {}, remote, type, }) {
|
|
4
|
+
export async function publishResource({ appId, definition, path, publishedResourcesIds = {}, remote, seed, type, }) {
|
|
5
5
|
const csv = path.endsWith('.csv');
|
|
6
6
|
let resources;
|
|
7
7
|
if (csv) {
|
|
@@ -16,15 +16,16 @@ export async function publishResource({ appId, definition, path, publishedResour
|
|
|
16
16
|
}
|
|
17
17
|
logger.info(`Publishing resource(s) from ${path}`);
|
|
18
18
|
let data;
|
|
19
|
+
const endpoint = `/api/apps/${appId}/${seed ? 'seed-' : ''}resources/${type}`;
|
|
19
20
|
if (csv) {
|
|
20
|
-
const response = await axios.post(
|
|
21
|
+
const response = await axios.post(endpoint, resources, {
|
|
21
22
|
baseURL: remote,
|
|
22
23
|
headers: { 'content-type': 'text/csv' },
|
|
23
24
|
});
|
|
24
25
|
data = response.data;
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
27
|
-
const response = await axios.post(
|
|
28
|
+
const response = await axios.post(endpoint, resources.map((resource) => {
|
|
28
29
|
var _a, _b, _c;
|
|
29
30
|
const resourceWithReferences = { ...resource };
|
|
30
31
|
for (const [referencedProperty, resourceReference] of Object.entries((_a = definition === null || definition === void 0 ? void 0 : definition.references) !== null && _a !== void 0 ? _a : {})) {
|
|
@@ -45,7 +46,7 @@ export async function publishResource({ appId, definition, path, publishedResour
|
|
|
45
46
|
.join('\n')}`);
|
|
46
47
|
return ids;
|
|
47
48
|
}
|
|
48
|
-
export async function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, }) {
|
|
49
|
+
export async function publishResourcesRecursively({ publishedResourcesIds, remote, resourcesToPublish, seed, }) {
|
|
49
50
|
let updatedPublishedResourcesIds = { ...publishedResourcesIds };
|
|
50
51
|
for (const resourceToPublish of resourcesToPublish) {
|
|
51
52
|
const resourceReferences = resourceToPublish.definition.references;
|
|
@@ -56,9 +57,10 @@ export async function publishResourcesRecursively({ publishedResourcesIds, remot
|
|
|
56
57
|
if (referencedResourcesToPublish.length &&
|
|
57
58
|
!updatedPublishedResourcesIds[referencedResourceType]) {
|
|
58
59
|
const referencedPublishedResourcesIds = await publishResourcesRecursively({
|
|
59
|
-
remote,
|
|
60
60
|
resourcesToPublish: referencedResourcesToPublish,
|
|
61
61
|
publishedResourcesIds: updatedPublishedResourcesIds,
|
|
62
|
+
remote,
|
|
63
|
+
seed,
|
|
62
64
|
});
|
|
63
65
|
updatedPublishedResourcesIds = {
|
|
64
66
|
...updatedPublishedResourcesIds,
|
|
@@ -72,6 +74,7 @@ export async function publishResourcesRecursively({ publishedResourcesIds, remot
|
|
|
72
74
|
...resourceToPublish,
|
|
73
75
|
publishedResourcesIds: updatedPublishedResourcesIds,
|
|
74
76
|
remote,
|
|
77
|
+
seed,
|
|
75
78
|
});
|
|
76
79
|
}
|
|
77
80
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
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.
|
|
46
|
-
"@appsemble/types": "0.
|
|
47
|
-
"@appsemble/utils": "0.
|
|
45
|
+
"@appsemble/node-utils": "0.27.0",
|
|
46
|
+
"@appsemble/types": "0.27.0",
|
|
47
|
+
"@appsemble/utils": "0.27.0",
|
|
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.
|
|
90
|
+
"@appsemble/types": "0.27.0",
|
|
91
91
|
"@types/concat-stream": "^2.0.0",
|
|
92
92
|
"@types/inquirer": "^9.0.0",
|
|
93
93
|
"@types/normalize-path": "^3.0.0",
|
package/server/options/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { applyAppServiceSecrets, checkRole,
|
|
1
|
+
import { applyAppServiceSecrets, checkRole, createAppAsset, createAppResourcesWithAssets, createSettings, createTheme, deleteAppAsset, deleteAppResource, email, getApp, getAppAssets, getAppBlockStyles, getAppDetails, getAppIcon, getAppMembers, getAppMessages, getAppResource, getAppResources, getAppScreenshots, getAppStyles, getAppTeams, getAppUrl, getAppUserInfo, getBlockAsset, getBlockMessages, getBlocksAssetsPaths, getCsp, getDbUpdated, getHost, getTheme, parseQuery, reloadUser, sendNotifications, updateAppResource, verifyResourceActionPermission, } from './index.js';
|
|
2
2
|
export const options = {
|
|
3
3
|
applyAppServiceSecrets,
|
|
4
4
|
checkRole,
|
|
5
|
-
checkSeededResources,
|
|
6
5
|
createAppAsset,
|
|
7
6
|
createAppResourcesWithAssets,
|
|
8
7
|
createSettings,
|
|
@@ -6,7 +6,7 @@ pages:
|
|
|
6
6
|
- name: Example Page A
|
|
7
7
|
blocks:
|
|
8
8
|
- type: action-button
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.27.0
|
|
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.
|
|
20
|
+
version: 0.27.0
|
|
21
21
|
parameters:
|
|
22
22
|
icon: arrow-left
|
|
23
23
|
actions:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function checkSeededResources(): Promise<boolean>;
|