@factorialco/fcode-cli 2.4.0 → 3.1.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 +332 -118
- package/dist/commands/clone.d.ts +0 -12
- package/dist/commands/clone.js +22 -135
- package/dist/commands/{settings → config}/get.d.ts +1 -1
- package/dist/commands/{settings → config}/get.js +7 -7
- package/dist/commands/{settings → config}/remove.d.ts +1 -1
- package/dist/commands/{settings → config}/remove.js +6 -6
- package/dist/commands/{settings → config}/set.d.ts +1 -1
- package/dist/commands/{settings → config}/set.js +8 -8
- package/dist/commands/dependencies/diff.d.ts +9 -0
- package/dist/commands/dependencies/diff.js +15 -0
- package/dist/commands/dependencies/pull.d.ts +9 -0
- package/dist/commands/dependencies/pull.js +44 -1
- package/dist/commands/diff.d.ts +12 -0
- package/dist/commands/diff.js +48 -0
- package/dist/commands/http.d.ts +1 -1
- package/dist/commands/http.js +7 -7
- package/dist/commands/i18n/diff.d.ts +9 -0
- package/dist/commands/i18n/diff.js +15 -0
- package/dist/commands/i18n/status.d.ts +3 -0
- package/dist/commands/i18n/status.js +1 -0
- package/dist/commands/modules/diff.d.ts +9 -0
- package/dist/commands/modules/diff.js +15 -0
- package/dist/commands/modules/status.d.ts +3 -0
- package/dist/commands/modules/status.js +1 -0
- package/dist/commands/processes/diff.d.ts +9 -0
- package/dist/commands/processes/diff.js +15 -0
- package/dist/commands/processes/status.d.ts +3 -0
- package/dist/commands/processes/status.js +1 -0
- package/dist/commands/pull.js +2 -2
- package/dist/commands/push.js +2 -2
- package/dist/commands/{team → settings}/aliases/delete.d.ts +1 -1
- package/dist/commands/{team → settings}/aliases/delete.js +6 -6
- package/dist/commands/{team → settings}/aliases/list.d.ts +1 -1
- package/dist/commands/{team → settings}/aliases/list.js +8 -8
- package/dist/commands/{team → settings}/aliases/set.d.ts +1 -1
- package/dist/commands/{team → settings}/aliases/set.js +8 -8
- package/dist/commands/settings/diff.d.ts +8 -0
- package/dist/commands/settings/diff.js +39 -0
- package/dist/commands/settings/pull.d.ts +11 -0
- package/dist/commands/settings/pull.js +41 -0
- package/dist/commands/{team → settings}/push.d.ts +4 -4
- package/dist/commands/{team → settings}/push.js +39 -39
- package/dist/commands/settings/status.d.ts +8 -0
- package/dist/commands/settings/status.js +46 -0
- package/dist/commands/{team → settings}/versions/create.d.ts +1 -1
- package/dist/commands/{team → settings}/versions/create.js +13 -10
- package/dist/commands/{team → settings}/versions/delete.d.ts +1 -1
- package/dist/commands/{team → settings}/versions/delete.js +10 -10
- package/dist/commands/{team → settings}/versions/list.d.ts +1 -1
- package/dist/commands/{team → settings}/versions/list.js +8 -8
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +10 -7
- package/dist/commands/team/clone.d.ts +14 -0
- package/dist/commands/team/clone.js +90 -0
- package/dist/commands/team/pull.d.ts +3 -7
- package/dist/commands/team/pull.js +24 -30
- package/dist/commands/team/status.d.ts +3 -4
- package/dist/commands/team/status.js +38 -33
- package/dist/commands/variables/diff.d.ts +9 -0
- package/dist/commands/variables/diff.js +15 -0
- package/dist/commands/variables/status.d.ts +3 -0
- package/dist/commands/variables/status.js +1 -0
- package/dist/config/dev-team.d.ts +64 -0
- package/dist/config/dev-team.js +76 -0
- package/dist/config/workspace.d.ts +2 -2
- package/dist/config/workspace.js +4 -2
- package/dist/core/commands/base-command.js +5 -0
- package/dist/core/commands/dependency-command.d.ts +2 -1
- package/dist/core/commands/dependency-command.js +3 -0
- package/dist/core/commands/i18n-command.js +2 -0
- package/dist/core/commands/modules-command.js +2 -0
- package/dist/core/commands/processes-command.js +2 -0
- package/dist/core/commands/resource-command.d.ts +26 -1
- package/dist/core/commands/resource-command.js +100 -3
- package/dist/core/commands/variables-command.d.ts +7 -0
- package/dist/core/commands/variables-command.js +13 -0
- package/dist/core/executors/javascript-execution-task.js +25 -3
- package/dist/core/types.d.ts +30 -7
- package/dist/core/types.js +38 -10
- package/dist/repositories/process.d.ts +8 -2
- package/dist/repositories/process.js +28 -18
- package/dist/repositories/programming-language-manifest.d.ts +15 -1
- package/dist/repositories/programming-language-manifest.js +45 -1
- package/dist/repositories/settings.d.ts +52 -0
- package/dist/repositories/settings.js +214 -0
- package/dist/runtime/libs/python/fcode/context.py +29 -2
- package/dist/services/dashboard-client.d.ts +43 -0
- package/dist/services/dashboard-client.js +108 -0
- package/dist/services/dev-team-service.d.ts +55 -0
- package/dist/services/dev-team-service.js +221 -0
- package/dist/services/fcode-graphql-client.d.ts +16 -2
- package/dist/services/fcode-graphql-client.js +56 -7
- package/dist/services/locale-resolver.d.ts +16 -5
- package/dist/services/locale-resolver.js +60 -12
- package/dist/services/resource-differ.d.ts +60 -0
- package/dist/services/resource-differ.js +135 -0
- package/dist/services/skills-installer.d.ts +16 -0
- package/dist/services/skills-installer.js +77 -0
- package/dist/services/team-versioning.d.ts +4 -4
- package/dist/services/team-versioning.js +4 -4
- package/dist/services/webhook-auth.js +1 -1
- package/dist/services/workspace-cloner.d.ts +31 -0
- package/dist/services/workspace-cloner.js +95 -0
- package/dist/utils/dev-slug.d.ts +12 -0
- package/dist/utils/dev-slug.js +37 -0
- package/dist/utils/diff.d.ts +19 -0
- package/dist/utils/diff.js +58 -0
- package/dist/utils/slugify.d.ts +17 -0
- package/dist/utils/slugify.js +41 -0
- package/oclif.manifest.json +428 -116
- package/package.json +12 -7
- package/dist/repositories/team.d.ts +0 -41
- package/dist/repositories/team.js +0 -170
|
@@ -3,16 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const color_1 = require("@oclif/color");
|
|
4
4
|
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
5
5
|
const cli_1 = require("../../../utils/cli");
|
|
6
|
-
class
|
|
6
|
+
class SettingsVersionsListCommand extends api_client_command_1.default {
|
|
7
7
|
async run() {
|
|
8
|
-
await this.parse(
|
|
8
|
+
await this.parse(SettingsVersionsListCommand);
|
|
9
9
|
const versions = await this._fcodeGraphQLClient.allTeamVersions();
|
|
10
10
|
if (versions.length === 0) {
|
|
11
|
-
cli_1.default.log('📋 No
|
|
12
|
-
cli_1.default.log(`Publish one with ${color_1.default.cyan('fcode
|
|
11
|
+
cli_1.default.log('📋 No workspace versions published yet.');
|
|
12
|
+
cli_1.default.log(`Publish one with ${color_1.default.cyan('fcode settings:versions:create [tag]')}`);
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
cli_1.default.log('🏷️
|
|
15
|
+
cli_1.default.log('🏷️ Workspace versions:\n');
|
|
16
16
|
for (const version of versions) {
|
|
17
17
|
const comment = version.comment ? ` — ${version.comment}` : '';
|
|
18
18
|
const author = version.createdBy?.name ? ` by ${version.createdBy.name}` : '';
|
|
@@ -20,6 +20,6 @@ class TeamVersionsListCommand extends api_client_command_1.default {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
exports.default =
|
|
23
|
+
SettingsVersionsListCommand.description = 'List the workspace versions published on the active workspace';
|
|
24
|
+
SettingsVersionsListCommand.examples = ['$ fcode settings:versions:list'];
|
|
25
|
+
exports.default = SettingsVersionsListCommand;
|
|
@@ -2,5 +2,8 @@ import BaseCommand from '../core/commands/base-command';
|
|
|
2
2
|
export default class Status extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
showInherited: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
5
8
|
run(): Promise<void>;
|
|
6
9
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -6,19 +6,22 @@ const status_2 = require("./processes/status");
|
|
|
6
6
|
const status_3 = require("./variables/status");
|
|
7
7
|
const status_4 = require("./i18n/status");
|
|
8
8
|
const status_5 = require("./dependencies/status");
|
|
9
|
-
const status_6 = require("./
|
|
9
|
+
const status_6 = require("./settings/status");
|
|
10
|
+
const types_1 = require("../core/types");
|
|
10
11
|
class Status extends base_command_1.default {
|
|
11
12
|
async run() {
|
|
12
|
-
await this.parse(Status);
|
|
13
|
+
const { flags: { showInherited }, } = await this.parse(Status);
|
|
14
|
+
const args = showInherited ? ['--showInherited'] : [];
|
|
13
15
|
await this.logActiveTeam();
|
|
14
|
-
await status_2.default.run(
|
|
15
|
-
await status_1.default.run(
|
|
16
|
-
await status_3.default.run(
|
|
17
|
-
await status_4.default.run(
|
|
16
|
+
await status_2.default.run(args);
|
|
17
|
+
await status_1.default.run(args);
|
|
18
|
+
await status_3.default.run(args);
|
|
19
|
+
await status_4.default.run(args);
|
|
18
20
|
await status_5.default.run([]);
|
|
19
21
|
await status_6.default.run([]);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
Status.description = 'Show team workspace status';
|
|
23
|
-
Status.examples = ['$ fcode status'];
|
|
25
|
+
Status.examples = ['$ fcode status', '$ fcode status --showInherited'];
|
|
26
|
+
Status.flags = types_1.ShowInheritedFlag;
|
|
24
27
|
exports.default = Status;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class TeamCloneCommand extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
teamId: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
skipSkillsSetup: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
init(): Promise<void>;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private skipSkills;
|
|
14
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const fs = require("fs-extra");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const color_1 = require("@oclif/color");
|
|
7
|
+
const dashboard_client_1 = require("../../services/dashboard-client");
|
|
8
|
+
const dev_team_1 = require("../../config/dev-team");
|
|
9
|
+
const global_1 = require("../../config/global");
|
|
10
|
+
const skills_installer_1 = require("../../services/skills-installer");
|
|
11
|
+
const dev_team_service_1 = require("../../services/dev-team-service");
|
|
12
|
+
const settings_1 = require("../../config/settings");
|
|
13
|
+
const slugify_1 = require("../../utils/slugify");
|
|
14
|
+
const cli_1 = require("../../utils/cli");
|
|
15
|
+
class TeamCloneCommand extends core_1.Command {
|
|
16
|
+
async init() {
|
|
17
|
+
const { url } = (0, global_1.parseTeamOrigin)('');
|
|
18
|
+
const { authUrl } = await new global_1.default().getAuthInfo(url);
|
|
19
|
+
await this.config.runHook('auth', { argv: [url, authUrl], id: 'login' });
|
|
20
|
+
}
|
|
21
|
+
async run() {
|
|
22
|
+
const { args, flags } = await this.parse(TeamCloneCommand);
|
|
23
|
+
const cloudUrl = global_1.FCODE_CLOUD_URL;
|
|
24
|
+
const client = new dashboard_client_1.default(cloudUrl);
|
|
25
|
+
const team = await (0, dev_team_service_1.resolveTeam)(client, args.teamId);
|
|
26
|
+
if (!team) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const remote = await (0, dev_team_service_1.fetchTeamRemote)(client, team.id);
|
|
30
|
+
if (remote.apps.length === 0 && remote.globalWorkspaces.length === 0) {
|
|
31
|
+
cli_1.default.log(`ℹ️ '${team.name}' has no Apps or global workspaces yet.`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const rootDir = path.resolve((0, slugify_1.slugifyOr)(team.name, team.id));
|
|
35
|
+
if (fs.pathExistsSync(rootDir) && fs.readdirSync(rootDir).length > 0) {
|
|
36
|
+
this.error(`Destination path '${path.basename(rootDir)}' already exists and is not empty.`, {
|
|
37
|
+
code: 'ERR_TEAM_DIR_IS_NOT_EMPTY',
|
|
38
|
+
exit: 1,
|
|
39
|
+
suggestions: [`cd ${path.basename(rootDir)}`, 'fcode team:pull'],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
cli_1.default.log(color_1.default.cyan(`\n🏢 cloning ${remote.apps.length} App(s) and ${remote.globalWorkspaces.length} global workspace(s) of '${team.name}'...`));
|
|
43
|
+
const metadata = {
|
|
44
|
+
developmentTeam: { id: team.id, name: team.name },
|
|
45
|
+
cloudUrl,
|
|
46
|
+
apps: {},
|
|
47
|
+
globalWorkspaces: {},
|
|
48
|
+
};
|
|
49
|
+
// Written before anything is cloned, so a run interrupted halfway still leaves a folder
|
|
50
|
+
// `fcode team:pull` recognises and can finish.
|
|
51
|
+
const config = new dev_team_1.default(rootDir);
|
|
52
|
+
config.write(metadata);
|
|
53
|
+
// Skills next: each workspace links to them as it lands, so an install that fails leaves
|
|
54
|
+
// everything cloned and only the links missing.
|
|
55
|
+
if (!(await this.skipSkills(flags.skipSkillsSetup))) {
|
|
56
|
+
await (0, skills_installer_1.installSkillsAt)(rootDir);
|
|
57
|
+
}
|
|
58
|
+
const result = await (0, dev_team_service_1.syncTeam)(rootDir, metadata, remote);
|
|
59
|
+
config.write(metadata);
|
|
60
|
+
if ((0, dev_team_service_1.reportOutcomes)(result)) {
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
cli_1.default.log(`\n⚠️ '${team.name}' cloned with errors — rerun 'fcode team:pull' inside the folder to retry.\n`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
cli_1.default.log(`\n🎉 '${team.name}' cloned successfully!\n`);
|
|
66
|
+
}
|
|
67
|
+
cli_1.default.log(`$ cd ${path.basename(rootDir)}\n`);
|
|
68
|
+
}
|
|
69
|
+
async skipSkills(flag) {
|
|
70
|
+
if (flag !== undefined) {
|
|
71
|
+
return flag;
|
|
72
|
+
}
|
|
73
|
+
return (await new global_1.default().getSetting(settings_1.SettingKeys.SKIP_SKILLS_SETUP)) === 'true';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
TeamCloneCommand.description = "Clone a development team into one folder — an App folder each, holding the App's development workspace, plus the team's global workspaces under global-workspaces/";
|
|
77
|
+
TeamCloneCommand.examples = ['$ fcode team:clone', '$ fcode team:clone 0f9c1a2b-3d4e-5f60-8a1b-2c3d4e5f6071'];
|
|
78
|
+
TeamCloneCommand.args = {
|
|
79
|
+
teamId: core_1.Args.string({
|
|
80
|
+
description: 'development team id (run without it to list the teams you belong to)',
|
|
81
|
+
name: 'teamId',
|
|
82
|
+
required: false,
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
TeamCloneCommand.flags = {
|
|
86
|
+
skipSkillsSetup: core_1.Flags.boolean({
|
|
87
|
+
description: 'Skip installation of the Factorial Code agent skills',
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
exports.default = TeamCloneCommand;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export default class TeamPullCommand extends ApiClientCommand {
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class TeamPullCommand extends Command {
|
|
4
3
|
static description: string;
|
|
5
4
|
static examples: string[];
|
|
6
|
-
|
|
7
|
-
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
-
};
|
|
9
|
-
_teamRepository: TeamRepository;
|
|
5
|
+
init(): Promise<void>;
|
|
10
6
|
run(): Promise<void>;
|
|
11
7
|
}
|
|
@@ -1,41 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
3
4
|
const color_1 = require("@oclif/color");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const dashboard_client_1 = require("../../services/dashboard-client");
|
|
6
|
+
const dev_team_1 = require("../../config/dev-team");
|
|
7
|
+
const global_1 = require("../../config/global");
|
|
8
|
+
const dev_team_service_1 = require("../../services/dev-team-service");
|
|
7
9
|
const cli_1 = require("../../utils/cli");
|
|
8
|
-
class TeamPullCommand extends
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
class TeamPullCommand extends core_1.Command {
|
|
11
|
+
async init() {
|
|
12
|
+
const { url } = (0, global_1.parseTeamOrigin)('');
|
|
13
|
+
const { authUrl } = await new global_1.default().getAuthInfo(url);
|
|
14
|
+
await this.config.runHook('auth', { argv: [url, authUrl], id: 'login' });
|
|
12
15
|
}
|
|
13
16
|
async run() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
await this.parse(TeamPullCommand);
|
|
18
|
+
const rootDir = process.cwd();
|
|
19
|
+
const config = new dev_team_1.default(rootDir);
|
|
20
|
+
const metadata = config.read();
|
|
21
|
+
const { developmentTeam } = metadata;
|
|
22
|
+
cli_1.default.log(color_1.default.cyan(`\n🏢 pulling '${developmentTeam.name}'...`));
|
|
23
|
+
const remote = await (0, dev_team_service_1.fetchTeamRemote)(new dashboard_client_1.default(metadata.cloudUrl), developmentTeam.id);
|
|
24
|
+
const result = await (0, dev_team_service_1.syncTeam)(rootDir, metadata, remote);
|
|
25
|
+
// Written even when a workspace failed, so the ones that did land are recorded and a rerun
|
|
26
|
+
// only retries what is still missing.
|
|
27
|
+
config.write(metadata);
|
|
28
|
+
if ((0, dev_team_service_1.reportOutcomes)(result)) {
|
|
29
|
+
process.exitCode = 1;
|
|
26
30
|
}
|
|
27
|
-
const [raw, versions, aliases] = await Promise.all([
|
|
28
|
-
this._fcodeGraphQLClient.team(),
|
|
29
|
-
this._fcodeGraphQLClient.allTeamVersions(),
|
|
30
|
-
this._fcodeGraphQLClient.allTeamVersionAliases(),
|
|
31
|
-
]);
|
|
32
|
-
const team = await this._teamRepository.fromRaw(raw);
|
|
33
|
-
this._teamRepository.applyCloudVersioning(team, versions, aliases);
|
|
34
|
-
this._teamRepository.save(team);
|
|
35
|
-
await this.workspaceConfig.setTeamStoredHash(team.contentHash);
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
|
-
TeamPullCommand.description = '
|
|
33
|
+
TeamPullCommand.description = 'Pull the development team folder: clones Apps and global workspaces added since the last run and pulls every workspace';
|
|
39
34
|
TeamPullCommand.examples = ['$ fcode team:pull'];
|
|
40
|
-
TeamPullCommand.flags = types_1.ForceFlag;
|
|
41
35
|
exports.default = TeamPullCommand;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export default class TeamStatusCommand extends ApiClientCommand {
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class TeamStatusCommand extends Command {
|
|
4
3
|
static description: string;
|
|
5
4
|
static examples: string[];
|
|
6
|
-
|
|
5
|
+
init(): Promise<void>;
|
|
7
6
|
run(): Promise<void>;
|
|
8
7
|
}
|
|
@@ -1,46 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const fs = require("fs-extra");
|
|
5
|
+
const path = require("node:path");
|
|
3
6
|
const color_1 = require("@oclif/color");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
7
|
+
const dev_team_1 = require("../../config/dev-team");
|
|
8
|
+
const global_1 = require("../../config/global");
|
|
9
|
+
const status_1 = require("../status");
|
|
10
|
+
const workspace_cloner_1 = require("../../services/workspace-cloner");
|
|
6
11
|
const cli_1 = require("../../utils/cli");
|
|
7
|
-
class TeamStatusCommand extends
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
class TeamStatusCommand extends core_1.Command {
|
|
13
|
+
async init() {
|
|
14
|
+
const { url } = (0, global_1.parseTeamOrigin)('');
|
|
15
|
+
const { authUrl } = await new global_1.default().getAuthInfo(url);
|
|
16
|
+
await this.config.runHook('auth', { argv: [url, authUrl], id: 'login' });
|
|
11
17
|
}
|
|
12
18
|
async run() {
|
|
13
19
|
await this.parse(TeamStatusCommand);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
const rootDir = process.cwd();
|
|
21
|
+
const metadata = new dev_team_1.default(rootDir).read();
|
|
22
|
+
const checkouts = [
|
|
23
|
+
...Object.values(metadata.globalWorkspaces).map(({ name, slug }) => ({ name, folder: (0, dev_team_1.globalWorkspaceFolder)(slug) })),
|
|
24
|
+
...Object.values(metadata.apps).map(({ name, folder }) => ({ name, folder: path.join(folder, dev_team_1.APP_WORKSPACE_DIR) })),
|
|
25
|
+
];
|
|
26
|
+
const failed = [];
|
|
27
|
+
for (const { name, folder } of checkouts) {
|
|
28
|
+
cli_1.default.log(color_1.default.cyan(`\n━━ ${name} (${folder}) ━━`));
|
|
29
|
+
const workspaceDir = path.join(rootDir, folder);
|
|
30
|
+
if (!fs.pathExistsSync(workspaceDir)) {
|
|
31
|
+
cli_1.default.log(color_1.default.yellow('⚠️ not cloned yet — run fcode team:pull'));
|
|
32
|
+
failed.push(folder);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
await (0, workspace_cloner_1.runInWorkspace)(workspaceDir, () => status_1.default.run([]));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
cli_1.default.log(color_1.default.red(`❌ ${error.message}`));
|
|
40
|
+
failed.push(folder);
|
|
41
|
+
}
|
|
18
42
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this._fcodeGraphQLClient.team(),
|
|
23
|
-
this._fcodeGraphQLClient.allTeamVersions(),
|
|
24
|
-
this._fcodeGraphQLClient.allTeamVersionAliases(),
|
|
25
|
-
]);
|
|
26
|
-
const remote = await this._teamRepository.fromRaw(raw);
|
|
27
|
-
this._teamRepository.applyCloudVersioning(remote, versions, aliases);
|
|
28
|
-
const localChanged = storedHash !== local.contentHash;
|
|
29
|
-
const remoteChanged = storedHash !== remote.contentHash;
|
|
30
|
-
if (localChanged && remoteChanged) {
|
|
31
|
-
cli_1.default.log(color_1.default.red('🔥 team settings conflict (changed both locally and in the cloud)'));
|
|
32
|
-
}
|
|
33
|
-
else if (localChanged) {
|
|
34
|
-
cli_1.default.log(color_1.default.yellow('🔺 team settings modified locally (run fcode team:push)'));
|
|
35
|
-
}
|
|
36
|
-
else if (remoteChanged) {
|
|
37
|
-
cli_1.default.log(color_1.default.yellow('🔻 team settings out of date (run fcode team:pull)'));
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
cli_1.default.log(color_1.default.green('✅ team settings up to date'));
|
|
43
|
+
if (failed.length > 0) {
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
cli_1.default.log(color_1.default.red(`\n${failed.length} workspace(s) could not be checked: ${failed.join(', ')}`));
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
|
-
TeamStatusCommand.description = 'Show
|
|
49
|
+
TeamStatusCommand.description = 'Show what differs from the cloud in every workspace of the development team folder';
|
|
45
50
|
TeamStatusCommand.examples = ['$ fcode team:status'];
|
|
46
51
|
exports.default = TeamStatusCommand;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import VariablesCommand from '../../core/commands/variables-command';
|
|
2
|
+
export default class VariablesDiff extends VariablesCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
resourceSlug: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const types_1 = require("../../core/types");
|
|
4
|
+
const variables_command_1 = require("../../core/commands/variables-command");
|
|
5
|
+
class VariablesDiff extends variables_command_1.default {
|
|
6
|
+
async run() {
|
|
7
|
+
if (await this.resourcesDiff()) {
|
|
8
|
+
this.exit(1);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
VariablesDiff.description = 'Show a unified diff between local variables and the cloud. Sensitive values are never printed. Exits 1 when they differ';
|
|
13
|
+
VariablesDiff.examples = ['$ fcode variables:diff', '$ fcode variables:diff MY_VARIABLE'];
|
|
14
|
+
VariablesDiff.args = types_1.ResourceSlugArg;
|
|
15
|
+
exports.default = VariablesDiff;
|
|
@@ -6,6 +6,9 @@ export default class VariablesStatus extends VariablesCommand {
|
|
|
6
6
|
static args: {
|
|
7
7
|
resourceSlug: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
8
|
};
|
|
9
|
+
static flags: {
|
|
10
|
+
showInherited: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
9
12
|
resourcesTableLog(resources: Array<MergedResource>): Promise<void>;
|
|
10
13
|
private logVariableWarnings;
|
|
11
14
|
run(): Promise<void>;
|
|
@@ -66,4 +66,5 @@ class VariablesStatus extends variables_command_1.default {
|
|
|
66
66
|
VariablesStatus.description = 'List all variables status';
|
|
67
67
|
VariablesStatus.examples = ['$ fcode variables:status'];
|
|
68
68
|
VariablesStatus.args = types_1.ResourceSlugArg;
|
|
69
|
+
VariablesStatus.flags = types_1.ShowInheritedFlag;
|
|
69
70
|
exports.default = VariablesStatus;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CLIError } from '../core/errors';
|
|
2
|
+
/**
|
|
3
|
+
* The workspace checkout inside an App folder is always called `app`, never after the slug it was
|
|
4
|
+
* cloned from. The slug is recorded in the metadata, and the directory is only a checkout: pointing
|
|
5
|
+
* it at another workspace with `fcode remote:add` (dev → prod) must not make its name a lie.
|
|
6
|
+
*/
|
|
7
|
+
export declare const APP_WORKSPACE_DIR = "app";
|
|
8
|
+
/**
|
|
9
|
+
* The global workspaces the team owns sit together under this folder, one checkout per workspace
|
|
10
|
+
* named after its slug. A global workspace is one platform team with a fixed slug, so — unlike an
|
|
11
|
+
* App — there is no name to derive a folder from and nothing to re-point the checkout at.
|
|
12
|
+
*/
|
|
13
|
+
export declare const GLOBAL_WORKSPACES_DIR = "global-workspaces";
|
|
14
|
+
export interface DevTeamApp {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string | null;
|
|
17
|
+
/** Directory holding this App, relative to the team root. Assigned once, then authoritative. */
|
|
18
|
+
folder: string;
|
|
19
|
+
/** Workspace the checkout was cloned from. The directory itself is always `APP_WORKSPACE_DIR`. */
|
|
20
|
+
devWorkspaceSlug: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DevTeamGlobalWorkspace {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
/** Platform team slug; also the checkout's folder name under `GLOBAL_WORKSPACES_DIR`. */
|
|
26
|
+
slug: string;
|
|
27
|
+
}
|
|
28
|
+
export interface DevTeamMetadata {
|
|
29
|
+
developmentTeam: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
};
|
|
33
|
+
cloudUrl: string;
|
|
34
|
+
/** Keyed by App id, so a renamed App keeps its folder. */
|
|
35
|
+
apps: Record<string, DevTeamApp>;
|
|
36
|
+
/** Keyed by global workspace id. */
|
|
37
|
+
globalWorkspaces: Record<string, DevTeamGlobalWorkspace>;
|
|
38
|
+
}
|
|
39
|
+
/** Where a global workspace is checked out, relative to the team root. */
|
|
40
|
+
export declare function globalWorkspaceFolder(slug: string): string;
|
|
41
|
+
export declare class NotDevTeamFolder extends CLIError {
|
|
42
|
+
constructor();
|
|
43
|
+
}
|
|
44
|
+
export default class DevTeamConfig {
|
|
45
|
+
private readonly rootDir;
|
|
46
|
+
constructor(rootDir?: string);
|
|
47
|
+
get configPath(): string;
|
|
48
|
+
exists(): boolean;
|
|
49
|
+
read(): DevTeamMetadata;
|
|
50
|
+
write(metadata: DevTeamMetadata): void;
|
|
51
|
+
}
|
|
52
|
+
/** The App metadata committed inside each App folder, so the folder says what it holds. */
|
|
53
|
+
export interface AppSettings {
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
description: string | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Same name as the workspace settings file one level below, deliberately: each level's
|
|
60
|
+
* `settings.json` describes the thing that level holds. They never collide — this one sits in the
|
|
61
|
+
* App folder, the workspace's inside `APP_WORKSPACE_DIR`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const APP_SETTINGS_FILENAME = "settings.json";
|
|
64
|
+
export declare function writeAppSettings(appDir: string, app: AppSettings): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APP_SETTINGS_FILENAME = exports.NotDevTeamFolder = exports.GLOBAL_WORKSPACES_DIR = exports.APP_WORKSPACE_DIR = void 0;
|
|
4
|
+
exports.globalWorkspaceFolder = globalWorkspaceFolder;
|
|
5
|
+
exports.writeAppSettings = writeAppSettings;
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const errors_1 = require("../core/errors");
|
|
9
|
+
/**
|
|
10
|
+
* `.fcode/team.json` at the root of a development-team checkout: what `fcode team:clone` writes
|
|
11
|
+
* and what `team:pull` / `team:status` recognise the folder by.
|
|
12
|
+
*
|
|
13
|
+
* Not to be confused with a workspace's `settings.json` (the file that used to be called
|
|
14
|
+
* `team.json`): this one describes the *development team* — the humans, the Apps they own and the
|
|
15
|
+
* global workspaces they maintain — and lives one level above the workspaces, inside `.fcode/`.
|
|
16
|
+
*/
|
|
17
|
+
const DEV_TEAM_CONFIG_PATH = path.join('.fcode', 'team.json');
|
|
18
|
+
/**
|
|
19
|
+
* The workspace checkout inside an App folder is always called `app`, never after the slug it was
|
|
20
|
+
* cloned from. The slug is recorded in the metadata, and the directory is only a checkout: pointing
|
|
21
|
+
* it at another workspace with `fcode remote:add` (dev → prod) must not make its name a lie.
|
|
22
|
+
*/
|
|
23
|
+
exports.APP_WORKSPACE_DIR = 'app';
|
|
24
|
+
/**
|
|
25
|
+
* The global workspaces the team owns sit together under this folder, one checkout per workspace
|
|
26
|
+
* named after its slug. A global workspace is one platform team with a fixed slug, so — unlike an
|
|
27
|
+
* App — there is no name to derive a folder from and nothing to re-point the checkout at.
|
|
28
|
+
*/
|
|
29
|
+
exports.GLOBAL_WORKSPACES_DIR = 'global-workspaces';
|
|
30
|
+
/** Where a global workspace is checked out, relative to the team root. */
|
|
31
|
+
function globalWorkspaceFolder(slug) {
|
|
32
|
+
return path.join(exports.GLOBAL_WORKSPACES_DIR, slug);
|
|
33
|
+
}
|
|
34
|
+
class NotDevTeamFolder extends errors_1.CLIError {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('⚠️ Current directory is not a Factorial Code development team folder.', {
|
|
37
|
+
code: 'ERR_NOT_DEV_TEAM_FOLDER',
|
|
38
|
+
exit: 1,
|
|
39
|
+
suggestions: ['fcode team:clone', 'cd <development-team-folder>'],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.NotDevTeamFolder = NotDevTeamFolder;
|
|
44
|
+
class DevTeamConfig {
|
|
45
|
+
constructor(rootDir = process.cwd()) {
|
|
46
|
+
this.rootDir = rootDir;
|
|
47
|
+
}
|
|
48
|
+
get configPath() {
|
|
49
|
+
return path.join(this.rootDir, DEV_TEAM_CONFIG_PATH);
|
|
50
|
+
}
|
|
51
|
+
exists() {
|
|
52
|
+
return fs.pathExistsSync(this.configPath);
|
|
53
|
+
}
|
|
54
|
+
read() {
|
|
55
|
+
if (!this.exists()) {
|
|
56
|
+
throw new NotDevTeamFolder();
|
|
57
|
+
}
|
|
58
|
+
const metadata = fs.readJsonSync(this.configPath);
|
|
59
|
+
// A team folder cloned before global workspaces were part of it has no entry; the next
|
|
60
|
+
// team:pull then clones them the way it clones Apps added since the last run.
|
|
61
|
+
return { ...metadata, globalWorkspaces: metadata.globalWorkspaces ?? {} };
|
|
62
|
+
}
|
|
63
|
+
write(metadata) {
|
|
64
|
+
fs.outputJsonSync(this.configPath, metadata, { spaces: 2 });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.default = DevTeamConfig;
|
|
68
|
+
/**
|
|
69
|
+
* Same name as the workspace settings file one level below, deliberately: each level's
|
|
70
|
+
* `settings.json` describes the thing that level holds. They never collide — this one sits in the
|
|
71
|
+
* App folder, the workspace's inside `APP_WORKSPACE_DIR`.
|
|
72
|
+
*/
|
|
73
|
+
exports.APP_SETTINGS_FILENAME = 'settings.json';
|
|
74
|
+
function writeAppSettings(appDir, app) {
|
|
75
|
+
fs.outputJsonSync(path.join(appDir, exports.APP_SETTINGS_FILENAME), app, { spaces: 2 });
|
|
76
|
+
}
|
|
@@ -19,8 +19,8 @@ declare class WorkspaceConfig extends Config {
|
|
|
19
19
|
exists(resourceType: string, slug: string): Promise<boolean>;
|
|
20
20
|
getInAnyTeam(resourceType: string, slug: string): Promise<Resource | null>;
|
|
21
21
|
getStoredHash(resource: Resource): Promise<string>;
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
getSettingsStoredHash(): Promise<string | undefined>;
|
|
23
|
+
setSettingsStoredHash(contentHash: string): Promise<void>;
|
|
24
24
|
add(resourceType: string, resourceMetadata: ResourceMetadata): Promise<void>;
|
|
25
25
|
remove(resourceType: string, slug: string): Promise<void>;
|
|
26
26
|
}
|
package/dist/config/workspace.js
CHANGED
|
@@ -132,10 +132,12 @@ class WorkspaceConfig extends index_1.default {
|
|
|
132
132
|
const metadata = (await this.getCollectionByClass(await this.getActiveTeam(), resource))[resource.slug];
|
|
133
133
|
return metadata?.contentHash;
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
// The `team` key keeps its name on disk: the hash is computed over the settings content, not the
|
|
136
|
+
// file name, so reusing it keeps every already-cloned workspace from reporting a phantom conflict.
|
|
137
|
+
async getSettingsStoredHash() {
|
|
136
138
|
return (await this.getActiveTeam()).team?.contentHash;
|
|
137
139
|
}
|
|
138
|
-
async
|
|
140
|
+
async setSettingsStoredHash(contentHash) {
|
|
139
141
|
const team = await this.getActiveTeam();
|
|
140
142
|
await this.setTeam({ ...team, team: { contentHash } });
|
|
141
143
|
}
|
|
@@ -14,6 +14,11 @@ class BaseCommand extends core_1.Command {
|
|
|
14
14
|
this.sessionRefreshRetried = false;
|
|
15
15
|
}
|
|
16
16
|
async catch(error) {
|
|
17
|
+
// `this.exit(code)` is a control-flow signal, not a failure: swallowing it here would print
|
|
18
|
+
// "EEXIT: 1" and let the process end with a success code. bin/run hands it to oclif's handle().
|
|
19
|
+
if (error instanceof core_1.Errors.ExitError) {
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
17
22
|
// do any extra work with error
|
|
18
23
|
if (error && error.response && error.response.status === 401) {
|
|
19
24
|
process.exitCode = 1;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ProgrammingLanguageManifest } from '../types';
|
|
1
|
+
import { ProgrammingLanguage, ProgrammingLanguageManifest, VersionedProgrammingLanguageManifest } from '../types';
|
|
2
2
|
import ResourceCommand from './resource-command';
|
|
3
3
|
export default abstract class DependencyCommand extends ResourceCommand<ProgrammingLanguageManifest> {
|
|
4
|
+
_versionedResourceQuery: (programmingLanguage: ProgrammingLanguage) => Promise<Array<VersionedProgrammingLanguageManifest> | null>;
|
|
4
5
|
init(): Promise<void>;
|
|
5
6
|
}
|
|
@@ -4,6 +4,7 @@ const programming_language_manifest_1 = require("../../repositories/programming-
|
|
|
4
4
|
const resource_merger_1 = require("../resource-merger");
|
|
5
5
|
const resource_pusher_1 = require("../resource-pusher");
|
|
6
6
|
const resource_command_1 = require("./resource-command");
|
|
7
|
+
const resource_differ_1 = require("../../services/resource-differ");
|
|
7
8
|
class DependencyCommand extends resource_command_1.default {
|
|
8
9
|
async init() {
|
|
9
10
|
await super.init();
|
|
@@ -11,6 +12,8 @@ class DependencyCommand extends resource_command_1.default {
|
|
|
11
12
|
this._resourceMerger = new resource_merger_1.default(this._resourceRepository);
|
|
12
13
|
this._resourcePusher = new resource_pusher_1.default(this._resourceRepository, this._fcodeGraphQLClient);
|
|
13
14
|
this._resourceQuery = () => this._fcodeGraphQLClient.allProgrammingLanguageManifest.bind(this._fcodeGraphQLClient)();
|
|
15
|
+
this._versionedResourceQuery = (programmingLanguage) => this._fcodeGraphQLClient.allVersionedDependencies.bind(this._fcodeGraphQLClient)(programmingLanguage);
|
|
16
|
+
this._diffFilesFor = resource_differ_1.manifestDiffFiles;
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
exports.default = DependencyCommand;
|
|
@@ -4,6 +4,7 @@ const locale_1 = require("../../repositories/locale");
|
|
|
4
4
|
const resource_merger_1 = require("../resource-merger");
|
|
5
5
|
const resource_pusher_1 = require("../resource-pusher");
|
|
6
6
|
const resource_command_1 = require("./resource-command");
|
|
7
|
+
const resource_differ_1 = require("../../services/resource-differ");
|
|
7
8
|
class I18nCommand extends resource_command_1.default {
|
|
8
9
|
async init() {
|
|
9
10
|
await super.init();
|
|
@@ -11,6 +12,7 @@ class I18nCommand extends resource_command_1.default {
|
|
|
11
12
|
this._resourceMerger = new resource_merger_1.default(this._resourceRepository);
|
|
12
13
|
this._resourcePusher = new resource_pusher_1.default(this._resourceRepository, this._fcodeGraphQLClient);
|
|
13
14
|
this._resourceQuery = () => this._fcodeGraphQLClient.allLocales.bind(this._fcodeGraphQLClient)();
|
|
15
|
+
this._diffFilesFor = resource_differ_1.localeDiffFiles;
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
exports.default = I18nCommand;
|