@samebase/alchemy-convex 0.19.0 → 0.20.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 +20 -3
- package/lib/{HubClient-C0XxgkK3.js → HubClient-Dzw8uZNy.js} +18 -6
- package/lib/HubClient-Dzw8uZNy.js.map +1 -0
- package/lib/cli.js +13 -1
- package/lib/cli.js.map +1 -1
- package/lib/index.d.ts +38 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +131 -4
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ManagementApi.ts +22 -1
- package/src/Providers.ts +13 -2
- package/src/Push.ts +19 -11
- package/src/SharedHubClient.ts +271 -0
- package/src/StackComment.ts +8 -0
- package/src/index.ts +5 -0
- package/lib/HubClient-C0XxgkK3.js.map +0 -1
package/README.md
CHANGED
|
@@ -308,6 +308,22 @@ call.
|
|
|
308
308
|
the `Convex.Deployment` writes to the deployment. Another `redirectUri` is a new client, and a
|
|
309
309
|
destroy deletes the client.
|
|
310
310
|
|
|
311
|
+
- `Convex.SharedHubClient`: `{ app, deployment, deploymentType, softwareVersion? }` gets a client of
|
|
312
|
+
the central identity hub through samebase.com, for an app that deploys in its own GitHub Actions
|
|
313
|
+
run. The workflow needs `permissions: id-token: write` and no Samebase key. Outputs `status`,
|
|
314
|
+
`issuer`, `clientId`, `clientSecret`, `registrationAccessToken`, `registrationClientUri`,
|
|
315
|
+
`deployment`, `deploymentType`, and `redirectUri`.
|
|
316
|
+
- Each deploy reads the saved client on the hub with its own token. A client that is gone gets a
|
|
317
|
+
new one, and a client on another deployment too.
|
|
318
|
+
- No failure stops a deploy or a destroy. A failed renewal keeps the saved values. Without saved
|
|
319
|
+
values, `status` is `off`, and `issuer`, `clientId`, and `clientSecret` are the `no-hub` values
|
|
320
|
+
of a checkout without a hub. A run without a GitHub OIDC token, such as on a laptop, does the
|
|
321
|
+
same, and logs no warning.
|
|
322
|
+
- `status` is `suspended` when the hub suspended the client. The client keeps its values.
|
|
323
|
+
- A destroy deletes a preview client. It does nothing to a production client, because the
|
|
324
|
+
production deployment can stay.
|
|
325
|
+
- `SAMEBASE_URL` replaces `https://samebase.com`.
|
|
326
|
+
|
|
311
327
|
A development deployment has no stack, so the package has a command for it. Run it in the directory
|
|
312
328
|
of the app, before the first push: it registers the deployment of that directory with
|
|
313
329
|
`CONVEX_ACCESS_TOKEN` or the Convex CLI login, and writes `HUB_ISSUER`, `HUB_CLIENT_ID`, and
|
|
@@ -320,9 +336,10 @@ npx alchemy-convex hub-client --team my-team --app scout --callback-path /auth/h
|
|
|
320
336
|
## Pull request comment
|
|
321
337
|
|
|
322
338
|
`alchemy-convex stack-comment` keeps one comment on a pull request. The comment has a card for each
|
|
323
|
-
stack of the repository at the stage `pr-<number>`: the folder, the address, the last run,
|
|
324
|
-
Convex deployments, Workers, and R2 buckets
|
|
325
|
-
declares cannot show
|
|
339
|
+
stack of the repository at the stage `pr-<number>`: the folder, the address, the last run, the
|
|
340
|
+
Convex deployments, Workers, and R2 buckets, and the Google sign-in status of a
|
|
341
|
+
`Convex.SharedHubClient`. A stack sees only itself, so a comment that a stack declares cannot show
|
|
342
|
+
this.
|
|
326
343
|
|
|
327
344
|
Run it after every deploy of the workflow. The run record gives the repository and the pull request,
|
|
328
345
|
so the command needs no argument with a team access token:
|
|
@@ -182,7 +182,11 @@ const TokenDetails = Schema.Union([Schema.Struct({
|
|
|
182
182
|
projectId: Schema.Number
|
|
183
183
|
})]);
|
|
184
184
|
/** The service for one way to get the bearer token. */
|
|
185
|
-
const makeService = (
|
|
185
|
+
const makeService = (source) => {
|
|
186
|
+
const credentials = source.pipe(Effect.map((resolved) => ({
|
|
187
|
+
...resolved,
|
|
188
|
+
accessToken: Redacted.make(Redacted.value(resolved.accessToken).trim())
|
|
189
|
+
})));
|
|
186
190
|
const client = Effect.cached(credentials.pipe(Effect.map((resolved) => createManagementClient(Redacted.value(resolved.accessToken))))).pipe(Effect.flatten);
|
|
187
191
|
const request = (operation, run) => client.pipe(Effect.flatMap((c) => settle(operation, () => run(c))));
|
|
188
192
|
const requestVoid = (operation, run) => client.pipe(Effect.flatMap((c) => settleVoid(operation, () => run(c))));
|
|
@@ -240,6 +244,7 @@ const makeService = (credentials) => {
|
|
|
240
244
|
return match.id;
|
|
241
245
|
});
|
|
242
246
|
const isCredentialToken = (secret) => credentials.pipe(Effect.map((resolved) => tokenPart(Redacted.value(resolved.accessToken)) === tokenPart(secret)));
|
|
247
|
+
const isOAuthCredential = credentials.pipe(Effect.map((resolved) => /^(team|project):/.test(Redacted.value(resolved.accessToken))));
|
|
243
248
|
const withToken = (token) => makeService(Effect.succeed({
|
|
244
249
|
accessToken: token,
|
|
245
250
|
source: "explicit"
|
|
@@ -250,6 +255,7 @@ const makeService = (credentials) => {
|
|
|
250
255
|
deploymentRequest,
|
|
251
256
|
resolveTeamId,
|
|
252
257
|
isCredentialToken,
|
|
258
|
+
isOAuthCredential,
|
|
253
259
|
withToken
|
|
254
260
|
};
|
|
255
261
|
};
|
|
@@ -298,7 +304,7 @@ rootDir: Schema.String }) {
|
|
|
298
304
|
var CodePushFailed = class extends Schema.TaggedError()("CodePushFailed", {
|
|
299
305
|
/** Exit code of the CLI. Undefined when it did not start or a signal stopped it. */
|
|
300
306
|
exitCode: Schema.UndefinedOr(Schema.Number),
|
|
301
|
-
/** The last lines of the CLI output, with the deploy key removed. */
|
|
307
|
+
/** The last lines of the CLI output, with the deploy key and the `Redacted` values of `env` removed. */
|
|
302
308
|
outputTail: Schema.String
|
|
303
309
|
}) {
|
|
304
310
|
get message() {
|
|
@@ -476,13 +482,15 @@ const cliEnvironment = (home) => ({
|
|
|
476
482
|
/**
|
|
477
483
|
* A deploy key of `deployment` that expires after PUSH_KEY_LIFETIME_MS, with
|
|
478
484
|
* only `allowedActions`, or every action when that is undefined. Delete it
|
|
479
|
-
* with {@link deleteDeployKey} when the work is done.
|
|
485
|
+
* with {@link deleteDeployKey} when the work is done. An OAuth credential sends
|
|
486
|
+
* only the name: Convex refuses both limits and returns the OAuth token.
|
|
480
487
|
*/
|
|
481
488
|
const createDeployKey = (api, deployment, name, allowedActions) => Effect.gen(function* () {
|
|
482
489
|
const now = yield* Clock.currentTimeMillis;
|
|
490
|
+
const oauth = yield* api.isOAuthCredential;
|
|
483
491
|
const created = yield* api.request("create deploy key", (client) => client.POST("/deployments/{deployment_name}/create_deploy_key", {
|
|
484
492
|
params: { path: { deployment_name: deployment } },
|
|
485
|
-
body: {
|
|
493
|
+
body: oauth ? { name } : {
|
|
486
494
|
name,
|
|
487
495
|
...allowedActions === void 0 ? {} : { allowedActions: [...allowedActions] },
|
|
488
496
|
expiresAt: now + PUSH_KEY_LIFETIME_MS
|
|
@@ -589,7 +597,11 @@ const push = (spawner, key, options) => Effect.gen(function* () {
|
|
|
589
597
|
const runPush = (spawner, key, options) => Effect.gen(function* () {
|
|
590
598
|
const { cwd, program } = options;
|
|
591
599
|
const secret = Redacted.value(key);
|
|
592
|
-
const secrets = [
|
|
600
|
+
const secrets = [
|
|
601
|
+
secret,
|
|
602
|
+
secret.slice(secret.indexOf("|") + 1),
|
|
603
|
+
...Object.values(options.env).flatMap((value) => Redacted.isRedacted(value) ? [Redacted.value(value)] : [])
|
|
604
|
+
].filter((value) => value.length >= 8);
|
|
593
605
|
const result = yield* withKeyFile(secret, ({ envFile, home }) => Effect.gen(function* () {
|
|
594
606
|
const [command, ...programArgs] = program;
|
|
595
607
|
const args = options.command === void 0 ? [
|
|
@@ -1945,4 +1957,4 @@ const registerDevelopmentClient = (options) => Effect.gen(function* () {
|
|
|
1945
1957
|
//#endregion
|
|
1946
1958
|
export { PushTargetMismatch as A, DEPLOY_KEY_ENV as B, DeploymentProvider as C, ConvexAppNotFound as D, CodePushFailed as E, ManagementApi as F, fromValues as H, ManagementApiLive as I, ACCESS_TOKEN_ENV as L, installedCli as M, parseDeployOutput as N, ConvexCliNotFound as O, ConvexApiError as P, Credentials as R, DeploymentIdentityChange as S, ProductionDeploymentNotFound as T, fromConfig as V, ProjectProvider as _, registerDevelopmentClient as a, AmbiguousDeployment as b, RUN_RECORD_KEY as c, withRunRecord as d, AmbiguousProject as f, ProjectNotFound as g, ProjectDeploymentChange as h, HubClientProvider as i, VariablesOwnedBySomeoneElse as j, EnvironmentNeedsDeployKey as k, RunRecordError as l, Project as m, HubClient as n, STORE_PROJECT_NAME as o, CreatedProjectNotFound as p, HubClientFailed as r, state as s, HUB_PROJECT_NAME as t, runRecord as u, ProjectTeamChange as v, DeploymentResource as w, Deployment as x, AbsoluteRootDir as y, CredentialsError as z };
|
|
1947
1959
|
|
|
1948
|
-
//# sourceMappingURL=HubClient-
|
|
1960
|
+
//# sourceMappingURL=HubClient-Dzw8uZNy.js.map
|