@factorialco/fcode-cli 2.0.0 → 2.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 +121 -1
- package/dist/commands/setup-debug.js +2 -2
- package/dist/commands/team/aliases/delete.d.ts +9 -0
- package/dist/commands/team/aliases/delete.js +28 -0
- package/dist/commands/team/aliases/list.d.ts +6 -0
- package/dist/commands/team/aliases/list.js +23 -0
- package/dist/commands/team/aliases/set.d.ts +10 -0
- package/dist/commands/team/aliases/set.js +25 -0
- package/dist/commands/team/pull.js +8 -1
- package/dist/commands/team/push.js +3 -0
- package/dist/commands/team/versions/create.d.ts +13 -0
- package/dist/commands/team/versions/create.js +42 -0
- package/dist/commands/team/versions/delete.d.ts +12 -0
- package/dist/commands/team/versions/delete.js +32 -0
- package/dist/commands/team/versions/list.d.ts +6 -0
- package/dist/commands/team/versions/list.js +25 -0
- package/dist/commands/variables/status.js +11 -0
- package/dist/config/settings.d.ts +1 -0
- package/dist/config/settings.js +6 -0
- package/dist/core/types.d.ts +37 -1
- package/dist/core/types.js +2 -1
- package/dist/env.d.ts +2 -2
- package/dist/env.js +1 -1
- package/dist/repositories/team.d.ts +7 -1
- package/dist/repositories/team.js +35 -1
- package/dist/repositories/variable.d.ts +24 -0
- package/dist/repositories/variable.js +70 -22
- package/dist/runtime/executor.js +2 -2
- package/dist/runtime/executor.py +2 -2
- package/dist/runtime/libs/python/fcode/__init__.py +2 -2
- package/dist/services/fcode-graphql-client.d.ts +10 -1
- package/dist/services/fcode-graphql-client.js +171 -23
- package/dist/utils/gitignore.d.ts +2 -0
- package/dist/utils/gitignore.js +6 -0
- package/oclif.manifest.json +350 -159
- package/package.json +10 -2
|
@@ -29,7 +29,15 @@ class TeamRepository {
|
|
|
29
29
|
}
|
|
30
30
|
catch { }
|
|
31
31
|
const parentTeamSlugs = Array.isArray(raw.parentTeamSlugs) ? raw.parentTeamSlugs : [];
|
|
32
|
-
|
|
32
|
+
const team = new types_1.Team(slug, parentTeamSlugs, raw.zoneId, this.parseErrorHandler(raw.errorHandlerConfig), this.parseWebhookAuth(raw.webhookAuth));
|
|
33
|
+
// Parsed so a find()+save() round-trip does not drop the pull-only fields.
|
|
34
|
+
if (Array.isArray(raw.versions)) {
|
|
35
|
+
team.versions = raw.versions;
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(raw.aliases)) {
|
|
38
|
+
team.aliases = raw.aliases;
|
|
39
|
+
}
|
|
40
|
+
return team;
|
|
33
41
|
}
|
|
34
42
|
save(team) {
|
|
35
43
|
(0, file_utils_1.outputFileSyncIfChanged)(TEAM_FILENAME, JSON.stringify(this.toFile(team), null, 2));
|
|
@@ -45,6 +53,25 @@ class TeamRepository {
|
|
|
45
53
|
}
|
|
46
54
|
return new types_1.Team(raw.slug, raw.parentTeamSlugs ?? [], raw.zoneId, errorHandlerConfig, this.parseWebhookAuth(raw.webhookAuth));
|
|
47
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Writes the cloud's team versions and aliases into the Team as the pull-only team.json
|
|
58
|
+
* fields (sorted for stable git diffs). These fields never enter the content hash and are
|
|
59
|
+
* stripped from pushes: all writes go through the team:versions/team:aliases commands.
|
|
60
|
+
*/
|
|
61
|
+
applyCloudVersioning(team, versions, aliases) {
|
|
62
|
+
team.versions = versions
|
|
63
|
+
.map((version) => {
|
|
64
|
+
const entry = { tag: version.tag, createdAt: version.createdAt };
|
|
65
|
+
if (version.comment) {
|
|
66
|
+
entry.comment = version.comment;
|
|
67
|
+
}
|
|
68
|
+
return entry;
|
|
69
|
+
})
|
|
70
|
+
.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
71
|
+
team.aliases = aliases
|
|
72
|
+
.map((alias) => ({ name: alias.name, tag: alias.tag }))
|
|
73
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
74
|
+
}
|
|
48
75
|
async resolveSlugToId(processSlug) {
|
|
49
76
|
const { processes } = await this.workspaceConfig.getActiveTeam();
|
|
50
77
|
return processes[processSlug]?.id ?? processSlug;
|
|
@@ -84,6 +111,13 @@ class TeamRepository {
|
|
|
84
111
|
if (team.webhookAuth !== undefined) {
|
|
85
112
|
file.webhookAuth = team.webhookAuth;
|
|
86
113
|
}
|
|
114
|
+
// Pull-only global versioning fields, appended after the pushable settings.
|
|
115
|
+
if (team.versions !== undefined) {
|
|
116
|
+
file.versions = team.versions;
|
|
117
|
+
}
|
|
118
|
+
if (team.aliases !== undefined) {
|
|
119
|
+
file.aliases = team.aliases;
|
|
120
|
+
}
|
|
87
121
|
return file;
|
|
88
122
|
}
|
|
89
123
|
}
|
|
@@ -4,16 +4,40 @@ export declare const DEFAULT_SENSITIVE_VALUE = "********";
|
|
|
4
4
|
declare class VariableRepository extends ResourceRepositoryBase<Variable> {
|
|
5
5
|
private filename;
|
|
6
6
|
private localFilename;
|
|
7
|
+
private inheritedFilename;
|
|
7
8
|
private filenameInitPromise;
|
|
8
9
|
constructor();
|
|
9
10
|
private ensureFilenamesInitialized;
|
|
10
11
|
findAll(): Promise<Variable[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Values as a local run sees them. Precedence, lowest first: variables inherited from
|
|
14
|
+
* parent workspaces, this workspace's own variables, and the local overrides file — the
|
|
15
|
+
* same order the platform applies, where a workspace's own value wins over an inherited one.
|
|
16
|
+
* An empty value is not an override but an unset one: the platform rejects blank values, and
|
|
17
|
+
* a variable emptied in variables.env is a removal (see `mapResourceStatus`), so an empty
|
|
18
|
+
* entry falls through to the layer below rather than blanking it.
|
|
19
|
+
*/
|
|
11
20
|
findAllForExecution(): Promise<Variable[]>;
|
|
21
|
+
/**
|
|
22
|
+
* An inherited key the workspace has given a value of its own, overriding it. An empty entry
|
|
23
|
+
* does not count: the platform has no empty values, so it reads as "not set" here too.
|
|
24
|
+
*/
|
|
25
|
+
private overridesInherited;
|
|
12
26
|
find(slug: string): Promise<Variable>;
|
|
27
|
+
/**
|
|
28
|
+
* Keys declared in the workspace's own variables file that its metadata does not claim yet.
|
|
29
|
+
* A key the workspace inherits counts as one of those: the metadata still points at the
|
|
30
|
+
* parent's variable, and adding it is what turns the entry into this workspace's own.
|
|
31
|
+
*/
|
|
13
32
|
findSlugsNotAdded(): Promise<Array<string>>;
|
|
14
33
|
protected getResourcePath(_resource: Variable): string;
|
|
15
34
|
protected removeFiles(variable: Variable): Promise<void>;
|
|
16
35
|
protected resourceToMetadata(variable: Variable): ResourceMetadata;
|
|
36
|
+
/**
|
|
37
|
+
* A variable owned by this workspace goes to the committed variables file; one inherited
|
|
38
|
+
* from a parent workspace goes to the gitignored inherited file, so a local run can use it
|
|
39
|
+
* without the child workspace's git history carrying a copy of its parent's values.
|
|
40
|
+
*/
|
|
17
41
|
protected saveFiles(variable: Variable): Promise<void>;
|
|
18
42
|
metadataFromFiles(slug: string, opts: AddResourceOptions): Promise<ResourceMetadata>;
|
|
19
43
|
private readVariablesMeta;
|
|
@@ -20,6 +20,7 @@ class VariableRepository extends resource_1.default {
|
|
|
20
20
|
});
|
|
21
21
|
this.filename = null;
|
|
22
22
|
this.localFilename = null;
|
|
23
|
+
this.inheritedFilename = null;
|
|
23
24
|
this.filenameInitPromise = null;
|
|
24
25
|
}
|
|
25
26
|
async ensureFilenamesInitialized() {
|
|
@@ -30,40 +31,67 @@ class VariableRepository extends resource_1.default {
|
|
|
30
31
|
const globalConfig = new global_1.default();
|
|
31
32
|
this.filename = await globalConfig.getSetting(settings_1.SettingKeys.VARIABLES_FILE_NAME);
|
|
32
33
|
this.localFilename = await globalConfig.getSetting(settings_1.SettingKeys.LOCAL_VARIABLES_FILE_NAME);
|
|
34
|
+
this.inheritedFilename = await globalConfig.getSetting(settings_1.SettingKeys.INHERITED_VARIABLES_FILE_NAME);
|
|
33
35
|
})();
|
|
34
36
|
return this.filenameInitPromise;
|
|
35
37
|
}
|
|
36
38
|
async findAll() {
|
|
37
39
|
await this.ensureFilenamesInitialized();
|
|
38
|
-
const rawVariables = this.readVariablesFromFile();
|
|
39
|
-
const
|
|
40
|
+
const rawVariables = this.readVariablesFromFile('own');
|
|
41
|
+
const inheritedRawVariables = this.readVariablesFromFile('inherited');
|
|
42
|
+
const localOverrideKeys = new Set(Object.keys(this.readVariablesFromFile('local')));
|
|
40
43
|
const meta = this.readVariablesMeta();
|
|
41
44
|
const variables = [];
|
|
42
45
|
const { variables: localVariables } = await this.workspaceConfig.getActiveTeam();
|
|
43
46
|
for (const localVariableKey of Object.keys(localVariables)) {
|
|
44
47
|
const localVariable = localVariables[localVariableKey];
|
|
45
48
|
const isSensitive = meta[localVariable.key]?.isSensitive ?? false;
|
|
46
|
-
|
|
49
|
+
// Writing an inherited key into variables.env is how you override it, so from that
|
|
50
|
+
// moment the variable is this workspace's: it drops the parent's id and inherited
|
|
51
|
+
// marker, which is what makes `push` create it here instead of skipping it.
|
|
52
|
+
const overridesInherited = this.overridesInherited(localVariable, rawVariables);
|
|
53
|
+
const resolvingTeamSlug = overridesInherited ? undefined : localVariable.resolvingTeamSlug;
|
|
54
|
+
const id = overridesInherited ? undefined : localVariable.id;
|
|
55
|
+
const rawValue = resolvingTeamSlug ? inheritedRawVariables[localVariable.key] : rawVariables[localVariable.key];
|
|
56
|
+
const variable = types_1.Variable.validate(new types_1.Variable(id, localVariable.key, rawValue, isSensitive, resolvingTeamSlug));
|
|
47
57
|
variable.overriddenLocally = localOverrideKeys.has(localVariable.key);
|
|
48
58
|
variables.push(variable);
|
|
49
59
|
}
|
|
50
60
|
return variables;
|
|
51
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Values as a local run sees them. Precedence, lowest first: variables inherited from
|
|
64
|
+
* parent workspaces, this workspace's own variables, and the local overrides file — the
|
|
65
|
+
* same order the platform applies, where a workspace's own value wins over an inherited one.
|
|
66
|
+
* An empty value is not an override but an unset one: the platform rejects blank values, and
|
|
67
|
+
* a variable emptied in variables.env is a removal (see `mapResourceStatus`), so an empty
|
|
68
|
+
* entry falls through to the layer below rather than blanking it.
|
|
69
|
+
*/
|
|
52
70
|
async findAllForExecution() {
|
|
53
71
|
await this.ensureFilenamesInitialized();
|
|
54
|
-
const rawVariables = this.readVariablesFromFile();
|
|
55
|
-
const
|
|
72
|
+
const rawVariables = this.readVariablesFromFile('own');
|
|
73
|
+
const inheritedRawVariables = this.readVariablesFromFile('inherited');
|
|
74
|
+
const localRawVariables = this.readVariablesFromFile('local');
|
|
56
75
|
const meta = this.readVariablesMeta();
|
|
57
76
|
const variables = [];
|
|
58
77
|
const { variables: localVariables } = await this.workspaceConfig.getActiveTeam();
|
|
59
78
|
for (const localVariableKey of Object.keys(localVariables)) {
|
|
60
79
|
const localVariable = localVariables[localVariableKey];
|
|
61
|
-
const
|
|
62
|
-
const isSensitive = meta[
|
|
63
|
-
|
|
80
|
+
const key = localVariable.key;
|
|
81
|
+
const isSensitive = meta[key]?.isSensitive ?? false;
|
|
82
|
+
const value = localRawVariables[key] || rawVariables[key] || inheritedRawVariables[key];
|
|
83
|
+
const resolvingTeamSlug = this.overridesInherited(localVariable, rawVariables) ? undefined : localVariable.resolvingTeamSlug;
|
|
84
|
+
variables.push(types_1.Variable.validate(new types_1.Variable(localVariable.id, key, value, isSensitive, resolvingTeamSlug)));
|
|
64
85
|
}
|
|
65
86
|
return variables;
|
|
66
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* An inherited key the workspace has given a value of its own, overriding it. An empty entry
|
|
90
|
+
* does not count: the platform has no empty values, so it reads as "not set" here too.
|
|
91
|
+
*/
|
|
92
|
+
overridesInherited(metadata, ownRawVariables) {
|
|
93
|
+
return Boolean(metadata.resolvingTeamSlug) && Boolean(ownRawVariables[metadata.key]);
|
|
94
|
+
}
|
|
67
95
|
async find(slug) {
|
|
68
96
|
let variables = await this.findAll();
|
|
69
97
|
variables = variables.filter((v) => v.key === slug);
|
|
@@ -72,12 +100,19 @@ class VariableRepository extends resource_1.default {
|
|
|
72
100
|
}
|
|
73
101
|
throw new errors_1.LocalVariableNotFound(new Error('Variable not found on team metadata'), slug);
|
|
74
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Keys declared in the workspace's own variables file that its metadata does not claim yet.
|
|
105
|
+
* A key the workspace inherits counts as one of those: the metadata still points at the
|
|
106
|
+
* parent's variable, and adding it is what turns the entry into this workspace's own.
|
|
107
|
+
*/
|
|
75
108
|
async findSlugsNotAdded() {
|
|
76
109
|
await this.ensureFilenamesInitialized();
|
|
110
|
+
const ownRawVariables = this.readVariablesFromFile('own');
|
|
77
111
|
const { variables: localVariables } = await this.workspaceConfig.getActiveTeam();
|
|
78
112
|
const slugsNotAdded = [];
|
|
79
|
-
for (const
|
|
80
|
-
|
|
113
|
+
for (const key of Object.keys(ownRawVariables)) {
|
|
114
|
+
const metadata = localVariables[key];
|
|
115
|
+
if (!metadata || this.overridesInherited(metadata, ownRawVariables)) {
|
|
81
116
|
slugsNotAdded.push(key);
|
|
82
117
|
}
|
|
83
118
|
}
|
|
@@ -88,9 +123,10 @@ class VariableRepository extends resource_1.default {
|
|
|
88
123
|
}
|
|
89
124
|
async removeFiles(variable) {
|
|
90
125
|
await this.ensureFilenamesInitialized();
|
|
91
|
-
|
|
126
|
+
const file = variable.resolvingTeamSlug ? 'inherited' : 'own';
|
|
127
|
+
const rawVariables = this.readVariablesFromFile(file);
|
|
92
128
|
delete rawVariables[variable.key];
|
|
93
|
-
this.writeRawVariables(rawVariables);
|
|
129
|
+
this.writeRawVariables(rawVariables, file);
|
|
94
130
|
const meta = this.readVariablesMeta();
|
|
95
131
|
delete meta[variable.key];
|
|
96
132
|
this.writeVariablesMeta(meta);
|
|
@@ -100,13 +136,20 @@ class VariableRepository extends resource_1.default {
|
|
|
100
136
|
id: variable.id,
|
|
101
137
|
slug: variable.slug,
|
|
102
138
|
key: variable.key,
|
|
139
|
+
...(variable.resolvingTeamSlug && { resolvingTeamSlug: variable.resolvingTeamSlug }),
|
|
103
140
|
};
|
|
104
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* A variable owned by this workspace goes to the committed variables file; one inherited
|
|
144
|
+
* from a parent workspace goes to the gitignored inherited file, so a local run can use it
|
|
145
|
+
* without the child workspace's git history carrying a copy of its parent's values.
|
|
146
|
+
*/
|
|
105
147
|
async saveFiles(variable) {
|
|
106
148
|
await this.ensureFilenamesInitialized();
|
|
107
|
-
|
|
108
|
-
rawVariables =
|
|
109
|
-
this.
|
|
149
|
+
const file = variable.resolvingTeamSlug ? 'inherited' : 'own';
|
|
150
|
+
const rawVariables = this.readVariablesFromFile(file);
|
|
151
|
+
rawVariables[variable.key] = this.valueToSave(variable);
|
|
152
|
+
this.writeRawVariables(rawVariables, file);
|
|
110
153
|
const meta = this.readVariablesMeta();
|
|
111
154
|
meta[variable.key] = { isSensitive: variable.isSensitive };
|
|
112
155
|
this.writeVariablesMeta(meta);
|
|
@@ -139,15 +182,20 @@ class VariableRepository extends resource_1.default {
|
|
|
139
182
|
}, {});
|
|
140
183
|
(0, file_utils_1.outputFileSyncIfChanged)(VARIABLES_META_FILENAME, JSON.stringify(sorted, null, 2));
|
|
141
184
|
}
|
|
142
|
-
filePath(
|
|
143
|
-
if (!this.filename || !this.localFilename) {
|
|
185
|
+
filePath(file = 'own') {
|
|
186
|
+
if (!this.filename || !this.localFilename || !this.inheritedFilename) {
|
|
144
187
|
throw new Error('Filenames not initialized. Call ensureFilenamesInitialized() first.');
|
|
145
188
|
}
|
|
146
|
-
|
|
189
|
+
const filenames = {
|
|
190
|
+
own: this.filename,
|
|
191
|
+
local: this.localFilename,
|
|
192
|
+
inherited: this.inheritedFilename,
|
|
193
|
+
};
|
|
194
|
+
return path.join(filenames[file]);
|
|
147
195
|
}
|
|
148
|
-
readVariablesFromFile(
|
|
196
|
+
readVariablesFromFile(file = 'own') {
|
|
149
197
|
try {
|
|
150
|
-
const result = (0, dotenv_1.parse)(fs.readFileSync(this.filePath(
|
|
198
|
+
const result = (0, dotenv_1.parse)(fs.readFileSync(this.filePath(file), 'utf8')) || {};
|
|
151
199
|
for (const [key, value] of Object.entries(result)) {
|
|
152
200
|
result[key] = (0, env_utils_1.unescapeEnvValue)(value);
|
|
153
201
|
}
|
|
@@ -163,12 +211,12 @@ class VariableRepository extends resource_1.default {
|
|
|
163
211
|
}
|
|
164
212
|
return variable.value;
|
|
165
213
|
}
|
|
166
|
-
writeRawVariables(rawVariables) {
|
|
214
|
+
writeRawVariables(rawVariables, file = 'own') {
|
|
167
215
|
const envContent = Object.entries(rawVariables)
|
|
168
216
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
169
217
|
.map(([key, value]) => `${key}=${(0, env_utils_1.escapeEnvValue)(value)}`)
|
|
170
218
|
.join('\n');
|
|
171
|
-
(0, file_utils_1.outputFileSyncIfChanged)(this.filePath(), envContent);
|
|
219
|
+
(0, file_utils_1.outputFileSyncIfChanged)(this.filePath(file), envContent);
|
|
172
220
|
}
|
|
173
221
|
}
|
|
174
222
|
exports.default = VariableRepository;
|
package/dist/runtime/executor.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('node:path')
|
|
|
3
3
|
const { execSync } = require('node:child_process')
|
|
4
4
|
const os = require('node:os')
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const FC_COMMAND = process.env.FC_COMMAND ?? "fcode"
|
|
7
7
|
|
|
8
8
|
const file = process.argv[2]
|
|
9
9
|
if (!fs.existsSync(file)) {
|
|
@@ -36,7 +36,7 @@ let dependenciesPath = ''
|
|
|
36
36
|
try {
|
|
37
37
|
version = getVersion(relativePath)
|
|
38
38
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fcode-'))
|
|
39
|
-
const command = [
|
|
39
|
+
const command = [FC_COMMAND, 'pre-debug', absolutePath, '-t', tempDir]
|
|
40
40
|
if (version) {
|
|
41
41
|
command.push('-v')
|
|
42
42
|
command.push(version)
|
package/dist/runtime/executor.py
CHANGED
|
@@ -4,7 +4,7 @@ import subprocess
|
|
|
4
4
|
import tempfile
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
FC_COMMAND = os.environ.get("FC_COMMAND", "fcode")
|
|
8
8
|
file = sys.argv[1]
|
|
9
9
|
if not os.path.exists(file):
|
|
10
10
|
print("File not found: " + file)
|
|
@@ -31,7 +31,7 @@ main_file_path = ""
|
|
|
31
31
|
try:
|
|
32
32
|
version = get_version(file)
|
|
33
33
|
tempDir = tempfile.TemporaryDirectory()
|
|
34
|
-
command = [
|
|
34
|
+
command = [FC_COMMAND, "pre-debug", file, "-t", tempDir.name]
|
|
35
35
|
if version != None:
|
|
36
36
|
command.append("-v")
|
|
37
37
|
command.append(version)
|
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
FC_COMMAND = os.environ.get("FC_COMMAND", "fcode")
|
|
7
7
|
|
|
8
8
|
metadata = {
|
|
9
9
|
"execution": {"process": {"name": "", "id": ""}},
|
|
@@ -24,7 +24,7 @@ else:
|
|
|
24
24
|
os.environ.get("FCODE_LOG_LEVEL", "") == "DEBUG" and print(
|
|
25
25
|
"[FCODE LIBRARY] Reading metadata from process-metadata command"
|
|
26
26
|
)
|
|
27
|
-
command = [
|
|
27
|
+
command = [FC_COMMAND, "process-metadata"]
|
|
28
28
|
if process_slug:
|
|
29
29
|
command.append(process_slug)
|
|
30
30
|
if process_slug and process_version:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GraphQLClient } from 'graphql-request';
|
|
2
|
-
import { Process, ProgrammingLanguage, ProgrammingLanguageManifest, RawTeamSettings, ScriptLibrary, TeamUpdateInput, Variable, VersionedProcess, VersionedScriptLibrary, ServiceAccount } from '../core/types';
|
|
2
|
+
import { Process, ProgrammingLanguage, ProgrammingLanguageManifest, RawTeamSettings, ScriptLibrary, TeamUpdateInput, TeamVersion, TeamVersionAlias, Variable, VersionedProcess, VersionedScriptLibrary, ServiceAccount } from '../core/types';
|
|
3
3
|
export type FcodeGraphQLClientOptions = {
|
|
4
4
|
url: string;
|
|
5
5
|
accessToken: string;
|
|
@@ -44,6 +44,15 @@ declare class FcodeGraphQLClient {
|
|
|
44
44
|
* undefined.
|
|
45
45
|
*/
|
|
46
46
|
mapTeamSettings(team: any): RawTeamSettings;
|
|
47
|
+
mapTeamVersion(teamVersion: any): TeamVersion;
|
|
48
|
+
mapTeamVersionAlias(alias: any): TeamVersionAlias;
|
|
49
|
+
allTeamVersions(): Promise<Array<TeamVersion>>;
|
|
50
|
+
createTeamVersion(tag: string, comment?: string): Promise<TeamVersion>;
|
|
51
|
+
deleteTeamVersion(tag: string): Promise<TeamVersion>;
|
|
52
|
+
allTeamVersionAliases(): Promise<Array<TeamVersionAlias>>;
|
|
53
|
+
/** The API upserts: an existing alias with the same name is re-pointed to the tag. */
|
|
54
|
+
createTeamVersionAlias(name: string, tag: string): Promise<TeamVersionAlias>;
|
|
55
|
+
deleteTeamVersionAlias(id: string): Promise<TeamVersionAlias>;
|
|
47
56
|
team(): Promise<RawTeamSettings>;
|
|
48
57
|
pushTeam(update: TeamUpdateInput): Promise<RawTeamSettings>;
|
|
49
58
|
mapProgrammingLanguageManifest(programmingLanguageManifest: any): ProgrammingLanguageManifest;
|
|
@@ -3,6 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const graphql_request_1 = require("graphql-request");
|
|
4
4
|
const types_1 = require("../core/types");
|
|
5
5
|
const _ = require("lodash");
|
|
6
|
+
// Shared team version selections so every query/mutation returns the same shape.
|
|
7
|
+
const TEAM_VERSION_FIELDS = `
|
|
8
|
+
id
|
|
9
|
+
tag
|
|
10
|
+
comment
|
|
11
|
+
createdAt
|
|
12
|
+
createdBy {
|
|
13
|
+
id
|
|
14
|
+
name
|
|
15
|
+
}
|
|
16
|
+
manifest {
|
|
17
|
+
resourceType
|
|
18
|
+
resourceId
|
|
19
|
+
slug
|
|
20
|
+
outcome
|
|
21
|
+
error
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const TEAM_VERSION_ALIAS_FIELDS = `
|
|
25
|
+
id
|
|
26
|
+
name
|
|
27
|
+
tag
|
|
28
|
+
createdAt
|
|
29
|
+
updatedAt
|
|
30
|
+
`;
|
|
6
31
|
// Shared process selection so pull queries and push mutations return the same
|
|
7
32
|
// fields, keeping mapProcess (and therefore contentHash) consistent.
|
|
8
33
|
const PROCESS_FIELDS = `
|
|
@@ -236,33 +261,46 @@ class FcodeGraphQLClient {
|
|
|
236
261
|
return Promise.reject(this.cleanError(error));
|
|
237
262
|
});
|
|
238
263
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
// Pages through every variable: a workspace lists its own plus the ones inherited from
|
|
265
|
+
// its parent workspaces, so a single page is not enough to be sure nothing is missed.
|
|
266
|
+
async allVariables() {
|
|
267
|
+
const pageSize = 200;
|
|
268
|
+
const variables = [];
|
|
269
|
+
let after = null;
|
|
270
|
+
try {
|
|
271
|
+
do {
|
|
272
|
+
const { teamVariables: { edges, pageInfo }, } = await this.client.request((0, graphql_request_1.gql) `
|
|
273
|
+
query teamVariables($first: Int!, $after: String) {
|
|
274
|
+
teamVariables(first: $first, after: $after) {
|
|
275
|
+
edges {
|
|
276
|
+
node {
|
|
277
|
+
id
|
|
278
|
+
key
|
|
279
|
+
value
|
|
280
|
+
isSensitive
|
|
281
|
+
resolvingTeamSlug
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
pageInfo {
|
|
285
|
+
hasNextPage
|
|
286
|
+
endCursor
|
|
250
287
|
}
|
|
251
|
-
}
|
|
252
|
-
pageInfo {
|
|
253
|
-
hasNextPage
|
|
254
|
-
endCursor
|
|
255
288
|
}
|
|
256
289
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
290
|
+
`, {
|
|
291
|
+
after,
|
|
292
|
+
first: pageSize,
|
|
293
|
+
});
|
|
294
|
+
for (const { node: { id, isSensitive, key, value, resolvingTeamSlug }, } of edges) {
|
|
295
|
+
variables.push(new types_1.Variable(id, key, value, isSensitive, this.inheritedTeam(resolvingTeamSlug)));
|
|
296
|
+
}
|
|
297
|
+
after = pageInfo?.hasNextPage ? pageInfo.endCursor : null;
|
|
298
|
+
} while (after);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
264
301
|
return Promise.reject(this.cleanError(error));
|
|
265
|
-
}
|
|
302
|
+
}
|
|
303
|
+
return variables;
|
|
266
304
|
}
|
|
267
305
|
toProcessInput(process, includeCreateOnly) {
|
|
268
306
|
const settings = {
|
|
@@ -603,6 +641,116 @@ class FcodeGraphQLClient {
|
|
|
603
641
|
}
|
|
604
642
|
return settings;
|
|
605
643
|
}
|
|
644
|
+
mapTeamVersion(teamVersion) {
|
|
645
|
+
return {
|
|
646
|
+
id: teamVersion.id,
|
|
647
|
+
tag: teamVersion.tag,
|
|
648
|
+
comment: teamVersion.comment ?? null,
|
|
649
|
+
manifest: (teamVersion.manifest ?? []).map((entry) => ({
|
|
650
|
+
resourceType: entry.resourceType,
|
|
651
|
+
resourceId: entry.resourceId,
|
|
652
|
+
slug: entry.slug,
|
|
653
|
+
outcome: entry.outcome,
|
|
654
|
+
error: entry.error ?? null,
|
|
655
|
+
})),
|
|
656
|
+
createdAt: teamVersion.createdAt,
|
|
657
|
+
createdBy: teamVersion.createdBy ?? null,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
mapTeamVersionAlias(alias) {
|
|
661
|
+
return {
|
|
662
|
+
id: alias.id,
|
|
663
|
+
name: alias.name,
|
|
664
|
+
tag: alias.tag,
|
|
665
|
+
createdAt: alias.createdAt,
|
|
666
|
+
updatedAt: alias.updatedAt ?? null,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
allTeamVersions() {
|
|
670
|
+
return this.client
|
|
671
|
+
.request((0, graphql_request_1.gql) `
|
|
672
|
+
query allTeamVersions {
|
|
673
|
+
allTeamVersions {
|
|
674
|
+
${TEAM_VERSION_FIELDS}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
`)
|
|
678
|
+
.then(({ allTeamVersions }) => allTeamVersions.map((teamVersion) => this.mapTeamVersion(teamVersion)))
|
|
679
|
+
.catch((error) => {
|
|
680
|
+
return Promise.reject(this.cleanError(error));
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
createTeamVersion(tag, comment) {
|
|
684
|
+
return this.client
|
|
685
|
+
.request((0, graphql_request_1.gql) `
|
|
686
|
+
mutation createTeamVersion($tag: String!, $comment: String) {
|
|
687
|
+
createTeamVersion(tag: $tag, comment: $comment) {
|
|
688
|
+
${TEAM_VERSION_FIELDS}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
`, { tag, comment: comment ?? null })
|
|
692
|
+
.then(({ createTeamVersion }) => this.mapTeamVersion(createTeamVersion))
|
|
693
|
+
.catch((error) => {
|
|
694
|
+
return Promise.reject(this.cleanError(error));
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
deleteTeamVersion(tag) {
|
|
698
|
+
return this.client
|
|
699
|
+
.request((0, graphql_request_1.gql) `
|
|
700
|
+
mutation deleteTeamVersion($tag: String!) {
|
|
701
|
+
deleteTeamVersion(tag: $tag) {
|
|
702
|
+
${TEAM_VERSION_FIELDS}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
`, { tag })
|
|
706
|
+
.then(({ deleteTeamVersion }) => this.mapTeamVersion(deleteTeamVersion))
|
|
707
|
+
.catch((error) => {
|
|
708
|
+
return Promise.reject(this.cleanError(error));
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
allTeamVersionAliases() {
|
|
712
|
+
return this.client
|
|
713
|
+
.request((0, graphql_request_1.gql) `
|
|
714
|
+
query allTeamVersionAliases {
|
|
715
|
+
allTeamVersionAliases {
|
|
716
|
+
${TEAM_VERSION_ALIAS_FIELDS}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
`)
|
|
720
|
+
.then(({ allTeamVersionAliases }) => allTeamVersionAliases.map((alias) => this.mapTeamVersionAlias(alias)))
|
|
721
|
+
.catch((error) => {
|
|
722
|
+
return Promise.reject(this.cleanError(error));
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
/** The API upserts: an existing alias with the same name is re-pointed to the tag. */
|
|
726
|
+
createTeamVersionAlias(name, tag) {
|
|
727
|
+
return this.client
|
|
728
|
+
.request((0, graphql_request_1.gql) `
|
|
729
|
+
mutation createTeamVersionAlias($input: TeamVersionAliasInput!) {
|
|
730
|
+
createTeamVersionAlias(input: $input) {
|
|
731
|
+
${TEAM_VERSION_ALIAS_FIELDS}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
`, { input: { name, tag } })
|
|
735
|
+
.then(({ createTeamVersionAlias }) => this.mapTeamVersionAlias(createTeamVersionAlias))
|
|
736
|
+
.catch((error) => {
|
|
737
|
+
return Promise.reject(this.cleanError(error));
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
deleteTeamVersionAlias(id) {
|
|
741
|
+
return this.client
|
|
742
|
+
.request((0, graphql_request_1.gql) `
|
|
743
|
+
mutation deleteTeamVersionAlias($id: ID!) {
|
|
744
|
+
deleteTeamVersionAlias(id: $id) {
|
|
745
|
+
${TEAM_VERSION_ALIAS_FIELDS}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
`, { id })
|
|
749
|
+
.then(({ deleteTeamVersionAlias }) => this.mapTeamVersionAlias(deleteTeamVersionAlias))
|
|
750
|
+
.catch((error) => {
|
|
751
|
+
return Promise.reject(this.cleanError(error));
|
|
752
|
+
});
|
|
753
|
+
}
|
|
606
754
|
team() {
|
|
607
755
|
return this.client
|
|
608
756
|
.request((0, graphql_request_1.gql) `
|
|
@@ -13,5 +13,7 @@ export declare function renderGitignoreWithInherited(existing: string, inherited
|
|
|
13
13
|
* Refreshes the workspace .gitignore so inherited processes/modules (owned by a
|
|
14
14
|
* parent team and pulled read-only) are not tracked by git. Regenerated from the
|
|
15
15
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
16
|
+
* Inherited variable values live in one file, which is ignored as soon as the
|
|
17
|
+
* workspace inherits any variable.
|
|
16
18
|
*/
|
|
17
19
|
export declare function syncInheritedGitignore(workspaceDir?: string): Promise<void>;
|
package/dist/utils/gitignore.js
CHANGED
|
@@ -6,6 +6,8 @@ exports.syncInheritedGitignore = syncInheritedGitignore;
|
|
|
6
6
|
const path = require("node:path");
|
|
7
7
|
const fs = require("fs-extra");
|
|
8
8
|
const workspace_1 = require("../config/workspace");
|
|
9
|
+
const global_1 = require("../config/global");
|
|
10
|
+
const settings_1 = require("../config/settings");
|
|
9
11
|
const filenamify_1 = require("./filenamify");
|
|
10
12
|
const file_utils_1 = require("./file-utils");
|
|
11
13
|
exports.INHERITED_BLOCK_START = '# BEGIN fcode inherited resources (auto-generated, do not edit)';
|
|
@@ -43,9 +45,12 @@ function stripManagedBlock(content) {
|
|
|
43
45
|
* Refreshes the workspace .gitignore so inherited processes/modules (owned by a
|
|
44
46
|
* parent team and pulled read-only) are not tracked by git. Regenerated from the
|
|
45
47
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
48
|
+
* Inherited variable values live in one file, which is ignored as soon as the
|
|
49
|
+
* workspace inherits any variable.
|
|
46
50
|
*/
|
|
47
51
|
async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
48
52
|
const team = await new workspace_1.default(workspaceDir).getActiveTeam();
|
|
53
|
+
const inheritedVariablesFileName = await new global_1.default().getSetting(settings_1.SettingKeys.INHERITED_VARIABLES_FILE_NAME);
|
|
49
54
|
const inheritedPaths = [
|
|
50
55
|
...Object.values(team.processes)
|
|
51
56
|
.filter((process) => process.resolvingTeamSlug)
|
|
@@ -53,6 +58,7 @@ async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
|
53
58
|
...Object.values(team.modules)
|
|
54
59
|
.filter((module) => module.resolvingTeamSlug)
|
|
55
60
|
.map((module) => `modules/${(0, filenamify_1.default)(module.name)}/`),
|
|
61
|
+
...(Object.values(team.variables ?? {}).some((variable) => variable.resolvingTeamSlug) ? [inheritedVariablesFileName] : []),
|
|
56
62
|
];
|
|
57
63
|
const gitignorePath = path.join(workspaceDir, '.gitignore');
|
|
58
64
|
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf8') : '';
|