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

View File

@@ -1,18 +1,86 @@
name: ADR accepted
on:
issues:
types:
- closed
jobs:
main:
name: ADR accepted
runs-on: ubuntu-latest
permissions:
contents: read
jobs:
accept:
runs-on: ubuntu-latest
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:
repo-token: ${{ secrets.GITHUB_TOKEN }}
label: "ADR: accepted"
path: docs/adrs
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- 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

View File

@@ -100,53 +100,55 @@ A direct installation of PostgreSQL will not put the `createdb` command on your
## Documentation
- [Infrastructure overview](#infrastructure-overview)
- [GitHub Repositories](#github-repositories)
- [Terraform](#terraform)
- [AWS](#aws)
- [New Relic](#new-relic)
- [Onboarding](#onboarding)
- [Setting up the infrastructure](#setting-up-the-infrastructure)
- [Testing](#testing)
- [CI testing](#ci-testing)
- [Manual testing](#manual-testing)
- [To run a local OWASP scan](#to-run-a-local-owasp-scan)
- [Deploying](#deploying)
- [Egress Proxy](#egress-proxy)
- [Sandbox environment](#sandbox-environment)
- [Database management](#database-management)
- [Initial state](#initial-state)
- [Data Model Diagram](#data-model-diagram)
- [Migrations](#migrations)
- [Purging user data](#purging-user-data)
- [One-off tasks](#one-off-tasks)
- [How messages are queued and sent](#how-messages-are-queued-and-sent)
- [Writing public APIs](#writing-public-apis)
- [Overview](#overview)
- [Documenting APIs](#documenting-apis)
- [New APIs](#new-apis)
- [API Usage](#api-usage)
- [Connecting to the API](#connecting-to-the-api)
- [Postman Documentation](#postman-documentation)
- [Using OpenAPI documentation](#using-openapi-documentation)
- [Queues and tasks](#queues-and-tasks)
- [Priority queue](#priority-queue)
- [Celery scheduled tasks](#celery-scheduled-tasks)
- [US Notify](#us-notify)
- [System Description](#system-description)
- [Run Book](#run-book)
- [ Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring)
- [ Restaging Apps](#-restaging-apps)
- [ Smoke-testing the App](#-smoke-testing-the-app)
- [ Configuration Management](#-configuration-management)
- [ DNS Changes](#-dns-changes)
- [Exporting test results for compliance monitoring](#exporting-test-results-for-compliance-monitoring)
- [ Known Gotchas](#-known-gotchas)
- [ User Account Management](#-user-account-management)
- [ SMS Phone Number Management](#-sms-phone-number-management)
- [Data Storage Policies \& Procedures](#data-storage-policies--procedures)
- [Potential PII Locations](#potential-pii-locations)
- [Data Retention Policy](#data-retention-policy)
- [Infrastructure overview](./docs/all.md#infrastructure-overview)
- [GitHub Repositories](./docs/all.md#github-repositories)
- [Terraform](./docs/all.md#terraform)
- [AWS](./docs/all.md#aws)
- [New Relic](./docs/all.md#new-relic)
- [Onboarding](./docs/all.md#onboarding)
- [Setting up the infrastructure](./docs/all.md#setting-up-the-infrastructure)
- [Using the logs](./docs/all.md#using-the-logs)
- [Testing](./docs/all.md#testing)
- [CI testing](./docs/all.md#ci-testing)
- [Manual testing](./docs/all.md#manual-testing)
- [To run a local OWASP scan](./docs/all.md#to-run-a-local-owasp-scan)
- [Deploying](./docs/all.md#deploying)
- [Egress Proxy](./docs/all.md#egress-proxy)
- [Managing environment variables](./docs/all.md#managing-environment-variables)
- [Sandbox environment](./docs/all.md#sandbox-environment)
- [Database management](./docs/all.md#database-management)
- [Initial state](./docs/all.md#initial-state)
- [Data Model Diagram](./docs/all.md#data-model-diagram)
- [Migrations](./docs/all.md#migrations)
- [Purging user data](./docs/all.md#purging-user-data)
- [One-off tasks](./docs/all.md#one-off-tasks)
- [How messages are queued and sent](./docs/all.md#how-messages-are-queued-and-sent)
- [Writing public APIs](./docs/all.md#writing-public-apis)
- [Overview](./docs/all.md#overview)
- [Documenting APIs](./docs/all.md#documenting-apis)
- [New APIs](./docs/all.md#new-apis)
- [API Usage](./docs/all.md#api-usage)
- [Connecting to the API](./docs/all.md#connecting-to-the-api)
- [Postman Documentation](./docs/all.md#postman-documentation)
- [Using OpenAPI documentation](./docs/all.md#using-openapi-documentation)
- [Queues and tasks](./docs/all.md#queues-and-tasks)
- [Priority queue](./docs/all.md#priority-queue)
- [Celery scheduled tasks](./docs/all.md#celery-scheduled-tasks)
- [US Notify](./docs/all.md#us-notify)
- [System Description](./docs/all.md#system-description)
- [Run Book](./docs/all.md#run-book)
- [ Alerts, Notifications, Monitoring](./docs/all.md#-alerts-notifications-monitoring)
- [ Restaging Apps](./docs/all.md#-restaging-apps)
- [ Smoke-testing the App](./docs/all.md#-smoke-testing-the-app)
- [ Configuration Management](./docs/all.md#-configuration-management)
- [ DNS Changes](./docs/all.md#-dns-changes)
- [Exporting test results for compliance monitoring](./docs/all.md#exporting-test-results-for-compliance-monitoring)
- [ Known Gotchas](./docs/all.md#-known-gotchas)
- [ User Account Management](./docs/all.md#-user-account-management)
- [ SMS Phone Number Management](./docs/all.md#-sms-phone-number-management)
- [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

View File

@@ -193,6 +193,12 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i
return
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(
template_id=notification["template"],
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,
key_type=KEY_TYPE_NORMAL,
created_at=datetime.utcnow(),
created_by_id=created_by_id,
job_id=notification.get("job", None),
job_row_number=notification.get("row_number", None),
notification_id=notification_id,

View File

@@ -4,6 +4,7 @@ import re
import time
from boto3 import client
from flask import current_app
from app.clients import AWS_CLIENT_CONFIG, Client
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
now = round(time.time() * 1000)
beginning = sent_at
current_app.logger.info(f"TIME RANGE TO CHECK {beginning} to {now}")
next_token = None
all_log_events = []
while True:
@@ -72,13 +74,20 @@ class AwsCloudwatchClient(Client):
all_log_events.extend(log_events)
if len(log_events) > 0:
# We found it
current_app.logger.info(
f"WE FOUND THE EVENT WE WERE LOOKING FOR? {log_events}"
)
break
next_token = response.get("nextToken")
if not next_token:
break
if len(all_log_events) == 0:
print(f"WE FOUND NO LOG EVENTS OVER TIME RANGE {beginning} to {now}")
return all_log_events
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
# 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
@@ -87,24 +96,39 @@ class AwsCloudwatchClient(Client):
account_number = account_number[0]
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 = filter_pattern.replace("XXXXX", message_id)
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:
current_app.logger.info(
"SHOULD RETURN SUCCESS BECAUSE WE FOUND A SUCCESS MESSAGE FOR MESSAGE ID"
)
event = all_log_events[0]
message = json.loads(event["message"])
current_app.logger.info(f"MESSAGE {message}")
return "success", message["delivery"]["providerResponse"]
log_group_name = (
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)
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:
current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE")
event = all_failed_events[0]
message = json.loads(event["message"])
current_app.logger.info(f"MESSAGE {message}")
return "failure", message["delivery"]["providerResponse"]
print(f"RAISING EXCEPTION FOR MESSAGE_ID {message_id}")
raise Exception(
f"No event found for message_id {message_id} notification_id {notification_id}"
)

View File

@@ -5,12 +5,14 @@
- [New Relic](#new-relic)
- [Onboarding](#onboarding)
- [Setting up the infrastructure](#setting-up-the-infrastructure)
- [Using the logs](#using-the-logs)
- [Testing](#testing)
- [CI testing](#ci-testing)
- [Manual testing](#manual-testing)
- [To run a local OWASP scan](#to-run-a-local-owasp-scan)
- [Deploying](#deploying)
- [Egress Proxy](#egress-proxy)
- [Managing environment variables](#managing-environment-variables)
- [Sandbox environment](#sandbox-environment)
- [Database management](#database-management)
- [Initial state](#initial-state)
@@ -85,13 +87,17 @@ In addition to terraform directories in the api and admin apps above:
## 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
There are several remote services required for local development:
* s3
* ses
* sns
* S3
* SES
* SNS
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)
# 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
```
@@ -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`
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
There is a sandbox space, complete with terraform and `deploy-config/sandbox.yml` file available

76
poetry.lock generated
View File

@@ -309,33 +309,33 @@ files = [
[[package]]
name = "black"
version = "23.7.0"
version = "23.9.1"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"},
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"},
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"},
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"},
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"},
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"},
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"},
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"},
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"},
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"},
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
{file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
{file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
{file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
{file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
{file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
{file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
{file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
{file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
{file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
{file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
{file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
{file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
{file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
{file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
{file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
{file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
]
[package.dependencies]
@@ -345,7 +345,7 @@ packaging = ">=22.0"
pathspec = ">=0.9.0"
platformdirs = ">=2"
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]
colorama = ["colorama (>=0.4.3)"]
@@ -1386,20 +1386,20 @@ smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
version = "3.1.36"
version = "3.1.37"
description = "GitPython is a Python library used to interact with Git repositories"
optional = false
python-versions = ">=3.7"
files = [
{file = "GitPython-3.1.36-py3-none-any.whl", hash = "sha256:8d22b5cfefd17c79914226982bb7851d6ade47545b1735a9d010a2a4c26d8388"},
{file = "GitPython-3.1.36.tar.gz", hash = "sha256:4bb0c2a6995e85064140d31a33289aa5dce80133a23d36fcd372d716c54d3ebf"},
{file = "GitPython-3.1.37-py3-none-any.whl", hash = "sha256:5f4c4187de49616d710a77e98ddf17b4782060a1788df441846bddefbb89ab33"},
{file = "GitPython-3.1.37.tar.gz", hash = "sha256:f9b9ddc0761c125d5780eab2d64be4873fc6817c2899cbcb34b02344bdc7bc54"},
]
[package.dependencies]
gitdb = ">=4.0.1,<5"
[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]]
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-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{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-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"},
@@ -2455,7 +2445,7 @@ smartypants = ">=2.0.1"
type = "git"
url = "https://github.com/GSA/notifications-utils.git"
reference = "main"
resolved_reference = "d3ad5b127a257f52f6dc36fe555a4b9c980c16b3"
resolved_reference = "5eadd38702fd187fd2954a5c763314b9b2446bea"
[[package]]
name = "numpy"
@@ -3842,13 +3832,13 @@ files = [
[[package]]
name = "types-pyyaml"
version = "6.0.12.11"
version = "6.0.12.12"
description = "Typing stubs for PyYAML"
optional = false
python-versions = "*"
files = [
{file = "types-PyYAML-6.0.12.11.tar.gz", hash = "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b"},
{file = "types_PyYAML-6.0.12.11-py3-none-any.whl", hash = "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d"},
{file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"},
{file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"},
]
[[package]]
@@ -4291,4 +4281,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "642319df54dfc549b650ff145855594e79d21c6191af06c85632595feef13482"
content-hash = "15aafd504a605f6621f3017ddfba898373885f90926eb16eef363ba5b33b53a6"

View File

@@ -14,7 +14,7 @@ asn1crypto = "==1.5.1"
async-timeout = "==4.0.2"
attrs = "==23.1.0"
awscli = "==1.29.15"
black = "==23.7.0"
black = "==23.9.1"
bcrypt = "==3.2.2"
beautifulsoup4 = "==4.12.2"
billiard = "==3.6.4.0"