@continuous-excellence/ze-great-dashboard-aws 0.1.23 → 0.1.24

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 CHANGED
@@ -14,8 +14,8 @@ opinion about your process. Every panel is a live read of a system that already
14
14
  Design doc: `ze-great-idea-pit/tool-ideas/trust-dashboard.md`. How this repo came to be shaped the
15
15
  way it is, including the quirks that look like bugs: `docs/initialization-log.md`.
16
16
 
17
- **Status: first Stage 2 slice.** GitHub Actions `pipeline-status` panels work end to end. Azure
18
- DevOps and `http-value` adapters remain to be built.
17
+ **Status: MVP in active use.** GitHub Actions `pipeline-status` and source-agnostic `http-value`
18
+ panels work end to end. Azure DevOps remains to be built.
19
19
 
20
20
  ## Quickstart
21
21
 
@@ -116,12 +116,14 @@ Server environment variables are documented in `.env.example`. The one that matt
116
116
  ## Deploying
117
117
 
118
118
  Push to `main`. CI checks, versions with [Tagger](https://github.com/robertfmurdock/tagger), publishes
119
- the client to S3, and points the Lambda at the new version.
119
+ the immutable client assets, then deploys the exact npm tarball to the repository-owned consumer
120
+ reference before publishing that same tarball.
120
121
 
121
122
  Infrastructure is a CloudFormation stack under `infra/`. On `main`, CI deploys the stack before it
122
- publishes assets and updates the Lambda. Other branches receive no AWS credentials. CloudFormation
123
+ publishes assets and updates the Lambda. The public client origin is
124
+ `https://public-assets.zegreatrob.com`. Other branches receive no AWS credentials. CloudFormation
123
125
  keeps the infrastructure state in AWS; `infra/README.md` documents the one-time GitHub OIDC role
124
- bootstrap.
126
+ bootstraps, including the consumer reference.
125
127
 
126
128
  For a consumer-managed deployment with the published AWS package, see [`docs/aws-setup.md`](docs/aws-setup.md).
127
129
 
package/dist/cli.js CHANGED
@@ -921,12 +921,9 @@ async function packageLambda(options) {
921
921
  );
922
922
  return packagedRelease;
923
923
  }
924
- async function deployLambda(options) {
925
- const artifactDir = resolve2(options.artifactDir);
924
+ async function publishClientAssets(options) {
926
925
  const assetsDir = resolve2(options.assetsDir);
927
- await readFile2(join2(artifactDir, "lambda.zip"));
928
926
  await readFile2(join2(assetsDir, "index.html"));
929
- if (options.dryRun) return;
930
927
  const assetPath = `${options.assetsBaseUrl.replace(/\/+$/, "")}/dashboard/${options.version}`;
931
928
  await run("aws", [
932
929
  "s3",
@@ -946,6 +943,15 @@ async function deployLambda(options) {
946
943
  "--cache-control",
947
944
  "public, max-age=60"
948
945
  ]);
946
+ return assetPath;
947
+ }
948
+ async function deployLambda(options) {
949
+ const artifactDir = resolve2(options.artifactDir);
950
+ const assetsDir = resolve2(options.assetsDir);
951
+ await readFile2(join2(artifactDir, "lambda.zip"));
952
+ await readFile2(join2(assetsDir, "index.html"));
953
+ if (options.dryRun) return;
954
+ const assetPath = await publishClientAssets(options);
949
955
  await run("aws", [
950
956
  "lambda",
951
957
  "update-function-code",
@@ -1180,6 +1186,15 @@ try {
1180
1186
  dryRun: args.includes("--dry-run")
1181
1187
  });
1182
1188
  console.log(JSON.stringify({ deployed: true, version }));
1189
+ } else if (args[0] === "publish-assets") {
1190
+ const version = requiredOption("--version", packageVersion);
1191
+ const assetPath = await publishClientAssets({
1192
+ assetsDir: option("--assets-dir", bundledAssets) ?? bundledAssets,
1193
+ assetsBucket: requiredOption("--assets-bucket"),
1194
+ assetsBaseUrl: requiredOption("--assets-base-url"),
1195
+ version
1196
+ });
1197
+ console.log(JSON.stringify({ published: true, version, assetPath }));
1183
1198
  } else if (args[0] === "doctor") {
1184
1199
  const parametersPath = option("--parameters", "aws-dashboard-parameters.json") ?? "aws-dashboard-parameters.json";
1185
1200
  const region = option("--region", process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-1") ?? "us-east-1";
@@ -1237,7 +1252,9 @@ try {
1237
1252
  `);
1238
1253
  console.log(JSON.stringify({ output }));
1239
1254
  } else if (args[0] !== "package")
1240
- throw new Error("Usage: ze-great-dashboard-aws package|parameters|deploy|doctor [options]");
1255
+ throw new Error(
1256
+ "Usage: ze-great-dashboard-aws package|parameters|publish-assets|deploy|doctor [options]"
1257
+ );
1241
1258
  else {
1242
1259
  const boardConfig = option("--board-config");
1243
1260
  const version = option("--version", process.env.DASHBOARD_VERSION ?? packageVersion);
package/dist/index.d.ts CHANGED
@@ -27,6 +27,14 @@ export type DeployLambdaOptions = {
27
27
  version: string;
28
28
  dryRun?: boolean;
29
29
  };
30
+ export type PublishClientAssetsOptions = {
31
+ assetsDir: string;
32
+ assetsBucket: string;
33
+ assetsBaseUrl: string;
34
+ version: string;
35
+ };
36
+ /** Publishes the immutable client half of a provider-managed release. */
37
+ export declare function publishClientAssets(options: PublishClientAssetsOptions): Promise<string>;
30
38
  /** Performs the AWS-specific half of a release using explicit deployment outputs. */
31
39
  export declare function deployLambda(options: DeployLambdaOptions): Promise<void>;
32
40
  export declare function cloudFormationTemplate(): Promise<string>;
package/dist/index.js CHANGED
@@ -908,12 +908,9 @@ async function packageLambda(options) {
908
908
  );
909
909
  return packagedRelease;
910
910
  }
911
- async function deployLambda(options) {
912
- const artifactDir = resolve2(options.artifactDir);
911
+ async function publishClientAssets(options) {
913
912
  const assetsDir = resolve2(options.assetsDir);
914
- await readFile2(join2(artifactDir, "lambda.zip"));
915
913
  await readFile2(join2(assetsDir, "index.html"));
916
- if (options.dryRun) return;
917
914
  const assetPath = `${options.assetsBaseUrl.replace(/\/+$/, "")}/dashboard/${options.version}`;
918
915
  await run("aws", [
919
916
  "s3",
@@ -933,6 +930,15 @@ async function deployLambda(options) {
933
930
  "--cache-control",
934
931
  "public, max-age=60"
935
932
  ]);
933
+ return assetPath;
934
+ }
935
+ async function deployLambda(options) {
936
+ const artifactDir = resolve2(options.artifactDir);
937
+ const assetsDir = resolve2(options.assetsDir);
938
+ await readFile2(join2(artifactDir, "lambda.zip"));
939
+ await readFile2(join2(assetsDir, "index.html"));
940
+ if (options.dryRun) return;
941
+ const assetPath = await publishClientAssets(options);
936
942
  await run("aws", [
937
943
  "lambda",
938
944
  "update-function-code",
@@ -960,5 +966,6 @@ async function cloudFormationTemplate() {
960
966
  export {
961
967
  cloudFormationTemplate,
962
968
  deployLambda,
963
- packageLambda
969
+ packageLambda,
970
+ publishClientAssets
964
971
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@continuous-excellence/ze-great-dashboard-aws",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "description": "AWS Lambda and CloudFormation adapter for Ze Great Dashboard.",
6
6
  "keywords": [
package/template.yml CHANGED
@@ -25,6 +25,9 @@ Resources:
25
25
  ServerRole:
26
26
  Type: AWS::IAM::Role
27
27
  Properties:
28
+ # A stable, Name-derived role name lets consumer bootstrap policies scope CloudFormation
29
+ # execution to this stack's runtime role rather than every role in the account.
30
+ RoleName: !Sub '${Name}-server'
28
31
  AssumeRolePolicyDocument:
29
32
  Version: '2012-10-17'
30
33
  Statement: [{ Effect: Allow, Principal: { Service: lambda.amazonaws.com }, Action: sts:AssumeRole }]
@@ -36,7 +39,12 @@ Resources:
36
39
  Statement:
37
40
  - Effect: Allow
38
41
  Action: [secretsmanager:DescribeSecret, secretsmanager:GetSecretValue]
39
- Resource: !If [HasSecretReference, !Ref SecretReference, !Ref AWS::NoValue]
42
+ # IAM requires every statement to have a resource. When no secret is configured,
43
+ # keep this read permission pointed at a nonexistent ARN rather than widening it.
44
+ Resource: !If
45
+ - HasSecretReference
46
+ - !Ref SecretReference
47
+ - !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:ze-great-dashboard-no-secret-configured'
40
48
  ServerLogGroup:
41
49
  Type: AWS::Logs::LogGroup
42
50
  Properties: { RetentionInDays: !Ref LogRetentionInDays, LogGroupName: !Sub '/aws/lambda/${Name}' }