@deployfoundation/foundation-deploy 0.1.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 +174 -0
- package/agent-image/Dockerfile +254 -0
- package/agent-image/bin/aws +36 -0
- package/agent-image/bin/gh +193 -0
- package/agent-image/bin/git-credential-sky +89 -0
- package/agent-image/security-overlay.yml +176 -0
- package/cdk.json +6 -0
- package/dist/bin/app.js +112 -0
- package/dist/bin/foundation-deploy.js +1906 -0
- package/dist/bin/release-account.js +154 -0
- package/dist/chunk-4aye5cee.js +2416 -0
- package/dist/chunk-9ddxyvq2.js +1455 -0
- package/dist/chunk-v7tz8g50.js +428 -0
- package/dist/src/index.js +88 -0
- package/package.json +38 -0
- package/pipeline/buildspec.yml +34 -0
- package/src/artifacts.ts +318 -0
- package/src/deploy/assets/github-app-manifest.yml +29 -0
- package/src/deploy/assets/slack-app-manifest.yml +95 -0
- package/src/deploy/aws.ts +265 -0
- package/src/deploy/cli.ts +212 -0
- package/src/deploy/config-sync.ts +93 -0
- package/src/deploy/config.ts +29 -0
- package/src/deploy/deploy.ts +566 -0
- package/src/deploy/endpoint.ts +242 -0
- package/src/deploy/github-app-create.ts +154 -0
- package/src/deploy/github-app-manifest.ts +53 -0
- package/src/deploy/image.ts +80 -0
- package/src/deploy/instance.ts +87 -0
- package/src/deploy/license-cache.ts +47 -0
- package/src/deploy/license.ts +272 -0
- package/src/deploy/paths.ts +65 -0
- package/src/deploy/post-deploy.ts +97 -0
- package/src/deploy/release.ts +282 -0
- package/src/deploy/runtime-secret.ts +241 -0
- package/src/deploy/setup.ts +393 -0
- package/src/deploy/sh.ts +74 -0
- package/src/deploy/slack-manifest.ts +112 -0
- package/src/deploy/stage-customization.ts +224 -0
- package/src/deploy/tracing.ts +243 -0
- package/src/deploy-permissions.ts +165 -0
- package/src/index.ts +60 -0
- package/src/lambda-bundle-context.ts +64 -0
- package/src/names.ts +170 -0
- package/src/release/kms.ts +86 -0
- package/src/release/manifest.ts +265 -0
- package/src/stacks/agent-stack.ts +938 -0
- package/src/stacks/api-stack.ts +1005 -0
- package/src/stacks/ci-stack.ts +96 -0
- package/src/stacks/data-stack.ts +446 -0
- package/src/stacks/network-stack.ts +282 -0
- package/src/stacks/newsletter-stack.ts +572 -0
- package/src/stacks/pipeline-stack.ts +242 -0
- package/src/stacks/release-account-stack.ts +229 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# git credential helper for the ambient GitHub path (github-broker.ts).
|
|
3
|
+
#
|
|
4
|
+
# git invokes `git-credential-sky get` for https://github.com operations
|
|
5
|
+
# (wired via /etc/gitconfig with `useHttpPath = true`, so the `get` request
|
|
6
|
+
# on stdin carries protocol/host/path). We answer ONLY for host=github.com
|
|
7
|
+
# over https — anything else gets no output, so the token can never be
|
|
8
|
+
# handed to a look-alike host. On a match we fetch a short-lived,
|
|
9
|
+
# repo-scoped GitHub App installation token from the in-container loopback
|
|
10
|
+
# broker and emit it in git's credential protocol, marked `ephemeral=1`
|
|
11
|
+
# (never stored) with `password_expiry_utc` so git >=2.34 discards it the
|
|
12
|
+
# moment it expires. When no broker is running (channel without the github
|
|
13
|
+
# connector), emit nothing and exit 0 — git falls through to anonymous
|
|
14
|
+
# access, so public clones still work.
|
|
15
|
+
#
|
|
16
|
+
# Broker contract: GET http://127.0.0.1:7791/git-credential[?repo=<path>] ->
|
|
17
|
+
# line 1: bare installation token
|
|
18
|
+
# line 2: UNIX-epoch expiry seconds
|
|
19
|
+
#
|
|
20
|
+
# The `path` git sends (useHttpPath = true) is forwarded as `repo=`, so the
|
|
21
|
+
# broker mints a token scoped to THAT repository instead of every repo the
|
|
22
|
+
# channel can reach. The broker reads only the first two path segments and
|
|
23
|
+
# refuses a repo the channel was not granted (HTTP 403).
|
|
24
|
+
|
|
25
|
+
# Overridable ONLY so this helper can be tested against a stub broker on an
|
|
26
|
+
# ephemeral port; production always uses the loopback default.
|
|
27
|
+
# No GitHub on this instance (capability off, or no App secret). Refuse loudly
|
|
28
|
+
# rather than emitting nothing, which git would silently retry anonymously.
|
|
29
|
+
if [ "${FOUNDATION_GITHUB_DISABLED:-}" = "1" ]; then
|
|
30
|
+
echo "GitHub is not connected on this instance." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
BROKER_URL="${FOUNDATION_GH_BROKER_URL:-http://127.0.0.1:7791/git-credential}"
|
|
35
|
+
|
|
36
|
+
[ "$1" = "get" ] || exit 0
|
|
37
|
+
|
|
38
|
+
# Parse the credential request (key=value lines, blank-line terminated).
|
|
39
|
+
host=""
|
|
40
|
+
protocol=""
|
|
41
|
+
path=""
|
|
42
|
+
while IFS= read -r line; do
|
|
43
|
+
[ -n "$line" ] || break
|
|
44
|
+
case "$line" in
|
|
45
|
+
host=*) host="${line#host=}" ;;
|
|
46
|
+
protocol=*) protocol="${line#protocol=}" ;;
|
|
47
|
+
path=*) path="${line#path=}" ;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
[ "$host" = "github.com" ] || exit 0
|
|
51
|
+
[ "$protocol" = "https" ] || exit 0
|
|
52
|
+
|
|
53
|
+
# Only a plain repo path is forwarded — anything with characters that would
|
|
54
|
+
# need URL-escaping is dropped, which costs breadth (a channel-wide token)
|
|
55
|
+
# but never sends a malformed scope.
|
|
56
|
+
query=""
|
|
57
|
+
case "$path" in
|
|
58
|
+
'' | *[!A-Za-z0-9._/-]*) ;;
|
|
59
|
+
*) query="?repo=$path" ;;
|
|
60
|
+
esac
|
|
61
|
+
|
|
62
|
+
response="$(curl -fsS --max-time 25 --retry 2 --retry-connrefused "$BROKER_URL$query" 2>/dev/null)"
|
|
63
|
+
rc=$?
|
|
64
|
+
# rc=7 (connection refused) = no broker: silent anonymous fallthrough is
|
|
65
|
+
# correct (public clones on connector-less channels). ANY other failure is
|
|
66
|
+
# a broker that exists but misbehaved: fail LOUDLY (git reports the helper
|
|
67
|
+
# error) instead of degrading to anonymous and a misleading 404.
|
|
68
|
+
if [ $rc -eq 7 ]; then exit 0; fi
|
|
69
|
+
if [ $rc -ne 0 ]; then
|
|
70
|
+
# curl exit 22 = an HTTP error from the broker; the one that is NOT
|
|
71
|
+
# transient is 403 ("repository not granted"), i.e. this repo is not in the
|
|
72
|
+
# channel's grants — ask a human to grant it rather than retrying.
|
|
73
|
+
echo "git-credential-sky: broker error (curl exit $rc) — transient unless this repo is not in the instance's GitHub App installation" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
token="$(printf '%s\n' "$response" | head -n 1)"
|
|
77
|
+
expiry="$(printf '%s\n' "$response" | sed -n '2p')"
|
|
78
|
+
if [ -z "$token" ]; then
|
|
79
|
+
echo "git-credential-sky: broker returned an empty token — transient; retry shortly" >&2
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
printf 'username=x-access-token\npassword=%s\nephemeral=1\n' "$token"
|
|
83
|
+
# Expiry only when the broker sent a well-formed epoch (defense in depth —
|
|
84
|
+
# a malformed line 2 must not corrupt the credential answer).
|
|
85
|
+
case "$expiry" in
|
|
86
|
+
'' | *[!0-9]*) ;;
|
|
87
|
+
*) printf 'password_expiry_utc=%s\n' "$expiry" ;;
|
|
88
|
+
esac
|
|
89
|
+
exit 0
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Foundation fail-hard security overlay (omp-ref §3.4).
|
|
2
|
+
#
|
|
3
|
+
# Loaded via PI_CONFIG_FILES: unlike global/project config (silently moved to
|
|
4
|
+
# .broken-* on parse failure), OVERLAY FILES FAIL HARD — a corrupt copy halts
|
|
5
|
+
# the container instead of silently degrading the security posture.
|
|
6
|
+
#
|
|
7
|
+
# Arrays in omp settings REPLACE — ship complete lists only here or in the
|
|
8
|
+
# per-session project config, never split across layers.
|
|
9
|
+
tools:
|
|
10
|
+
approvalMode: yolo
|
|
11
|
+
# Global timeout ceiling in seconds — caps every resolved bash/eval timeout
|
|
12
|
+
# (defaults included). NOTE: omp treats `timeout: 0` ("no deadline") as
|
|
13
|
+
# bypassing this ceiling and Foundation ships no tool_call hook to rewrite it; the
|
|
14
|
+
# 15-minute AgentCore invocation limit is the effective backstop.
|
|
15
|
+
maxTimeout: 600
|
|
16
|
+
approval:
|
|
17
|
+
ast_edit: deny
|
|
18
|
+
browser: deny
|
|
19
|
+
computer: deny
|
|
20
|
+
debug: deny
|
|
21
|
+
goal: deny
|
|
22
|
+
grep: deny
|
|
23
|
+
hub: deny
|
|
24
|
+
init_experiment: deny
|
|
25
|
+
inspect_image: deny
|
|
26
|
+
# `learn` is NOT denied here: omp already gates it on `memory.backend`
|
|
27
|
+
# being on (tools/index.ts), and that value is written per session from
|
|
28
|
+
# `config.memory.ompBackend` (default `off`). A deny here would make the
|
|
29
|
+
# opt-in impossible to exercise. Every OTHER omp memory tool stays denied
|
|
30
|
+
# below, because each needs a backend Foundation will not run.
|
|
31
|
+
log_experiment: deny
|
|
32
|
+
# omp's remaining memory tools. `recall`/`retain`/`reflect` need the
|
|
33
|
+
# `hindsight` (remote HTTP server) or `mnemopi` (SQLite + embeddings)
|
|
34
|
+
# backend, and `memory_edit` needs `mnemopi`. Foundation runs neither — SQLite on
|
|
35
|
+
# the NFS-like S3 Files mount is a corruption risk and there is no
|
|
36
|
+
# Hindsight server — so these are denied outright rather than left to the
|
|
37
|
+
# backend gate. See docs/memory-backends.md.
|
|
38
|
+
memory_edit: deny
|
|
39
|
+
recall: deny
|
|
40
|
+
reflect: deny
|
|
41
|
+
retain: deny
|
|
42
|
+
run_experiment: deny
|
|
43
|
+
# `task: deny` was REMOVED 2026-09-01 when subagents were enabled (see
|
|
44
|
+
# ts/agent/src/tools/exec.ts TOOL_ALLOWLIST). A deny here would have left
|
|
45
|
+
# the tool activated-but-dead: deny wins in every approval mode.
|
|
46
|
+
todo: deny
|
|
47
|
+
update_notes: deny
|
|
48
|
+
web_search: deny
|
|
49
|
+
bash:
|
|
50
|
+
# Deny rules hold even in yolo mode and match compound-command segments
|
|
51
|
+
# (omp docs/tools/bash.md). Ambient git credentials (github-broker.ts)
|
|
52
|
+
# make force-pushes POSSIBLE — these rules make the destructive ones
|
|
53
|
+
# refuse locally; GitHub branch protection is the server-side backstop.
|
|
54
|
+
patterns:
|
|
55
|
+
- pattern: "git push*--force*"
|
|
56
|
+
action: deny
|
|
57
|
+
- pattern: "git push*-f *"
|
|
58
|
+
action: deny
|
|
59
|
+
- pattern: "git push*--delete*"
|
|
60
|
+
action: deny
|
|
61
|
+
# HackerOne #3556799 class (cited by claude-code-action's push wrapper):
|
|
62
|
+
# --receive-pack/--exec run arbitrary commands server-side, ext:: remotes
|
|
63
|
+
# run arbitrary commands locally, and URL-form remotes push the repo to
|
|
64
|
+
# hosts outside the credential helper's github.com scope.
|
|
65
|
+
- pattern: "git push*--receive-pack*"
|
|
66
|
+
action: deny
|
|
67
|
+
- pattern: "git push*--exec*"
|
|
68
|
+
action: deny
|
|
69
|
+
- pattern: "*ext::*"
|
|
70
|
+
action: deny
|
|
71
|
+
- pattern: "git push http*"
|
|
72
|
+
action: deny
|
|
73
|
+
- pattern: "git push git@*"
|
|
74
|
+
action: deny
|
|
75
|
+
- pattern: "git push ssh:*"
|
|
76
|
+
action: deny
|
|
77
|
+
# NOTE: colon-refspec deletes (`git push origin :branch`) are NOT denied —
|
|
78
|
+
# a glob on `:` would also break legitimate `push HEAD:refs/heads/x`.
|
|
79
|
+
# GitHub branch protection covers the remainder.
|
|
80
|
+
# Foundation additions: local branch/repo destruction and direct pushes to the default branch.
|
|
81
|
+
- pattern: "git branch -D*"
|
|
82
|
+
action: deny
|
|
83
|
+
- pattern: "git branch --delete --force*"
|
|
84
|
+
action: deny
|
|
85
|
+
- pattern: "gh repo delete*"
|
|
86
|
+
action: deny
|
|
87
|
+
- pattern: "git push*origin main*"
|
|
88
|
+
action: deny
|
|
89
|
+
- pattern: "git push*origin master*"
|
|
90
|
+
action: deny
|
|
91
|
+
- pattern: "git push*HEAD:main*"
|
|
92
|
+
action: deny
|
|
93
|
+
- pattern: "git push*HEAD:master*"
|
|
94
|
+
action: deny
|
|
95
|
+
# Ambient AWS (aws-broker.ts). BELT AND BRACES ONLY: the real guard is
|
|
96
|
+
# IAM — every profile fetches credentials for a role carrying AWS's
|
|
97
|
+
# ReadOnlyAccess and nothing else, so a mutation is refused by the API
|
|
98
|
+
# whatever gets typed here. These patterns make the refusal LOCAL and
|
|
99
|
+
# legible instead of an AccessDenied the model has to interpret, and they
|
|
100
|
+
# cover the verbs whose blast radius is worst if IAM is ever loosened.
|
|
101
|
+
- pattern: "aws *delete*"
|
|
102
|
+
action: deny
|
|
103
|
+
- pattern: "aws *put*"
|
|
104
|
+
action: deny
|
|
105
|
+
- pattern: "aws *create*"
|
|
106
|
+
action: deny
|
|
107
|
+
- pattern: "aws *update*"
|
|
108
|
+
action: deny
|
|
109
|
+
- pattern: "aws *terminate*"
|
|
110
|
+
action: deny
|
|
111
|
+
- pattern: "aws *modify*"
|
|
112
|
+
action: deny
|
|
113
|
+
- pattern: "aws *attach*"
|
|
114
|
+
action: deny
|
|
115
|
+
- pattern: "aws *detach*"
|
|
116
|
+
action: deny
|
|
117
|
+
- pattern: "aws *run-instances*"
|
|
118
|
+
action: deny
|
|
119
|
+
# `start-query` and `stop-query` (Logs Insights) are how the log tools
|
|
120
|
+
# would be driven from bash, so the start/stop verbs are NOT denied
|
|
121
|
+
# wholesale. `aws logs put-*` is already covered by the `put` rule above.
|
|
122
|
+
- pattern: "aws *start-instances*"
|
|
123
|
+
action: deny
|
|
124
|
+
- pattern: "aws *stop-instances*"
|
|
125
|
+
action: deny
|
|
126
|
+
# omp's native auto-learn: after a substantial turn the agent may distill what
|
|
127
|
+
# it did into `<agentDir>/managed-skills/<name>/SKILL.md` — a path symlinked to
|
|
128
|
+
# the persistent mount, so a learned skill survives the microVM. `autoContinue`
|
|
129
|
+
# is off: learning must never extend a turn the human is waiting on. `learn`
|
|
130
|
+
# (the other omp learning tool) is available only when an instance sets
|
|
131
|
+
# `memory.ompBackend: local`, which is off by default.
|
|
132
|
+
autolearn:
|
|
133
|
+
enabled: true
|
|
134
|
+
minToolCalls: 6
|
|
135
|
+
autoContinue: false
|
|
136
|
+
task:
|
|
137
|
+
# Recursion ceiling (omp's own default, pinned explicitly). The child's TOOL
|
|
138
|
+
# LIST is NOT here — it comes from the materialized
|
|
139
|
+
# `<workspace>/.omp/agents/task.md` definition; this file carries the bounds
|
|
140
|
+
# that must survive a project-config write failure.
|
|
141
|
+
maxRecursionDepth: 2
|
|
142
|
+
isolation:
|
|
143
|
+
# Isolated subagents are OFF. An isolated child is the one child type omp
|
|
144
|
+
# constructs with BOTH extension preload lists cleared
|
|
145
|
+
# (task/isolation-runner.ts), so it would rebuild its tool set from ambient
|
|
146
|
+
# discovery. With mode "none" the `isolated` parameter is not offered on the
|
|
147
|
+
# task schema and any request for it throws at preflight, so such a child
|
|
148
|
+
# cannot exist.
|
|
149
|
+
mode: none
|
|
150
|
+
# omp's default. `per-call` was pinned by the TS rewrite for "isolation
|
|
151
|
+
# between calls" the microVM already provides, while omp's eval tool text
|
|
152
|
+
# promises state persistence — see the matching setting in exec.ts.
|
|
153
|
+
python:
|
|
154
|
+
kernelMode: session
|
|
155
|
+
# The two capabilities that would otherwise AUTO-WIDEN an explicit tool list
|
|
156
|
+
# with a tool this file denies — at the top level and in every subagent.
|
|
157
|
+
astEdit:
|
|
158
|
+
# omp's default is TRUE, and it pushes the denied `ast_edit` into any explicit
|
|
159
|
+
# tool list containing `edit`. Off, not just denied.
|
|
160
|
+
enabled: false
|
|
161
|
+
web_search:
|
|
162
|
+
enabled: false
|
|
163
|
+
magicKeywords:
|
|
164
|
+
enabled: true
|
|
165
|
+
ultrathink: true
|
|
166
|
+
# Keep OMP's other built-in prompt modes off; this change is only for
|
|
167
|
+
# ultrathink. OMP 18.1.14 has no ultracode magic keyword or hook.
|
|
168
|
+
orchestrate: false
|
|
169
|
+
workflow: false
|
|
170
|
+
# NOTE: `memory.backend` is deliberately ABSENT from this fail-hard overlay.
|
|
171
|
+
# It is written per session into `<workspace>/.omp/config.yml` from
|
|
172
|
+
# `config.memory.ompBackend`, so an instance can opt in without an image build.
|
|
173
|
+
# This fails safe: omp's own schema default is `off`, so a project-config write
|
|
174
|
+
# that never lands leaves omp's memory subsystem off.
|
|
175
|
+
mcp:
|
|
176
|
+
enableProjectConfig: false
|
package/cdk.json
ADDED
package/dist/bin/app.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {
|
|
3
|
+
FoundationAgent,
|
|
4
|
+
FoundationApi,
|
|
5
|
+
FoundationCi,
|
|
6
|
+
FoundationData,
|
|
7
|
+
FoundationNetwork,
|
|
8
|
+
FoundationPipeline,
|
|
9
|
+
NewsletterStack
|
|
10
|
+
} from "../chunk-4aye5cee.js";
|
|
11
|
+
import {
|
|
12
|
+
adminsFor,
|
|
13
|
+
instanceNames,
|
|
14
|
+
loadInstanceFile
|
|
15
|
+
} from "../chunk-9ddxyvq2.js";
|
|
16
|
+
|
|
17
|
+
// bin/app.ts
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { basename, dirname, relative, resolve, sep } from "node:path";
|
|
20
|
+
import * as cdk from "aws-cdk-lib";
|
|
21
|
+
var app = new cdk.App;
|
|
22
|
+
var instanceFilePath = app.node.tryGetContext("instanceFile");
|
|
23
|
+
if (instanceFilePath === undefined || instanceFilePath === "")
|
|
24
|
+
throw new Error("-c instanceFile=<path> is required: the path to the deployment's instance YAML");
|
|
25
|
+
var { instance, configPath } = loadInstanceFile(instanceFilePath);
|
|
26
|
+
var names = instanceNames(instance);
|
|
27
|
+
var env = {
|
|
28
|
+
account: process.env.CDK_DEFAULT_ACCOUNT,
|
|
29
|
+
region: process.env.CDK_DEFAULT_REGION ?? instance.aws.region
|
|
30
|
+
};
|
|
31
|
+
var admins = app.node.tryGetContext("admins") ?? adminsFor(configPath);
|
|
32
|
+
var alarmEmail = app.node.tryGetContext("alarmEmail");
|
|
33
|
+
var pipelineInstanceFile = app.node.tryGetContext("pipelineInstanceFile") ?? repoRelativePath(instanceFilePath);
|
|
34
|
+
var network = new FoundationNetwork(app, names.network, { env, instance, configPath });
|
|
35
|
+
var data = new FoundationData(app, names.data, { env, instance });
|
|
36
|
+
var agent = new FoundationAgent(app, names.agent, {
|
|
37
|
+
env,
|
|
38
|
+
instance,
|
|
39
|
+
vpc: network.vpc,
|
|
40
|
+
egressSubnets: network.egressSubnets,
|
|
41
|
+
agentSecurityGroup: network.agentSecurityGroup,
|
|
42
|
+
mountTargetSecurityGroup: network.mountTargetSecurityGroup,
|
|
43
|
+
fileSystemId: data.fileSystem.getAtt("FileSystemId").toString(),
|
|
44
|
+
dataKey: data.dataKey,
|
|
45
|
+
bucket: data.bucket,
|
|
46
|
+
documentBucket: data.documentBucket,
|
|
47
|
+
table: data.table,
|
|
48
|
+
itemsTable: data.itemsTable,
|
|
49
|
+
...data.crmTable === undefined ? {} : { crmTable: data.crmTable },
|
|
50
|
+
...data.upworkApprovalTable === undefined ? {} : { upworkApprovalTable: data.upworkApprovalTable },
|
|
51
|
+
secrets: data.secrets
|
|
52
|
+
});
|
|
53
|
+
new FoundationCi(app, names.ci, { env, instance, bucket: data.bucket, dataKey: data.dataKey });
|
|
54
|
+
if (instance.deploy.via === "codepipeline")
|
|
55
|
+
new FoundationPipeline(app, names.pipeline, {
|
|
56
|
+
env,
|
|
57
|
+
instance,
|
|
58
|
+
bucket: data.bucket,
|
|
59
|
+
dataKey: data.dataKey,
|
|
60
|
+
instanceFilePath: pipelineInstanceFile,
|
|
61
|
+
...alarmEmail === undefined ? {} : { alarmEmail }
|
|
62
|
+
});
|
|
63
|
+
var api = new FoundationApi(app, names.api, {
|
|
64
|
+
env,
|
|
65
|
+
instance,
|
|
66
|
+
configPath,
|
|
67
|
+
dataKey: data.dataKey,
|
|
68
|
+
table: data.table,
|
|
69
|
+
itemsTable: data.itemsTable,
|
|
70
|
+
...data.crmTable === undefined ? {} : { crmTable: data.crmTable },
|
|
71
|
+
...data.upworkApprovalTable === undefined ? {} : { upworkApprovalTable: data.upworkApprovalTable },
|
|
72
|
+
secrets: {
|
|
73
|
+
signing: data.secrets.signing,
|
|
74
|
+
slackApp: data.secrets.slackApp,
|
|
75
|
+
googleCalendar: data.secrets.googleCalendar,
|
|
76
|
+
googleOauth: data.secrets.googleOauth,
|
|
77
|
+
googleDrive: data.secrets.googleDrive,
|
|
78
|
+
googleEmail: data.secrets.googleEmail,
|
|
79
|
+
...data.secrets.otterApi === undefined ? {} : { otterApi: data.secrets.otterApi },
|
|
80
|
+
...data.secrets.knockOauthClient === undefined ? {} : { knockOauthClient: data.secrets.knockOauthClient },
|
|
81
|
+
...data.secrets.knockCredential === undefined ? {} : { knockCredential: data.secrets.knockCredential },
|
|
82
|
+
...data.secrets.upwork === undefined ? {} : { upwork: data.secrets.upwork }
|
|
83
|
+
},
|
|
84
|
+
agentRuntimeArn: agent.agentRuntimeArn,
|
|
85
|
+
admins,
|
|
86
|
+
alarmEmail
|
|
87
|
+
});
|
|
88
|
+
api.addStackDependency(agent);
|
|
89
|
+
if (instance.newsletter.enabled)
|
|
90
|
+
new NewsletterStack(app, names.newsletter, {
|
|
91
|
+
env,
|
|
92
|
+
instance,
|
|
93
|
+
...alarmEmail === undefined ? {} : { alarmEmail }
|
|
94
|
+
});
|
|
95
|
+
cdk.Tags.of(app).add("project", instance.naming.secretPrefix);
|
|
96
|
+
app.synth();
|
|
97
|
+
function repoRelativePath(filePath) {
|
|
98
|
+
const absolute = resolve(filePath);
|
|
99
|
+
try {
|
|
100
|
+
const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
101
|
+
cwd: dirname(absolute),
|
|
102
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
103
|
+
}).toString().trim();
|
|
104
|
+
const rel = relative(top, absolute);
|
|
105
|
+
if (rel !== "" && !rel.startsWith(".."))
|
|
106
|
+
return rel.split(sep).join("/");
|
|
107
|
+
} catch {}
|
|
108
|
+
return basename(absolute);
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
env
|
|
112
|
+
};
|