declapract-typescript-ehmpathy 0.39.18 → 0.40.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.
Files changed (26) hide show
  1. package/dist/practices/cicd-common/best-practice/.github/workflows/.declastruct.yml +109 -0
  2. package/dist/practices/cicd-package/best-practice/.github/workflows/provision.yml +24 -0
  3. package/dist/practices/cicd-service/best-practice/.github/workflows/provision.yml +9 -13
  4. package/dist/practices/domain/best-practice/package.json +1 -1
  5. package/dist/practices/errors/best-practice/package.json +1 -1
  6. package/dist/practices/lint/best-practice/.declapract.todo.md +5 -0
  7. package/dist/practices/lint/best-practice/.eslintrc.js +21 -1
  8. package/dist/practices/lint/best-practice/package.json +1 -0
  9. package/dist/practices/{terraform-github/best-practice → provision-github/bad-practices/terraform-github}/provision/github/environment/main.tf +1 -1
  10. package/dist/practices/provision-github/best-practice/package.json +6 -0
  11. package/dist/practices/provision-github/best-practice/package.json.declapract.ts +3 -0
  12. package/dist/practices/provision-github/best-practice/provision/github/declastruct.resources.ts +124 -0
  13. package/dist/practices/tests/best-practice/jest.acceptance.config.ts +3 -0
  14. package/dist/practices/tests/best-practice/jest.acceptance.env.ts +23 -2
  15. package/dist/practices/tests/best-practice/jest.integration.config.ts +3 -0
  16. package/dist/practices/tests/best-practice/jest.integration.env.ts +23 -2
  17. package/dist/practices/tests/best-practice/jest.unit.config.ts +3 -0
  18. package/dist/practices/tests/best-practice/jest.unit.env.ts +16 -1
  19. package/dist/practices/tests/best-practice/jest.unit.env.ts.declapract.ts +3 -2
  20. package/dist/practices/tests/best-practice/package.json +1 -1
  21. package/dist/useCases.yml +3 -2
  22. package/package.json +3 -3
  23. package/readme.md +131 -20
  24. /package/dist/practices/{terraform-github/best-practice → provision-github/bad-practices/terraform-github}/provision/github/environment/import-existing-repo.sh +0 -0
  25. /package/dist/practices/{terraform-github/best-practice → provision-github/bad-practices/terraform-github}/provision/github/environment/versions.tf +0 -0
  26. /package/dist/practices/{terraform-github/best-practice → provision-github/bad-practices/terraform-github}/provision/github/product/repository.tf +0 -0
@@ -0,0 +1,109 @@
1
+ name: .declastruct
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ wish-path:
7
+ type: string
8
+ description: "the path to the wish declaration"
9
+ github-environment:
10
+ type: string
11
+ description: "the github environment that the apply step will be executed in"
12
+ allow-apply:
13
+ type: boolean
14
+ description: "whether the apply step is enabled. defaults to true on main"
15
+ default: ${{ github.ref == 'refs/heads/main' }}
16
+ secrets:
17
+ github-token:
18
+ required: false
19
+ description: optional credentials to support authenticating with github provider
20
+
21
+ jobs:
22
+ # install the dependencies
23
+ install:
24
+ uses: ./.github/workflows/.install.yml
25
+
26
+ plan:
27
+ runs-on: ubuntu-latest
28
+ needs: [install]
29
+ outputs:
30
+ has-changes-planned: ${{ steps.evaluate-plan.outputs.has-changes-planned }}
31
+ steps:
32
+ - name: checkout
33
+ uses: actions/checkout@v3
34
+
35
+ - name: set node-version
36
+ uses: actions/setup-node@v3
37
+ with:
38
+ node-version-file: ".nvmrc"
39
+
40
+ - name: get node-modules from cache
41
+ uses: actions/cache/restore@v4
42
+ with:
43
+ path: ./node_modules
44
+ key: ${{ needs.install.outputs.node-modules-cache-key }}
45
+
46
+ - name: declastruct plan
47
+ id: plan
48
+ run: npx declastruct plan --wish ${{ inputs.wish-path }} --into ${{ inputs.wish-path }}.plan.json | tee ./plan.log
49
+ env:
50
+ GITHUB_TOKEN: ${{ secrets.github-token }} # allow specifying a github token to pass to the terraform command
51
+
52
+ - name: evaluate plan
53
+ id: evaluate-plan
54
+ run: |
55
+ # check whether it said there were required changes
56
+ if grep "Everything is in sync" ./plan.log
57
+ then
58
+ echo "has-changes-planned=false" >> "$GITHUB_OUTPUT"
59
+ else
60
+ echo "has-changes-planned=true" >> "$GITHUB_OUTPUT"
61
+ fi
62
+
63
+ - name: has changes planned?
64
+ run: echo "${{ steps.evaluate-plan.outputs.has-changes-planned }}"
65
+
66
+ - name: upload plan artifact
67
+ if: ${{ inputs.allow-apply == true && steps.evaluate-plan.outputs.has-changes-planned == 'true' }}
68
+ uses: actions/upload-artifact@v4
69
+ with:
70
+ name: declastruct-plan
71
+ path: |
72
+ ${{ inputs.wish-path }}.plan.json
73
+ retention-days: 1
74
+ include-hidden-files: false
75
+
76
+ apply:
77
+ runs-on: ubuntu-latest
78
+ environment: ${{ inputs.github-environment }}
79
+ needs: [install, plan]
80
+ if: ${{ inputs.allow-apply == true && needs.plan.outputs.has-changes-planned == 'true' }}
81
+ steps:
82
+ - name: checkout
83
+ uses: actions/checkout@v3
84
+
85
+ - name: set node-version
86
+ uses: actions/setup-node@v3
87
+ with:
88
+ node-version-file: ".nvmrc"
89
+
90
+ - name: get node-modules from cache
91
+ uses: actions/cache/restore@v4
92
+ with:
93
+ path: ./node_modules
94
+ key: ${{ needs.install.outputs.node-modules-cache-key }}
95
+
96
+ - name: extract directory from wish-path
97
+ id: extract-dir
98
+ run: echo "wish-dir=$(dirname "${{ inputs.wish-path }}")" >> "$GITHUB_OUTPUT"
99
+
100
+ - name: download plan artifact
101
+ uses: actions/download-artifact@v4
102
+ with:
103
+ name: declastruct-plan
104
+ path: ${{ steps.extract-dir.outputs.wish-dir }}
105
+
106
+ - name: declastruct apply
107
+ run: npx declastruct apply --plan ${{ inputs.wish-path }}.plan.json | tee ./apply.log
108
+ env:
109
+ GITHUB_TOKEN: ${{ secrets.github-token }} # allow specifying a github token to pass to the terraform command
@@ -0,0 +1,24 @@
1
+ name: provision
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+ branches:
8
+ - "main"
9
+ - "master"
10
+ pull_request:
11
+ workflow_dispatch:
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }} # per [workflow] x [branch, tag]
15
+ cancel-in-progress: true # cancel workflows for non-latest commits
16
+
17
+ jobs:
18
+ github:
19
+ uses: ./.github/workflows/.declastruct.yml
20
+ with:
21
+ wish-path: provision/github/declastruct.resources.ts
22
+ github-environment: prod
23
+ secrets:
24
+ github-token: ${{ secrets.PROVISION_GITHUB_GITHUB_TOKEN }}
@@ -5,8 +5,8 @@ on:
5
5
  tags:
6
6
  - v*
7
7
  branches:
8
- - 'main'
9
- - 'master'
8
+ - "main"
9
+ - "master"
10
10
  pull_request:
11
11
  workflow_dispatch:
12
12
 
@@ -21,7 +21,7 @@ jobs:
21
21
  working-directory: provision/aws/environments/test
22
22
  github-environment: dev
23
23
  aws-region: us-east-1
24
- aws-account-id: '@declapract{variable.awsAccountId.dev}'
24
+ aws-account-id: "@declapract{variable.awsAccountId.dev}"
25
25
  secrets:
26
26
  aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
27
27
  aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
@@ -32,7 +32,7 @@ jobs:
32
32
  working-directory: provision/aws/environments/dev
33
33
  github-environment: dev
34
34
  aws-region: us-east-1
35
- aws-account-id: '@declapract{variable.awsAccountId.dev}'
35
+ aws-account-id: "@declapract{variable.awsAccountId.dev}"
36
36
  secrets:
37
37
  aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
38
38
  aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
@@ -43,22 +43,18 @@ jobs:
43
43
  working-directory: provision/aws/environments/prod
44
44
  github-environment: prod
45
45
  aws-region: us-east-1
46
- aws-account-id: '@declapract{variable.awsAccountId.prod}'
46
+ aws-account-id: "@declapract{variable.awsAccountId.prod}"
47
47
  allow-apply: ${{ startsWith(github.ref, 'refs/tags/') }} # only apply to prod on tags
48
48
  secrets:
49
49
  aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
50
50
  aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}
51
51
 
52
52
  github:
53
- uses: ./.github/workflows/.terraform.yml
53
+ uses: ./.github/workflows/.declastruct.yml
54
54
  with:
55
- working-directory: provision/github/environment
55
+ wish-path: provision/github/declastruct.resources.ts
56
56
  github-environment: prod
57
- aws-region: us-east-1
58
- aws-account-id: '@declapract{variable.awsAccountId.prod}'
59
57
  secrets:
60
- aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
61
- aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}
62
58
  github-token: ${{ secrets.PROVISION_GITHUB_GITHUB_TOKEN }}
63
59
 
64
60
  sql-schema-dev:
@@ -67,7 +63,7 @@ jobs:
67
63
  stage: dev
68
64
  github-environment: dev
69
65
  aws-region: us-east-1
70
- aws-account-id: '@declapract{variable.awsAccountId.dev}'
66
+ aws-account-id: "@declapract{variable.awsAccountId.dev}"
71
67
  secrets:
72
68
  aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
73
69
  aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
@@ -79,7 +75,7 @@ jobs:
79
75
  stage: prod
80
76
  github-environment: prod
81
77
  aws-region: us-east-1
82
- aws-account-id: '@declapract{variable.awsAccountId.prod}'
78
+ aws-account-id: "@declapract{variable.awsAccountId.prod}"
83
79
  allow-apply: ${{ startsWith(github.ref, 'refs/tags/') }} # only apply to prod on tags
84
80
  secrets:
85
81
  aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "dependencies": {
3
- "domain-objects": "@declapract{check.minVersion('0.25.7')}",
3
+ "domain-objects": "@declapract{check.minVersion('0.29.1')}",
4
4
  "joi": "@declapract{check.minVersion('17.4.0')}",
5
5
  "type-fns": "@declapract{check.minVersion('1.17.0')}"
6
6
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "dependencies": {
3
- "helpful-errors": "@declapract{check.minVersion('1.3.8')}"
3
+ "helpful-errors": "@declapract{check.minVersion('1.5.3')}"
4
4
  }
5
5
  }
@@ -0,0 +1,5 @@
1
+ # todo
2
+
3
+ replace with biome
4
+
5
+ its faster and requires less deps
@@ -6,6 +6,7 @@ module.exports = {
6
6
  'airbnb-typescript/base', // uses the airbnb recommended rules
7
7
  'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
8
8
  ],
9
+ plugins: ['unused-imports'],
9
10
  parserOptions: {
10
11
  project: './tsconfig.json',
11
12
  ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
@@ -31,7 +32,26 @@ module.exports = {
31
32
  },
32
33
  ],
33
34
  '@typescript-eslint/no-explicit-any': 'error', // forbid any type for better type safety; you can use `never` or `unknown` instead when its truly unknown or never needed to be known
34
- '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
35
+ '@typescript-eslint/consistent-type-imports': [
36
+ 'error',
37
+ {
38
+ prefer: 'type-imports', // enforce using `import type` for type-only imports
39
+ fixStyle: 'inline-type-imports', // use inline `import { type Foo }` style
40
+ disallowTypeAnnotations: true, // disallow using `import type` in type annotations
41
+ },
42
+ ],
43
+ 'no-unused-vars': 'off', // turn off base rule as it can report incorrect errors
44
+ '@typescript-eslint/no-unused-vars': 'off', // turn off in favor of unused-imports/no-unused-vars
45
+ 'unused-imports/no-unused-imports': 'error', // auto-fixable rule to remove unused imports
46
+ 'unused-imports/no-unused-vars': [
47
+ 'warn',
48
+ {
49
+ vars: 'all',
50
+ varsIgnorePattern: '^_',
51
+ args: 'after-used',
52
+ argsIgnorePattern: '^_',
53
+ },
54
+ ],
35
55
  'import/no-cycle': 'off',
36
56
  'max-classes-per-file': 'off',
37
57
  '@typescript-eslint/no-use-before-define': 'off',
@@ -7,6 +7,7 @@
7
7
  "eslint-config-prettier": "8.5.0",
8
8
  "eslint-plugin-import": "2.26.0",
9
9
  "eslint-plugin-prettier": "4.2.1",
10
+ "eslint-plugin-unused-imports": "4.1.4",
10
11
  "depcheck": "1.4.3"
11
12
  },
12
13
  "scripts": {
@@ -5,7 +5,7 @@ provider "github" {
5
5
 
6
6
  terraform {
7
7
  backend "s3" {
8
- bucket = "terraform-state-@declapract{variable.infrastructureNamespaceId}-prod" # tracked in the prod aws account's s3 bucket, so `use.@declapract{variable.organizationName}.prod`
8
+ bucket = "terraform-state-xdeclapract{variable.infrastructureNamespaceId}-prod" # tracked in the prod aws account's s3 bucket, so `use.@declapract{variable.organizationName}.prod`
9
9
  key = "@declapract{variable.projectName}-github"
10
10
  region = "us-east-1"
11
11
  encrypt = true
@@ -0,0 +1,6 @@
1
+ {
2
+ "devDependencies": {
3
+ "declastruct": "@declapract{check.minVersion('1.1.7')}",
4
+ "declastruct-github": "@declapract{check.minVersion('1.1.1')}"
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ import { FileCheckType } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
@@ -0,0 +1,124 @@
1
+ import { DeclastructProvider } from 'declastruct';
2
+ import {
3
+ DeclaredGithubBranch,
4
+ DeclaredGithubBranchProtection,
5
+ DeclaredGithubRepo,
6
+ DeclaredGithubRepoConfig,
7
+ getDeclastructGithubProvider,
8
+ } from 'declastruct-github';
9
+ import { DomainEntity, RefByUnique } from 'domain-objects';
10
+ import { UnexpectedCodePathError } from 'helpful-errors';
11
+
12
+ import pkg from '../../package.json';
13
+
14
+ export const getProviders = async (): Promise<DeclastructProvider[]> => [
15
+ getDeclastructGithubProvider(
16
+ {
17
+ credentials: {
18
+ token:
19
+ process.env.GITHUB_TOKEN ??
20
+ UnexpectedCodePathError.throw('github token not supplied'),
21
+ },
22
+ },
23
+ {
24
+ log: {
25
+ info: () => {},
26
+ debug: () => {},
27
+ warn: console.warn,
28
+ error: console.error,
29
+ },
30
+ },
31
+ ),
32
+ ];
33
+
34
+ export const getResources = async (): Promise<DomainEntity<any>[]> => {
35
+ // declare the repo
36
+ const repo = DeclaredGithubRepo.as({
37
+ owner: '@declapract{variable.organizationName}',
38
+ name: '@declapract{variable.projectName}',
39
+ description: (pkg as any).description ?? null,
40
+ visibility: (pkg as any).private === true ? 'private' : 'public',
41
+ private: (pkg as any).private ?? false, // todo: why do we have to specify this twice?
42
+ homepage: (pkg as any).homepage ?? null,
43
+
44
+ // things we haven't changed from the defaults
45
+ archived: false,
46
+ });
47
+
48
+ // ref the main branch
49
+ const branchMain = RefByUnique.as<typeof DeclaredGithubBranch>({
50
+ name: 'main',
51
+ repo,
52
+ });
53
+
54
+ // declare config for the repo
55
+ const repoConfig = DeclaredGithubRepoConfig.as({
56
+ repo,
57
+
58
+ // explicitly set the main branch
59
+ defaultBranch: branchMain.name,
60
+
61
+ // we only use issues; the rest is noise today
62
+ hasIssues: true,
63
+ hasProjects: false,
64
+ hasWiki: false,
65
+ hasDownloads: false,
66
+ isTemplate: false,
67
+
68
+ // only squash merges are allowed
69
+ allowSquashMerge: true,
70
+ allowMergeCommit: false, // but especially not merge merges. never merge merges
71
+ allowRebaseMerge: false,
72
+
73
+ // allow nice to haves for pulls
74
+ allowAutoMerge: true,
75
+ allowUpdateBranch: true,
76
+
77
+ // always cleanup after yourself
78
+ deleteBranchOnMerge: true,
79
+
80
+ // configure messages
81
+ mergeCommitMessage: 'PR_TITLE',
82
+ mergeCommitTitle: 'MERGE_MESSAGE',
83
+ squashMergeCommitMessage: 'COMMIT_MESSAGES',
84
+ squashMergeCommitTitle: 'COMMIT_OR_PR_TITLE',
85
+ webCommitSignoffRequired: false,
86
+ });
87
+
88
+ // declare protection for that branch, too
89
+ const branchMainProtection = DeclaredGithubBranchProtection.as({
90
+ branch: branchMain,
91
+
92
+ enforceAdmins: true, // yes, even admins need to follow this (note: they can still take the time to go and change the settings temporarily for the exceptions)
93
+ allowsDeletions: false, // dont allow the `main` branch to be deleted
94
+ allowsForcePushes: false, // dont allow `main` branch to be force pushed to
95
+ requireLinearHistory: false, // # no ugly merge commits, woo! 🎉
96
+
97
+ requiredStatusChecks: {
98
+ strict: true, // branch must be up to date. otherwise, we dont know if it will really pass once it is merged
99
+ contexts: [
100
+ 'suite / install / npm',
101
+ 'suite / test-commits',
102
+ 'suite / test-types',
103
+ 'suite / test-format',
104
+ 'suite / test-lint',
105
+ 'suite / test-unit',
106
+ 'suite / test-integration',
107
+ 'suite / test-acceptance-locally',
108
+ 'pullreq-title', // "review / pullreq-title",
109
+ ],
110
+ },
111
+
112
+ // things we haven't changed from the defaults
113
+ allowForkSyncing: false,
114
+ blockCreations: false,
115
+ lockBranch: false,
116
+ requiredConversationResolution: false,
117
+ requiredPullRequestReviews: null,
118
+ requiredSignatures: false,
119
+ restrictions: null,
120
+ });
121
+
122
+ // and return the full set
123
+ return [repo, repoConfig, branchMainProtection];
124
+ };
@@ -22,6 +22,9 @@ const config: Config = {
22
22
  testMatch: ['**/*.acceptance.test.ts'],
23
23
  setupFiles: ['core-js'],
24
24
  setupFilesAfterEnv: ['./jest.acceptance.env.ts'],
25
+
26
+ // use 50% of threads to leave headroom for other processes
27
+ maxWorkers: '50%', // https://stackoverflow.com/questions/71287710/why-does-jest-run-faster-with-maxworkers-50
25
28
  };
26
29
 
27
30
  // eslint-disable-next-line import/no-default-export
@@ -1,13 +1,34 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import util from 'util';
4
+
1
5
  // eslint-disable-next-line no-undef
2
6
  jest.setTimeout(90000); // we're calling downstream apis
3
7
 
8
+ // set console.log to not truncate nested objects
9
+ util.inspect.defaultOptions.depth = 5;
10
+
11
+ /**
12
+ * .what = verify that we're running from a valid project directory; otherwise, fail fast
13
+ * .why = prevent confusion and hard-to-debug errors from running tests in the wrong directory
14
+ */
15
+ if (!existsSync(join(process.cwd(), 'package.json')))
16
+ throw new Error('no package.json found in cwd. are you @gitroot?');
17
+
4
18
  /**
5
- * .what = verify that the env has sufficient auth to run the tests; otherwise, fail fast
19
+ * .what = verify that the env has sufficient auth to run the tests if aws is used; otherwise, fail fast
6
20
  * .why =
7
21
  * - prevent time wasted waiting on tests to fail due to lack of credentials
8
22
  * - prevent time wasted debugging tests which are failing due to hard-to-read missed credential errors
9
23
  */
10
- if (!(process.env.AWS_PROFILE || process.env.AWS_ACCESS_KEY_ID))
24
+ const declapractUsePath = join(process.cwd(), 'declapract.use.ts');
25
+ const requiresAwsAuth =
26
+ existsSync(declapractUsePath) &&
27
+ readFileSync(declapractUsePath, 'utf8').includes('awsAccountId');
28
+ if (
29
+ requiresAwsAuth &&
30
+ !(process.env.AWS_PROFILE || process.env.AWS_ACCESS_KEY_ID)
31
+ )
11
32
  throw new Error(
12
33
  'no aws credentials present. please authenticate with aws to run acceptance tests',
13
34
  );
@@ -22,6 +22,9 @@ const config: Config = {
22
22
  testMatch: ['**/*.integration.test.ts'],
23
23
  setupFiles: ['core-js'],
24
24
  setupFilesAfterEnv: ['./jest.integration.env.ts'],
25
+
26
+ // use 50% of threads to leave headroom for other processes
27
+ maxWorkers: '50%', // https://stackoverflow.com/questions/71287710/why-does-jest-run-faster-with-maxworkers-50
25
28
  };
26
29
 
27
30
  // eslint-disable-next-line import/no-default-export
@@ -1,6 +1,20 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import util from 'util';
4
+
1
5
  // eslint-disable-next-line no-undef
2
6
  jest.setTimeout(90000); // since we're calling downstream apis
3
7
 
8
+ // set console.log to not truncate nested objects
9
+ util.inspect.defaultOptions.depth = 5;
10
+
11
+ /**
12
+ * .what = verify that we're running from a valid project directory; otherwise, fail fast
13
+ * .why = prevent confusion and hard-to-debug errors from running tests in the wrong directory
14
+ */
15
+ if (!existsSync(join(process.cwd(), 'package.json')))
16
+ throw new Error('no package.json found in cwd. are you @gitroot?');
17
+
4
18
  /**
5
19
  * sanity check that unit tests are only run the 'test' environment
6
20
  *
@@ -15,12 +29,19 @@ if (
15
29
  throw new Error(`integration.test is not targeting stage 'test'`);
16
30
 
17
31
  /**
18
- * .what = verify that the env has sufficient auth to run the tests; otherwise, fail fast
32
+ * .what = verify that the env has sufficient auth to run the tests if aws is used; otherwise, fail fast
19
33
  * .why =
20
34
  * - prevent time wasted waiting on tests to fail due to lack of credentials
21
35
  * - prevent time wasted debugging tests which are failing due to hard-to-read missed credential errors
22
36
  */
23
- if (!(process.env.AWS_PROFILE || process.env.AWS_ACCESS_KEY_ID))
37
+ const declapractUsePath = join(process.cwd(), 'declapract.use.ts');
38
+ const requiresAwsAuth =
39
+ existsSync(declapractUsePath) &&
40
+ readFileSync(declapractUsePath, 'utf8').includes('awsAccountId');
41
+ if (
42
+ requiresAwsAuth &&
43
+ !(process.env.AWS_PROFILE || process.env.AWS_ACCESS_KEY_ID)
44
+ )
24
45
  throw new Error(
25
46
  'no aws credentials present. please authenticate with aws to run integration tests',
26
47
  );
@@ -27,6 +27,9 @@ const config: Config = {
27
27
  ],
28
28
  setupFiles: ['core-js'],
29
29
  setupFilesAfterEnv: ['./jest.unit.env.ts'],
30
+
31
+ // use 50% of threads to leave headroom for other processes
32
+ maxWorkers: '50%', // https://stackoverflow.com/questions/71287710/why-does-jest-run-faster-with-maxworkers-50
30
33
  };
31
34
 
32
35
  // eslint-disable-next-line import/no-default-export
@@ -1,7 +1,22 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import util from 'util';
4
+
5
+ // mock that getConfig just returns plaintext test env config in unit tests
1
6
  jest.mock('./src/utils/config/getConfig', () => ({
2
- getConfig: jest.fn().mockImplementation(() => require('./config/test.json')), // mock that getConfig just returns plaintext test env config in unit tests
7
+ getConfig: jest.fn().mockImplementation(() => require('./config/test.json')),
3
8
  }));
4
9
 
10
+ // set console.log to not truncate nested objects
11
+ util.inspect.defaultOptions.depth = 5;
12
+
13
+ /**
14
+ * .what = verify that we're running from a valid project directory; otherwise, fail fast
15
+ * .why = prevent confusion and hard-to-debug errors from running tests in the wrong directory
16
+ */
17
+ if (!existsSync(join(process.cwd(), 'package.json')))
18
+ throw new Error('no package.json found in cwd. are you @gitroot?');
19
+
5
20
  /**
6
21
  * sanity check that unit tests are only run the 'test' environment
7
22
  *
@@ -15,8 +15,9 @@ export const contents: FileContentsFunction = async (context) => {
15
15
  let contents = contentsSuperset;
16
16
  if (!context.projectPractices.includes('config'))
17
17
  contents = contents.replace(
18
- `jest.mock('./src/utils/config/getConfig', () => ({
19
- getConfig: jest.fn().mockImplementation(() => require('./config/test.json')), // mock that getConfig just returns plaintext test env config in unit tests
18
+ `// mock that getConfig just returns plaintext test env config in unit tests
19
+ jest.mock('./src/utils/config/getConfig', () => ({
20
+ getConfig: jest.fn().mockImplementation(() => require('./config/test.json')),
20
21
  }));
21
22
 
22
23
  `,
@@ -3,7 +3,7 @@
3
3
  "@types/jest": "@declapract{check.minVersion('29.2.4')}",
4
4
  "jest": "@declapract{check.minVersion('29.3.1')}",
5
5
  "test-fns": "@declapract{check.minVersion('1.4.2')}",
6
- "ts-jest": "@declapract{check.minVersion('29.1.3')}",
6
+ "ts-jest": "@declapract{check.minVersion('29.4.5')}",
7
7
  "ts-node": "@declapract{check.minVersion('10.9.2')}",
8
8
  "core-js": "@declapract{check.minVersion('3.26.1')}",
9
9
  "@babel/core": "@declapract{check.minVersion('7.28.5')}",
package/dist/useCases.yml CHANGED
@@ -23,6 +23,7 @@ use-cases:
23
23
  practices:
24
24
  - cicd-package
25
25
  - node-package
26
+ - provision-github
26
27
  lambda-service:
27
28
  extends:
28
29
  - typescript-project
@@ -40,8 +41,8 @@ use-cases:
40
41
  - node-service
41
42
  - runtime-type-checking
42
43
  - serverless
44
+ - provision-github
43
45
  - terraform-common
44
- - terraform-github
45
46
  - terraform-aws
46
47
  - tests-service
47
48
  - uuid
@@ -70,7 +71,7 @@ use-cases:
70
71
  - package-json-order
71
72
  - prettier
72
73
  - terraform-common
73
- - terraform-github
74
+ - provision-github
74
75
  - lint
75
76
  - lint-react
76
77
  - lint-react-native
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.39.18",
5
+ "version": "0.40.0",
6
6
  "license": "MIT",
7
7
  "main": "src/index.js",
8
8
  "repository": "ehmpathy/declapract-typescript-ehmpathy",
@@ -41,7 +41,7 @@
41
41
  "prepare:husky": "npx husky install && chmod ug+x .husky/*"
42
42
  },
43
43
  "dependencies": {
44
- "domain-objects": "0.22.1",
44
+ "domain-objects": "0.29.2",
45
45
  "expect": "29.4.2",
46
46
  "flat": "5.0.2",
47
47
  "helpful-errors": "1.3.8",
@@ -68,12 +68,12 @@
68
68
  "eslint-config-prettier": "8.5.0",
69
69
  "eslint-plugin-import": "2.26.0",
70
70
  "eslint-plugin-prettier": "4.2.1",
71
+ "eslint-plugin-unused-imports": "4.3.0",
71
72
  "husky": "8.0.3",
72
73
  "jest": "29.3.1",
73
74
  "prettier": "2.8.1",
74
75
  "test-fns": "1.4.2",
75
76
  "ts-jest": "29.1.3",
76
- "ts-node": "10.9.2",
77
77
  "type-fns": "0.8.1",
78
78
  "typescript": "5.4.5",
79
79
  "visualogic": "1.2.3"
package/readme.md CHANGED
@@ -1,39 +1,150 @@
1
1
  # declapract-typescript-ehmpathy
2
2
 
3
- declapract = declared best practices
3
+ Declared best practices for TypeScript projects.
4
4
 
5
- this repo contains the [ehmpathy org's](https://github.com/ehmpathy) declared best practices for typescript, for usage with [declapract](https://github.com/ehmpathy/declapract)
5
+ Sync your projects with the latest and greatest best practices with codemods powered by [declapract](https://github.com/ehmpathy/declapract).
6
6
 
7
- # usage
7
+ ## What is this?
8
+
9
+ This package contains battle-tested TypeScript best practices from the [ehmpathy org](https://github.com/ehmpathy), packaged for automated enforcement via codemods through [declapract](https://github.com/ehmpathy/declapract).
10
+
11
+ Instead of adhoc configuration of linters, formatters, test frameworks, cicd pipelines, and infrastructure for every project, this package provides:
12
+
13
+ - **40+ declared practices** covering TypeScript development, testing, CI/CD, AWS infrastructure, and more
14
+ - **Automated checking** to verify your repo follows best practices - and avoids bad practices
15
+ - **Automatic fixes** for most common issues
16
+ - **Project templates** to bootstrap new projects with everything configured
17
+
18
+ ## How to use it?
19
+
20
+ ### 1. Install declapract
8
21
 
9
- 1. add `declapract` to your repo
10
22
  ```sh
11
23
  npm install --save-dev declapract
12
24
  ```
13
25
 
14
- 2. add a declapract usage config to your repo
26
+ ### 2. Configure your project
27
+
28
+ Create `declapract.use.yml` in your repo:
29
+
15
30
  ```yml
16
- # declapract.use.yml
17
31
  declarations: npm:declapract-typescript-ehmpathy
18
- useCase: lambda-service # specify which use case your repo is following, see `declapract-typescript-ehmpathy:src/useCases.yml` for options
19
- variables: # specify the values of the variables to use against checks
20
- organizationName: 'awesome-org'
21
- projectName: 'svc-awesome-thing'
22
- infrastructureNamespaceId: 'abcde12345'
23
- slackReleaseWebHook: 'https://...'
32
+ useCase: typescript-project # Choose from: typescript-project, npm-package, lambda-service, lambda-service-with-rds, lambda-service-with-dynamodb, app-react-native-expo
33
+ variables:
34
+ organizationName: 'my-org'
35
+ projectName: 'my-project'
36
+ infrastructureNamespaceId: 'prod-abc123'
37
+ slackReleaseWebHook: 'https://hooks.slack.com/...'
24
38
  ```
25
39
 
26
- 3. clone a declared best practices example, do bootstrap a new repo
40
+ ### 3. Plan changes
41
+
27
42
  ```sh
28
- declapract clone lambda-service-with-rds # bootstrap a new lambda-service-with-rds repo
29
- ```
43
+ # plan all practices
44
+ declapract plan
30
45
 
31
- 4. check that your repo is conforming to best practices
32
- ```
33
- declapract check
46
+ # or scope to one
47
+ declapract plan --practice
34
48
  ```
35
49
 
36
- 5. fix a specific practice that your repo is failing to conform to (if it has an automatic fix declared)
50
+ ### 4. Apply changes
51
+
37
52
  ```sh
38
- declapract fix --practice dates-and-times # e.g., apply a fix for the the dates-and-times practice
53
+ # apply all practices
54
+ declapract apply
55
+
56
+ # or scope to one
57
+ declapract apply --practice
39
58
  ```
59
+
60
+ ## Benefits
61
+
62
+ ### Consistency
63
+ All projects follow the same patterns, making it easy to switch between codebases
64
+
65
+ ### Quality
66
+ Enforced best practices catch issues before code review
67
+
68
+ ### Speed
69
+ Bootstrap new projects in minutes instead of hours
70
+
71
+ ### Maintenance
72
+ Update best practices across all repos by updating one dependency
73
+
74
+ ### Documentation
75
+ Practices are explicitly declared and versioned, serving as living documentation
76
+
77
+ ## What use cases are supported?
78
+
79
+ This package defines complete sets of practices for common project types:
80
+
81
+ ### `typescript-project`
82
+ Base configuration for any TypeScript project (18 core practices)
83
+
84
+ ### `npm-package`
85
+ TypeScript packages published to npm (adds CI/CD for publishing)
86
+
87
+ ### `lambda-service`
88
+ AWS Lambda microservices (adds AWS infrastructure, handlers, config, logging)
89
+
90
+ ### `lambda-service-with-rds`
91
+ Lambda services with PostgreSQL database (adds RDS provisioning and DAOs)
92
+
93
+ ### `lambda-service-with-dynamodb`
94
+ Lambda services with DynamoDB (adds DynamoDB DAOs)
95
+
96
+ ### `app-react-native-expo`
97
+ React Native mobile apps with Expo
98
+
99
+ ## What practices are included?
100
+
101
+ ### Core TypeScript Development
102
+ - **typescript** - Strict TypeScript configuration
103
+ - **lint** - ESLint with TypeScript rules (airbnb-typescript)
104
+ - **format** - Prettier configuration
105
+ - **domain** - Domain-driven design with `domain-objects`
106
+ - **directory-structure-src** - Consistent project structures
107
+ - **runtime-type-checking** - Type-safe validation with `domain-objects`
108
+ - **errors** - Structured error handling with `helpful-errors`
109
+ - etc
110
+
111
+ ### Tests & Quality
112
+ - **tests** - Jest configuration for unit, integration, and acceptance tests
113
+ - **conventional-commits** - Enforced commit message standards
114
+ - **husky** - Git hooks for pre-commit validation
115
+ - etc
116
+
117
+ ### CI/CD
118
+ - **cicd-common** - GitHub Actions workflows for testing
119
+ - **cicd-package** - Publishing workflows for npm packages
120
+ - **cicd-service** - Deployment workflows for services
121
+
122
+ ### AWS Lambda Services
123
+ - **lambda-handlers** - Standard Lambda function structure
124
+ - **lambda-clients** - Type-safe Lambda client wrappers
125
+ - **config** - Environment configuration with AWS Parameter Store
126
+ - **logs** - Structured logging with `simple-log-methods`
127
+ - **uuid** - UUID generation standards
128
+ - etc
129
+
130
+ ### Infrastructure
131
+ - **terraform-common** - Common Terraform patterns
132
+ - **terraform-aws** - AWS-specific Terraform modules
133
+ - **declastruct-github** - GitHub repository configuration
134
+ - **environments-aws** - Multi-environment setup (dev/test/prod)
135
+ - etc
136
+
137
+ ### Persistence
138
+ - **persist-with-rds** - PostgreSQL with `sql-dao-generator`
139
+ - **persist-with-dynamodb** - DynamoDB DAO patterns
140
+
141
+ ### React Native
142
+ - **app-react-native-expo** - Expo app configuration
143
+ - **lint-react** - React-specific linting
144
+ - **lint-react-native** - React Native linting
145
+
146
+ ## Learn more
147
+
148
+ - [declapract documentation](https://github.com/ehmpathy/declapract)
149
+ - [full list of practices](./src/practices)
150
+ - [full list of usecases](./src/useCases.yml)