Skip to content

Instantly share code, notes, and snippets.

@rolandoam
Last active December 28, 2025 18:02
Show Gist options
  • Select an option

  • Save rolandoam/d86f2d8968527f6b46a03cac1b4f02e5 to your computer and use it in GitHub Desktop.

Select an option

Save rolandoam/d86f2d8968527f6b46a03cac1b4f02e5 to your computer and use it in GitHub Desktop.
(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