appwrite-utils-cli 1.8.8 → 1.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -25,3 +25,11 @@ All notable changes to this project will be documented in this file.
|
|
|
25
25
|
- Enhancement: Interactive selection adds “Use same selection as before” when choosing tables per database, speeding up multi-DB workflows.
|
|
26
26
|
- Behavior: Non-interactive `--push` with both `--dbIds` and `--collectionIds` is fully non-interactive. The provided IDs are applied as-is to every selected database and the confirmation summary is skipped.
|
|
27
27
|
- Improvement: Selective push reloads local config from disk before pushing to ensure the latest YAML/TS changes are used.
|
|
28
|
+
- Fix: Sync-from-Appwrite now captures function `scopes` and writes them to config; deployments use `scopes` from either central config or `.fnconfig.yaml`.
|
|
29
|
+
- Change: Removed global prompt to choose between central `config.yaml` and per-function `.fnconfig.yaml` during function deployments. The tool now prompts per function only when both sources exist for that function (with an option to merge where `.fnconfig` overrides).
|
|
30
|
+
|
|
31
|
+
## 1.8.9 - 2025-10-29
|
|
32
|
+
|
|
33
|
+
- Fix: Functions sync now fetches full details via `getFunction` per function to reliably capture `scopes` (prevents empty scopes in some environments).
|
|
34
|
+
- Confirmed schema: YAML `appwrite-config.schema.json` supports `functions[].dirPath`; writers preserve `dirPath` when present.
|
|
35
|
+
- Polish: Per-function config source selection is now applied at deploy time when both central and `.fnconfig.yaml` are present.
|
|
@@ -111,38 +111,14 @@ export const functionCommands = {
|
|
|
111
111
|
MessageFormatter.error("Failed to initialize controller or load config", undefined, { prefix: "Functions" });
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
|
-
//
|
|
115
|
-
|
|
114
|
+
// Discover per-function .fnconfig.yaml definitions and merge with central list for selection
|
|
115
|
+
// No global prompt; we'll handle conflicts per-function if both exist.
|
|
116
|
+
let discovered = [];
|
|
117
|
+
let central = cli.controller.config.functions || [];
|
|
116
118
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
name: 'source',
|
|
121
|
-
message: 'Select function config source:',
|
|
122
|
-
choices: [
|
|
123
|
-
{ name: 'config.yaml functions (central)', value: 'central' },
|
|
124
|
-
{ name: '.fnconfig.yaml (discovered per-function)', value: 'fnconfig' },
|
|
125
|
-
{ name: 'Both (merge; .fnconfig overrides)', value: 'both' },
|
|
126
|
-
],
|
|
127
|
-
default: 'both'
|
|
128
|
-
}
|
|
129
|
-
]);
|
|
130
|
-
sourceChoice = answer.source;
|
|
131
|
-
}
|
|
132
|
-
catch { }
|
|
133
|
-
try {
|
|
134
|
-
const discovered = discoverFnConfigs(cli.currentDir);
|
|
135
|
-
const central = cli.controller.config.functions || [];
|
|
136
|
-
if (sourceChoice === 'central') {
|
|
137
|
-
cli.controller.config.functions = central;
|
|
138
|
-
}
|
|
139
|
-
else if (sourceChoice === 'fnconfig') {
|
|
140
|
-
cli.controller.config.functions = discovered;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
const merged = mergeDiscoveredFunctions(central, discovered);
|
|
144
|
-
cli.controller.config.functions = merged;
|
|
145
|
-
}
|
|
119
|
+
discovered = discoverFnConfigs(cli.currentDir);
|
|
120
|
+
const merged = mergeDiscoveredFunctions(central, discovered);
|
|
121
|
+
cli.controller.config.functions = merged;
|
|
146
122
|
}
|
|
147
123
|
catch { }
|
|
148
124
|
const functions = await cli.selectFunctions("Select function(s) to deploy:", true, true);
|
|
@@ -155,22 +131,51 @@ export const functionCommands = {
|
|
|
155
131
|
MessageFormatter.error("Invalid function configuration", undefined, { prefix: "Functions" });
|
|
156
132
|
return;
|
|
157
133
|
}
|
|
134
|
+
// Resolve effective config for this function (prefer per-function choice if both sources exist)
|
|
135
|
+
const byIdOrName = (arr) => arr.find((f) => f?.$id === functionConfig.$id || f?.name === functionConfig.name);
|
|
136
|
+
const centralDef = byIdOrName(central);
|
|
137
|
+
const discoveredDef = byIdOrName(discovered);
|
|
138
|
+
let effectiveConfig = functionConfig;
|
|
139
|
+
if (centralDef && discoveredDef) {
|
|
140
|
+
try {
|
|
141
|
+
const answer = await inquirer.prompt([
|
|
142
|
+
{
|
|
143
|
+
type: 'list',
|
|
144
|
+
name: 'cfgChoice',
|
|
145
|
+
message: `Multiple configs found for '${functionConfig.name}'. Which to use?`,
|
|
146
|
+
choices: [
|
|
147
|
+
{ name: 'config.yaml (central)', value: 'central' },
|
|
148
|
+
{ name: '.fnconfig.yaml (local file)', value: 'fnconfig' },
|
|
149
|
+
{ name: 'Merge (.fnconfig overrides central)', value: 'merge' },
|
|
150
|
+
],
|
|
151
|
+
default: 'fnconfig'
|
|
152
|
+
}
|
|
153
|
+
]);
|
|
154
|
+
if (answer.cfgChoice === 'central')
|
|
155
|
+
effectiveConfig = centralDef;
|
|
156
|
+
else if (answer.cfgChoice === 'fnconfig')
|
|
157
|
+
effectiveConfig = discoveredDef;
|
|
158
|
+
else
|
|
159
|
+
effectiveConfig = { ...centralDef, ...discoveredDef };
|
|
160
|
+
}
|
|
161
|
+
catch { }
|
|
162
|
+
}
|
|
158
163
|
// Ensure functions array exists
|
|
159
164
|
if (!cli.controller.config.functions) {
|
|
160
165
|
cli.controller.config.functions = [];
|
|
161
166
|
}
|
|
162
|
-
const functionNameLower =
|
|
167
|
+
const functionNameLower = effectiveConfig.name
|
|
163
168
|
.toLowerCase()
|
|
164
169
|
.replace(/\s+/g, "-");
|
|
165
170
|
// Debug logging
|
|
166
171
|
MessageFormatter.info(`🔍 Function deployment debug:`, { prefix: "Functions" });
|
|
167
|
-
MessageFormatter.info(` Function name: ${
|
|
168
|
-
MessageFormatter.info(` Function ID: ${
|
|
169
|
-
MessageFormatter.info(` Config dirPath: ${
|
|
170
|
-
if (
|
|
171
|
-
const expandedPath =
|
|
172
|
-
?
|
|
173
|
-
:
|
|
172
|
+
MessageFormatter.info(` Function name: ${effectiveConfig.name}`, { prefix: "Functions" });
|
|
173
|
+
MessageFormatter.info(` Function ID: ${effectiveConfig.$id}`, { prefix: "Functions" });
|
|
174
|
+
MessageFormatter.info(` Config dirPath: ${effectiveConfig.dirPath || 'undefined'}`, { prefix: "Functions" });
|
|
175
|
+
if (effectiveConfig.dirPath) {
|
|
176
|
+
const expandedPath = effectiveConfig.dirPath.startsWith('~/')
|
|
177
|
+
? effectiveConfig.dirPath.replace('~', os.homedir())
|
|
178
|
+
: effectiveConfig.dirPath;
|
|
174
179
|
MessageFormatter.info(` Expanded dirPath: ${expandedPath}`, { prefix: "Functions" });
|
|
175
180
|
}
|
|
176
181
|
MessageFormatter.info(` Appwrite folder: ${cli.controller.getAppwriteFolderPath()}`, { prefix: "Functions" });
|
|
@@ -182,10 +187,10 @@ export const functionCommands = {
|
|
|
182
187
|
// Check locations in priority order:
|
|
183
188
|
const priorityLocations = [
|
|
184
189
|
// 1. Config dirPath if specified (with tilde expansion)
|
|
185
|
-
|
|
186
|
-
? (require('node:path').isAbsolute(expandTildePath(
|
|
187
|
-
? expandTildePath(
|
|
188
|
-
: require('node:path').resolve(yamlBaseDir, expandTildePath(
|
|
190
|
+
effectiveConfig.dirPath
|
|
191
|
+
? (require('node:path').isAbsolute(expandTildePath(effectiveConfig.dirPath))
|
|
192
|
+
? expandTildePath(effectiveConfig.dirPath)
|
|
193
|
+
: require('node:path').resolve(yamlBaseDir, expandTildePath(effectiveConfig.dirPath)))
|
|
189
194
|
: undefined,
|
|
190
195
|
// 2. Appwrite config folder/functions/name
|
|
191
196
|
join(cli.controller.getAppwriteFolderPath(), "functions", functionNameLower),
|
|
@@ -226,10 +231,10 @@ export const functionCommands = {
|
|
|
226
231
|
if (shouldDownload) {
|
|
227
232
|
try {
|
|
228
233
|
MessageFormatter.progress("Downloading latest deployment...", { prefix: "Functions" });
|
|
229
|
-
const { path: downloadedPath, function: remoteFunction } = await downloadLatestFunctionDeployment(cli.controller.appwriteServer,
|
|
234
|
+
const { path: downloadedPath, function: remoteFunction } = await downloadLatestFunctionDeployment(cli.controller.appwriteServer, effectiveConfig.$id, join(cli.controller.getAppwriteFolderPath(), "functions"));
|
|
230
235
|
MessageFormatter.success(`✨ Function downloaded to ${downloadedPath}`, { prefix: "Functions" });
|
|
231
236
|
functionPath = downloadedPath;
|
|
232
|
-
|
|
237
|
+
effectiveConfig.dirPath = downloadedPath;
|
|
233
238
|
const existingIndex = cli.controller.config.functions.findIndex((f) => f?.$id === remoteFunction.$id);
|
|
234
239
|
if (existingIndex >= 0) {
|
|
235
240
|
cli.controller.config.functions[existingIndex].dirPath =
|
|
@@ -243,7 +248,7 @@ export const functionCommands = {
|
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
else {
|
|
246
|
-
MessageFormatter.error(`Function ${
|
|
251
|
+
MessageFormatter.error(`Function ${effectiveConfig.name} not found locally. Cannot deploy.`, undefined, { prefix: "Functions" });
|
|
247
252
|
return;
|
|
248
253
|
}
|
|
249
254
|
}
|
|
@@ -252,8 +257,8 @@ export const functionCommands = {
|
|
|
252
257
|
return;
|
|
253
258
|
}
|
|
254
259
|
try {
|
|
255
|
-
await deployLocalFunction(cli.controller.appwriteServer,
|
|
256
|
-
...
|
|
260
|
+
await deployLocalFunction(cli.controller.appwriteServer, effectiveConfig.name, {
|
|
261
|
+
...effectiveConfig,
|
|
257
262
|
dirPath: functionPath,
|
|
258
263
|
}, functionPath);
|
|
259
264
|
MessageFormatter.success("Function deployed successfully!", { prefix: "Functions" });
|
|
@@ -7,7 +7,7 @@ import { CollectionSchema, attributeSchema, AppwriteConfigSchema, permissionsSch
|
|
|
7
7
|
import { getDatabaseFromConfig } from "./afterImportActions.js";
|
|
8
8
|
import { getAdapterFromConfig } from "../utils/getClientFromConfig.js";
|
|
9
9
|
import { listBuckets } from "../storage/methods.js";
|
|
10
|
-
import { listFunctions, listFunctionDeployments } from "../functions/methods.js";
|
|
10
|
+
import { listFunctions, listFunctionDeployments, getFunction } from "../functions/methods.js";
|
|
11
11
|
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
12
12
|
import { isLegacyDatabases } from "../utils/typeGuards.js";
|
|
13
13
|
/**
|
|
@@ -420,7 +420,18 @@ export class AppwriteToX {
|
|
|
420
420
|
const remoteFunctions = await listFunctions(this.config.appwriteClient, [
|
|
421
421
|
Query.limit(1000),
|
|
422
422
|
]);
|
|
423
|
-
|
|
423
|
+
// Fetch full details per function to ensure 'scopes' and other fields are present
|
|
424
|
+
const detailedFunctions = [];
|
|
425
|
+
for (const f of remoteFunctions.functions) {
|
|
426
|
+
try {
|
|
427
|
+
const full = await getFunction(this.config.appwriteClient, f.$id);
|
|
428
|
+
detailedFunctions.push(full);
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
detailedFunctions.push(f);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
this.updatedConfig.functions = detailedFunctions.map((func) => ({
|
|
424
435
|
$id: func.$id,
|
|
425
436
|
name: func.name,
|
|
426
437
|
runtime: func.runtime,
|
|
@@ -432,6 +443,7 @@ export class AppwriteToX {
|
|
|
432
443
|
logging: func.logging !== false,
|
|
433
444
|
entrypoint: func.entrypoint || "src/index.ts",
|
|
434
445
|
commands: func.commands || "npm install",
|
|
446
|
+
scopes: Array.isArray(func.scopes) ? func.scopes : [],
|
|
435
447
|
dirPath: `functions/${func.name}`,
|
|
436
448
|
specification: func.specification,
|
|
437
449
|
}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appwrite-utils-cli",
|
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.9",
|
|
5
5
|
"main": "src/main.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -138,36 +138,14 @@ export const functionCommands = {
|
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
//
|
|
142
|
-
|
|
141
|
+
// Discover per-function .fnconfig.yaml definitions and merge with central list for selection
|
|
142
|
+
// No global prompt; we'll handle conflicts per-function if both exist.
|
|
143
|
+
let discovered: any[] = [];
|
|
144
|
+
let central: any[] = (cli as any).controller!.config!.functions || [];
|
|
143
145
|
try {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
name: 'source',
|
|
148
|
-
message: 'Select function config source:',
|
|
149
|
-
choices: [
|
|
150
|
-
{ name: 'config.yaml functions (central)', value: 'central' },
|
|
151
|
-
{ name: '.fnconfig.yaml (discovered per-function)', value: 'fnconfig' },
|
|
152
|
-
{ name: 'Both (merge; .fnconfig overrides)', value: 'both' },
|
|
153
|
-
],
|
|
154
|
-
default: 'both'
|
|
155
|
-
}
|
|
156
|
-
]);
|
|
157
|
-
sourceChoice = answer.source;
|
|
158
|
-
} catch {}
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const discovered = discoverFnConfigs((cli as any).currentDir);
|
|
162
|
-
const central = (cli as any).controller!.config!.functions || [];
|
|
163
|
-
if (sourceChoice === 'central') {
|
|
164
|
-
(cli as any).controller!.config!.functions = central as any;
|
|
165
|
-
} else if (sourceChoice === 'fnconfig') {
|
|
166
|
-
(cli as any).controller!.config!.functions = discovered as any;
|
|
167
|
-
} else {
|
|
168
|
-
const merged = mergeDiscoveredFunctions(central, discovered);
|
|
169
|
-
(cli as any).controller!.config!.functions = merged as any;
|
|
170
|
-
}
|
|
146
|
+
discovered = discoverFnConfigs((cli as any).currentDir) as any[];
|
|
147
|
+
const merged = mergeDiscoveredFunctions(central as any, discovered as any);
|
|
148
|
+
(cli as any).controller!.config!.functions = merged as any;
|
|
171
149
|
} catch {}
|
|
172
150
|
|
|
173
151
|
const functions = await (cli as any).selectFunctions(
|
|
@@ -181,32 +159,59 @@ export const functionCommands = {
|
|
|
181
159
|
return;
|
|
182
160
|
}
|
|
183
161
|
|
|
184
|
-
for (const functionConfig of functions) {
|
|
162
|
+
for (const functionConfig of functions) {
|
|
185
163
|
if (!functionConfig) {
|
|
186
164
|
MessageFormatter.error("Invalid function configuration", undefined, { prefix: "Functions" });
|
|
187
165
|
return;
|
|
188
166
|
}
|
|
189
167
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
168
|
+
// Resolve effective config for this function (prefer per-function choice if both sources exist)
|
|
169
|
+
const byIdOrName = (arr: any[]) => arr.find((f:any) => f?.$id === functionConfig.$id || f?.name === functionConfig.name);
|
|
170
|
+
const centralDef = byIdOrName(central as any[]);
|
|
171
|
+
const discoveredDef = byIdOrName(discovered as any[]);
|
|
172
|
+
|
|
173
|
+
let effectiveConfig = functionConfig;
|
|
174
|
+
if (centralDef && discoveredDef) {
|
|
175
|
+
try {
|
|
176
|
+
const answer = await inquirer.prompt([
|
|
177
|
+
{
|
|
178
|
+
type: 'list',
|
|
179
|
+
name: 'cfgChoice',
|
|
180
|
+
message: `Multiple configs found for '${functionConfig.name}'. Which to use?`,
|
|
181
|
+
choices: [
|
|
182
|
+
{ name: 'config.yaml (central)', value: 'central' },
|
|
183
|
+
{ name: '.fnconfig.yaml (local file)', value: 'fnconfig' },
|
|
184
|
+
{ name: 'Merge (.fnconfig overrides central)', value: 'merge' },
|
|
185
|
+
],
|
|
186
|
+
default: 'fnconfig'
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
if (answer.cfgChoice === 'central') effectiveConfig = centralDef;
|
|
190
|
+
else if (answer.cfgChoice === 'fnconfig') effectiveConfig = discoveredDef;
|
|
191
|
+
else effectiveConfig = { ...centralDef, ...discoveredDef };
|
|
192
|
+
} catch {}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Ensure functions array exists
|
|
196
|
+
if (!(cli as any).controller.config.functions) {
|
|
197
|
+
(cli as any).controller.config.functions = [];
|
|
198
|
+
}
|
|
194
199
|
|
|
195
|
-
const functionNameLower =
|
|
196
|
-
.toLowerCase()
|
|
197
|
-
.replace(/\s+/g, "-");
|
|
200
|
+
const functionNameLower = effectiveConfig.name
|
|
201
|
+
.toLowerCase()
|
|
202
|
+
.replace(/\s+/g, "-");
|
|
198
203
|
|
|
199
204
|
// Debug logging
|
|
200
205
|
MessageFormatter.info(`🔍 Function deployment debug:`, { prefix: "Functions" });
|
|
201
|
-
MessageFormatter.info(` Function name: ${
|
|
202
|
-
MessageFormatter.info(` Function ID: ${
|
|
203
|
-
MessageFormatter.info(` Config dirPath: ${
|
|
204
|
-
if (
|
|
205
|
-
const expandedPath =
|
|
206
|
-
?
|
|
207
|
-
:
|
|
208
|
-
MessageFormatter.info(` Expanded dirPath: ${expandedPath}`, { prefix: "Functions" });
|
|
209
|
-
}
|
|
206
|
+
MessageFormatter.info(` Function name: ${effectiveConfig.name}`, { prefix: "Functions" });
|
|
207
|
+
MessageFormatter.info(` Function ID: ${effectiveConfig.$id}`, { prefix: "Functions" });
|
|
208
|
+
MessageFormatter.info(` Config dirPath: ${effectiveConfig.dirPath || 'undefined'}`, { prefix: "Functions" });
|
|
209
|
+
if (effectiveConfig.dirPath) {
|
|
210
|
+
const expandedPath = effectiveConfig.dirPath.startsWith('~/')
|
|
211
|
+
? effectiveConfig.dirPath.replace('~', os.homedir())
|
|
212
|
+
: effectiveConfig.dirPath;
|
|
213
|
+
MessageFormatter.info(` Expanded dirPath: ${expandedPath}`, { prefix: "Functions" });
|
|
214
|
+
}
|
|
210
215
|
MessageFormatter.info(` Appwrite folder: ${(cli as any).controller.getAppwriteFolderPath()}`, { prefix: "Functions" });
|
|
211
216
|
MessageFormatter.info(` Current working dir: ${process.cwd()}`, { prefix: "Functions" });
|
|
212
217
|
|
|
@@ -218,10 +223,10 @@ export const functionCommands = {
|
|
|
218
223
|
// Check locations in priority order:
|
|
219
224
|
const priorityLocations = [
|
|
220
225
|
// 1. Config dirPath if specified (with tilde expansion)
|
|
221
|
-
|
|
222
|
-
? (require('node:path').isAbsolute(expandTildePath(
|
|
223
|
-
? expandTildePath(
|
|
224
|
-
: require('node:path').resolve(yamlBaseDir, expandTildePath(
|
|
226
|
+
effectiveConfig.dirPath
|
|
227
|
+
? (require('node:path').isAbsolute(expandTildePath(effectiveConfig.dirPath))
|
|
228
|
+
? expandTildePath(effectiveConfig.dirPath)
|
|
229
|
+
: require('node:path').resolve(yamlBaseDir, expandTildePath(effectiveConfig.dirPath)))
|
|
225
230
|
: undefined,
|
|
226
231
|
// 2. Appwrite config folder/functions/name
|
|
227
232
|
join(
|
|
@@ -240,7 +245,7 @@ export const functionCommands = {
|
|
|
240
245
|
MessageFormatter.info(` ${i + 1}. ${loc}`, { prefix: "Functions" });
|
|
241
246
|
});
|
|
242
247
|
|
|
243
|
-
let functionPath: string | null = null;
|
|
248
|
+
let functionPath: string | null = null;
|
|
244
249
|
|
|
245
250
|
// Check each priority location
|
|
246
251
|
for (const location of priorityLocations) {
|
|
@@ -280,21 +285,21 @@ export const functionCommands = {
|
|
|
280
285
|
const { path: downloadedPath, function: remoteFunction } =
|
|
281
286
|
await downloadLatestFunctionDeployment(
|
|
282
287
|
(cli as any).controller.appwriteServer!,
|
|
283
|
-
|
|
284
|
-
join((cli as any).controller.getAppwriteFolderPath()!, "functions")
|
|
285
|
-
);
|
|
288
|
+
effectiveConfig.$id,
|
|
289
|
+
join((cli as any).controller.getAppwriteFolderPath()!, "functions")
|
|
290
|
+
);
|
|
286
291
|
MessageFormatter.success(`✨ Function downloaded to ${downloadedPath}`, { prefix: "Functions" });
|
|
287
292
|
|
|
288
293
|
functionPath = downloadedPath;
|
|
289
|
-
|
|
294
|
+
effectiveConfig.dirPath = downloadedPath;
|
|
290
295
|
|
|
291
296
|
const existingIndex = (cli as any).controller.config.functions.findIndex(
|
|
292
297
|
(f: any) => f?.$id === remoteFunction.$id
|
|
293
298
|
);
|
|
294
299
|
|
|
295
300
|
if (existingIndex >= 0) {
|
|
296
|
-
(cli as any).controller.config.functions[existingIndex].dirPath =
|
|
297
|
-
downloadedPath;
|
|
301
|
+
(cli as any).controller.config.functions[existingIndex].dirPath =
|
|
302
|
+
downloadedPath;
|
|
298
303
|
}
|
|
299
304
|
|
|
300
305
|
await (cli as any).reloadConfigWithSessionPreservation();
|
|
@@ -303,9 +308,9 @@ export const functionCommands = {
|
|
|
303
308
|
return;
|
|
304
309
|
}
|
|
305
310
|
} else {
|
|
306
|
-
MessageFormatter.error(`Function ${
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
311
|
+
MessageFormatter.error(`Function ${effectiveConfig.name} not found locally. Cannot deploy.`, undefined, { prefix: "Functions" });
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
if (!(cli as any).controller.appwriteServer) {
|
|
@@ -316,13 +321,13 @@ export const functionCommands = {
|
|
|
316
321
|
try {
|
|
317
322
|
await deployLocalFunction(
|
|
318
323
|
(cli as any).controller.appwriteServer,
|
|
319
|
-
|
|
320
|
-
{
|
|
321
|
-
...
|
|
322
|
-
dirPath: functionPath,
|
|
323
|
-
},
|
|
324
|
-
functionPath
|
|
325
|
-
);
|
|
324
|
+
effectiveConfig.name,
|
|
325
|
+
{
|
|
326
|
+
...effectiveConfig,
|
|
327
|
+
dirPath: functionPath,
|
|
328
|
+
},
|
|
329
|
+
functionPath
|
|
330
|
+
);
|
|
326
331
|
MessageFormatter.success("Function deployed successfully!", { prefix: "Functions" });
|
|
327
332
|
} catch (error) {
|
|
328
333
|
MessageFormatter.error("Failed to deploy function", error instanceof Error ? error : new Error(String(error)), { prefix: "Functions" });
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { getDatabaseFromConfig } from "./afterImportActions.js";
|
|
29
29
|
import { getAdapterFromConfig } from "../utils/getClientFromConfig.js";
|
|
30
30
|
import { listBuckets } from "../storage/methods.js";
|
|
31
|
-
import { listFunctions, listFunctionDeployments } from "../functions/methods.js";
|
|
31
|
+
import { listFunctions, listFunctionDeployments, getFunction } from "../functions/methods.js";
|
|
32
32
|
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
33
33
|
import { isLegacyDatabases } from "../utils/typeGuards.js";
|
|
34
34
|
import type { DatabaseAdapter } from "../adapters/DatabaseAdapter.js";
|
|
@@ -570,27 +570,39 @@ export class AppwriteToX {
|
|
|
570
570
|
antivirus: bucket.antivirus,
|
|
571
571
|
}));
|
|
572
572
|
|
|
573
|
-
const remoteFunctions = await listFunctions(this.config.appwriteClient!, [
|
|
574
|
-
Query.limit(1000),
|
|
575
|
-
]);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
573
|
+
const remoteFunctions = await listFunctions(this.config.appwriteClient!, [
|
|
574
|
+
Query.limit(1000),
|
|
575
|
+
]);
|
|
576
|
+
|
|
577
|
+
// Fetch full details per function to ensure 'scopes' and other fields are present
|
|
578
|
+
const detailedFunctions: any[] = [];
|
|
579
|
+
for (const f of remoteFunctions.functions) {
|
|
580
|
+
try {
|
|
581
|
+
const full = await getFunction(this.config.appwriteClient!, f.$id);
|
|
582
|
+
detailedFunctions.push(full);
|
|
583
|
+
} catch {
|
|
584
|
+
detailedFunctions.push(f);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
this.updatedConfig.functions = detailedFunctions.map(
|
|
589
|
+
(func: any) => ({
|
|
590
|
+
$id: func.$id,
|
|
591
|
+
name: func.name,
|
|
592
|
+
runtime: func.runtime as Runtime,
|
|
593
|
+
execute: func.execute,
|
|
594
|
+
events: func.events || [],
|
|
595
|
+
schedule: func.schedule || "",
|
|
596
|
+
timeout: func.timeout || 15,
|
|
597
|
+
enabled: func.enabled !== false,
|
|
598
|
+
logging: func.logging !== false,
|
|
599
|
+
entrypoint: func.entrypoint || "src/index.ts",
|
|
600
|
+
commands: func.commands || "npm install",
|
|
601
|
+
scopes: Array.isArray(func.scopes) ? func.scopes : [],
|
|
602
|
+
dirPath: `functions/${func.name}`,
|
|
603
|
+
specification: func.specification as Specification,
|
|
604
|
+
})
|
|
605
|
+
);
|
|
594
606
|
|
|
595
607
|
// Make sure to update the config with all changes including databases
|
|
596
608
|
updatedConfig.functions = this.updatedConfig.functions;
|