declapract-typescript-ehmpathy 0.40.0 → 0.42.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/practices/cicd-common/best-practice/.github/workflows/.install.yml +9 -6
- package/dist/practices/cicd-common/best-practice/.github/workflows/.test.yml +17 -11
- package/dist/practices/cicd-common/best-practice/.github/workflows/release.yml +2 -2
- package/dist/practices/cicd-common/best-practice/.github/workflows/review.yml +1 -1
- package/dist/practices/cicd-service/best-practice/.agent/repo=.this/skills/use.rds.capacity.sh +45 -0
- package/dist/practices/cicd-service/best-practice/.agent/repo=.this/skills/use.vpc.tunnel.ts +79 -0
- package/dist/practices/cicd-service/best-practice/.github/workflows/.deploy-sls.yml +11 -46
- package/dist/practices/cicd-service/best-practice/.github/workflows/.sql-schema-control.yml +12 -59
- package/dist/practices/cicd-service/best-practice/.github/workflows/.terraform.yml +5 -5
- package/dist/practices/cicd-service/best-practice/.github/workflows/provision.yml +0 -2
- package/dist/practices/cicd-service/best-practice/package.json +7 -0
- package/dist/practices/cicd-service/best-practice/package.json.declapract.ts +3 -0
- package/dist/practices/domain/best-practice/package.json +2 -2
- package/dist/practices/git/best-practice/.gitignore.declapract.ts +1 -0
- package/dist/practices/package-json-order/best-practice/package.json.declapract.ts +8 -6
- package/dist/practices/persist-with-dynamodb/best-practice/package.json +5 -4
- package/dist/practices/persist-with-rds/bad-practices/old-packagejson-script-names/package.json +9 -0
- package/dist/practices/persist-with-rds/bad-practices/old-packagejson-script-names/package.json.declapract.ts +22 -0
- package/dist/practices/persist-with-rds/best-practice/config/dev.json +21 -13
- package/dist/practices/persist-with-rds/best-practice/config/prod.json +21 -13
- package/dist/practices/persist-with-rds/best-practice/config/prod.json.declapract.ts +13 -1
- package/dist/practices/persist-with-rds/best-practice/config/test.json +18 -13
- package/dist/practices/persist-with-rds/best-practice/config/test.json.declapract.ts +13 -1
- package/dist/practices/persist-with-rds/best-practice/package.json +8 -6
- package/dist/practices/persist-with-rds/best-practice/provision/schema/connection.config.js +10 -11
- package/dist/practices/persist-with-rds/best-practice/provision/schema/deploy.database.sh +1 -1
- package/dist/practices/persist-with-rds/best-practice/src/utils/config/Config.ts +26 -0
- package/dist/practices/persist-with-rds/best-practice/src/utils/config/Config.ts.declapract.ts +39 -0
- package/dist/practices/persist-with-rds/best-practice/src/utils/database/getDatabaseConnection.ts +30 -9
- package/dist/practices/pnpm/bad-practices/package-lock-json/package-lock.json.declapract.ts +3 -0
- package/dist/practices/pnpm/best-practice/.agent/repo=.this/skills/use.npm.alias.sh +44 -0
- package/dist/practices/pnpm/best-practice/package.json +3 -0
- package/dist/practices/pnpm/best-practice/package.json.declapract.ts +3 -0
- package/dist/practices/provision-github/best-practice/package.json +2 -2
- package/dist/practices/tests/best-practice/package.json +1 -1
- package/dist/practices/typescript/best-practice/tsconfig.build.json +7 -6
- package/dist/useCases.yml +1 -0
- package/package.json +2 -2
|
@@ -5,10 +5,10 @@ on:
|
|
|
5
5
|
outputs:
|
|
6
6
|
node-modules-cache-key:
|
|
7
7
|
description: a max(stable) cache key to the node modules of this commit's dependencies
|
|
8
|
-
value: ${{ jobs.
|
|
8
|
+
value: ${{ jobs.pnpm.outputs.node-modules-cache-key }}
|
|
9
9
|
|
|
10
10
|
jobs:
|
|
11
|
-
|
|
11
|
+
pnpm:
|
|
12
12
|
runs-on: ubuntu-24.04
|
|
13
13
|
outputs:
|
|
14
14
|
node-modules-cache-key: ${{ steps.cache.outputs.cache-primary-key }}
|
|
@@ -16,15 +16,18 @@ jobs:
|
|
|
16
16
|
- name: checkout
|
|
17
17
|
uses: actions/checkout@v3
|
|
18
18
|
|
|
19
|
+
- name: setup pnpm
|
|
20
|
+
uses: pnpm/action-setup@v4
|
|
21
|
+
|
|
19
22
|
- name: set node-version
|
|
20
23
|
uses: actions/setup-node@v3
|
|
21
24
|
with:
|
|
22
|
-
node-version-file:
|
|
25
|
+
node-version-file: ".nvmrc"
|
|
23
26
|
|
|
24
27
|
- name: node-modules deps hash
|
|
25
28
|
id: deps-hash
|
|
26
29
|
run: |
|
|
27
|
-
PACKAGE_DEPS_HASH=$(
|
|
30
|
+
PACKAGE_DEPS_HASH=$(md5sum pnpm-lock.yaml | awk '{print $1}');
|
|
28
31
|
echo "PACKAGE_DEPS_HASH=$PACKAGE_DEPS_HASH"
|
|
29
32
|
echo "package-deps-hash=$PACKAGE_DEPS_HASH" >> "$GITHUB_OUTPUT"
|
|
30
33
|
- name: node-modules cache get
|
|
@@ -32,11 +35,11 @@ jobs:
|
|
|
32
35
|
id: cache
|
|
33
36
|
with:
|
|
34
37
|
path: ./node_modules
|
|
35
|
-
key: ${{ runner.os }}-
|
|
38
|
+
key: ${{ runner.os }}-pnpm-${{ steps.deps-hash.outputs.package-deps-hash }}
|
|
36
39
|
|
|
37
40
|
- name: node-modules cache miss install
|
|
38
41
|
if: steps.cache.outputs.cache-hit != 'true'
|
|
39
|
-
run:
|
|
42
|
+
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
40
43
|
|
|
41
44
|
- name: node-modules cache set
|
|
42
45
|
if: steps.cache.outputs.cache-hit != 'true'
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
- name: set node-version
|
|
37
37
|
uses: actions/setup-node@v3
|
|
38
38
|
with:
|
|
39
|
-
node-version-file:
|
|
39
|
+
node-version-file: ".nvmrc"
|
|
40
40
|
|
|
41
41
|
- name: get node-modules from cache
|
|
42
42
|
uses: actions/cache/restore@v4
|
|
@@ -57,7 +57,7 @@ jobs:
|
|
|
57
57
|
- name: set node-version
|
|
58
58
|
uses: actions/setup-node@v3
|
|
59
59
|
with:
|
|
60
|
-
node-version-file:
|
|
60
|
+
node-version-file: ".nvmrc"
|
|
61
61
|
|
|
62
62
|
- name: get node-modules from cache
|
|
63
63
|
uses: actions/cache/restore@v4
|
|
@@ -78,7 +78,7 @@ jobs:
|
|
|
78
78
|
- name: set node version
|
|
79
79
|
uses: actions/setup-node@v3
|
|
80
80
|
with:
|
|
81
|
-
node-version-file:
|
|
81
|
+
node-version-file: ".nvmrc"
|
|
82
82
|
|
|
83
83
|
- name: set terraform version
|
|
84
84
|
uses: hashicorp/setup-terraform@v3
|
|
@@ -102,7 +102,7 @@ jobs:
|
|
|
102
102
|
- name: set node-version
|
|
103
103
|
uses: actions/setup-node@v3
|
|
104
104
|
with:
|
|
105
|
-
node-version-file:
|
|
105
|
+
node-version-file: ".nvmrc"
|
|
106
106
|
|
|
107
107
|
- name: get node-modules from cache
|
|
108
108
|
uses: actions/cache/restore@v4
|
|
@@ -123,7 +123,7 @@ jobs:
|
|
|
123
123
|
- name: set node-version
|
|
124
124
|
uses: actions/setup-node@v3
|
|
125
125
|
with:
|
|
126
|
-
node-version-file:
|
|
126
|
+
node-version-file: ".nvmrc"
|
|
127
127
|
|
|
128
128
|
- name: get node-modules from cache
|
|
129
129
|
uses: actions/cache/restore@v4
|
|
@@ -144,7 +144,7 @@ jobs:
|
|
|
144
144
|
- name: set node-version
|
|
145
145
|
uses: actions/setup-node@v3
|
|
146
146
|
with:
|
|
147
|
-
node-version-file:
|
|
147
|
+
node-version-file: ".nvmrc"
|
|
148
148
|
|
|
149
149
|
- name: get node-modules from cache
|
|
150
150
|
uses: actions/cache/restore@v4
|
|
@@ -168,8 +168,11 @@ jobs:
|
|
|
168
168
|
&& echo 'wrong aws account' && exit 1 \
|
|
169
169
|
|| echo 'correct aws account';
|
|
170
170
|
|
|
171
|
-
- name:
|
|
172
|
-
run: npm run
|
|
171
|
+
- name: start:testdb
|
|
172
|
+
run: npm run start:testdb --if-present
|
|
173
|
+
|
|
174
|
+
- name: start:livedb:dev
|
|
175
|
+
run: npm run start:livedb:dev --if-present
|
|
173
176
|
|
|
174
177
|
- name: test:integration
|
|
175
178
|
run: THOROUGH=true npm run test:integration
|
|
@@ -184,7 +187,7 @@ jobs:
|
|
|
184
187
|
- name: set node-version
|
|
185
188
|
uses: actions/setup-node@v3
|
|
186
189
|
with:
|
|
187
|
-
node-version-file:
|
|
190
|
+
node-version-file: ".nvmrc"
|
|
188
191
|
|
|
189
192
|
- name: get node-modules from cache
|
|
190
193
|
uses: actions/cache/restore@v4
|
|
@@ -208,8 +211,11 @@ jobs:
|
|
|
208
211
|
&& echo 'wrong aws account' && exit 1 \
|
|
209
212
|
|| echo 'correct aws account';
|
|
210
213
|
|
|
211
|
-
- name:
|
|
212
|
-
run: npm run
|
|
214
|
+
- name: start:testdb
|
|
215
|
+
run: npm run start:testdb --if-present
|
|
216
|
+
|
|
217
|
+
- name: start:livedb:dev
|
|
218
|
+
run: npm run start:livedb:dev --if-present
|
|
213
219
|
|
|
214
220
|
- name: test:acceptance:locally
|
|
215
221
|
run: npm run test:acceptance:locally
|
|
@@ -9,9 +9,9 @@ jobs:
|
|
|
9
9
|
release-please:
|
|
10
10
|
runs-on: ubuntu-24.04
|
|
11
11
|
steps:
|
|
12
|
-
- uses: google-github-actions/release-please-action@v3
|
|
12
|
+
- uses: google-github-actions/release-please-action@v3.7.6 # https://github.com/googleapis/release-please-action/issues/840
|
|
13
13
|
with:
|
|
14
14
|
token: ${{ secrets.RELEASE_PLEASE_GITHUB_TOKEN }}
|
|
15
15
|
release-type: node
|
|
16
|
-
pull-request-title-pattern:
|
|
16
|
+
pull-request-title-pattern: "chore(release): v${version} 🎉"
|
|
17
17
|
changelog-path: changelog.md
|
package/dist/practices/cicd-service/best-practice/.agent/repo=.this/skills/use.rds.capacity.sh
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# SKILL: use.rds.capacity
|
|
4
|
+
#
|
|
5
|
+
# Ensures the RDS database has capacity and is ready to accept connections.
|
|
6
|
+
#
|
|
7
|
+
# What it does:
|
|
8
|
+
# 1. Opens a VPC tunnel to the database cluster (via use.vpc.tunnel)
|
|
9
|
+
# 2. Extracts the database host and port from the tunnel configuration
|
|
10
|
+
# 3. Polls the database until it responds (waking serverless RDS if paused)
|
|
11
|
+
#
|
|
12
|
+
# When to use:
|
|
13
|
+
# - Before running tests or migrations that need database access
|
|
14
|
+
# - When a serverless RDS instance may be paused and needs to be awakened
|
|
15
|
+
# - Any time you need to ensure the database is ready before proceeding
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# STAGE=dev ./.agent/repo=.this/skills/use.rds.capacity.sh
|
|
19
|
+
#
|
|
20
|
+
# Prerequisites:
|
|
21
|
+
# - STAGE environment variable must be set
|
|
22
|
+
# - AWS credentials configured with SSM access
|
|
23
|
+
# - sudo access (for /etc/hosts modification via vpc tunnel)
|
|
24
|
+
# - pg_isready command available (postgresql-client)
|
|
25
|
+
#
|
|
26
|
+
set -eo pipefail
|
|
27
|
+
|
|
28
|
+
# failfast if STAGE is not declared
|
|
29
|
+
[[ -z "${STAGE:-}" ]] && echo "STAGE is not set" && exit 1
|
|
30
|
+
|
|
31
|
+
set -u
|
|
32
|
+
|
|
33
|
+
# ensure the dev tunnel is awake
|
|
34
|
+
.agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
35
|
+
|
|
36
|
+
# ping until available
|
|
37
|
+
npx declastruct plan --wish .agent/repo=.this/skills/use.vpc.tunnel.ts --into .temp/tunnel.plan.json
|
|
38
|
+
DB_HOST=$(jq -r '.changes[] | select(.forResource.class == "DeclaredUnixHostAlias") | .state.desired.from' .temp/tunnel.plan.json)
|
|
39
|
+
DB_PORT=$(jq -r '.changes[] | select(.forResource.class == "DeclaredAwsVpcTunnel") | .state.desired.from.port' .temp/tunnel.plan.json)
|
|
40
|
+
|
|
41
|
+
# await for the database to have capacity (awakens serverless rds if paused)
|
|
42
|
+
echo "Awaiting database capacity at $DB_HOST:$DB_PORT..."
|
|
43
|
+
timeout 180 bash -c "until pg_isready -h $DB_HOST -p $DB_PORT; do sleep 5; done"
|
|
44
|
+
echo "Database is ready"
|
|
45
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
//bin/true && exec npx declastruct apply --plan yolo --wish "$0"
|
|
3
|
+
//
|
|
4
|
+
// SKILL: use.vpc.tunnel
|
|
5
|
+
//
|
|
6
|
+
// Opens a secure VPC tunnel to the ahbodedb database cluster via AWS SSM.
|
|
7
|
+
//
|
|
8
|
+
// What it does:
|
|
9
|
+
// 1. Creates an SSM tunnel through the vpc-main-bastion to the database cluster
|
|
10
|
+
// 2. Binds the tunnel to localhost:$port
|
|
11
|
+
// 3. Adds a /etc/hosts alias so the database can be reached via a friendly hostname
|
|
12
|
+
//
|
|
13
|
+
// When to use:
|
|
14
|
+
// - Before acceptance tests that need remote database access
|
|
15
|
+
// - When you need to connect to the database locally
|
|
16
|
+
// - Any time local code needs to reach an RDS cluster in the VPC
|
|
17
|
+
//
|
|
18
|
+
// Usage:
|
|
19
|
+
// Direct execution: ./.agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
20
|
+
// Via declastruct: npx declastruct apply --plan yolo --wish .agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
21
|
+
//
|
|
22
|
+
// Prerequisites:
|
|
23
|
+
// - AWS credentials configured with SSM access
|
|
24
|
+
// - sudo access (for /etc/hosts modification), if not already set
|
|
25
|
+
//
|
|
26
|
+
// Why via declastruct:
|
|
27
|
+
// Declastruct enables declarative instructions — you specify *what* you want
|
|
28
|
+
// (a tunnel, a host alias) rather than *how* to get it (spawn ssm-proxy, edit
|
|
29
|
+
// /etc/hosts, track PIDs, cleanup, etc...). The runtime diffs current vs desired state
|
|
30
|
+
// and applies only necessary changes. This makes skills more intuitive and
|
|
31
|
+
// maintainable, as well as idempotent and safe to run repeatedly.
|
|
32
|
+
//
|
|
33
|
+
import { DeclastructProvider } from 'declastruct';
|
|
34
|
+
import {
|
|
35
|
+
DeclaredAwsVpcTunnel,
|
|
36
|
+
getDeclastructAwsProvider,
|
|
37
|
+
} from 'declastruct-aws';
|
|
38
|
+
import {
|
|
39
|
+
DeclaredUnixHostAlias,
|
|
40
|
+
getDeclastructUnixNetworkProvider,
|
|
41
|
+
} from 'declastruct-unix-network';
|
|
42
|
+
|
|
43
|
+
import { getConfig } from '../../../src/utils/config/getConfig';
|
|
44
|
+
|
|
45
|
+
export const getProviders = async (): Promise<DeclastructProvider[]> => [
|
|
46
|
+
await getDeclastructAwsProvider({}, { log: console }),
|
|
47
|
+
await getDeclastructUnixNetworkProvider({}, { log: console }),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const getResources = async () => {
|
|
51
|
+
// grab the config
|
|
52
|
+
const config = await getConfig();
|
|
53
|
+
|
|
54
|
+
// open the tunnel
|
|
55
|
+
const tunnel = DeclaredAwsVpcTunnel.as({
|
|
56
|
+
via: {
|
|
57
|
+
mechanism: 'aws.ssm',
|
|
58
|
+
bastion: { exid: 'vpc-main-bastion' },
|
|
59
|
+
},
|
|
60
|
+
into: {
|
|
61
|
+
cluster: { name: 'ahbodedb' },
|
|
62
|
+
},
|
|
63
|
+
from: {
|
|
64
|
+
host: 'localhost',
|
|
65
|
+
port: config.database.tunnel.local.port,
|
|
66
|
+
},
|
|
67
|
+
status: 'OPEN',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// bind the host alias
|
|
71
|
+
const hostAlias = DeclaredUnixHostAlias.as({
|
|
72
|
+
via: '/etc/hosts',
|
|
73
|
+
from: config.database.tunnel.local.host,
|
|
74
|
+
into: '127.0.0.1',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// instruct to set each
|
|
78
|
+
return [tunnel, hostAlias];
|
|
79
|
+
};
|
|
@@ -5,11 +5,11 @@ on:
|
|
|
5
5
|
inputs:
|
|
6
6
|
stage:
|
|
7
7
|
type: string
|
|
8
|
-
description:
|
|
8
|
+
description: "the stage to deploy to"
|
|
9
9
|
required: true
|
|
10
10
|
github-environment:
|
|
11
11
|
type: string
|
|
12
|
-
description:
|
|
12
|
+
description: "the github environment that the apply step will be executed in"
|
|
13
13
|
required: true
|
|
14
14
|
aws-region:
|
|
15
15
|
type: string
|
|
@@ -31,9 +31,6 @@ on:
|
|
|
31
31
|
aws-secret-access-key:
|
|
32
32
|
required: true
|
|
33
33
|
description: required credentials to authenticate with aws provider and state persistance
|
|
34
|
-
open-vpn-config:
|
|
35
|
-
required: false
|
|
36
|
-
description: complete openvpn config required to enter the vpn, if needed
|
|
37
34
|
pagerduty-integration-key:
|
|
38
35
|
required: false
|
|
39
36
|
description: enables sending pagerduty alarms on failure
|
|
@@ -52,7 +49,7 @@ jobs:
|
|
|
52
49
|
- name: set node-version
|
|
53
50
|
uses: actions/setup-node@v3
|
|
54
51
|
with:
|
|
55
|
-
node-version-file:
|
|
52
|
+
node-version-file: ".nvmrc"
|
|
56
53
|
|
|
57
54
|
- name: configure aws credentials
|
|
58
55
|
uses: aws-actions/configure-aws-credentials@v1
|
|
@@ -70,14 +67,10 @@ jobs:
|
|
|
70
67
|
|
|
71
68
|
- name: node-modules cache get
|
|
72
69
|
uses: actions/cache/restore@v4
|
|
73
|
-
id: cache
|
|
74
70
|
with:
|
|
75
71
|
path: ./node_modules
|
|
76
72
|
key: ${{ needs.install.outputs.node-modules-cache-key }}
|
|
77
|
-
|
|
78
|
-
- name: node-modules cache miss install
|
|
79
|
-
if: steps.cache.outputs.cache-hit != 'true'
|
|
80
|
-
run: npm ci --ignore-scripts --prefer-offline --no-audit
|
|
73
|
+
fail-on-cache-miss: true
|
|
81
74
|
|
|
82
75
|
- name: deploy
|
|
83
76
|
run: STAGE=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy
|
|
@@ -92,7 +85,7 @@ jobs:
|
|
|
92
85
|
- name: set node-version
|
|
93
86
|
uses: actions/setup-node@v3
|
|
94
87
|
with:
|
|
95
|
-
node-version-file:
|
|
88
|
+
node-version-file: ".nvmrc"
|
|
96
89
|
|
|
97
90
|
- name: configure aws credentials
|
|
98
91
|
uses: aws-actions/configure-aws-credentials@v1
|
|
@@ -110,49 +103,25 @@ jobs:
|
|
|
110
103
|
|
|
111
104
|
- name: node-modules cache get
|
|
112
105
|
uses: actions/cache/restore@v4
|
|
113
|
-
id: cache
|
|
114
106
|
with:
|
|
115
107
|
path: ./node_modules
|
|
116
108
|
key: ${{ needs.install.outputs.node-modules-cache-key }}
|
|
109
|
+
fail-on-cache-miss: true
|
|
117
110
|
|
|
118
|
-
- name:
|
|
119
|
-
if: steps.cache.outputs.cache-hit != 'true'
|
|
120
|
-
run: npm ci --ignore-scripts --prefer-offline --no-audit
|
|
121
|
-
|
|
122
|
-
- name: vpn:prepare
|
|
123
|
-
if: inputs.needs-vpn-for-acceptance
|
|
124
|
-
run: |
|
|
125
|
-
sudo apt update \
|
|
126
|
-
&& sudo apt-get install openvpn openvpn-systemd-resolved \
|
|
127
|
-
&& mkdir ~/.vpn \
|
|
128
|
-
&& echo "${{ secrets.open-vpn-config }}" | base64 -d > ~/.vpn/vpn.connection.ovpn
|
|
129
|
-
|
|
130
|
-
- name: vpn:connect
|
|
111
|
+
- name: vpc:tunnel:open
|
|
131
112
|
if: inputs.needs-vpn-for-acceptance
|
|
132
|
-
run:
|
|
133
|
-
# create the log file, so that we have permissions to read it
|
|
134
|
-
touch openvpn.log
|
|
135
|
-
|
|
136
|
-
# start openvpn in the background
|
|
137
|
-
sudo openvpn --config ~/.vpn/vpn.connection.ovpn --daemon --log openvpn.log
|
|
138
|
-
|
|
139
|
-
# wait until we've confirmed that it successfully connected; https://superuser.com/a/900134/425694
|
|
140
|
-
( tail -f -n0 openvpn.log & ) | grep -q "Initialization Sequence Completed"
|
|
113
|
+
run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
141
114
|
|
|
142
115
|
- name: test:acceptance
|
|
143
116
|
run: STAGE=${{ inputs.stage }} npm run test:acceptance
|
|
144
117
|
|
|
145
|
-
- name: vpn:disconnect
|
|
146
|
-
if: inputs.needs-vpn-for-acceptance
|
|
147
|
-
run: sudo killall openvpn
|
|
148
|
-
|
|
149
118
|
- name: alarm on failure
|
|
150
119
|
env:
|
|
151
120
|
PAGERDUTY_INTEGRATION_KEY: ${{ secrets.pagerduty-integration-key }}
|
|
152
121
|
if: failure() && env.PAGERDUTY_INTEGRATION_KEY
|
|
153
122
|
uses: Entle/action-pagerduty-alert@0.2.0 # https://github.com/marketplace/actions/pagerduty-alert
|
|
154
123
|
with:
|
|
155
|
-
pagerduty-integration-key:
|
|
124
|
+
pagerduty-integration-key: "${{ secrets.pagerduty-integration-key }}"
|
|
156
125
|
pagerduty-dedup-key: github_workflow_failed
|
|
157
126
|
|
|
158
127
|
prune:
|
|
@@ -165,7 +134,7 @@ jobs:
|
|
|
165
134
|
- name: set node-version
|
|
166
135
|
uses: actions/setup-node@v3
|
|
167
136
|
with:
|
|
168
|
-
node-version-file:
|
|
137
|
+
node-version-file: ".nvmrc"
|
|
169
138
|
|
|
170
139
|
- name: configure aws credentials
|
|
171
140
|
uses: aws-actions/configure-aws-credentials@v1
|
|
@@ -183,14 +152,10 @@ jobs:
|
|
|
183
152
|
|
|
184
153
|
- name: node-modules cache get
|
|
185
154
|
uses: actions/cache/restore@v4
|
|
186
|
-
id: cache
|
|
187
155
|
with:
|
|
188
156
|
path: ./node_modules
|
|
189
157
|
key: ${{ needs.install.outputs.node-modules-cache-key }}
|
|
190
|
-
|
|
191
|
-
- name: node-modules cache miss install
|
|
192
|
-
if: steps.cache.outputs.cache-hit != 'true'
|
|
193
|
-
run: npm ci --ignore-scripts --prefer-offline --no-audit
|
|
158
|
+
fail-on-cache-miss: true
|
|
194
159
|
|
|
195
160
|
- name: prune
|
|
196
161
|
run: STAGE=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy:prune
|
|
@@ -5,14 +5,14 @@ on:
|
|
|
5
5
|
inputs:
|
|
6
6
|
stage:
|
|
7
7
|
type: string
|
|
8
|
-
description:
|
|
8
|
+
description: "the stage to execute against"
|
|
9
9
|
required: true
|
|
10
10
|
github-environment:
|
|
11
11
|
type: string
|
|
12
|
-
description:
|
|
12
|
+
description: "the github environment that the apply step will be executed in"
|
|
13
13
|
allow-apply:
|
|
14
14
|
type: boolean
|
|
15
|
-
description:
|
|
15
|
+
description: "whether the apply step is enabled. defaults to true on main"
|
|
16
16
|
default: ${{ github.ref == 'refs/heads/main' }}
|
|
17
17
|
aws-region:
|
|
18
18
|
type: string
|
|
@@ -27,9 +27,6 @@ on:
|
|
|
27
27
|
aws-secret-access-key:
|
|
28
28
|
required: true
|
|
29
29
|
description: required credentials to authenticate with aws provider for db credentials
|
|
30
|
-
open-vpn-config:
|
|
31
|
-
required: true
|
|
32
|
-
description: complete openvpn config required to enter the vpn
|
|
33
30
|
|
|
34
31
|
jobs:
|
|
35
32
|
install:
|
|
@@ -47,18 +44,14 @@ jobs:
|
|
|
47
44
|
- name: set node-version
|
|
48
45
|
uses: actions/setup-node@v3
|
|
49
46
|
with:
|
|
50
|
-
node-version-file:
|
|
47
|
+
node-version-file: ".nvmrc"
|
|
51
48
|
|
|
52
49
|
- name: node-modules cache get
|
|
53
50
|
uses: actions/cache/restore@v4
|
|
54
|
-
id: cache
|
|
55
51
|
with:
|
|
56
52
|
path: ./node_modules
|
|
57
53
|
key: ${{ needs.install.outputs.node-modules-cache-key }}
|
|
58
|
-
|
|
59
|
-
- name: node-modules cache miss install
|
|
60
|
-
if: steps.cache.outputs.cache-hit != 'true'
|
|
61
|
-
run: npm ci --ignore-scripts --prefer-offline --no-audit
|
|
54
|
+
fail-on-cache-miss: true
|
|
62
55
|
|
|
63
56
|
- name: configure aws credentials
|
|
64
57
|
uses: aws-actions/configure-aws-credentials@v1
|
|
@@ -74,23 +67,8 @@ jobs:
|
|
|
74
67
|
&& echo 'wrong aws account' && exit 1 \
|
|
75
68
|
|| echo 'correct aws account';
|
|
76
69
|
|
|
77
|
-
- name:
|
|
78
|
-
run:
|
|
79
|
-
sudo apt update \
|
|
80
|
-
&& sudo apt-get install openvpn openvpn-systemd-resolved \
|
|
81
|
-
&& mkdir ~/.vpn \
|
|
82
|
-
&& echo "${{ secrets.open-vpn-config }}" | base64 -d > ~/.vpn/vpn.connection.ovpn
|
|
83
|
-
|
|
84
|
-
- name: vpn:connect
|
|
85
|
-
run: |
|
|
86
|
-
# create the log file, so that we have permissions to read it
|
|
87
|
-
touch openvpn.log
|
|
88
|
-
|
|
89
|
-
# start openvpn in the background
|
|
90
|
-
sudo openvpn --config ~/.vpn/vpn.connection.ovpn --daemon --log openvpn.log
|
|
91
|
-
|
|
92
|
-
# wait until we've confirmed that it successfully connected; https://superuser.com/a/900134/425694
|
|
93
|
-
( tail -f -n0 openvpn.log & ) | grep -q "Initialization Sequence Completed"
|
|
70
|
+
- name: vpc:tunnel:open
|
|
71
|
+
run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
94
72
|
|
|
95
73
|
- name: plan
|
|
96
74
|
run: STAGE=${{ inputs.stage }} npm run provision:schema:plan | tee ./plan.log
|
|
@@ -101,7 +79,7 @@ jobs:
|
|
|
101
79
|
# check that there was not a connection error
|
|
102
80
|
if grep "connect ETIMEDOUT" ./plan.log
|
|
103
81
|
then
|
|
104
|
-
echo "🛑 connection timed out, could not execute plan. is
|
|
82
|
+
echo "🛑 connection timed out, could not execute plan. is vpc tunnel working?"
|
|
105
83
|
exit 1
|
|
106
84
|
fi
|
|
107
85
|
|
|
@@ -116,9 +94,6 @@ jobs:
|
|
|
116
94
|
- name: has changes planned?
|
|
117
95
|
run: echo "${{ steps.evaluate-plan.outputs.has-changes-planned }}"
|
|
118
96
|
|
|
119
|
-
- name: vpn:disconnect
|
|
120
|
-
run: sudo killall openvpn
|
|
121
|
-
|
|
122
97
|
apply:
|
|
123
98
|
runs-on: ubuntu-24.04
|
|
124
99
|
environment: ${{ inputs.github-environment }}
|
|
@@ -131,18 +106,14 @@ jobs:
|
|
|
131
106
|
- name: set node-version
|
|
132
107
|
uses: actions/setup-node@v3
|
|
133
108
|
with:
|
|
134
|
-
node-version-file:
|
|
109
|
+
node-version-file: ".nvmrc"
|
|
135
110
|
|
|
136
111
|
- name: node-modules cache get
|
|
137
112
|
uses: actions/cache/restore@v4
|
|
138
|
-
id: cache
|
|
139
113
|
with:
|
|
140
114
|
path: ./node_modules
|
|
141
115
|
key: ${{ needs.install.outputs.node-modules-cache-key }}
|
|
142
|
-
|
|
143
|
-
- name: node-modules cache miss install
|
|
144
|
-
if: steps.cache.outputs.cache-hit != 'true'
|
|
145
|
-
run: npm ci --ignore-scripts --prefer-offline --no-audit
|
|
116
|
+
fail-on-cache-miss: true
|
|
146
117
|
|
|
147
118
|
- name: configure aws credentials
|
|
148
119
|
uses: aws-actions/configure-aws-credentials@v1
|
|
@@ -158,26 +129,8 @@ jobs:
|
|
|
158
129
|
&& echo 'wrong aws account' && exit 1 \
|
|
159
130
|
|| echo 'correct aws account';
|
|
160
131
|
|
|
161
|
-
- name:
|
|
162
|
-
run:
|
|
163
|
-
sudo apt update \
|
|
164
|
-
&& sudo apt-get install openvpn openvpn-systemd-resolved \
|
|
165
|
-
&& mkdir ~/.vpn \
|
|
166
|
-
&& echo "${{ secrets.open-vpn-config }}" | base64 -d > ~/.vpn/vpn.connection.ovpn
|
|
167
|
-
|
|
168
|
-
- name: vpn:connect
|
|
169
|
-
run: |
|
|
170
|
-
# create the log file, so that we have permissions to read it
|
|
171
|
-
touch openvpn.log
|
|
172
|
-
|
|
173
|
-
# start openvpn in the background
|
|
174
|
-
sudo openvpn --config ~/.vpn/vpn.connection.ovpn --daemon --log openvpn.log
|
|
175
|
-
|
|
176
|
-
# wait until we've confirmed that it successfully connected; https://superuser.com/a/900134/425694
|
|
177
|
-
( tail -f -n0 openvpn.log & ) | grep -q "Initialization Sequence Completed"
|
|
132
|
+
- name: vpc:tunnel:open
|
|
133
|
+
run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
|
|
178
134
|
|
|
179
135
|
- name: apply
|
|
180
136
|
run: STAGE=${{ inputs.stage }} npm run provision:schema:apply
|
|
181
|
-
|
|
182
|
-
- name: vpn:disconnect
|
|
183
|
-
run: sudo killall openvpn
|
|
@@ -5,13 +5,13 @@ on:
|
|
|
5
5
|
inputs:
|
|
6
6
|
working-directory:
|
|
7
7
|
type: string
|
|
8
|
-
description:
|
|
8
|
+
description: "the directory from within which to execute terraform commands"
|
|
9
9
|
github-environment:
|
|
10
10
|
type: string
|
|
11
|
-
description:
|
|
11
|
+
description: "the github environment that the apply step will be executed in"
|
|
12
12
|
allow-apply:
|
|
13
13
|
type: boolean
|
|
14
|
-
description:
|
|
14
|
+
description: "whether the apply step is enabled. defaults to true on main"
|
|
15
15
|
default: ${{ github.ref == 'refs/heads/main' }}
|
|
16
16
|
aws-region:
|
|
17
17
|
type: string
|
|
@@ -32,7 +32,7 @@ on:
|
|
|
32
32
|
|
|
33
33
|
jobs:
|
|
34
34
|
plan:
|
|
35
|
-
runs-on: ubuntu-
|
|
35
|
+
runs-on: ubuntu-24.04
|
|
36
36
|
defaults:
|
|
37
37
|
run:
|
|
38
38
|
working-directory: ${{ inputs.working-directory }}
|
|
@@ -85,7 +85,7 @@ jobs:
|
|
|
85
85
|
run: echo "${{ steps.evaluate-plan.outputs.has-changes-planned }}"
|
|
86
86
|
|
|
87
87
|
apply:
|
|
88
|
-
runs-on: ubuntu-
|
|
88
|
+
runs-on: ubuntu-24.04
|
|
89
89
|
environment: ${{ inputs.github-environment }}
|
|
90
90
|
needs: plan
|
|
91
91
|
if: ${{ inputs.allow-apply == true && needs.plan.outputs.has-changes-planned == 'true' }}
|
|
@@ -67,7 +67,6 @@ jobs:
|
|
|
67
67
|
secrets:
|
|
68
68
|
aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
|
|
69
69
|
aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
|
70
|
-
open-vpn-config: ${{ secrets.DEV_OPEN_VPN_CONFIG }}
|
|
71
70
|
|
|
72
71
|
sql-schema-prod:
|
|
73
72
|
uses: ./.github/workflows/.sql-schema-control.yml
|
|
@@ -80,4 +79,3 @@ jobs:
|
|
|
80
79
|
secrets:
|
|
81
80
|
aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
|
|
82
81
|
aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}
|
|
83
|
-
open-vpn-config: ${{ secrets.PROD_OPEN_VPN_CONFIG }}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dependencies": {
|
|
3
|
-
"domain-objects": "@declapract{check.minVersion('0.
|
|
3
|
+
"domain-objects": "@declapract{check.minVersion('0.31.0')}",
|
|
4
4
|
"joi": "@declapract{check.minVersion('17.4.0')}",
|
|
5
|
-
"type-fns": "@declapract{check.minVersion('1.
|
|
5
|
+
"type-fns": "@declapract{check.minVersion('1.21.0')}"
|
|
6
6
|
}
|
|
7
7
|
}
|
|
@@ -38,15 +38,17 @@ export const desiredRelativeKeyOrder = {
|
|
|
38
38
|
'build:clean',
|
|
39
39
|
'build:compile',
|
|
40
40
|
'build',
|
|
41
|
-
'provision:docker:clear',
|
|
42
|
-
'provision:docker:prepare',
|
|
43
|
-
'provision:docker:up',
|
|
44
|
-
'provision:docker:await',
|
|
45
|
-
'provision:docker:down',
|
|
46
41
|
'provision:schema:plan',
|
|
47
42
|
'provision:schema:apply',
|
|
48
43
|
'provision:schema:sync',
|
|
49
|
-
'provision:
|
|
44
|
+
'provision:testdb:docker:clear',
|
|
45
|
+
'provision:testdb:docker:prepare',
|
|
46
|
+
'provision:testdb:docker:up',
|
|
47
|
+
'provision:testdb:docker:await',
|
|
48
|
+
'provision:testdb:docker:down',
|
|
49
|
+
'provision:testdb',
|
|
50
|
+
'start:testdb',
|
|
51
|
+
'start:livedb:dev',
|
|
50
52
|
'test:commits',
|
|
51
53
|
'test:types',
|
|
52
54
|
'test:format:prettier',
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"generate:dao:dynamodb": "npx dynamodb-dao-generator generate && npm run fix:format",
|
|
10
|
-
"provision:docker:up": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml up -d --force-recreate --build --renew-anon-volumes",
|
|
11
|
-
"provision:docker:down": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml down",
|
|
12
|
-
"provision:dynamodb:schema": "terraform -chdir=provision/aws/environments/test apply -auto-approve",
|
|
13
|
-
"provision:
|
|
10
|
+
"provision:testdb:docker:up": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml up -d --force-recreate --build --renew-anon-volumes",
|
|
11
|
+
"provision:testdb:docker:down": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml down",
|
|
12
|
+
"provision:testdb:dynamodb:schema": "terraform -chdir=provision/aws/environments/test apply -auto-approve",
|
|
13
|
+
"provision:testdb": "npm run provision:testdb:docker:up && npm run provision:testdb:dynamodb:schema",
|
|
14
|
+
"start:testdb": "npm run provision:testdb"
|
|
14
15
|
}
|
|
15
16
|
}
|
package/dist/practices/persist-with-rds/bad-practices/old-packagejson-script-names/package.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scripts": {
|
|
3
|
+
"provision:docker:clear": "docker rm -f $(docker ps -a -f 'publish=7821' -q) 2>/dev/null || true && echo 'ensured port is available 👍'",
|
|
4
|
+
"provision:docker:prepare": "cp provision/schema/sql/init/.extensions.sql provision/docker/integration-test-db/init/extensions.sql && cp provision/schema/sql/init/.schema.sql provision/docker/integration-test-db/init/schema.sql && cp provision/schema/sql/init/.user.cicd.sql provision/docker/integration-test-db/init/user.cicd.sql",
|
|
5
|
+
"provision:docker:up": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml up -d --force-recreate --build --renew-anon-volumes",
|
|
6
|
+
"provision:docker:await": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml exec -T postgres /root/wait-for-postgres.sh",
|
|
7
|
+
"provision:docker:down": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml down"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FileCheckType, FileFixFunction } from 'declapract';
|
|
2
|
+
|
|
3
|
+
export const check = FileCheckType.CONTAINS;
|
|
4
|
+
|
|
5
|
+
export const fix: FileFixFunction = (contents) => {
|
|
6
|
+
if (!contents) return { contents }; // do nothing if no contents
|
|
7
|
+
const packageJSON = JSON.parse(contents);
|
|
8
|
+
const updatedPackageJSON = {
|
|
9
|
+
...packageJSON,
|
|
10
|
+
scripts: {
|
|
11
|
+
...packageJSON.scripts,
|
|
12
|
+
'provision:docker:clear': undefined,
|
|
13
|
+
'provision:docker:prepare': undefined,
|
|
14
|
+
'provision:docker:up': undefined,
|
|
15
|
+
'provision:docker:await': undefined,
|
|
16
|
+
'provision:docker:down': undefined,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
contents: JSON.stringify(updatedPackageJSON, null, 2),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"database": {
|
|
3
|
-
"
|
|
4
|
-
"host": "@declapract{variable.databaseClusterHost.dev}",
|
|
5
|
-
"port": 5432,
|
|
3
|
+
"target": {
|
|
6
4
|
"database": "@declapract{variable.databaseName}",
|
|
7
|
-
"schema": "@declapract{variable.databaseName}"
|
|
8
|
-
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
9
|
-
"password": "__CHANG3_ME__"
|
|
5
|
+
"schema": "@declapract{variable.databaseName}"
|
|
10
6
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
7
|
+
"role": {
|
|
8
|
+
"cicd": {
|
|
9
|
+
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
10
|
+
"password": "__CHANG3_ME__"
|
|
11
|
+
},
|
|
12
|
+
"crud": {
|
|
13
|
+
"username": "@declapract{variable.databaseUserName.serviceUser}",
|
|
14
|
+
"password": "__CHANG3_ME__"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"tunnel": {
|
|
18
|
+
"local": {
|
|
19
|
+
"host": "@declapract{variable.databaseTunnelHost.dev}",
|
|
20
|
+
"port": 15432
|
|
21
|
+
},
|
|
22
|
+
"lambda": {
|
|
23
|
+
"host": "@declapract{variable.databaseClusterHost.dev}",
|
|
24
|
+
"port": 5432
|
|
25
|
+
}
|
|
18
26
|
}
|
|
19
27
|
}
|
|
20
28
|
}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"database": {
|
|
3
|
-
"
|
|
4
|
-
"host": "@declapract{variable.databaseClusterHost.prod}",
|
|
5
|
-
"port": 5432,
|
|
3
|
+
"target": {
|
|
6
4
|
"database": "@declapract{variable.databaseName}",
|
|
7
|
-
"schema": "@declapract{variable.databaseName}"
|
|
8
|
-
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
9
|
-
"password": "__PARAM__"
|
|
5
|
+
"schema": "@declapract{variable.databaseName}"
|
|
10
6
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
7
|
+
"role": {
|
|
8
|
+
"cicd": {
|
|
9
|
+
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
10
|
+
"password": "__PARAM__"
|
|
11
|
+
},
|
|
12
|
+
"crud": {
|
|
13
|
+
"username": "@declapract{variable.databaseUserName.serviceUser}",
|
|
14
|
+
"password": "__PARAM__"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"tunnel": {
|
|
18
|
+
"local": {
|
|
19
|
+
"host": "@declapract{variable.databaseTunnelHost.prod}",
|
|
20
|
+
"port": 15433
|
|
21
|
+
},
|
|
22
|
+
"lambda": {
|
|
23
|
+
"host": "@declapract{variable.databaseClusterHost.prod}",
|
|
24
|
+
"port": 5432
|
|
25
|
+
}
|
|
18
26
|
}
|
|
19
27
|
}
|
|
20
28
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
import { FileCheckType } from 'declapract';
|
|
1
|
+
import { FileCheckType, FileFixFunction } from 'declapract';
|
|
2
2
|
|
|
3
3
|
export const check = FileCheckType.CONTAINS;
|
|
4
|
+
|
|
5
|
+
export const fix: FileFixFunction = (contents, context) => {
|
|
6
|
+
if (!contents) return { contents: context.declaredFileContents }; // init as declared if file dne
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
contents: JSON.stringify(
|
|
10
|
+
{ ...JSON.parse(contents), ...JSON.parse(context.declaredFileContents!) },
|
|
11
|
+
null,
|
|
12
|
+
2,
|
|
13
|
+
),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"database": {
|
|
3
|
-
"
|
|
4
|
-
"host": "localhost",
|
|
5
|
-
"port": 7821,
|
|
3
|
+
"target": {
|
|
6
4
|
"database": "@declapract{variable.databaseName}",
|
|
7
|
-
"schema": "@declapract{variable.databaseName}"
|
|
8
|
-
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
9
|
-
"password": "__CHANG3_ME__"
|
|
5
|
+
"schema": "@declapract{variable.databaseName}"
|
|
10
6
|
},
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
7
|
+
"role": {
|
|
8
|
+
"cicd": {
|
|
9
|
+
"username": "@declapract{variable.databaseUserName.cicdUser}",
|
|
10
|
+
"password": "__CHANG3_ME__"
|
|
11
|
+
},
|
|
12
|
+
"crud": {
|
|
13
|
+
"username": "@declapract{variable.databaseUserName.serviceUser}",
|
|
14
|
+
"password": "__CHANG3_ME__"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"tunnel": {
|
|
18
|
+
"local": {
|
|
19
|
+
"host": "localhost",
|
|
20
|
+
"port": 7821
|
|
21
|
+
},
|
|
22
|
+
"lambda": null
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
import { FileCheckType } from 'declapract';
|
|
1
|
+
import { FileCheckType, FileFixFunction } from 'declapract';
|
|
2
2
|
|
|
3
3
|
export const check = FileCheckType.CONTAINS;
|
|
4
|
+
|
|
5
|
+
export const fix: FileFixFunction = (contents, context) => {
|
|
6
|
+
if (!contents) return { contents: context.declaredFileContents }; // init as declared if file dne
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
contents: JSON.stringify(
|
|
10
|
+
{ ...JSON.parse(contents), ...JSON.parse(context.declaredFileContents!) },
|
|
11
|
+
null,
|
|
12
|
+
2,
|
|
13
|
+
),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -16,14 +16,16 @@
|
|
|
16
16
|
"generate:dao:postgres": "npx sql-dao-generator generate && npm run fix:format",
|
|
17
17
|
"generate:schema": "npx sql-schema-generator generate -c codegen.sql.schema.yml && npm run fix:format",
|
|
18
18
|
"generate:types-from-sql": "npx sql-code-generator generate -c codegen.sql.types.yml && npm run fix:format",
|
|
19
|
-
"provision:docker:clear": "docker rm -f $(docker ps -a -f 'publish=7821' -q) 2>/dev/null || true && echo 'ensured port is available 👍'",
|
|
20
|
-
"provision:docker:prepare": "cp provision/schema/sql/init/.extensions.sql provision/docker/integration-test-db/init/extensions.sql && cp provision/schema/sql/init/.schema.sql provision/docker/integration-test-db/init/schema.sql && cp provision/schema/sql/init/.user.cicd.sql provision/docker/integration-test-db/init/user.cicd.sql",
|
|
21
|
-
"provision:docker:up": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml up -d --force-recreate --build --renew-anon-volumes",
|
|
22
|
-
"provision:docker:await": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml exec -T postgres /root/wait-for-postgres.sh",
|
|
23
|
-
"provision:docker:down": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml down",
|
|
24
19
|
"provision:schema:plan": "npx sql-schema-control plan -c provision/schema/control.yml",
|
|
25
20
|
"provision:schema:apply": "npx sql-schema-control apply -c provision/schema/control.yml",
|
|
26
21
|
"provision:schema:sync": "npx sql-schema-control sync -c provision/schema/control.yml",
|
|
27
|
-
"provision:
|
|
22
|
+
"provision:testdb:docker:clear": "docker rm -f $(docker ps -a -f 'publish=7821' -q) 2>/dev/null || true && echo 'ensured port is available 👍'",
|
|
23
|
+
"provision:testdb:docker:prepare": "cp provision/schema/sql/init/.extensions.sql provision/docker/integration-test-db/init/extensions.sql && cp provision/schema/sql/init/.schema.sql provision/docker/integration-test-db/init/schema.sql && cp provision/schema/sql/init/.user.cicd.sql provision/docker/integration-test-db/init/user.cicd.sql",
|
|
24
|
+
"provision:testdb:docker:up": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml up -d --force-recreate --build --renew-anon-volumes",
|
|
25
|
+
"provision:testdb:docker:await": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml exec -T postgres /root/wait-for-postgres.sh",
|
|
26
|
+
"provision:testdb:docker:down": "docker compose -f ./provision/docker/integration-test-db/docker-compose.yml down",
|
|
27
|
+
"provision:testdb": "npm run provision:testdb:docker:clear && npm run provision:testdb:docker:prepare && npm run provision:testdb:docker:up && npm run provision:testdb:docker:await && npm run provision:schema:plan && npm run provision:schema:apply && npm run provision:schema:plan",
|
|
28
|
+
"start:testdb": "npm run provision:testdb",
|
|
29
|
+
"start:livedb:dev": "echo 'will ping the database until assured its not asleep' && STAGE=dev .agent/repo=.this/skills/use.rds.capacity.sh"
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -4,20 +4,19 @@ const configInstance = new Config();
|
|
|
4
4
|
const getConfig = async () =>
|
|
5
5
|
configInstance.get(process.env.STAGE || undefined);
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const promiseSchemaControlCredentials = async () => {
|
|
8
8
|
const config = await getConfig();
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
password: dbConfig.password,
|
|
9
|
+
const credentials = {
|
|
10
|
+
host: config.database.tunnel.local.host,
|
|
11
|
+
port: config.database.tunnel.local.port,
|
|
12
|
+
database: config.database.target.database, // i.e., db = schema
|
|
13
|
+
schema: config.database.target.schema,
|
|
14
|
+
username: config.database.role.cicd.username,
|
|
15
|
+
password: config.database.role.cicd.password,
|
|
17
16
|
};
|
|
18
|
-
return
|
|
17
|
+
return credentials;
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
module.exports = {
|
|
22
|
-
promiseConfig:
|
|
21
|
+
promiseConfig: promiseSchemaControlCredentials,
|
|
23
22
|
};
|
|
@@ -54,7 +54,7 @@ fi;
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
# define the postgres connecition string
|
|
57
|
-
CLUSTER_HOST=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.
|
|
57
|
+
CLUSTER_HOST=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.databaseTunnelHost.prod}" || echo "@declapract{variable.databaseTunnelHost.dev}");
|
|
58
58
|
CLUSTER_CONNECTION_STRING=postgresql://postgres:$POSTGRES_ADMIN_PASSWORD@$CLUSTER_HOST:5432
|
|
59
59
|
ROOT_DB_CONNECTION_STRING=$CLUSTER_CONNECTION_STRING/postgres
|
|
60
60
|
SVC_DB_CONNECTION_STRING=$CLUSTER_CONNECTION_STRING/@declapract{variable.databaseName}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
database: {
|
|
2
|
+
target: {
|
|
3
|
+
database: string;
|
|
4
|
+
schema: string;
|
|
5
|
+
};
|
|
6
|
+
role: {
|
|
7
|
+
cicd: {
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
};
|
|
11
|
+
crud: {
|
|
12
|
+
username: string;
|
|
13
|
+
password: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
tunnel: {
|
|
17
|
+
local: {
|
|
18
|
+
host: string;
|
|
19
|
+
port: number;
|
|
20
|
+
};
|
|
21
|
+
lambda: {
|
|
22
|
+
host: string;
|
|
23
|
+
port: number;
|
|
24
|
+
} | null;
|
|
25
|
+
};
|
|
26
|
+
};
|
package/dist/practices/persist-with-rds/best-practice/src/utils/config/Config.ts.declapract.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FileCheckType, FileFixFunction } from 'declapract';
|
|
2
|
+
import { UnexpectedCodePathError } from 'helpful-errors';
|
|
3
|
+
|
|
4
|
+
export const check = FileCheckType.CONTAINS; // practice must contain this
|
|
5
|
+
|
|
6
|
+
const variantsToReplace = [
|
|
7
|
+
`
|
|
8
|
+
database: {
|
|
9
|
+
service: {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
database: string;
|
|
13
|
+
schema: string;
|
|
14
|
+
username: string;
|
|
15
|
+
password: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
`.trim(),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export const fix: FileFixFunction = (contents, context) => {
|
|
22
|
+
// if no contents yet, can't fix
|
|
23
|
+
if (!contents) return { contents };
|
|
24
|
+
|
|
25
|
+
// otherwise, try and fix with one of the variants we support
|
|
26
|
+
const desiredContents = variantsToReplace.reduce(
|
|
27
|
+
(contentsNow, thisVariant) =>
|
|
28
|
+
contentsNow?.replace(
|
|
29
|
+
thisVariant,
|
|
30
|
+
context.declaredFileContents?.trim() ??
|
|
31
|
+
UnexpectedCodePathError.throw(
|
|
32
|
+
'expected to have declared best practice but found null',
|
|
33
|
+
{ context },
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
contents,
|
|
37
|
+
);
|
|
38
|
+
return { contents: desiredContents };
|
|
39
|
+
};
|
package/dist/practices/persist-with-rds/best-practice/src/utils/database/getDatabaseConnection.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { HelpfulError, UnexpectedCodePathError } from 'helpful-errors';
|
|
1
2
|
import pg, { Client, QueryResult, QueryResultRow } from 'pg';
|
|
2
3
|
|
|
3
4
|
import { getConfig } from '../config/getConfig';
|
|
5
|
+
import { environment } from '../environment';
|
|
4
6
|
|
|
5
7
|
// https://github.com/brianc/node-postgres/pull/353#issuecomment-283709264
|
|
6
8
|
pg.types.setTypeParser(20, (value) => parseInt(value, 10)); // cast bigints to numbers; by default, pg returns bigints as strings, since max val of bigint is bigger than max safe value in js
|
|
@@ -14,7 +16,7 @@ export interface DatabaseConnection {
|
|
|
14
16
|
end: () => Promise<void>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
export class DatabaseQueryError extends
|
|
19
|
+
export class DatabaseQueryError extends HelpfulError {
|
|
18
20
|
constructor({
|
|
19
21
|
sql,
|
|
20
22
|
values,
|
|
@@ -33,27 +35,46 @@ sql:
|
|
|
33
35
|
values:
|
|
34
36
|
${JSON.stringify(values)}
|
|
35
37
|
`.trim();
|
|
36
|
-
super(message);
|
|
38
|
+
super(message, { sql, values, cause: caught });
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export const getDatabaseConnection = async (): Promise<DatabaseConnection> => {
|
|
41
43
|
const config = await getConfig();
|
|
42
|
-
const
|
|
44
|
+
const target = config.database.target;
|
|
45
|
+
const role = config.database.role.crud;
|
|
46
|
+
|
|
47
|
+
// determine which tunnel to use based on environment.server
|
|
48
|
+
const tunnel =
|
|
49
|
+
environment.server === 'AWS:LAMBDA'
|
|
50
|
+
? config.database.tunnel.lambda
|
|
51
|
+
: config.database.tunnel.local;
|
|
52
|
+
|
|
53
|
+
// ensure tunnel is defined for the requested server
|
|
54
|
+
if (!tunnel) {
|
|
55
|
+
throw new UnexpectedCodePathError(
|
|
56
|
+
`Database tunnel not configured for environment.server + env.access`,
|
|
57
|
+
{ environment },
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// instantiate the client
|
|
43
62
|
const client = new Client({
|
|
44
|
-
host:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
host: tunnel.host,
|
|
64
|
+
port: tunnel.port,
|
|
65
|
+
user: role.username,
|
|
66
|
+
password: role.password,
|
|
67
|
+
database: target.database,
|
|
49
68
|
});
|
|
50
69
|
await client.connect();
|
|
51
|
-
await client.query(`SET search_path TO ${
|
|
70
|
+
await client.query(`SET search_path TO ${target.schema}, public;`); // https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-
|
|
52
71
|
const dbConnection = {
|
|
53
72
|
query: ({ sql, values }: { sql: string; values?: (string | number)[] }) =>
|
|
54
73
|
client.query(sql, values),
|
|
55
74
|
end: () => client.end(),
|
|
56
75
|
};
|
|
76
|
+
|
|
77
|
+
// declare our interface
|
|
57
78
|
return {
|
|
58
79
|
query: (args: { sql: string; values?: any[] }) =>
|
|
59
80
|
dbConnection.query(args).catch((error) => {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# SKILL: use.npm.alias
|
|
4
|
+
#
|
|
5
|
+
# Installs shell aliases that redirect npm commands to pnpm for faster package management.
|
|
6
|
+
#
|
|
7
|
+
# What it does:
|
|
8
|
+
# 1. Creates an alias 'npm.slow' pointing to the original npm binary
|
|
9
|
+
# 2. Creates an alias 'npm' that redirects to pnpm
|
|
10
|
+
# 3. Persists aliases to ~/.bash_aliases (sourced by ~/.bashrc)
|
|
11
|
+
#
|
|
12
|
+
# When to use:
|
|
13
|
+
# - After setting up a new development environment
|
|
14
|
+
# - When you want npm commands to use pnpm transparently
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
# ./.agent/repo=.this/skills/use.npm.alias.sh
|
|
18
|
+
#
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
BASH_ALIASES="${HOME}/.bash_aliases"
|
|
22
|
+
|
|
23
|
+
# ensure ~/.bash_aliases exists
|
|
24
|
+
touch "$BASH_ALIASES"
|
|
25
|
+
|
|
26
|
+
# findsert npm.slow (only add if not already defined)
|
|
27
|
+
if ! grep -q "^alias npm.slow=" "$BASH_ALIASES" 2>/dev/null; then
|
|
28
|
+
NPM_PATH=$(which npm)
|
|
29
|
+
echo "alias npm.slow=\"$NPM_PATH\"" >> "$BASH_ALIASES"
|
|
30
|
+
echo "Added: alias npm.slow=\"$NPM_PATH\""
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# upsert npm => pnpm
|
|
34
|
+
if grep -q "^alias npm=" "$BASH_ALIASES" 2>/dev/null; then
|
|
35
|
+
sed -i 's/^alias npm=.*/alias npm="pnpm"/' "$BASH_ALIASES"
|
|
36
|
+
else
|
|
37
|
+
echo 'alias npm="pnpm"' >> "$BASH_ALIASES"
|
|
38
|
+
fi
|
|
39
|
+
echo "Added: alias npm=\"pnpm\""
|
|
40
|
+
|
|
41
|
+
# report
|
|
42
|
+
echo ""
|
|
43
|
+
echo "Aliases installed to $BASH_ALIASES"
|
|
44
|
+
echo "Run 'source $BASH_ALIASES' or open a new terminal to activate."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"devDependencies": {
|
|
3
|
-
"declastruct": "@declapract{check.minVersion('1.
|
|
4
|
-
"declastruct-github": "@declapract{check.minVersion('1.
|
|
3
|
+
"declastruct": "@declapract{check.minVersion('1.3.0')}",
|
|
4
|
+
"declastruct-github": "@declapract{check.minVersion('1.0.3')}"
|
|
5
5
|
}
|
|
6
6
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"jest": "@declapract{check.minVersion('29.3.1')}",
|
|
5
5
|
"test-fns": "@declapract{check.minVersion('1.4.2')}",
|
|
6
6
|
"ts-jest": "@declapract{check.minVersion('29.4.5')}",
|
|
7
|
-
"
|
|
7
|
+
"tsx": "@declapract{check.minVersion('4.20.6')}",
|
|
8
8
|
"core-js": "@declapract{check.minVersion('3.26.1')}",
|
|
9
9
|
"@babel/core": "@declapract{check.minVersion('7.28.5')}",
|
|
10
10
|
"@babel/preset-env": "@declapract{check.minVersion('7.28.5')}",
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
"rootDir": "src"
|
|
5
5
|
},
|
|
6
6
|
"include": [
|
|
7
|
-
"src/**/*.ts"
|
|
7
|
+
"src/**/*.ts",
|
|
8
|
+
"nontyped_modules/**/*.d.ts"
|
|
8
9
|
],
|
|
9
10
|
"exclude": [
|
|
10
|
-
"**/*.test.
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
11
|
+
"**/*.test.ts", // all explicitly .test files are dev only assets too
|
|
12
|
+
"**/*.test.js",
|
|
13
|
+
"**/.test/**/*",
|
|
14
|
+
"**/.scratch/**/*",
|
|
15
|
+
"**/__test*__/**/*" // todo: deprecate this pattern in favor of .test
|
|
15
16
|
]
|
|
16
17
|
}
|
package/dist/useCases.yml
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "declapract-typescript-ehmpathy",
|
|
3
3
|
"author": "ehmpathy",
|
|
4
4
|
"description": "declapract best practices declarations for typescript",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.42.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"repository": "ehmpathy/declapract-typescript-ehmpathy",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"domain-objects": "0.29.2",
|
|
45
45
|
"expect": "29.4.2",
|
|
46
46
|
"flat": "5.0.2",
|
|
47
|
-
"helpful-errors": "1.3
|
|
47
|
+
"helpful-errors": "1.5.3",
|
|
48
48
|
"simple-log-methods": "0.5.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|