Merge pull request #503 from GSA/main

Production deploy 9/25/23
This commit is contained in:
Carlo Costino
2023-09-25 17:43:57 -04:00
committed by GitHub
7 changed files with 234 additions and 103 deletions
+77 -9
View File
@@ -1,18 +1,86 @@
name: ADR accepted name: ADR accepted
on: on:
issues: issues:
types: types:
- closed - closed
jobs: permissions:
main: contents: read
name: ADR accepted
runs-on: ubuntu-latest
jobs:
accept:
runs-on: ubuntu-latest
steps: steps:
- name: memorialize the ADR
uses: 18F/adr-automation/accepted@actioning - name: check for tags
if: "${{ !contains(github.event.issue.labels.*.name, 'ADR: accepted' )}}"
shell: bash
run: exit 0
- name: checkout main branch
uses: actions/checkout@v3
with:
ref: main
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
label: "ADR: accepted"
path: docs/adrs - name: get ADR number
id: next
shell: bash
run: |
mkdir -p docs/adrs
LAST_ADR=$(ls docs/adrs/*.md | grep -Eo "/[0-9]+-" | sort | tail -n1 | grep -Eo "[0-9]+")
LAST_ADR=$(echo "$LAST_ADR" | sed -E 's/^0+//')
NEXT_ADR=$(($LAST_ADR + 1))
NEXT_ADR=$(printf "%04i" "$NEXT_ADR")
echo "number=$NEXT_ADR" >> "$GITHUB_OUTPUT"
- name: get date
id: date
shell: bash
run: echo "date=$(date +'%B %d, %Y')" >> "$GITHUB_OUTPUT"
- name: build filename
id: filename
shell: bash
run: |
SLUG=$(printf '%q\n' "${{ github.event.issue.title }}" | tr A-Z a-z)
SLUG=$(printf '%q\n' "$SLUG" | iconv -c -t ascii//TRANSLIT)
SLUG=$(printf '%q\n' "$SLUG" | sed -E 's/[^a-z0-9]+/-/g' | sed -E 's/-+/-/g' | sed -E 's/^-+|-+$//g')
FILENAME="docs/adrs/${{ steps.next.outputs.number }}-$SLUG.md"
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
echo "filename=$FILENAME" >> "$GITHUB_OUTPUT"
- name: write the ADR
uses: DamianReeves/write-file-action@v1.2
with:
path: ${{ steps.filename.outputs.filename }}
write-mode: overwrite
contents: |
# ${{ github.event.issue.title }}
Status: Accepted
Date: ${{ steps.date.outputs.date }}
${{ github.event.issue.body }}
- name: branch, commit, and open PR
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH="adr/auto/${{ steps.filename.outputs.slug }}"
git config --global user.email "tts@gsa.gov"
git config --global user.name "Notify ADR Automation"
git checkout -b $BRANCH
git add docs/adrs/*.md
git commit -m "add ADR ${{ steps.next.outputs.number }}: ${{ github.event.issue.title }}"
git push -f origin $BRANCH
gh pr create \
--title "Add ADR ${{ steps.next.outputs.number }} to the repo" \
--body "This pull request was opened automatically because #${{ github.event.issue.number }} was closed after being marked as an approved ADR. It contains a markdown file capturing the ADR body at the time the issue was closed. Please verify that the markdown is correct before merging!" || true
gh pr merge $BRANCH --auto --squash || true
+49 -47
View File
@@ -100,53 +100,55 @@ A direct installation of PostgreSQL will not put the `createdb` command on your
## Documentation ## Documentation
- [Infrastructure overview](#infrastructure-overview) - [Infrastructure overview](./docs/all.md#infrastructure-overview)
- [GitHub Repositories](#github-repositories) - [GitHub Repositories](./docs/all.md#github-repositories)
- [Terraform](#terraform) - [Terraform](./docs/all.md#terraform)
- [AWS](#aws) - [AWS](./docs/all.md#aws)
- [New Relic](#new-relic) - [New Relic](./docs/all.md#new-relic)
- [Onboarding](#onboarding) - [Onboarding](./docs/all.md#onboarding)
- [Setting up the infrastructure](#setting-up-the-infrastructure) - [Setting up the infrastructure](./docs/all.md#setting-up-the-infrastructure)
- [Testing](#testing) - [Using the logs](./docs/all.md#using-the-logs)
- [CI testing](#ci-testing) - [Testing](./docs/all.md#testing)
- [Manual testing](#manual-testing) - [CI testing](./docs/all.md#ci-testing)
- [To run a local OWASP scan](#to-run-a-local-owasp-scan) - [Manual testing](./docs/all.md#manual-testing)
- [Deploying](#deploying) - [To run a local OWASP scan](./docs/all.md#to-run-a-local-owasp-scan)
- [Egress Proxy](#egress-proxy) - [Deploying](./docs/all.md#deploying)
- [Sandbox environment](#sandbox-environment) - [Egress Proxy](./docs/all.md#egress-proxy)
- [Database management](#database-management) - [Managing environment variables](./docs/all.md#managing-environment-variables)
- [Initial state](#initial-state) - [Sandbox environment](./docs/all.md#sandbox-environment)
- [Data Model Diagram](#data-model-diagram) - [Database management](./docs/all.md#database-management)
- [Migrations](#migrations) - [Initial state](./docs/all.md#initial-state)
- [Purging user data](#purging-user-data) - [Data Model Diagram](./docs/all.md#data-model-diagram)
- [One-off tasks](#one-off-tasks) - [Migrations](./docs/all.md#migrations)
- [How messages are queued and sent](#how-messages-are-queued-and-sent) - [Purging user data](./docs/all.md#purging-user-data)
- [Writing public APIs](#writing-public-apis) - [One-off tasks](./docs/all.md#one-off-tasks)
- [Overview](#overview) - [How messages are queued and sent](./docs/all.md#how-messages-are-queued-and-sent)
- [Documenting APIs](#documenting-apis) - [Writing public APIs](./docs/all.md#writing-public-apis)
- [New APIs](#new-apis) - [Overview](./docs/all.md#overview)
- [API Usage](#api-usage) - [Documenting APIs](./docs/all.md#documenting-apis)
- [Connecting to the API](#connecting-to-the-api) - [New APIs](./docs/all.md#new-apis)
- [Postman Documentation](#postman-documentation) - [API Usage](./docs/all.md#api-usage)
- [Using OpenAPI documentation](#using-openapi-documentation) - [Connecting to the API](./docs/all.md#connecting-to-the-api)
- [Queues and tasks](#queues-and-tasks) - [Postman Documentation](./docs/all.md#postman-documentation)
- [Priority queue](#priority-queue) - [Using OpenAPI documentation](./docs/all.md#using-openapi-documentation)
- [Celery scheduled tasks](#celery-scheduled-tasks) - [Queues and tasks](./docs/all.md#queues-and-tasks)
- [US Notify](#us-notify) - [Priority queue](./docs/all.md#priority-queue)
- [System Description](#system-description) - [Celery scheduled tasks](./docs/all.md#celery-scheduled-tasks)
- [Run Book](#run-book) - [US Notify](./docs/all.md#us-notify)
- [ Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring) - [System Description](./docs/all.md#system-description)
- [ Restaging Apps](#-restaging-apps) - [Run Book](./docs/all.md#run-book)
- [ Smoke-testing the App](#-smoke-testing-the-app) - [ Alerts, Notifications, Monitoring](./docs/all.md#-alerts-notifications-monitoring)
- [ Configuration Management](#-configuration-management) - [ Restaging Apps](./docs/all.md#-restaging-apps)
- [ DNS Changes](#-dns-changes) - [ Smoke-testing the App](./docs/all.md#-smoke-testing-the-app)
- [Exporting test results for compliance monitoring](#exporting-test-results-for-compliance-monitoring) - [ Configuration Management](./docs/all.md#-configuration-management)
- [ Known Gotchas](#-known-gotchas) - [ DNS Changes](./docs/all.md#-dns-changes)
- [ User Account Management](#-user-account-management) - [Exporting test results for compliance monitoring](./docs/all.md#exporting-test-results-for-compliance-monitoring)
- [ SMS Phone Number Management](#-sms-phone-number-management) - [ Known Gotchas](./docs/all.md#-known-gotchas)
- [Data Storage Policies \& Procedures](#data-storage-policies--procedures) - [ User Account Management](./docs/all.md#-user-account-management)
- [Potential PII Locations](#potential-pii-locations) - [ SMS Phone Number Management](./docs/all.md#-sms-phone-number-management)
- [Data Retention Policy](#data-retention-policy) - [Data Storage Policies \& Procedures](./docs/all.md#data-storage-policies--procedures)
- [Potential PII Locations](./docs/all.md#potential-pii-locations)
- [Data Retention Policy](./docs/all.md#data-retention-policy)
## License && public domain ## License && public domain
+7
View File
@@ -193,6 +193,12 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i
return return
try: try:
job_id = notification.get("job", None)
created_by_id = None
if job_id:
job = dao_get_job_by_id(job_id)
created_by_id = job.created_by_id
saved_notification = persist_notification( saved_notification = persist_notification(
template_id=notification["template"], template_id=notification["template"],
template_version=notification["template_version"], template_version=notification["template_version"],
@@ -203,6 +209,7 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i
api_key_id=None, api_key_id=None,
key_type=KEY_TYPE_NORMAL, key_type=KEY_TYPE_NORMAL,
created_at=datetime.utcnow(), created_at=datetime.utcnow(),
created_by_id=created_by_id,
job_id=notification.get("job", None), job_id=notification.get("job", None),
job_row_number=notification.get("row_number", None), job_row_number=notification.get("row_number", None),
notification_id=notification_id, notification_id=notification_id,
+24
View File
@@ -4,6 +4,7 @@ import re
import time import time
from boto3 import client from boto3 import client
from flask import current_app
from app.clients import AWS_CLIENT_CONFIG, Client from app.clients import AWS_CLIENT_CONFIG, Client
from app.cloudfoundry_config import cloud_config from app.cloudfoundry_config import cloud_config
@@ -50,6 +51,7 @@ class AwsCloudwatchClient(Client):
# Check all cloudwatch logs from the time the notification was sent (currently 5 minutes previously) until now # Check all cloudwatch logs from the time the notification was sent (currently 5 minutes previously) until now
now = round(time.time() * 1000) now = round(time.time() * 1000)
beginning = sent_at beginning = sent_at
current_app.logger.info(f"TIME RANGE TO CHECK {beginning} to {now}")
next_token = None next_token = None
all_log_events = [] all_log_events = []
while True: while True:
@@ -72,13 +74,20 @@ class AwsCloudwatchClient(Client):
all_log_events.extend(log_events) all_log_events.extend(log_events)
if len(log_events) > 0: if len(log_events) > 0:
# We found it # We found it
current_app.logger.info(
f"WE FOUND THE EVENT WE WERE LOOKING FOR? {log_events}"
)
break break
next_token = response.get("nextToken") next_token = response.get("nextToken")
if not next_token: if not next_token:
break break
if len(all_log_events) == 0:
print(f"WE FOUND NO LOG EVENTS OVER TIME RANGE {beginning} to {now}")
return all_log_events return all_log_events
def check_sms(self, message_id, notification_id, created_at): def check_sms(self, message_id, notification_id, created_at):
if os.getenv("LOCALSTACK_ENDPOINT_URL"):
current_app.logger.info("GADZOOKS WE ARE RUNNING WITH LOCALSTACK")
region = cloud_config.sns_region region = cloud_config.sns_region
# TODO this clumsy approach to getting the account number will be fixed as part of notify-api #258 # TODO this clumsy approach to getting the account number will be fixed as part of notify-api #258
account_number = cloud_config.ses_domain_arn account_number = cloud_config.ses_domain_arn
@@ -87,24 +96,39 @@ class AwsCloudwatchClient(Client):
account_number = account_number[0] account_number = account_number[0]
log_group_name = f"sns/{region}/{account_number}/DirectPublishToPhoneNumber" log_group_name = f"sns/{region}/{account_number}/DirectPublishToPhoneNumber"
current_app.logger.info(
f"LOG GROUP NAME: {log_group_name} MESSAGE ID: {message_id}"
)
filter_pattern = '{$.notification.messageId="XXXXX"}' filter_pattern = '{$.notification.messageId="XXXXX"}'
filter_pattern = filter_pattern.replace("XXXXX", message_id) filter_pattern = filter_pattern.replace("XXXXX", message_id)
all_log_events = self._get_log(filter_pattern, log_group_name, created_at) all_log_events = self._get_log(filter_pattern, log_group_name, created_at)
current_app.logger.info(f"NUMBER OF ALL LOG EVENTS {len(all_log_events)}")
if all_log_events and len(all_log_events) > 0: if all_log_events and len(all_log_events) > 0:
current_app.logger.info(
"SHOULD RETURN SUCCESS BECAUSE WE FOUND A SUCCESS MESSAGE FOR MESSAGE ID"
)
event = all_log_events[0] event = all_log_events[0]
message = json.loads(event["message"]) message = json.loads(event["message"])
current_app.logger.info(f"MESSAGE {message}")
return "success", message["delivery"]["providerResponse"] return "success", message["delivery"]["providerResponse"]
log_group_name = ( log_group_name = (
f"sns/{region}/{account_number}/DirectPublishToPhoneNumber/Failure" f"sns/{region}/{account_number}/DirectPublishToPhoneNumber/Failure"
) )
current_app.logger.info(f"FAILURE LOG GROUP NAME {log_group_name}")
all_failed_events = self._get_log(filter_pattern, log_group_name, created_at) all_failed_events = self._get_log(filter_pattern, log_group_name, created_at)
current_app.logger.info(
f"NUMBER OF ALL FAILED LOG EVENTS {len(all_failed_events)}"
)
if all_failed_events and len(all_failed_events) > 0: if all_failed_events and len(all_failed_events) > 0:
current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE")
event = all_failed_events[0] event = all_failed_events[0]
message = json.loads(event["message"]) message = json.loads(event["message"])
current_app.logger.info(f"MESSAGE {message}")
return "failure", message["delivery"]["providerResponse"] return "failure", message["delivery"]["providerResponse"]
print(f"RAISING EXCEPTION FOR MESSAGE_ID {message_id}")
raise Exception( raise Exception(
f"No event found for message_id {message_id} notification_id {notification_id}" f"No event found for message_id {message_id} notification_id {notification_id}"
) )
+43 -3
View File
@@ -5,12 +5,14 @@
- [New Relic](#new-relic) - [New Relic](#new-relic)
- [Onboarding](#onboarding) - [Onboarding](#onboarding)
- [Setting up the infrastructure](#setting-up-the-infrastructure) - [Setting up the infrastructure](#setting-up-the-infrastructure)
- [Using the logs](#using-the-logs)
- [Testing](#testing) - [Testing](#testing)
- [CI testing](#ci-testing) - [CI testing](#ci-testing)
- [Manual testing](#manual-testing) - [Manual testing](#manual-testing)
- [To run a local OWASP scan](#to-run-a-local-owasp-scan) - [To run a local OWASP scan](#to-run-a-local-owasp-scan)
- [Deploying](#deploying) - [Deploying](#deploying)
- [Egress Proxy](#egress-proxy) - [Egress Proxy](#egress-proxy)
- [Managing environment variables](#managing-environment-variables)
- [Sandbox environment](#sandbox-environment) - [Sandbox environment](#sandbox-environment)
- [Database management](#database-management) - [Database management](#database-management)
- [Initial state](#initial-state) - [Initial state](#initial-state)
@@ -85,13 +87,17 @@ In addition to terraform directories in the api and admin apps above:
## Terraform ## Terraform
We use Terraform to manage our infrastructure, providing consistent setups across the environments.
Our Terraform configurations manage components via cloud.gov. This means that the configurations should work out of the box if you are using a Cloud Foundry platform, but will not work for setups based on raw AWS.
### Development ### Development
There are several remote services required for local development: There are several remote services required for local development:
* s3 * S3
* ses * SES
* sns * SNS
Credentials for these services are created by running: Credentials for these services are created by running:
@@ -205,6 +211,20 @@ Example answers for toll-free registration form
![example answers for toll-free registration form](./toll-free-registration.png) ![example answers for toll-free registration form](./toll-free-registration.png)
# Using the logs
If you're using the `cf` CLI, you can run `cf logs notify-api-ENV` and/or `cf logs notify-admin-ENV` to stream logs in real time. Add `--recent` to get the last few logs, though logs often move pretty quickly.
For general log searching, [the cloud.gov Kibana instance](https://logs.fr.cloud.gov/) is powerful, though quite complex to get started. For shortcuts to errors, some team members have New Relic access.
The links below will open a filtered view with logs from both applications, which can then be filtered further. However, for the links to work, you need to paste them into the URL bar while *already* logged into and viewing the Kibana page. If not, you'll just be redirected to the generic dashboard.
Production: https://logs.fr.cloud.gov/app/discover#/view/218a6790-596d-11ee-a43a-090d426b9a38
Demo: https://logs.fr.cloud.gov/app/discover#/view/891392a0-596e-11ee-921a-1b6b2f4d89ed
Staging: https://logs.fr.cloud.gov/app/discover#/view/73d7c820-596e-11ee-a43a-090d426b9a38
Once in the view, you'll likely want to adjust the time range in the upper right of the page.
# Testing # Testing
``` ```
@@ -304,6 +324,26 @@ application to a select list of allowed domains.
Update the allowed domains by updating `deploy-config/egress_proxy/notify-api-<env>.allow.acl` Update the allowed domains by updating `deploy-config/egress_proxy/notify-api-<env>.allow.acl`
and deploying an updated version of the application throught he normal deploy process. and deploying an updated version of the application throught he normal deploy process.
## Managing environment variables
For an environment variable to make its way into the cloud.gov environment, it *must* end up in the `manifest.yml` file. Based on the deployment approach described above, there are 2 ways for this to happen.
### Secret environment variables
Because secrets are pulled from GitHub, they must be passed from our action to the deploy action and then placed into `manifest.yml`. This means that they should be in a 4 places:
- [ ] The GitHub secrets store
- [ ] The deploy action in the `env` section using the format `{secrets.SECRET_NAME}`
- [ ] The deploy action in the `push_arguments` section using the format `--var SECRET_NAME="$SECRET_NAME"`
- [ ] The manifest using the format `SECRET_NAME: ((SECRET_NAME))`
### Public environment variables
Public env vars make up the configuration in `deploy-config`. These are pulled in together by the `--vars-file` line in the deploy action. To add or update one, it should be in 2 places:
- [ ] The relevant YAML file in `deploy-config` using the format `var_name: value`
- [ ] The manifest using the format `((var_name))`
## Sandbox environment ## Sandbox environment
There is a sandbox space, complete with terraform and `deploy-config/sandbox.yml` file available There is a sandbox space, complete with terraform and `deploy-config/sandbox.yml` file available
Generated
+33 -43
View File
@@ -309,33 +309,33 @@ files = [
[[package]] [[package]]
name = "black" name = "black"
version = "23.7.0" version = "23.9.1"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
] ]
[package.dependencies] [package.dependencies]
@@ -345,7 +345,7 @@ packaging = ">=22.0"
pathspec = ">=0.9.0" pathspec = ">=0.9.0"
platformdirs = ">=2" platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras] [package.extras]
colorama = ["colorama (>=0.4.3)"] colorama = ["colorama (>=0.4.3)"]
@@ -1386,20 +1386,20 @@ smmap = ">=3.0.1,<6"
[[package]] [[package]]
name = "gitpython" name = "gitpython"
version = "3.1.36" version = "3.1.37"
description = "GitPython is a Python library used to interact with Git repositories" description = "GitPython is a Python library used to interact with Git repositories"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "GitPython-3.1.36-py3-none-any.whl", hash = "sha256:8d22b5cfefd17c79914226982bb7851d6ade47545b1735a9d010a2a4c26d8388"}, {file = "GitPython-3.1.37-py3-none-any.whl", hash = "sha256:5f4c4187de49616d710a77e98ddf17b4782060a1788df441846bddefbb89ab33"},
{file = "GitPython-3.1.36.tar.gz", hash = "sha256:4bb0c2a6995e85064140d31a33289aa5dce80133a23d36fcd372d716c54d3ebf"}, {file = "GitPython-3.1.37.tar.gz", hash = "sha256:f9b9ddc0761c125d5780eab2d64be4873fc6817c2899cbcb34b02344bdc7bc54"},
] ]
[package.dependencies] [package.dependencies]
gitdb = ">=4.0.1,<5" gitdb = ">=4.0.1,<5"
[package.extras] [package.extras]
test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar", "virtualenv"] test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar"]
[[package]] [[package]]
name = "govuk-bank-holidays" name = "govuk-bank-holidays"
@@ -2044,16 +2044,6 @@ files = [
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
@@ -2455,7 +2445,7 @@ smartypants = ">=2.0.1"
type = "git" type = "git"
url = "https://github.com/GSA/notifications-utils.git" url = "https://github.com/GSA/notifications-utils.git"
reference = "main" reference = "main"
resolved_reference = "d3ad5b127a257f52f6dc36fe555a4b9c980c16b3" resolved_reference = "5eadd38702fd187fd2954a5c763314b9b2446bea"
[[package]] [[package]]
name = "numpy" name = "numpy"
@@ -3842,13 +3832,13 @@ files = [
[[package]] [[package]]
name = "types-pyyaml" name = "types-pyyaml"
version = "6.0.12.11" version = "6.0.12.12"
description = "Typing stubs for PyYAML" description = "Typing stubs for PyYAML"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "types-PyYAML-6.0.12.11.tar.gz", hash = "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b"}, {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"},
{file = "types_PyYAML-6.0.12.11-py3-none-any.whl", hash = "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d"}, {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"},
] ]
[[package]] [[package]]
@@ -4291,4 +4281,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "642319df54dfc549b650ff145855594e79d21c6191af06c85632595feef13482" content-hash = "15aafd504a605f6621f3017ddfba898373885f90926eb16eef363ba5b33b53a6"
+1 -1
View File
@@ -14,7 +14,7 @@ asn1crypto = "==1.5.1"
async-timeout = "==4.0.2" async-timeout = "==4.0.2"
attrs = "==23.1.0" attrs = "==23.1.0"
awscli = "==1.29.15" awscli = "==1.29.15"
black = "==23.7.0" black = "==23.9.1"
bcrypt = "==3.2.2" bcrypt = "==3.2.2"
beautifulsoup4 = "==4.12.2" beautifulsoup4 = "==4.12.2"
billiard = "==3.6.4.0" billiard = "==3.6.4.0"