@faable/faable 1.5.29 → 1.5.30

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.
@@ -7,12 +7,8 @@ import { CredentialsStore } from '../lib/CredentialsStore.js';
7
7
 
8
8
  const context = async () => {
9
9
  let api;
10
- if (process.env.FAABLE_APIKEY) {
11
- // Apikey in environment
12
- const apikey = process.env.FAABLE_APIKEY;
13
- api = FaableApi.create({ authStrategy: apikey_strategy, auth: { apikey } });
14
- }
15
- else if (process.env.FAABLE_TOKEN) {
10
+ // Auth resolution: FAABLE_TOKEN → OIDC (CI) → local `faable login` credentials.
11
+ if (process.env.FAABLE_TOKEN) {
16
12
  // Token in environment
17
13
  const token = process.env.FAABLE_TOKEN;
18
14
  api = FaableApi.create({ authStrategy: bearer_strategy, auth: { token } });
@@ -1,12 +1,34 @@
1
+ import axios from 'axios';
1
2
  import { create_base_client } from '../base_client.js';
2
3
 
3
4
  const exchangeGithubOidcToken = async (gh_token) => {
4
5
  const client = create_base_client();
5
- const res = await client.post("/auth/github-oidc", {
6
- token: gh_token
7
- });
8
- const { access_token, app_id } = res.data;
9
- return { access_token, app_id };
6
+ try {
7
+ const res = await client.post("/auth/github-oidc", {
8
+ token: gh_token
9
+ });
10
+ const { access_token, app_id } = res.data;
11
+ return { access_token, app_id };
12
+ }
13
+ catch (err) {
14
+ if (axios.isAxiosError(err)) {
15
+ const status = err.response?.status;
16
+ const serverMessage = err.response?.data?.message;
17
+ // No app is linked to this repository — turn the cryptic 404 into an
18
+ // actionable next step.
19
+ if (status === 404) {
20
+ throw new Error('No app linked to this repository. Run "faable link" locally to link it, ' +
21
+ "or link it from the dashboard (https://dashboard.faable.com).");
22
+ }
23
+ // Monorepo: several apps are linked to the same repository.
24
+ if (status === 400) {
25
+ throw new Error(serverMessage ||
26
+ "This repository has multiple linked apps. Specify which one with `faable deploy <app_id>`.");
27
+ }
28
+ throw new Error(`Faable OIDC token exchange failed (${status ?? "network error"})${serverMessage ? `: ${serverMessage}` : ""}`);
29
+ }
30
+ throw err;
31
+ }
10
32
  };
11
33
  const oidc_strategy = (config) => {
12
34
  const { idToken } = config;
@@ -1,5 +1,6 @@
1
1
  import { context } from '../../api/context.js';
2
2
  import { cmd } from '../../lib/cmd.js';
3
+ import { Configuration } from '../../lib/Configuration.js';
3
4
  import { log } from '../../log.js';
4
5
  import { check_environment } from './check_environment.js';
5
6
  import { git_context } from './git_context.js';
@@ -30,9 +31,13 @@ const deploy = {
30
31
  const { api } = ctx;
31
32
  // Resolve runtime
32
33
  const { runtime } = await runtime_detection(workdir);
33
- const app_id = args.app_id || ctx.appId;
34
+ // app_id resolution (the user never has to look one up):
35
+ // 1. explicit positional (monorepo escape hatch)
36
+ // 2. OIDC in CI — the backend resolves the app from the linked repository
37
+ // 3. locally — the app saved by `faable link` in faable.json
38
+ const app_id = args.app_id || ctx.appId || Configuration.instance().app_id;
34
39
  if (!app_id) {
35
- throw new Error('Missing <app_id>, run inside github action or pass <app_id> after deploy command');
40
+ throw new Error('No app linked to this repository. Run "faable link" to link it (or link it from the dashboard).');
36
41
  }
37
42
  const app = await api.getApp(app_id);
38
43
  // Check if we can build docker images
@@ -24,14 +24,10 @@ const getGitRemoteUrl = async (workdir) => {
24
24
  }
25
25
  };
26
26
  const link = {
27
- command: "link [app_id]",
27
+ command: "link",
28
28
  describe: "Link the local repository with a Faable app",
29
29
  builder: (yargs) => {
30
30
  return yargs
31
- .positional("app_id", {
32
- type: "string",
33
- description: "app_id to link this repository",
34
- })
35
31
  .option("workdir", {
36
32
  alias: "w",
37
33
  type: "string",
@@ -41,7 +37,14 @@ const link = {
41
37
  },
42
38
  handler: async (args) => {
43
39
  const workdir = args.workdir || process.cwd();
44
- const { app_id } = args;
40
+ // `faable link <app_id>` is deprecated. Linking is now fully interactive and
41
+ // the repository is auto-detected from the current folder — users never need
42
+ // to look up an app_id. Warn if an extra positional was passed and ignore it.
43
+ const stray = (args._ ?? []).slice(1);
44
+ if (stray.length > 0) {
45
+ log.warn('Passing an app_id to "faable link" is deprecated and ignored. ' +
46
+ 'Just run "faable link" and select the app from the list.');
47
+ }
45
48
  const config = Configuration.instance();
46
49
  if (config.app_id) {
47
50
  log.info(`This repository is already linked to app: "${config.app_slug}" (${config.app_id})`);
@@ -60,26 +63,20 @@ const link = {
60
63
  const { api } = await context();
61
64
  log.info("Checking local git repository...");
62
65
  const gitUrl = await getGitRemoteUrl(workdir);
63
- let selectedApp;
64
- if (!app_id) {
65
- const apps = await api.list();
66
- if (apps.length === 0) {
67
- log.error("No apps found in your account. Create one first at https://faable.com");
68
- return;
69
- }
70
- selectedApp = await prompts({
71
- type: "select",
72
- name: "selectedApp",
73
- message: "Select the Faable app to link with this repository:",
74
- choices: apps.map((app) => ({
75
- title: `${app.name} (${app.url})`,
76
- value: app,
77
- })),
78
- });
79
- }
80
- else {
81
- selectedApp = await api.getApp(app_id);
66
+ const apps = await api.list();
67
+ if (apps.length === 0) {
68
+ log.error("No apps found in your account. Create one first at https://faable.com");
69
+ return;
82
70
  }
71
+ const { selectedApp } = await prompts({
72
+ type: "select",
73
+ name: "selectedApp",
74
+ message: "Select the Faable app to link with this repository:",
75
+ choices: apps.map((app) => ({
76
+ title: `${app.name} (${app.url})`,
77
+ value: app,
78
+ })),
79
+ });
83
80
  if (!selectedApp) {
84
81
  log.info("Link cancelled.");
85
82
  return;
@@ -49,7 +49,7 @@ class Configuration {
49
49
  return this.config.app_slug;
50
50
  }
51
51
  get app_id() {
52
- return this.config.app_slug;
52
+ return this.config.app_id;
53
53
  }
54
54
  }
55
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faable/faable",
3
- "version": "1.5.29",
3
+ "version": "1.5.30",
4
4
  "main": "dist/index.js",
5
5
  "license": "MIT",
6
6
  "author": "Marc Pomar <marc@faable.com>",