From 6799c9f67dc26b7f6acf9aa81e1d58c878604219 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 21:34:04 +0000 Subject: [PATCH 01/16] feat: set up basic config for Sentry --- eligibility_server/app.py | 4 +++- eligibility_server/sentry.py | 14 ++++++++++++++ pyproject.toml | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 eligibility_server/sentry.py diff --git a/eligibility_server/app.py b/eligibility_server/app.py index e9d3021e..7982c9ac 100644 --- a/eligibility_server/app.py +++ b/eligibility_server/app.py @@ -7,7 +7,7 @@ from flask_restful import Api from flask.logging import default_handler -from eligibility_server import db +from eligibility_server import db, sentry from eligibility_server.keypair import get_server_public_key from eligibility_server.settings import Configuration from eligibility_server.verify import Verify @@ -15,6 +15,8 @@ config = Configuration() +sentry.configure() + app = Flask(__name__) app.config.from_object("eligibility_server.settings") app.config.from_envvar("ELIGIBILITY_SERVER_SETTINGS", silent=True) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py new file mode 100644 index 00000000..1bd99c73 --- /dev/null +++ b/eligibility_server/sentry.py @@ -0,0 +1,14 @@ +import sentry_sdk +from sentry_sdk.integrations.flask import FlaskIntegration + + +def configure(): + sentry_sdk.init( + integrations=[ + FlaskIntegration(), + ], + # Set traces_sample_rate to 1.0 to capture 100% + # of transactions for performance monitoring. + # We recommend adjusting this value in production. + traces_sample_rate=1.0, + ) diff --git a/pyproject.toml b/pyproject.toml index bb2a8a50..7910b385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ dependencies = [ "Flask==2.3.3", "Flask-RESTful==0.3.10", "Flask-SQLAlchemy==3.1.1", - "requests==2.31.0" + "requests==2.31.0", + "sentry-sdk[flask]==1.19.1" ] [project.optional-dependencies] From 9da35de500809e32e336346b7e9d2ecfa70c63da Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 22:03:43 +0000 Subject: [PATCH 02/16] refactor: explicit check for SENTRY_DSN Sentry by default will try to use SENTRY_DSN for the dsn option if no argument is passed in, but this makes it clearer --- eligibility_server/app.py | 3 +-- eligibility_server/sentry.py | 27 +++++++++++++++++---------- eligibility_server/settings.py | 11 +++++++++++ tests/test_settings.py | 11 +++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/eligibility_server/app.py b/eligibility_server/app.py index 7982c9ac..8487f845 100644 --- a/eligibility_server/app.py +++ b/eligibility_server/app.py @@ -15,8 +15,6 @@ config = Configuration() -sentry.configure() - app = Flask(__name__) app.config.from_object("eligibility_server.settings") app.config.from_envvar("ELIGIBILITY_SERVER_SETTINGS", silent=True) @@ -25,6 +23,7 @@ # use an app context for access to config settings with app.app_context(): + sentry.configure(config) # configure root logger first, to prevent duplicate log entries from Flask's logger logging.basicConfig(level=config.log_level, format=format_string) # configure Flask's logger diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 1bd99c73..b11ada24 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -1,14 +1,21 @@ import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration +from eligibility_server.settings import Configuration -def configure(): - sentry_sdk.init( - integrations=[ - FlaskIntegration(), - ], - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production. - traces_sample_rate=1.0, - ) + +def configure(config: Configuration): + SENTRY_DSN = config.sentry_dsn + if SENTRY_DSN: + sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=[ + FlaskIntegration(), + ], + # Set traces_sample_rate to 1.0 to capture 100% + # of transactions for performance monitoring. + # We recommend adjusting this value in production. + traces_sample_rate=1.0, + ) + else: + print("SENTRY_DSN not set, so won't send events") diff --git a/eligibility_server/settings.py b/eligibility_server/settings.py index 0e9d979f..def1e915 100644 --- a/eligibility_server/settings.py +++ b/eligibility_server/settings.py @@ -43,6 +43,10 @@ CSV_QUOTING = 3 CSV_QUOTECHAR = '"' +# Sentry + +SENTRY_DSN = None + class Configuration: # App settings @@ -134,3 +138,10 @@ def csv_quoting(self): @property def csv_quotechar(self): return str(current_app.config["CSV_QUOTECHAR"]) + + # Sentry + + @property + def sentry_dsn(self): + sentry_dsn = current_app.config["SENTRY_DSN"] + return str(sentry_dsn) if sentry_dsn else None diff --git a/tests/test_settings.py b/tests/test_settings.py index 63232320..e37588ef 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -225,3 +225,14 @@ def test_configuration_csv_quotechar(mocker, configuration: Configuration): mocker.patch.dict("eligibility_server.settings.current_app.config", mocked_config) assert configuration.csv_quotechar == new_value + + +@pytest.mark.usefixtures("flask") +def test_configuration_sentry_dsn(mocker, configuration: Configuration): + assert configuration.sentry_dsn is None + + new_value = "https://sentry.example.com" + mocked_config = {"SENTRY_DSN": new_value} + mocker.patch.dict("eligibility_server.settings.current_app.config", mocked_config) + + assert configuration.sentry_dsn == new_value From d059f73849d78c9125531add2d966f1b36ad7d95 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 22:21:26 +0000 Subject: [PATCH 03/16] feat(sentry): add Sentry environment option via environment variable the value is not secret and can be driven by a Terraform local var --- eligibility_server/sentry.py | 9 ++++++--- terraform/app_service.tf | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index b11ada24..987abf54 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -1,9 +1,14 @@ +import os + import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration from eligibility_server.settings import Configuration +SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT", "local") + + def configure(config: Configuration): SENTRY_DSN = config.sentry_dsn if SENTRY_DSN: @@ -12,10 +17,8 @@ def configure(config: Configuration): integrations=[ FlaskIntegration(), ], - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production. traces_sample_rate=1.0, + environment=SENTRY_ENVIRONMENT, ) else: print("SENTRY_DSN not set, so won't send events") diff --git a/terraform/app_service.tf b/terraform/app_service.tf index 512b1cc8..a35dfa57 100644 --- a/terraform/app_service.tf +++ b/terraform/app_service.tf @@ -56,6 +56,9 @@ resource "azurerm_linux_web_app" "main" { "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false" "WEBSITES_PORT" = "8000" "WEBSITES_CONTAINER_START_TIME_LIMIT" = "1800" + + # Sentry + "SENTRY_ENVIRONMENT" = "${local.env_name}" } identity { From 1742b4fdadf572763d2879f82291e10d5bbaaf18 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 22:34:47 +0000 Subject: [PATCH 04/16] feat(sentry): add git SHA as release --- eligibility_server/sentry.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 987abf54..4eb701a3 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -1,4 +1,6 @@ import os +import shutil +import subprocess import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration @@ -9,9 +11,32 @@ SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT", "local") +def git_available(): + return bool(shutil.which("git")) + + +# https://stackoverflow.com/a/24584384/358804 +def is_git_directory(path="."): + dev_null = open(os.devnull, "w") + return subprocess.call(["git", "-C", path, "status"], stderr=dev_null, stdout=dev_null) == 0 + + +# https://stackoverflow.com/a/21901260/358804 +def get_git_revision_hash(): + return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii").strip() + + +def get_release() -> str: + """Returns the first available: the SHA from Git, the value from sha.txt, or the VERSION.""" + return get_git_revision_hash() + + def configure(config: Configuration): SENTRY_DSN = config.sentry_dsn if SENTRY_DSN: + release = get_release() + print(f"Enabling Sentry for environment '{SENTRY_ENVIRONMENT}', release '{release}'...") + sentry_sdk.init( dsn=SENTRY_DSN, integrations=[ @@ -19,6 +44,7 @@ def configure(config: Configuration): ], traces_sample_rate=1.0, environment=SENTRY_ENVIRONMENT, + release=release, ) else: print("SENTRY_DSN not set, so won't send events") From 3242383ea587d7cc68de0b88c997050a58c53336 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 22:36:07 +0000 Subject: [PATCH 05/16] chore(sentry): specify in_app_include option https://docs.sentry.io/platforms/python/guides/flask/configuration/options/#in-app-include --- eligibility_server/sentry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 4eb701a3..998f081c 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -45,6 +45,7 @@ def configure(config: Configuration): traces_sample_rate=1.0, environment=SENTRY_ENVIRONMENT, release=release, + in_app_include=["eligibility_server"], ) else: print("SENTRY_DSN not set, so won't send events") From 5ad5218540abe0cfcd67c12c9327ccfaa81bbf7c Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 13 Apr 2023 22:38:31 +0000 Subject: [PATCH 06/16] chore(sentry): specify custom denylist to avoid sending PII --- eligibility_server/sentry.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 998f081c..8cbd62c2 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -4,6 +4,7 @@ import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration +from sentry_sdk.scrubber import EventScrubber, DEFAULT_DENYLIST from eligibility_server.settings import Configuration @@ -31,6 +32,12 @@ def get_release() -> str: return get_git_revision_hash() +def get_denylist(): + # custom denylist + denylist = DEFAULT_DENYLIST + ["sub", "name"] + return denylist + + def configure(config: Configuration): SENTRY_DSN = config.sentry_dsn if SENTRY_DSN: @@ -46,6 +53,10 @@ def configure(config: Configuration): environment=SENTRY_ENVIRONMENT, release=release, in_app_include=["eligibility_server"], + # send_default_pii must be False (the default) for a custom EventScrubber/denylist + # https://docs.sentry.io/platforms/python/data-management/sensitive-data/#event_scrubber + send_default_pii=False, + event_scrubber=EventScrubber(denylist=get_denylist()), ) else: print("SENTRY_DSN not set, so won't send events") From cd80b5b6900447f08d70dc01ab69e4bdfaeb9932 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 17 Apr 2023 20:53:38 +0000 Subject: [PATCH 07/16] refactor(sentry): clean up unneeded code / comments --- eligibility_server/sentry.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 8cbd62c2..236e6337 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -1,5 +1,4 @@ import os -import shutil import subprocess import sentry_sdk @@ -12,23 +11,12 @@ SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT", "local") -def git_available(): - return bool(shutil.which("git")) - - -# https://stackoverflow.com/a/24584384/358804 -def is_git_directory(path="."): - dev_null = open(os.devnull, "w") - return subprocess.call(["git", "-C", path, "status"], stderr=dev_null, stdout=dev_null) == 0 - - # https://stackoverflow.com/a/21901260/358804 def get_git_revision_hash(): return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii").strip() def get_release() -> str: - """Returns the first available: the SHA from Git, the value from sha.txt, or the VERSION.""" return get_git_revision_hash() From a3782f296d61538cdf815b34a5a1938ab07bd8aa Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 17 Apr 2023 21:38:23 +0000 Subject: [PATCH 08/16] chore(sentry): add endpoint for testing only and only if DEBUG_MODE --- eligibility_server/app.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/eligibility_server/app.py b/eligibility_server/app.py index 8487f845..8eb09a79 100644 --- a/eligibility_server/app.py +++ b/eligibility_server/app.py @@ -38,6 +38,14 @@ def TextResponse(content): return response +with app.app_context(): + if config.debug_mode: + + @app.route("/sentry-debug") + def trigger_error(): + raise ValueError("testing Sentry for eligibility-server") + + @app.route("/healthcheck") def healthcheck(): app.logger.info("Request healthcheck") From 8f5a28e4353487ea7802120c3494b0dbb8c765b7 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 17 Apr 2023 21:50:56 +0000 Subject: [PATCH 09/16] docs: add section for Sentry settings --- docs/configuration/settings.md | 24 ++++++++++++++++++++++++ eligibility_server/app.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md index 03fa19c0..c80d06c9 100644 --- a/docs/configuration/settings.md +++ b/docs/configuration/settings.md @@ -133,3 +133,27 @@ These are the possible values for the `CSV_QUOTING` variable: - `csv.QUOTE_ALL`: 1 - To be used when all the values in the CSV file are present inside quotation marks - `csv.QUOTE_NONNUMERIC`: 2 - To be used when the CSV file uses quotes around non-numeric entries - `csv.QUOTE_NONE`: 3 - To be used when the CSV file does not use quotes around entries + +## Sentry + +### `SENTRY_DSN` + +Cal-ITP's Sentry instance collects both [errors ("Issues")](https://sentry.calitp.org/organizations/sentry/issues/?project=4) and app [performance info](https://sentry.calitp.org/organizations/sentry/performance/?project=4). + +[Alerts are sent to #benefits-notify in Slack.](https://sentry.calitp.org/organizations/sentry/alerts/rules/eligibility-server/10/details/) [Others can be configured.](https://sentry.calitp.org/organizations/sentry/alerts/rules/) + +You can troubleshoot Sentry itself by [turning on debug mode](#debug_mode) and visiting `/error/`. + +!!! tldr "Sentry docs" + + [Data Source Name (DSN)](https://docs.sentry.io/product/sentry-basics/dsn-explainer/) + +Enables sending events to Sentry. + +### `SENTRY_ENVIRONMENT` + +!!! tldr "Sentry docs" + + [`environment` config value](https://docs.sentry.io/platforms/python/configuration/options/#environment) + +Segments errors by which deployment they occur in. This defaults to `local`, and can be set to match one of the environment names. diff --git a/eligibility_server/app.py b/eligibility_server/app.py index 8eb09a79..3184f2db 100644 --- a/eligibility_server/app.py +++ b/eligibility_server/app.py @@ -41,7 +41,7 @@ def TextResponse(content): with app.app_context(): if config.debug_mode: - @app.route("/sentry-debug") + @app.route("/error") def trigger_error(): raise ValueError("testing Sentry for eligibility-server") From 39c8c1adb65bb831a55cd8385e6cbb316b97619f Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 11 Oct 2023 15:47:55 +0000 Subject: [PATCH 10/16] chore(sentry): use logger instead of print --- eligibility_server/sentry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 236e6337..49b5eb07 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -1,3 +1,4 @@ +import logging import os import subprocess @@ -7,6 +8,8 @@ from eligibility_server.settings import Configuration +logger = logging.getLogger(__name__) + SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT", "local") @@ -30,7 +33,7 @@ def configure(config: Configuration): SENTRY_DSN = config.sentry_dsn if SENTRY_DSN: release = get_release() - print(f"Enabling Sentry for environment '{SENTRY_ENVIRONMENT}', release '{release}'...") + logger.info(f"Enabling Sentry for environment '{SENTRY_ENVIRONMENT}', release '{release}'...") sentry_sdk.init( dsn=SENTRY_DSN, @@ -47,4 +50,4 @@ def configure(config: Configuration): event_scrubber=EventScrubber(denylist=get_denylist()), ) else: - print("SENTRY_DSN not set, so won't send events") + logger.warning("SENTRY_DSN not set, so won't send events") From 339085241ad8c9d408270c789ef7a98531ca9cca Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 11 Oct 2023 15:49:07 +0000 Subject: [PATCH 11/16] feat(sentry): introduce setting to configure traces sample rate see also cal-itp/benefits#1360 --- eligibility_server/sentry.py | 13 ++++++++++++- eligibility_server/settings.py | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index 49b5eb07..c2355950 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -29,6 +29,17 @@ def get_denylist(): return denylist +def get_traces_sample_rate(config: Configuration): + rate = config.sentry_traces_sample_rate + if rate < 0.0 or rate > 1.0: + logger.warning("SENTRY_TRACES_SAMPLE_RATE was not in the range [0.0, 1.0], defaulting to 0.0") + rate = 0.0 + else: + logger.info(f"SENTRY_TRACES_SAMPLE_RATE set to: {rate}") + + return rate + + def configure(config: Configuration): SENTRY_DSN = config.sentry_dsn if SENTRY_DSN: @@ -40,7 +51,7 @@ def configure(config: Configuration): integrations=[ FlaskIntegration(), ], - traces_sample_rate=1.0, + traces_sample_rate=get_traces_sample_rate(config), environment=SENTRY_ENVIRONMENT, release=release, in_app_include=["eligibility_server"], diff --git a/eligibility_server/settings.py b/eligibility_server/settings.py index def1e915..306e19ff 100644 --- a/eligibility_server/settings.py +++ b/eligibility_server/settings.py @@ -46,6 +46,7 @@ # Sentry SENTRY_DSN = None +SENTRY_TRACES_SAMPLE_RATE = 0.0 class Configuration: @@ -145,3 +146,7 @@ def csv_quotechar(self): def sentry_dsn(self): sentry_dsn = current_app.config["SENTRY_DSN"] return str(sentry_dsn) if sentry_dsn else None + + @property + def sentry_traces_sample_rate(self): + return float(current_app.config["SENTRY_TRACES_SAMPLE_RATE"]) From c9cbba5f4ac02b69061a4fce941ae0416721a40c Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 11 Oct 2023 21:56:10 +0000 Subject: [PATCH 12/16] feat: introduce setting for the agency name default is explicitly unset --- eligibility_server/settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eligibility_server/settings.py b/eligibility_server/settings.py index 306e19ff..7412aca1 100644 --- a/eligibility_server/settings.py +++ b/eligibility_server/settings.py @@ -5,6 +5,7 @@ # App settings +AGENCY_NAME = "[unset]" APP_NAME = "eligibility_server.app" DEBUG_MODE = True HOST = "0.0.0.0" # nosec @@ -52,6 +53,10 @@ class Configuration: # App settings + @property + def agency_name(self): + return str(current_app.config["AGENCY_NAME"]) + @property def app_name(self): return str(current_app.config["APP_NAME"]) From 0d68e4da013d3f02accd35dc00c74a34b5418189 Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 11 Oct 2023 21:56:46 +0000 Subject: [PATCH 13/16] feat(sentry): custom tag with agency name --- eligibility_server/sentry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eligibility_server/sentry.py b/eligibility_server/sentry.py index c2355950..a97d847e 100644 --- a/eligibility_server/sentry.py +++ b/eligibility_server/sentry.py @@ -60,5 +60,7 @@ def configure(config: Configuration): send_default_pii=False, event_scrubber=EventScrubber(denylist=get_denylist()), ) + + sentry_sdk.set_tag("agency_name", config.agency_name) else: logger.warning("SENTRY_DSN not set, so won't send events") From adab6cfb09180f35b2fc16a9c4a3390f894aeab4 Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Thu, 12 Oct 2023 10:53:55 -0700 Subject: [PATCH 14/16] docs(config): describe AGENCY_NAME --- docs/configuration/settings.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md index c80d06c9..7ce2da60 100644 --- a/docs/configuration/settings.md +++ b/docs/configuration/settings.md @@ -6,6 +6,10 @@ The sections below outline in more detail the settings that you either must set ## App settings +### `AGENCY_NAME` + +The name of the agency that this server is deployed for + ### `APP_NAME` The name set on the Flask app From 1b56aadcc8f2ec2c79c3296b50faf5220a92740a Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Thu, 12 Oct 2023 10:59:02 -0700 Subject: [PATCH 15/16] docs(sentry): describe SENTRY_TRACES_SAMPLE_RATE --- docs/configuration/settings.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md index 7ce2da60..10884713 100644 --- a/docs/configuration/settings.md +++ b/docs/configuration/settings.md @@ -161,3 +161,13 @@ Enables sending events to Sentry. [`environment` config value](https://docs.sentry.io/platforms/python/configuration/options/#environment) Segments errors by which deployment they occur in. This defaults to `local`, and can be set to match one of the environment names. + +### `SENTRY_TRACES_SAMPLE_RATE` + +!!! tldr "Sentry docs" + + [`traces_sample_rate` config value](https://docs.sentry.io/platforms/python/configuration/options/#traces-sample-rate) + +Control the volume of transactions sent to Sentry. Value must be a float in the range `[0.0, 1.0]`. + +The default is `0.0` (i.e. no transactions are tracked). From 487930843829aa52e1a766944aadffe193502827 Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 8 Nov 2023 11:17:58 -0800 Subject: [PATCH 16/16] chore(env): comment describing relative path --- .env.sample | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env.sample b/.env.sample index 3a4bbbd5..f56fa610 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,7 @@ FLASK_APP=eligibility_server/app.py + +# this sample uses an odd relative path because the value is +# used by code under the eligibility_server directory +# and thus the config folder is one level up + ELIGIBILITY_SERVER_SETTINGS=../config/sample.py