From 3537795e700ce509028b3e2b6e7463f03af42427 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Tue, 5 Sep 2023 15:22:17 +0200 Subject: [PATCH 01/30] feat: add integration tests --- .github/workflows/deploy.yaml | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..eaf8f64 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,78 @@ +name: deploy and integration test + +permissions: + id-token: write # required for requesting the JWT + contents: read # required for actions/checkout + +on: + workflow_dispatch: + +jobs: + deploy-and-integration-test: + runs-on: ubuntu-latest + + steps: + + - name: Checkout repository with template stack + uses: actions/checkout@v3 + with: + repository: developmentseed/eoapi-template + path: eoapi-template + + - name: Set up python + uses: actions/setup-python@v2 + with: + python-version: 3.10 + + - name: Install dependencies to deploy the stack + run: | + cd eoapi-template + python -m venv .deployment_venv + source .deployment_venv/bin/activate + pip install -r requirements.txt + npm install -g aws-cdk + deactivate + cd .. + + - name: Deploy the stack + env: + AWS_DEFAULT_REGION: us-east-1 + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + cd eoapi-template + source .deployment_venv/bin/activate + cdk deploy --all --require-approval never + echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'stacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'titiler')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + deactivate + cd .. + + - name: Checkout the repo with the tests + uses: actions/checkout@v3 + with: + repository: developmentseed/eoapi-tests + path: tests + + - name: Test the stack + env: + ingestor_url: ${{ steps.deploy_the_stack.outputs.ingestor_url }} + stac_api_url: ${{ steps.deploy_the_stack.outputs.stac_api_url }} + titiler_pgstac_api_url: ${{ steps.deploy_the_stack.outputs.titiler_api_url }} + run: | + cd tests + python -m venv .tests_venv + source .tests_venv/bin/activate + pip install -e tests + pytest eoapi-tests + deactivate + cd .. + + - name: Tear down the stack + run: | + cd eoapi-template + source .deployment_venv/bin/activate + cdk destroy --all --require-approval never + deactivate + cd .. From f1c1d1f7ae9f25db62b9edae457d3f07bf29b8b4 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 6 Sep 2023 18:12:47 +0900 Subject: [PATCH 02/30] add eoapi-cdk upgrade command and pip cache --- .github/workflows/deploy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index eaf8f64..f376e5e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -23,6 +23,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.10 + cache: pip - name: Install dependencies to deploy the stack run: | @@ -30,6 +31,7 @@ jobs: python -m venv .deployment_venv source .deployment_venv/bin/activate pip install -r requirements.txt + pip install --upgrade eoapi-cdk npm install -g aws-cdk deactivate cd .. From aab0acb1d55a953f3aec9ab2f7a30817aeee144a Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 6 Sep 2023 18:13:38 +0900 Subject: [PATCH 03/30] test commit, to revert --- lib/stac-api/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index a6e5212..25d62e4 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -5,6 +5,7 @@ import { aws_lambda as lambda, aws_secretsmanager as secretsmanager, CfnOutput, + DockerImage, } from "aws-cdk-lib"; import { PythonFunction, @@ -52,6 +53,9 @@ export class PgStacApiLambda extends Construct { vpcSubnets: props.subnetSelection, allowPublicSubnet: true, memorySize: 8192, + bundling: { + image: DockerImage.fromBuild(__dirname) + } }); props.dbSecret.grantRead(this.stacApiLambdaFunction); From d9749deb43359fb01b697e1b69c46c6eedc79edf Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 6 Sep 2023 18:23:51 +0900 Subject: [PATCH 04/30] Revert "test commit, to revert" This reverts commit aab0acb1d55a953f3aec9ab2f7a30817aeee144a. --- lib/stac-api/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index 25d62e4..a6e5212 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -5,7 +5,6 @@ import { aws_lambda as lambda, aws_secretsmanager as secretsmanager, CfnOutput, - DockerImage, } from "aws-cdk-lib"; import { PythonFunction, @@ -53,9 +52,6 @@ export class PgStacApiLambda extends Construct { vpcSubnets: props.subnetSelection, allowPublicSubnet: true, memorySize: 8192, - bundling: { - image: DockerImage.fromBuild(__dirname) - } }); props.dbSecret.grantRead(this.stacApiLambdaFunction); From 1300c4ede3f0c8b32559da0d5a3bb9893bb7b6b1 Mon Sep 17 00:00:00 2001 From: emileten Date: Mon, 11 Sep 2023 12:21:51 +0900 Subject: [PATCH 05/30] update workflow --- .github/workflows/deploy.yaml | 65 ++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index f376e5e..ab4aa45 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -5,11 +5,20 @@ permissions: contents: read # required for actions/checkout on: - workflow_dispatch: + + push: + branches: + - "feat/add-integration-tests" + jobs: deploy-and-integration-test: runs-on: ubuntu-latest + env: + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} + steps: @@ -22,59 +31,61 @@ jobs: - name: Set up python uses: actions/setup-python@v2 with: - python-version: 3.10 cache: pip - - - name: Install dependencies to deploy the stack + + - name: Install deployment environment + working-directory: eoapi-template run: | - cd eoapi-template python -m venv .deployment_venv source .deployment_venv/bin/activate pip install -r requirements.txt pip install --upgrade eoapi-cdk npm install -g aws-cdk deactivate - cd .. + + - name: Synthesize the stack + working-directory: eoapi-template + run: | + source .deployment_venv/bin/activate + cdk synth --debug --all --require-approval never + deactivate + - name: Deploy the stack - env: - AWS_DEFAULT_REGION: us-east-1 - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + id: deploy_step + working-directory: eoapi-template run: | - cd eoapi-template source .deployment_venv/bin/activate - cdk deploy --all --require-approval never - echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'stacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?contains(OutputKey, 'titiler')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + cdk deploy --ci --all --require-approval never + echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT deactivate - cd .. - name: Checkout the repo with the tests uses: actions/checkout@v3 with: repository: developmentseed/eoapi-tests + ref: initial-PR path: tests - name: Test the stack + working-directory: tests env: - ingestor_url: ${{ steps.deploy_the_stack.outputs.ingestor_url }} - stac_api_url: ${{ steps.deploy_the_stack.outputs.stac_api_url }} - titiler_pgstac_api_url: ${{ steps.deploy_the_stack.outputs.titiler_api_url }} + ingestor_url: ${{ steps.deploy_step.outputs.ingestor_url }} + stac_api_url: ${{ steps.deploy_step.outputs.stac_api_url }} + titiler_pgstac_api_url: ${{ steps.deploy_step.outputs.titiler_pgstac_api_url }} run: | - cd tests python -m venv .tests_venv source .tests_venv/bin/activate - pip install -e tests - pytest eoapi-tests + pip install -e . + pytest eoapi_tests deactivate - cd .. - - name: Tear down the stack + - name: Always tear down the stack + if: always() + working-directory: eoapi-template run: | - cd eoapi-template source .deployment_venv/bin/activate - cdk destroy --all --require-approval never + cdk destroy --ci --all --force deactivate - cd .. From c47f6d66136c9dea2dfdfd84a2a54c59efa9566c Mon Sep 17 00:00:00 2001 From: emileten Date: Mon, 11 Sep 2023 17:45:00 +0900 Subject: [PATCH 06/30] update workflow to run when release happens, move tests here --- .github/workflows/deploy.yaml | 32 ++-- .gitignore | 3 + README.md | 3 + tests/LICENSE | 21 +++ tests/README.md | 16 ++ tests/eoapi_tests/conftest.py | 22 +++ .../eoapi_tests/fixtures/test_collection.json | 45 +++++ tests/eoapi_tests/fixtures/test_item.json | 12 ++ .../fixtures/test_titiler_search_request.json | 3 + tests/eoapi_tests/ingestion.py | 166 ++++++++++++++++++ tests/eoapi_tests/settings.py | 12 ++ tests/eoapi_tests/test_stac_ingestion.py | 123 +++++++++++++ tests/pyproject.toml | 21 +++ 13 files changed, 465 insertions(+), 14 deletions(-) create mode 100644 tests/LICENSE create mode 100644 tests/README.md create mode 100644 tests/eoapi_tests/conftest.py create mode 100644 tests/eoapi_tests/fixtures/test_collection.json create mode 100644 tests/eoapi_tests/fixtures/test_item.json create mode 100644 tests/eoapi_tests/fixtures/test_titiler_search_request.json create mode 100644 tests/eoapi_tests/ingestion.py create mode 100644 tests/eoapi_tests/settings.py create mode 100644 tests/eoapi_tests/test_stac_ingestion.py create mode 100644 tests/pyproject.toml diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ab4aa45..1fdc6d7 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,18 +1,17 @@ -name: deploy and integration test +name: Deploy & Test Deployment permissions: id-token: write # required for requesting the JWT contents: read # required for actions/checkout on: - - push: - branches: - - "feat/add-integration-tests" + workflow_run: + workflows: [Distribute] + types: [completed] - jobs: - deploy-and-integration-test: + on-successful-release: + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest env: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} @@ -62,23 +61,28 @@ jobs: echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT deactivate - - name: Checkout the repo with the tests + - name: Checkout eoapi-cdk repository uses: actions/checkout@v3 with: - repository: developmentseed/eoapi-tests - ref: initial-PR - path: tests + repository: developmentseed/eoapi-cdk + path: eoapi-cdk + + - name: Install test environment + working-directory: eoapi-cdk/tests + run: | + python -m venv .tests_venv + source .tests_venv/bin/activate + pip install -r requirements.txt + deactivate - name: Test the stack - working-directory: tests + working-directory: eoapi-cdk/tests env: ingestor_url: ${{ steps.deploy_step.outputs.ingestor_url }} stac_api_url: ${{ steps.deploy_step.outputs.stac_api_url }} titiler_pgstac_api_url: ${{ steps.deploy_step.outputs.titiler_pgstac_api_url }} run: | - python -m venv .tests_venv source .tests_venv/bin/activate - pip install -e . pytest eoapi_tests deactivate diff --git a/.gitignore b/.gitignore index da73da4..0ce8378 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ docs __pycache__ .venv .tox +tests/*.egg* +tests/*venv* +tests/__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index 62ad4ad..7dbc51a 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,6 @@ Versioning is automatically handled via [Conventional Commits](https://www.conve _Warning_: If you rebase `main`, you must ensure that the commits referenced by tags point to commits that are within the `main` branch. If a commit references a commit that is no longer on the `main` branch, Semantic Release will fail to detect the correct version of the project. [More information](https://github.com/semantic-release/semantic-release/issues/1121#issuecomment-517945233). +## Tests + +Each new release triggers an integration test against a running deployment that uses the newly releases constructs. See the corresponding [github workflow](https://github.com/developmentseed/eoapi-cdk/blob/main/.github/workflows/deploy.yaml) and the [tests definition](https://github.com/developmentseed/eoapi-cdk/blob/main/tests). \ No newline at end of file diff --git a/tests/LICENSE b/tests/LICENSE new file mode 100644 index 0000000..d24c62b --- /dev/null +++ b/tests/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Development Seed + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..0dcdaf5 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,16 @@ +# Integration tests + +Standard integration tests for a suite of deployed [eoAPI](https://github.com/developmentseed/eoAPI) services. + +## Environment + +See `eoapi_tests/settings.py` that defines the required and optional environment variables. + +## Installation & Usage + +``` +python -m venv .testing_environment +source .testing_environment/bin/activate +pip install -e . +pytest eoapi_tests +``` \ No newline at end of file diff --git a/tests/eoapi_tests/conftest.py b/tests/eoapi_tests/conftest.py new file mode 100644 index 0000000..ebf9e48 --- /dev/null +++ b/tests/eoapi_tests/conftest.py @@ -0,0 +1,22 @@ +import pytest +from ingestion import StacIngestion + + +@pytest.fixture(scope="module") +def stac_ingestion_instance(): + return StacIngestion() + + +@pytest.fixture(scope="module") +def test_collection(stac_ingestion_instance): + return stac_ingestion_instance.get_test_collection() + + +@pytest.fixture(scope="module") +def test_item(stac_ingestion_instance): + return stac_ingestion_instance.get_test_item() + + +@pytest.fixture(scope="module") +def test_titiler_search_request(stac_ingestion_instance): + return stac_ingestion_instance.get_test_titiler_search_request() diff --git a/tests/eoapi_tests/fixtures/test_collection.json b/tests/eoapi_tests/fixtures/test_collection.json new file mode 100644 index 0000000..4d34153 --- /dev/null +++ b/tests/eoapi_tests/fixtures/test_collection.json @@ -0,0 +1,45 @@ +{ + "id":"test_collection", + "type":"Collection", + "links": [], + "title":"Test Collection", + "extent": { + "spatial": { + "bbox": [ + [ + -180, 51.6, 180, 78 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2019-01-01T00:00:00.000Z", + "2021-01-01T00:00:00.000Z" + ] + ] + } + }, + "license":"CC-BY", + "description":"Test collection", + "item_assets": { + "tif": { + "type":"image/tiff; application=geotiff; profile=cloud-optimized", + "roles": [ + "data", + "layer" + ], + "title":"Test collection", + "description":"Test collection" + }, + "csv": { + "type":"text/csv", + "roles": [ + "data" + ], + "title":"CSV", + "description":"Test collection" + } + }, + "stac_version":"1.0.0" +} \ No newline at end of file diff --git a/tests/eoapi_tests/fixtures/test_item.json b/tests/eoapi_tests/fixtures/test_item.json new file mode 100644 index 0000000..bff8ea9 --- /dev/null +++ b/tests/eoapi_tests/fixtures/test_item.json @@ -0,0 +1,12 @@ +{ + "type": "Feature", + "id": "test_item", + "stac_version":"1.0.0", + "collection": "test_collection", + "links":[{"rel":"collection","type":"application/json","href":"https://stac.dit.maap-project.org/collections/test_collection"},{"rel":"parent","type":"application/json","href":"https://stac.dit.maap-project.org/collections/test_collection"},{"rel":"root","type":"application/json","href":"https://stac.dit.maap-project.org/"},{"rel":"self","type":"application/geo+json","href":"https://stac.dit.maap-project.org/collections/test_collection/items/test_item1"}], + "bbox":[-78.40290984426046,51.07724585591961,-77.04127077089376,51.92130718597872], + "assets":{"csv":{"href":"s3://nasa-maap-data-store/file-staging/nasa-map/icesat2-boreal/boreal_agb_202302031675450345_0177_train_data.csv","type":"text/csv","roles":["data"],"title":"CSV","description":"CSV of training data"},"tif":{"href":"s3://nasa-maap-data-store/file-staging/nasa-map/icesat2-boreal/boreal_agb_202302031675450345_0177.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized","roles":["data"],"title":"Cloud Optimized GeoTIFF of boreal data","description":"Cloud Optimized GeoTIFF of boreal data","raster:bands":[{"scale":1.0,"nodata":-9999.0,"offset":0.0,"sampling":"area","data_type":"float32","histogram":{"max":105.78067016601562,"min":7.345508575439453,"count":11,"buckets":[2194,2380,972,469,298,184,85,30,8,4]},"statistics":{"mean":25.281058933423914,"stddev":13.868902983070951,"maximum":105.78067016601562,"minimum":7.345508575439453,"valid_percent":0.6317138671875}},{"scale":1.0,"nodata":-9999.0,"offset":0.0,"sampling":"area","data_type":"float32","histogram":{"max":60.75077819824219,"min":1.4587666988372803,"count":11,"buckets":[5140,1167,250,42,14,4,3,1,2,1]},"statistics":{"mean":5.982097533589976,"stddev":3.7930746502586974,"maximum":60.75077819824219,"minimum":1.4587666988372803,"valid_percent":0.6317138671875}}]}}, + "properties":{"datetime":"2023-02-15T00:00:00+00:00","proj:bbox":[4598521.999999994,5643304.000000009,4688521.999999994,5733304.000000009],"proj:shape":[3000,3000],"proj:geometry":{"type":"Polygon","coordinates":[[[4598521.999999994,5643304.000000009],[4688521.999999994,5643304.000000009],[4688521.999999994,5733304.000000009],[4598521.999999994,5733304.000000009],[4598521.999999994,5643304.000000009]]]},"proj:transform":[30.0,0.0,4598521.999999994,0.0,-30.0,5733304.000000009,0.0,0.0,1.0]}, + "stac_extensions":["https://stac-extensions.github.io/projection/v1.1.0/schema.json","https://stac-extensions.github.io/raster/v1.1.0/schema.json"], + "geometry":{"type":"Polygon","coordinates":[[[14.114837413118664,67.218607039971],[12.438229073696998,67.70894310918132],[11.17860397724852,67.06631312684836],[12.837754767891637,66.58867761064732],[14.114837413118664,67.218607039971]]]} +} \ No newline at end of file diff --git a/tests/eoapi_tests/fixtures/test_titiler_search_request.json b/tests/eoapi_tests/fixtures/test_titiler_search_request.json new file mode 100644 index 0000000..62aa226 --- /dev/null +++ b/tests/eoapi_tests/fixtures/test_titiler_search_request.json @@ -0,0 +1,3 @@ +{ + "collections": ["test_collection"] +} \ No newline at end of file diff --git a/tests/eoapi_tests/ingestion.py b/tests/eoapi_tests/ingestion.py new file mode 100644 index 0000000..62f3bc9 --- /dev/null +++ b/tests/eoapi_tests/ingestion.py @@ -0,0 +1,166 @@ +import json +import os + +import boto3 +import pystac +import requests +from pystac import STACValidationError +from settings import eoapiDeploymentSettings + + +class StacIngestion: + + """Class representing various test operations""" + + def __init__(self): + self.eoapi_deployment_settings = eoapiDeploymentSettings() + self.current_file_path = os.path.dirname(os.path.abspath(__file__)) + self.headers = self.get_headers() + + def validate_collection(self, collection): + try: + pystac.validation.validate_dict(collection) + except STACValidationError: + raise STACValidationError("Validation failed for the collection") + + def validate_item(self, item): + try: + pystac.validation.validate_dict(item) + except STACValidationError: + raise STACValidationError("Validation failed for the item") + + def get_authentication_token(self) -> str: + if not self.eoapi_deployment_settings.secret_id: + raise ValueError("You should provide a secret id") + + client = boto3.client("secretsmanager", region_name="us-west-2") + + try: + res_secret = client.get_secret_value( + SecretId=self.eoapi_deployment_settings.secret_id + ) + except client.exceptions.ResourceNotFoundException: + raise Exception( + "Unable to find a secret for " + "{self.eoapi_deployment_settings.secret_id}. " + "\n\nHint: Check your stage and service id." + "Also, verify that the correct " + "AWS_PROFILE is set on your environment." + ) + + # Authentication - Get TOKEN + secret = json.loads(res_secret["SecretString"]) + client_secret = secret["client_secret"] + client_id = secret["client_id"] + cognito_domain = secret["cognito_domain"] + scope = secret["scope"] + + res_token = requests.post( + f"{cognito_domain}/oauth2/token", + headers={ + "Content-Type": "application/x-www-form-urlencoded", + }, + auth=(client_id, client_secret), + data={ + "grant_type": "client_credentials", + # A space-separated list of scopes + # to request for the generated access token. + "scope": scope, + }, + ) + + token = res_token.json()["access_token"] + return token + + def get_headers(self) -> dict: + if self.eoapi_deployment_settings.secret_id: + return { + "headers": { + "Authorization": f"bearer {self.get_authentication_token()}" + } + } + else: + return {"params": {"provided_by": "eoapi-tests"}} + + def insert_collection(self, collection): + response = requests.post( + self.eoapi_deployment_settings.ingestor_url + + self.eoapi_deployment_settings.collections_endpoint, + json=collection, + **self.headers, + ) + return response + + def insert_item(self, item): + response = requests.post( + self.eoapi_deployment_settings.ingestor_url + + self.eoapi_deployment_settings.items_endpoint, + json=item, + **self.headers, + ) + return response + + def query_collection(self, collection_id): + response = requests.get( + self.eoapi_deployment_settings.stac_api_url + + self.eoapi_deployment_settings.collections_endpoint + + f"/{collection_id}" + ) + return response + + def query_items(self, collection_id): + response = requests.get( + self.eoapi_deployment_settings.stac_api_url + + self.eoapi_deployment_settings.collections_endpoint + + f"/{collection_id}/items" + ) + return response + + def register_mosaic(self, search_request): + response = requests.post( + self.eoapi_deployment_settings.titiler_pgstac_api_url + "/mosaic/register", + json=search_request, + ) + return response + + def list_mosaic_assets(self, search_id): + """list the assets of the first tile""" + response = requests.get( + self.eoapi_deployment_settings.titiler_pgstac_api_url + + f"/mosaic/{search_id}/tiles/0/0/0/assets" + ) + return response + + def get_test_collection(self): + with open( + os.path.join(self.current_file_path, "fixtures", "test_collection.json"), + "r", + ) as f: + test_collection = json.load(f) + return test_collection + + def get_test_item(self): + with open( + os.path.join(self.current_file_path, "fixtures", "test_item.json"), "r" + ) as f: + test_item = json.load(f) + return test_item + + def get_test_titiler_search_request(self): + with open( + os.path.join( + self.current_file_path, "fixtures", "test_titiler_search_request.json" + ), + "r", + ) as f: + test_titiler_search_request = json.load(f) + return test_titiler_search_request + + def delete_collection(self, collection_id): + response = requests.delete( + self.eoapi_deployment_settings.ingestor_url + + self.eoapi_deployment_settings.collections_endpoint + + f"/{collection_id}", + **self.headers, + ) + return response diff --git a/tests/eoapi_tests/settings.py b/tests/eoapi_tests/settings.py new file mode 100644 index 0000000..de5d60e --- /dev/null +++ b/tests/eoapi_tests/settings.py @@ -0,0 +1,12 @@ +from typing import Optional + +from pydantic_settings import BaseSettings + + +class eoapiDeploymentSettings(BaseSettings): + ingestor_url: str + stac_api_url: str + titiler_pgstac_api_url: str + secret_id: Optional[str] = None + collections_endpoint: Optional[str] = "/collections" + items_endpoint: Optional[str] = "/ingestions" diff --git a/tests/eoapi_tests/test_stac_ingestion.py b/tests/eoapi_tests/test_stac_ingestion.py new file mode 100644 index 0000000..d09344a --- /dev/null +++ b/tests/eoapi_tests/test_stac_ingestion.py @@ -0,0 +1,123 @@ +import time + +import pystac +import pytest + +INSERTION_WAIT_TIME = 10 + +# Test validating the collection +pytest.mark.order(0) + + +def test_validate_collection(test_collection): + pystac.validation.validate_dict(test_collection) + + +# Test validating the item +pytest.mark.order(1) + + +def test_validate_item(test_item): + pystac.validation.validate_dict(test_item) + + +# Test inserting collection +pytest.mark.order(2) + + +def test_insert_collection(stac_ingestion_instance, test_collection): + response = stac_ingestion_instance.insert_collection(test_collection) + assert response.status_code in [ + 200, + 201, + ], f"Failed to insert the test_collection :\n{response.text}" + # Wait for the collection to be inserted + time.sleep(INSERTION_WAIT_TIME) + + +# Test inserting item +pytest.mark.order(3) + + +def test_insert_item(stac_ingestion_instance, test_item): + response = stac_ingestion_instance.insert_item(test_item) + assert response.status_code in [ + 200, + 201, + ], f"Failed to insert the test_item :\n{response.text}" + # Wait for the item to be inserted + time.sleep(INSERTION_WAIT_TIME) + + +# Test querying collection and verifying inserted collection +pytest.mark.order(4) + + +def test_query_collection(stac_ingestion_instance, test_collection): + response = stac_ingestion_instance.query_collection(test_collection["id"]) + assert response.status_code in [ + 200, + 201, + ], f"Failed to query the test_collection :\n{response.text}" + + +# Test registering a mosaic and querying its assets +pytest.mark.order(5) + + +def test_titiler_pgstac( + stac_ingestion_instance, test_titiler_search_request, test_item +): + register_response = stac_ingestion_instance.register_mosaic( + test_titiler_search_request + ) + assert register_response.status_code in [ + 200, + 201, + ], f"Failed to register the mosaic :\n{register_response.text}" + search_id = register_response.json()["searchid"] + # allow for some time for the mosaic to be inserted + time.sleep(INSERTION_WAIT_TIME) + asset_query_response = stac_ingestion_instance.list_mosaic_assets(search_id) + assert asset_query_response.status_code in [ + 200, + 201, + ], "Failed to query the mosaic's assets" + "for mosaic {search_id} :\n{asset_query_response.text}" + assets_json = asset_query_response.json() + # expects a single item in the collection + assert len(assets_json) == 1 + assert all([k in assets_json[0]["assets"] for k in test_item["assets"].keys()]) + + +# Test querying items and verifying inserted items +pytest.mark.order(6) + + +def test_query_items(stac_ingestion_instance, test_collection, test_item): + response = stac_ingestion_instance.query_items(test_collection["id"]) + assert response.status_code in [ + 200, + 201, + ], f"Failed to query the items :\n{response.text}" + item = response.json()["features"][0] + assert ( + item["id"] == test_item["id"] + ), f"Inserted item - {test_item} \n not found in the queried items {item}" + + +# Test querying collection and verifying inserted collection +pytest.mark.order(7) + + +def test_delete_collection(stac_ingestion_instance, test_collection): + response = stac_ingestion_instance.delete_collection(test_collection["id"]) + assert response.status_code in [ + 200, + 201, + ], f"Failed to delete the test_collection :\n{response.text}" + + +# Run the tests +if __name__ == "__main__": + pytest.main() diff --git a/tests/pyproject.toml b/tests/pyproject.toml new file mode 100644 index 0000000..06213cc --- /dev/null +++ b/tests/pyproject.toml @@ -0,0 +1,21 @@ +[build-system] +requires = ["setuptools", "wheel"] + +[project] +name = "eoapi_tests" +version = "0.1.0" +description = "test suite for eoAPI deployments" +authors = [ + {name = "Emile Tenezakis", email = "emile@developmentseed.org"} +] +license = {file = "LICENSE"} + +dependencies = [ + "pytest==7.4.0", + "boto3==1.28.39", + "pystac==1.8.3", + "pystac[validation]==1.8.3", + "requests==2.31.0", + "pydantic-settings==2.0.3", + "pytest-order==1.1.0" +] \ No newline at end of file From a63c9097aef59484cebd5ebebe8508fe94d2c8e8 Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 21 Sep 2023 17:13:19 +0900 Subject: [PATCH 07/30] instead of cloning the eoapi-template repo, copy the code here so that we dont depend on an external repository. Modify accordingly the tests folder, with two subfolders (tests and cdk code) and the top folder renamed to integration_tests --- .github/workflows/deploy.yaml | 40 +-- integration_tests/cdk/README.md | 49 +++ integration_tests/cdk/app.py | 17 ++ integration_tests/cdk/cdk.json | 32 ++ integration_tests/cdk/config.py | 181 +++++++++++ .../cdk/eoapi_template/__init__.py | 0 .../cdk/eoapi_template/pgStacInfra.py | 287 ++++++++++++++++++ integration_tests/cdk/eoapi_template/vpc.py | 55 ++++ integration_tests/cdk/package-lock.json | 42 +++ integration_tests/cdk/package.json | 8 + integration_tests/cdk/requirements.txt | 16 + {tests => integration_tests/tests}/README.md | 0 .../tests}/eoapi_tests/conftest.py | 0 .../eoapi_tests/fixtures/test_collection.json | 0 .../eoapi_tests/fixtures/test_item.json | 0 .../fixtures/test_titiler_search_request.json | 0 .../tests}/eoapi_tests/ingestion.py | 0 .../tests}/eoapi_tests/settings.py | 0 .../tests}/eoapi_tests/test_stac_ingestion.py | 0 .../tests}/pyproject.toml | 0 tests/LICENSE | 21 -- 21 files changed, 707 insertions(+), 41 deletions(-) create mode 100644 integration_tests/cdk/README.md create mode 100644 integration_tests/cdk/app.py create mode 100644 integration_tests/cdk/cdk.json create mode 100644 integration_tests/cdk/config.py create mode 100644 integration_tests/cdk/eoapi_template/__init__.py create mode 100644 integration_tests/cdk/eoapi_template/pgStacInfra.py create mode 100644 integration_tests/cdk/eoapi_template/vpc.py create mode 100644 integration_tests/cdk/package-lock.json create mode 100644 integration_tests/cdk/package.json create mode 100644 integration_tests/cdk/requirements.txt rename {tests => integration_tests/tests}/README.md (100%) rename {tests => integration_tests/tests}/eoapi_tests/conftest.py (100%) rename {tests => integration_tests/tests}/eoapi_tests/fixtures/test_collection.json (100%) rename {tests => integration_tests/tests}/eoapi_tests/fixtures/test_item.json (100%) rename {tests => integration_tests/tests}/eoapi_tests/fixtures/test_titiler_search_request.json (100%) rename {tests => integration_tests/tests}/eoapi_tests/ingestion.py (100%) rename {tests => integration_tests/tests}/eoapi_tests/settings.py (100%) rename {tests => integration_tests/tests}/eoapi_tests/test_stac_ingestion.py (100%) rename {tests => integration_tests/tests}/pyproject.toml (100%) delete mode 100644 tests/LICENSE diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1fdc6d7..a91573c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -5,11 +5,15 @@ permissions: contents: read # required for actions/checkout on: + # run this workflow when the `Distribute` workflow completes workflow_run: workflows: [Distribute] types: [completed] + # ... or manually. + workflow_dispatch: jobs: + # run a job on a successful `Workflow` run. on-successful-release: if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest @@ -21,38 +25,40 @@ jobs: steps: - - name: Checkout repository with template stack + - name: Checkout repository # for runs after `Distribute`, pulling from main will always give the latest eoapi-cdk. uses: actions/checkout@v3 - with: - repository: developmentseed/eoapi-template - path: eoapi-template - name: Set up python uses: actions/setup-python@v2 with: cache: pip - + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install deployment environment - working-directory: eoapi-template + working-directory: integration_tests/cdk run: | python -m venv .deployment_venv source .deployment_venv/bin/activate pip install -r requirements.txt - pip install --upgrade eoapi-cdk - npm install -g aws-cdk + pip install --upgrade eoapi-cdk # make sure to take the latest version of eoapi-cdk since we're integration-testing the newest release. + npm install deactivate - name: Synthesize the stack - working-directory: eoapi-template + working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate cdk synth --debug --all --require-approval never deactivate - + # deploys and grabs URLs from the output for later tests - name: Deploy the stack id: deploy_step - working-directory: eoapi-template + working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate cdk deploy --ci --all --require-approval never @@ -61,14 +67,8 @@ jobs: echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT deactivate - - name: Checkout eoapi-cdk repository - uses: actions/checkout@v3 - with: - repository: developmentseed/eoapi-cdk - path: eoapi-cdk - - name: Install test environment - working-directory: eoapi-cdk/tests + working-directory: integration_tests/tests run: | python -m venv .tests_venv source .tests_venv/bin/activate @@ -76,7 +76,7 @@ jobs: deactivate - name: Test the stack - working-directory: eoapi-cdk/tests + working-directory: integration_tests/tests env: ingestor_url: ${{ steps.deploy_step.outputs.ingestor_url }} stac_api_url: ${{ steps.deploy_step.outputs.stac_api_url }} @@ -88,7 +88,7 @@ jobs: - name: Always tear down the stack if: always() - working-directory: eoapi-template + working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate cdk destroy --ci --all --force diff --git a/integration_tests/cdk/README.md b/integration_tests/cdk/README.md new file mode 100644 index 0000000..1c7ad49 --- /dev/null +++ b/integration_tests/cdk/README.md @@ -0,0 +1,49 @@ +This is a non-forked version of [eoapi-template](https://github.com/developmentseed/eoapi-template). + +# Deployment CDK code for eoapi-cdk integration tests + +This is a wrapper CDK code that provides the `eoapi-cdk` deployment to run integration tests on the latest releases of the `eoapi-cdk` constructs. + +## Requirements + +- python +- docker +- node +- AWS credentials environment variables configured to point to an account. +- **Optional** a `config.yaml` file to override the default deployment settings defined in `config.py`. + +## Installation + +Install python dependencies with + +``` +python -m venv .venv +source .venv/bin/activate +python -m pip install -r requirements.txt +``` + +Note that `eoapi-cdk` isn't pinned, so that the latest version is always installed. Also install node dependencies with + +``` +npm install +``` + +Verify that the `cdk` CLI is available. Since `aws-cdk` is installed as a local dependency, you can use the `npx` node package runner tool, that comes with `npm`. + +``` +npx cdk --version +``` + +## Deployment + +First, synthesize the app + +``` +npx cdk synth --all +``` + +Then, deploy + +``` +npx cdk deploy --all --require-approval never +``` \ No newline at end of file diff --git a/integration_tests/cdk/app.py b/integration_tests/cdk/app.py new file mode 100644 index 0000000..f86c201 --- /dev/null +++ b/integration_tests/cdk/app.py @@ -0,0 +1,17 @@ +from aws_cdk import App + +from config import build_app_config +from eoapi_template import pgStacInfra, vpc + +app = App() + +app_config = build_app_config() + +vpc_stack = vpc.VpcStack(scope=app, app_config=app_config) + +pgstac_infra_stack = pgStacInfra.pgStacInfraStack( + scope=app, + vpc=vpc_stack.vpc, + app_config=app_config, +) +app.synth() diff --git a/integration_tests/cdk/cdk.json b/integration_tests/cdk/cdk.json new file mode 100644 index 0000000..d9313bf --- /dev/null +++ b/integration_tests/cdk/cdk.json @@ -0,0 +1,32 @@ +{ + "app": "python3 app.py", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "requirements*.txt", + "source.bat", + "**/*.pyc", + "**/*.tmp", + "**/__pycache__", + "tests", + "scripts", + "*venv" + ] + }, + "context": { + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ] + } +} \ No newline at end of file diff --git a/integration_tests/cdk/config.py b/integration_tests/cdk/config.py new file mode 100644 index 0000000..82b3d22 --- /dev/null +++ b/integration_tests/cdk/config.py @@ -0,0 +1,181 @@ +from typing import Any, Dict, List, Union + +import pydantic +import yaml +from aws_cdk import aws_ec2 +from pydantic_core.core_schema import FieldValidationInfo +from pydantic_settings import BaseSettings + + +class AppConfig(BaseSettings): + project_id: str = pydantic.Field( + description="Project ID", default="eoapi-template-demo" + ) + stage: str = pydantic.Field(description="Stage of deployment", default="test") + # because of its validator, `tags` should always come after `project_id` and `stage` + tags: Dict[str, str] | None = pydantic.Field( + description="""Tags to apply to resources. If none provided, + will default to the defaults defined in `default_tags`. + Note that if tags are passed to the CDK CLI via `--tags`, + they will override any tags defined here.""", + default=None, + ) + auth_provider_jwks_url: str | None = pydantic.Field( + description="""Auth Provider JSON Web Key Set URL for + ingestion authentication. If not provided, + no authentication will be required.""", + default=None, + ) + data_access_role_arn: str | None = pydantic.Field( + description="""Role ARN for data access, that will be + used by the STAC ingestor for validation of assets + located in S3 and for the tiler application to access + assets located in S3. If none, the role will be + created at runtime with full S3 read access. If + provided, the existing role must be configured to + allow the tiler and STAC ingestor lambda roles to + assume it. See https://github.com/developmentseed/eoapi-cdk""", + default=None, + ) + db_instance_type: str = pydantic.Field( + description="Database instance type", default="t3.micro" + ) + db_allocated_storage: int = pydantic.Field( + description="Allocated storage for the database", default=5 + ) + public_db_subnet: bool = pydantic.Field( + description="Whether to put the database in a public subnet", default=True + ) + nat_gateway_count: int = pydantic.Field( + description="Number of NAT gateways to create", + default=0, + ) + bastion_host: bool = pydantic.Field( + description="""Whether to create a bastion host. It can typically + be used to make administrative connections to the database if + `public_db_subnet` is False""", + default=False, + ) + bastion_host_create_elastic_ip: bool = pydantic.Field( + description="""Whether to create an elastic IP for the bastion host. + Ignored if `bastion_host` equals `False`""", + default=False, + ) + bastion_host_allow_ip_list: List[str] = pydantic.Field( + description="""YAML file containing list of IP addresses to + allow SSH access to the bastion host. Ignored if `bastion_host` + equals `False`.""", + default=[], + ) + bastion_host_user_data: Union[Dict[str, Any], aws_ec2.UserData] = pydantic.Field( + description="""Path to file containing user data for the bastion host. + Ignored if `bastion_host` equals `False`.""", + default=aws_ec2.UserData.for_linux(), + ) + titiler_buckets: List[str] = pydantic.Field( + description="""Path to YAML file containing list of + buckets to grant access to the titiler API""", + default=[], + ) + acm_certificate_arn: str | None = pydantic.Field( + description="""ARN of ACM certificate to use for + custom domain names. If provided, + CDNs are created for all the APIs""", + default=None, + ) + stac_api_custom_domain: str | None = pydantic.Field( + description="""Custom domain name for the STAC API. + Must provide `acm_certificate_arn`""", + default=None, + ) + titiler_pgstac_api_custom_domain: str | None = pydantic.Field( + description="""Custom domain name for the titiler pgstac API. + Must provide `acm_certificate_arn`""", + default=None, + ) + stac_ingestor_api_custom_domain: str | None = pydantic.Field( + description="""Custom domain name for the STAC ingestor API. + Must provide `acm_certificate_arn`""", + default=None, + ) + tipg_api_custom_domain: str | None = pydantic.Field( + description="""Custom domain name for the tipg API. + Must provide `acm_certificate_arn`""", + default=None, + ) + stac_browser_version: str | None = pydantic.Field( + description="""Version of the Radiant Earth STAC browser to deploy. + If none provided, no STAC browser will be deployed. + If provided, `stac_api_custom_domain` must be provided + as it will be used as a backend.""", + default=None, + ) + + @pydantic.field_validator("tags") + def default_tags(cls, v, info: FieldValidationInfo): + return v or {"project_id": info.data["project_id"], "stage": info.data["stage"]} + + @pydantic.model_validator(mode="after") + def validate_nat_gateway_count(self) -> "AppConfig": + if not self.public_db_subnet and ( + self.nat_gateway_count is not None and self.nat_gateway_count <= 0 + ): + raise ValueError( + """if the database and its associated services instances + are to be located in the private subnet of the VPC, NAT + gateways are needed to allow egress from the services + and therefore `nat_gateway_count` has to be > 0.""" + ) + else: + return self + + @pydantic.model_validator(mode="after") + def validate_stac_browser_version(self) -> "AppConfig": + if ( + self.stac_browser_version is not None + and self.stac_api_custom_domain is None + ): + raise ValueError( + """If a STAC browser version is provided, + a custom domain must be provided for the STAC API""" + ) + else: + return self + + @pydantic.model_validator(mode="after") + def validate_acm_certificate_arn(self) -> "AppConfig": + if self.acm_certificate_arn is None and any( + [ + self.stac_api_custom_domain, + self.titiler_pgstac_api_custom_domain, + self.stac_ingestor_api_custom_domain, + self.tipg_api_custom_domain, + ] + ): + raise ValueError( + """If any custom domain is provided, + an ACM certificate ARN must be provided""" + ) + else: + return self + + def build_service_name(self, service_id: str) -> str: + return f"{self.project_id}-{self.stage}-{service_id}" + + +def build_app_config() -> AppConfig: + """Builds the AppConfig object from config.yaml file if exists, + otherwise use defaults""" + try: + with open("config.yaml") as f: + print("Loading config from config.yaml") + app_config = yaml.safe_load(f) + app_config = ( + {} if app_config is None else app_config + ) # if config is empty, set it to an empty dict + app_config = AppConfig(**app_config) + except FileNotFoundError: + # if no config at the expected path, using defaults + app_config = AppConfig() + + return app_config diff --git a/integration_tests/cdk/eoapi_template/__init__.py b/integration_tests/cdk/eoapi_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration_tests/cdk/eoapi_template/pgStacInfra.py b/integration_tests/cdk/eoapi_template/pgStacInfra.py new file mode 100644 index 0000000..0f12e8d --- /dev/null +++ b/integration_tests/cdk/eoapi_template/pgStacInfra.py @@ -0,0 +1,287 @@ +import boto3 +import yaml +from aws_cdk import ( + RemovalPolicy, + Stack, + aws_certificatemanager, + aws_ec2, + aws_iam, + aws_rds, + aws_s3, +) +from aws_cdk.aws_apigateway import DomainNameOptions +from aws_cdk.aws_apigatewayv2_alpha import DomainName +from constructs import Construct +from eoapi_cdk import ( + BastionHost, + PgStacApiLambda, + PgStacDatabase, + StacBrowser, + StacIngestor, + TiPgApiLambda, + TitilerPgstacApiLambda, +) + +from config import AppConfig + + +class pgStacInfraStack(Stack): + def __init__( + self, + scope: Construct, + vpc: aws_ec2.Vpc, + app_config: AppConfig, + **kwargs, + ) -> None: + super().__init__( + scope, + id=app_config.build_service_name("pgSTAC-infra"), + tags=app_config.tags, + **kwargs, + ) + + pgstac_db = PgStacDatabase( + self, + "pgstac-db", + vpc=vpc, + engine=aws_rds.DatabaseInstanceEngine.postgres( + version=aws_rds.PostgresEngineVersion.VER_14 + ), + vpc_subnets=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PUBLIC + if app_config.public_db_subnet + else aws_ec2.SubnetType.PRIVATE_ISOLATED + ), + allocated_storage=app_config.db_allocated_storage, + instance_type=aws_ec2.InstanceType(app_config.db_instance_type), + ) + + stac_api_lambda = PgStacApiLambda( + self, + "pgstac-api", + api_env={ + "NAME": app_config.build_service_name("STAC API"), + "description": f"{app_config.stage} STAC API", + }, + vpc=vpc, + db=pgstac_db.db, + db_secret=pgstac_db.pgstac_secret, + subnet_selection=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PUBLIC + if app_config.public_db_subnet + else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS + ), + stac_api_domain_name=DomainName( + self, + "stac-api-domain-name", + domain_name=app_config.stac_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "stac-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.stac_api_custom_domain + else None, + ) + + TitilerPgstacApiLambda( + self, + "titiler-pgstac-api", + api_env={ + "NAME": app_config.build_service_name("titiler pgSTAC API"), + "description": f"{app_config.stage} titiler pgstac API", + }, + vpc=vpc, + db=pgstac_db.db, + db_secret=pgstac_db.pgstac_secret, + subnet_selection=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PUBLIC + if app_config.public_db_subnet + else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS + ), + buckets=app_config.titiler_buckets, + titiler_pgstac_api_domain_name=DomainName( + self, + "titiler-pgstac-api-domain-name", + domain_name=app_config.titiler_pgstac_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "titiler-pgstac-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.titiler_pgstac_api_custom_domain + else None, + ) + + TiPgApiLambda( + self, + "tipg-api", + api_env={ + "NAME": app_config.build_service_name("tipg API"), + "description": f"{app_config.stage} tipg API", + }, + vpc=vpc, + db=pgstac_db.db, + db_secret=pgstac_db.pgstac_secret, + subnet_selection=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PUBLIC + if app_config.public_db_subnet + else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS + ), + tipg_api_domain_name=DomainName( + self, + "tipg-api-domain-name", + domain_name=app_config.tipg_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "tipg-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.tipg_api_custom_domain + else None, + ) + + if app_config.bastion_host: + BastionHost( + self, + "bastion-host", + vpc=vpc, + db=pgstac_db.db, + ipv4_allowlist=app_config.bastion_host_allow_ip_list, + user_data=aws_ec2.UserData.custom( + yaml.dump(app_config.bastion_host_user_data) + ) + if app_config.bastion_host_user_data is not None + else aws_ec2.UserData.for_linux(), + create_elastic_ip=app_config.bastion_host_create_elastic_ip, + ) + + if app_config.data_access_role_arn: + # importing provided role from arn. + # the stac ingestor will try to assume it when called, + # so it must be listed in the data access role trust policy. + data_access_role = aws_iam.Role.from_role_arn( + self, + "data-access-role", + role_arn=app_config.data_access_role_arn, + ) + else: + data_access_role = self._create_data_access_role() + + stac_ingestor_env = {"REQUESTER_PAYS": "True"} + + if app_config.auth_provider_jwks_url: + stac_ingestor_env["JWKS_URL"] = app_config.auth_provider_jwks_url + + stac_ingestor = StacIngestor( + self, + "stac-ingestor", + stac_url=stac_api_lambda.url, + stage=app_config.stage, + vpc=vpc, + data_access_role=data_access_role, + stac_db_secret=pgstac_db.pgstac_secret, + stac_db_security_group=pgstac_db.db.connections.security_groups[0], + subnet_selection=aws_ec2.SubnetSelection( + subnet_type=aws_ec2.SubnetType.PRIVATE_WITH_EGRESS + ), + api_env=stac_ingestor_env, + ingestor_domain_name_options=DomainNameOptions( + domain_name=app_config.stac_ingestor_api_custom_domain, + certificate=aws_certificatemanager.Certificate.from_certificate_arn( + self, + "stac-ingestor-api-cdn-certificate", + certificate_arn=app_config.acm_certificate_arn, + ), + ) + if app_config.stac_ingestor_api_custom_domain + else None, + ) + + if app_config.stac_browser_version: + stac_browser_bucket = aws_s3.Bucket( + self, + "stac-browser-bucket", + bucket_name=app_config.build_service_name("stac-browser"), + removal_policy=RemovalPolicy.DESTROY, + auto_delete_objects=True, + website_index_document="index.html", + public_read_access=True, + block_public_access=aws_s3.BlockPublicAccess( + block_public_acls=False, + block_public_policy=False, + ignore_public_acls=False, + restrict_public_buckets=False, + ), + object_ownership=aws_s3.ObjectOwnership.OBJECT_WRITER, + ) + StacBrowser( + self, + "stac-browser", + github_repo_tag=app_config.stac_browser_version, + stac_catalog_url=f"https://{app_config.stac_api_custom_domain}", + website_index_document="index.html", + bucket_arn=stac_browser_bucket.bucket_arn, + ) + + # we can only do that if the role is created here. + # If injecting a role, that role's trust relationship + # must be already set up, or set up after this deployment. + if not app_config.data_access_role_arn: + data_access_role = self._grant_assume_role_with_principal_pattern( + data_access_role, stac_ingestor.handler_role.role_name + ) + + def _create_data_access_role(self) -> aws_iam.Role: + """ + Creates an IAM role with full S3 read access. + """ + + data_access_role = aws_iam.Role( + self, + "data-access-role", + assumed_by=aws_iam.ServicePrincipal("lambda.amazonaws.com"), + ) + + data_access_role.add_to_policy( + aws_iam.PolicyStatement( + actions=[ + "s3:Get*", + ], + resources=["*"], + effect=aws_iam.Effect.ALLOW, + ) + ) + return data_access_role + + def _grant_assume_role_with_principal_pattern( + self, + role_to_assume: aws_iam.Role, + principal_pattern: str, + account_id: str = boto3.client("sts").get_caller_identity().get("Account"), + ) -> aws_iam.Role: + """ + Grants assume role permissions to the role of the given + account with the given name pattern. Default account + is the current account. + """ + + role_to_assume.assume_role_policy.add_statements( + aws_iam.PolicyStatement( + effect=aws_iam.Effect.ALLOW, + principals=[aws_iam.AnyPrincipal()], + actions=["sts:AssumeRole"], + conditions={ + "StringLike": { + "aws:PrincipalArn": [ + f"arn:aws:iam::{account_id}:role/{principal_pattern}" + ] + } + }, + ) + ) + + return role_to_assume diff --git a/integration_tests/cdk/eoapi_template/vpc.py b/integration_tests/cdk/eoapi_template/vpc.py new file mode 100644 index 0000000..bc723f4 --- /dev/null +++ b/integration_tests/cdk/eoapi_template/vpc.py @@ -0,0 +1,55 @@ +from aws_cdk import Stack, aws_ec2 +from constructs import Construct + +from config import AppConfig + + +class VpcStack(Stack): + def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: + super().__init__( + scope, + id=app_config.build_service_name("pgSTAC-vpc"), + tags=app_config.tags, + **kwargs + ) + + self.vpc = aws_ec2.Vpc( + self, + "vpc", + subnet_configuration=[ + aws_ec2.SubnetConfiguration( + name="ingress", subnet_type=aws_ec2.SubnetType.PUBLIC, cidr_mask=24 + ), + aws_ec2.SubnetConfiguration( + name="application", + subnet_type=aws_ec2.SubnetType.PRIVATE_WITH_EGRESS, + cidr_mask=24, + ), + aws_ec2.SubnetConfiguration( + name="rds", + subnet_type=aws_ec2.SubnetType.PRIVATE_ISOLATED, + cidr_mask=24, + ), + ], + nat_gateways=app_config.nat_gateway_count, + ) + + self.vpc.add_gateway_endpoint( + "DynamoDbEndpoint", service=aws_ec2.GatewayVpcEndpointAwsService.DYNAMODB + ) + + self.vpc.add_interface_endpoint( + "SecretsManagerEndpoint", + service=aws_ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER, + ) + + self.export_value( + self.vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PUBLIC) + .subnets[0] + .subnet_id + ) + self.export_value( + self.vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PUBLIC) + .subnets[1] + .subnet_id + ) diff --git a/integration_tests/cdk/package-lock.json b/integration_tests/cdk/package-lock.json new file mode 100644 index 0000000..9428ef3 --- /dev/null +++ b/integration_tests/cdk/package-lock.json @@ -0,0 +1,42 @@ +{ + "name": "eoapi-template", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eoapi-template", + "version": "0.1.0", + "dependencies": { + "aws-cdk": "^2.81.0" + } + }, + "node_modules/aws-cdk": { + "version": "2.96.2", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.96.2.tgz", + "integrity": "sha512-13ERpPV99OFAD75PLOtl0rRMXTWn6bCrmUPwYKkLwIMkj2xWCBiwo2Y9Qg+UzEszm5NMHA1N4ichSvuZ0mt2IQ==", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + } + } +} diff --git a/integration_tests/cdk/package.json b/integration_tests/cdk/package.json new file mode 100644 index 0000000..411cfc3 --- /dev/null +++ b/integration_tests/cdk/package.json @@ -0,0 +1,8 @@ +{ + "name": "eoapi-template", + "version": "0.1.0", + "dependencies": { + "aws-cdk": "^2.81.0" + } + } + \ No newline at end of file diff --git a/integration_tests/cdk/requirements.txt b/integration_tests/cdk/requirements.txt new file mode 100644 index 0000000..f628d3f --- /dev/null +++ b/integration_tests/cdk/requirements.txt @@ -0,0 +1,16 @@ +aws-cdk-lib>=2.75.0 +aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 +aws-cdk.aws-apigatewayv2-alpha==2.95.1a0 +eoapi-cdk +constructs>=10.0.0,<11.0.0 +pydantic==2.0.2 +pydantic-settings==2.0.1 +black==22.3.0 +boto3==1.24.15 +boto3-stubs[cognito-idp,cognito-identity] +flake8==4.0.1 +click==8.1.3 +requests==2.28.0 +python-dotenv==1.0.0 +pyyaml==6.0 +types-PyYAML==6.0.12.10 diff --git a/tests/README.md b/integration_tests/tests/README.md similarity index 100% rename from tests/README.md rename to integration_tests/tests/README.md diff --git a/tests/eoapi_tests/conftest.py b/integration_tests/tests/eoapi_tests/conftest.py similarity index 100% rename from tests/eoapi_tests/conftest.py rename to integration_tests/tests/eoapi_tests/conftest.py diff --git a/tests/eoapi_tests/fixtures/test_collection.json b/integration_tests/tests/eoapi_tests/fixtures/test_collection.json similarity index 100% rename from tests/eoapi_tests/fixtures/test_collection.json rename to integration_tests/tests/eoapi_tests/fixtures/test_collection.json diff --git a/tests/eoapi_tests/fixtures/test_item.json b/integration_tests/tests/eoapi_tests/fixtures/test_item.json similarity index 100% rename from tests/eoapi_tests/fixtures/test_item.json rename to integration_tests/tests/eoapi_tests/fixtures/test_item.json diff --git a/tests/eoapi_tests/fixtures/test_titiler_search_request.json b/integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json similarity index 100% rename from tests/eoapi_tests/fixtures/test_titiler_search_request.json rename to integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json diff --git a/tests/eoapi_tests/ingestion.py b/integration_tests/tests/eoapi_tests/ingestion.py similarity index 100% rename from tests/eoapi_tests/ingestion.py rename to integration_tests/tests/eoapi_tests/ingestion.py diff --git a/tests/eoapi_tests/settings.py b/integration_tests/tests/eoapi_tests/settings.py similarity index 100% rename from tests/eoapi_tests/settings.py rename to integration_tests/tests/eoapi_tests/settings.py diff --git a/tests/eoapi_tests/test_stac_ingestion.py b/integration_tests/tests/eoapi_tests/test_stac_ingestion.py similarity index 100% rename from tests/eoapi_tests/test_stac_ingestion.py rename to integration_tests/tests/eoapi_tests/test_stac_ingestion.py diff --git a/tests/pyproject.toml b/integration_tests/tests/pyproject.toml similarity index 100% rename from tests/pyproject.toml rename to integration_tests/tests/pyproject.toml diff --git a/tests/LICENSE b/tests/LICENSE deleted file mode 100644 index d24c62b..0000000 --- a/tests/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Development Seed - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file From 6ebc27188201e192b29c6894755366e6dd3c3bde Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 21 Sep 2023 17:17:12 +0900 Subject: [PATCH 08/30] trailing wsp --- integration_tests/cdk/config.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/integration_tests/cdk/config.py b/integration_tests/cdk/config.py index 82b3d22..70457c5 100644 --- a/integration_tests/cdk/config.py +++ b/integration_tests/cdk/config.py @@ -14,15 +14,15 @@ class AppConfig(BaseSettings): stage: str = pydantic.Field(description="Stage of deployment", default="test") # because of its validator, `tags` should always come after `project_id` and `stage` tags: Dict[str, str] | None = pydantic.Field( - description="""Tags to apply to resources. If none provided, + description="""Tags to apply to resources. If none provided, will default to the defaults defined in `default_tags`. - Note that if tags are passed to the CDK CLI via `--tags`, + Note that if tags are passed to the CDK CLI via `--tags`, they will override any tags defined here.""", default=None, ) auth_provider_jwks_url: str | None = pydantic.Field( description="""Auth Provider JSON Web Key Set URL for - ingestion authentication. If not provided, + ingestion authentication. If not provided, no authentication will be required.""", default=None, ) @@ -51,8 +51,8 @@ class AppConfig(BaseSettings): default=0, ) bastion_host: bool = pydantic.Field( - description="""Whether to create a bastion host. It can typically - be used to make administrative connections to the database if + description="""Whether to create a bastion host. It can typically + be used to make administrative connections to the database if `public_db_subnet` is False""", default=False, ) @@ -62,7 +62,7 @@ class AppConfig(BaseSettings): default=False, ) bastion_host_allow_ip_list: List[str] = pydantic.Field( - description="""YAML file containing list of IP addresses to + description="""YAML file containing list of IP addresses to allow SSH access to the bastion host. Ignored if `bastion_host` equals `False`.""", default=[], @@ -78,18 +78,18 @@ class AppConfig(BaseSettings): default=[], ) acm_certificate_arn: str | None = pydantic.Field( - description="""ARN of ACM certificate to use for + description="""ARN of ACM certificate to use for custom domain names. If provided, CDNs are created for all the APIs""", default=None, ) stac_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the STAC API. + description="""Custom domain name for the STAC API. Must provide `acm_certificate_arn`""", default=None, ) titiler_pgstac_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the titiler pgstac API. + description="""Custom domain name for the titiler pgstac API. Must provide `acm_certificate_arn`""", default=None, ) @@ -99,7 +99,7 @@ class AppConfig(BaseSettings): default=None, ) tipg_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the tipg API. + description="""Custom domain name for the tipg API. Must provide `acm_certificate_arn`""", default=None, ) @@ -136,7 +136,7 @@ def validate_stac_browser_version(self) -> "AppConfig": and self.stac_api_custom_domain is None ): raise ValueError( - """If a STAC browser version is provided, + """If a STAC browser version is provided, a custom domain must be provided for the STAC API""" ) else: @@ -153,7 +153,7 @@ def validate_acm_certificate_arn(self) -> "AppConfig": ] ): raise ValueError( - """If any custom domain is provided, + """If any custom domain is provided, an ACM certificate ARN must be provided""" ) else: From 80f03ba3e23716f412c1bf9b969e6793d7a3dd35 Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 21 Sep 2023 17:17:25 +0900 Subject: [PATCH 09/30] run action with push to try --- .github/workflows/deploy.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a91573c..bfc37be 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -11,11 +11,15 @@ on: types: [completed] # ... or manually. workflow_dispatch: + + # remove later + push: + branches: + - "feat/add-integration-tests" jobs: # run a job on a successful `Workflow` run. on-successful-release: - if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest env: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} @@ -52,7 +56,7 @@ jobs: working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate - cdk synth --debug --all --require-approval never + npx cdk synth --debug --all --require-approval never deactivate # deploys and grabs URLs from the output for later tests @@ -61,7 +65,7 @@ jobs: working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate - cdk deploy --ci --all --require-approval never + npx cdk deploy --ci --all --require-approval never echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT @@ -91,5 +95,5 @@ jobs: working-directory: integration_tests/cdk run: | source .deployment_venv/bin/activate - cdk destroy --ci --all --force + npx cdk destroy --ci --all --force deactivate From 4ff82c23c0a09f7b0cd5d42308a3f851f9757a87 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 27 Sep 2023 10:04:47 +0900 Subject: [PATCH 10/30] move the trigger of integration tests to distribute, right before release --- .github/workflows/distribute.yaml | 9 +++-- .../{deploy.yaml => integration-test.yaml} | 33 +++++++++++-------- integration_tests/cdk/README.md | 10 +++++- integration_tests/cdk/requirements.txt | 1 - 4 files changed, 32 insertions(+), 21 deletions(-) rename .github/workflows/{deploy.yaml => integration-test.yaml} (85%) diff --git a/.github/workflows/distribute.yaml b/.github/workflows/distribute.yaml index e4600a3..e484b8f 100644 --- a/.github/workflows/distribute.yaml +++ b/.github/workflows/distribute.yaml @@ -9,15 +9,14 @@ jobs: package: uses: ./.github/workflows/build.yaml + integration-test: + uses: ./.github/workflows/integration-test.yaml + needs: package + distribute-python: runs-on: ubuntu-latest needs: package steps: - - uses: actions/download-artifact@v3 - with: - name: python - path: dist - - run: pip install twine - run: twine upload dist/* diff --git a/.github/workflows/deploy.yaml b/.github/workflows/integration-test.yaml similarity index 85% rename from .github/workflows/deploy.yaml rename to .github/workflows/integration-test.yaml index bfc37be..eb9fe79 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/integration-test.yaml @@ -1,25 +1,22 @@ -name: Deploy & Test Deployment +name: Deploy & Integration Test permissions: id-token: write # required for requesting the JWT contents: read # required for actions/checkout on: - # run this workflow when the `Distribute` workflow completes - workflow_run: - workflows: [Distribute] - types: [completed] - # ... or manually. + workflow_call: + workflow_dispatch: # remove later - push: - branches: - - "feat/add-integration-tests" + # push: + # branches: + # - "feat/add-integration-tests" jobs: - # run a job on a successful `Workflow` run. - on-successful-release: + deploy-and-integration-test: + name: Deploy & Integration Test runs-on: ubuntu-latest env: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} @@ -41,14 +38,22 @@ jobs: uses: actions/setup-node@v3 with: node-version: '18' - + + - name: Download compiled eoapi-cdk artifact + uses: actions/download-artifact@v3 + with: + name: python + path: dist + + - name: Install eoapi-cdk from artifact + run: pip install dist/python/*.gz + - name: Install deployment environment working-directory: integration_tests/cdk run: | python -m venv .deployment_venv source .deployment_venv/bin/activate pip install -r requirements.txt - pip install --upgrade eoapi-cdk # make sure to take the latest version of eoapi-cdk since we're integration-testing the newest release. npm install deactivate @@ -76,7 +81,7 @@ jobs: run: | python -m venv .tests_venv source .tests_venv/bin/activate - pip install -r requirements.txt + pip install -e . deactivate - name: Test the stack diff --git a/integration_tests/cdk/README.md b/integration_tests/cdk/README.md index 1c7ad49..3711c67 100644 --- a/integration_tests/cdk/README.md +++ b/integration_tests/cdk/README.md @@ -22,7 +22,15 @@ source .venv/bin/activate python -m pip install -r requirements.txt ``` -Note that `eoapi-cdk` isn't pinned, so that the latest version is always installed. Also install node dependencies with +Install the latest `eoapi-cdk` either from PyPI: + +``` +pip install eoapi-cdk +``` + +Or alternatively, compile and package from the root of this repository to get the python version of the constructs locally. + +Also install node dependencies with ``` npm install diff --git a/integration_tests/cdk/requirements.txt b/integration_tests/cdk/requirements.txt index f628d3f..53f55e7 100644 --- a/integration_tests/cdk/requirements.txt +++ b/integration_tests/cdk/requirements.txt @@ -1,7 +1,6 @@ aws-cdk-lib>=2.75.0 aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 aws-cdk.aws-apigatewayv2-alpha==2.95.1a0 -eoapi-cdk constructs>=10.0.0,<11.0.0 pydantic==2.0.2 pydantic-settings==2.0.1 From eb20644460d8f3f59339eb48bb3239cabea83860 Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 5 Oct 2023 18:51:51 +0900 Subject: [PATCH 11/30] feat!: custom runtime for bootstrapper and custom runtimes from Dockerfile for all apps BREAKING CHANGE: clients need to provide aws_lambda.AssetCode to configure their apps. Solely the python application and the requirements.txt file is not supported anymore. --- lib/bootstrapper/index.ts | 39 +- lib/bootstrapper/runtime/Dockerfile | 5 +- lib/database/index.ts | 2 - lib/index.ts | 1 + lib/ingestor-api/index.ts | 136 +- lib/ingestor-api/runtime/Dockerfile | 17 + lib/ingestor-api/runtime/requirements.txt | 1 - lib/stac-api/index.ts | 79 +- lib/stac-api/runtime/Dockerfile | 17 + lib/stac-api/runtime/requirements.txt | 1 - lib/tipg-api/index.ts | 63 +- lib/tipg-api/runtime/Dockerfile | 17 + lib/titiler-pgstac-api/index.ts | 87 +- lib/titiler-pgstac-api/runtime/Dockerfile | 18 + .../runtime/requirements.txt | 3 +- lib/utils/index.ts | 34 + package-lock.json | 3296 +++++++++++++++-- package.json | 10 +- 18 files changed, 3327 insertions(+), 499 deletions(-) create mode 100644 lib/ingestor-api/runtime/Dockerfile create mode 100644 lib/stac-api/runtime/Dockerfile create mode 100644 lib/tipg-api/runtime/Dockerfile create mode 100644 lib/titiler-pgstac-api/runtime/Dockerfile create mode 100644 lib/utils/index.ts diff --git a/lib/bootstrapper/index.ts b/lib/bootstrapper/index.ts index e4cd30d..958b837 100644 --- a/lib/bootstrapper/index.ts +++ b/lib/bootstrapper/index.ts @@ -10,6 +10,7 @@ import { RemovalPolicy, } from "aws-cdk-lib"; import { Construct } from "constructs"; +import { CustomLambdaFunctionOptions } from "../utils"; function hasVpc( instance: aws_rds.DatabaseInstance | aws_rds.IDatabaseInstance @@ -17,8 +18,6 @@ function hasVpc( return (instance as aws_rds.DatabaseInstance).vpc !== undefined; } -const DEFAULT_PGSTAC_VERSION = "0.6.13"; - /** * Bootstraps a database instance, installing pgSTAC onto the database. */ @@ -28,17 +27,18 @@ export class BootstrapPgStac extends Construct { constructor(scope: Construct, id: string, props: BootstrapPgStacProps) { super(scope, id); - const { pgstacVersion = DEFAULT_PGSTAC_VERSION } = props; const handler = new aws_lambda.Function(this, "lambda", { - handler: "handler.handler", - runtime: aws_lambda.Runtime.PYTHON_3_8, - code: aws_lambda.Code.fromDockerBuild(__dirname, { + ...props.lambdaFunctionOptions ?? { + runtime: aws_lambda.Runtime.PYTHON_3_8, + handler: "handler.handler", + memorySize: 128, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.minutes(2) + }, + code: props.lambdaAssetCode ?? aws_lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PGSTAC_VERSION: pgstacVersion }, }), - timeout: Duration.minutes(2), vpc: hasVpc(props.database) ? props.database.vpc : props.vpc, - logRetention: aws_logs.RetentionDays.ONE_WEEK, }); this.secret = new aws_secretsmanager.Secret(this, "secret", { @@ -75,9 +75,6 @@ export class BootstrapPgStac extends Construct { new CustomResource(this, "bootstrapper", { serviceToken: handler.functionArn, properties: { - // By setting pgstac_version in the properties assures - // that Create/Update events will be passed to the service token - pgstac_version: pgstacVersion, conn_secret_arn: props.dbSecret.secretArn, new_user_secret_arn: this.secret.secretArn, }, @@ -127,16 +124,22 @@ export interface BootstrapPgStacProps { readonly pgstacUsername?: string; /** - * pgSTAC version to be installed. + * Prefix to assign to the generated `secrets_manager.Secret` * - * @default 0.6.8 + * @default pgstac */ - readonly pgstacVersion?: string; + readonly secretsPrefix?: string; /** - * Prefix to assign to the generated `secrets_manager.Secret` + * Optional settings for the lambda function. * - * @default pgstac + * @default - defined in the construct. */ - readonly secretsPrefix?: string; + readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; + + /** + * Optional lambda asset code + * @default default runtime defined in this repository + */ + readonly lambdaAssetCode?: aws_lambda.AssetCode; } diff --git a/lib/bootstrapper/runtime/Dockerfile b/lib/bootstrapper/runtime/Dockerfile index aaa58da..f9e7ace 100644 --- a/lib/bootstrapper/runtime/Dockerfile +++ b/lib/bootstrapper/runtime/Dockerfile @@ -1,11 +1,8 @@ FROM lambci/lambda:build-python3.8 -ARG PGSTAC_VERSION -RUN echo "Using PGSTAC Version ${PGSTAC_VERSION}" - WORKDIR /tmp -RUN pip install httpx psycopg[binary,pool] pypgstac==${PGSTAC_VERSION} -t /asset +RUN pip install httpx psycopg[binary,pool] pypgstac==0.6.13 -t /asset COPY runtime/handler.py /asset/handler.py diff --git a/lib/database/index.ts b/lib/database/index.ts index 3e9392b..ffa8a91 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -50,7 +50,6 @@ export class PgStacDatabase extends Construct { database: this.db, dbSecret: this.db.secret!, pgstacDbName: props.pgstacDbName, - pgstacVersion: props.pgstacVersion, pgstacUsername: props.pgstacUsername, secretsPrefix: props.secretsPrefix, }); @@ -100,7 +99,6 @@ export class PgStacDatabase extends Construct { export interface PgStacDatabaseProps extends rds.DatabaseInstanceProps { readonly pgstacDbName?: BootstrapPgStacProps["pgstacDbName"]; - readonly pgstacVersion?: BootstrapPgStacProps["pgstacVersion"]; readonly pgstacUsername?: BootstrapPgStacProps["pgstacUsername"]; readonly secretsPrefix?: BootstrapPgStacProps["secretsPrefix"]; } diff --git a/lib/index.ts b/lib/index.ts index 6ff9cce..53b0b67 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -6,3 +6,4 @@ export * from "./stac-api"; export * from "./titiler-pgstac-api"; export * from "./stac-browser"; export * from "./tipg-api"; +export * from "./utils"; \ No newline at end of file diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index dc65f02..1b1fe66 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -4,6 +4,7 @@ import { aws_ec2 as ec2, aws_iam as iam, aws_lambda as lambda, + aws_logs, aws_lambda_event_sources as events, aws_secretsmanager as secretsmanager, aws_ssm as ssm, @@ -11,8 +12,8 @@ import { RemovalPolicy, Stack, } from "aws-cdk-lib"; -import { PythonFunction, PythonFunctionProps } from "@aws-cdk/aws-lambda-python-alpha"; import { Construct } from "constructs"; +import { CustomLambdaFunctionOptions } from "../utils"; export class StacIngestor extends Construct { table: dynamodb.Table; @@ -55,7 +56,8 @@ export class StacIngestor extends Construct { dbVpc: props.vpc, dbSecurityGroup: props.stacDbSecurityGroup, subnetSelection: props.subnetSelection, - apiCode: props.apiCode, + lambdaAssetCode: props.apiLambdaAssetCode, + lambdaFunctionOptions: props.apiLambdaFunctionOptions, }); this.buildApiEndpoint({ @@ -73,7 +75,8 @@ export class StacIngestor extends Construct { dbVpc: props.vpc, dbSecurityGroup: props.stacDbSecurityGroup, subnetSelection: props.subnetSelection, - ingestorCode: props.ingestorCode, + lambdaAssetCode: props.ingestorLambdaAssetCode, + lambdaFunctionOptions: props.ingestorLambdaFunctionOptions }); this.registerSsmParameter({ @@ -110,25 +113,27 @@ export class StacIngestor extends Construct { dbVpc: ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; subnetSelection: ec2.SubnetSelection - apiCode?: ApiCode; - }): PythonFunction { - - const apiCode = props.apiCode || { - entry: `${__dirname}/runtime`, - index: "src/handler.py", - handler: "handler", - }; - - const handler = new PythonFunction(this, "api-handler", { - ...apiCode, - runtime: lambda.Runtime.PYTHON_3_9, - timeout: Duration.seconds(30), - environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, + lambdaFunctionOptions?: CustomLambdaFunctionOptions; + lambdaAssetCode?: lambda.AssetCode; + }): lambda.Function { + + const handler = new lambda.Function(this, "api-handler", { + ...props.lambdaFunctionOptions ?? { + runtime: lambda.Runtime.PYTHON_3_9, + handler: "handler.handler", + memorySize: 2048, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30) + }, + code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.9' }, + }), vpc: props.dbVpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, - role: this.handlerRole, - memorySize: 2048, + environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, + role: this.handlerRole }); // Allow handler to read DB secret @@ -153,26 +158,28 @@ export class StacIngestor extends Construct { dbVpc: ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; subnetSelection: ec2.SubnetSelection; - ingestorCode?: IngestorCode; - }): PythonFunction { - - - - const ingestorCode = props.ingestorCode || { - entry: `${__dirname}/runtime`, - index: "src/ingestor.py", - handler: "handler", - }; + lambdaFunctionOptions?: CustomLambdaFunctionOptions; + lambdaAssetCode?: lambda.AssetCode; + }): lambda.Function { - const handler = new PythonFunction(this, "stac-ingestor", { - ...ingestorCode, - runtime: lambda.Runtime.PYTHON_3_9, - timeout: Duration.seconds(180), - environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, + + const handler = new lambda.Function(this, "stac-ingestor", { + ...props.lambdaFunctionOptions ?? { + runtime: lambda.Runtime.PYTHON_3_9, + handler: "handler.handler", + memorySize: 2048, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(180) + }, + code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.9' }, + }), vpc: props.dbVpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, - memorySize: 2048, + environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, + role: this.handlerRole }); // Allow handler to read DB secret @@ -309,54 +316,29 @@ export interface StacIngestorProps { readonly ingestorDomainNameOptions?: apigateway.DomainNameOptions; /** - * Custom code for the ingestor api. - * - * @default - default in the runtime folder. + * Optional api lambda asset code + * @default - default runtime defined in this repository. */ - readonly apiCode?: ApiCode; - - /** - * Custom code for the ingestor. - * - * @default - default in the runtime folder. - */ - readonly ingestorCode?: IngestorCode; -} - -export interface ApiCode { + readonly apiLambdaAssetCode?: lambda.AssetCode; /** - * Path to the source of the function or the location for dependencies, for the api lambda. + * Optional ingestor lambda asset code + * @default - default runtime defined in this repository. */ - readonly entry: PythonFunctionProps["entry"]; + readonly ingestorLambdaAssetCode?: lambda.AssetCode; /** - * Path to the index file containing the exported handler, relative to `api_lambda_entry`. - */ - readonly index: PythonFunctionProps["index"]; - - /** - * The name of the exported handler in the `api_lambda_index` file. - */ - readonly handler: PythonFunctionProps["handler"]; - -} - -export interface IngestorCode { + * Optional settings for the api lambda function. + * + * @default - defined in the construct. + */ + readonly apiLambdaFunctionOptions?: CustomLambdaFunctionOptions; /** - * Path to the source of the function or the location for dependencies, for the ingestor lambda. - */ - readonly entry: PythonFunctionProps["entry"]; - - /** - * Path to the index file containing the exported handler, relative to `ingestor_lambda_entry`. - */ - readonly index: PythonFunctionProps["index"]; - - /** - * The name of the exported handler in the `ingestor_lambda_index` file. - */ - readonly handler: PythonFunctionProps["handler"]; - + * Optional settings for the ingestor lambda function. + * + * @default - defined in the construct. + */ +readonly ingestorLambdaFunctionOptions?: CustomLambdaFunctionOptions; + } \ No newline at end of file diff --git a/lib/ingestor-api/runtime/Dockerfile b/lib/ingestor-api/runtime/Dockerfile new file mode 100644 index 0000000..604f813 --- /dev/null +++ b/lib/ingestor-api/runtime/Dockerfile @@ -0,0 +1,17 @@ +FROM public.ecr.aws/lambda/python:3.10 + +WORKDIR /tmp +RUN python -m pip install pip -U + +COPY runtime/requirements.txt requirements.txt +RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic + +# Reduce package size and remove useless files +RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; +RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf +RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f +RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf + +COPY runtime/src/*.py /asset/ + +CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/ingestor-api/runtime/requirements.txt b/lib/ingestor-api/runtime/requirements.txt index 349a2ed..25bfb2c 100644 --- a/lib/ingestor-api/runtime/requirements.txt +++ b/lib/ingestor-api/runtime/requirements.txt @@ -1,7 +1,6 @@ Authlib==1.0.1 cachetools==5.1.0 fastapi>=0.75.1 -mangum>=0.15.0 orjson>=3.6.8 psycopg[binary,pool]>=3.0.15 pydantic_ssm_settings>=0.2.0 diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index a6e5212..6bcc1af 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -5,53 +5,42 @@ import { aws_lambda as lambda, aws_secretsmanager as secretsmanager, CfnOutput, + Duration, + aws_logs, } from "aws-cdk-lib"; -import { - PythonFunction, - PythonFunctionProps, -} from "@aws-cdk/aws-lambda-python-alpha"; import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; +import { CustomLambdaFunctionOptions } from "../utils"; export class PgStacApiLambda extends Construct { readonly url: string; - public stacApiLambdaFunction: PythonFunction; + public stacApiLambdaFunction: lambda.Function; constructor(scope: Construct, id: string, props: PgStacApiLambdaProps) { super(scope, id); - - const apiCode = props.apiCode || { - entry: `${__dirname}/runtime`, - index: "src/handler.py", - handler: "handler", - }; - - this.stacApiLambdaFunction = new PythonFunction(this, "stac-api", { - ...apiCode, - /** - * NOTE: Unable to use Py3.9, due to issues with hashes: - * - * ERROR: Hashes are required in --require-hashes mode, but they are missing - * from some requirements. Here is a list of those requirements along with the - * hashes their downloaded archives actually had. Add lines like these to your - * requirements files to prevent tampering. (If you did not enable - * --require-hashes manually, note that it turns on automatically when any - * package has a hash.) - * anyio==3.6.1 --hash=sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be - * */ - runtime: lambda.Runtime.PYTHON_3_8, - architecture: lambda.Architecture.X86_64, + + this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { + ...props.lambdaFunctionOptions ?? { + runtime: lambda.Runtime.PYTHON_3_8, + handler: "handler.handler", + memorySize: 8192, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30) + }, + code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.8' }, + }), + vpc: props.vpc, + vpcSubnets: props.subnetSelection, + allowPublicSubnet: true, environment: { PGSTAC_SECRET_ARN: props.dbSecret.secretArn, DB_MIN_CONN_SIZE: "0", DB_MAX_CONN_SIZE: "1", ...props.apiEnv, }, - vpc: props.vpc, - vpcSubnets: props.subnetSelection, - allowPublicSubnet: true, - memorySize: 8192, }); props.dbSecret.grantRead(this.stacApiLambdaFunction); @@ -94,13 +83,6 @@ export interface PgStacApiLambdaProps { */ readonly dbSecret: secretsmanager.ISecret; - /** - * Custom code to run for fastapi-pgstac. - * - * @default - simplified version of fastapi-pgstac - */ - readonly apiCode?: ApiEntrypoint; - /** * Customized environment variables to send to fastapi-pgstac runtime. */ @@ -110,19 +92,18 @@ export interface PgStacApiLambdaProps { * Custom Domain Name Options for STAC API, */ readonly stacApiDomainName?: IDomainName; -} -export interface ApiEntrypoint { /** - * Path to the source of the function or the location for dependencies. - */ - readonly entry: PythonFunctionProps["entry"]; - /** - * The path (relative to entry) to the index file containing the exported handler. - */ - readonly index: PythonFunctionProps["index"]; + * Optional settings for the lambda function. + * + * @default - defined in the construct. + */ + readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; + /** - * The name of the exported handler in the index file. + * Optional lambda asset code + * @default - default runtime defined in this repository. */ - readonly handler: PythonFunctionProps["handler"]; + readonly lambdaAssetCode?: lambda.AssetCode; } + diff --git a/lib/stac-api/runtime/Dockerfile b/lib/stac-api/runtime/Dockerfile new file mode 100644 index 0000000..604f813 --- /dev/null +++ b/lib/stac-api/runtime/Dockerfile @@ -0,0 +1,17 @@ +FROM public.ecr.aws/lambda/python:3.10 + +WORKDIR /tmp +RUN python -m pip install pip -U + +COPY runtime/requirements.txt requirements.txt +RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic + +# Reduce package size and remove useless files +RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; +RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf +RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f +RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf + +COPY runtime/src/*.py /asset/ + +CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/stac-api/runtime/requirements.txt b/lib/stac-api/runtime/requirements.txt index bb0bfe9..5005945 100644 --- a/lib/stac-api/runtime/requirements.txt +++ b/lib/stac-api/runtime/requirements.txt @@ -1,4 +1,3 @@ -mangum==0.15.1 stac-fastapi.api==2.4.1 stac-fastapi.extensions==2.4.1 stac-fastapi.pgstac==2.4.1 diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index c9f7fe7..eb33870 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -3,46 +3,44 @@ import { aws_ec2 as ec2, aws_rds as rds, aws_lambda as lambda, + aws_logs as logs, aws_secretsmanager as secretsmanager, CfnOutput, Duration, } from "aws-cdk-lib"; - import { - PythonFunction, - PythonFunctionProps, - } from "@aws-cdk/aws-lambda-python-alpha"; import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; + import { CustomLambdaFunctionOptions } from "../utils"; export class TiPgApiLambda extends Construct { readonly url: string; - public tiPgLambdaFunction: PythonFunction; + public tiPgLambdaFunction: lambda.Function; constructor(scope: Construct, id: string, props: TiPgApiLambdaProps) { super(scope, id); - const apiCode = props.apiCode || { - entry: `${__dirname}/runtime`, - index: "src/handler.py", - handler: "handler", - }; - - this.tiPgLambdaFunction = new PythonFunction(this, "tipg-api", { - ...apiCode, - runtime: lambda.Runtime.PYTHON_3_10, - architecture: lambda.Architecture.X86_64, + this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { + ...props.lambdaFunctionOptions ?? { + runtime: lambda.Runtime.PYTHON_3_10, + handler: "handler.handler", + memorySize: 1024, + logRetention: logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30) + }, + code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.10' }, + }), + vpc: props.vpc, + vpcSubnets: props.subnetSelection, + allowPublicSubnet: true, environment: { PGSTAC_SECRET_ARN: props.dbSecret.secretArn, DB_MIN_CONN_SIZE: "1", DB_MAX_CONN_SIZE: "1", ...props.apiEnv, }, - vpc: props.vpc, - vpcSubnets: props.subnetSelection, - allowPublicSubnet: true, - memorySize: 1024, - timeout: Duration.seconds(30), }); props.dbSecret.grantRead(this.tiPgLambdaFunction); @@ -86,12 +84,6 @@ import { */ readonly dbSecret: secretsmanager.ISecret; - /** - * Custom code to run for the application. - * - * @default - simplified version of tipg. - */ - readonly apiCode?: TiPgApiEntrypoint; /** * Customized environment variables to send to titiler-pgstac runtime. @@ -105,19 +97,18 @@ import { * @default - undefined */ readonly tipgApiDomainName?: IDomainName; - } - export interface TiPgApiEntrypoint { - /** - * Path to the source of the function or the location for dependencies. - */ - readonly entry: PythonFunctionProps["entry"]; + /** - * The path (relative to entry) to the index file containing the exported handler. + * Optional settings for the lambda function. + * + * @default - defined in the construct. */ - readonly index: PythonFunctionProps["index"]; + readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; + /** - * The name of the exported handler in the index file. + * Optional lambda asset code + * @default - default runtime defined in this repository. */ - readonly handler: PythonFunctionProps["handler"]; + readonly lambdaAssetCode?: lambda.AssetCode; } diff --git a/lib/tipg-api/runtime/Dockerfile b/lib/tipg-api/runtime/Dockerfile new file mode 100644 index 0000000..2a1ef11 --- /dev/null +++ b/lib/tipg-api/runtime/Dockerfile @@ -0,0 +1,17 @@ +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:3.10 + +WORKDIR /tmp +RUN python -m pip install pip -U + +COPY runtime/requirements.txt requirements.txt +RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic + +# Reduce package size and remove useless files +RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; +RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf +RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f +RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf + +COPY runtime/src/*.py /asset/ + +CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index 0a09fb0..7d26ead 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -7,14 +7,12 @@ import { aws_secretsmanager as secretsmanager, CfnOutput, Duration, - aws_logs, - BundlingOptions + aws_logs } from "aws-cdk-lib"; - import { Runtime } from 'aws-cdk-lib/aws-lambda'; - import {PythonFunction} from "@aws-cdk/aws-lambda-python-alpha"; import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; +import { CustomLambdaFunctionOptions } from "../utils"; // default settings that can be overridden by the user-provided environment. @@ -39,29 +37,24 @@ import { constructor(scope: Construct, id: string, props: TitilerPgStacApiLambdaProps) { super(scope, id); - - - // if user provided environment variables, merge them with the defaults. - const apiEnv = props.apiEnv ? { ...defaultTitilerPgstacEnv, ...props.apiEnv, "PGSTAC_SECRET_ARN": props.dbSecret.secretArn } : defaultTitilerPgstacEnv; - - const pythonLambdaOptions: TitilerPgstacPythonLambdaOptions = props.pythonLambdaOptions ?? { - runtime: lambda.Runtime.PYTHON_3_10, - entry: `${__dirname}/runtime`, - index: "src/handler.py", - handler: "handler", - memorySize: 3008, - architecture: lambda.Architecture.X86_64 - } - - this.titilerPgstacLambdaFunction = new PythonFunction(this, "titiler-pgstac-api", { - ...pythonLambdaOptions, - environment: apiEnv, + + this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { + ...props.lambdaFunctionOptions ?? { + runtime: lambda.Runtime.PYTHON_3_10, // update Dockerfile if this is changed. + handler: "handler.handler", + memorySize: 3008, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30) + }, + code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + }), vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30) - }) + // if user provided environment variables, merge them with the defaults. + environment: props.apiEnv ? { ...defaultTitilerPgstacEnv, ...props.apiEnv, "PGSTAC_SECRET_ARN": props.dbSecret.secretArn } : defaultTitilerPgstacEnv, + }); // grant access to buckets using addToRolePolicy if (props.buckets) { @@ -133,51 +126,17 @@ import { readonly titilerPgstacApiDomainName?: IDomainName; /** - * Optional settings for the titiler-pgstac python lambda function. + * Optional settings for the lambda function. * * @default - defined in the construct. */ - readonly pythonLambdaOptions?: TitilerPgstacPythonLambdaOptions; - - } - - - export interface TitilerPgstacPythonLambdaOptions { + readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; /** - * Path to the source of the function or the location for dependencies. + * Optional lambda asset code + * @default default runtime defined in this repository */ - readonly entry: string; - /** - * The runtime environment. Only runtimes of the Python family are - * supported. - */ - readonly runtime: Runtime; - - /** - * The path (relative to entry) to the index file containing the exported handler. - * - */ - readonly index: string; - /** - * The name of the exported handler in the index file. - */ - readonly handler: string; - - /** - * Bundling options to use for this function. Use this to specify custom bundling options like - * the bundling Docker image, asset hash type, custom hash, architecture, etc. - */ - readonly bundling?: BundlingOptions; - - /** - * The amount of memory, in MB, that is allocated to your Lambda function. - */ - readonly memorySize: number; - - /** - * The system architectures compatible with this lambda function. - */ - readonly architecture: lambda.Architecture; + readonly lambdaAssetCode?: lambda.AssetCode; } + diff --git a/lib/titiler-pgstac-api/runtime/Dockerfile b/lib/titiler-pgstac-api/runtime/Dockerfile new file mode 100644 index 0000000..1fba5ce --- /dev/null +++ b/lib/titiler-pgstac-api/runtime/Dockerfile @@ -0,0 +1,18 @@ +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:3.10 + +WORKDIR /tmp +RUN python -m pip install pip -U + +COPY runtime/requirements.txt requirements.txt +RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset + +# Reduce package size and remove useless files +RUN find /asset -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; +RUN find /asset -type d -name '__pycache__' -print0 | xargs -0 rm -rf +RUN find /asset -type f -name '*.py' -print0 | xargs -0 rm -f +RUN find /asset -type d -name 'tests' -print0 | xargs -0 rm -rf +RUN rm -rdf /asset/numpy/doc/ /asset/boto3* /asset/botocore* /asset/bin /asset/geos_license /asset/Misc + +COPY runtime/src/*.py /asset/ + +CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/titiler-pgstac-api/runtime/requirements.txt b/lib/titiler-pgstac-api/runtime/requirements.txt index 7a7838b..20e5c18 100644 --- a/lib/titiler-pgstac-api/runtime/requirements.txt +++ b/lib/titiler-pgstac-api/runtime/requirements.txt @@ -1,3 +1,2 @@ titiler.pgstac==0.5.1 -psycopg[binary, pool] -mangum>=0.14,<0.15 +psycopg[binary, pool] \ No newline at end of file diff --git a/lib/utils/index.ts b/lib/utils/index.ts new file mode 100644 index 0000000..80b12b1 --- /dev/null +++ b/lib/utils/index.ts @@ -0,0 +1,34 @@ +import { + aws_lambda as lambda, + aws_logs as logs, + Duration, + } from "aws-cdk-lib"; + +export interface CustomLambdaFunctionOptions { + + /*** + * The runtime environment for the Lambda function that you are uploading. + */ + readonly runtime: lambda.Runtime; + + /** + * The function execution time (in seconds) after which Lambda terminates the function. + */ + readonly timeout: Duration; + + /** + * The amount of memory, in MB, that is allocated to your Lambda function. + */ + readonly memorySize: number; + + /** + * handler for the lambda function + */ + readonly handler: string; + + /** + * log retention period for the lambda function + */ + readonly logRetention: logs.RetentionDays; + + } diff --git a/package-lock.json b/package-lock.json index 4af7193..fc13754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,22 @@ { - "name": "cdk-pgstac", + "name": "eoapi-cdk", "version": "5.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "cdk-pgstac", + "name": "eoapi-cdk", "version": "5.4.0", + "bundleDependencies": [ + "module" + ], "license": "ISC", "dependencies": { "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.47.0-alpha.0", "@aws-cdk/aws-lambda-python-alpha": "^2.47.0-alpha.0", - "aws-cdk-lib": "^2.75.0", - "constructs": "^10.1.113" + "aws-cdk-lib": "^2.99.1", + "constructs": "^10.1.113", + "module": "^1.2.5" }, "devDependencies": { "@qiwi/semantic-release-gh-pages-plugin": "^5.2.3", @@ -28,24 +32,24 @@ "semantic-release": "^19.0.5" }, "peerDependencies": { - "aws-cdk-lib": "^2.75.0", + "aws-cdk-lib": "^2.99.1", "constructs": "^10.1.113" } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.192", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.192.tgz", - "integrity": "sha512-kabo2WJR1lMh8ER2MYUdiN0inYIO8+K/ken6obNcdxIKf9d1llP8wlfU3JwNMv5IkNqoVWLPToZ8D2dCL2VAPQ==" + "version": "2.2.200", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.200.tgz", + "integrity": "sha512-Kf5J8DfJK4wZFWT2Myca0lhwke7LwHcHBo+4TvWOGJrFVVKVuuiLCkzPPRBQQVDj0Vtn2NBokZAz8pfMpAqAKg==" }, "node_modules/@aws-cdk/asset-kubectl-v20": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.165", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", - "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==" + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", + "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" }, "node_modules/@aws-cdk/aws-apigatewayv2-alpha": { "version": "2.47.0-alpha.0", @@ -201,6 +205,41 @@ "node": ">=0.1.90" } }, + "node_modules/@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "inBundle": true, + "dependencies": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "inBundle": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "inBundle": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/@jsii/check-node": { "version": "1.68.0", "resolved": "https://registry.npmjs.org/@jsii/check-node/-/check-node-1.68.0.tgz", @@ -867,6 +906,18 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==", + "inBundle": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -974,6 +1025,27 @@ "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", "dev": true }, + "node_modules/arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "inBundle": true, + "dependencies": { + "arr-flatten": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -998,6 +1070,15 @@ "node": ">=0.10.0" } }, + "node_modules/array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -1037,10 +1118,22 @@ "node": ">= 4.0.0" } }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "inBundle": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/aws-cdk-lib": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.84.0.tgz", - "integrity": "sha512-4zLtCLCIs5Ia4WRGqiXRwxSkpGaNy3NxMexO9qYHSuIYpqf4sHObzZ0tDHZCFL5Wkui3sCu3OLQWrRHrr93HvA==", + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.99.1.tgz", + "integrity": "sha512-mUhuT2JTy3VyX9o9IOSuy7UYDimFHGnmpASwTb4rD10Hksb1dTqqN2BsXU5kogHakYevBD3vwYc87rOVso4M7Q==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -1054,9 +1147,9 @@ "yaml" ], "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.177", - "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@aws-cdk/asset-awscli-v1": "^2.2.200", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.1.1", @@ -1064,7 +1157,7 @@ "jsonschema": "^1.4.1", "minimatch": "^3.1.2", "punycode": "^2.3.0", - "semver": "^7.5.1", + "semver": "^7.5.4", "table": "^6.8.1", "yaml": "1.10.2" }, @@ -1280,7 +1373,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.5.1", + "version": "7.5.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -1381,7 +1474,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "inBundle": true }, "node_modules/before-after-hook": { "version": "2.2.3", @@ -1408,7 +1501,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "inBundle": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1611,6 +1704,21 @@ "node": ">=0.8" } }, + "node_modules/clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", + "inBundle": true + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/codemaker": { "version": "1.68.0", "resolved": "https://registry.npmjs.org/codemaker/-/codemaker-1.68.0.tgz", @@ -1701,7 +1809,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "inBundle": true }, "node_modules/concat-stream": { "version": "1.6.2", @@ -1804,11 +1912,17 @@ "node": ">=10" } }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "inBundle": true + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "inBundle": true }, "node_modules/cosmiconfig": { "version": "7.0.1", @@ -1887,6 +2001,18 @@ "node": ">=8" } }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "inBundle": true, + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, "node_modules/date-format": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz", @@ -1910,7 +2036,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, + "inBundle": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -1924,6 +2050,41 @@ } } }, + "node_modules/debug-fabulous": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-0.0.4.tgz", + "integrity": "sha512-mmVKpY/O4UIl6ZDn5Owf8jEauO6uQiuF4Jz9iTuflSmvqNm6/64xARk/qCq5ZJxu141Ic2lCmL1TSMHIYoyiTw==", + "inBundle": true, + "dependencies": { + "debug": "2.X", + "lazy-debug-legacy": "0.0.X", + "object-assign": "4.1.0" + } + }, + "node_modules/debug-fabulous/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "inBundle": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug-fabulous/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "inBundle": true + }, + "node_modules/debug-fabulous/node_modules/object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha512-Lbc7GfN7XFaK30bzUN3cDYLOkT0dH05S0ax1QikylHUD9+Z9PRF3G1iYwX3kcz+6AlzTFGkUgMxz6l3aUwbwTA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decamelize": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", @@ -1967,6 +2128,15 @@ "node": ">=0.10.0" } }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "inBundle": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2058,7 +2228,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true, + "inBundle": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2106,6 +2276,18 @@ "readable-stream": "^2.0.2" } }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "inBundle": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "node_modules/email-addresses": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", @@ -2119,6 +2301,15 @@ "dev": true, "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "inBundle": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", @@ -2143,7 +2334,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, + "inBundle": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -2321,6 +2512,109 @@ "node": ">= 8" } }, + "node_modules/expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", + "inBundle": true, + "dependencies": { + "is-posix-bracket": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "inBundle": true, + "dependencies": { + "fill-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "inBundle": true, + "dependencies": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "inBundle": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "inBundle": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "inBundle": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "inBundle": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", + "inBundle": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2377,6 +2671,15 @@ "node": ">=0.8.0" } }, + "node_modules/filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -2460,6 +2763,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha512-ArRi5axuv66gEsyl3UuK80CzW7t56hem73YGNYxNWTGNKFJUadSb9Gu9SHijYEUi8ulQMf1bJomYNwSCPHhtTQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -2467,6 +2779,27 @@ "dev": true, "license": "ISC" }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "inBundle": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -2501,6 +2834,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -2535,7 +2869,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "inBundle": true }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -2779,6 +3113,49 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", + "inBundle": true, + "dependencies": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", + "inBundle": true, + "dependencies": { + "is-glob": "^2.0.0" + } + }, + "node_modules/glob-base/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "inBundle": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2806,32 +3183,251 @@ "glob": "*" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, + "node_modules/glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha512-piN8XVAO2sNxwVLokL4PswgJvK/uQ6+awwXUVRTGF+rRfgCZpn4hOqxiRuTEbU/k3qgKl0DACYQ/0Sge54UMQg==", + "inBundle": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "extend": "^3.0.0", + "glob": "^5.0.3", + "glob-parent": "^3.0.0", + "micromatch": "^2.3.7", + "ordered-read-streams": "^0.3.0", + "through2": "^0.6.0", + "to-absolute-glob": "^0.1.1", + "unique-stream": "^2.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.10" } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "node_modules/glob-stream/node_modules/braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", + "inBundle": true, + "dependencies": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "inBundle": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "inBundle": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "inBundle": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "inBundle": true + }, + "node_modules/glob-stream/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "inBundle": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", + "inBundle": true, + "dependencies": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/micromatch/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/micromatch/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "inBundle": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "inBundle": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "inBundle": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/glob-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "inBundle": true + }, + "node_modules/glob-stream/node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "inBundle": true, + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "inBundle": true, "license": "ISC" }, + "node_modules/gulp-sourcemaps": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz", + "integrity": "sha512-2NYnMpB67LJhc36sEv+hNY05UOy1lD9DPtLi+en4hbGH+085G9Zzh3cet2VEqrDlQrLk9Eho0MM9dZ3Z+dL0XA==", + "inBundle": true, + "dependencies": { + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "4.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "0.0.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom": "2.X", + "through2": "2.X", + "vinyl": "1.X" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "inBundle": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "inBundle": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -2866,7 +3462,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, + "inBundle": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -2874,6 +3470,27 @@ "node": ">= 0.4.0" } }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "inBundle": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -2945,7 +3562,7 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "inBundle": true }, "node_modules/http-basic": { "version": "8.1.3", @@ -3017,6 +3634,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, "engines": { "node": ">= 4" } @@ -3077,7 +3695,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, + "inBundle": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3087,7 +3705,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "inBundle": true }, "node_modules/ini": { "version": "1.3.8", @@ -3125,11 +3743,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "inBundle": true }, "node_modules/is-bigint": { "version": "1.0.4", @@ -3171,6 +3798,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "inBundle": true + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -3187,7 +3820,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, + "inBundle": true, "dependencies": { "has": "^1.0.3" }, @@ -3210,11 +3843,41 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", + "inBundle": true, + "dependencies": { + "is-primitive": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.10.0" } @@ -3322,6 +3985,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3413,6 +4094,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "inBundle": true + }, + "node_modules/is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha512-CvG8EtJZ8FyzVOGPzrDorzyN65W1Ld8BVnqshRCah6pFIsprGx3dKgFtjLn/Vw9kGqR4OlR84U7yhT9ZVTyWIQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3429,7 +4125,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "inBundle": true }, "node_modules/isexe": { "version": "2.0.0", @@ -3437,6 +4133,18 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "inBundle": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/issue-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", @@ -3647,6 +4355,12 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "inBundle": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -3657,6 +4371,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -3694,7 +4409,40 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-debug-legacy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz", + "integrity": "sha512-GFWaIBcBjxWWKI5OghwYEsPOR8JFh2xEcc3ZFV0ONYL0oHz0PHINJCfxJyztUq2XzcHncyO7fsRR550Gtfnk6g==", + "inBundle": true, + "peerDependencies": { + "debug": "*" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "inBundle": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "inBundle": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } @@ -3738,6 +4486,24 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "inBundle": true + }, + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "inBundle": true + }, + "node_modules/lodash.assigninwith": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assigninwith/-/lodash.assigninwith-4.2.0.tgz", + "integrity": "sha512-oYOjtZzQnecm7PJcxrDbL20OHv3tTtOQdRBSnlor6s0MO6VOFTOC+JyBIJUNUEzsBi1I0oslWtFAAG6QQbFIWQ==", + "inBundle": true + }, "node_modules/lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -3750,6 +4516,12 @@ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "inBundle": true + }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -3768,6 +4540,47 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "dev": true }, + "node_modules/lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==", + "inBundle": true + }, + "node_modules/lodash.rest": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/lodash.rest/-/lodash.rest-4.0.5.tgz", + "integrity": "sha512-hsypEpebNAt0hj1aX9isQqi2CIZoNS1lP6PSWhB3hcMnBivobYzPZRPYq4cr38+RtvrlxQTgaW+sIuHAhBoHrA==", + "inBundle": true + }, + "node_modules/lodash.template": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.2.4.tgz", + "integrity": "sha512-PmEQ9TtYbeYg6lNwJpSjkp4J4KttYLuKF1C6jeFBidyzbOFu0KvVnLicZBf0sGfScARwgOBqxnV/rWuaqRwang==", + "inBundle": true, + "dependencies": { + "lodash._reinterpolate": "~3.0.0", + "lodash.assigninwith": "^4.0.0", + "lodash.keys": "^4.0.0", + "lodash.rest": "^4.0.0", + "lodash.templatesettings": "^4.0.0", + "lodash.tostring": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "inBundle": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.tostring": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.4.tgz", + "integrity": "sha512-xWHJ0LY7cSz/C/4ghNNiYA1Ong0VLdzAzrjDHvOzN+eJHzDEHme2+k+w/9Pk8dtdwcASMUbxN1/mtj6mFI25Ng==", + "inBundle": true + }, "node_modules/lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -3795,6 +4608,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -3839,6 +4653,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-stream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.6.tgz", + "integrity": "sha512-RG9wAgznUY0foT30MMfnXh4jS0ObmOuxoGKe/ppYvM55RfquNdIvEEf6e+euczNVVzJIVbkgxg7GJBpYDhQ/Zg==", + "inBundle": true + }, "node_modules/marked": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", @@ -3883,6 +4703,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "inBundle": true + }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", @@ -4002,83 +4828,460 @@ "node": ">=10.0.0" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "inBundle": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "inBundle": true, + "license": "MIT" + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "inBundle": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/module/-/module-1.2.5.tgz", + "integrity": "sha512-Y+j9HcHf8V6YtNBkLbPmREAUi5xGbAdb9ycXpo2roABDPrJEzd79kmoH5Ib9lpxcNVsHWa1LhRZJcflUq2+N3w==", + "inBundle": true, + "dependencies": { + "chalk": "1.1.3", + "concat-stream": "1.5.1", + "lodash.template": "4.2.4", + "map-stream": "0.0.6", + "tildify": "1.2.0", + "vinyl-fs": "2.4.3", + "yargs": "4.6.0" + }, + "bin": { + "module": "dist/cli.js" + } + }, + "node_modules/module/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "inBundle": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "inBundle": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/module/node_modules/concat-stream": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", + "integrity": "sha512-eYF1Q4RxUUwq8ApyPD9ebWsYjVrJmTMLGzbGXv4qTZ5iP7FLm+oWN4x2XIzLEZ745xiwRM9DmIB0Ix1Nz8Epmg==", + "engines": [ + "node >= 0.8" + ], + "inBundle": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "node_modules/module/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "inBundle": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/module/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "inBundle": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "inBundle": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "inBundle": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "inBundle": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "inBundle": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "inBundle": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/pkg-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-1.1.3.tgz", + "integrity": "sha512-9hHgE5+Xai/ChrnahNP8Ke0VNF/s41IZIB/d24eMHEaRamdPg+wwlRm2lTb5wMvE8eTIKrYZsrxfuOwt3dpsIQ==", + "inBundle": true, + "dependencies": { + "find-up": "^1.0.0", + "load-json-file": "^1.1.0", + "object-assign": "^4.0.1", + "symbol": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==", + "inBundle": true + }, + "node_modules/module/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "inBundle": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "inBundle": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw==", + "inBundle": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/module/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "inBundle": true + }, + "node_modules/module/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "inBundle": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, + "node_modules/module/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "inBundle": true, "dependencies": { - "mime-db": "1.52.0" + "ansi-regex": "^2.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, + "node_modules/module/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "inBundle": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, + "node_modules/module/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "inBundle": true, "engines": { - "node": ">=4" + "node": ">=0.8.0" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/module/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "inBundle": true, "dependencies": { - "brace-expansion": "^1.1.7" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, + "node_modules/module/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "inBundle": true + }, + "node_modules/module/node_modules/yargs": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.6.0.tgz", + "integrity": "sha512-KmjJbWBkYiSRUChcOSa4rtBxDXf0j4ISz+tpeNa4LKIBllgKnkemJ3x4yo4Yydp3wPU4/xJTaKTLLZ8V7zhI7A==", + "inBundle": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" + "camelcase": "^2.0.1", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "pkg-conf": "^1.1.2", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1", + "string-width": "^1.0.1", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.0" + } + }, + "node_modules/module/node_modules/yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==", + "inBundle": true, + "dependencies": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" } }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true, + "node_modules/module/node_modules/yargs-parser/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "inBundle": true, "engines": { "node": ">=0.10.0" } @@ -4087,7 +5290,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, + "inBundle": true, "license": "MIT" }, "node_modules/neo-async": { @@ -4223,7 +5426,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, + "inBundle": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -4235,7 +5438,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, + "inBundle": true, "bin": { "semver": "bin/semver" } @@ -6945,11 +8148,20 @@ "inBundle": true, "license": "ISC" }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.10.0" } @@ -6990,11 +8202,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "inBundle": true, + "dependencies": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "inBundle": true, "dependencies": { "wrappy": "1" } @@ -7023,6 +8248,46 @@ "node": ">= 14.6.0" } }, + "node_modules/ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha512-xQvd8qvx9U1iYY9aVqPpoF5V9uaWJKV6ZGljkh/jkiNX0DiQsjbWvRumbh10QTMDE8DheaOEU8xi0szbrgjzcw==", + "inBundle": true, + "dependencies": { + "is-stream": "^1.0.1", + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "inBundle": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -7141,6 +8406,42 @@ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", "dev": true }, + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "inBundle": true, + "dependencies": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "inBundle": true, + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -7175,6 +8476,12 @@ "protocols": "^2.0.1" } }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "inBundle": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7188,7 +8495,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.10.0" } @@ -7206,7 +8513,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "inBundle": true }, "node_modules/path-type": { "version": "3.0.0", @@ -7257,7 +8564,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.10.0" } @@ -7266,7 +8573,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, + "inBundle": true, "dependencies": { "pinkie": "^2.0.0" }, @@ -7366,6 +8673,15 @@ "node": ">=8" } }, + "node_modules/preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -7385,7 +8701,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "inBundle": true }, "node_modules/promise": { "version": "8.2.0", @@ -7412,6 +8728,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7481,6 +8798,29 @@ "node": ">=8" } }, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "inBundle": true, + "dependencies": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -7582,7 +8922,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, + "inBundle": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7627,6 +8967,18 @@ "esprima": "~4.0.0" } }, + "node_modules/regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "inBundle": true, + "dependencies": { + "is-equal-shallow": "^0.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -7656,6 +9008,39 @@ "node": ">=6.0.0" } }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "inBundle": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "inBundle": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", + "inBundle": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7675,11 +9060,17 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "inBundle": true + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, + "inBundle": true, "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -7701,6 +9092,13 @@ "node": ">=8" } }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "inBundle": true + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -7769,7 +9167,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "inBundle": true }, "node_modules/safe-regex-test": { "version": "1.0.0", @@ -7843,6 +9241,7 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -8102,11 +9501,32 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "inBundle": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "inBundle": true + }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -8117,7 +9537,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, + "inBundle": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -8127,13 +9547,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "inBundle": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, + "inBundle": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -8143,7 +9563,7 @@ "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true + "inBundle": true }, "node_modules/spdx-license-list": { "version": "6.6.0", @@ -8203,6 +9623,12 @@ "readable-stream": "^2.0.2" } }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "inBundle": true + }, "node_modules/streamroller": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz", @@ -8257,7 +9683,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "inBundle": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -8335,19 +9761,44 @@ "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha512-7jfJB9YpI2Z0aH3wu10ZqitvYJaE0s5IzFuWE+0pbb4Q/armTloEUShymkDO47YSLnjAW52mlXT//hs9wXNNJQ==", + "inBundle": true, + "dependencies": { + "first-chunk-stream": "^1.0.0", + "strip-bom": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, + "node_modules/strip-bom-stream/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "inBundle": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/strip-final-newline": { @@ -8431,7 +9882,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, + "inBundle": true, "engines": { "node": ">= 0.4" }, @@ -8439,6 +9890,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/symbol/-/symbol-0.2.3.tgz", + "integrity": "sha512-IUW+ek7apEaW5bFhS6WpYoNtVpNTlNoqB/PH7YiMWQTxSPeXCzG4PILVakwXivJt3ZXWeO1fIJnUd/L9A/VeGA==", + "inBundle": true + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -8545,6 +10002,26 @@ "readable-stream": "3" } }, + "node_modules/through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha512-miwWajb1B80NvIVKXFPN/o7+vJc4jYUvnZCwvhicRAoTxdD9wbcjri70j+BenCrN/JXEPKDjhpw4iY7yiNsCGg==", + "inBundle": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2-filter/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "inBundle": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/through2/node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -8559,6 +10036,30 @@ "node": ">= 6" } }, + "node_modules/tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha512-Y9q1GaV/BO65Z9Yf4NOGMuwt3SGdptkZBnaaKfTQakrDyCLiuO1Kc5wxW4xLdsjzunRtqtOdhekiUFmZbklwYQ==", + "inBundle": true, + "dependencies": { + "os-homedir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha512-Vvl5x6zNf9iVG1QTWeknmWrKzZxaeKfIDRibrZCR3b2V/2NlFJuD2HV7P7AVjaKLZNqLPHqyr0jGrW0fTcxCPQ==", + "inBundle": true, + "dependencies": { + "extend-shallow": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8650,7 +10151,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true + "inBundle": true }, "node_modules/typescript": { "version": "3.9.10", @@ -8700,6 +10201,36 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "inBundle": true, + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/unique-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "inBundle": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/unique-stream/node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "inBundle": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -8722,6 +10253,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -8736,6 +10268,13 @@ "punycode": "^2.1.0" } }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "inBundle": true + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -8746,18 +10285,109 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "inBundle": true + }, + "node_modules/vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==", + "inBundle": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, + "inBundle": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", + "inBundle": true, + "dependencies": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/vinyl-fs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.3.tgz", + "integrity": "sha512-XxYoy5HpHrVd76tpnI5Vv/+b/xlEVusOmn4LjQ01s2JyiDMNaUm3Rb7Y3xTkRw+YoRBVoUrCs7EAIFNXyIlI8Q==", + "inBundle": true, + "dependencies": { + "duplexify": "^3.2.0", + "glob-stream": "^5.3.2", + "graceful-fs": "^4.0.0", + "gulp-sourcemaps": "^1.5.2", + "is-valid-glob": "^0.3.0", + "lazystream": "^1.0.0", + "lodash.isequal": "^4.0.0", + "merge-stream": "^1.0.0", + "mkdirp": "^0.5.0", + "object-assign": "^4.0.0", + "readable-stream": "^2.0.4", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^1.0.0", + "through2": "^2.0.0", + "through2-filter": "^2.0.0", + "vali-date": "^1.0.0", + "vinyl": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/vinyl-fs/node_modules/merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha512-e6RM36aegd4f+r8BZCcYXlO2P3H6xbUM6ktL2Xmf45GAOit9bI4z6/3VU7JwllVO1L7u0UDSg/EhzQ5lmMLolA==", + "inBundle": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/vinyl-fs/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "inBundle": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-fs/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "inBundle": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/vinyl/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "inBundle": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -8802,6 +10432,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==", + "inBundle": true, + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -8836,7 +10478,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "inBundle": true }, "node_modules/xmlbuilder": { "version": "15.1.1", @@ -8852,7 +10494,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, + "inBundle": true, "engines": { "node": ">=0.4" } @@ -8871,6 +10513,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC" }, "node_modules/yaml": { @@ -8914,19 +10557,19 @@ }, "dependencies": { "@aws-cdk/asset-awscli-v1": { - "version": "2.2.192", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.192.tgz", - "integrity": "sha512-kabo2WJR1lMh8ER2MYUdiN0inYIO8+K/ken6obNcdxIKf9d1llP8wlfU3JwNMv5IkNqoVWLPToZ8D2dCL2VAPQ==" + "version": "2.2.200", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.200.tgz", + "integrity": "sha512-Kf5J8DfJK4wZFWT2Myca0lhwke7LwHcHBo+4TvWOGJrFVVKVuuiLCkzPPRBQQVDj0Vtn2NBokZAz8pfMpAqAKg==" }, "@aws-cdk/asset-kubectl-v20": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" }, - "@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.165", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", - "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==" + "@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", + "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" }, "@aws-cdk/aws-apigatewayv2-alpha": { "version": "2.47.0-alpha.0", @@ -9038,6 +10681,34 @@ "dev": true, "optional": true }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "@jsii/check-node": { "version": "1.68.0", "resolved": "https://registry.npmjs.org/@jsii/check-node/-/check-node-1.68.0.tgz", @@ -9588,6 +11259,11 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==" + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -9665,6 +11341,19 @@ "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", "dev": true }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -9683,6 +11372,11 @@ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==" + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -9716,14 +11410,19 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, "aws-cdk-lib": { - "version": "2.84.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.84.0.tgz", - "integrity": "sha512-4zLtCLCIs5Ia4WRGqiXRwxSkpGaNy3NxMexO9qYHSuIYpqf4sHObzZ0tDHZCFL5Wkui3sCu3OLQWrRHrr93HvA==", + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.99.1.tgz", + "integrity": "sha512-mUhuT2JTy3VyX9o9IOSuy7UYDimFHGnmpASwTb4rD10Hksb1dTqqN2BsXU5kogHakYevBD3vwYc87rOVso4M7Q==", "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.177", - "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@aws-cdk/asset-awscli-v1": "^2.2.200", + "@aws-cdk/asset-kubectl-v20": "^2.1.2", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.1.1", @@ -9731,7 +11430,7 @@ "jsonschema": "^1.4.1", "minimatch": "^3.1.2", "punycode": "^2.3.0", - "semver": "^7.5.1", + "semver": "^7.5.4", "table": "^6.8.1", "yaml": "1.10.2" }, @@ -9868,7 +11567,7 @@ "bundled": true }, "semver": { - "version": "7.5.1", + "version": "7.5.4", "bundled": true, "requires": { "lru-cache": "^6.0.0" @@ -9934,8 +11633,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "before-after-hook": { "version": "2.2.3", @@ -9959,7 +11657,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10102,6 +11799,16 @@ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" + }, "codemaker": { "version": "1.68.0", "resolved": "https://registry.npmjs.org/codemaker/-/codemaker-1.68.0.tgz", @@ -10174,8 +11881,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { "version": "1.6.2", @@ -10253,11 +11959,15 @@ "through2": "^4.0.0" } }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "cosmiconfig": { "version": "7.0.1", @@ -10319,6 +12029,17 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, "date-format": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz", @@ -10335,11 +12056,40 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } }, + "debug-fabulous": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-0.0.4.tgz", + "integrity": "sha512-mmVKpY/O4UIl6ZDn5Owf8jEauO6uQiuF4Jz9iTuflSmvqNm6/64xARk/qCq5ZJxu141Ic2lCmL1TSMHIYoyiTw==", + "requires": { + "debug": "2.X", + "lazy-debug-legacy": "0.0.X", + "object-assign": "4.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha512-Lbc7GfN7XFaK30bzUN3cDYLOkT0dH05S0ax1QikylHUD9+Z9PRF3G1iYwX3kcz+6AlzTFGkUgMxz6l3aUwbwTA==" + } + } + }, "decamelize": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", @@ -10370,6 +12120,11 @@ } } }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -10434,8 +12189,7 @@ "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==" }, "dir-glob": { "version": "3.0.1", @@ -10472,6 +12226,17 @@ "readable-stream": "^2.0.2" } }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "email-addresses": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", @@ -10484,6 +12249,14 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", @@ -10505,7 +12278,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -10631,6 +12403,80 @@ } } }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", + "requires": { + "is-extglob": "^1.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10676,6 +12522,11 @@ } } }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==" + }, "filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -10732,12 +12583,30 @@ "semver-regex": "^3.1.2" } }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha512-ArRi5axuv66gEsyl3UuK80CzW7t56hem73YGNYxNWTGNKFJUadSb9Gu9SHijYEUi8ulQMf1bJomYNwSCPHhtTQ==" + }, "flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "requires": { + "for-in": "^1.0.1" + } + }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -10758,6 +12627,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -10779,9 +12649,8 @@ }, "function-bind": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "function.prototype.name": { "version": "1.1.5", @@ -10973,6 +12842,38 @@ "path-is-absolute": "^1.0.0" } }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -10991,6 +12892,143 @@ "@types/glob": "*" } }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha512-piN8XVAO2sNxwVLokL4PswgJvK/uQ6+awwXUVRTGF+rRfgCZpn4hOqxiRuTEbU/k3qgKl0DACYQ/0Sge54UMQg==", + "requires": { + "extend": "^3.0.0", + "glob": "^5.0.3", + "glob-parent": "^3.0.0", + "micromatch": "^2.3.7", + "ordered-read-streams": "^0.3.0", + "through2": "^0.6.0", + "to-absolute-glob": "^0.1.1", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "requires": { + "is-extglob": "^2.1.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + } + } + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -11010,6 +13048,43 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "gulp-sourcemaps": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz", + "integrity": "sha512-2NYnMpB67LJhc36sEv+hNY05UOy1lD9DPtLi+en4hbGH+085G9Zzh3cet2VEqrDlQrLk9Eho0MM9dZ3Z+dL0XA==", + "requires": { + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "4.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "0.0.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom": "2.X", + "through2": "2.X", + "vinyl": "1.X" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -11033,11 +13108,25 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + } + } + }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -11083,8 +13172,7 @@ "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "http-basic": { "version": "8.1.3", @@ -11145,7 +13233,8 @@ "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true }, "ignore-by-default": { "version": "1.0.1", @@ -11187,7 +13276,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -11196,8 +13284,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.8", @@ -11226,11 +13313,15 @@ "p-is-promise": "^3.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-bigint": { "version": "1.0.4", @@ -11260,6 +13351,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -11270,7 +13366,6 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -11284,11 +13379,28 @@ "has-tostringtag": "^1.0.0" } }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -11356,6 +13468,16 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==" + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -11417,6 +13539,16 @@ "text-extensions": "^1.0.0" } }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha512-CvG8EtJZ8FyzVOGPzrDorzyN65W1Ld8BVnqshRCah6pFIsprGx3dKgFtjLn/Vw9kGqR4OlR84U7yhT9ZVTyWIQ==" + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -11429,8 +13561,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isexe": { "version": "2.0.0", @@ -11438,6 +13569,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "requires": { + "isarray": "1.0.0" + } + }, "issue-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", @@ -11609,6 +13748,11 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -11619,6 +13763,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" @@ -11643,8 +13788,29 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "lazy-debug-legacy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz", + "integrity": "sha512-GFWaIBcBjxWWKI5OghwYEsPOR8JFh2xEcc3ZFV0ONYL0oHz0PHINJCfxJyztUq2XzcHncyO7fsRR550Gtfnk6g==", + "requires": {} + }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "requires": { + "invert-kv": "^1.0.0" + } }, "lines-and-columns": { "version": "1.2.4", @@ -11679,6 +13845,21 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==" + }, + "lodash.assigninwith": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assigninwith/-/lodash.assigninwith-4.2.0.tgz", + "integrity": "sha512-oYOjtZzQnecm7PJcxrDbL20OHv3tTtOQdRBSnlor6s0MO6VOFTOC+JyBIJUNUEzsBi1I0oslWtFAAG6QQbFIWQ==" + }, "lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -11691,6 +13872,11 @@ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -11709,6 +13895,42 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "dev": true }, + "lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==" + }, + "lodash.rest": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/lodash.rest/-/lodash.rest-4.0.5.tgz", + "integrity": "sha512-hsypEpebNAt0hj1aX9isQqi2CIZoNS1lP6PSWhB3hcMnBivobYzPZRPYq4cr38+RtvrlxQTgaW+sIuHAhBoHrA==" + }, + "lodash.template": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.2.4.tgz", + "integrity": "sha512-PmEQ9TtYbeYg6lNwJpSjkp4J4KttYLuKF1C6jeFBidyzbOFu0KvVnLicZBf0sGfScARwgOBqxnV/rWuaqRwang==", + "requires": { + "lodash._reinterpolate": "~3.0.0", + "lodash.assigninwith": "^4.0.0", + "lodash.keys": "^4.0.0", + "lodash.rest": "^4.0.0", + "lodash.templatesettings": "^4.0.0", + "lodash.tostring": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.tostring": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.4.tgz", + "integrity": "sha512-xWHJ0LY7cSz/C/4ghNNiYA1Ong0VLdzAzrjDHvOzN+eJHzDEHme2+k+w/9Pk8dtdwcASMUbxN1/mtj6mFI25Ng==" + }, "lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -11732,6 +13954,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -11759,6 +13982,11 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, + "map-stream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.6.tgz", + "integrity": "sha512-RG9wAgznUY0foT30MMfnXh4jS0ObmOuxoGKe/ppYvM55RfquNdIvEEf6e+euczNVVzJIVbkgxg7GJBpYDhQ/Zg==" + }, "marked": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", @@ -11787,6 +14015,11 @@ } } }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" + }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", @@ -11906,7 +14139,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11914,8 +14146,7 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minimist-options": { "version": "4.1.0", @@ -11928,17 +14159,290 @@ "kind-of": "^6.0.3" } }, - "modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true - }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "module": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/module/-/module-1.2.5.tgz", + "integrity": "sha512-Y+j9HcHf8V6YtNBkLbPmREAUi5xGbAdb9ycXpo2roABDPrJEzd79kmoH5Ib9lpxcNVsHWa1LhRZJcflUq2+N3w==", + "requires": { + "chalk": "1.1.3", + "concat-stream": "1.5.1", + "lodash.template": "4.2.4", + "map-stream": "0.0.6", + "tildify": "1.2.0", + "vinyl-fs": "2.4.3", + "yargs": "4.6.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "concat-stream": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.1.tgz", + "integrity": "sha512-eYF1Q4RxUUwq8ApyPD9ebWsYjVrJmTMLGzbGXv4qTZ5iP7FLm+oWN4x2XIzLEZ745xiwRM9DmIB0Ix1Nz8Epmg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pkg-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-1.1.3.tgz", + "integrity": "sha512-9hHgE5+Xai/ChrnahNP8Ke0VNF/s41IZIB/d24eMHEaRamdPg+wwlRm2lTb5wMvE8eTIKrYZsrxfuOwt3dpsIQ==", + "requires": { + "find-up": "^1.0.0", + "load-json-file": "^1.1.0", + "object-assign": "^4.0.1", + "symbol": "^0.2.1" + } + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha512-TXcFfb63BQe1+ySzsHZI/5v1aJPCShfqvWJ64ayNImXMsN1Cd0YGk/wm8KB7/OeessgPc9QvS9Zou8QTkFzsLw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + }, + "yargs": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.6.0.tgz", + "integrity": "sha512-KmjJbWBkYiSRUChcOSa4rtBxDXf0j4ISz+tpeNa4LKIBllgKnkemJ3x4yo4Yydp3wPU4/xJTaKTLLZ8V7zhI7A==", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "pkg-conf": "^1.1.2", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1", + "string-width": "^1.0.1", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.0" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==" + } + } + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "neo-async": { "version": "2.6.2", @@ -12039,7 +14543,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -12050,8 +14553,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, @@ -13948,11 +16450,15 @@ } } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.12.2", @@ -13978,11 +16484,19 @@ "object-keys": "^1.1.1" } }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "requires": { "wrappy": "1" } @@ -14002,6 +16516,35 @@ "integrity": "sha512-U5bHVg5nC4OHPNd6ZDSotQ1ccRA+4Rb+bWcY+IJuf4imtO3wrJeSDlKVgMf92mwdVDHMZJ3QKJgElCASdr9Bgw==", "dev": true }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha512-xQvd8qvx9U1iYY9aVqPpoF5V9uaWJKV6ZGljkh/jkiNX0DiQsjbWvRumbh10QTMDE8DheaOEU8xi0szbrgjzcw==", + "requires": { + "is-stream": "^1.0.1", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "requires": { + "lcid": "^1.0.0" + } + }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -14084,6 +16627,32 @@ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", "dev": true }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -14115,6 +16684,11 @@ "protocols": "^2.0.1" } }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==" + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -14124,8 +16698,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "2.0.1", @@ -14136,8 +16709,7 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-type": { "version": "3.0.0", @@ -14169,14 +16741,12 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -14251,6 +16821,11 @@ "find-up": "^4.0.0" } }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==" + }, "prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -14260,8 +16835,7 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise": { "version": "8.2.0", @@ -14287,7 +16861,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "q": { "version": "1.5.1", @@ -14326,6 +16901,23 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -14404,7 +16996,6 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -14443,6 +17034,14 @@ "esprima": "~4.0.0" } }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -14463,6 +17062,26 @@ "rc": "1.2.8" } }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==" + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14475,11 +17094,15 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, "requires": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -14492,6 +17115,11 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" + }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -14531,8 +17159,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex-test": { "version": "1.0.0", @@ -14596,6 +17223,7 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -14793,8 +17421,24 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" }, "spawn-error-forwarder": { "version": "1.0.0", @@ -14806,7 +17450,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -14815,14 +17458,12 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -14831,8 +17472,7 @@ "spdx-license-ids": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" }, "spdx-license-list": { "version": "6.6.0", @@ -14881,6 +17521,11 @@ "readable-stream": "^2.0.2" } }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "streamroller": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz", @@ -14924,7 +17569,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14994,6 +17638,25 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha512-7jfJB9YpI2Z0aH3wu10ZqitvYJaE0s5IzFuWE+0pbb4Q/armTloEUShymkDO47YSLnjAW52mlXT//hs9wXNNJQ==", + "requires": { + "first-chunk-stream": "^1.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -15054,8 +17717,12 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "symbol": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/symbol/-/symbol-0.2.3.tgz", + "integrity": "sha512-IUW+ek7apEaW5bFhS6WpYoNtVpNTlNoqB/PH7YiMWQTxSPeXCzG4PILVakwXivJt3ZXWeO1fIJnUd/L9A/VeGA==" }, "temp-dir": { "version": "2.0.0", @@ -15156,6 +17823,42 @@ } } }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha512-miwWajb1B80NvIVKXFPN/o7+vJc4jYUvnZCwvhicRAoTxdD9wbcjri70j+BenCrN/JXEPKDjhpw4iY7yiNsCGg==", + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha512-Y9q1GaV/BO65Z9Yf4NOGMuwt3SGdptkZBnaaKfTQakrDyCLiuO1Kc5wxW4xLdsjzunRtqtOdhekiUFmZbklwYQ==", + "requires": { + "os-homedir": "^1.0.0" + } + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha512-Vvl5x6zNf9iVG1QTWeknmWrKzZxaeKfIDRibrZCR3b2V/2NlFJuD2HV7P7AVjaKLZNqLPHqyr0jGrW0fTcxCPQ==", + "requires": { + "extend-shallow": "^2.0.1" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -15224,8 +17927,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "typescript": { "version": "3.9.10", @@ -15258,6 +17960,35 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + } + } + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -15276,7 +18007,8 @@ "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true }, "uri-js": { "version": "4.4.1", @@ -15287,6 +18019,11 @@ "punycode": "^2.1.0" } }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" + }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -15296,19 +18033,90 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==" }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + } + } + }, + "vinyl-fs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.3.tgz", + "integrity": "sha512-XxYoy5HpHrVd76tpnI5Vv/+b/xlEVusOmn4LjQ01s2JyiDMNaUm3Rb7Y3xTkRw+YoRBVoUrCs7EAIFNXyIlI8Q==", + "requires": { + "duplexify": "^3.2.0", + "glob-stream": "^5.3.2", + "graceful-fs": "^4.0.0", + "gulp-sourcemaps": "^1.5.2", + "is-valid-glob": "^0.3.0", + "lazystream": "^1.0.0", + "lodash.isequal": "^4.0.0", + "merge-stream": "^1.0.0", + "mkdirp": "^0.5.0", + "object-assign": "^4.0.0", + "readable-stream": "^2.0.4", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^1.0.0", + "through2": "^2.0.0", + "through2-filter": "^2.0.0", + "vali-date": "^1.0.0", + "vinyl": "^1.0.0" + }, + "dependencies": { + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha512-e6RM36aegd4f+r8BZCcYXlO2P3H6xbUM6ktL2Xmf45GAOit9bI4z6/3VU7JwllVO1L7u0UDSg/EhzQ5lmMLolA==", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -15347,6 +18155,11 @@ "is-symbol": "^1.0.3" } }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==" + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -15373,8 +18186,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "xmlbuilder": { "version": "15.1.1", @@ -15385,8 +18197,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "5.0.8", @@ -15397,7 +18208,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 2fc7c64..2f257d0 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,15 @@ "dependencies": { "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.47.0-alpha.0", "@aws-cdk/aws-lambda-python-alpha": "^2.47.0-alpha.0", - "aws-cdk-lib": "^2.75.0", - "constructs": "^10.1.113" + "aws-cdk-lib": "^2.99.1", + "constructs": "^10.1.113", + "module": "^1.2.5" }, + "bundledDependencies": [ + "module" + ], "peerDependencies": { - "aws-cdk-lib": "^2.75.0", + "aws-cdk-lib": "^2.99.1", "constructs": "^10.1.113" }, "release": { From 385ab1bf9565a9e56a33fa6559f2500f83dbf969 Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 10 Oct 2023 17:40:06 +0900 Subject: [PATCH 12/30] fix a couple bugs found in the first changes --- lib/bootstrapper/index.ts | 22 +++++++++++++++++++ lib/bootstrapper/runtime/Dockerfile | 9 ++++++-- lib/ingestor-api/index.ts | 4 ++-- lib/ingestor-api/runtime/Dockerfile | 15 ++++++------- lib/stac-api/index.ts | 6 ++--- lib/stac-api/runtime/Dockerfile | 12 ++++------ lib/tipg-api/runtime/Dockerfile | 6 ++--- lib/tipg-api/runtime/requirements.txt | 2 -- lib/tipg-api/runtime/src/handler.py | 2 +- lib/titiler-pgstac-api/index.ts | 1 + lib/titiler-pgstac-api/runtime/Dockerfile | 3 ++- lib/titiler-pgstac-api/runtime/src/handler.py | 2 +- 12 files changed, 53 insertions(+), 31 deletions(-) delete mode 100644 lib/tipg-api/runtime/requirements.txt diff --git a/lib/bootstrapper/index.ts b/lib/bootstrapper/index.ts index 958b837..29fb1a7 100644 --- a/lib/bootstrapper/index.ts +++ b/lib/bootstrapper/index.ts @@ -12,6 +12,8 @@ import { import { Construct } from "constructs"; import { CustomLambdaFunctionOptions } from "../utils"; +const DEFAULT_PGSTAC_VERSION = "0.6.13"; + function hasVpc( instance: aws_rds.DatabaseInstance | aws_rds.IDatabaseInstance ): instance is aws_rds.DatabaseInstance { @@ -27,6 +29,15 @@ export class BootstrapPgStac extends Construct { constructor(scope: Construct, id: string, props: BootstrapPgStacProps) { super(scope, id); + // if `lambdaAssetCode` is provided, `pgstacVersion` must be provided, otherwise throw an error + if (props.lambdaAssetCode && !props.pgstacVersion) { + throw new Error( + "If `lambdaAssetCode` is provided, `pgstacVersion` must be provided as well." + ); + } + + const pgstacVersion = props.pgstacVersion ?? DEFAULT_PGSTAC_VERSION; + const handler = new aws_lambda.Function(this, "lambda", { ...props.lambdaFunctionOptions ?? { runtime: aws_lambda.Runtime.PYTHON_3_8, @@ -37,6 +48,7 @@ export class BootstrapPgStac extends Construct { }, code: props.lambdaAssetCode ?? aws_lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", + buildArgs: {PGSTAC_VERSION: pgstacVersion, PYTHON_VERSION: "3.8"} }), vpc: hasVpc(props.database) ? props.database.vpc : props.vpc, }); @@ -75,6 +87,7 @@ export class BootstrapPgStac extends Construct { new CustomResource(this, "bootstrapper", { serviceToken: handler.functionArn, properties: { + pgstac_version: pgstacVersion, conn_secret_arn: props.dbSecret.secretArn, new_user_secret_arn: this.secret.secretArn, }, @@ -142,4 +155,13 @@ export interface BootstrapPgStacProps { * @default default runtime defined in this repository */ readonly lambdaAssetCode?: aws_lambda.AssetCode; + + /** + * pgSTAC database version to be installed. If a custom `lambdaAssetCode` + * is provided, must be provided and match the version of pgSTAC installed + * in the lambda function. + * + * @default 0.6.13 + */ + readonly pgstacVersion?: string; } diff --git a/lib/bootstrapper/runtime/Dockerfile b/lib/bootstrapper/runtime/Dockerfile index f9e7ace..d8d01de 100644 --- a/lib/bootstrapper/runtime/Dockerfile +++ b/lib/bootstrapper/runtime/Dockerfile @@ -1,8 +1,13 @@ -FROM lambci/lambda:build-python3.8 +ARG PYTHON_VERSION +FROM lambci/lambda:build-python${PYTHON_VERSION} + +ARG PGSTAC_VERSION +RUN echo "PGSTAC_VERSION: ${PGSTAC_VERSION}" +RUN echo "PYTHON_VERSION: ${PYTHON_VERSION}" WORKDIR /tmp -RUN pip install httpx psycopg[binary,pool] pypgstac==0.6.13 -t /asset +RUN pip install httpx psycopg[binary,pool] pypgstac==${PGSTAC_VERSION} -t /asset COPY runtime/handler.py /asset/handler.py diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index 1b1fe66..2c9e5d9 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -120,7 +120,7 @@ export class StacIngestor extends Construct { const handler = new lambda.Function(this, "api-handler", { ...props.lambdaFunctionOptions ?? { runtime: lambda.Runtime.PYTHON_3_9, - handler: "handler.handler", + handler: "src.handler.handler", memorySize: 2048, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30) @@ -166,7 +166,7 @@ export class StacIngestor extends Construct { const handler = new lambda.Function(this, "stac-ingestor", { ...props.lambdaFunctionOptions ?? { runtime: lambda.Runtime.PYTHON_3_9, - handler: "handler.handler", + handler: "src.ingestor.handler", memorySize: 2048, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(180) diff --git a/lib/ingestor-api/runtime/Dockerfile b/lib/ingestor-api/runtime/Dockerfile index 604f813..02eb7da 100644 --- a/lib/ingestor-api/runtime/Dockerfile +++ b/lib/ingestor-api/runtime/Dockerfile @@ -1,17 +1,16 @@ -FROM public.ecr.aws/lambda/python:3.10 +ARG PYTHON_VERSION +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} WORKDIR /tmp + +RUN yum install -y git + RUN python -m pip install pip -U COPY runtime/requirements.txt requirements.txt RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic -# Reduce package size and remove useless files -RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; -RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf - -COPY runtime/src/*.py /asset/ +RUN mkdir -p /asset/src +COPY runtime/src/*.py /asset/src/ CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index 6bcc1af..34a03b3 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -22,15 +22,15 @@ export class PgStacApiLambda extends Construct { this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_8, - handler: "handler.handler", + runtime: lambda.Runtime.PYTHON_3_10, + handler: "src.handler.handler", memorySize: 8192, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30) }, code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.8' }, + buildArgs: { PYTHON_VERSION: '3.10' }, }), vpc: props.vpc, vpcSubnets: props.subnetSelection, diff --git a/lib/stac-api/runtime/Dockerfile b/lib/stac-api/runtime/Dockerfile index 604f813..3f8e0ac 100644 --- a/lib/stac-api/runtime/Dockerfile +++ b/lib/stac-api/runtime/Dockerfile @@ -1,4 +1,5 @@ -FROM public.ecr.aws/lambda/python:3.10 +ARG PYTHON_VERSION +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} WORKDIR /tmp RUN python -m pip install pip -U @@ -6,12 +7,7 @@ RUN python -m pip install pip -U COPY runtime/requirements.txt requirements.txt RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic -# Reduce package size and remove useless files -RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; -RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf - -COPY runtime/src/*.py /asset/ +RUN mkdir -p /asset/src +COPY runtime/src/*.py /asset/src/ CMD ["echo", "hello world"] \ No newline at end of file diff --git a/lib/tipg-api/runtime/Dockerfile b/lib/tipg-api/runtime/Dockerfile index 2a1ef11..25f5bc2 100644 --- a/lib/tipg-api/runtime/Dockerfile +++ b/lib/tipg-api/runtime/Dockerfile @@ -1,10 +1,10 @@ -FROM --platform=linux/amd64 public.ecr.aws/lambda/python:3.10 +ARG PYTHON_VERSION +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} WORKDIR /tmp RUN python -m pip install pip -U -COPY runtime/requirements.txt requirements.txt -RUN python -m pip install -r requirements.txt "mangum>=0.14,<0.15" -t /asset --no-binary pydantic +RUN python -m pip install tipg==0.3.1 "mangum>=0.14,<0.15" -t /asset --no-binary pydantic # Reduce package size and remove useless files RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; diff --git a/lib/tipg-api/runtime/requirements.txt b/lib/tipg-api/runtime/requirements.txt deleted file mode 100644 index fde7382..0000000 --- a/lib/tipg-api/runtime/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -tipg==0.3.1 -mangum==0.15.1 diff --git a/lib/tipg-api/runtime/src/handler.py b/lib/tipg-api/runtime/src/handler.py index 40e358a..cdc480d 100644 --- a/lib/tipg-api/runtime/src/handler.py +++ b/lib/tipg-api/runtime/src/handler.py @@ -5,7 +5,7 @@ import asyncio import os from mangum import Mangum -from src.utils import load_pgstac_secret +from utils import load_pgstac_secret load_pgstac_secret(os.environ["PGSTAC_SECRET_ARN"]) # required for the below imports diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index 7d26ead..190565c 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -48,6 +48,7 @@ import { CustomLambdaFunctionOptions } from "../utils"; }, code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.10' } }), vpc: props.vpc, vpcSubnets: props.subnetSelection, diff --git a/lib/titiler-pgstac-api/runtime/Dockerfile b/lib/titiler-pgstac-api/runtime/Dockerfile index 1fba5ce..e5cf74e 100644 --- a/lib/titiler-pgstac-api/runtime/Dockerfile +++ b/lib/titiler-pgstac-api/runtime/Dockerfile @@ -1,4 +1,5 @@ -FROM --platform=linux/amd64 public.ecr.aws/lambda/python:3.10 +ARG PYTHON_VERSION +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} WORKDIR /tmp RUN python -m pip install pip -U diff --git a/lib/titiler-pgstac-api/runtime/src/handler.py b/lib/titiler-pgstac-api/runtime/src/handler.py index 23e8ed2..2b5a987 100644 --- a/lib/titiler-pgstac-api/runtime/src/handler.py +++ b/lib/titiler-pgstac-api/runtime/src/handler.py @@ -5,7 +5,7 @@ import asyncio import os from mangum import Mangum -from src.utils import get_secret_dict +from utils import get_secret_dict pgstac_secret_arn = os.environ["PGSTAC_SECRET_ARN"] From dbcada8cf65e3264a71144c660e9a688db9f1e59 Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 12 Oct 2023 14:19:37 +0900 Subject: [PATCH 13/30] avoid maintaining custom interfaces for configurable lambda properties. Allow the user to provide anything and let the CDK method raise error and overwrite values defined within our construct. Make this clear in the documentation --- lib/bootstrapper/index.ts | 74 ++++++++++++++++----------------- lib/ingestor-api/index.ts | 73 ++++++++++++++------------------ lib/stac-api/index.ts | 30 ++++++------- lib/tipg-api/index.ts | 34 +++++++-------- lib/titiler-pgstac-api/index.ts | 30 ++++++------- lib/utils/index.ts | 31 +------------- 6 files changed, 110 insertions(+), 162 deletions(-) diff --git a/lib/bootstrapper/index.ts b/lib/bootstrapper/index.ts index 29fb1a7..767c64e 100644 --- a/lib/bootstrapper/index.ts +++ b/lib/bootstrapper/index.ts @@ -10,7 +10,7 @@ import { RemovalPolicy, } from "aws-cdk-lib"; import { Construct } from "constructs"; -import { CustomLambdaFunctionOptions } from "../utils"; +import { CustomLambdaFunctionProps } from "../utils"; const DEFAULT_PGSTAC_VERSION = "0.6.13"; @@ -29,27 +29,20 @@ export class BootstrapPgStac extends Construct { constructor(scope: Construct, id: string, props: BootstrapPgStacProps) { super(scope, id); - // if `lambdaAssetCode` is provided, `pgstacVersion` must be provided, otherwise throw an error - if (props.lambdaAssetCode && !props.pgstacVersion) { - throw new Error( - "If `lambdaAssetCode` is provided, `pgstacVersion` must be provided as well." - ); - } - - const pgstacVersion = props.pgstacVersion ?? DEFAULT_PGSTAC_VERSION; - const handler = new aws_lambda.Function(this, "lambda", { - ...props.lambdaFunctionOptions ?? { - runtime: aws_lambda.Runtime.PYTHON_3_8, - handler: "handler.handler", - memorySize: 128, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.minutes(2) - }, - code: props.lambdaAssetCode ?? aws_lambda.Code.fromDockerBuild(__dirname, { + // defaults for configurable properties + runtime: aws_lambda.Runtime.PYTHON_3_8, + handler: "handler.handler", + memorySize: 128, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.minutes(2), + code: aws_lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: {PGSTAC_VERSION: pgstacVersion, PYTHON_VERSION: "3.8"} + buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.8"} }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user vpc: hasVpc(props.database) ? props.database.vpc : props.vpc, }); @@ -83,14 +76,25 @@ export class BootstrapPgStac extends Construct { // connect to database props.database.connections.allowFrom(handler, aws_ec2.Port.tcp(5432)); + let customResourceProperties : { [key: string]: any} = {}; + + // if customResourceProperties are provided, fill in the values. + if (props.customResourceProperties) { + Object.assign(customResourceProperties, props.customResourceProperties); + } + + // update properties + customResourceProperties["conn_secret_arn"] = props.dbSecret.secretArn; + customResourceProperties["new_user_secret_arn"] = this.secret.secretArn; + + // if props.lambdaFunctionOptions doesn't have 'code' defined, update pgstac_version (needed for default runtime) + if (!props.lambdaFunctionOptions?.code) { + customResourceProperties["pgstac_version"] = DEFAULT_PGSTAC_VERSION; + } // this.connections = props.database.connections; new CustomResource(this, "bootstrapper", { serviceToken: handler.functionArn, - properties: { - pgstac_version: pgstacVersion, - conn_secret_arn: props.dbSecret.secretArn, - new_user_secret_arn: this.secret.secretArn, - }, + properties: customResourceProperties, removalPolicy: RemovalPolicy.RETAIN, // This retains the custom resource (which doesn't really exist), not the database }); } @@ -144,24 +148,20 @@ export interface BootstrapPgStacProps { readonly secretsPrefix?: string; /** - * Optional settings for the lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * * @default - defined in the construct. */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; /** - * Optional lambda asset code - * @default default runtime defined in this repository + * Lambda function Custom Resource properties. A custom resource property is going to be created + * to trigger the boostrapping lambda function. This parameter allows the user to specify additional properties + * on top of the defaults ones. + * */ - readonly lambdaAssetCode?: aws_lambda.AssetCode; + readonly customResourceProperties?: { + [key: string]: any; +} - /** - * pgSTAC database version to be installed. If a custom `lambdaAssetCode` - * is provided, must be provided and match the version of pgSTAC installed - * in the lambda function. - * - * @default 0.6.13 - */ - readonly pgstacVersion?: string; } diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index 2c9e5d9..be6e3d0 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -13,7 +13,7 @@ import { Stack, } from "aws-cdk-lib"; import { Construct } from "constructs"; -import { CustomLambdaFunctionOptions } from "../utils"; +import { CustomLambdaFunctionProps } from "../utils"; export class StacIngestor extends Construct { table: dynamodb.Table; @@ -56,7 +56,6 @@ export class StacIngestor extends Construct { dbVpc: props.vpc, dbSecurityGroup: props.stacDbSecurityGroup, subnetSelection: props.subnetSelection, - lambdaAssetCode: props.apiLambdaAssetCode, lambdaFunctionOptions: props.apiLambdaFunctionOptions, }); @@ -75,7 +74,6 @@ export class StacIngestor extends Construct { dbVpc: props.vpc, dbSecurityGroup: props.stacDbSecurityGroup, subnetSelection: props.subnetSelection, - lambdaAssetCode: props.ingestorLambdaAssetCode, lambdaFunctionOptions: props.ingestorLambdaFunctionOptions }); @@ -113,22 +111,23 @@ export class StacIngestor extends Construct { dbVpc: ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; subnetSelection: ec2.SubnetSelection - lambdaFunctionOptions?: CustomLambdaFunctionOptions; - lambdaAssetCode?: lambda.AssetCode; + lambdaFunctionOptions?: CustomLambdaFunctionProps; }): lambda.Function { const handler = new lambda.Function(this, "api-handler", { - ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_9, - handler: "src.handler.handler", - memorySize: 2048, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30) - }, - code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + // defaults for configurable properties + runtime: lambda.Runtime.PYTHON_3_9, + handler: "src.handler.handler", + memorySize: 2048, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code:lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.9' }, }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.dbVpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -158,23 +157,25 @@ export class StacIngestor extends Construct { dbVpc: ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; subnetSelection: ec2.SubnetSelection; - lambdaFunctionOptions?: CustomLambdaFunctionOptions; - lambdaAssetCode?: lambda.AssetCode; + lambdaFunctionOptions?: CustomLambdaFunctionProps; }): lambda.Function { - const handler = new lambda.Function(this, "stac-ingestor", { - ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_9, - handler: "src.ingestor.handler", - memorySize: 2048, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(180) - }, - code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + const handler = new lambda.Function(this, "stac-ingestor",{ + // defaults for configurable properties + runtime: lambda.Runtime.PYTHON_3_9, + handler: "src.ingestor.handler", + memorySize: 2048, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(180), + code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.9' }, }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + + // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.dbVpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -316,29 +317,17 @@ export interface StacIngestorProps { readonly ingestorDomainNameOptions?: apigateway.DomainNameOptions; /** - * Optional api lambda asset code - * @default - default runtime defined in this repository. - */ - readonly apiLambdaAssetCode?: lambda.AssetCode; - - /** - * Optional ingestor lambda asset code - * @default - default runtime defined in this repository. - */ - readonly ingestorLambdaAssetCode?: lambda.AssetCode; - - /** - * Optional settings for the api lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * - * @default - defined in the construct. + * @default - default settings are defined in the construct. */ - readonly apiLambdaFunctionOptions?: CustomLambdaFunctionOptions; + readonly apiLambdaFunctionOptions?: CustomLambdaFunctionProps; /** - * Optional settings for the ingestor lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * - * @default - defined in the construct. + * @default - default settings are defined in the construct. */ -readonly ingestorLambdaFunctionOptions?: CustomLambdaFunctionOptions; +readonly ingestorLambdaFunctionOptions?: CustomLambdaFunctionProps; } \ No newline at end of file diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index 34a03b3..9d713e0 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -11,7 +11,7 @@ import { import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; -import { CustomLambdaFunctionOptions } from "../utils"; +import { CustomLambdaFunctionProps } from "../utils"; export class PgStacApiLambda extends Construct { readonly url: string; @@ -21,17 +21,19 @@ export class PgStacApiLambda extends Construct { super(scope, id); this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { - ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_10, - handler: "src.handler.handler", - memorySize: 8192, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30) - }, - code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + // defaults for configurable properties + runtime: lambda.Runtime.PYTHON_3_10, + handler: "src.handler.handler", + memorySize: 8192, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.10' }, }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -94,16 +96,10 @@ export interface PgStacApiLambdaProps { readonly stacApiDomainName?: IDomainName; /** - * Optional settings for the lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * * @default - defined in the construct. */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; - - /** - * Optional lambda asset code - * @default - default runtime defined in this repository. - */ - readonly lambdaAssetCode?: lambda.AssetCode; + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; } diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index eb33870..de984d5 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -11,7 +11,7 @@ import { import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; - import { CustomLambdaFunctionOptions } from "../utils"; + import { CustomLambdaFunctionProps } from "../utils"; export class TiPgApiLambda extends Construct { readonly url: string; @@ -21,17 +21,19 @@ import { super(scope, id); this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { - ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_10, - handler: "handler.handler", - memorySize: 1024, - logRetention: logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30) - }, - code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { - file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.10' }, + // defaults for configurable properties + runtime: lambda.Runtime.PYTHON_3_10, + handler: "handler.handler", + memorySize: 1024, + logRetention: logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code: lambda.Code.fromDockerBuild(__dirname, { + file: "runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: '3.10' }, }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -100,15 +102,9 @@ import { /** - * Optional settings for the lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * * @default - defined in the construct. */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; - - /** - * Optional lambda asset code - * @default - default runtime defined in this repository. - */ - readonly lambdaAssetCode?: lambda.AssetCode; + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; } diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index 190565c..cb833ee 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -12,7 +12,7 @@ import { import { IDomainName, HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { Construct } from "constructs"; -import { CustomLambdaFunctionOptions } from "../utils"; +import { CustomLambdaFunctionProps } from "../utils"; // default settings that can be overridden by the user-provided environment. @@ -39,17 +39,19 @@ import { CustomLambdaFunctionOptions } from "../utils"; super(scope, id); this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { - ...props.lambdaFunctionOptions ?? { - runtime: lambda.Runtime.PYTHON_3_10, // update Dockerfile if this is changed. - handler: "handler.handler", - memorySize: 3008, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30) - }, - code: props.lambdaAssetCode ?? lambda.Code.fromDockerBuild(__dirname, { + // defaults for configurable properties + runtime: lambda.Runtime.PYTHON_3_10, + handler: "handler.handler", + memorySize: 3008, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.10' } }), + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -127,17 +129,11 @@ import { CustomLambdaFunctionOptions } from "../utils"; readonly titilerPgstacApiDomainName?: IDomainName; /** - * Optional settings for the lambda function. + * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. * * @default - defined in the construct. */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionOptions; - - /** - * Optional lambda asset code - * @default default runtime defined in this repository - */ - readonly lambdaAssetCode?: lambda.AssetCode; + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; } diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 80b12b1..43c5821 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -1,34 +1,5 @@ import { aws_lambda as lambda, - aws_logs as logs, - Duration, } from "aws-cdk-lib"; -export interface CustomLambdaFunctionOptions { - - /*** - * The runtime environment for the Lambda function that you are uploading. - */ - readonly runtime: lambda.Runtime; - - /** - * The function execution time (in seconds) after which Lambda terminates the function. - */ - readonly timeout: Duration; - - /** - * The amount of memory, in MB, that is allocated to your Lambda function. - */ - readonly memorySize: number; - - /** - * handler for the lambda function - */ - readonly handler: string; - - /** - * log retention period for the lambda function - */ - readonly logRetention: logs.RetentionDays; - - } +export type CustomLambdaFunctionProps = lambda.FunctionProps | any; From 75733989aa0ec199469b941b7eb6c842de142b40 Mon Sep 17 00:00:00 2001 From: emileten Date: Thu, 12 Oct 2023 17:39:36 +0900 Subject: [PATCH 14/30] expose bootstrapper props in pgstacdatabase construct constructor --- lib/database/index.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/database/index.ts b/lib/database/index.ts index ffa8a91..7e95d7c 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -49,9 +49,7 @@ export class PgStacDatabase extends Construct { vpc: props.vpc, database: this.db, dbSecret: this.db.secret!, - pgstacDbName: props.pgstacDbName, - pgstacUsername: props.pgstacUsername, - secretsPrefix: props.secretsPrefix, + ...props.bootstrapperProps, }); this.pgstacSecret = bootstrap.secret; @@ -98,9 +96,7 @@ export class PgStacDatabase extends Construct { } export interface PgStacDatabaseProps extends rds.DatabaseInstanceProps { - readonly pgstacDbName?: BootstrapPgStacProps["pgstacDbName"]; - readonly pgstacUsername?: BootstrapPgStacProps["pgstacUsername"]; - readonly secretsPrefix?: BootstrapPgStacProps["secretsPrefix"]; + readonly bootstrapperProps?: BootstrapPgStacProps | any; } export interface DatabaseParameters { From 926a6a1ce59cebb2e0bdd7678dd1ef1e44dd2694 Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 17 Oct 2023 14:25:58 +0900 Subject: [PATCH 15/30] merge database and boostrapper files to solve casting bug --- lib/bootstrapper/index.ts | 167 ------------------ .../bootstrapper_runtime}/Dockerfile | 2 +- .../bootstrapper_runtime}/handler.py | 0 lib/database/index.ts | 127 ++++++++++++- lib/index.ts | 1 - lib/ingestor-api/runtime/src/main.py | 7 + lib/stac-api/index.ts | 2 + 7 files changed, 129 insertions(+), 177 deletions(-) delete mode 100644 lib/bootstrapper/index.ts rename lib/{bootstrapper/runtime => database/bootstrapper_runtime}/Dockerfile (90%) rename lib/{bootstrapper/runtime => database/bootstrapper_runtime}/handler.py (100%) diff --git a/lib/bootstrapper/index.ts b/lib/bootstrapper/index.ts deleted file mode 100644 index 767c64e..0000000 --- a/lib/bootstrapper/index.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - aws_ec2, - aws_rds, - aws_lambda, - aws_logs, - aws_secretsmanager, - CustomResource, - Duration, - Stack, - RemovalPolicy, -} from "aws-cdk-lib"; -import { Construct } from "constructs"; -import { CustomLambdaFunctionProps } from "../utils"; - -const DEFAULT_PGSTAC_VERSION = "0.6.13"; - -function hasVpc( - instance: aws_rds.DatabaseInstance | aws_rds.IDatabaseInstance -): instance is aws_rds.DatabaseInstance { - return (instance as aws_rds.DatabaseInstance).vpc !== undefined; -} - -/** - * Bootstraps a database instance, installing pgSTAC onto the database. - */ -export class BootstrapPgStac extends Construct { - secret: aws_secretsmanager.ISecret; - - constructor(scope: Construct, id: string, props: BootstrapPgStacProps) { - super(scope, id); - - const handler = new aws_lambda.Function(this, "lambda", { - // defaults for configurable properties - runtime: aws_lambda.Runtime.PYTHON_3_8, - handler: "handler.handler", - memorySize: 128, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.minutes(2), - code: aws_lambda.Code.fromDockerBuild(__dirname, { - file: "runtime/Dockerfile", - buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.8"} - }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user - vpc: hasVpc(props.database) ? props.database.vpc : props.vpc, - }); - - this.secret = new aws_secretsmanager.Secret(this, "secret", { - secretName: [ - props.secretsPrefix || "pgstac", - id, - this.node.addr.slice(-8), - ].join("/"), - generateSecretString: { - secretStringTemplate: JSON.stringify({ - dbname: props.pgstacDbName || "pgstac", - engine: "postgres", - port: 5432, - host: props.database.instanceEndpoint.hostname, - username: props.pgstacUsername || "pgstac_user", - }), - generateStringKey: "password", - excludePunctuation: true, - }, - description: `PgSTAC database bootstrapped by ${ - Stack.of(this).stackName - }`, - }); - - // Allow lambda to... - // read new user secret - this.secret.grantRead(handler); - // read database secret - props.dbSecret.grantRead(handler); - // connect to database - props.database.connections.allowFrom(handler, aws_ec2.Port.tcp(5432)); - - let customResourceProperties : { [key: string]: any} = {}; - - // if customResourceProperties are provided, fill in the values. - if (props.customResourceProperties) { - Object.assign(customResourceProperties, props.customResourceProperties); - } - - // update properties - customResourceProperties["conn_secret_arn"] = props.dbSecret.secretArn; - customResourceProperties["new_user_secret_arn"] = this.secret.secretArn; - - // if props.lambdaFunctionOptions doesn't have 'code' defined, update pgstac_version (needed for default runtime) - if (!props.lambdaFunctionOptions?.code) { - customResourceProperties["pgstac_version"] = DEFAULT_PGSTAC_VERSION; - } - // this.connections = props.database.connections; - new CustomResource(this, "bootstrapper", { - serviceToken: handler.functionArn, - properties: customResourceProperties, - removalPolicy: RemovalPolicy.RETAIN, // This retains the custom resource (which doesn't really exist), not the database - }); - } -} - -export interface BootstrapPgStacProps { - /** - * VPC in which the database resides. - * - * Note - Must be explicitely set if the `database` only conforms to the - * `aws_rds.IDatabaseInstace` interface (ie it is a reference to a database instance - * rather than a database instance.) - * - * @default - `vpc` property of the `database` instance provided. - */ - readonly vpc?: aws_ec2.IVpc; - - /** - * Database onto which pgSTAC should be installed. - */ - readonly database: aws_rds.DatabaseInstance | aws_rds.IDatabaseInstance; - - /** - * Secret containing valid connection details for the database instance. Secret must - * conform to the format of CDK's `DatabaseInstance` (i.e. a JSON object containing a - * `username`, `password`, `host`, `port`, and optionally a `dbname`). If a `dbname` - * property is not specified within the secret, the bootstrapper will attempt to - * connect to a database with the name of `"postgres"`. - */ - readonly dbSecret: aws_secretsmanager.ISecret; - - /** - * Name of database that is to be created and onto which pgSTAC will be installed. - * - * @default pgstac - */ - readonly pgstacDbName?: string; - - /** - * Name of user that will be generated for connecting to the pgSTAC database. - * - * @default pgstac_user - */ - readonly pgstacUsername?: string; - - /** - * Prefix to assign to the generated `secrets_manager.Secret` - * - * @default pgstac - */ - readonly secretsPrefix?: string; - - /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. - * - * @default - defined in the construct. - */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; - - /** - * Lambda function Custom Resource properties. A custom resource property is going to be created - * to trigger the boostrapping lambda function. This parameter allows the user to specify additional properties - * on top of the defaults ones. - * - */ - readonly customResourceProperties?: { - [key: string]: any; -} - -} diff --git a/lib/bootstrapper/runtime/Dockerfile b/lib/database/bootstrapper_runtime/Dockerfile similarity index 90% rename from lib/bootstrapper/runtime/Dockerfile rename to lib/database/bootstrapper_runtime/Dockerfile index d8d01de..0b0b9a4 100644 --- a/lib/bootstrapper/runtime/Dockerfile +++ b/lib/database/bootstrapper_runtime/Dockerfile @@ -9,7 +9,7 @@ WORKDIR /tmp RUN pip install httpx psycopg[binary,pool] pypgstac==${PGSTAC_VERSION} -t /asset -COPY runtime/handler.py /asset/handler.py +COPY bootstrapper_runtime/handler.py /asset/handler.py # https://stackoverflow.com/a/61746719 # Tip from eoAPI: turns out, asyncio is part of python diff --git a/lib/bootstrapper/runtime/handler.py b/lib/database/bootstrapper_runtime/handler.py similarity index 100% rename from lib/bootstrapper/runtime/handler.py rename to lib/database/bootstrapper_runtime/handler.py diff --git a/lib/database/index.ts b/lib/database/index.ts index 7e95d7c..419a88f 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -1,12 +1,26 @@ import { Stack, aws_rds as rds, + aws_ec2 as ec2, aws_secretsmanager as secretsmanager, + aws_lambda, + CustomResource, + RemovalPolicy, + Duration, + aws_logs, + } from "aws-cdk-lib"; import { Construct } from "constructs"; -import { BootstrapPgStac, BootstrapPgStacProps } from "../bootstrapper"; +import { CustomLambdaFunctionProps } from "../utils"; const instanceSizes: Record = require("./instance-memory.json"); +const DEFAULT_PGSTAC_VERSION = "0.6.13"; + +function hasVpc( + instance: rds.DatabaseInstance | rds.IDatabaseInstance +): instance is rds.DatabaseInstance { + return (instance as rds.DatabaseInstance).vpc !== undefined; +} /** * An RDS instance with pgSTAC installed. This is a wrapper around the @@ -45,14 +59,75 @@ export class PgStacDatabase extends Construct { ...props, }); - const bootstrap = new BootstrapPgStac(this, "bootstrap-pgstac-instance", { - vpc: props.vpc, - database: this.db, - dbSecret: this.db.secret!, - ...props.bootstrapperProps, + const handler = new aws_lambda.Function(this, "lambda", { + // defaults for configurable properties + runtime: aws_lambda.Runtime.PYTHON_3_8, + handler: "handler.handler", + memorySize: 128, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.minutes(2), + code: aws_lambda.Code.fromDockerBuild(__dirname, { + file: "bootstrapper_runtime/Dockerfile", + buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.8"} + }), + // overwrites defaults with user-provided configurable properties + ...props.bootstrapperLambdaFunctionOptions, + // Non configurable properties that are going to be overwritten even if provided by the user + vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, + }); + + this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { + secretName: [ + props.secretsPrefix || "pgstac", + id, + this.node.addr.slice(-8), + ].join("/"), + generateSecretString: { + secretStringTemplate: JSON.stringify({ + dbname: props.pgstacDbName || "pgstac", + engine: "postgres", + port: 5432, + host: this.db.instanceEndpoint.hostname, + username: props.pgstacUsername || "pgstac_user", + }), + generateStringKey: "password", + excludePunctuation: true, + }, + description: `PgSTAC database bootstrapped by ${ + Stack.of(this).stackName + }`, + }); + + // Allow lambda to... + // read new user secret + this.pgstacSecret.grantRead(handler); + // read database secret + this.db.secret!.grantRead(handler); + // connect to database + this.db.connections.allowFrom(handler, ec2.Port.tcp(5432)); + + let customResourceProperties : { [key: string]: any} = {}; + + // if customResourceProperties are provided, fill in the values. + if (props.customResourceProperties) { + Object.assign(customResourceProperties, props.customResourceProperties); + } + + // update properties + customResourceProperties["conn_secret_arn"] = this.db.secret!.secretArn; + customResourceProperties["new_user_secret_arn"] = this.pgstacSecret.secretArn; + + // if props.lambdaFunctionOptions doesn't have 'code' defined, update pgstac_version (needed for default runtime) + if (!props.bootstrapperLambdaFunctionOptions?.code) { + customResourceProperties["pgstac_version"] = DEFAULT_PGSTAC_VERSION; + } + // this.connections = props.database.connections; + new CustomResource(this, "bootstrapper", { + serviceToken: handler.functionArn, + properties: customResourceProperties, + removalPolicy: RemovalPolicy.RETAIN, // This retains the custom resource (which doesn't really exist), not the database }); - this.pgstacSecret = bootstrap.secret; } public getParameters( @@ -96,7 +171,43 @@ export class PgStacDatabase extends Construct { } export interface PgStacDatabaseProps extends rds.DatabaseInstanceProps { - readonly bootstrapperProps?: BootstrapPgStacProps | any; + /** + * Name of database that is to be created and onto which pgSTAC will be installed. + * + * @default pgstac + */ + readonly pgstacDbName?: string; + + /** + * Prefix to assign to the generated `secrets_manager.Secret` + * + * @default pgstac + */ + readonly secretsPrefix?: string; + + /** + * Name of user that will be generated for connecting to the pgSTAC database. + * + * @default pgstac_user + */ + readonly pgstacUsername?: string; + + /** + * Lambda function Custom Resource properties. A custom resource property is going to be created + * to trigger the boostrapping lambda function. This parameter allows the user to specify additional properties + * on top of the defaults ones. + * + */ + readonly customResourceProperties?: { + [key: string]: any; +} + + /** + * Optional settings for the bootstrapper lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * + * @default - defined in the construct. + */ + readonly bootstrapperLambdaFunctionOptions?: CustomLambdaFunctionProps; } export interface DatabaseParameters { diff --git a/lib/index.ts b/lib/index.ts index 53b0b67..6f543fd 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,5 +1,4 @@ export * from "./bastion-host"; -export * from "./bootstrapper"; export * from "./database"; export * from "./ingestor-api"; export * from "./stac-api"; diff --git a/lib/ingestor-api/runtime/src/main.py b/lib/ingestor-api/runtime/src/main.py index 8ab4678..bd6a62a 100644 --- a/lib/ingestor-api/runtime/src/main.py +++ b/lib/ingestor-api/runtime/src/main.py @@ -123,3 +123,10 @@ def who_am_i(username=Depends(dependencies.get_username)): Return username for the provided request """ return {"username": username} + +@app.get("/root_path") +def return_root_path(): + """ + Return root path for the provided request + """ + return {"root_path": config.settings.root_path} \ No newline at end of file diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index 9d713e0..a0028b4 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -20,6 +20,8 @@ export class PgStacApiLambda extends Construct { constructor(scope: Construct, id: string, props: PgStacApiLambdaProps) { super(scope, id); + console.log(props) + console.log(props.lambdaFunctionOptions); this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { // defaults for configurable properties runtime: lambda.Runtime.PYTHON_3_10, From f725f28001eb3a1d8948f9d5eaec930dc84bfd4f Mon Sep 17 00:00:00 2001 From: emileten Date: Fri, 27 Oct 2023 16:40:46 +0900 Subject: [PATCH 16/30] bump and harmonize pypgstac to 0.7.10 across apps --- lib/database/index.ts | 2 +- lib/ingestor-api/runtime/requirements.txt | 2 +- lib/stac-api/runtime/requirements.txt | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/database/index.ts b/lib/database/index.ts index 419a88f..04811a9 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -14,7 +14,7 @@ import { Construct } from "constructs"; import { CustomLambdaFunctionProps } from "../utils"; const instanceSizes: Record = require("./instance-memory.json"); -const DEFAULT_PGSTAC_VERSION = "0.6.13"; +const DEFAULT_PGSTAC_VERSION = "0.7.10"; function hasVpc( instance: rds.DatabaseInstance | rds.IDatabaseInstance diff --git a/lib/ingestor-api/runtime/requirements.txt b/lib/ingestor-api/runtime/requirements.txt index 25bfb2c..fcbb503 100644 --- a/lib/ingestor-api/runtime/requirements.txt +++ b/lib/ingestor-api/runtime/requirements.txt @@ -5,7 +5,7 @@ orjson>=3.6.8 psycopg[binary,pool]>=3.0.15 pydantic_ssm_settings>=0.2.0 pydantic>=1.9.0 -pypgstac==0.6.13 +pypgstac==0.7.10 requests>=2.27.1 # Waiting for https://github.com/stac-utils/stac-pydantic/pull/116 stac-pydantic @ git+https://github.com/alukach/stac-pydantic.git@patch-1 diff --git a/lib/stac-api/runtime/requirements.txt b/lib/stac-api/runtime/requirements.txt index 5005945..744fce5 100644 --- a/lib/stac-api/runtime/requirements.txt +++ b/lib/stac-api/runtime/requirements.txt @@ -1,7 +1,7 @@ -stac-fastapi.api==2.4.1 -stac-fastapi.extensions==2.4.1 -stac-fastapi.pgstac==2.4.1 -stac-fastapi.types==2.4.1 +stac-fastapi.api==2.4.8 +stac-fastapi.extensions==2.4.8 +stac-fastapi.pgstac==2.4.8 +stac-fastapi.types==2.4.8 # https://github.com/stac-utils/stac-fastapi/pull/466 pygeoif==0.7 starlette_cramjam \ No newline at end of file From f48e334157912394f762e6de18ea8f8668469f28 Mon Sep 17 00:00:00 2001 From: emileten Date: Fri, 27 Oct 2023 17:03:45 +0900 Subject: [PATCH 17/30] bump cachetools --- lib/ingestor-api/runtime/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ingestor-api/runtime/requirements.txt b/lib/ingestor-api/runtime/requirements.txt index fcbb503..7aff84a 100644 --- a/lib/ingestor-api/runtime/requirements.txt +++ b/lib/ingestor-api/runtime/requirements.txt @@ -1,5 +1,5 @@ Authlib==1.0.1 -cachetools==5.1.0 +cachetools==5.3.0 fastapi>=0.75.1 orjson>=3.6.8 psycopg[binary,pool]>=3.0.15 From 7b62f2a62b707ea769a6b7013c1fb021c9323135 Mon Sep 17 00:00:00 2001 From: emileten Date: Fri, 27 Oct 2023 19:03:32 +0900 Subject: [PATCH 18/30] some changes to allow for less security --- lib/database/index.ts | 1 + lib/ingestor-api/index.ts | 39 +++++++++++++++++++-------------- lib/stac-api/index.ts | 9 ++++---- lib/tipg-api/index.ts | 9 ++++---- lib/titiler-pgstac-api/index.ts | 9 +++++--- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/lib/database/index.ts b/lib/database/index.ts index 04811a9..f179177 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -74,6 +74,7 @@ export class PgStacDatabase extends Construct { ...props.bootstrapperLambdaFunctionOptions, // Non configurable properties that are going to be overwritten even if provided by the user vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, + allowPublicSubnet: true }); this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index be6e3d0..d156682 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -108,9 +108,9 @@ export class StacIngestor extends Construct { dataAccessRole: iam.IRole; stage: string; dbSecret: secretsmanager.ISecret; - dbVpc: ec2.IVpc; + dbVpc: undefined | ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; - subnetSelection: ec2.SubnetSelection + subnetSelection: undefined | ec2.SubnetSelection lambdaFunctionOptions?: CustomLambdaFunctionProps; }): lambda.Function { @@ -139,11 +139,14 @@ export class StacIngestor extends Construct { props.dbSecret.grantRead(handler); // Allow handler to connect to DB - props.dbSecurityGroup.addIngressRule( - handler.connections.securityGroups[0], - ec2.Port.tcp(5432), - "Allow connections from STAC Ingestor" - ); + + if (props.dbVpc){ + props.dbSecurityGroup.addIngressRule( + handler.connections.securityGroups[0], + ec2.Port.tcp(5432), + "Allow connections from STAC Ingestor" + ); + } props.table.grantReadWriteData(handler); @@ -154,9 +157,9 @@ export class StacIngestor extends Construct { table: dynamodb.ITable; env: Record; dbSecret: secretsmanager.ISecret; - dbVpc: ec2.IVpc; + dbVpc: undefined | ec2.IVpc; dbSecurityGroup: ec2.ISecurityGroup; - subnetSelection: ec2.SubnetSelection; + subnetSelection: undefined | ec2.SubnetSelection; lambdaFunctionOptions?: CustomLambdaFunctionProps; }): lambda.Function { @@ -187,11 +190,13 @@ export class StacIngestor extends Construct { props.dbSecret.grantRead(handler); // Allow handler to connect to DB - props.dbSecurityGroup.addIngressRule( - handler.connections.securityGroups[0], - ec2.Port.tcp(5432), - "Allow connections from STAC Ingestor" - ); + if (props.dbVpc){ + props.dbSecurityGroup.addIngressRule( + handler.connections.securityGroups[0], + ec2.Port.tcp(5432), + "Allow connections from STAC Ingestor" + ); + } // Allow handler to write results back to DBƒ props.table.grantWriteData(handler); @@ -284,7 +289,7 @@ export interface StacIngestorProps { /** * VPC running pgSTAC DB */ - readonly vpc: ec2.IVpc; + readonly vpc?: ec2.IVpc; /** * Security Group used by pgSTAC DB @@ -292,9 +297,9 @@ export interface StacIngestorProps { readonly stacDbSecurityGroup: ec2.ISecurityGroup; /** - * Boolean indicating whether or not pgSTAC DB is in a public subnet + * Subnet into which the lambda should be deployed if using a VPC */ - readonly subnetSelection: ec2.SubnetSelection; + readonly subnetSelection?: ec2.SubnetSelection; /** * Environment variables to be sent to Lambda. diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index a0028b4..0257ddb 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -48,8 +48,9 @@ export class PgStacApiLambda extends Construct { }); props.dbSecret.grantRead(this.stacApiLambdaFunction); - this.stacApiLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432)); - + if (props.vpc){ + this.stacApiLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432)); + } const stacApi = new HttpApi(this, `${Stack.of(this).stackName}-stac-api`, { defaultDomainMapping: props.stacApiDomainName ? { domainName: props.stacApiDomainName @@ -70,7 +71,7 @@ export interface PgStacApiLambdaProps { /** * VPC into which the lambda should be deployed. */ - readonly vpc: ec2.IVpc; + readonly vpc?: ec2.IVpc; /** * RDS Instance with installed pgSTAC. @@ -80,7 +81,7 @@ export interface PgStacApiLambdaProps { /** * Subnet into which the lambda should be deployed. */ - readonly subnetSelection: ec2.SubnetSelection; + readonly subnetSelection?: ec2.SubnetSelection; /** * Secret containing connection information for pgSTAC database. diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index de984d5..99aea1d 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -46,8 +46,9 @@ import { }); props.dbSecret.grantRead(this.tiPgLambdaFunction); - this.tiPgLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from tipg"); - + if (props.vpc){ + this.tiPgLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from tipg"); + } const tipgApi = new HttpApi(this, `${Stack.of(this).stackName}-tipg-api`, { defaultDomainMapping: props.tipgApiDomainName ? { domainName: props.tipgApiDomainName @@ -69,7 +70,7 @@ import { /** * VPC into which the lambda should be deployed. */ - readonly vpc: ec2.IVpc; + readonly vpc?: ec2.IVpc; /** * RDS Instance with installed pgSTAC. @@ -79,7 +80,7 @@ import { /** * Subnet into which the lambda should be deployed. */ - readonly subnetSelection: ec2.SubnetSelection; + readonly subnetSelection?: ec2.SubnetSelection; /** * Secret containing connection information for pgSTAC database. diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index cb833ee..5c99a1f 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -70,7 +70,10 @@ import { CustomLambdaFunctionProps } from "../utils"; } props.dbSecret.grantRead(this.titilerPgstacLambdaFunction); - this.titilerPgstacLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from titiler"); + + if (props.vpc) { + this.titilerPgstacLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from titiler"); + } const stacApi = new HttpApi(this, `${Stack.of(this).stackName}-titiler-pgstac-api`, { defaultDomainMapping: props.titilerPgstacApiDomainName ? { @@ -93,7 +96,7 @@ import { CustomLambdaFunctionProps } from "../utils"; /** * VPC into which the lambda should be deployed. */ - readonly vpc: ec2.IVpc; + readonly vpc?: ec2.IVpc; /** * RDS Instance with installed pgSTAC. @@ -103,7 +106,7 @@ import { CustomLambdaFunctionProps } from "../utils"; /** * Subnet into which the lambda should be deployed. */ - readonly subnetSelection: ec2.SubnetSelection; + readonly subnetSelection?: ec2.SubnetSelection; /** * Secret containing connection information for pgSTAC database. From 7c759815ca06c99cfbfcbc4e014f49288eca8bcb Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 31 Oct 2023 15:01:39 +0900 Subject: [PATCH 19/30] bump python to 3.11 --- lib/database/index.ts | 4 ++-- lib/ingestor-api/index.ts | 8 ++++---- lib/stac-api/index.ts | 4 ++-- lib/tipg-api/index.ts | 4 ++-- lib/titiler-pgstac-api/index.ts | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/database/index.ts b/lib/database/index.ts index f179177..481f322 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -61,14 +61,14 @@ export class PgStacDatabase extends Construct { const handler = new aws_lambda.Function(this, "lambda", { // defaults for configurable properties - runtime: aws_lambda.Runtime.PYTHON_3_8, + runtime: aws_lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 128, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.minutes(2), code: aws_lambda.Code.fromDockerBuild(__dirname, { file: "bootstrapper_runtime/Dockerfile", - buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.8"} + buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.11"} }), // overwrites defaults with user-provided configurable properties ...props.bootstrapperLambdaFunctionOptions, diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index d156682..c36baad 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -116,14 +116,14 @@ export class StacIngestor extends Construct { const handler = new lambda.Function(this, "api-handler", { // defaults for configurable properties - runtime: lambda.Runtime.PYTHON_3_9, + runtime: lambda.Runtime.PYTHON_3_11, handler: "src.handler.handler", memorySize: 2048, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30), code:lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.9' }, + buildArgs: { PYTHON_VERSION: '3.11' }, }), // overwrites defaults with user-provided configurable properties ...props.lambdaFunctionOptions, @@ -166,14 +166,14 @@ export class StacIngestor extends Construct { const handler = new lambda.Function(this, "stac-ingestor",{ // defaults for configurable properties - runtime: lambda.Runtime.PYTHON_3_9, + runtime: lambda.Runtime.PYTHON_3_11, handler: "src.ingestor.handler", memorySize: 2048, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(180), code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.9' }, + buildArgs: { PYTHON_VERSION: '3.11' }, }), // overwrites defaults with user-provided configurable properties ...props.lambdaFunctionOptions, diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index 0257ddb..bcfe434 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -24,14 +24,14 @@ export class PgStacApiLambda extends Construct { console.log(props.lambdaFunctionOptions); this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { // defaults for configurable properties - runtime: lambda.Runtime.PYTHON_3_10, + runtime: lambda.Runtime.PYTHON_3_11, handler: "src.handler.handler", memorySize: 8192, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30), code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.10' }, + buildArgs: { PYTHON_VERSION: '3.11' }, }), // overwrites defaults with user-provided configurable properties ...props.lambdaFunctionOptions, diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index 99aea1d..d50518b 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -22,14 +22,14 @@ import { this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { // defaults for configurable properties - runtime: lambda.Runtime.PYTHON_3_10, + runtime: lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 1024, logRetention: logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30), code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.10' }, + buildArgs: { PYTHON_VERSION: '3.11' }, }), // overwrites defaults with user-provided configurable properties ...props.lambdaFunctionOptions, diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index 5c99a1f..7ac8fe9 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -40,14 +40,14 @@ import { CustomLambdaFunctionProps } from "../utils"; this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { // defaults for configurable properties - runtime: lambda.Runtime.PYTHON_3_10, + runtime: lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 3008, logRetention: aws_logs.RetentionDays.ONE_WEEK, timeout: Duration.seconds(30), code: lambda.Code.fromDockerBuild(__dirname, { file: "runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.10' } + buildArgs: { PYTHON_VERSION: '3.11' } }), // overwrites defaults with user-provided configurable properties ...props.lambdaFunctionOptions, From ccfce6b13d2104b099e12f6350d21a7a50bc3452 Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 31 Oct 2023 15:34:39 +0900 Subject: [PATCH 20/30] change base image for bootstrapper to use python 311 --- lib/database/bootstrapper_runtime/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/database/bootstrapper_runtime/Dockerfile b/lib/database/bootstrapper_runtime/Dockerfile index 0b0b9a4..09e0f9a 100644 --- a/lib/database/bootstrapper_runtime/Dockerfile +++ b/lib/database/bootstrapper_runtime/Dockerfile @@ -1,5 +1,5 @@ ARG PYTHON_VERSION -FROM lambci/lambda:build-python${PYTHON_VERSION} +FROM --platform=linux/amd64 public.ecr.aws/lambda/python:${PYTHON_VERSION} ARG PGSTAC_VERSION RUN echo "PGSTAC_VERSION: ${PGSTAC_VERSION}" From a7aea2bd29cb207ab1e98321cdcc67c5f41e68a8 Mon Sep 17 00:00:00 2001 From: emileten Date: Tue, 31 Oct 2023 15:34:44 +0900 Subject: [PATCH 21/30] fix linting issues --- lib/ingestor-api/runtime/src/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ingestor-api/runtime/src/main.py b/lib/ingestor-api/runtime/src/main.py index bd6a62a..6d217f6 100644 --- a/lib/ingestor-api/runtime/src/main.py +++ b/lib/ingestor-api/runtime/src/main.py @@ -124,9 +124,10 @@ def who_am_i(username=Depends(dependencies.get_username)): """ return {"username": username} + @app.get("/root_path") def return_root_path(): """ Return root path for the provided request """ - return {"root_path": config.settings.root_path} \ No newline at end of file + return {"root_path": config.settings.root_path} From 64e67b73c41acb53540a5beecc5d9a2fd21bd381 Mon Sep 17 00:00:00 2001 From: emileten Date: Mon, 19 Feb 2024 19:52:58 +0300 Subject: [PATCH 22/30] move integration tests to step before release, improve naming of workflows --- .github/workflows/build.yaml | 81 +++++- .github/workflows/build_and_release.yaml | 17 ++ .github/workflows/distribute.yaml | 4 - .github/workflows/integration-test.yaml | 104 -------- .github/workflows/test.yaml | 13 - integration_tests/cdk/config.py | 142 +---------- .../cdk/eoapi_template/pgStacInfra.py | 239 +----------------- integration_tests/cdk/eoapi_template/vpc.py | 25 +- integration_tests/cdk/package-lock.json | 8 +- integration_tests/cdk/package.json | 2 +- integration_tests/cdk/requirements.txt | 6 +- integration_tests/tests/README.md | 16 -- .../tests/eoapi_tests/conftest.py | 22 -- .../eoapi_tests/fixtures/test_collection.json | 45 ---- .../tests/eoapi_tests/fixtures/test_item.json | 12 - .../fixtures/test_titiler_search_request.json | 3 - .../tests/eoapi_tests/ingestion.py | 166 ------------ .../tests/eoapi_tests/settings.py | 12 - .../tests/eoapi_tests/test_stac_ingestion.py | 123 --------- integration_tests/tests/pyproject.toml | 21 -- lib/database/bootstrapper_runtime/handler.py | 2 +- lib/database/index.ts | 3 +- 22 files changed, 136 insertions(+), 930 deletions(-) create mode 100644 .github/workflows/build_and_release.yaml delete mode 100644 .github/workflows/integration-test.yaml delete mode 100644 .github/workflows/test.yaml delete mode 100644 integration_tests/tests/README.md delete mode 100644 integration_tests/tests/eoapi_tests/conftest.py delete mode 100644 integration_tests/tests/eoapi_tests/fixtures/test_collection.json delete mode 100644 integration_tests/tests/eoapi_tests/fixtures/test_item.json delete mode 100644 integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json delete mode 100644 integration_tests/tests/eoapi_tests/ingestion.py delete mode 100644 integration_tests/tests/eoapi_tests/settings.py delete mode 100644 integration_tests/tests/eoapi_tests/test_stac_ingestion.py delete mode 100644 integration_tests/tests/pyproject.toml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fee9392..d9d635e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,17 +12,30 @@ on: required: false DS_RELEASE_BOT_PRIVATE_KEY: required: false - + AWS_DEFAULT_REGION_DEPLOY: + required: false + AWS_ACCESS_KEY_ID_DEPLOY: + required: false + AWS_SECRET_ACCESS_KEY_DEPLOY: + required: false + AWS_ACCOUNT_ID: + required: false jobs: build_and_package: name: Build and package runs-on: ubuntu-latest + timeout-minutes: 60 + env: + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} + AWS_DEFAULT_ACCOUNT: ${{ secrets.AWS_ACCOUNT_ID }} steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 cache: "npm" - name: Install Dependencies @@ -65,8 +78,70 @@ jobs: app_id: ${{ secrets.DS_RELEASE_BOT_ID }} private_key: ${{ secrets.DS_RELEASE_BOT_PRIVATE_KEY }} - - name: Maybe Release 🚀 + - name: Check release + id: check_release if: ${{ inputs.release }} + run: | + SHOULD_RELEASE=false + npm run semantic-release --dry-run > check_release_output.txt + if grep -q "Published release" check_release_output.txt; then + echo "SHOULD_RELEASE=true" >> $GITHUB_OUTPUT + else + echo "SHOULD_RELEASE=false" >> $GITHUB_OUTPUT + fi + + - name: Install deployment environment + if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" + id: install_deploy_env + run: | + # install deployment environment with eoapi-cdk from build + python -m venv .deployment_venv + source .deployment_venv/bin/activate + pip install dist/python/*.gz + cd integration_tests/cdk + pip install -r requirements.txt + npm install + deactivate + cd - + + + - name: Deploy test stack + if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" + id: deploy_step + run: | + source .deployment_venv/bin/activate + + # synthesize the stack + cd integration_tests/cdk + npx cdk synth --debug --all --require-approval never + + # deploy the stack and grab URLs for testing + npx cdk deploy --ci --all --require-approval never + echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + deactivate + cd - + + - name: Tear down any infrastructure + if: always() + run: | + cd integration_tests/cdk + # run this only if we find a 'cdk.out' directory, which means there might be things to tear down + if [ -d "cdk.out" ]; then + cd - + source .deployment_venv/bin/activate + cd integration_tests/cdk + # see https://github.com/aws/aws-cdk/issues/24946 + rm -f cdk.out/synth.lock + npx cdk destroy --ci --all --force + fi + + + # run if the previous step set SHOULD_RELEASE to true + - name: Maybe Release 🚀 + # only run if the previous step set SHOULD_RELEASE to true + if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" run: | npm run semantic-release env: diff --git a/.github/workflows/build_and_release.yaml b/.github/workflows/build_and_release.yaml new file mode 100644 index 0000000..e252d72 --- /dev/null +++ b/.github/workflows/build_and_release.yaml @@ -0,0 +1,17 @@ +name: Build & try to release + +on: + push: + +jobs: + package: + uses: ./.github/workflows/build.yaml + with: + release: true + secrets: + DS_RELEASE_BOT_ID: ${{ secrets.DS_RELEASE_BOT_ID }} + DS_RELEASE_BOT_PRIVATE_KEY: ${{ secrets.DS_RELEASE_BOT_PRIVATE_KEY }} + AWS_DEFAULT_REGION_DEPLOY: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} + AWS_ACCESS_KEY_ID_DEPLOY: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} + AWS_SECRET_ACCESS_KEY_DEPLOY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} + AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} diff --git a/.github/workflows/distribute.yaml b/.github/workflows/distribute.yaml index e484b8f..fd7fe8a 100644 --- a/.github/workflows/distribute.yaml +++ b/.github/workflows/distribute.yaml @@ -9,10 +9,6 @@ jobs: package: uses: ./.github/workflows/build.yaml - integration-test: - uses: ./.github/workflows/integration-test.yaml - needs: package - distribute-python: runs-on: ubuntu-latest needs: package diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml deleted file mode 100644 index eb9fe79..0000000 --- a/.github/workflows/integration-test.yaml +++ /dev/null @@ -1,104 +0,0 @@ -name: Deploy & Integration Test - -permissions: - id-token: write # required for requesting the JWT - contents: read # required for actions/checkout - -on: - workflow_call: - - workflow_dispatch: - - # remove later - # push: - # branches: - # - "feat/add-integration-tests" - -jobs: - deploy-and-integration-test: - name: Deploy & Integration Test - runs-on: ubuntu-latest - env: - AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} - - - steps: - - - name: Checkout repository # for runs after `Distribute`, pulling from main will always give the latest eoapi-cdk. - uses: actions/checkout@v3 - - - name: Set up python - uses: actions/setup-python@v2 - with: - cache: pip - - - name: Set up node - uses: actions/setup-node@v3 - with: - node-version: '18' - - - name: Download compiled eoapi-cdk artifact - uses: actions/download-artifact@v3 - with: - name: python - path: dist - - - name: Install eoapi-cdk from artifact - run: pip install dist/python/*.gz - - - name: Install deployment environment - working-directory: integration_tests/cdk - run: | - python -m venv .deployment_venv - source .deployment_venv/bin/activate - pip install -r requirements.txt - npm install - deactivate - - - name: Synthesize the stack - working-directory: integration_tests/cdk - run: | - source .deployment_venv/bin/activate - npx cdk synth --debug --all --require-approval never - deactivate - - # deploys and grabs URLs from the output for later tests - - name: Deploy the stack - id: deploy_step - working-directory: integration_tests/cdk - run: | - source .deployment_venv/bin/activate - npx cdk deploy --ci --all --require-approval never - echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-template-demo-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - deactivate - - - name: Install test environment - working-directory: integration_tests/tests - run: | - python -m venv .tests_venv - source .tests_venv/bin/activate - pip install -e . - deactivate - - - name: Test the stack - working-directory: integration_tests/tests - env: - ingestor_url: ${{ steps.deploy_step.outputs.ingestor_url }} - stac_api_url: ${{ steps.deploy_step.outputs.stac_api_url }} - titiler_pgstac_api_url: ${{ steps.deploy_step.outputs.titiler_pgstac_api_url }} - run: | - source .tests_venv/bin/activate - pytest eoapi_tests - deactivate - - - name: Always tear down the stack - if: always() - working-directory: integration_tests/cdk - run: | - source .deployment_venv/bin/activate - npx cdk destroy --ci --all --force - deactivate diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index e543929..0000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: Test & Build - -on: - push: - -jobs: - package: - uses: ./.github/workflows/build.yaml - with: - release: true - secrets: - DS_RELEASE_BOT_ID: ${{ secrets.DS_RELEASE_BOT_ID }} - DS_RELEASE_BOT_PRIVATE_KEY: ${{ secrets.DS_RELEASE_BOT_PRIVATE_KEY }} diff --git a/integration_tests/cdk/config.py b/integration_tests/cdk/config.py index 70457c5..0bc6c46 100644 --- a/integration_tests/cdk/config.py +++ b/integration_tests/cdk/config.py @@ -1,15 +1,19 @@ -from typing import Any, Dict, List, Union +from typing import Dict import pydantic import yaml -from aws_cdk import aws_ec2 from pydantic_core.core_schema import FieldValidationInfo -from pydantic_settings import BaseSettings - +from pydantic_settings import BaseSettings, SettingsConfigDict class AppConfig(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env" + ) + aws_default_account: str = pydantic.Field( + description="AWS account ID" + ) project_id: str = pydantic.Field( - description="Project ID", default="eoapi-template-demo" + description="Project ID", default="eoapi-cdk-integration" ) stage: str = pydantic.Field(description="Stage of deployment", default="test") # because of its validator, `tags` should always come after `project_id` and `stage` @@ -20,145 +24,17 @@ class AppConfig(BaseSettings): they will override any tags defined here.""", default=None, ) - auth_provider_jwks_url: str | None = pydantic.Field( - description="""Auth Provider JSON Web Key Set URL for - ingestion authentication. If not provided, - no authentication will be required.""", - default=None, - ) - data_access_role_arn: str | None = pydantic.Field( - description="""Role ARN for data access, that will be - used by the STAC ingestor for validation of assets - located in S3 and for the tiler application to access - assets located in S3. If none, the role will be - created at runtime with full S3 read access. If - provided, the existing role must be configured to - allow the tiler and STAC ingestor lambda roles to - assume it. See https://github.com/developmentseed/eoapi-cdk""", - default=None, - ) db_instance_type: str = pydantic.Field( description="Database instance type", default="t3.micro" ) db_allocated_storage: int = pydantic.Field( description="Allocated storage for the database", default=5 ) - public_db_subnet: bool = pydantic.Field( - description="Whether to put the database in a public subnet", default=True - ) - nat_gateway_count: int = pydantic.Field( - description="Number of NAT gateways to create", - default=0, - ) - bastion_host: bool = pydantic.Field( - description="""Whether to create a bastion host. It can typically - be used to make administrative connections to the database if - `public_db_subnet` is False""", - default=False, - ) - bastion_host_create_elastic_ip: bool = pydantic.Field( - description="""Whether to create an elastic IP for the bastion host. - Ignored if `bastion_host` equals `False`""", - default=False, - ) - bastion_host_allow_ip_list: List[str] = pydantic.Field( - description="""YAML file containing list of IP addresses to - allow SSH access to the bastion host. Ignored if `bastion_host` - equals `False`.""", - default=[], - ) - bastion_host_user_data: Union[Dict[str, Any], aws_ec2.UserData] = pydantic.Field( - description="""Path to file containing user data for the bastion host. - Ignored if `bastion_host` equals `False`.""", - default=aws_ec2.UserData.for_linux(), - ) - titiler_buckets: List[str] = pydantic.Field( - description="""Path to YAML file containing list of - buckets to grant access to the titiler API""", - default=[], - ) - acm_certificate_arn: str | None = pydantic.Field( - description="""ARN of ACM certificate to use for - custom domain names. If provided, - CDNs are created for all the APIs""", - default=None, - ) - stac_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the STAC API. - Must provide `acm_certificate_arn`""", - default=None, - ) - titiler_pgstac_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the titiler pgstac API. - Must provide `acm_certificate_arn`""", - default=None, - ) - stac_ingestor_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the STAC ingestor API. - Must provide `acm_certificate_arn`""", - default=None, - ) - tipg_api_custom_domain: str | None = pydantic.Field( - description="""Custom domain name for the tipg API. - Must provide `acm_certificate_arn`""", - default=None, - ) - stac_browser_version: str | None = pydantic.Field( - description="""Version of the Radiant Earth STAC browser to deploy. - If none provided, no STAC browser will be deployed. - If provided, `stac_api_custom_domain` must be provided - as it will be used as a backend.""", - default=None, - ) @pydantic.field_validator("tags") def default_tags(cls, v, info: FieldValidationInfo): return v or {"project_id": info.data["project_id"], "stage": info.data["stage"]} - @pydantic.model_validator(mode="after") - def validate_nat_gateway_count(self) -> "AppConfig": - if not self.public_db_subnet and ( - self.nat_gateway_count is not None and self.nat_gateway_count <= 0 - ): - raise ValueError( - """if the database and its associated services instances - are to be located in the private subnet of the VPC, NAT - gateways are needed to allow egress from the services - and therefore `nat_gateway_count` has to be > 0.""" - ) - else: - return self - - @pydantic.model_validator(mode="after") - def validate_stac_browser_version(self) -> "AppConfig": - if ( - self.stac_browser_version is not None - and self.stac_api_custom_domain is None - ): - raise ValueError( - """If a STAC browser version is provided, - a custom domain must be provided for the STAC API""" - ) - else: - return self - - @pydantic.model_validator(mode="after") - def validate_acm_certificate_arn(self) -> "AppConfig": - if self.acm_certificate_arn is None and any( - [ - self.stac_api_custom_domain, - self.titiler_pgstac_api_custom_domain, - self.stac_ingestor_api_custom_domain, - self.tipg_api_custom_domain, - ] - ): - raise ValueError( - """If any custom domain is provided, - an ACM certificate ARN must be provided""" - ) - else: - return self - def build_service_name(self, service_id: str) -> str: return f"{self.project_id}-{self.stage}-{service_id}" diff --git a/integration_tests/cdk/eoapi_template/pgStacInfra.py b/integration_tests/cdk/eoapi_template/pgStacInfra.py index 0f12e8d..f4d70b0 100644 --- a/integration_tests/cdk/eoapi_template/pgStacInfra.py +++ b/integration_tests/cdk/eoapi_template/pgStacInfra.py @@ -1,24 +1,12 @@ -import boto3 -import yaml from aws_cdk import ( - RemovalPolicy, Stack, - aws_certificatemanager, aws_ec2, - aws_iam, aws_rds, - aws_s3, ) -from aws_cdk.aws_apigateway import DomainNameOptions -from aws_cdk.aws_apigatewayv2_alpha import DomainName from constructs import Construct from eoapi_cdk import ( - BastionHost, PgStacApiLambda, PgStacDatabase, - StacBrowser, - StacIngestor, - TiPgApiLambda, TitilerPgstacApiLambda, ) @@ -48,41 +36,27 @@ def __init__( version=aws_rds.PostgresEngineVersion.VER_14 ), vpc_subnets=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PUBLIC - if app_config.public_db_subnet - else aws_ec2.SubnetType.PRIVATE_ISOLATED + subnet_type=aws_ec2.SubnetType.PUBLIC ), allocated_storage=app_config.db_allocated_storage, instance_type=aws_ec2.InstanceType(app_config.db_instance_type), + bootstrapper_lambda_function_options={ + "allow_public_subnet": True, + } ) + + pgstac_db.db.connections.allow_default_port_from_any_ipv4() - stac_api_lambda = PgStacApiLambda( + + PgStacApiLambda( self, "pgstac-api", api_env={ "NAME": app_config.build_service_name("STAC API"), "description": f"{app_config.stage} STAC API", }, - vpc=vpc, db=pgstac_db.db, - db_secret=pgstac_db.pgstac_secret, - subnet_selection=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PUBLIC - if app_config.public_db_subnet - else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS - ), - stac_api_domain_name=DomainName( - self, - "stac-api-domain-name", - domain_name=app_config.stac_api_custom_domain, - certificate=aws_certificatemanager.Certificate.from_certificate_arn( - self, - "stac-api-cdn-certificate", - certificate_arn=app_config.acm_certificate_arn, - ), - ) - if app_config.stac_api_custom_domain - else None, + db_secret=pgstac_db.pgstac_secret ) TitilerPgstacApiLambda( @@ -92,196 +66,11 @@ def __init__( "NAME": app_config.build_service_name("titiler pgSTAC API"), "description": f"{app_config.stage} titiler pgstac API", }, - vpc=vpc, db=pgstac_db.db, db_secret=pgstac_db.pgstac_secret, - subnet_selection=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PUBLIC - if app_config.public_db_subnet - else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS - ), - buckets=app_config.titiler_buckets, - titiler_pgstac_api_domain_name=DomainName( - self, - "titiler-pgstac-api-domain-name", - domain_name=app_config.titiler_pgstac_api_custom_domain, - certificate=aws_certificatemanager.Certificate.from_certificate_arn( - self, - "titiler-pgstac-api-cdn-certificate", - certificate_arn=app_config.acm_certificate_arn, - ), - ) - if app_config.titiler_pgstac_api_custom_domain - else None, + buckets=[], + lambda_function_options={ + "allow_public_subnet": True, + }, ) - - TiPgApiLambda( - self, - "tipg-api", - api_env={ - "NAME": app_config.build_service_name("tipg API"), - "description": f"{app_config.stage} tipg API", - }, - vpc=vpc, - db=pgstac_db.db, - db_secret=pgstac_db.pgstac_secret, - subnet_selection=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PUBLIC - if app_config.public_db_subnet - else aws_ec2.SubnetType.PRIVATE_WITH_EGRESS - ), - tipg_api_domain_name=DomainName( - self, - "tipg-api-domain-name", - domain_name=app_config.tipg_api_custom_domain, - certificate=aws_certificatemanager.Certificate.from_certificate_arn( - self, - "tipg-api-cdn-certificate", - certificate_arn=app_config.acm_certificate_arn, - ), - ) - if app_config.tipg_api_custom_domain - else None, - ) - - if app_config.bastion_host: - BastionHost( - self, - "bastion-host", - vpc=vpc, - db=pgstac_db.db, - ipv4_allowlist=app_config.bastion_host_allow_ip_list, - user_data=aws_ec2.UserData.custom( - yaml.dump(app_config.bastion_host_user_data) - ) - if app_config.bastion_host_user_data is not None - else aws_ec2.UserData.for_linux(), - create_elastic_ip=app_config.bastion_host_create_elastic_ip, - ) - - if app_config.data_access_role_arn: - # importing provided role from arn. - # the stac ingestor will try to assume it when called, - # so it must be listed in the data access role trust policy. - data_access_role = aws_iam.Role.from_role_arn( - self, - "data-access-role", - role_arn=app_config.data_access_role_arn, - ) - else: - data_access_role = self._create_data_access_role() - - stac_ingestor_env = {"REQUESTER_PAYS": "True"} - - if app_config.auth_provider_jwks_url: - stac_ingestor_env["JWKS_URL"] = app_config.auth_provider_jwks_url - - stac_ingestor = StacIngestor( - self, - "stac-ingestor", - stac_url=stac_api_lambda.url, - stage=app_config.stage, - vpc=vpc, - data_access_role=data_access_role, - stac_db_secret=pgstac_db.pgstac_secret, - stac_db_security_group=pgstac_db.db.connections.security_groups[0], - subnet_selection=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PRIVATE_WITH_EGRESS - ), - api_env=stac_ingestor_env, - ingestor_domain_name_options=DomainNameOptions( - domain_name=app_config.stac_ingestor_api_custom_domain, - certificate=aws_certificatemanager.Certificate.from_certificate_arn( - self, - "stac-ingestor-api-cdn-certificate", - certificate_arn=app_config.acm_certificate_arn, - ), - ) - if app_config.stac_ingestor_api_custom_domain - else None, - ) - - if app_config.stac_browser_version: - stac_browser_bucket = aws_s3.Bucket( - self, - "stac-browser-bucket", - bucket_name=app_config.build_service_name("stac-browser"), - removal_policy=RemovalPolicy.DESTROY, - auto_delete_objects=True, - website_index_document="index.html", - public_read_access=True, - block_public_access=aws_s3.BlockPublicAccess( - block_public_acls=False, - block_public_policy=False, - ignore_public_acls=False, - restrict_public_buckets=False, - ), - object_ownership=aws_s3.ObjectOwnership.OBJECT_WRITER, - ) - StacBrowser( - self, - "stac-browser", - github_repo_tag=app_config.stac_browser_version, - stac_catalog_url=f"https://{app_config.stac_api_custom_domain}", - website_index_document="index.html", - bucket_arn=stac_browser_bucket.bucket_arn, - ) - - # we can only do that if the role is created here. - # If injecting a role, that role's trust relationship - # must be already set up, or set up after this deployment. - if not app_config.data_access_role_arn: - data_access_role = self._grant_assume_role_with_principal_pattern( - data_access_role, stac_ingestor.handler_role.role_name - ) - - def _create_data_access_role(self) -> aws_iam.Role: - """ - Creates an IAM role with full S3 read access. - """ - - data_access_role = aws_iam.Role( - self, - "data-access-role", - assumed_by=aws_iam.ServicePrincipal("lambda.amazonaws.com"), - ) - - data_access_role.add_to_policy( - aws_iam.PolicyStatement( - actions=[ - "s3:Get*", - ], - resources=["*"], - effect=aws_iam.Effect.ALLOW, - ) - ) - return data_access_role - - def _grant_assume_role_with_principal_pattern( - self, - role_to_assume: aws_iam.Role, - principal_pattern: str, - account_id: str = boto3.client("sts").get_caller_identity().get("Account"), - ) -> aws_iam.Role: - """ - Grants assume role permissions to the role of the given - account with the given name pattern. Default account - is the current account. - """ - - role_to_assume.assume_role_policy.add_statements( - aws_iam.PolicyStatement( - effect=aws_iam.Effect.ALLOW, - principals=[aws_iam.AnyPrincipal()], - actions=["sts:AssumeRole"], - conditions={ - "StringLike": { - "aws:PrincipalArn": [ - f"arn:aws:iam::{account_id}:role/{principal_pattern}" - ] - } - }, - ) - ) - - return role_to_assume + \ No newline at end of file diff --git a/integration_tests/cdk/eoapi_template/vpc.py b/integration_tests/cdk/eoapi_template/vpc.py index bc723f4..b6bd3ba 100644 --- a/integration_tests/cdk/eoapi_template/vpc.py +++ b/integration_tests/cdk/eoapi_template/vpc.py @@ -20,29 +20,20 @@ def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: aws_ec2.SubnetConfiguration( name="ingress", subnet_type=aws_ec2.SubnetType.PUBLIC, cidr_mask=24 ), - aws_ec2.SubnetConfiguration( - name="application", - subnet_type=aws_ec2.SubnetType.PRIVATE_WITH_EGRESS, - cidr_mask=24, - ), - aws_ec2.SubnetConfiguration( - name="rds", - subnet_type=aws_ec2.SubnetType.PRIVATE_ISOLATED, - cidr_mask=24, - ), - ], - nat_gateways=app_config.nat_gateway_count, - ) - - self.vpc.add_gateway_endpoint( - "DynamoDbEndpoint", service=aws_ec2.GatewayVpcEndpointAwsService.DYNAMODB + ] ) + self.vpc.add_interface_endpoint( "SecretsManagerEndpoint", service=aws_ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER, ) - + + self.vpc.add_interface_endpoint( + "CloudWatchEndpoint", + service=aws_ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, + ) + self.export_value( self.vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PUBLIC) .subnets[0] diff --git a/integration_tests/cdk/package-lock.json b/integration_tests/cdk/package-lock.json index 9428ef3..5fa908a 100644 --- a/integration_tests/cdk/package-lock.json +++ b/integration_tests/cdk/package-lock.json @@ -8,13 +8,13 @@ "name": "eoapi-template", "version": "0.1.0", "dependencies": { - "aws-cdk": "^2.81.0" + "aws-cdk": "^2.99.1" } }, "node_modules/aws-cdk": { - "version": "2.96.2", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.96.2.tgz", - "integrity": "sha512-13ERpPV99OFAD75PLOtl0rRMXTWn6bCrmUPwYKkLwIMkj2xWCBiwo2Y9Qg+UzEszm5NMHA1N4ichSvuZ0mt2IQ==", + "version": "2.128.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.128.0.tgz", + "integrity": "sha512-epOAr/0WKqmyaKqBc7N0Ky5++93pu+v6yVN9jNOa4JYkAkGbeTS3vR9bj/W0o94jnlgWevG3HNHr83jtRvw/4A==", "bin": { "cdk": "bin/cdk" }, diff --git a/integration_tests/cdk/package.json b/integration_tests/cdk/package.json index 411cfc3..aaaa2d9 100644 --- a/integration_tests/cdk/package.json +++ b/integration_tests/cdk/package.json @@ -2,7 +2,7 @@ "name": "eoapi-template", "version": "0.1.0", "dependencies": { - "aws-cdk": "^2.81.0" + "aws-cdk": "^2.99.1" } } \ No newline at end of file diff --git a/integration_tests/cdk/requirements.txt b/integration_tests/cdk/requirements.txt index 53f55e7..3761dc1 100644 --- a/integration_tests/cdk/requirements.txt +++ b/integration_tests/cdk/requirements.txt @@ -1,6 +1,6 @@ -aws-cdk-lib>=2.75.0 -aws_cdk.aws_cognito_identitypool_alpha>=2.75.0a0 -aws-cdk.aws-apigatewayv2-alpha==2.95.1a0 +aws-cdk-lib>=2.99.1 +aws_cdk.aws_cognito_identitypool_alpha>=2.99.0a0 +aws-cdk.aws-apigatewayv2-alpha>=2.99.0a0 constructs>=10.0.0,<11.0.0 pydantic==2.0.2 pydantic-settings==2.0.1 diff --git a/integration_tests/tests/README.md b/integration_tests/tests/README.md deleted file mode 100644 index 0dcdaf5..0000000 --- a/integration_tests/tests/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Integration tests - -Standard integration tests for a suite of deployed [eoAPI](https://github.com/developmentseed/eoAPI) services. - -## Environment - -See `eoapi_tests/settings.py` that defines the required and optional environment variables. - -## Installation & Usage - -``` -python -m venv .testing_environment -source .testing_environment/bin/activate -pip install -e . -pytest eoapi_tests -``` \ No newline at end of file diff --git a/integration_tests/tests/eoapi_tests/conftest.py b/integration_tests/tests/eoapi_tests/conftest.py deleted file mode 100644 index ebf9e48..0000000 --- a/integration_tests/tests/eoapi_tests/conftest.py +++ /dev/null @@ -1,22 +0,0 @@ -import pytest -from ingestion import StacIngestion - - -@pytest.fixture(scope="module") -def stac_ingestion_instance(): - return StacIngestion() - - -@pytest.fixture(scope="module") -def test_collection(stac_ingestion_instance): - return stac_ingestion_instance.get_test_collection() - - -@pytest.fixture(scope="module") -def test_item(stac_ingestion_instance): - return stac_ingestion_instance.get_test_item() - - -@pytest.fixture(scope="module") -def test_titiler_search_request(stac_ingestion_instance): - return stac_ingestion_instance.get_test_titiler_search_request() diff --git a/integration_tests/tests/eoapi_tests/fixtures/test_collection.json b/integration_tests/tests/eoapi_tests/fixtures/test_collection.json deleted file mode 100644 index 4d34153..0000000 --- a/integration_tests/tests/eoapi_tests/fixtures/test_collection.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "id":"test_collection", - "type":"Collection", - "links": [], - "title":"Test Collection", - "extent": { - "spatial": { - "bbox": [ - [ - -180, 51.6, 180, 78 - ] - ] - }, - "temporal": { - "interval": [ - [ - "2019-01-01T00:00:00.000Z", - "2021-01-01T00:00:00.000Z" - ] - ] - } - }, - "license":"CC-BY", - "description":"Test collection", - "item_assets": { - "tif": { - "type":"image/tiff; application=geotiff; profile=cloud-optimized", - "roles": [ - "data", - "layer" - ], - "title":"Test collection", - "description":"Test collection" - }, - "csv": { - "type":"text/csv", - "roles": [ - "data" - ], - "title":"CSV", - "description":"Test collection" - } - }, - "stac_version":"1.0.0" -} \ No newline at end of file diff --git a/integration_tests/tests/eoapi_tests/fixtures/test_item.json b/integration_tests/tests/eoapi_tests/fixtures/test_item.json deleted file mode 100644 index bff8ea9..0000000 --- a/integration_tests/tests/eoapi_tests/fixtures/test_item.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "Feature", - "id": "test_item", - "stac_version":"1.0.0", - "collection": "test_collection", - "links":[{"rel":"collection","type":"application/json","href":"https://stac.dit.maap-project.org/collections/test_collection"},{"rel":"parent","type":"application/json","href":"https://stac.dit.maap-project.org/collections/test_collection"},{"rel":"root","type":"application/json","href":"https://stac.dit.maap-project.org/"},{"rel":"self","type":"application/geo+json","href":"https://stac.dit.maap-project.org/collections/test_collection/items/test_item1"}], - "bbox":[-78.40290984426046,51.07724585591961,-77.04127077089376,51.92130718597872], - "assets":{"csv":{"href":"s3://nasa-maap-data-store/file-staging/nasa-map/icesat2-boreal/boreal_agb_202302031675450345_0177_train_data.csv","type":"text/csv","roles":["data"],"title":"CSV","description":"CSV of training data"},"tif":{"href":"s3://nasa-maap-data-store/file-staging/nasa-map/icesat2-boreal/boreal_agb_202302031675450345_0177.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized","roles":["data"],"title":"Cloud Optimized GeoTIFF of boreal data","description":"Cloud Optimized GeoTIFF of boreal data","raster:bands":[{"scale":1.0,"nodata":-9999.0,"offset":0.0,"sampling":"area","data_type":"float32","histogram":{"max":105.78067016601562,"min":7.345508575439453,"count":11,"buckets":[2194,2380,972,469,298,184,85,30,8,4]},"statistics":{"mean":25.281058933423914,"stddev":13.868902983070951,"maximum":105.78067016601562,"minimum":7.345508575439453,"valid_percent":0.6317138671875}},{"scale":1.0,"nodata":-9999.0,"offset":0.0,"sampling":"area","data_type":"float32","histogram":{"max":60.75077819824219,"min":1.4587666988372803,"count":11,"buckets":[5140,1167,250,42,14,4,3,1,2,1]},"statistics":{"mean":5.982097533589976,"stddev":3.7930746502586974,"maximum":60.75077819824219,"minimum":1.4587666988372803,"valid_percent":0.6317138671875}}]}}, - "properties":{"datetime":"2023-02-15T00:00:00+00:00","proj:bbox":[4598521.999999994,5643304.000000009,4688521.999999994,5733304.000000009],"proj:shape":[3000,3000],"proj:geometry":{"type":"Polygon","coordinates":[[[4598521.999999994,5643304.000000009],[4688521.999999994,5643304.000000009],[4688521.999999994,5733304.000000009],[4598521.999999994,5733304.000000009],[4598521.999999994,5643304.000000009]]]},"proj:transform":[30.0,0.0,4598521.999999994,0.0,-30.0,5733304.000000009,0.0,0.0,1.0]}, - "stac_extensions":["https://stac-extensions.github.io/projection/v1.1.0/schema.json","https://stac-extensions.github.io/raster/v1.1.0/schema.json"], - "geometry":{"type":"Polygon","coordinates":[[[14.114837413118664,67.218607039971],[12.438229073696998,67.70894310918132],[11.17860397724852,67.06631312684836],[12.837754767891637,66.58867761064732],[14.114837413118664,67.218607039971]]]} -} \ No newline at end of file diff --git a/integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json b/integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json deleted file mode 100644 index 62aa226..0000000 --- a/integration_tests/tests/eoapi_tests/fixtures/test_titiler_search_request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "collections": ["test_collection"] -} \ No newline at end of file diff --git a/integration_tests/tests/eoapi_tests/ingestion.py b/integration_tests/tests/eoapi_tests/ingestion.py deleted file mode 100644 index 62f3bc9..0000000 --- a/integration_tests/tests/eoapi_tests/ingestion.py +++ /dev/null @@ -1,166 +0,0 @@ -import json -import os - -import boto3 -import pystac -import requests -from pystac import STACValidationError -from settings import eoapiDeploymentSettings - - -class StacIngestion: - - """Class representing various test operations""" - - def __init__(self): - self.eoapi_deployment_settings = eoapiDeploymentSettings() - self.current_file_path = os.path.dirname(os.path.abspath(__file__)) - self.headers = self.get_headers() - - def validate_collection(self, collection): - try: - pystac.validation.validate_dict(collection) - except STACValidationError: - raise STACValidationError("Validation failed for the collection") - - def validate_item(self, item): - try: - pystac.validation.validate_dict(item) - except STACValidationError: - raise STACValidationError("Validation failed for the item") - - def get_authentication_token(self) -> str: - if not self.eoapi_deployment_settings.secret_id: - raise ValueError("You should provide a secret id") - - client = boto3.client("secretsmanager", region_name="us-west-2") - - try: - res_secret = client.get_secret_value( - SecretId=self.eoapi_deployment_settings.secret_id - ) - except client.exceptions.ResourceNotFoundException: - raise Exception( - "Unable to find a secret for " - "{self.eoapi_deployment_settings.secret_id}. " - "\n\nHint: Check your stage and service id." - "Also, verify that the correct " - "AWS_PROFILE is set on your environment." - ) - - # Authentication - Get TOKEN - secret = json.loads(res_secret["SecretString"]) - client_secret = secret["client_secret"] - client_id = secret["client_id"] - cognito_domain = secret["cognito_domain"] - scope = secret["scope"] - - res_token = requests.post( - f"{cognito_domain}/oauth2/token", - headers={ - "Content-Type": "application/x-www-form-urlencoded", - }, - auth=(client_id, client_secret), - data={ - "grant_type": "client_credentials", - # A space-separated list of scopes - # to request for the generated access token. - "scope": scope, - }, - ) - - token = res_token.json()["access_token"] - return token - - def get_headers(self) -> dict: - if self.eoapi_deployment_settings.secret_id: - return { - "headers": { - "Authorization": f"bearer {self.get_authentication_token()}" - } - } - else: - return {"params": {"provided_by": "eoapi-tests"}} - - def insert_collection(self, collection): - response = requests.post( - self.eoapi_deployment_settings.ingestor_url - + self.eoapi_deployment_settings.collections_endpoint, - json=collection, - **self.headers, - ) - return response - - def insert_item(self, item): - response = requests.post( - self.eoapi_deployment_settings.ingestor_url - + self.eoapi_deployment_settings.items_endpoint, - json=item, - **self.headers, - ) - return response - - def query_collection(self, collection_id): - response = requests.get( - self.eoapi_deployment_settings.stac_api_url - + self.eoapi_deployment_settings.collections_endpoint - + f"/{collection_id}" - ) - return response - - def query_items(self, collection_id): - response = requests.get( - self.eoapi_deployment_settings.stac_api_url - + self.eoapi_deployment_settings.collections_endpoint - + f"/{collection_id}/items" - ) - return response - - def register_mosaic(self, search_request): - response = requests.post( - self.eoapi_deployment_settings.titiler_pgstac_api_url + "/mosaic/register", - json=search_request, - ) - return response - - def list_mosaic_assets(self, search_id): - """list the assets of the first tile""" - response = requests.get( - self.eoapi_deployment_settings.titiler_pgstac_api_url - + f"/mosaic/{search_id}/tiles/0/0/0/assets" - ) - return response - - def get_test_collection(self): - with open( - os.path.join(self.current_file_path, "fixtures", "test_collection.json"), - "r", - ) as f: - test_collection = json.load(f) - return test_collection - - def get_test_item(self): - with open( - os.path.join(self.current_file_path, "fixtures", "test_item.json"), "r" - ) as f: - test_item = json.load(f) - return test_item - - def get_test_titiler_search_request(self): - with open( - os.path.join( - self.current_file_path, "fixtures", "test_titiler_search_request.json" - ), - "r", - ) as f: - test_titiler_search_request = json.load(f) - return test_titiler_search_request - - def delete_collection(self, collection_id): - response = requests.delete( - self.eoapi_deployment_settings.ingestor_url - + self.eoapi_deployment_settings.collections_endpoint - + f"/{collection_id}", - **self.headers, - ) - return response diff --git a/integration_tests/tests/eoapi_tests/settings.py b/integration_tests/tests/eoapi_tests/settings.py deleted file mode 100644 index de5d60e..0000000 --- a/integration_tests/tests/eoapi_tests/settings.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Optional - -from pydantic_settings import BaseSettings - - -class eoapiDeploymentSettings(BaseSettings): - ingestor_url: str - stac_api_url: str - titiler_pgstac_api_url: str - secret_id: Optional[str] = None - collections_endpoint: Optional[str] = "/collections" - items_endpoint: Optional[str] = "/ingestions" diff --git a/integration_tests/tests/eoapi_tests/test_stac_ingestion.py b/integration_tests/tests/eoapi_tests/test_stac_ingestion.py deleted file mode 100644 index d09344a..0000000 --- a/integration_tests/tests/eoapi_tests/test_stac_ingestion.py +++ /dev/null @@ -1,123 +0,0 @@ -import time - -import pystac -import pytest - -INSERTION_WAIT_TIME = 10 - -# Test validating the collection -pytest.mark.order(0) - - -def test_validate_collection(test_collection): - pystac.validation.validate_dict(test_collection) - - -# Test validating the item -pytest.mark.order(1) - - -def test_validate_item(test_item): - pystac.validation.validate_dict(test_item) - - -# Test inserting collection -pytest.mark.order(2) - - -def test_insert_collection(stac_ingestion_instance, test_collection): - response = stac_ingestion_instance.insert_collection(test_collection) - assert response.status_code in [ - 200, - 201, - ], f"Failed to insert the test_collection :\n{response.text}" - # Wait for the collection to be inserted - time.sleep(INSERTION_WAIT_TIME) - - -# Test inserting item -pytest.mark.order(3) - - -def test_insert_item(stac_ingestion_instance, test_item): - response = stac_ingestion_instance.insert_item(test_item) - assert response.status_code in [ - 200, - 201, - ], f"Failed to insert the test_item :\n{response.text}" - # Wait for the item to be inserted - time.sleep(INSERTION_WAIT_TIME) - - -# Test querying collection and verifying inserted collection -pytest.mark.order(4) - - -def test_query_collection(stac_ingestion_instance, test_collection): - response = stac_ingestion_instance.query_collection(test_collection["id"]) - assert response.status_code in [ - 200, - 201, - ], f"Failed to query the test_collection :\n{response.text}" - - -# Test registering a mosaic and querying its assets -pytest.mark.order(5) - - -def test_titiler_pgstac( - stac_ingestion_instance, test_titiler_search_request, test_item -): - register_response = stac_ingestion_instance.register_mosaic( - test_titiler_search_request - ) - assert register_response.status_code in [ - 200, - 201, - ], f"Failed to register the mosaic :\n{register_response.text}" - search_id = register_response.json()["searchid"] - # allow for some time for the mosaic to be inserted - time.sleep(INSERTION_WAIT_TIME) - asset_query_response = stac_ingestion_instance.list_mosaic_assets(search_id) - assert asset_query_response.status_code in [ - 200, - 201, - ], "Failed to query the mosaic's assets" - "for mosaic {search_id} :\n{asset_query_response.text}" - assets_json = asset_query_response.json() - # expects a single item in the collection - assert len(assets_json) == 1 - assert all([k in assets_json[0]["assets"] for k in test_item["assets"].keys()]) - - -# Test querying items and verifying inserted items -pytest.mark.order(6) - - -def test_query_items(stac_ingestion_instance, test_collection, test_item): - response = stac_ingestion_instance.query_items(test_collection["id"]) - assert response.status_code in [ - 200, - 201, - ], f"Failed to query the items :\n{response.text}" - item = response.json()["features"][0] - assert ( - item["id"] == test_item["id"] - ), f"Inserted item - {test_item} \n not found in the queried items {item}" - - -# Test querying collection and verifying inserted collection -pytest.mark.order(7) - - -def test_delete_collection(stac_ingestion_instance, test_collection): - response = stac_ingestion_instance.delete_collection(test_collection["id"]) - assert response.status_code in [ - 200, - 201, - ], f"Failed to delete the test_collection :\n{response.text}" - - -# Run the tests -if __name__ == "__main__": - pytest.main() diff --git a/integration_tests/tests/pyproject.toml b/integration_tests/tests/pyproject.toml deleted file mode 100644 index 06213cc..0000000 --- a/integration_tests/tests/pyproject.toml +++ /dev/null @@ -1,21 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel"] - -[project] -name = "eoapi_tests" -version = "0.1.0" -description = "test suite for eoAPI deployments" -authors = [ - {name = "Emile Tenezakis", email = "emile@developmentseed.org"} -] -license = {file = "LICENSE"} - -dependencies = [ - "pytest==7.4.0", - "boto3==1.28.39", - "pystac==1.8.3", - "pystac[validation]==1.8.3", - "requests==2.31.0", - "pydantic-settings==2.0.3", - "pytest-order==1.1.0" -] \ No newline at end of file diff --git a/lib/database/bootstrapper_runtime/handler.py b/lib/database/bootstrapper_runtime/handler.py index 0334729..9313d3c 100644 --- a/lib/database/bootstrapper_runtime/handler.py +++ b/lib/database/bootstrapper_runtime/handler.py @@ -58,7 +58,7 @@ def send( headers = {"content-type": "", "content-length": str(len(json_responseBody))} try: - response = httpx.put(responseUrl, data=json_responseBody, headers=headers) + response = httpx.put(responseUrl, data=json_responseBody, headers=headers, timeout=30) print("Status code: " + response.status_code) except Exception as e: print("send(..) failed executing httpx.put(..): " + str(e)) diff --git a/lib/database/index.ts b/lib/database/index.ts index 481f322..99ff510 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -73,8 +73,7 @@ export class PgStacDatabase extends Construct { // overwrites defaults with user-provided configurable properties ...props.bootstrapperLambdaFunctionOptions, // Non configurable properties that are going to be overwritten even if provided by the user - vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, - allowPublicSubnet: true + vpc: hasVpc(this.db) ? this.db.vpc : props.vpc }); this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { From c7904ef70f70194a7c6d1d0107cef27cdbcb9224 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Tue, 20 Feb 2024 10:50:47 -0500 Subject: [PATCH 23/30] lint --- integration_tests/cdk/config.py | 1 + integration_tests/cdk/eoapi_template/pgStacInfra.py | 10 ++++------ integration_tests/cdk/eoapi_template/vpc.py | 5 ++--- lib/database/bootstrapper_runtime/handler.py | 8 +++++++- lib/ingestor-api/runtime/src/loader.py | 1 + lib/ingestor-api/runtime/src/services.py | 2 +- lib/stac-api/runtime/src/config.py | 1 + 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/integration_tests/cdk/config.py b/integration_tests/cdk/config.py index 0bc6c46..b1ebcdd 100644 --- a/integration_tests/cdk/config.py +++ b/integration_tests/cdk/config.py @@ -5,6 +5,7 @@ from pydantic_core.core_schema import FieldValidationInfo from pydantic_settings import BaseSettings, SettingsConfigDict + class AppConfig(BaseSettings): model_config = SettingsConfigDict( env_file=".env" diff --git a/integration_tests/cdk/eoapi_template/pgStacInfra.py b/integration_tests/cdk/eoapi_template/pgStacInfra.py index f4d70b0..e585ae0 100644 --- a/integration_tests/cdk/eoapi_template/pgStacInfra.py +++ b/integration_tests/cdk/eoapi_template/pgStacInfra.py @@ -36,17 +36,16 @@ def __init__( version=aws_rds.PostgresEngineVersion.VER_14 ), vpc_subnets=aws_ec2.SubnetSelection( - subnet_type=aws_ec2.SubnetType.PUBLIC + subnet_type=aws_ec2.SubnetType.PUBLIC, ), allocated_storage=app_config.db_allocated_storage, instance_type=aws_ec2.InstanceType(app_config.db_instance_type), - bootstrapper_lambda_function_options={ + bootstrapper_lambda_function_options={ "allow_public_subnet": True, } ) - - pgstac_db.db.connections.allow_default_port_from_any_ipv4() + pgstac_db.db.connections.allow_default_port_from_any_ipv4() PgStacApiLambda( self, @@ -71,6 +70,5 @@ def __init__( buckets=[], lambda_function_options={ "allow_public_subnet": True, - }, + }, ) - \ No newline at end of file diff --git a/integration_tests/cdk/eoapi_template/vpc.py b/integration_tests/cdk/eoapi_template/vpc.py index b6bd3ba..fdcc495 100644 --- a/integration_tests/cdk/eoapi_template/vpc.py +++ b/integration_tests/cdk/eoapi_template/vpc.py @@ -23,17 +23,16 @@ def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: ] ) - self.vpc.add_interface_endpoint( "SecretsManagerEndpoint", service=aws_ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER, ) - + self.vpc.add_interface_endpoint( "CloudWatchEndpoint", service=aws_ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, ) - + self.export_value( self.vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PUBLIC) .subnets[0] diff --git a/lib/database/bootstrapper_runtime/handler.py b/lib/database/bootstrapper_runtime/handler.py index 9313d3c..317e5a0 100644 --- a/lib/database/bootstrapper_runtime/handler.py +++ b/lib/database/bootstrapper_runtime/handler.py @@ -2,6 +2,7 @@ Custom resource lambda handler to bootstrap Postgres db. Source: https://github.com/developmentseed/eoAPI/blob/master/deployment/handlers/db_handler.py """ + import json import boto3 @@ -58,7 +59,12 @@ def send( headers = {"content-type": "", "content-length": str(len(json_responseBody))} try: - response = httpx.put(responseUrl, data=json_responseBody, headers=headers, timeout=30) + response = httpx.put( + responseUrl, + data=json_responseBody, + headers=headers, + timeout=30, + ) print("Status code: " + response.status_code) except Exception as e: print("send(..) failed executing httpx.put(..): " + str(e)) diff --git a/lib/ingestor-api/runtime/src/loader.py b/lib/ingestor-api/runtime/src/loader.py index 82fa7f9..5cbd486 100644 --- a/lib/ingestor-api/runtime/src/loader.py +++ b/lib/ingestor-api/runtime/src/loader.py @@ -1,4 +1,5 @@ """Utilities to bulk load data into pgstac from json/ndjson.""" + import logging from pypgstac.load import Loader as BaseLoader diff --git a/lib/ingestor-api/runtime/src/services.py b/lib/ingestor-api/runtime/src/services.py index ee827a5..ef263fe 100644 --- a/lib/ingestor-api/runtime/src/services.py +++ b/lib/ingestor-api/runtime/src/services.py @@ -40,5 +40,5 @@ def fetch_many( } -class NotInDb(Exception): +class NotInDb(Exception): # noqa ... diff --git a/lib/stac-api/runtime/src/config.py b/lib/stac-api/runtime/src/config.py index 27dfe6d..948dc8a 100644 --- a/lib/stac-api/runtime/src/config.py +++ b/lib/stac-api/runtime/src/config.py @@ -1,5 +1,6 @@ """API settings. Based on https://github.com/developmentseed/eoAPI/tree/master/src/eoapi/stac""" + import base64 import json from typing import Optional From 92b4bf9045bef3ef81bd4ebd1e3be496f2627802 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Tue, 20 Feb 2024 11:00:29 -0500 Subject: [PATCH 24/30] fix moto requirement --- lib/ingestor-api/runtime/dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ingestor-api/runtime/dev_requirements.txt b/lib/ingestor-api/runtime/dev_requirements.txt index 4270b89..81c05ba 100644 --- a/lib/ingestor-api/runtime/dev_requirements.txt +++ b/lib/ingestor-api/runtime/dev_requirements.txt @@ -1,2 +1,2 @@ httpx -moto[dynamodb, ssm]>=4.0.9 \ No newline at end of file +moto[dynamodb, ssm]>=4.0.9,<5.0 From 97b5a3209a6710489ec8812f807c94a26d5076ed Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 13:08:12 +0300 Subject: [PATCH 25/30] test to fix deployment : try adding s3 endpoint and force allow public subnet --- integration_tests/cdk/config.py | 2 +- integration_tests/cdk/eoapi_template/vpc.py | 4 ++++ lib/database/index.ts | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/integration_tests/cdk/config.py b/integration_tests/cdk/config.py index b1ebcdd..729aa8c 100644 --- a/integration_tests/cdk/config.py +++ b/integration_tests/cdk/config.py @@ -14,7 +14,7 @@ class AppConfig(BaseSettings): description="AWS account ID" ) project_id: str = pydantic.Field( - description="Project ID", default="eoapi-cdk-integration" + description="Project ID", default="eoapi-cdk" ) stage: str = pydantic.Field(description="Stage of deployment", default="test") # because of its validator, `tags` should always come after `project_id` and `stage` diff --git a/integration_tests/cdk/eoapi_template/vpc.py b/integration_tests/cdk/eoapi_template/vpc.py index fdcc495..600f189 100644 --- a/integration_tests/cdk/eoapi_template/vpc.py +++ b/integration_tests/cdk/eoapi_template/vpc.py @@ -32,6 +32,10 @@ def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: "CloudWatchEndpoint", service=aws_ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, ) + + self.vpc.add_gateway_endpoint( + "S3", service=aws_ec2.GatewayVpcEndpointAwsService.S3 + ) self.export_value( self.vpc.select_subnets(subnet_type=aws_ec2.SubnetType.PUBLIC) diff --git a/lib/database/index.ts b/lib/database/index.ts index 99ff510..b930e32 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -73,7 +73,8 @@ export class PgStacDatabase extends Construct { // overwrites defaults with user-provided configurable properties ...props.bootstrapperLambdaFunctionOptions, // Non configurable properties that are going to be overwritten even if provided by the user - vpc: hasVpc(this.db) ? this.db.vpc : props.vpc + vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, + allowPublicSubnet: true, }); this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { From 4ede0a5ab42c4288ffc8d868ee42857e0281a7fc Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 17:18:02 +0300 Subject: [PATCH 26/30] lint and make lambda functions more configurable --- .../cdk/eoapi_template/pgStacInfra.py | 7 ++---- integration_tests/cdk/eoapi_template/vpc.py | 2 +- lib/database/index.ts | 9 +++---- lib/ingestor-api/index.ts | 25 ++++++++----------- lib/stac-api/index.ts | 9 +++---- lib/tipg-api/index.ts | 9 +++---- lib/titiler-pgstac-api/index.ts | 9 +++---- tox.ini | 4 +-- 8 files changed, 32 insertions(+), 42 deletions(-) diff --git a/integration_tests/cdk/eoapi_template/pgStacInfra.py b/integration_tests/cdk/eoapi_template/pgStacInfra.py index e585ae0..cbb5491 100644 --- a/integration_tests/cdk/eoapi_template/pgStacInfra.py +++ b/integration_tests/cdk/eoapi_template/pgStacInfra.py @@ -1,7 +1,7 @@ from aws_cdk import ( Stack, aws_ec2, - aws_rds, + aws_rds ) from constructs import Construct from eoapi_cdk import ( @@ -39,10 +39,7 @@ def __init__( subnet_type=aws_ec2.SubnetType.PUBLIC, ), allocated_storage=app_config.db_allocated_storage, - instance_type=aws_ec2.InstanceType(app_config.db_instance_type), - bootstrapper_lambda_function_options={ - "allow_public_subnet": True, - } + instance_type=aws_ec2.InstanceType(app_config.db_instance_type) ) pgstac_db.db.connections.allow_default_port_from_any_ipv4() diff --git a/integration_tests/cdk/eoapi_template/vpc.py b/integration_tests/cdk/eoapi_template/vpc.py index 600f189..d17967f 100644 --- a/integration_tests/cdk/eoapi_template/vpc.py +++ b/integration_tests/cdk/eoapi_template/vpc.py @@ -32,7 +32,7 @@ def __init__(self, scope: Construct, app_config: AppConfig, **kwargs) -> None: "CloudWatchEndpoint", service=aws_ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, ) - + self.vpc.add_gateway_endpoint( "S3", service=aws_ec2.GatewayVpcEndpointAwsService.S3 ) diff --git a/lib/database/index.ts b/lib/database/index.ts index b930e32..2e64efe 100644 --- a/lib/database/index.ts +++ b/lib/database/index.ts @@ -60,7 +60,7 @@ export class PgStacDatabase extends Construct { }); const handler = new aws_lambda.Function(this, "lambda", { - // defaults for configurable properties + // defaults runtime: aws_lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 128, @@ -70,11 +70,10 @@ export class PgStacDatabase extends Construct { file: "bootstrapper_runtime/Dockerfile", buildArgs: {PGSTAC_VERSION: DEFAULT_PGSTAC_VERSION, PYTHON_VERSION: "3.11"} }), - // overwrites defaults with user-provided configurable properties - ...props.bootstrapperLambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user vpc: hasVpc(this.db) ? this.db.vpc : props.vpc, allowPublicSubnet: true, + // overwrites defaults with user-provided configurable properties, + ...props.bootstrapperLambdaFunctionOptions, }); this.pgstacSecret = new secretsmanager.Secret(this, "bootstrappersecret", { @@ -204,7 +203,7 @@ export interface PgStacDatabaseProps extends rds.DatabaseInstanceProps { } /** - * Optional settings for the bootstrapper lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - defined in the construct. */ diff --git a/lib/ingestor-api/index.ts b/lib/ingestor-api/index.ts index c36baad..e39f7ce 100644 --- a/lib/ingestor-api/index.ts +++ b/lib/ingestor-api/index.ts @@ -115,7 +115,7 @@ export class StacIngestor extends Construct { }): lambda.Function { const handler = new lambda.Function(this, "api-handler", { - // defaults for configurable properties + // defaults runtime: lambda.Runtime.PYTHON_3_11, handler: "src.handler.handler", memorySize: 2048, @@ -125,14 +125,13 @@ export class StacIngestor extends Construct { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.11' }, }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user + allowPublicSubnet: true, vpc: props.dbVpc, vpcSubnets: props.subnetSelection, - allowPublicSubnet: true, environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, - role: this.handlerRole + role: this.handlerRole, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, }); // Allow handler to read DB secret @@ -165,7 +164,7 @@ export class StacIngestor extends Construct { const handler = new lambda.Function(this, "stac-ingestor",{ - // defaults for configurable properties + // defaults runtime: lambda.Runtime.PYTHON_3_11, handler: "src.ingestor.handler", memorySize: 2048, @@ -175,15 +174,13 @@ export class StacIngestor extends Construct { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.11' }, }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - - // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.dbVpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, environment: { DB_SECRET_ARN: props.dbSecret.secretArn, ...props.env }, - role: this.handlerRole + role: this.handlerRole, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, }); // Allow handler to read DB secret @@ -322,14 +319,14 @@ export interface StacIngestorProps { readonly ingestorDomainNameOptions?: apigateway.DomainNameOptions; /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - default settings are defined in the construct. */ readonly apiLambdaFunctionOptions?: CustomLambdaFunctionProps; /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - default settings are defined in the construct. */ diff --git a/lib/stac-api/index.ts b/lib/stac-api/index.ts index bcfe434..77c4bc2 100644 --- a/lib/stac-api/index.ts +++ b/lib/stac-api/index.ts @@ -23,7 +23,7 @@ export class PgStacApiLambda extends Construct { console.log(props) console.log(props.lambdaFunctionOptions); this.stacApiLambdaFunction = new lambda.Function(this, "lambda", { - // defaults for configurable properties + // defaults runtime: lambda.Runtime.PYTHON_3_11, handler: "src.handler.handler", memorySize: 8192, @@ -33,9 +33,6 @@ export class PgStacApiLambda extends Construct { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.11' }, }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -45,6 +42,8 @@ export class PgStacApiLambda extends Construct { DB_MAX_CONN_SIZE: "1", ...props.apiEnv, }, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions }); props.dbSecret.grantRead(this.stacApiLambdaFunction); @@ -99,7 +98,7 @@ export interface PgStacApiLambdaProps { readonly stacApiDomainName?: IDomainName; /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - defined in the construct. */ diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index d50518b..d40a3df 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -21,7 +21,7 @@ import { super(scope, id); this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { - // defaults for configurable properties + // defaults runtime: lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 1024, @@ -31,9 +31,6 @@ import { file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.11' }, }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, @@ -43,6 +40,8 @@ import { DB_MAX_CONN_SIZE: "1", ...props.apiEnv, }, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions }); props.dbSecret.grantRead(this.tiPgLambdaFunction); @@ -103,7 +102,7 @@ import { /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - defined in the construct. */ diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index 7ac8fe9..726744d 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -39,7 +39,7 @@ import { CustomLambdaFunctionProps } from "../utils"; super(scope, id); this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { - // defaults for configurable properties + // defaults runtime: lambda.Runtime.PYTHON_3_11, handler: "handler.handler", memorySize: 3008, @@ -49,14 +49,13 @@ import { CustomLambdaFunctionProps } from "../utils"; file: "runtime/Dockerfile", buildArgs: { PYTHON_VERSION: '3.11' } }), - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - // Non configurable properties that are going to be overwritten even if provided by the user vpc: props.vpc, vpcSubnets: props.subnetSelection, allowPublicSubnet: true, // if user provided environment variables, merge them with the defaults. environment: props.apiEnv ? { ...defaultTitilerPgstacEnv, ...props.apiEnv, "PGSTAC_SECRET_ARN": props.dbSecret.secretArn } : defaultTitilerPgstacEnv, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, }); // grant access to buckets using addToRolePolicy @@ -132,7 +131,7 @@ import { CustomLambdaFunctionProps } from "../utils"; readonly titilerPgstacApiDomainName?: IDomainName; /** - * Optional settings for the lambda function. Can be anything that can be configured on the lambda function, but some will be overwritten by values defined here. + * Can be used to override the default lambda function properties. * * @default - defined in the construct. */ diff --git a/tox.ini b/tox.ini index 3b42230..79db331 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ exclude = __pycache__ .git .tox - venv* + *venv* toxenv* devenv* cdk.out @@ -38,7 +38,7 @@ exclude = __pycache__ .git .tox - venv* + *venv* toxenv* devenv* cdk.out From b4de0358eadd3cdabae3076ea4b8189b572d7bcd Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 18:42:12 +0300 Subject: [PATCH 27/30] moving deploy to a separate workflow --- .github/workflows/build.yaml | 79 +----------------------- .github/workflows/build_and_release.yaml | 4 -- .github/workflows/deploy.yaml | 77 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 81 deletions(-) create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d9d635e..4a206d1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,24 +12,11 @@ on: required: false DS_RELEASE_BOT_PRIVATE_KEY: required: false - AWS_DEFAULT_REGION_DEPLOY: - required: false - AWS_ACCESS_KEY_ID_DEPLOY: - required: false - AWS_SECRET_ACCESS_KEY_DEPLOY: - required: false - AWS_ACCOUNT_ID: - required: false + jobs: build_and_package: name: Build and package runs-on: ubuntu-latest - timeout-minutes: 60 - env: - AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} - AWS_DEFAULT_ACCOUNT: ${{ secrets.AWS_ACCOUNT_ID }} steps: - uses: actions/checkout@v3 @@ -78,70 +65,8 @@ jobs: app_id: ${{ secrets.DS_RELEASE_BOT_ID }} private_key: ${{ secrets.DS_RELEASE_BOT_PRIVATE_KEY }} - - name: Check release - id: check_release - if: ${{ inputs.release }} - run: | - SHOULD_RELEASE=false - npm run semantic-release --dry-run > check_release_output.txt - if grep -q "Published release" check_release_output.txt; then - echo "SHOULD_RELEASE=true" >> $GITHUB_OUTPUT - else - echo "SHOULD_RELEASE=false" >> $GITHUB_OUTPUT - fi - - - name: Install deployment environment - if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" - id: install_deploy_env - run: | - # install deployment environment with eoapi-cdk from build - python -m venv .deployment_venv - source .deployment_venv/bin/activate - pip install dist/python/*.gz - cd integration_tests/cdk - pip install -r requirements.txt - npm install - deactivate - cd - - - - - name: Deploy test stack - if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" - id: deploy_step - run: | - source .deployment_venv/bin/activate - - # synthesize the stack - cd integration_tests/cdk - npx cdk synth --debug --all --require-approval never - - # deploy the stack and grab URLs for testing - npx cdk deploy --ci --all --require-approval never - echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - deactivate - cd - - - - name: Tear down any infrastructure - if: always() - run: | - cd integration_tests/cdk - # run this only if we find a 'cdk.out' directory, which means there might be things to tear down - if [ -d "cdk.out" ]; then - cd - - source .deployment_venv/bin/activate - cd integration_tests/cdk - # see https://github.com/aws/aws-cdk/issues/24946 - rm -f cdk.out/synth.lock - npx cdk destroy --ci --all --force - fi - - - # run if the previous step set SHOULD_RELEASE to true - name: Maybe Release 🚀 - # only run if the previous step set SHOULD_RELEASE to true - if: "${{ inputs.release && steps.check_release.outputs.SHOULD_RELEASE }}" + if: "${{ inputs.release }}" run: | npm run semantic-release env: diff --git a/.github/workflows/build_and_release.yaml b/.github/workflows/build_and_release.yaml index e252d72..83219c0 100644 --- a/.github/workflows/build_and_release.yaml +++ b/.github/workflows/build_and_release.yaml @@ -11,7 +11,3 @@ jobs: secrets: DS_RELEASE_BOT_ID: ${{ secrets.DS_RELEASE_BOT_ID }} DS_RELEASE_BOT_PRIVATE_KEY: ${{ secrets.DS_RELEASE_BOT_PRIVATE_KEY }} - AWS_DEFAULT_REGION_DEPLOY: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} - AWS_ACCESS_KEY_ID_DEPLOY: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} - AWS_SECRET_ACCESS_KEY_DEPLOY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} - AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..c279f7e --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,77 @@ +name: Test deployment + +on: + merge_group: + +jobs: + build_package_and_deploy: + name: Build, package and deploy + runs-on: ubuntu-latest + timeout-minutes: 60 + env: + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION_DEPLOY }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEPLOY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEPLOY }} + AWS_DEFAULT_ACCOUNT: ${{ secrets.AWS_ACCOUNT_ID }} + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: "npm" + + - name: Install Dependencies + run: npm ci + + - name: Compile project + run: npm run build + + - name: Generate distribution packages + run: npm run package + + + - name: Install deployment environment + id: install_deploy_env + run: | + # install deployment environment with eoapi-cdk from build + python -m venv .deployment_venv + source .deployment_venv/bin/activate + pip install dist/python/*.gz + cd integration_tests/cdk + pip install -r requirements.txt + npm install + deactivate + cd - + + + - name: Deploy test stack + id: deploy_step + run: | + source .deployment_venv/bin/activate + + # synthesize the stack + cd integration_tests/cdk + npx cdk synth --debug --all --require-approval never + + # deploy the stack and grab URLs for testing + npx cdk deploy --ci --all --require-approval never + echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT + deactivate + cd - + + - name: Tear down any infrastructure + if: always() + run: | + cd integration_tests/cdk + # run this only if we find a 'cdk.out' directory, which means there might be things to tear down + if [ -d "cdk.out" ]; then + cd - + source .deployment_venv/bin/activate + cd integration_tests/cdk + # see https://github.com/aws/aws-cdk/issues/24946 + rm -f cdk.out/synth.lock + npx cdk destroy --ci --all --force + fi From 92cec0da607778aed114dac5099ff733fa73628c Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 19:00:11 +0300 Subject: [PATCH 28/30] remove useless dependencies in deployment tests, turn on pull request trigger to check the action works --- .github/workflows/deploy.yaml | 13 ++++++------- integration_tests/cdk/requirements.txt | 8 -------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c279f7e..8f34c33 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,7 +1,9 @@ name: Test deployment on: + pull_request: merge_group: + branches: [ main ] jobs: build_package_and_deploy: @@ -54,11 +56,8 @@ jobs: cd integration_tests/cdk npx cdk synth --debug --all --require-approval never - # deploy the stack and grab URLs for testing + # deploy the stack npx cdk deploy --ci --all --require-approval never - echo "ingestor_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'stacingestor')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "stac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'pgstacapi')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT - echo "titiler_pgstac_api_url=$(aws cloudformation describe-stacks --stack-name eoapi-cdk-integration-test-pgSTAC-infra --query "Stacks[0].Outputs[?starts_with(OutputKey, 'titilerpgstac')].OutputValue | [0]" --output text)" >> $GITHUB_OUTPUT deactivate cd - @@ -67,11 +66,11 @@ jobs: run: | cd integration_tests/cdk # run this only if we find a 'cdk.out' directory, which means there might be things to tear down + # see https://github.com/aws/aws-cdk/issues/24946 + # rm -f cdk.out/synth.lock if [ -d "cdk.out" ]; then cd - source .deployment_venv/bin/activate cd integration_tests/cdk - # see https://github.com/aws/aws-cdk/issues/24946 - rm -f cdk.out/synth.lock - npx cdk destroy --ci --all --force + npx cdk destroy --output cdk-destroy.out --ci --all --force fi diff --git a/integration_tests/cdk/requirements.txt b/integration_tests/cdk/requirements.txt index 3761dc1..540a518 100644 --- a/integration_tests/cdk/requirements.txt +++ b/integration_tests/cdk/requirements.txt @@ -1,15 +1,7 @@ aws-cdk-lib>=2.99.1 -aws_cdk.aws_cognito_identitypool_alpha>=2.99.0a0 -aws-cdk.aws-apigatewayv2-alpha>=2.99.0a0 constructs>=10.0.0,<11.0.0 pydantic==2.0.2 pydantic-settings==2.0.1 -black==22.3.0 -boto3==1.24.15 -boto3-stubs[cognito-idp,cognito-identity] -flake8==4.0.1 -click==8.1.3 -requests==2.28.0 python-dotenv==1.0.0 pyyaml==6.0 types-PyYAML==6.0.12.10 From b4fe0f6d480b652e67763802a0aa281e010a42d5 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 19:23:00 +0300 Subject: [PATCH 29/30] when tearing down the infrastructure, synthesize the cloud formation assets into another directory to avoid conflicts --- .github/workflows/deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8f34c33..82245aa 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,7 +1,6 @@ name: Test deployment on: - pull_request: merge_group: branches: [ main ] @@ -66,11 +65,12 @@ jobs: run: | cd integration_tests/cdk # run this only if we find a 'cdk.out' directory, which means there might be things to tear down - # see https://github.com/aws/aws-cdk/issues/24946 - # rm -f cdk.out/synth.lock if [ -d "cdk.out" ]; then cd - source .deployment_venv/bin/activate cd integration_tests/cdk + # see https://github.com/aws/aws-cdk/issues/24946 + # this didn't work : rm -f cdk.out/synth.lock + # so we just duplicate the cdk output to cdk-destroy.out npx cdk destroy --output cdk-destroy.out --ci --all --force fi From b08e8b328c490743bf951ff8711484a6ccac3735 Mon Sep 17 00:00:00 2001 From: emileten Date: Wed, 21 Feb 2024 19:37:24 +0300 Subject: [PATCH 30/30] update readmes and revive the artifact download in python distribution --- .github/workflows/distribute.yaml | 6 ++++++ README.md | 2 +- integration_tests/cdk/README.md | 6 ++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/distribute.yaml b/.github/workflows/distribute.yaml index fd7fe8a..960d4a4 100644 --- a/.github/workflows/distribute.yaml +++ b/.github/workflows/distribute.yaml @@ -13,6 +13,12 @@ jobs: runs-on: ubuntu-latest needs: package steps: + + - uses: actions/download-artifact@v3 + with: + name: python + path: dist + - run: pip install twine - run: twine upload dist/* diff --git a/README.md b/README.md index 7dbc51a..698f7a2 100644 --- a/README.md +++ b/README.md @@ -56,4 +56,4 @@ _Warning_: If you rebase `main`, you must ensure that the commits referenced by ## Tests -Each new release triggers an integration test against a running deployment that uses the newly releases constructs. See the corresponding [github workflow](https://github.com/developmentseed/eoapi-cdk/blob/main/.github/workflows/deploy.yaml) and the [tests definition](https://github.com/developmentseed/eoapi-cdk/blob/main/tests). \ No newline at end of file +Each pull request to `main` is added to a [merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#triggering-merge-group-checks-with-github-actions) so that a "deployment test" workflow can run before the merge actually happens. If the deployment fails, the merge is cancelled. Here is [the definition of this workflow](https://github.com/developmentseed/eoapi-cdk/blob/main/.github/workflows/deploy.yaml) and the [tests definition](https://github.com/developmentseed/eoapi-cdk/blob/main/tests). \ No newline at end of file diff --git a/integration_tests/cdk/README.md b/integration_tests/cdk/README.md index 3711c67..18232d4 100644 --- a/integration_tests/cdk/README.md +++ b/integration_tests/cdk/README.md @@ -1,8 +1,7 @@ -This is a non-forked version of [eoapi-template](https://github.com/developmentseed/eoapi-template). -# Deployment CDK code for eoapi-cdk integration tests +# Deployment CDK code for eoapi-cdk deployment tests -This is a wrapper CDK code that provides the `eoapi-cdk` deployment to run integration tests on the latest releases of the `eoapi-cdk` constructs. +This is a wrapper CDK code that is used to test a deployment of the `eoapi-cdk` constructs before a release happens. ## Requirements @@ -10,7 +9,6 @@ This is a wrapper CDK code that provides the `eoapi-cdk` deployment to run integ - docker - node - AWS credentials environment variables configured to point to an account. -- **Optional** a `config.yaml` file to override the default deployment settings defined in `config.py`. ## Installation