@faable/faable 1.21.0 → 1.23.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/context.js
CHANGED
|
@@ -7,7 +7,7 @@ import { CredentialsStore } from '../lib/CredentialsStore.js';
|
|
|
7
7
|
import { loadLiveCredentials } from './session.js';
|
|
8
8
|
import { log } from '../log.js';
|
|
9
9
|
|
|
10
|
-
const context = async () => {
|
|
10
|
+
const context = async (targetAppId) => {
|
|
11
11
|
let api;
|
|
12
12
|
// Auth resolution: FAABLE_TOKEN → OIDC (CI) → local `faable login` credentials.
|
|
13
13
|
if (process.env.FAABLE_TOKEN) {
|
|
@@ -19,7 +19,12 @@ const context = async () => {
|
|
|
19
19
|
// Github actions environment
|
|
20
20
|
try {
|
|
21
21
|
const idToken = process.env.FAABLE_ID_TOKEN || (await getIDToken("https://faable.com"));
|
|
22
|
-
|
|
22
|
+
// targetAppId disambiguates a monorepo (several apps, one repo) in the
|
|
23
|
+
// OIDC exchange — from `faable deploy <app_id>`.
|
|
24
|
+
api = FaableApi.create({
|
|
25
|
+
authStrategy: oidc_strategy,
|
|
26
|
+
auth: { idToken, appId: targetAppId },
|
|
27
|
+
});
|
|
23
28
|
}
|
|
24
29
|
catch (_) {
|
|
25
30
|
console.error("Error fetching token, configure 'permissions: id-token: write'");
|
|
@@ -54,8 +59,8 @@ const context = async () => {
|
|
|
54
59
|
// Exits with a uniform message when there is no session at all. An expired
|
|
55
60
|
// session that could not be refreshed still surfaces here as a working `api`
|
|
56
61
|
// whose first call returns 401 — FaableApi maps that to the same re-login hint.
|
|
57
|
-
const requireApi = async () => {
|
|
58
|
-
const ctx = await context();
|
|
62
|
+
const requireApi = async (targetAppId) => {
|
|
63
|
+
const ctx = await context(targetAppId);
|
|
59
64
|
if (!ctx.api) {
|
|
60
65
|
log.error("❌ Not logged in. Run 'faable login' first.");
|
|
61
66
|
process.exit(1);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import { create_base_client } from '../base_client.js';
|
|
3
3
|
|
|
4
|
-
const exchangeGithubOidcToken = async (gh_token) => {
|
|
4
|
+
const exchangeGithubOidcToken = async (gh_token, target_app_id) => {
|
|
5
5
|
const client = create_base_client();
|
|
6
6
|
try {
|
|
7
|
+
// Monorepo: several apps share one repo, so the exchange can't infer which
|
|
8
|
+
// app the workflow targets — pass the explicit app_id (`faable deploy
|
|
9
|
+
// <app_id>`). The api verifies it's linked to the OIDC token's repository.
|
|
7
10
|
const res = await client.post("/auth/github-oidc", {
|
|
8
|
-
token: gh_token
|
|
11
|
+
token: gh_token,
|
|
12
|
+
...(target_app_id ? { app_id: target_app_id } : {})
|
|
9
13
|
});
|
|
10
14
|
const { access_token, app_id } = res.data;
|
|
11
15
|
return { access_token, app_id };
|
|
@@ -31,28 +35,24 @@ const exchangeGithubOidcToken = async (gh_token) => {
|
|
|
31
35
|
}
|
|
32
36
|
};
|
|
33
37
|
const oidc_strategy = (config) => {
|
|
34
|
-
const { idToken } = config;
|
|
38
|
+
const { idToken, appId } = config;
|
|
35
39
|
if (!idToken) {
|
|
36
40
|
throw new Error("Missing idToken.");
|
|
37
41
|
}
|
|
38
42
|
let token_ex;
|
|
43
|
+
const ensure = async () => {
|
|
44
|
+
if (!token_ex)
|
|
45
|
+
token_ex = await exchangeGithubOidcToken(idToken, appId);
|
|
46
|
+
return token_ex;
|
|
47
|
+
};
|
|
39
48
|
return {
|
|
40
49
|
headers: async () => {
|
|
41
|
-
|
|
42
|
-
const ex = await exchangeGithubOidcToken(idToken);
|
|
43
|
-
token_ex = ex;
|
|
44
|
-
}
|
|
50
|
+
const ex = await ensure();
|
|
45
51
|
return {
|
|
46
|
-
Authorization: `Bearer ${
|
|
52
|
+
Authorization: `Bearer ${ex.access_token}`,
|
|
47
53
|
};
|
|
48
54
|
},
|
|
49
|
-
app_id: async () =>
|
|
50
|
-
if (!token_ex) {
|
|
51
|
-
const ex = await exchangeGithubOidcToken(idToken);
|
|
52
|
-
token_ex = ex;
|
|
53
|
-
}
|
|
54
|
-
return token_ex.app_id;
|
|
55
|
-
}
|
|
55
|
+
app_id: async () => (await ensure()).app_id,
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -54,11 +54,20 @@ const deploy = {
|
|
|
54
54
|
// into the build-log buffer) and cmd() (which captures subprocess output
|
|
55
55
|
// into the same buffer). Must happen before any detect/build call.
|
|
56
56
|
configure_buildpacks({ log, exec: cmd });
|
|
57
|
-
|
|
57
|
+
// Pass the explicit app target to the OIDC exchange so a monorepo (several
|
|
58
|
+
// apps, one repo) can be disambiguated in CI.
|
|
59
|
+
const ctx = await requireApi(args.app_id);
|
|
58
60
|
const { api } = ctx;
|
|
59
61
|
const config = Configuration.instance().deployConfig();
|
|
60
62
|
const app_id = await resolve_app_id(args.app_id, ctx.appId, api, workdir);
|
|
61
63
|
const app = await api.getApp(app_id);
|
|
64
|
+
// Monorepo Root Directory: the server is the source of truth
|
|
65
|
+
// (App.root_dir), so no repo config file is needed. A local
|
|
66
|
+
// faable.json rootDir (dev override) still wins if set.
|
|
67
|
+
if (!config.rootDir && app.root_dir) {
|
|
68
|
+
config.rootDir = app.root_dir;
|
|
69
|
+
log.info(`📁 Monorepo root directory (from app): ${app.root_dir}`);
|
|
70
|
+
}
|
|
62
71
|
// Capture the commit/ref/actor so the deployment records which commit
|
|
63
72
|
// it came from and who pushed it (env in CI, git fallback locally).
|
|
64
73
|
const git = await git_context({ workdir });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faable/faable",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Pomar <marc@faable.com>",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@actions/core": "^3.0.0",
|
|
30
|
-
"@faabletools/buildpacks": "^1.
|
|
30
|
+
"@faabletools/buildpacks": "^1.7.0",
|
|
31
31
|
"axios": "^1.13.2",
|
|
32
32
|
"fs-extra": "^11.3.2",
|
|
33
33
|
"handlebars": "^4.7.8",
|