Last active
December 28, 2025 18:02
-
-
Save rolandoam/d86f2d8968527f6b46a03cac1b4f02e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (module dotenv | |
| (dotenv/load) | |
| (import | |
| scheme | |
| chicken.base | |
| chicken.string | |
| chicken.process-context | |
| chicken.io | |
| chicken.file | |
| chicken.condition | |
| srfi-1 ;; last | |
| srfi-13 | |
| regex | |
| aws | |
| logger) | |
| (logger/install DOTENV) | |
| (define ENV-RE "^(\\s*#.*|(\\s*export)?\\s*(\\w+)\\s*=\\s*(\"(.+)\"|(.+)))$") | |
| (define (parse-env-line line) | |
| (let* ((re (regexp ENV-RE)) | |
| (parts (string-match re line)) | |
| (commented? (and parts (not (list-ref parts 2)) (not (list-ref parts 3)))) | |
| (key (and parts (not commented?) (list-ref parts 3))) ; group 3 is key | |
| (quoted-val (and parts (not commented?) (list-ref parts 5))) ; group 5 is quoted value | |
| (unquoted-val (and parts (not commented?) (list-ref parts 6))) ; group 6 is unquoted value | |
| (val (or quoted-val unquoted-val))) | |
| (if (and key val) | |
| (values key val) | |
| (values #f #f)))) | |
| ;; wrapper that only overrides current env variables if passed the | |
| ;; force param | |
| (define (setenv key val #!optional force) | |
| (let ((existing (get-environment-variable key))) | |
| (if (or force (not existing)) | |
| (begin | |
| (d "Setting env " key) | |
| (set-environment-variable! key val)) | |
| (d "Not overriding " key)))) | |
| ;; Load environment variables from SSM Parameter Store | |
| ;; Strips the path prefix from parameter names to get env var names | |
| ;; Example: /myapp/prod/LIVEKIT_API_KEY -> LIVEKIT_API_KEY | |
| (define (load-from-ssm prefix) | |
| (d "Loading environment from AWS SSM Parameter Store: " prefix) | |
| (handle-exceptions | |
| exn | |
| (begin | |
| (d "ERROR: Failed to load from SSM: " ((condition-property-accessor 'exn 'message) exn)) | |
| (d "Continuing without SSM parameters...")) | |
| (let* ((params (aws/ssm/get-parameters-by-path prefix)) | |
| (prefix-len (string-length prefix))) | |
| (for-each | |
| (lambda (param) | |
| (let* ((full-name (car param)) | |
| (value (cdr param)) | |
| ;; Strip prefix to get env var name | |
| (env-name (if (substring=? prefix full-name) | |
| (substring full-name prefix-len) | |
| full-name))) | |
| (d "Trying to set " env-name " from SSM") | |
| (setenv env-name value))) | |
| params) | |
| (d "Loaded " (length params) " parameters from SSM")))) | |
| ;; Load environment variables from .env file or SSM Parameter Store | |
| ;; If .env file exists, loads from file (local development) | |
| ;; If .env file doesn't exist, tries to load from SSM using prefix | |
| ;; ssm-prefix can be provided or defaults to SSM_PARAMETER_PREFIX env var | |
| (define (dotenv/load #!optional (file ".env") (ssm-prefix #f)) | |
| (let ((prefix (or ssm-prefix (get-environment-variable "SSM_PARAMETER_PREFIX")))) | |
| (cond | |
| ;; .env file exists - use it (local development) | |
| ((file-exists? file) | |
| (d "Loading environment from file: " file) | |
| (with-input-from-file file | |
| (lambda () | |
| (let loop ((current-line (read-line))) | |
| (when (not (eq? current-line #!eof)) | |
| (let-values (((key val) (parse-env-line current-line))) | |
| (if (and key val) | |
| (setenv key val)) | |
| (loop (read-line)))))))) | |
| ;; No .env file but SSM prefix is set - use SSM (production) | |
| (prefix | |
| (load-from-ssm prefix)) | |
| ;; No .env file and no SSM prefix - warn and continue | |
| (else | |
| (d "WARNING: No .env file found and SSM_PARAMETER_PREFIX not set") | |
| (d "Continuing without environment configuration..."))))) | |
| ;; load on import | |
| (dotenv/load) | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment