Skip to content

Commit

Permalink
Merge pull request #506 from LeonLuttenberger/feat/list-of-env-params
Browse files Browse the repository at this point in the history
feat: Support list of env files
  • Loading branch information
dgraeber authored Feb 22, 2024
2 parents 4f28d89 + d2d597f commit 52661f6
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 33 deletions.
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ web_modules/

# dotenv environment variables file
.env
.env.test
.env.*

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down Expand Up @@ -385,4 +385,8 @@ archive/
_build/

# Pytest Setup
/module/
/module/

# Testing
seedfarmer.yaml
tmp-metadata
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch

### New

- support list of env files using `--env-file`

### Changes
- adding `AwsCodeSeederDeployed` and `SeedFarmerDeployed` to all module metadata output for reference (versions used to deploy successfully)
- adding `AWS_CODESEEDER_VERSION` and `SEEDFARMER_VERSION` to all module environment parameters for reference (versions currently in use)
Expand Down
2 changes: 2 additions & 0 deletions docs/source/manifests.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@ In this example, the `glue-db-suffix` parameter will be exposed to the CodeBuild
### Environment Variables
`SeedFarmer` supports using [Dotenv](https://github.com/theskumar/python-dotenv) for dynamic replacement. When a file named `.env` is placed at the projecr root (where `seedfarmer.yaml` resides), any value in a manifest with a key of `envVariable` will be matched and replaced with the corresponding environment variable. You can pass in overriding `.env` files by using the `--env-file` on CLI command invocation.

`SeedFarmer` also supports passing multiple `.env`, by using `--env-file` multiple times. For example: `seedfarmer apply --env-file .env.shared --env-file .env.secret`. If the same value is present in multiple `.env` files, subsequent files will override the value from the previous one. In the aforementioned example, values from `.env.secret` will override values from `.env.shared`.

```yaml
name: opensearch
path: modules/core/opensearch/
Expand Down
21 changes: 12 additions & 9 deletions seedfarmer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@


import logging
import os
from typing import Optional
from typing import List, Optional

import click
from dotenv import load_dotenv

import seedfarmer
from seedfarmer import DEBUG_LOGGING_FORMAT, commands, config, enable_debug
from seedfarmer.cli_groups import bootstrap, init, list, metadata, projectpolicy, remove, store
from seedfarmer.output_utils import print_bolded
from seedfarmer.utils import load_dotenv_files

_logger: logging.Logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -65,8 +64,10 @@ def version() -> None:
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand Down Expand Up @@ -116,7 +117,7 @@ def apply(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
dry_run: bool,
show_manifest: bool,
Expand All @@ -129,7 +130,7 @@ def apply(
enable_debug(format=DEBUG_LOGGING_FORMAT)

# Load environment variables from .env file if it exists
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)
load_dotenv_files(config.OPS_ROOT, env_files)

_logger.info("Apply request with manifest %s", spec)
if dry_run:
Expand Down Expand Up @@ -186,8 +187,10 @@ def apply(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand Down Expand Up @@ -217,7 +220,7 @@ def destroy(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
enable_session_timeout: bool,
session_timeout_interval: int,
Expand All @@ -227,7 +230,7 @@ def destroy(
enable_debug(format=DEBUG_LOGGING_FORMAT)

# Load environment variables from .env file if it exists
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)
load_dotenv_files(config.OPS_ROOT, env_files)

# MUST use seedfarmer.yaml so we can initialize codeseeder configs
project = config.PROJECT
Expand Down
60 changes: 39 additions & 21 deletions seedfarmer/cli_groups/_list_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@

import json
import logging
import os
import sys
from typing import Optional
from typing import List, Optional

import click
from dotenv import load_dotenv

import seedfarmer.mgmt.build_info as bi
import seedfarmer.mgmt.deploy_utils as du
Expand All @@ -33,6 +31,7 @@
print_manifest_inventory,
)
from seedfarmer.services.session_manager import SessionManager
from seedfarmer.utils import load_dotenv_files

_logger: logging.Logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -110,8 +109,10 @@ def list() -> None:
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -128,7 +129,7 @@ def list_dependencies(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
) -> None:
if debug:
Expand All @@ -137,7 +138,8 @@ def list_dependencies(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

SessionManager().get_or_create(project_name=project, profile=profile, region_name=region, qualifier=qualifier)
dep_manifest = du.generate_deployed_manifest(deployment_name=deployment, skip_deploy_spec=True)
Expand Down Expand Up @@ -203,8 +205,10 @@ def list_dependencies(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -221,7 +225,7 @@ def list_deployspec(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
) -> None:
if debug:
Expand All @@ -230,7 +234,8 @@ def list_deployspec(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

session = SessionManager().get_or_create(
project_name=project, profile=profile, region_name=region, qualifier=qualifier
Expand Down Expand Up @@ -310,8 +315,10 @@ def list_deployspec(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -328,7 +335,7 @@ def list_module_metadata(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
export_local_env: bool,
debug: bool,
) -> None:
Expand All @@ -338,7 +345,8 @@ def list_module_metadata(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

session = SessionManager().get_or_create(
project_name=project, profile=profile, region_name=region, qualifier=qualifier
Expand Down Expand Up @@ -407,8 +415,10 @@ def list_module_metadata(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -423,7 +433,7 @@ def list_all_module_metadata(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
) -> None:
if debug:
Expand All @@ -432,7 +442,8 @@ def list_all_module_metadata(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

session = SessionManager().get_or_create(
project_name=project, profile=profile, region_name=region, qualifier=qualifier
Expand Down Expand Up @@ -500,8 +511,10 @@ def list_all_module_metadata(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -516,7 +529,7 @@ def list_modules(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
debug: bool,
) -> None:
if debug:
Expand All @@ -525,7 +538,9 @@ def list_modules(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

SessionManager().get_or_create(project_name=project, profile=profile, region_name=region, qualifier=qualifier)

dep_manifest = du.generate_deployed_manifest(deployment_name=deployment, skip_deploy_spec=True)
Expand Down Expand Up @@ -647,8 +662,10 @@ def list_deployments(
)
@click.option(
"--env-file",
default=".env",
"env_files",
default=[".env"],
help="A relative path to the .env file to load environment variables from",
multiple=True,
required=False,
)
@click.option(
Expand All @@ -666,7 +683,7 @@ def list_build_env_params(
profile: Optional[str],
region: Optional[str],
qualifier: Optional[str],
env_file: str,
env_files: List[str],
export_local_env: str,
debug: bool,
) -> None:
Expand All @@ -678,7 +695,8 @@ def list_build_env_params(

if project is None:
project = _load_project()
load_dotenv(dotenv_path=os.path.join(config.OPS_ROOT, env_file), verbose=True, override=True)

load_dotenv_files(config.OPS_ROOT, env_files=env_files)

session = SessionManager().get_or_create(
project_name=project, profile=profile, region_name=region, qualifier=qualifier
Expand Down
27 changes: 26 additions & 1 deletion seedfarmer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

import hashlib
import logging
from typing import Optional
import os
from typing import List, Optional

import humps
import yaml
from boto3 import Session
from dotenv import dotenv_values, load_dotenv

from seedfarmer.services._service_utils import get_region, get_sts_identity_info

Expand Down Expand Up @@ -158,3 +160,26 @@ def get_deployment_role_arn(

def valid_qualifier(qualifer: str) -> bool:
return True if ((len(qualifer) <= 6) and qualifer.isalnum()) else False


def load_dotenv_files(root_path: str, env_files: List[str]) -> None:
"""
Load the environment variables from the .env files
Parameters
----------
root_path : str
The path to the root of the project
env_files : List[str]
The list of the .env files to load
"""
loaded_values = {}

for env_file in env_files:
_logger.info("Loading environment variables from %s", env_file)
dotenv_path = os.path.join(root_path, env_file)

load_dotenv(dotenv_path=dotenv_path, verbose=True, override=True)
loaded_values.update(dotenv_values(dotenv_path, verbose=True))

_logger.debug("Loaded environment variables: %s", loaded_values)
Loading

0 comments on commit 52661f6

Please sign in to comment.