feat(P2): Nova rebrand — code/env-vars/consumer-path (REQ-158/159/160)
core/env.py dual-read helper (D-108); 21 ACDL_*→NOVA_* env vars migrated across core/scripts/adapters/tests/workflows + .env/.env.secrets (key rename, values stay). G-106 binding: run_platform.sh:288-289 + regression_verify.py:309-312 dual-read (NOVA first, ACDL fallback). G-108 binding: Gitea NOVA_* secrets created via API + workflow secrets: refs updated (deploy.yml + modules-lifecycle.yml, .gitea + .github). acdl_tagging.py→nova_tagging.py (D-109 warn mode, nova:* enforced). .acdl/→.nova/ consumer path (resolver + deploy workflow + schema + tests + docs). Test fixtures updated; pytest + run_ci.sh PASS. ---ci--- project: acdl phase: 2 milestone: v1.15 status: execute ---/ci---
This commit is contained in:
@@ -408,14 +408,16 @@ class TestAdapterDedupMergesSameModule:
|
||||
|
||||
|
||||
class TestAdapterRemoteStateKeyOverride:
|
||||
"""P2-2 (v1.14, REQ-139): ACDL_REMOTE_STATE_KEY env var overrides the
|
||||
default 'platform/terraform.tfstate' key in the emitted
|
||||
"""P2-2 (v1.14, REQ-139): NOVA_REMOTE_STATE_KEY env var (P2 renamed from
|
||||
ACDL_REMOTE_STATE_KEY; dual-read NOVA_* preferred, ACDL_* fallback until
|
||||
P5) overrides the default 'platform/terraform.tfstate' key in the emitted
|
||||
data terraform_remote_state block. This is the load-bearing correctness
|
||||
mechanism for the microservice L2 lifecycle (remote state points at the
|
||||
CI VPC, not the platform VPC)."""
|
||||
|
||||
def test_default_remote_state_key(self, tmp_path, monkeypatch):
|
||||
"""When ACDL_REMOTE_STATE_KEY is unset, the default key is used."""
|
||||
"""When NOVA_REMOTE_STATE_KEY is unset, the default key is used."""
|
||||
monkeypatch.delenv("NOVA_REMOTE_STATE_KEY", raising=False)
|
||||
monkeypatch.delenv("ACDL_REMOTE_STATE_KEY", raising=False)
|
||||
stack = {
|
||||
"resources": [
|
||||
@@ -431,9 +433,9 @@ class TestAdapterRemoteStateKeyOverride:
|
||||
assert "platform/terraform.tfstate" in main_tf
|
||||
|
||||
def test_env_override_remote_state_key(self, tmp_path, monkeypatch):
|
||||
"""When ACDL_REMOTE_STATE_KEY is set, the emitted data block uses
|
||||
"""When NOVA_REMOTE_STATE_KEY is set, the emitted data block uses
|
||||
the overridden key (e.g. 'spike/ci-vpc/terraform.tfstate')."""
|
||||
monkeypatch.setenv("ACDL_REMOTE_STATE_KEY", "spike/ci-vpc/terraform.tfstate")
|
||||
monkeypatch.setenv("NOVA_REMOTE_STATE_KEY", "spike/ci-vpc/terraform.tfstate")
|
||||
stack = {
|
||||
"resources": [
|
||||
{"id": "s3", "type": "aws:s3:bucket", "module": "s3@1.0.0", "inputs": {"bucket_name": "test", "region": "us-east-1"}}
|
||||
|
||||
@@ -102,6 +102,8 @@ def test_dr_blocks_on_missing_dr_drill():
|
||||
|
||||
def test_signature_skip_when_key_unset(monkeypatch, capsys):
|
||||
"""D-089: signature verification is skipped when the signing key is unset."""
|
||||
# P2: dual-read — both NOVA_* and ACDL_* must be unset for the skip.
|
||||
monkeypatch.delenv("NOVA_ATTESTATION_SIGNING_KEY_ID", raising=False)
|
||||
monkeypatch.delenv("ACDL_ATTESTATION_SIGNING_KEY_ID", raising=False)
|
||||
artifact = {"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
"type": "x", "payload": {}, "signature": "sig"}
|
||||
@@ -112,7 +114,7 @@ def test_signature_skip_when_key_unset(monkeypatch, capsys):
|
||||
|
||||
def test_signature_required_when_key_set(monkeypatch):
|
||||
"""When the signing key is set, a missing signature fails."""
|
||||
monkeypatch.setenv("ACDL_ATTESTATION_SIGNING_KEY_ID", "kms-key-id")
|
||||
monkeypatch.setenv("NOVA_ATTESTATION_SIGNING_KEY_ID", "kms-key-id")
|
||||
artifact = {"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
||||
"type": "x", "payload": {}} # no signature
|
||||
assert _verify_signature(artifact) is False
|
||||
|
||||
@@ -67,10 +67,10 @@ class TestToPcr:
|
||||
|
||||
|
||||
class TestRuleMapTagging:
|
||||
def test_acdl_tag_naming_is_real_rule(self):
|
||||
# D-054 / D-043 closure: ACDL_TAG_NAMING is now a real custom Checkov
|
||||
# rule, not a synthetic SKIPPED record.
|
||||
assert RULE_MAP["ACDL_TAG_NAMING"] == ("tagging-standard", "medium")
|
||||
def test_nova_tag_naming_is_real_rule(self):
|
||||
# D-054 / D-043 closure, P2 rename (REQ-158): NOVA_TAG_NAMING is now
|
||||
# a real custom Checkov rule, not a synthetic SKIPPED record.
|
||||
assert RULE_MAP["NOVA_TAG_NAMING"] == ("tagging-standard", "medium")
|
||||
|
||||
|
||||
class TestAdapt:
|
||||
@@ -100,13 +100,13 @@ class TestAdapt:
|
||||
|
||||
def test_adapt_does_not_emit_synthetic_tag_naming(self, tmp_path):
|
||||
# D-043 closure: adapt() no longer appends a synthetic SKIPPED
|
||||
# ACDL_TAG_NAMING record. The custom Checkov rule (loaded via
|
||||
# NOVA_TAG_NAMING record. The custom Checkov rule (loaded via
|
||||
# --external-checks-dir) produces real PASS/FAIL records instead.
|
||||
data = self._sample_checkov_json()
|
||||
f = tmp_path / "checkov.json"
|
||||
f.write_text(json.dumps(data))
|
||||
results = adapt(str(f), "c-1")
|
||||
tag = [r for r in results if r["ruleId"] == "ACDL_TAG_NAMING"]
|
||||
tag = [r for r in results if r["ruleId"] == "NOVA_TAG_NAMING"]
|
||||
assert tag == [] # no synthetic record
|
||||
|
||||
def test_adapt_has_passed_and_failed(self, tmp_path):
|
||||
|
||||
@@ -78,4 +78,6 @@ def test_run_platform_sh_has_environment_flag():
|
||||
text = (ROOT / "scripts" / "run_platform.sh").read_text()
|
||||
assert "--environment" in text
|
||||
assert "ENVIRONMENT_OVERRIDE" in text
|
||||
assert "ACDL_ENVIRONMENT_OVERRIDE" in text
|
||||
# P2 (REQ-159): NOVA_* preferred; ACDL_* kept as dual-read fallback until P5.
|
||||
assert "NOVA_ENVIRONMENT_OVERRIDE" in text
|
||||
assert "ACDL_ENVIRONMENT_OVERRIDE" in text # legacy fallback, removed in P5
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Unit tests for the dual-read env helper (core/env.py, D-108, REQ-159).
|
||||
|
||||
Covers the four cases:
|
||||
- both NOVA_* and ACDL_* set (NOVA wins)
|
||||
- only NOVA_* set
|
||||
- only ACDL_* set (fallback)
|
||||
- neither set (default returned)
|
||||
|
||||
The ACDL_* fallback is the intentional dual-read source and is removed
|
||||
in P5 (REQ-164). These fixtures deliberately keep the ACDL_* names as
|
||||
the fallback source — they are the one allowed ACDL_* reference.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from core import env
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_env(monkeypatch):
|
||||
"""Ensure no ACDL_*/NOVA_* leakage between tests."""
|
||||
for key in list(__import__("os").environ):
|
||||
if key.startswith(("ACDL_", "NOVA_")):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
yield
|
||||
|
||||
|
||||
def test_both_set_nova_wins(monkeypatch):
|
||||
monkeypatch.setenv("NOVA_AWS_ACCOUNT_ID", "nova-value")
|
||||
monkeypatch.setenv("ACDL_AWS_ACCOUNT_ID", "acdl-value")
|
||||
assert env.get_env("AWS_ACCOUNT_ID") == "nova-value"
|
||||
|
||||
|
||||
def test_only_nova_set(monkeypatch):
|
||||
monkeypatch.setenv("NOVA_AWS_ACCOUNT_ID", "nova-value")
|
||||
assert env.get_env("AWS_ACCOUNT_ID") == "nova-value"
|
||||
|
||||
|
||||
def test_only_acdl_set_fallback(monkeypatch):
|
||||
# ACDL_* is the intentional dual-read fallback source (removed in P5).
|
||||
monkeypatch.setenv("ACDL_AWS_ACCOUNT_ID", "acdl-value")
|
||||
assert env.get_env("AWS_ACCOUNT_ID") == "acdl-value"
|
||||
|
||||
|
||||
def test_neither_set_returns_default():
|
||||
assert env.get_env("AWS_ACCOUNT_ID") is None
|
||||
assert env.get_env("AWS_ACCOUNT_ID", default="581513795199") == "581513795199"
|
||||
|
||||
|
||||
def test_blank_nova_falls_back_to_acdl(monkeypatch):
|
||||
# An explicitly-empty NOVA key must not shadow the ACDL fallback.
|
||||
monkeypatch.setenv("NOVA_AWS_ACCOUNT_ID", "")
|
||||
monkeypatch.setenv("ACDL_AWS_ACCOUNT_ID", "acdl-value")
|
||||
assert env.get_env("AWS_ACCOUNT_ID") == "acdl-value"
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
The lifecycle scripts (run_lifecycle_test.sh, run_lifecycle_destroy.sh,
|
||||
run_l2_lifecycle_test.sh, run_l2_lifecycle_destroy.sh) wrap run_platform.sh.
|
||||
REQ-134 (v1.12) adds the ACDL_LIFECYCLE_MODE env var: default "plan" runs
|
||||
`run_platform.sh --plan-only` (fast, no AWS mutation); "full" runs the real
|
||||
`--apply`/`--destroy` against live AWS.
|
||||
REQ-134 (v1.12) adds the NOVA_LIFECYCLE_MODE env var (P2 renamed from
|
||||
ACDL_LIFECYCLE_MODE, dual-read NOVA_* preferred / ACDL_* fallback until
|
||||
P5): default "plan" runs `run_platform.sh --plan-only` (fast, no AWS
|
||||
mutation); "full" runs the real `--apply`/`--destroy` against live AWS.
|
||||
|
||||
These tests verify the dispatch logic offline by inspecting script content
|
||||
(running the scripts end-to-end requires AWS credentials in full mode).
|
||||
@@ -32,12 +33,14 @@ class TestLifecycleModeFlag:
|
||||
|
||||
@pytest.mark.parametrize("script", SCRIPTS)
|
||||
def test_script_reads_acdl_lifecycle_mode(self, script):
|
||||
"""Every lifecycle script reads ACDL_LIFECYCLE_MODE with a 'plan' default."""
|
||||
"""Every lifecycle script reads NOVA_LIFECYCLE_MODE (dual-read with
|
||||
ACDL_LIFECYCLE_MODE fallback) with a 'plan' default."""
|
||||
src = _read(script)
|
||||
# The default must be 'plan' (the speed-up default). The scripts use
|
||||
# an intermediate LIFECYCLE_MODE var sourced from ACDL_LIFECYCLE_MODE.
|
||||
assert "${ACDL_LIFECYCLE_MODE:-plan}" in src, \
|
||||
f"{script} must read ACDL_LIFECYCLE_MODE defaulting to 'plan'"
|
||||
# P2 (REQ-159): dual-read NOVA_* preferred, ACDL_* fallback. The
|
||||
# ACDL_LIFECYCLE_MODE:-plan substring is still present inside the
|
||||
# nested dual-read expression (removed in P5).
|
||||
assert "${NOVA_LIFECYCLE_MODE:-${ACDL_LIFECYCLE_MODE:-plan}}" in src, \
|
||||
f"{script} must dual-read NOVA_/ACDL_LIFECYCLE_MODE defaulting to 'plan'"
|
||||
assert "LIFECYCLE_MODE=" in src, \
|
||||
f"{script} must assign LIFECYCLE_MODE from the env var"
|
||||
|
||||
|
||||
@@ -179,11 +179,11 @@ def test_local_lambda_stub_rejects_missing_field(tmp_path):
|
||||
def test_run_local_e2e_microservice():
|
||||
"""Headline E2E: contract -> resolver -> adapter -> local S3 backend
|
||||
-> local ECS (HTTP 200) -> flat-file outbox -> local Lambda. No AWS."""
|
||||
os.environ["ACDL_LOCAL_TIER"] = "1"
|
||||
os.environ["NOVA_LOCAL_TIER"] = "1"
|
||||
try:
|
||||
result = le.run_local_e2e("contracts/microservice.yml")
|
||||
finally:
|
||||
os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
os.environ.pop("NOVA_LOCAL_TIER", None); os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
assert result["tier"] == "local-emulator"
|
||||
assert result["backend"] == "local"
|
||||
assert result["ecs"] is not None
|
||||
@@ -196,11 +196,11 @@ def test_run_local_e2e_microservice():
|
||||
def test_run_local_e2e_static_assets():
|
||||
"""Static-assets stack has no ECS service; the local E2E must still
|
||||
complete (ecs=None) and the outbox chain + Lambda stub must pass."""
|
||||
os.environ["ACDL_LOCAL_TIER"] = "1"
|
||||
os.environ["NOVA_LOCAL_TIER"] = "1"
|
||||
try:
|
||||
result = le.run_local_e2e("contracts/static-assets.yml")
|
||||
finally:
|
||||
os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
os.environ.pop("NOVA_LOCAL_TIER", None); os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
assert result["tier"] == "local-emulator"
|
||||
assert result["ecs"] is None # no ECS service in this stack
|
||||
assert result["outbox_chain_verified"] is True
|
||||
@@ -209,9 +209,9 @@ def test_run_local_e2e_static_assets():
|
||||
|
||||
def test_is_local_tier_flag():
|
||||
assert le.is_local_tier() is False
|
||||
os.environ["ACDL_LOCAL_TIER"] = "1"
|
||||
os.environ["NOVA_LOCAL_TIER"] = "1"
|
||||
try:
|
||||
assert le.is_local_tier() is True
|
||||
finally:
|
||||
os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
os.environ.pop("NOVA_LOCAL_TIER", None); os.environ.pop("ACDL_LOCAL_TIER", None)
|
||||
assert le.is_local_tier() is False
|
||||
@@ -376,7 +376,9 @@ class TestCli:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestKmsFailLoud:
|
||||
"""P1-3: SSM publisher must fail loud when ACDL_KMS_KEY_ID is unset."""
|
||||
"""P1-3: SSM publisher must fail loud when NOVA_KMS_KEY_ID is unset
|
||||
(P2 renamed from ACDL_KMS_KEY_ID; dual-read NOVA_* preferred,
|
||||
ACDL_* fallback until P5)."""
|
||||
|
||||
def test_kms_unset_raises(self, monkeypatch):
|
||||
from moto import mock_aws
|
||||
@@ -385,11 +387,14 @@ class TestKmsFailLoud:
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "us-east-1")
|
||||
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "testing")
|
||||
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "testing")
|
||||
# Both NOVA_* and ACDL_* unset → helper returns default (None) → fail loud.
|
||||
monkeypatch.delenv("NOVA_KMS_KEY_ID", raising=False)
|
||||
monkeypatch.delenv("ACDL_KMS_KEY_ID", raising=False)
|
||||
monkeypatch.delenv("NOVA_ALLOW_DEFAULT_KMS", raising=False)
|
||||
monkeypatch.delenv("ACDL_ALLOW_DEFAULT_KMS", raising=False)
|
||||
|
||||
with mock_aws():
|
||||
with pytest.raises(RuntimeError, match="ACDL_KMS_KEY_ID is not set"):
|
||||
with pytest.raises(RuntimeError, match="NOVA_KMS_KEY_ID is not set"):
|
||||
publish_to_ssm({"vpc_id": "vpc-1"}, "dev", "c-1")
|
||||
|
||||
def test_kms_unset_allow_default_kms_escape_hatch(self, monkeypatch):
|
||||
@@ -399,8 +404,9 @@ class TestKmsFailLoud:
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "us-east-1")
|
||||
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "testing")
|
||||
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "testing")
|
||||
monkeypatch.delenv("NOVA_KMS_KEY_ID", raising=False)
|
||||
monkeypatch.delenv("ACDL_KMS_KEY_ID", raising=False)
|
||||
monkeypatch.setenv("ACDL_ALLOW_DEFAULT_KMS", "1")
|
||||
monkeypatch.setenv("NOVA_ALLOW_DEFAULT_KMS", "1")
|
||||
|
||||
with mock_aws():
|
||||
ssm = boto3.client("ssm", region_name="us-east-1")
|
||||
@@ -415,8 +421,8 @@ class TestKmsFailLoud:
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", "us-east-1")
|
||||
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "testing")
|
||||
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "testing")
|
||||
monkeypatch.setenv("ACDL_KMS_KEY_ID", "arn:aws:kms:us-east-1:123:key/abc")
|
||||
monkeypatch.setenv("ACDL_ALLOW_DEFAULT_KMS", "1")
|
||||
monkeypatch.setenv("NOVA_KMS_KEY_ID", "arn:aws:kms:us-east-1:123:key/abc")
|
||||
monkeypatch.setenv("NOVA_ALLOW_DEFAULT_KMS", "1")
|
||||
|
||||
from core.output_publisher import _kms_key_id
|
||||
assert _kms_key_id() == "arn:aws:kms:us-east-1:123:key/abc"
|
||||
|
||||
@@ -310,7 +310,7 @@ class TestDeployWorkflowConformance:
|
||||
wf = _load_workflow(".gitea/workflows/deploy.yml")
|
||||
inputs = wf["on"]["workflow_call"]["inputs"]
|
||||
assert "contract" in inputs
|
||||
assert inputs["contract"]["default"] == ".acdl/contract.yml"
|
||||
assert inputs["contract"]["default"] == ".nova/contract.yml"
|
||||
|
||||
def test_deploy_workflow_has_mode_input(self):
|
||||
wf = _load_workflow(".gitea/workflows/deploy.yml")
|
||||
@@ -646,20 +646,20 @@ class TestModulesLifecyclePipeline:
|
||||
assert set(inputs["lifecycle_mode"].get("options", [])) == {"plan", "full"}
|
||||
|
||||
def test_lifecycle_job_passes_mode_env_to_steps(self):
|
||||
"""The lifecycle job sets ACDL_LIFECYCLE_MODE env so scripts dispatch
|
||||
"""The lifecycle job sets NOVA_LIFECYCLE_MODE env so scripts dispatch
|
||||
to plan-only by default, full on override."""
|
||||
wf = _load_workflow(".gitea/workflows/modules-lifecycle.yml")
|
||||
env = wf["jobs"]["lifecycle"].get("env", {})
|
||||
assert "ACDL_LIFECYCLE_MODE" in env
|
||||
assert "NOVA_LIFECYCLE_MODE" in env
|
||||
# The expression must resolve to 'plan' when no input/var is set.
|
||||
assert "plan" in env["ACDL_LIFECYCLE_MODE"]
|
||||
assert "plan" in env["NOVA_LIFECYCLE_MODE"]
|
||||
|
||||
def test_l2_lifecycle_job_passes_mode_env_to_steps(self):
|
||||
"""The L2 lifecycle job also sets ACDL_LIFECYCLE_MODE env."""
|
||||
"""The L2 lifecycle job also sets NOVA_LIFECYCLE_MODE env."""
|
||||
wf = _load_workflow(".gitea/workflows/modules-lifecycle.yml")
|
||||
env = wf["jobs"]["l2-lifecycle"].get("env", {})
|
||||
assert "ACDL_LIFECYCLE_MODE" in env
|
||||
assert "plan" in env["ACDL_LIFECYCLE_MODE"]
|
||||
assert "NOVA_LIFECYCLE_MODE" in env
|
||||
assert "plan" in env["NOVA_LIFECYCLE_MODE"]
|
||||
|
||||
def test_ci_vpc_apply_skipped_in_plan_mode(self):
|
||||
"""The CI VPC apply job is skipped in plan mode (nothing is applied)."""
|
||||
|
||||
@@ -29,6 +29,7 @@ def test_route_halt_publishes_to_sns_when_arn_set(monkeypatch):
|
||||
|
||||
def test_route_halt_falls_back_to_stderr_when_arn_unset(monkeypatch, capsys):
|
||||
"""Without ACDL_SOD_HALT_TOPIC_ARN, a stderr emission occurs."""
|
||||
monkeypatch.delenv("NOVA_SOD_HALT_TOPIC_ARN", raising=False)
|
||||
monkeypatch.delenv("ACDL_SOD_HALT_TOPIC_ARN", raising=False)
|
||||
# Mock outbox_writer.write_event to avoid AWS calls.
|
||||
with mock.patch("core.outbox_writer.write_event", return_value=None):
|
||||
@@ -40,6 +41,7 @@ def test_route_halt_falls_back_to_stderr_when_arn_unset(monkeypatch, capsys):
|
||||
|
||||
def test_route_halt_outbox_fallback_writes_event(monkeypatch):
|
||||
"""Without the SNS ARN, the outbox fallback writes a SEPARATION_OF_DUTIES_VIOLATION event."""
|
||||
monkeypatch.delenv("NOVA_SOD_HALT_TOPIC_ARN", raising=False)
|
||||
monkeypatch.delenv("ACDL_SOD_HALT_TOPIC_ARN", raising=False)
|
||||
with mock.patch("core.outbox_writer.write_event") as mock_write:
|
||||
route_halt_artifact("contract-789", "sod violation", oncall_client=None)
|
||||
|
||||
@@ -106,9 +106,11 @@ class TestCreateStateBackend:
|
||||
"""terraform/bootstrap/create_state_backend.py — mock boto3."""
|
||||
|
||||
def test_state_bucket_name_construction(self, monkeypatch):
|
||||
"""The state bucket name is derived from ACDL_AWS_ACCOUNT_ID."""
|
||||
monkeypatch.setenv("ACDL_AWS_ACCOUNT_ID", "123456789012")
|
||||
account_id = os.environ.get("ACDL_AWS_ACCOUNT_ID", "581513795199")
|
||||
"""The state bucket name is derived from NOVA_AWS_ACCOUNT_ID
|
||||
(P2 renamed from ACDL_AWS_ACCOUNT_ID; the bucket name acdl-tfstate-*
|
||||
stays until P4, REQ-163)."""
|
||||
monkeypatch.setenv("NOVA_AWS_ACCOUNT_ID", "123456789012")
|
||||
account_id = os.environ.get("NOVA_AWS_ACCOUNT_ID", "581513795199")
|
||||
state_bucket = f"acdl-tfstate-{account_id}-us-east-1"
|
||||
assert state_bucket == "acdl-tfstate-123456789012-us-east-1"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user