vpcatlas 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vpcatlas-0.4.2/.gitattributes +3 -0
- vpcatlas-0.4.2/.github/CODEOWNERS +2 -0
- vpcatlas-0.4.2/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- vpcatlas-0.4.2/.github/actions/setup/action.yml +29 -0
- vpcatlas-0.4.2/.github/dependabot.yml +43 -0
- vpcatlas-0.4.2/.github/workflows/ci.yml +552 -0
- vpcatlas-0.4.2/.github/workflows/codeql.yml +46 -0
- vpcatlas-0.4.2/.github/workflows/nightly.yml +174 -0
- vpcatlas-0.4.2/.github/workflows/release.yml +649 -0
- vpcatlas-0.4.2/.github/workflows/scorecard.yml +56 -0
- vpcatlas-0.4.2/.gitignore +24 -0
- vpcatlas-0.4.2/.pre-commit-config.yaml +7 -0
- vpcatlas-0.4.2/CHANGELOG.md +228 -0
- vpcatlas-0.4.2/CODE_OF_CONDUCT.md +106 -0
- vpcatlas-0.4.2/CONTRIBUTING.md +168 -0
- vpcatlas-0.4.2/LICENSE +21 -0
- vpcatlas-0.4.2/Makefile +52 -0
- vpcatlas-0.4.2/PKG-INFO +350 -0
- vpcatlas-0.4.2/README.md +316 -0
- vpcatlas-0.4.2/SECURITY.md +54 -0
- vpcatlas-0.4.2/demo/README.md +106 -0
- vpcatlas-0.4.2/demo/fake_aws.py +1149 -0
- vpcatlas-0.4.2/demo/vpcatlas.tape +47 -0
- vpcatlas-0.4.2/deploy/Dockerfile +75 -0
- vpcatlas-0.4.2/deploy/Dockerfile.dockerignore +25 -0
- vpcatlas-0.4.2/deploy/Dockerfile.lambda +45 -0
- vpcatlas-0.4.2/deploy/Dockerfile.lambda.dockerignore +25 -0
- vpcatlas-0.4.2/deploy/docker-compose.yml +59 -0
- vpcatlas-0.4.2/deploy/ecs/README.md +57 -0
- vpcatlas-0.4.2/deploy/ecs/task-definition.json +60 -0
- vpcatlas-0.4.2/deploy/iam/auditor-policy.json +264 -0
- vpcatlas-0.4.2/deploy/kubernetes/deployment.yaml +96 -0
- vpcatlas-0.4.2/deploy/kubernetes/service.yaml +18 -0
- vpcatlas-0.4.2/deploy/kubernetes/serviceaccount.yaml +14 -0
- vpcatlas-0.4.2/deploy/lambda/README.md +102 -0
- vpcatlas-0.4.2/docs/architecture.md +59 -0
- vpcatlas-0.4.2/docs/assets/dashboard.png +0 -0
- vpcatlas-0.4.2/docs/assets/demo.gif +0 -0
- vpcatlas-0.4.2/docs/assets/region-eu-west-1.png +0 -0
- vpcatlas-0.4.2/docs/assets/vpc-prod.png +0 -0
- vpcatlas-0.4.2/docs/changelog.md +234 -0
- vpcatlas-0.4.2/docs/cli/diagram.md +100 -0
- vpcatlas-0.4.2/docs/cli/discover.md +106 -0
- vpcatlas-0.4.2/docs/cli/exit-codes.md +23 -0
- vpcatlas-0.4.2/docs/cli/iam-policy.md +81 -0
- vpcatlas-0.4.2/docs/cli/index.md +130 -0
- vpcatlas-0.4.2/docs/cli/inventory.md +94 -0
- vpcatlas-0.4.2/docs/cli/serve.md +140 -0
- vpcatlas-0.4.2/docs/configuration.md +173 -0
- vpcatlas-0.4.2/docs/contributing.md +194 -0
- vpcatlas-0.4.2/docs/coverage.md +161 -0
- vpcatlas-0.4.2/docs/credentials.md +359 -0
- vpcatlas-0.4.2/docs/cross-account.md +138 -0
- vpcatlas-0.4.2/docs/dashboard.md +254 -0
- vpcatlas-0.4.2/docs/deploy/compose.md +71 -0
- vpcatlas-0.4.2/docs/deploy/docker.md +141 -0
- vpcatlas-0.4.2/docs/deploy/ecs.md +97 -0
- vpcatlas-0.4.2/docs/deploy/kubernetes.md +115 -0
- vpcatlas-0.4.2/docs/deploy/lambda.md +134 -0
- vpcatlas-0.4.2/docs/deploy.md +25 -0
- vpcatlas-0.4.2/docs/diagram-quality.md +348 -0
- vpcatlas-0.4.2/docs/diagram-style.md +1062 -0
- vpcatlas-0.4.2/docs/engineering.md +170 -0
- vpcatlas-0.4.2/docs/faq.md +104 -0
- vpcatlas-0.4.2/docs/iam.md +713 -0
- vpcatlas-0.4.2/docs/index.md +84 -0
- vpcatlas-0.4.2/docs/install.md +408 -0
- vpcatlas-0.4.2/docs/landscape.md +92 -0
- vpcatlas-0.4.2/docs/limitations.md +157 -0
- vpcatlas-0.4.2/docs/model.md +161 -0
- vpcatlas-0.4.2/docs/outputs/diagrams.md +130 -0
- vpcatlas-0.4.2/docs/outputs/graph.md +112 -0
- vpcatlas-0.4.2/docs/outputs/inventory.md +198 -0
- vpcatlas-0.4.2/docs/quickstart.md +140 -0
- vpcatlas-0.4.2/docs/troubleshooting.md +260 -0
- vpcatlas-0.4.2/mkdocs.yml +79 -0
- vpcatlas-0.4.2/packaging/pyinstaller/entry.py +8 -0
- vpcatlas-0.4.2/packaging/pyinstaller/vpcatlas.spec +97 -0
- vpcatlas-0.4.2/pyproject.toml +56 -0
- vpcatlas-0.4.2/requirements-build.lock +69 -0
- vpcatlas-0.4.2/requirements-release.lock +522 -0
- vpcatlas-0.4.2/requirements.lock +883 -0
- vpcatlas-0.4.2/scripts/bench_layout.py +126 -0
- vpcatlas-0.4.2/scripts/build-binary.sh +147 -0
- vpcatlas-0.4.2/scripts/demo.sh +153 -0
- vpcatlas-0.4.2/scripts/install.ps1 +331 -0
- vpcatlas-0.4.2/scripts/install.sh +214 -0
- vpcatlas-0.4.2/scripts/lock.sh +38 -0
- vpcatlas-0.4.2/scripts/publish-docs.sh +15 -0
- vpcatlas-0.4.2/scripts/requirements-build.in +4 -0
- vpcatlas-0.4.2/scripts/requirements-release.in +5 -0
- vpcatlas-0.4.2/src/vpcatlas/__init__.py +3 -0
- vpcatlas-0.4.2/src/vpcatlas/__main__.py +8 -0
- vpcatlas-0.4.2/src/vpcatlas/auditor-policy.json +264 -0
- vpcatlas-0.4.2/src/vpcatlas/cli.py +1054 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/__init__.py +22 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/_dns_targets.py +321 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/account_services.py +379 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/acm.py +94 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/apigw.py +350 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/base.py +465 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/cloudfront.py +107 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/discover.py +333 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/dynamodb.py +41 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_instances.py +191 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_peering.py +55 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_tgw.py +236 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_vpc.py +499 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ecs.py +151 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/efs.py +66 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/eks.py +106 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/elasticache.py +159 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/elb.py +107 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/endpoints.py +126 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/lambda_fn.py +120 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/linker.py +1044 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/network_firewall.py +102 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/prefix_lists.py +48 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/ram.py +104 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/rds.py +167 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/route53.py +183 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/route53_resolver.py +90 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/s3.py +182 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/session.py +162 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/shield.py +65 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/targets.py +159 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/vpn_dx.py +262 -0
- vpcatlas-0.4.2/src/vpcatlas/collectors/wafv2.py +155 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/__init__.py +6 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/app.py +724 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/healthcheck.py +23 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/history.py +232 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/lambda_handler.py +62 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/scan.py +304 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/security.py +197 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/service.py +1038 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/settings.py +245 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/snapshot.py +207 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/static/app.css +261 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/static/app.js +575 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/static/vendor/NOTICE.txt +15 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/static/vendor/viewer-static.min.js +9176 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/store.py +392 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/base.html +20 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/diff.html +181 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/history.html +410 -0
- vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/index.html +150 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/__init__.py +105 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/behind.py +176 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/drawio.py +623 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/families.py +94 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/graphview.py +504 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/layout.py +3988 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/lucid.py +573 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/lucid_classes.json +38 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/registry.py +1080 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/service_rows.py +762 -0
- vpcatlas-0.4.2/src/vpcatlas/exporters/vpc_level.py +1038 -0
- vpcatlas-0.4.2/src/vpcatlas/inventory/__init__.py +27 -0
- vpcatlas-0.4.2/src/vpcatlas/inventory/build.py +816 -0
- vpcatlas-0.4.2/src/vpcatlas/inventory/render.py +541 -0
- vpcatlas-0.4.2/src/vpcatlas/inventory/schema.py +147 -0
- vpcatlas-0.4.2/src/vpcatlas/model/__init__.py +155 -0
- vpcatlas-0.4.2/src/vpcatlas/model/diff.py +321 -0
- vpcatlas-0.4.2/src/vpcatlas/model/graph.py +399 -0
- vpcatlas-0.4.2/src/vpcatlas/model/redact.py +57 -0
- vpcatlas-0.4.2/src/vpcatlas/model/resources.py +1188 -0
- vpcatlas-0.4.2/src/vpcatlas/services.py +131 -0
- vpcatlas-0.4.2/tests/atlas_fixtures.py +184 -0
- vpcatlas-0.4.2/tests/compute_fakes.py +111 -0
- vpcatlas-0.4.2/tests/conftest.py +67 -0
- vpcatlas-0.4.2/tests/data_collectors_fakes.py +82 -0
- vpcatlas-0.4.2/tests/fixtures/__init__.py +0 -0
- vpcatlas-0.4.2/tests/fixtures/fanout.py +462 -0
- vpcatlas-0.4.2/tests/fixtures/malz.py +1370 -0
- vpcatlas-0.4.2/tests/test_cli.py +203 -0
- vpcatlas-0.4.2/tests/test_cli_options.py +70 -0
- vpcatlas-0.4.2/tests/test_collector_dynamodb.py +48 -0
- vpcatlas-0.4.2/tests/test_collector_efs.py +92 -0
- vpcatlas-0.4.2/tests/test_collector_elasticache.py +93 -0
- vpcatlas-0.4.2/tests/test_collector_rds.py +124 -0
- vpcatlas-0.4.2/tests/test_collector_s3.py +167 -0
- vpcatlas-0.4.2/tests/test_collectors.py +57 -0
- vpcatlas-0.4.2/tests/test_collectors_account_services.py +518 -0
- vpcatlas-0.4.2/tests/test_collectors_acm.py +122 -0
- vpcatlas-0.4.2/tests/test_collectors_apigw.py +244 -0
- vpcatlas-0.4.2/tests/test_collectors_cloudfront.py +159 -0
- vpcatlas-0.4.2/tests/test_collectors_ec2_instances.py +169 -0
- vpcatlas-0.4.2/tests/test_collectors_ecs.py +121 -0
- vpcatlas-0.4.2/tests/test_collectors_eks.py +102 -0
- vpcatlas-0.4.2/tests/test_collectors_lambda_fn.py +111 -0
- vpcatlas-0.4.2/tests/test_collectors_privacy.py +295 -0
- vpcatlas-0.4.2/tests/test_collectors_registry.py +51 -0
- vpcatlas-0.4.2/tests/test_collectors_route53.py +338 -0
- vpcatlas-0.4.2/tests/test_collectors_shield.py +85 -0
- vpcatlas-0.4.2/tests/test_collectors_targets.py +164 -0
- vpcatlas-0.4.2/tests/test_collectors_wafv2.py +158 -0
- vpcatlas-0.4.2/tests/test_credentials.py +613 -0
- vpcatlas-0.4.2/tests/test_dashboard.py +1021 -0
- vpcatlas-0.4.2/tests/test_dashboard_drawing.py +314 -0
- vpcatlas-0.4.2/tests/test_dashboard_families.py +179 -0
- vpcatlas-0.4.2/tests/test_dashboard_hardening.py +495 -0
- vpcatlas-0.4.2/tests/test_dashboard_lazy.py +218 -0
- vpcatlas-0.4.2/tests/test_diff.py +339 -0
- vpcatlas-0.4.2/tests/test_diff_highlight.py +318 -0
- vpcatlas-0.4.2/tests/test_drawio_layers.py +193 -0
- vpcatlas-0.4.2/tests/test_exporters.py +81 -0
- vpcatlas-0.4.2/tests/test_exporters_style.py +1126 -0
- vpcatlas-0.4.2/tests/test_fan_trunk.py +418 -0
- vpcatlas-0.4.2/tests/test_fanout_fixture.py +151 -0
- vpcatlas-0.4.2/tests/test_global_overlap.py +59 -0
- vpcatlas-0.4.2/tests/test_history_store.py +426 -0
- vpcatlas-0.4.2/tests/test_iam_policy_matches_collectors.py +181 -0
- vpcatlas-0.4.2/tests/test_install_ps1.py +129 -0
- vpcatlas-0.4.2/tests/test_install_sh.py +188 -0
- vpcatlas-0.4.2/tests/test_installer_release_lookup.py +19 -0
- vpcatlas-0.4.2/tests/test_inventory.py +184 -0
- vpcatlas-0.4.2/tests/test_inventory_render.py +263 -0
- vpcatlas-0.4.2/tests/test_inventory_services.py +116 -0
- vpcatlas-0.4.2/tests/test_layout_fold.py +205 -0
- vpcatlas-0.4.2/tests/test_layout_quality.py +1772 -0
- vpcatlas-0.4.2/tests/test_linker_services.py +366 -0
- vpcatlas-0.4.2/tests/test_malz_fixture.py +451 -0
- vpcatlas-0.4.2/tests/test_model_external_accounts.py +110 -0
- vpcatlas-0.4.2/tests/test_model_services.py +250 -0
- vpcatlas-0.4.2/tests/test_operator_identity_leaks.py +73 -0
- vpcatlas-0.4.2/tests/test_route_budget.py +303 -0
- vpcatlas-0.4.2/tests/test_service_filters.py +335 -0
- vpcatlas-0.4.2/tests/test_startup.py +120 -0
- vpcatlas-0.4.2/tests/test_supply_chain.py +114 -0
- vpcatlas-0.4.2/tests/test_vpc_level.py +433 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
<!-- What does this change, and what problem does it solve? Link the issue if there is one. -->
|
|
4
|
+
|
|
5
|
+
## How it was tested
|
|
6
|
+
|
|
7
|
+
<!-- Commands you ran (make lint test, pytest -k ..., a scan against a fixture, a build) and what you saw. -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Tests added or updated, and `make lint test` passes.
|
|
12
|
+
- [ ] Documentation updated (README, the matching page in `docs/`) if a command, route, permission or output changed.
|
|
13
|
+
- [ ] `CHANGELOG.md` has an entry under `## [Unreleased]`.
|
|
14
|
+
- [ ] No secrets, real account ids, ARNs or customer names in the diff or in the fixtures (use `111111111111`-style ids).
|
|
15
|
+
- [ ] A new AWS call is read-only and is listed in the auditor policy, in Coverage and in IAM.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Set up Python and install vpcatlas
|
|
2
|
+
description: Python with a pip cache keyed on pyproject.toml, then vpcatlas installed in editable mode with the chosen extras. The caller checks the code out first.
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
python-version:
|
|
6
|
+
description: Python version to install.
|
|
7
|
+
required: false
|
|
8
|
+
default: "3.12"
|
|
9
|
+
extras:
|
|
10
|
+
description: Extras of vpcatlas to install, comma separated, for example dev or dev,docs.
|
|
11
|
+
required: false
|
|
12
|
+
default: dev
|
|
13
|
+
|
|
14
|
+
runs:
|
|
15
|
+
using: composite
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ inputs.python-version }}
|
|
20
|
+
cache: pip
|
|
21
|
+
cache-dependency-path: pyproject.toml
|
|
22
|
+
|
|
23
|
+
- name: Install vpcatlas
|
|
24
|
+
shell: bash
|
|
25
|
+
env:
|
|
26
|
+
EXTRAS: ${{ inputs.extras }}
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
python -m pip install -e ".[${EXTRAS}]"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Weekly update pull requests. Actions and the Docker base images are pinned to digests or commit SHAs, so
|
|
2
|
+
# Dependabot is what keeps them current; each bump arrives as a reviewable diff with the upstream release notes.
|
|
3
|
+
version: 2
|
|
4
|
+
|
|
5
|
+
updates:
|
|
6
|
+
- package-ecosystem: github-actions
|
|
7
|
+
directory: /
|
|
8
|
+
schedule:
|
|
9
|
+
interval: weekly
|
|
10
|
+
day: monday
|
|
11
|
+
cooldown:
|
|
12
|
+
default-days: 7
|
|
13
|
+
open-pull-requests-limit: 5
|
|
14
|
+
commit-message:
|
|
15
|
+
prefix: ci(deps)
|
|
16
|
+
groups:
|
|
17
|
+
actions:
|
|
18
|
+
patterns: ["*"]
|
|
19
|
+
|
|
20
|
+
- package-ecosystem: pip
|
|
21
|
+
directory: /
|
|
22
|
+
schedule:
|
|
23
|
+
interval: weekly
|
|
24
|
+
day: monday
|
|
25
|
+
cooldown:
|
|
26
|
+
default-days: 7
|
|
27
|
+
open-pull-requests-limit: 5
|
|
28
|
+
commit-message:
|
|
29
|
+
prefix: build(deps)
|
|
30
|
+
groups:
|
|
31
|
+
python-minor:
|
|
32
|
+
update-types: [minor, patch]
|
|
33
|
+
|
|
34
|
+
- package-ecosystem: docker
|
|
35
|
+
directory: /deploy
|
|
36
|
+
schedule:
|
|
37
|
+
interval: weekly
|
|
38
|
+
day: monday
|
|
39
|
+
cooldown:
|
|
40
|
+
default-days: 7
|
|
41
|
+
open-pull-requests-limit: 5
|
|
42
|
+
commit-message:
|
|
43
|
+
prefix: build(docker)
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
# Every change runs the same checks a release depends on: lint, types, tests on three systems, the docs, the Python
|
|
4
|
+
# package, both container images and the installers. The job names below are the required checks of the main branch;
|
|
5
|
+
# rename one only together with the branch protection rule.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ci-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
lint:
|
|
22
|
+
name: Lint
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 15
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
# zizmor uploads its findings to the Security tab.
|
|
28
|
+
security-events: write
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
31
|
+
with:
|
|
32
|
+
persist-credentials: false
|
|
33
|
+
|
|
34
|
+
- uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
|
|
35
|
+
|
|
36
|
+
- name: Lint
|
|
37
|
+
run: ruff check .
|
|
38
|
+
|
|
39
|
+
- name: Check formatting
|
|
40
|
+
run: ruff format --check .
|
|
41
|
+
|
|
42
|
+
# Checks the syntax, the expressions and the shell scripts of every workflow.
|
|
43
|
+
- name: Check the workflows with actionlint
|
|
44
|
+
uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
|
|
45
|
+
with:
|
|
46
|
+
args: -color
|
|
47
|
+
|
|
48
|
+
# Looks for security problems in the workflows (unpinned actions, injectable expressions, wide permissions).
|
|
49
|
+
# First as a gate: the findings are printed in the log and fail the job.
|
|
50
|
+
- name: Check the workflows with zizmor
|
|
51
|
+
uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1
|
|
52
|
+
with:
|
|
53
|
+
advanced-security: false
|
|
54
|
+
|
|
55
|
+
# Then again as a report for the Security tab. A pull request from a fork has a read-only token and cannot
|
|
56
|
+
# upload there, so the report is skipped for it.
|
|
57
|
+
- name: Report the zizmor findings to the Security tab
|
|
58
|
+
if: ${{ !cancelled() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}
|
|
59
|
+
uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1
|
|
60
|
+
with:
|
|
61
|
+
advanced-security: true
|
|
62
|
+
|
|
63
|
+
typecheck:
|
|
64
|
+
name: Type check
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
timeout-minutes: 15
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
69
|
+
with:
|
|
70
|
+
persist-credentials: false
|
|
71
|
+
|
|
72
|
+
- uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
|
|
73
|
+
|
|
74
|
+
# The configuration lives in pyproject.toml ([tool.mypy]).
|
|
75
|
+
- name: mypy
|
|
76
|
+
run: python -m mypy
|
|
77
|
+
|
|
78
|
+
test:
|
|
79
|
+
name: Tests (${{ matrix.os }}, py${{ matrix.python }})
|
|
80
|
+
runs-on: ${{ matrix.os }}
|
|
81
|
+
timeout-minutes: 30
|
|
82
|
+
strategy:
|
|
83
|
+
fail-fast: false
|
|
84
|
+
matrix:
|
|
85
|
+
include:
|
|
86
|
+
# Only this leg measures coverage and keeps the report.
|
|
87
|
+
- { os: ubuntu-latest, python: "3.12", coverage: true }
|
|
88
|
+
- { os: ubuntu-latest, python: "3.13" }
|
|
89
|
+
- { os: macos-14, python: "3.12" }
|
|
90
|
+
- { os: windows-2022, python: "3.12" }
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
93
|
+
with:
|
|
94
|
+
persist-credentials: false
|
|
95
|
+
|
|
96
|
+
- uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
|
|
97
|
+
with:
|
|
98
|
+
python-version: ${{ matrix.python }}
|
|
99
|
+
|
|
100
|
+
- name: Test
|
|
101
|
+
if: ${{ !matrix.coverage }}
|
|
102
|
+
run: pytest
|
|
103
|
+
|
|
104
|
+
- name: Test with coverage
|
|
105
|
+
if: ${{ matrix.coverage }}
|
|
106
|
+
run: pytest --cov=vpcatlas --cov-report=xml --cov-report=term --cov-fail-under=88
|
|
107
|
+
|
|
108
|
+
# The total and the five files with the lowest line coverage, in the summary of the run.
|
|
109
|
+
- name: Coverage summary
|
|
110
|
+
if: ${{ always() && matrix.coverage }}
|
|
111
|
+
run: |
|
|
112
|
+
python - <<'PY'
|
|
113
|
+
import os
|
|
114
|
+
import xml.etree.ElementTree as ET
|
|
115
|
+
|
|
116
|
+
if not os.path.exists("coverage.xml"):
|
|
117
|
+
raise SystemExit("coverage.xml was not written")
|
|
118
|
+
root = ET.parse("coverage.xml").getroot()
|
|
119
|
+
rows = []
|
|
120
|
+
for cls in root.iter("class"):
|
|
121
|
+
lines = cls.findall("./lines/line")
|
|
122
|
+
if not lines:
|
|
123
|
+
continue
|
|
124
|
+
hit = sum(1 for line in lines if int(line.get("hits", "0")) > 0)
|
|
125
|
+
rows.append((hit / len(lines), len(lines) - hit, cls.get("filename", "?")))
|
|
126
|
+
rows.sort(key=lambda row: (row[0], -row[1], row[2]))
|
|
127
|
+
total = float(root.get("line-rate", "0")) * 100
|
|
128
|
+
out = ["## Coverage", "", f"Total line coverage: **{total:.1f}%** (the gate is 88%)", ""]
|
|
129
|
+
out += ["Five least covered files:", "", "| File | Covered | Lines missed |", "|---|---:|---:|"]
|
|
130
|
+
out += [f"| `{name}` | {rate * 100:.1f}% | {missed} |" for rate, missed, name in rows[:5]]
|
|
131
|
+
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
|
|
132
|
+
summary.write("\n".join(out) + "\n")
|
|
133
|
+
PY
|
|
134
|
+
|
|
135
|
+
- name: Keep the coverage report
|
|
136
|
+
if: ${{ always() && matrix.coverage }}
|
|
137
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
138
|
+
with:
|
|
139
|
+
name: coverage-xml
|
|
140
|
+
path: coverage.xml
|
|
141
|
+
if-no-files-found: ignore
|
|
142
|
+
|
|
143
|
+
docs:
|
|
144
|
+
name: Docs
|
|
145
|
+
runs-on: ubuntu-latest
|
|
146
|
+
timeout-minutes: 15
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
149
|
+
with:
|
|
150
|
+
persist-credentials: false
|
|
151
|
+
|
|
152
|
+
- uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
|
|
153
|
+
with:
|
|
154
|
+
extras: docs
|
|
155
|
+
|
|
156
|
+
# --strict turns every broken link and every warning into an error.
|
|
157
|
+
- name: Build the site
|
|
158
|
+
run: mkdocs build --strict
|
|
159
|
+
|
|
160
|
+
- name: Keep the site
|
|
161
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
162
|
+
with:
|
|
163
|
+
name: docs-site
|
|
164
|
+
path: site/
|
|
165
|
+
retention-days: 3
|
|
166
|
+
if-no-files-found: error
|
|
167
|
+
|
|
168
|
+
package:
|
|
169
|
+
name: Python package
|
|
170
|
+
runs-on: ubuntu-latest
|
|
171
|
+
timeout-minutes: 15
|
|
172
|
+
steps:
|
|
173
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
174
|
+
with:
|
|
175
|
+
persist-credentials: false
|
|
176
|
+
|
|
177
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
178
|
+
with:
|
|
179
|
+
python-version: "3.12"
|
|
180
|
+
|
|
181
|
+
# The same steps as the package job of the release workflow, so a broken package shows up before the tag.
|
|
182
|
+
- name: Build the sdist and the wheel
|
|
183
|
+
run: |
|
|
184
|
+
python -m pip install --require-hashes -r requirements-release.lock
|
|
185
|
+
python -m build --no-isolation
|
|
186
|
+
twine check dist/*
|
|
187
|
+
|
|
188
|
+
- name: The wheel carries the dashboard
|
|
189
|
+
run: |
|
|
190
|
+
wheels=(dist/*.whl)
|
|
191
|
+
wheel="${wheels[0]}"
|
|
192
|
+
for file in dashboard/templates/index.html dashboard/static/app.js auditor-policy.json; do
|
|
193
|
+
unzip -l "$wheel" | grep -q "vpcatlas/${file}" || { echo "${file} is missing from ${wheel}" >&2; exit 1; }
|
|
194
|
+
done
|
|
195
|
+
|
|
196
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
197
|
+
with:
|
|
198
|
+
name: python-dist
|
|
199
|
+
path: dist/
|
|
200
|
+
retention-days: 3
|
|
201
|
+
if-no-files-found: error
|
|
202
|
+
|
|
203
|
+
images:
|
|
204
|
+
name: Images
|
|
205
|
+
runs-on: ubuntu-latest
|
|
206
|
+
timeout-minutes: 40
|
|
207
|
+
permissions:
|
|
208
|
+
contents: read
|
|
209
|
+
# The Trivy report is uploaded to the Security tab.
|
|
210
|
+
security-events: write
|
|
211
|
+
steps:
|
|
212
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
213
|
+
with:
|
|
214
|
+
persist-credentials: false
|
|
215
|
+
|
|
216
|
+
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
|
217
|
+
|
|
218
|
+
- name: Build the dashboard image
|
|
219
|
+
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
|
220
|
+
with:
|
|
221
|
+
context: .
|
|
222
|
+
file: deploy/Dockerfile
|
|
223
|
+
load: true
|
|
224
|
+
push: false
|
|
225
|
+
tags: vpcatlas:ci
|
|
226
|
+
cache-from: type=gha,scope=dashboard
|
|
227
|
+
cache-to: type=gha,scope=dashboard,mode=max
|
|
228
|
+
|
|
229
|
+
- name: Smoke test the dashboard image
|
|
230
|
+
run: |
|
|
231
|
+
docker run -d --name atlas -p 8080:8080 -e AWS_EC2_METADATA_DISABLED=true vpcatlas:ci
|
|
232
|
+
for _ in $(seq 1 30); do
|
|
233
|
+
if curl -fsS http://127.0.0.1:8080/healthz > /dev/null; then break; fi
|
|
234
|
+
sleep 2
|
|
235
|
+
done
|
|
236
|
+
curl -fsS http://127.0.0.1:8080/healthz
|
|
237
|
+
echo
|
|
238
|
+
curl -fsS http://127.0.0.1:8080/ | grep -q "VPC Atlas"
|
|
239
|
+
curl -fsS http://127.0.0.1:8080/static/app.js > /dev/null
|
|
240
|
+
docker exec atlas python -m vpcatlas.dashboard.healthcheck
|
|
241
|
+
docker exec atlas vpcatlas --help > /dev/null
|
|
242
|
+
|
|
243
|
+
- name: Container logs
|
|
244
|
+
if: always()
|
|
245
|
+
run: docker logs atlas || true
|
|
246
|
+
|
|
247
|
+
- name: Build the Lambda image
|
|
248
|
+
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
|
249
|
+
with:
|
|
250
|
+
context: .
|
|
251
|
+
file: deploy/Dockerfile.lambda
|
|
252
|
+
load: true
|
|
253
|
+
push: false
|
|
254
|
+
tags: vpcatlas-lambda:ci
|
|
255
|
+
cache-from: type=gha,scope=lambda
|
|
256
|
+
cache-to: type=gha,scope=lambda,mode=max
|
|
257
|
+
|
|
258
|
+
# The Lambda base image carries the Runtime Interface Emulator: it listens on port 8080 and runs the handler
|
|
259
|
+
# for every POST to /2015-03-31/functions/function/invocations. The event is what API Gateway (HTTP API v2)
|
|
260
|
+
# sends for GET /healthz.
|
|
261
|
+
- name: Smoke test the Lambda image
|
|
262
|
+
run: |
|
|
263
|
+
docker run -d --name lambda -p 9009:8080 vpcatlas-lambda:ci
|
|
264
|
+
event="${RUNNER_TEMP}/event.json"
|
|
265
|
+
out="${RUNNER_TEMP}/lambda-response.json"
|
|
266
|
+
cat > "$event" <<'JSON'
|
|
267
|
+
{"version":"2.0","routeKey":"GET /healthz","rawPath":"/healthz","rawQueryString":"","headers":{"host":"localhost","accept":"*/*"},"requestContext":{"accountId":"000000000000","apiId":"local","domainName":"localhost","http":{"method":"GET","path":"/healthz","protocol":"HTTP/1.1","sourceIp":"127.0.0.1","userAgent":"ci"},"requestId":"ci","routeKey":"GET /healthz","stage":"$default","time":"18/Oct/2026:00:00:00 +0000","timeEpoch":0},"isBase64Encoded":false}
|
|
268
|
+
JSON
|
|
269
|
+
for _ in $(seq 1 30); do
|
|
270
|
+
if curl -sS --max-time 30 -X POST "http://127.0.0.1:9009/2015-03-31/functions/function/invocations" -d "@${event}" -o "$out"; then break; fi
|
|
271
|
+
sleep 2
|
|
272
|
+
done
|
|
273
|
+
cat "$out"
|
|
274
|
+
echo
|
|
275
|
+
python3 -c 'import json, sys; sys.exit(0 if json.load(open(sys.argv[1])).get("statusCode") == 200 else 1)' "$out" ||
|
|
276
|
+
{ echo "The Lambda handler did not answer with statusCode 200" >&2; exit 1; }
|
|
277
|
+
|
|
278
|
+
- name: Lambda container logs
|
|
279
|
+
if: failure()
|
|
280
|
+
run: docker logs lambda || true
|
|
281
|
+
|
|
282
|
+
# The report goes to the Security tab; this run never fails on it (the gate is the next-but-two step).
|
|
283
|
+
- name: Scan the dashboard image with Trivy
|
|
284
|
+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
285
|
+
with:
|
|
286
|
+
image-ref: vpcatlas:ci
|
|
287
|
+
format: sarif
|
|
288
|
+
output: trivy.sarif
|
|
289
|
+
severity: CRITICAL,HIGH
|
|
290
|
+
limit-severities-for-sarif: true
|
|
291
|
+
ignore-unfixed: true
|
|
292
|
+
exit-code: "0"
|
|
293
|
+
|
|
294
|
+
# A pull request from a fork has a read-only token and cannot upload.
|
|
295
|
+
- name: Upload the Trivy report
|
|
296
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
|
297
|
+
uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
|
298
|
+
with:
|
|
299
|
+
sarif_file: trivy.sarif
|
|
300
|
+
category: trivy-dashboard
|
|
301
|
+
|
|
302
|
+
- name: Image summary
|
|
303
|
+
if: always()
|
|
304
|
+
run: |
|
|
305
|
+
python3 - <<'PY'
|
|
306
|
+
import json
|
|
307
|
+
import os
|
|
308
|
+
import subprocess
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def size_mb(image):
|
|
312
|
+
try:
|
|
313
|
+
raw = subprocess.run(
|
|
314
|
+
["docker", "image", "inspect", "--format", "{{.Size}}", image],
|
|
315
|
+
check=True,
|
|
316
|
+
capture_output=True,
|
|
317
|
+
text=True,
|
|
318
|
+
).stdout.strip()
|
|
319
|
+
return f"{int(raw) / 1_000_000:.1f} MB"
|
|
320
|
+
except (subprocess.CalledProcessError, ValueError):
|
|
321
|
+
return "not built"
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
counts = {"CRITICAL": 0, "HIGH": 0}
|
|
325
|
+
scanned = os.path.exists("trivy.sarif")
|
|
326
|
+
if scanned:
|
|
327
|
+
with open("trivy.sarif", encoding="utf-8") as handle:
|
|
328
|
+
report = json.load(handle)
|
|
329
|
+
for run in report.get("runs", []):
|
|
330
|
+
rules = {
|
|
331
|
+
rule.get("id"): rule.get("properties", {}).get("security-severity")
|
|
332
|
+
for rule in run.get("tool", {}).get("driver", {}).get("rules", [])
|
|
333
|
+
}
|
|
334
|
+
for result in run.get("results", []):
|
|
335
|
+
try:
|
|
336
|
+
score = float(rules.get(result.get("ruleId")) or 0)
|
|
337
|
+
except ValueError:
|
|
338
|
+
score = 0.0
|
|
339
|
+
counts["CRITICAL" if score >= 9.0 else "HIGH"] += 1
|
|
340
|
+
out = ["## Images", "", "| Image | Size |", "|---|---:|"]
|
|
341
|
+
out += [f"| dashboard (`vpcatlas:ci`) | {size_mb('vpcatlas:ci')} |"]
|
|
342
|
+
out += [f"| Lambda (`vpcatlas-lambda:ci`) | {size_mb('vpcatlas-lambda:ci')} |", ""]
|
|
343
|
+
if scanned:
|
|
344
|
+
out += ["Trivy, dashboard image, fixed vulnerabilities only:", ""]
|
|
345
|
+
out += ["| Severity | Findings |", "|---|---:|"]
|
|
346
|
+
out += [f"| CRITICAL | {counts['CRITICAL']} |", f"| HIGH | {counts['HIGH']} |"]
|
|
347
|
+
else:
|
|
348
|
+
out += ["Trivy report not written."]
|
|
349
|
+
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
|
|
350
|
+
summary.write("\n".join(out) + "\n")
|
|
351
|
+
PY
|
|
352
|
+
|
|
353
|
+
# The gate: a critical vulnerability with a fix available fails the job.
|
|
354
|
+
- name: Gate on critical vulnerabilities
|
|
355
|
+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
356
|
+
with:
|
|
357
|
+
image-ref: vpcatlas:ci
|
|
358
|
+
format: table
|
|
359
|
+
severity: CRITICAL
|
|
360
|
+
ignore-unfixed: true
|
|
361
|
+
exit-code: "1"
|
|
362
|
+
|
|
363
|
+
- name: Gate on critical vulnerabilities (Lambda image)
|
|
364
|
+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
365
|
+
with:
|
|
366
|
+
image-ref: vpcatlas-lambda:ci
|
|
367
|
+
format: table
|
|
368
|
+
severity: CRITICAL
|
|
369
|
+
ignore-unfixed: true
|
|
370
|
+
exit-code: "1"
|
|
371
|
+
|
|
372
|
+
installer:
|
|
373
|
+
name: Installer on ${{ matrix.os }}
|
|
374
|
+
runs-on: ${{ matrix.os }}
|
|
375
|
+
timeout-minutes: 30
|
|
376
|
+
strategy:
|
|
377
|
+
fail-fast: false
|
|
378
|
+
matrix:
|
|
379
|
+
os: [windows-2022, ubuntu-22.04, macos-14]
|
|
380
|
+
steps:
|
|
381
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
382
|
+
with:
|
|
383
|
+
persist-credentials: false
|
|
384
|
+
|
|
385
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
386
|
+
with:
|
|
387
|
+
python-version: "3.12"
|
|
388
|
+
|
|
389
|
+
# Builds the archive (a .zip on Windows, a .tar.gz elsewhere) and runs the program inside it (--version,
|
|
390
|
+
# serve --help, a real dashboard request).
|
|
391
|
+
- name: Build the archive
|
|
392
|
+
shell: bash
|
|
393
|
+
run: bash scripts/build-binary.sh
|
|
394
|
+
|
|
395
|
+
# Windows PowerShell 5.1, with the script run as text, the way `irm <url> | iex` runs it. The archive and its
|
|
396
|
+
# checksum come from dist/ instead of the network.
|
|
397
|
+
- name: Install through a pipe with Windows PowerShell
|
|
398
|
+
if: matrix.os == 'windows-2022'
|
|
399
|
+
shell: powershell
|
|
400
|
+
run: |
|
|
401
|
+
$ErrorActionPreference = "Stop"
|
|
402
|
+
$env:VPCATLAS_ARCHIVE = (Get-ChildItem dist/vpcatlas-*.zip).FullName
|
|
403
|
+
$env:VPCATLAS_SUMS = "$env:VPCATLAS_ARCHIVE.sha256"
|
|
404
|
+
Get-Content -Raw scripts/install.ps1 | Invoke-Expression
|
|
405
|
+
vpcatlas --version
|
|
406
|
+
|
|
407
|
+
- name: The shim and the persistent PATH work from a new PowerShell
|
|
408
|
+
if: matrix.os == 'windows-2022'
|
|
409
|
+
shell: pwsh
|
|
410
|
+
run: |
|
|
411
|
+
$p = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
412
|
+
$env:Path = "$p;$env:Path"
|
|
413
|
+
vpcatlas --version
|
|
414
|
+
vpcatlas serve --help | Out-Null
|
|
415
|
+
|
|
416
|
+
- name: The shim works from cmd.exe
|
|
417
|
+
if: matrix.os == 'windows-2022'
|
|
418
|
+
# The point of the step is cmd.exe itself: users run the shim from it.
|
|
419
|
+
shell: cmd # zizmor: ignore[misfeature]
|
|
420
|
+
run: |
|
|
421
|
+
set "PATH=%LOCALAPPDATA%\Programs\vpcatlas;%PATH%"
|
|
422
|
+
vpcatlas --version
|
|
423
|
+
|
|
424
|
+
- name: Install as a file with parameters into other directories
|
|
425
|
+
if: matrix.os == 'windows-2022'
|
|
426
|
+
shell: pwsh
|
|
427
|
+
run: |
|
|
428
|
+
$zip = (Get-ChildItem dist/vpcatlas-*.zip).FullName
|
|
429
|
+
./scripts/install.ps1 -Archive $zip -Sums "$zip.sha256" -InstallDir "$env:RUNNER_TEMP\vpcatlas-bin" -DataDir "$env:RUNNER_TEMP\vpcatlas-home"
|
|
430
|
+
& "$env:RUNNER_TEMP\vpcatlas-bin\vpcatlas.cmd" --version
|
|
431
|
+
|
|
432
|
+
# Negative control: the same archive with a checksum file that does not match it. The installer has to stop with
|
|
433
|
+
# a checksum error and leave nothing in either directory.
|
|
434
|
+
- name: The installer refuses a wrong checksum
|
|
435
|
+
if: matrix.os == 'windows-2022'
|
|
436
|
+
shell: pwsh
|
|
437
|
+
run: |
|
|
438
|
+
$zip = (Get-ChildItem dist/vpcatlas-*.zip).FullName
|
|
439
|
+
$badSums = Join-Path $env:RUNNER_TEMP 'bad-SHA256SUMS'
|
|
440
|
+
$badBin = "$env:RUNNER_TEMP\bad-bin"
|
|
441
|
+
$badHome = "$env:RUNNER_TEMP\bad-home"
|
|
442
|
+
Set-Content -LiteralPath $badSums -Value (('0' * 64) + ' ' + [IO.Path]::GetFileName($zip))
|
|
443
|
+
$message = $null
|
|
444
|
+
try {
|
|
445
|
+
./scripts/install.ps1 -Archive $zip -Sums $badSums -InstallDir $badBin -DataDir $badHome
|
|
446
|
+
} catch {
|
|
447
|
+
$message = $_.Exception.Message
|
|
448
|
+
}
|
|
449
|
+
if (-not $message) { throw 'The installer accepted a wrong checksum: no error was raised.' }
|
|
450
|
+
if ($message -notmatch 'checksum mismatch') { throw "The installer failed, but not on the checksum: $message" }
|
|
451
|
+
foreach ($dir in @($badBin, $badHome)) {
|
|
452
|
+
if (Test-Path -LiteralPath $dir) {
|
|
453
|
+
$left = @(Get-ChildItem -LiteralPath $dir -Recurse -Force | Where-Object { ($_.PSIsContainer -and $_.Name -match '^\d+\.\d+\.\d+') -or $_.Name -eq 'vpcatlas.cmd' })
|
|
454
|
+
if ($left.Count -gt 0) { throw "The installer left files behind after a wrong checksum: $($left.FullName -join ', ')" }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
Write-Host "Refused as expected: $message"
|
|
458
|
+
|
|
459
|
+
- name: Keep the Windows archive
|
|
460
|
+
if: matrix.os == 'windows-2022'
|
|
461
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
462
|
+
with:
|
|
463
|
+
name: windows-x86_64-zip
|
|
464
|
+
path: |
|
|
465
|
+
dist/vpcatlas-*.zip
|
|
466
|
+
dist/vpcatlas-*.zip.sha256
|
|
467
|
+
retention-days: 3
|
|
468
|
+
if-no-files-found: error
|
|
469
|
+
|
|
470
|
+
# The script is piped into sh, the way `curl -fsSL https://vpcatlas.com/install.sh | sh` runs it, with the
|
|
471
|
+
# default directories. The archive and its checksum come from dist/ instead of the network.
|
|
472
|
+
- name: Install through a pipe with sh
|
|
473
|
+
if: matrix.os != 'windows-2022'
|
|
474
|
+
shell: bash
|
|
475
|
+
run: |
|
|
476
|
+
archives=("$PWD"/dist/vpcatlas-*.tar.gz)
|
|
477
|
+
[ "${#archives[@]}" -eq 1 ] || { echo "expected one archive in dist/, found ${#archives[@]}" >&2; exit 1; }
|
|
478
|
+
export VPCATLAS_ARCHIVE="${archives[0]}"
|
|
479
|
+
export VPCATLAS_SUMS="${VPCATLAS_ARCHIVE}.sha256"
|
|
480
|
+
# The pipe is the point of this step, so the useless-cat warning does not apply.
|
|
481
|
+
# shellcheck disable=SC2002
|
|
482
|
+
cat scripts/install.sh | sh
|
|
483
|
+
# The script put the link in /usr/local/bin or in ~/.local/bin; a new shell would find it there.
|
|
484
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
485
|
+
version="$(basename "$VPCATLAS_ARCHIVE" | sed -n 's/^vpcatlas-\(.*\)-[a-z]*-[a-z0-9_]*\.tar\.gz$/\1/p')"
|
|
486
|
+
[ -n "$version" ] || { echo "could not read the version from $VPCATLAS_ARCHIVE" >&2; exit 1; }
|
|
487
|
+
link="$(command -v vpcatlas)"
|
|
488
|
+
echo "link: ${link} -> $(readlink "$link")"
|
|
489
|
+
[ -L "$link" ] || { echo "${link} is not a symbolic link" >&2; exit 1; }
|
|
490
|
+
vpcatlas --version | grep -F "$version"
|
|
491
|
+
vpcatlas serve --help > /dev/null
|
|
492
|
+
|
|
493
|
+
- name: Install into other directories
|
|
494
|
+
if: matrix.os != 'windows-2022'
|
|
495
|
+
shell: bash
|
|
496
|
+
run: |
|
|
497
|
+
archives=("$PWD"/dist/vpcatlas-*.tar.gz)
|
|
498
|
+
export VPCATLAS_ARCHIVE="${archives[0]}"
|
|
499
|
+
export VPCATLAS_SUMS="${VPCATLAS_ARCHIVE}.sha256"
|
|
500
|
+
export VPCATLAS_INSTALL_DIR="${RUNNER_TEMP}/vpcatlas-bin"
|
|
501
|
+
export VPCATLAS_HOME="${RUNNER_TEMP}/vpcatlas-home"
|
|
502
|
+
sh scripts/install.sh
|
|
503
|
+
[ -L "${VPCATLAS_INSTALL_DIR}/vpcatlas" ] || { echo "no link in ${VPCATLAS_INSTALL_DIR}" >&2; exit 1; }
|
|
504
|
+
"${VPCATLAS_INSTALL_DIR}/vpcatlas" --version
|
|
505
|
+
[ -n "$(find "$VPCATLAS_HOME" -mindepth 2 -maxdepth 2 -name _internal)" ] ||
|
|
506
|
+
{ echo "no installed folder in ${VPCATLAS_HOME}" >&2; exit 1; }
|
|
507
|
+
|
|
508
|
+
# Negative control: the same archive with a checksum file that does not match it. The installer has to stop
|
|
509
|
+
# with a checksum error and leave nothing in either directory.
|
|
510
|
+
- name: The installer refuses a wrong checksum
|
|
511
|
+
if: matrix.os != 'windows-2022'
|
|
512
|
+
shell: bash
|
|
513
|
+
run: |
|
|
514
|
+
archives=("$PWD"/dist/vpcatlas-*.tar.gz)
|
|
515
|
+
export VPCATLAS_ARCHIVE="${archives[0]}"
|
|
516
|
+
export VPCATLAS_SUMS="${RUNNER_TEMP}/bad-SHA256SUMS"
|
|
517
|
+
export VPCATLAS_INSTALL_DIR="${RUNNER_TEMP}/bad-bin"
|
|
518
|
+
export VPCATLAS_HOME="${RUNNER_TEMP}/bad-home"
|
|
519
|
+
printf '%s %s\n' "0000000000000000000000000000000000000000000000000000000000000000" "$(basename "$VPCATLAS_ARCHIVE")" > "$VPCATLAS_SUMS"
|
|
520
|
+
status=0
|
|
521
|
+
output="$(sh scripts/install.sh 2>&1)" || status=$?
|
|
522
|
+
echo "$output"
|
|
523
|
+
[ "$status" -ne 0 ] || { echo "The installer accepted a wrong checksum" >&2; exit 1; }
|
|
524
|
+
echo "$output" | grep -q "checksum mismatch" || { echo "The installer failed, but not on the checksum" >&2; exit 1; }
|
|
525
|
+
if [ -e "${VPCATLAS_INSTALL_DIR}/vpcatlas" ] || [ -L "${VPCATLAS_INSTALL_DIR}/vpcatlas" ]; then
|
|
526
|
+
echo "The installer left a link behind after a wrong checksum" >&2
|
|
527
|
+
exit 1
|
|
528
|
+
fi
|
|
529
|
+
if [ -d "$VPCATLAS_HOME" ] && [ -n "$(find "$VPCATLAS_HOME" -mindepth 1)" ]; then
|
|
530
|
+
echo "The installer left files behind after a wrong checksum" >&2
|
|
531
|
+
exit 1
|
|
532
|
+
fi
|
|
533
|
+
echo "Refused as expected"
|
|
534
|
+
|
|
535
|
+
dependency-review:
|
|
536
|
+
name: Dependency review
|
|
537
|
+
if: github.event_name == 'pull_request'
|
|
538
|
+
runs-on: ubuntu-latest
|
|
539
|
+
timeout-minutes: 10
|
|
540
|
+
permissions:
|
|
541
|
+
contents: read
|
|
542
|
+
# The summary is posted as a comment on the pull request.
|
|
543
|
+
pull-requests: write
|
|
544
|
+
steps:
|
|
545
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
546
|
+
with:
|
|
547
|
+
persist-credentials: false
|
|
548
|
+
|
|
549
|
+
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
|
550
|
+
with:
|
|
551
|
+
fail-on-severity: high
|
|
552
|
+
comment-summary-in-pr: on-failure
|