mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 09:37:03 +02:00
Merge branch 'main' into ik/setup-uswds
This commit is contained in:
commit
0561d1f0ae
23 changed files with 552 additions and 101 deletions
|
@ -35,10 +35,10 @@ cf login -a api.fr.cloud.gov --sso
|
|||
- [ ] Setup [commit signing in Github](#setting-up-commit-signing) and with git locally.
|
||||
|
||||
### Steps for the onboarder
|
||||
- [ ] Add the onboardee to cloud.gov org and relevant spaces as a SpaceDeveloper
|
||||
- [ ] Add the onboardee to cloud.gov org (cisa-getgov-prototyping) and relevant spaces (unstable) as a SpaceDeveloper
|
||||
|
||||
```bash
|
||||
cf set-space-role <cloud.account@email.gov> sandbox-gsa dotgov-poc SpaceDeveloper
|
||||
cf set-space-role <cloud.account@email.gov> cisa-getgov-prototyping unstable SpaceDeveloper
|
||||
```
|
||||
- [ ] Add the onboardee to our login.gov sandbox team (`.gov registrar poc`) via the [dashboard](https://dashboard.int.identitysandbox.gov/)
|
||||
|
||||
|
|
22
.github/workflows/deploy.yaml
vendored
22
.github/workflows/deploy.yaml
vendored
|
@ -3,8 +3,7 @@ name: Build and deploy
|
|||
# This workflow runs on pushes to main (typically,
|
||||
# a merged pull request) and on pushes of tagged commits.
|
||||
|
||||
# Pushes to main will deploy to Unstable; tagged commits
|
||||
# will deploy to Staging
|
||||
# Pushes to main will deploy to Staging
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -17,9 +16,9 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy-unstable:
|
||||
deploy-staging:
|
||||
# if this job runs on a branch, we deduce that code
|
||||
# has been pushed to main and should be deployed to unstable
|
||||
# has been pushed to main and should be deployed to staging
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -30,13 +29,8 @@ jobs:
|
|||
env:
|
||||
DEPLOY_NOW: thanks
|
||||
with:
|
||||
cf_username: ${{ secrets.CF_USERNAME }}
|
||||
cf_password: ${{ secrets.CF_PASSWORD }}
|
||||
cf_org: sandbox-gsa
|
||||
cf_space: dotgov-poc
|
||||
push_arguments: "-f ops/manifests/manifest-unstable.yaml"
|
||||
|
||||
# deploy-staging:
|
||||
# # if this job runs on a tag, we deduce that code
|
||||
# # has been tagged for release and should be deployed to staging
|
||||
# if: ${{ github.ref_type == 'tag' }}
|
||||
cf_username: ${{ secrets.CF_STAGING_USERNAME }}
|
||||
cf_password: ${{ secrets.CF_STAGING_PASSWORD }}
|
||||
cf_org: cisa-getgov-prototyping
|
||||
cf_space: staging
|
||||
push_arguments: "-f ops/manifests/manifest-staging.yaml"
|
||||
|
|
17
.github/workflows/migrate.yaml
vendored
17
.github/workflows/migrate.yaml
vendored
|
@ -3,7 +3,7 @@ name: Run Migrations
|
|||
# This workflow can be run from the CLI
|
||||
# gh workflow run migrate.yaml -f environment=sandbox
|
||||
# OR
|
||||
# cf run-task getgov-unstable --wait \
|
||||
# cf run-task getgov-staging --wait \
|
||||
# --command 'python manage.py migrate' --name migrate
|
||||
|
||||
on:
|
||||
|
@ -13,22 +13,19 @@ on:
|
|||
type: choice
|
||||
description: Where should we run migrations
|
||||
options:
|
||||
- unstable
|
||||
- staging
|
||||
|
||||
jobs:
|
||||
migrate-unstable:
|
||||
if: ${{ github.event.inputs.environment == 'unstable' }}
|
||||
migrate-staging:
|
||||
if: ${{ github.event.inputs.environment == 'staging' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run Django migrations for unstable
|
||||
- name: Run Django migrations for staging
|
||||
uses: 18f/cg-deploy-action@main
|
||||
with:
|
||||
cf_username: ${{ secrets.CF_USERNAME }}
|
||||
cf_password: ${{ secrets.CF_PASSWORD }}
|
||||
cf_org: sandbox-gsa
|
||||
cf_space: dotgov-poc
|
||||
full_command: "cf run-task getgov-unstable --wait --command 'python manage.py migrate' --name migrate"
|
||||
cf_org: cisa-getgov-prototyping
|
||||
cf_space: staging
|
||||
full_command: "cf run-task getgov-staging --wait --command 'python manage.py migrate' --name migrate"
|
||||
|
||||
# migrate:
|
||||
# if: ${{ github.event.inputs.environment == 'staging' }}
|
37
.github/workflows/test.yaml
vendored
37
.github/workflows/test.yaml
vendored
|
@ -33,3 +33,40 @@ jobs:
|
|||
- name: Run bandit security scanning
|
||||
working-directory: ./src
|
||||
run: bandit -r .
|
||||
|
||||
python-test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_DB: app
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: feedabee
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
- name: Install Django
|
||||
working-directory: ./src
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pipenv
|
||||
pipenv install --system --dev
|
||||
- name: Test
|
||||
working-directory: ./src
|
||||
env:
|
||||
PYTHONUNBUFFERED: yup
|
||||
DATABASE_URL: postgres://user:feedabee@localhost/app
|
||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
||||
DJANGO_SECRET_KEY: feedabee
|
||||
DJANGO_DEBUG: True
|
||||
run: ./manage.py test
|
||||
|
|
63
docs/developer/README.md
Normal file
63
docs/developer/README.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Development
|
||||
========================
|
||||
|
||||
If you're new to Django, see [Getting Started with Django](https://www.djangoproject.com/start/) for an introduction to the framework.
|
||||
|
||||
## Local Setup
|
||||
|
||||
* Install Docker <https://docs.docker.com/get-docker/>
|
||||
* Initialize the application:
|
||||
|
||||
```shell
|
||||
cd src
|
||||
docker-compose build
|
||||
```
|
||||
* Run the server: `docker-compose up`
|
||||
|
||||
Press Ctrl-c when you'd like to exit or pass `-d` to run in detached mode.
|
||||
|
||||
Visit the running application at [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
## Setting Vars
|
||||
|
||||
Every environment variable for local development is set in [src/docker-compose.yml](../../src/docker-compose.yml).
|
||||
|
||||
Including variables which would be secrets and set via a different mechanism elsewhere.
|
||||
|
||||
## Viewing Logs
|
||||
|
||||
If you run via `docker-compose up`, you'll see the logs in your terminal.
|
||||
|
||||
If you run via `docker-compose up -d`, you can get logs with `docker-compose logs -f`.
|
||||
|
||||
You can change the logging verbosity, if needed. Do a web search for "django log level".
|
||||
|
||||
## Running tests
|
||||
|
||||
Crash course on Docker's `run` vs `exec`: in order to run the tests inside of a container, a container must be running. If you already have a container running, you can use `exec`. If you do not, you can use `run`, which will attempt to start one.
|
||||
|
||||
To get a container running:
|
||||
|
||||
```shell
|
||||
cd src
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Django's test suite:
|
||||
|
||||
```shell
|
||||
docker-compose exec app ./manage.py test
|
||||
```
|
||||
|
||||
OR
|
||||
|
||||
```shell
|
||||
docker-compose exec app python -Wa ./manage.py test # view deprecation warnings
|
||||
```
|
||||
|
||||
Linters:
|
||||
|
||||
```shell
|
||||
docker-compose exec app ./manage.py lint
|
||||
```
|
45
docs/operations/README.md
Normal file
45
docs/operations/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Operations
|
||||
========================
|
||||
|
||||
Some basic information and setup steps are included in this README.
|
||||
|
||||
Instructions for specific actions can be found in our [runbooks](./runbooks/).
|
||||
|
||||
## Continuous Delivery
|
||||
|
||||
We use a [cloud.gov service account](https://cloud.gov/docs/services/cloud-gov-service-account/) to deploy from this repository to cloud.gov with a SpaceDeveloper user.
|
||||
|
||||
## Authenticating to Cloud.gov via the command line
|
||||
|
||||
You'll need the [Cloud Foundry CLI](https://docs.cloud.gov/getting-started/setup/).
|
||||
|
||||
We use the V7 Cloud Foundry CLI.
|
||||
|
||||
```shell
|
||||
cf login -a api.fr.cloud.gov --sso
|
||||
```
|
||||
|
||||
After authenticating, make sure you are targeting the correct org and space!
|
||||
|
||||
```bash
|
||||
cf spaces
|
||||
cf target -o <ORG> -s <SPACE>
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
In sandbox, created with `cf create-service aws-rds micro-psql getgov-ENV-database`.
|
||||
|
||||
Binding the database in `manifest-<ENVIRONMENT>.json` automatically inserts the connection string into the environment as `DATABASE_URL`.
|
||||
|
||||
[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).
|
||||
|
||||
# Deploy
|
||||
|
||||
We have two environments: `unstable` and `staging`. Developers can deploy locally to unstable whenever they want. However, only our CD service can deploy to `staging`, and it does so on every commit to `main`. This is to ensure that we have a "golden" environment to point to, and can still test things out in an unstable space. To deploy locally to `unstable`:
|
||||
|
||||
```bash
|
||||
cf target -o cisa-getgov-prototyping -s unstable
|
||||
cf push getgov-unstable -f ops/manifests/manifest-unstable.yaml
|
||||
cf run-task getgov-unstable --command 'python manage.py migrate' --name migrate
|
||||
```
|
|
@ -1,24 +1,7 @@
|
|||
# Operations
|
||||
# HOWTO Rotate the Application's Secrets
|
||||
========================
|
||||
|
||||
## Authenticating
|
||||
|
||||
You'll need the [Cloud Foundry CLI](https://docs.cloud.gov/getting-started/setup/).
|
||||
|
||||
We use the V7 Cloud Foundry CLI.
|
||||
|
||||
```shell
|
||||
cf login -a api.fr.cloud.gov --sso
|
||||
```
|
||||
|
||||
After authenticating, make sure you are targeting the correct org and space!
|
||||
|
||||
```bash
|
||||
cf spaces
|
||||
cf target -o <ORG> -s <SPACE>
|
||||
```
|
||||
|
||||
## Rotating Environment Secrets
|
||||
Secrets are read from the running environment.
|
||||
|
||||
Secrets were originally created with:
|
||||
|
||||
|
@ -44,16 +27,8 @@ To rotate secrets, create a new `credentials-<ENVIRONMENT>.json` file, upload it
|
|||
Example:
|
||||
|
||||
```bash
|
||||
cf uups getgov-credentials -p credentials-unstable.json
|
||||
cf cups getgov-credentials -p credentials-unstable.json
|
||||
cf restage getgov-unstable --strategy rolling
|
||||
```
|
||||
|
||||
Non-secret environment variables can be declared in `manifest-<ENVIRONMENT>.json` directly.
|
||||
|
||||
## Database
|
||||
|
||||
In sandbox, created with `cf create-service aws-rds micro-psql getgov-database`.
|
||||
|
||||
Binding the database in `manifest-<ENVIRONMENT>.json` automatically inserts the connection string into the environment as `DATABASE_URL`.
|
||||
|
||||
[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).
|
|
@ -0,0 +1,12 @@
|
|||
# HOWTO Rotate Cloud.gov Secrets
|
||||
========================
|
||||
|
||||
These are the secrets GitHub uses to access Cloud.gov during continuous deployment.
|
||||
|
||||
Make sure that you have cf v7 and not cf v8 as it will not work with this script.
|
||||
|
||||
Secrets are set and rotated using the [cloud.gov secret rotation script](../../../ops/scripts/rotate_cloud_secrets.sh).
|
||||
|
||||
Prerequisites for running the script are installations of `jq`, `gh`, and the `cf` CLI tool.
|
||||
|
||||
NOTE: Secrets must be rotated every 90 days. This script can be used for that routine rotation or it can be used to revoke and re-create tokens if they are compromised.
|
8
docs/operations/runbooks/update_python_dependencies.md
Normal file
8
docs/operations/runbooks/update_python_dependencies.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# HOWTO Update Python Dependencies
|
||||
========================
|
||||
|
||||
1. Check the [Pipfile](./src/Pipfile) for pinned dependencies and manually adjust the version numbers
|
||||
1. Run `cd src`, `docker-compose up -d`, and `docker-compose exec app pipenv update` to perform the upgrade and generate a new [Pipfile.lock](./src/Pipfile.lock)
|
||||
1. (optional) Run `docker-compose stop` and `docker-compose build` to build a new image for local development with the updated dependencies.
|
||||
|
||||
The reason for de-coupling the `build` and `update` steps is to increase consistency between builds and reduce "it works on my laptop!". Therefore, `build` uses the lock file as-is; dependencies are never updated except by explicit choice.
|
|
@ -1,17 +0,0 @@
|
|||
# Cloud.gov Continuous Delivery
|
||||
|
||||
We use a [cloud.gov service account](https://cloud.gov/docs/services/cloud-gov-service-account/) to deploy from this repository to cloud.gov with a SpaceDeveloper user.
|
||||
|
||||
## Rotating Cloud.gov Secrets
|
||||
|
||||
Make sure that you have cf v7 and not cf v8 as it will not work with this script.
|
||||
|
||||
Secrets are set and rotated using the [cloud.gov secret rotation script](./scripts/rotate_cloud_secrets.sh).
|
||||
|
||||
Prerequistes for running the script are installations of `jq`, `gh`, and the `cf` CLI tool.
|
||||
|
||||
NOTE: Secrets must be rotated every 90 days. This script can be used for that routine rotation or it can be used to revoke and re-create tokens if they are compromised.
|
||||
|
||||
## Github Action
|
||||
|
||||
TBD info about how we are using the github action to deploy.
|
23
ops/manifests/manifest-staging.yaml
Normal file
23
ops/manifests/manifest-staging.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
applications:
|
||||
- name: getgov-staging
|
||||
buildpacks:
|
||||
- python_buildpack
|
||||
path: ../../src
|
||||
instances: 1
|
||||
memory: 512M
|
||||
stack: cflinuxfs3
|
||||
timeout: 180
|
||||
command: gunicorn registrar.config.wsgi -t 60
|
||||
health-check-type: http
|
||||
health-check-http-endpoint: /health
|
||||
env:
|
||||
# Send stdout and stderr straight to the terminal without buffering
|
||||
PYTHONUNBUFFERED: yup
|
||||
# Tell Django where to find its configuration
|
||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
||||
routes:
|
||||
- route: getgov-staging.app.cloud.gov
|
||||
services:
|
||||
- getgov-credentials
|
||||
- getgov-staging-database
|
|
@ -1,11 +1,16 @@
|
|||
# NOTE: This script does not work with cf v8. We recommend using cf v7 for all cloud.gov commands.
|
||||
if [ ! $(command -v gh) ] || [ ! $(command -v jq) ] || [ ! $(command -v cf) ]; then
|
||||
echo "jq, cf, and gh packages must be installed. Please install via your preferred manager."
|
||||
exit 1
|
||||
echo "jq, cf, and gh packages must be installed. Please install via your preferred manager."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cf spaces
|
||||
read -p "Are you logged in to the dotgov-poc CF space above? (y/n) " -n 1 -r
|
||||
if [ -z "$1" ]; then
|
||||
echo 'Please specify a space to target (i.e. unstable, staging)' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cf target -o cisa-getgov-prototyping -s $1
|
||||
read -p "Are you logged in to the cisa-getgov-prototyping CF org above and targeting the correct space? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
|
@ -13,7 +18,7 @@ then
|
|||
fi
|
||||
|
||||
gh auth status
|
||||
read -p "Are you logged into a Github account with access to cisagov/dotgov? (y/n) " -n 1 -r
|
||||
read -p "Are you logged into a Github account with access to cisagov/getgov? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
|
@ -21,6 +26,7 @@ then
|
|||
fi
|
||||
|
||||
echo "Great, removing and replacing Github CD account..."
|
||||
cf target -s $1
|
||||
cf delete-service-key github-cd-account github-cd-key
|
||||
cf create-service-key github-cd-account github-cd-key
|
||||
cf service-key github-cd-account github-cd-key
|
||||
|
@ -31,8 +37,9 @@ then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
upcase_space=$(printf "%s" "$1" | tr '[:lower:]' '[:upper:]')
|
||||
cf service-key github-cd-account github-cd-key | sed 1,2d | jq -r '[.username, .password]|@tsv' |
|
||||
while read -r username password; do
|
||||
gh secret --repo cisagov/dotgov set CF_USERNAME --body $username
|
||||
gh secret --repo cisagov/dotgov set CF_PASSWORD --body $password
|
||||
gh secret --repo cisagov/getgov set CF_${upcase_space}_USERNAME --body $username
|
||||
gh secret --repo cisagov/getgov set CF_${upcase_space}_PASSWORD --body $password
|
||||
done
|
|
@ -16,3 +16,8 @@ whitenoise = "*"
|
|||
[dev-packages]
|
||||
django-debug-toolbar = "*"
|
||||
nplusone = "*"
|
||||
bandit = "*"
|
||||
black = "*"
|
||||
flake8 = "*"
|
||||
mypy = "*"
|
||||
types-requests = "*"
|
||||
|
|
241
src/Pipfile.lock
generated
241
src/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "6f9b2bb95a6e9e6d1ad667fb9cf5473b37a2a21dbc3821281548f9de29528872"
|
||||
"sha256": "a699f5429c7a64d18840baefdfc9731d7c73d57ed55fd701bcc411efeb6e4035"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -244,6 +244,43 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.5.2"
|
||||
},
|
||||
"bandit": {
|
||||
"hashes": [
|
||||
"sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2",
|
||||
"sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.7.4"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411",
|
||||
"sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c",
|
||||
"sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497",
|
||||
"sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e",
|
||||
"sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342",
|
||||
"sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27",
|
||||
"sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41",
|
||||
"sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab",
|
||||
"sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5",
|
||||
"sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16",
|
||||
"sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e",
|
||||
"sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c",
|
||||
"sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe",
|
||||
"sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3",
|
||||
"sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec",
|
||||
"sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3",
|
||||
"sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd",
|
||||
"sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c",
|
||||
"sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4",
|
||||
"sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90",
|
||||
"sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869",
|
||||
"sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747",
|
||||
"sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==22.8.0"
|
||||
},
|
||||
"blinker": {
|
||||
"hashes": [
|
||||
"sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36",
|
||||
|
@ -252,6 +289,14 @@
|
|||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==1.5"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:a153ffd5143bf26a877bfae2f4ec736ebd8924a46600ca089ad96b54a1d4e28e",
|
||||
|
@ -268,6 +313,74 @@
|
|||
"index": "pypi",
|
||||
"version": "==3.6.0"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db",
|
||||
"sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.0.4"
|
||||
},
|
||||
"gitdb": {
|
||||
"hashes": [
|
||||
"sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd",
|
||||
"sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.0.9"
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
"sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704",
|
||||
"sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.27"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655",
|
||||
"sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9",
|
||||
"sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3",
|
||||
"sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6",
|
||||
"sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0",
|
||||
"sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58",
|
||||
"sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103",
|
||||
"sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09",
|
||||
"sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417",
|
||||
"sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56",
|
||||
"sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2",
|
||||
"sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856",
|
||||
"sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0",
|
||||
"sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8",
|
||||
"sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27",
|
||||
"sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5",
|
||||
"sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71",
|
||||
"sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27",
|
||||
"sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe",
|
||||
"sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca",
|
||||
"sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf",
|
||||
"sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9",
|
||||
"sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.971"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"nplusone": {
|
||||
"hashes": [
|
||||
"sha256:1726c0a10c0aa7eabb04e24db2882ff97b6b7ee29d729a8d97dcbd12ef5a5651",
|
||||
|
@ -276,6 +389,85 @@
|
|||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:01eecd304ba0e6eeed188ae5fa568e99ef10265af7fd9ab737d6412b4ee0ab85",
|
||||
"sha256:aefa80ac32d5bf1f96139dca67cefb69a431beff4e6bf1168468f37d7ab87015"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a",
|
||||
"sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"
|
||||
],
|
||||
"markers": "python_version >= '2.6'",
|
||||
"version": "==5.10.0"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
|
||||
"sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.2"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785",
|
||||
"sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.9.1"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2",
|
||||
"sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
|
||||
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
|
||||
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
|
||||
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
|
||||
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
|
||||
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==6.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
|
@ -284,6 +476,14 @@
|
|||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"smmap": {
|
||||
"hashes": [
|
||||
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
|
||||
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.0.0"
|
||||
},
|
||||
"sqlparse": {
|
||||
"hashes": [
|
||||
"sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae",
|
||||
|
@ -291,6 +491,45 @@
|
|||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
"sha256:87e4d27fe96d0d7e4fc24f0cbe3463baae4ec51e81d95fbe60d2474636e0c7d8",
|
||||
"sha256:f82cc99a1ff552310d19c379827c2c64dd9f85a38bcd5559db2470161867b786"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.0.0"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_full_version < '3.11.0a7'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"types-requests": {
|
||||
"hashes": [
|
||||
"sha256:86cb66d3de2f53eac5c09adc42cf6547eefbd0c7e1210beca1ee751c35d96083",
|
||||
"sha256:feaf581bd580497a47fe845d506fa3b91b484cf706ff27774e87659837de9962"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.9"
|
||||
},
|
||||
"types-urllib3": {
|
||||
"hashes": [
|
||||
"sha256:333e675b188a1c1fd980b4b352f9e40572413a4c1ac689c23cd546e96310070a",
|
||||
"sha256:b78e819f0e350221d0689a5666162e467ba3910737bafda14b5c2c85e9bb1e56"
|
||||
],
|
||||
"version": "==1.26.23"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
|
||||
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,6 @@
|
|||
# Registrar
|
||||
# Registrar source code
|
||||
========================
|
||||
|
||||
## Development
|
||||
[Developer documentation](../docs/developer/README.md) is in [docs/developer](../docs/developer/).
|
||||
|
||||
If you're new to Django, see [Getting Started with Django](https://www.djangoproject.com/start/) for an introduction to the framework.
|
||||
|
||||
### Local Setup
|
||||
|
||||
* Install Docker <https://docs.docker.com/get-docker/>
|
||||
* Initialize the application:
|
||||
|
||||
```shell
|
||||
cd src
|
||||
docker-compose build
|
||||
```
|
||||
* Run the server: `docker-compose up`
|
||||
|
||||
### Update Dependencies
|
||||
|
||||
1. Check the [Pipfile](./src/Pipfile) for pinned dependencies and manually adjust the version numbers
|
||||
1. Run `cd src`, `docker-compose up`, and `docker-compose exec app pipenv update` to perform the upgrade and generate a new [Pipfile.lock](./src/Pipfile.lock)
|
||||
[Operations documentation](../docs/operations/README.md) is in [docs/operations](../docs/operations/).
|
|
@ -28,9 +28,11 @@ services:
|
|||
stdin_open: true
|
||||
tty: true
|
||||
ports:
|
||||
- "8080:8000"
|
||||
- "8080:8080"
|
||||
# command: "python"
|
||||
command: "python manage.py runserver 0.0.0.0:8000"
|
||||
command: >
|
||||
bash -c " python manage.py migrate &&
|
||||
python manage.py runserver 0.0.0.0:8080"
|
||||
|
||||
db:
|
||||
image: postgres:latest
|
||||
|
|
|
@ -78,6 +78,8 @@ INSTALLED_APPS = [
|
|||
# (and any other places you specify) into a single location
|
||||
# that can easily be served in production
|
||||
"django.contrib.staticfiles",
|
||||
# let's be sure to install our own application!
|
||||
"registrar",
|
||||
]
|
||||
|
||||
# Middleware are routines for processing web requests.
|
||||
|
@ -392,6 +394,7 @@ SECURE_SSL_REDIRECT = True
|
|||
# web server configurations.
|
||||
ALLOWED_HOSTS = [
|
||||
"getgov-unstable.app.cloud.gov",
|
||||
"getgov-staging.app.cloud.gov",
|
||||
"get.gov",
|
||||
]
|
||||
|
||||
|
|
0
src/registrar/management/__init__.py
Normal file
0
src/registrar/management/__init__.py
Normal file
0
src/registrar/management/commands/__init__.py
Normal file
0
src/registrar/management/commands/__init__.py
Normal file
63
src/registrar/management/commands/lint.py
Normal file
63
src/registrar/management/commands/lint.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
from subprocess import run, CalledProcessError # nosec
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Helper command for running installed linters.
|
||||
|
||||
Run using `./manage.py lint`.
|
||||
"""
|
||||
|
||||
help = "Runs linters: flake8 black mypy bandit"
|
||||
|
||||
# Add new linters here.
|
||||
#
|
||||
# To maintain security while using subprocess, avoid passing user input
|
||||
# and, in all cases, make sure to use a list (not a string) for flags/args
|
||||
# as this will quote the output.
|
||||
linters = {
|
||||
"flake8": {
|
||||
"purpose": "Linting",
|
||||
"args": ["flake8", ".", "--count", "--show-source", "--statistics"],
|
||||
},
|
||||
"black": {
|
||||
"purpose": "Formatting",
|
||||
"args": ["black", "--check", "."],
|
||||
},
|
||||
"mypy": {
|
||||
"purpose": "Type checking",
|
||||
"args": ["mypy", "."],
|
||||
},
|
||||
"bandit": {
|
||||
"purpose": "Security scanning",
|
||||
"args": ["bandit", "-r", "."],
|
||||
},
|
||||
}
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("linters", nargs="*", type=str)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
for linter in self.linters.values():
|
||||
self.stdout.write(f"[manage.py lint] {linter['purpose']}. . .")
|
||||
result = run(linter["args"])
|
||||
if result.returncode:
|
||||
self.stderr.write(
|
||||
self.style.NOTICE(
|
||||
"[manage.py lint] Re-try with: [docker-compose exec app] "
|
||||
f"{' '.join(linter['args'])}"
|
||||
)
|
||||
)
|
||||
break
|
||||
else:
|
||||
self.stdout.write(
|
||||
f"[manage.py lint] {linter['purpose']} completed with success!"
|
||||
)
|
||||
except CalledProcessError as e:
|
||||
raise CommandError(e)
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS("[manage.py lint] All linters ran successfully.")
|
||||
)
|
0
src/registrar/tests/__init__.py
Normal file
0
src/registrar/tests/__init__.py
Normal file
0
src/registrar/tests/common.py
Normal file
0
src/registrar/tests/common.py
Normal file
11
src/registrar/tests/test_views.py
Normal file
11
src/registrar/tests/test_views.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django.test import Client, TestCase
|
||||
|
||||
|
||||
class HealthTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
def test_health_check_endpoint(self):
|
||||
response = self.client.get("/health/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"OK")
|
Loading…
Add table
Add a link
Reference in a new issue