@faable/faable 1.30.0 → 1.31.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/dist/api/FaableApi.js
CHANGED
|
@@ -12,6 +12,20 @@ const firstPage = async (res) => {
|
|
|
12
12
|
const items = (await res).results;
|
|
13
13
|
return items;
|
|
14
14
|
};
|
|
15
|
+
// Walk the cursor to exhaustion. `list()` needs this instead of `firstPage`:
|
|
16
|
+
// a user who sees many apps (admins see all of them) gets a multi-page
|
|
17
|
+
// listing, and matching by repository against a truncated first page made
|
|
18
|
+
// every repo-resolved command answer "No app linked to this repository".
|
|
19
|
+
const allPages = async (fetch_page) => {
|
|
20
|
+
const items = [];
|
|
21
|
+
let next;
|
|
22
|
+
do {
|
|
23
|
+
const page = await fetch_page(next);
|
|
24
|
+
items.push(...page.results);
|
|
25
|
+
next = page.next ?? undefined;
|
|
26
|
+
} while (next);
|
|
27
|
+
return items;
|
|
28
|
+
};
|
|
15
29
|
const data = async (res) => {
|
|
16
30
|
const items = (await res).data;
|
|
17
31
|
return items;
|
|
@@ -84,7 +98,9 @@ class FaableApi {
|
|
|
84
98
|
return new FaableApi(config);
|
|
85
99
|
}
|
|
86
100
|
async list() {
|
|
87
|
-
return
|
|
101
|
+
return allPages((next) => data(this.client.get(`/app`, {
|
|
102
|
+
params: { pageSize: 200, ...(next ? { next } : {}) },
|
|
103
|
+
})));
|
|
88
104
|
}
|
|
89
105
|
async getBySlug(slug) {
|
|
90
106
|
return data(this.client.get(`/app/slug/${slug}`));
|
|
@@ -19,7 +19,9 @@ import { is_superseded } from './superseded.js';
|
|
|
19
19
|
|
|
20
20
|
const deploy = {
|
|
21
21
|
command: 'deploy [app_id]',
|
|
22
|
-
|
|
22
|
+
// Name the subcommand groups so `faable --help` makes them discoverable
|
|
23
|
+
// without digging into `faable deploy --help`.
|
|
24
|
+
describe: 'Deploy a faable app and manage it (secrets, domains, logs, deployments…)',
|
|
23
25
|
builder: yargs => {
|
|
24
26
|
// Product subcommands live under `deploy` (yargs matches them before the
|
|
25
27
|
// app_id positional, so `faable deploy <app_id>` keeps working).
|
|
@@ -5,10 +5,11 @@ import { log } from '../../log.js';
|
|
|
5
5
|
// app_id resolution (the user never has to look one up):
|
|
6
6
|
// 1. explicit (positional on `deploy`, --app on subcommands)
|
|
7
7
|
// 2. OIDC in CI — the backend resolves the app from the linked repository
|
|
8
|
-
// 3. locally —
|
|
8
|
+
// 3. locally — a legacy app_id in faable.json (older CLIs wrote it on
|
|
9
|
+
// `faable deploy link`; the current link only persists in the API)
|
|
9
10
|
// 4. locally — the app whose linked repository matches the git origin remote
|
|
10
11
|
// of the working directory (repos are connected in the dashboard when the
|
|
11
|
-
// app is created,
|
|
12
|
+
// app is created, or via `faable deploy link`)
|
|
12
13
|
const resolve_app_id = async (explicit, ctxAppId, api, workdir = process.cwd()) => {
|
|
13
14
|
const app_id = explicit || ctxAppId || Configuration.instance().app_id;
|
|
14
15
|
if (app_id)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { requireApi } from '../../api/context.js';
|
|
2
2
|
import prompts from 'prompts';
|
|
3
3
|
import { log } from '../../log.js';
|
|
4
|
-
import { Configuration } from '../../lib/Configuration.js';
|
|
5
4
|
import { getGitRemoteUrl } from '../../lib/git_remote.js';
|
|
6
5
|
|
|
7
6
|
const DEPLOY_DOCS_URL = "https://faable.com/docs/deploy/github-actions";
|
|
@@ -27,9 +26,27 @@ const link = {
|
|
|
27
26
|
log.warn('Passing an app_id to "faable link" is deprecated and ignored. ' +
|
|
28
27
|
'Just run "faable link" and select the app from the list.');
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
log.info("Checking local git repository...");
|
|
30
|
+
const gitUrl = await getGitRemoteUrl(workdir);
|
|
31
|
+
if (!gitUrl) {
|
|
32
|
+
log.error("No git remote URL detected. Add a GitHub 'origin' remote and try again.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const { api } = await requireApi();
|
|
36
|
+
const apps = await api.list();
|
|
37
|
+
if (apps.length === 0) {
|
|
38
|
+
log.error("No apps found in your account. Create one first at https://faable.com");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// The link lives in the API (app.repository) — every command resolves the
|
|
42
|
+
// app from the git remote, so nothing is written locally. Warn (and
|
|
43
|
+
// confirm) when the repository is already linked to an app.
|
|
44
|
+
const alreadyLinked = apps.filter((app) => app.repository === gitUrl);
|
|
45
|
+
if (alreadyLinked.length > 0) {
|
|
46
|
+
const names = alreadyLinked
|
|
47
|
+
.map((app) => `"${app.name}" (${app.id})`)
|
|
48
|
+
.join(", ");
|
|
49
|
+
log.info(`This repository is already linked to: ${names}`);
|
|
33
50
|
const { relink } = await prompts({
|
|
34
51
|
type: "toggle",
|
|
35
52
|
name: "relink",
|
|
@@ -42,14 +59,6 @@ const link = {
|
|
|
42
59
|
return;
|
|
43
60
|
}
|
|
44
61
|
}
|
|
45
|
-
const { api } = await requireApi();
|
|
46
|
-
log.info("Checking local git repository...");
|
|
47
|
-
const gitUrl = await getGitRemoteUrl(workdir);
|
|
48
|
-
const apps = await api.list();
|
|
49
|
-
if (apps.length === 0) {
|
|
50
|
-
log.error("No apps found in your account. Create one first at https://faable.com");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
62
|
const { selectedApp } = await prompts({
|
|
54
63
|
type: "select",
|
|
55
64
|
name: "selectedApp",
|
|
@@ -64,10 +73,6 @@ const link = {
|
|
|
64
73
|
return;
|
|
65
74
|
}
|
|
66
75
|
log.info(`Linking to "${selectedApp.name}" (${selectedApp.id})...`);
|
|
67
|
-
if (!gitUrl) {
|
|
68
|
-
log.error("No git remote URL detected. Add a GitHub 'origin' remote and try again.");
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
76
|
// The API verifies that the user has a connected GitHub identity AND
|
|
72
77
|
// access to the repository before persisting the link.
|
|
73
78
|
let linked;
|
|
@@ -95,9 +100,6 @@ const link = {
|
|
|
95
100
|
return;
|
|
96
101
|
}
|
|
97
102
|
log.info(`Linked repository ${gitUrl} to ${selectedApp.name}.`);
|
|
98
|
-
// Save locally for CLI convenience (only after the API confirms the link)
|
|
99
|
-
Configuration.instance().saveConfig({ app_slug: selectedApp.name, app_id: selectedApp.id });
|
|
100
|
-
log.info(`Successfully linked local repository to ${selectedApp.name}.`);
|
|
101
103
|
// Deploy v4: push-to-deploy is server-side by default — no workflow to
|
|
102
104
|
// scaffold. A repo that brings its own Faable workflow keeps deploying
|
|
103
105
|
// through it (the API leaves the trigger on the Action in that case).
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,10 @@ yg.scriptName('faable')
|
|
|
50
50
|
.command(upgrade)
|
|
51
51
|
.command(link_deprecated)
|
|
52
52
|
.demandCommand(1)
|
|
53
|
+
// Reject unknown (sub)commands loudly. Without this, a typo'd or
|
|
54
|
+
// not-yet-existing command (`faable secrets list` on a version where it
|
|
55
|
+
// lived elsewhere) exited silently with just the version banner.
|
|
56
|
+
.strictCommands()
|
|
53
57
|
.help()
|
|
54
58
|
.fail(function (msg, err) {
|
|
55
59
|
if (err) {
|
|
@@ -58,8 +62,11 @@ yg.scriptName('faable')
|
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
60
64
|
if (msg) {
|
|
65
|
+
// Validation failure (unknown command, missing subcommand…): show the
|
|
66
|
+
// help, then fail red — a bad invocation must not exit 0.
|
|
61
67
|
yg.showHelp();
|
|
62
|
-
log.
|
|
68
|
+
log.error(`❌ ${msg}`);
|
|
69
|
+
process.exit(1);
|
|
63
70
|
}
|
|
64
71
|
})
|
|
65
72
|
.parse(hideBin(process.argv), {});
|
|
@@ -23,12 +23,6 @@ class Configuration {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
saveConfig(updates) {
|
|
27
|
-
this.config = { ...this.config, ...updates };
|
|
28
|
-
const config_path = path__default.join(process.cwd(), this.config_file);
|
|
29
|
-
fs.writeJSONSync(config_path, this.config, { spaces: 2 });
|
|
30
|
-
log.info(`Configuration saved to: ${this.config_file}`);
|
|
31
|
-
}
|
|
32
26
|
static instance() {
|
|
33
27
|
if (!Configuration._instance) {
|
|
34
28
|
Configuration._instance = new Configuration();
|
|
@@ -58,9 +52,6 @@ class Configuration {
|
|
|
58
52
|
next: this.config.next,
|
|
59
53
|
};
|
|
60
54
|
}
|
|
61
|
-
get app_slug() {
|
|
62
|
-
return this.config.app_slug;
|
|
63
|
-
}
|
|
64
55
|
get app_id() {
|
|
65
56
|
return this.config.app_id;
|
|
66
57
|
}
|