also looking at this
fix(ci): deploy with encrypted production env
#82
6 files
+89
-16
| @@ -0,0 +1,48 @@ | ||
| 1 | import { appendFileSync } from 'node:fs' | |
| 2 | import { loadEnv } from '@stacksjs/env/plugin' | |
| 3 | ||
| 4 | const result = loadEnv({ | |
| 5 | path: '.env.production', | |
| 6 | keysFile: '.env.keys', | |
| 7 | env: 'production', | |
| 8 | overload: true, | |
| 9 | quiet: true, | |
| 10 | }) | |
| 11 | ||
| 12 | if (result.errors.length > 0) | |
| 13 | throw new Error(`Could not load .env.production: ${result.errors.join('; ')}`) | |
| 14 | ||
| 15 | const required = [ | |
| 16 | 'AWS_DEFAULT_REGION', | |
| 17 | 'AWS_S3_BUCKET', | |
| 18 | 'AWS_CLOUDFRONT_DISTRIBUTION_ID', | |
| 19 | ] as const | |
| 20 | ||
| 21 | const values = Object.fromEntries(required.map((key) => { | |
| 22 | const value = process.env[key] | |
| 23 | ||
| 24 | if (!value || value.startsWith('encrypted:')) | |
| 25 | throw new Error(`Missing decrypted production value: ${key}`) | |
| 26 | ||
| 27 | if (value.includes('\n') || value.includes('\r')) | |
| 28 | throw new Error(`Production value must be single-line: ${key}`) | |
| 29 | ||
| 30 | return [key, value] | |
| 31 | })) | |
| 32 | ||
| 33 | if (!/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(values.AWS_DEFAULT_REGION)) | |
| 34 | throw new Error('AWS_DEFAULT_REGION is invalid') | |
| 35 | ||
| 36 | if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(values.AWS_S3_BUCKET)) | |
| 37 | throw new Error('AWS_S3_BUCKET is invalid') | |
| 38 | ||
| 39 | if (!/^[A-Z0-9]{13,14}$/.test(values.AWS_CLOUDFRONT_DISTRIBUTION_ID)) | |
| 40 | throw new Error('AWS_CLOUDFRONT_DISTRIBUTION_ID is invalid') | |
| 41 | ||
| 42 | const githubEnv = process.env.GITHUB_ENV | |
| 43 | ||
| 44 | if (!githubEnv) | |
| 45 | throw new Error('GITHUB_ENV is required') | |
| 46 | ||
| 47 | appendFileSync(githubEnv, `${Object.entries(values).map(([key, value]) => `${key}=${value}`).join('\n')}\n`) | |
| 48 | console.log(`Loaded ${required.length} encrypted production values`) | |