Skip to content

Commit 5f9d14b

Browse files
authored
Decoupling (#89)
* refactor: scaffold build system for configurator decoupling Add build script that reassembles configurator.html from src/ parts. Currently a trivial split (CSS, HTML skeleton, JS monolith) that produces a byte-identical output to the original. This establishes the round-trip verification baseline for subsequent extraction phases. * refactor: extract i18n, service definitions, and template generators Split the monolithic JS into separate files: - src/js/i18n/all.js — translations + engine (~2,463 lines) - src/js/services/registry.js — event names, sources, permissions (~185 lines) - src/js/deploy/template-main.js — CFN template generator (~3,113 lines) - src/js/deploy/template-org.js — org/per-account templates (~229 lines) - src/js/deploy/instructions.js — instructions generator (~83 lines) - src/js/deploy/script-deploy.js — deploy.sh generator (~1,084 lines) Build output remains byte-identical to the original. * refactor: extract constants and clean up structure Split constants (TEMPLATE_VERSION, VERSION_HISTORY, renderVersionHistory) into src/js/constants.js. Remaining flow logic stays in app-pre.js for now — per-flow splitting will be done in a subsequent pass. Current structure: - src/css/styles.css (183 lines) - src/html/configurator.html (964 lines) - src/js/constants.js (345 lines) - src/js/app-pre.js (1,786 lines) — all flow logic - src/js/i18n/all.js (2,463 lines) — translations + engine - src/js/services/registry.js (185 lines) - src/js/deploy/template-main.js (3,113 lines) - src/js/deploy/instructions.js (83 lines) - src/js/deploy/template-org.js (229 lines) - src/js/deploy/script-deploy.js (1,084 lines) - src/js/app-post.js (92 lines) — output/download Build output remains byte-identical to the original. * refactor: split flow logic into per-flow modules Split app-pre.js into: - src/js/shared/ui.js (40 lines) — selectMode, step navigation - src/js/editor/editor-flow.js (795 lines) — editor flow - src/js/upgrade/upgrade-flow.js (124 lines) — upgrade flow - src/js/delete/delete-flow.js (408 lines) — delete flow - src/js/deploy/deploy-flow.js (419 lines) — deploy flow UI Output is functionally identical (same byte count, all functions present). Function order differs from the original since the interleaved flows are now grouped by module. * refactor: split i18n into per-locale files, extract Lambda Python - Split i18n/all.js into 7 locale files + engine.js - Extract Lambda Python handler (2,082 lines) to src/templates/lambda-handler.py - Rename app-post.js → app.js Build output is functionally identical (all functions and i18n keys present, 216 bytes larger due to variable wrapper declarations). * refactor: split service registry into per-service modules Break the monolithic ALL_EVENT_NAMES/ALL_SOURCES/TAGGING_PERMISSIONS arrays into ~80 per-service files (e.g., ec2.js, rds.js, s3.js). Each file declares its source, events, and permissions. index.js aggregates them into the three flat arrays. Build script auto-discovers service files via directory listing. Adding a new service = drop a new .js file in src/js/services/. Includes README.md documenting the per-service format. * chore: clean up pycache from templates * test: add vitest unit tests and build verification - scripts/verify-build.js — 13 sanity checks on built HTML - tests/unit/services.test.js — 10 tests (service module format, no duplicate sources) - tests/unit/i18n.test.js — 13 tests (locale files, engine functions) - tests/unit/build.test.js — 7 tests (built output completeness) - tests/unit/lambda.test.js — 5 tests (Python handler structure) - vitest.config.js + package.json scripts (build, test, verify) 35 tests, all passing. * chore: add CI workflow, update E2E path, replace root configurator - .github/workflows/build.yml — test → build → verify on PRs, auto-commit built output on main - Update generate_deploy_sh.js to use build/configurator.html - Replace root configurator.html with redirect to build/ * refactor: wire Lambda Python into build, update README - template-main.js now uses ${LAMBDA_HANDLER_CODE} placeholder (3,113 → 1,032 lines; Python lives in src/templates/lambda-handler.py) - Build script reads .py, indents for YAML, injects into JS bundle - README updated: paths, Components table, new Development section - All 35 tests passing, build verification clean * fix(ci): update lint scripts to read build/configurator.html All lint scripts and workflows now read from build/configurator.html instead of the root (which is now a redirect). Added Node.js setup + build step to 6 lint jobs so the built file exists before linting. * fix(ci): single build job with artifact sharing in lint workflow Replace 6 redundant per-job builds with one shared build-configurator job. Lint jobs that need build/configurator.html now declare needs: build-configurator and download the artifact. * fix(ci): add build step to E2E workflow E2E workflow now builds configurator.html from src/ before Playwright generates deploy.sh. Ensures E2E always tests the current source, not a potentially stale committed build. * fix(ci): update sync-check to scan full file for permissions After per-service split, permissions are in individual service module objects rather than a single TAGGING_PERMISSIONS array. Updated sync-check.py to scan the entire built HTML for IAM action patterns instead of searching only within the TAGGING_PERMISSIONS block. * docs: update all references from configurator.html to build/ Updated OVERVIEW.md, INSTRUCTIONS.md, LIMITATIONS.md, README.md, VERSIONING.md, and design-reconciliation.md. CHANGELOG.md left unchanged (historical references). * docs: add DEVELOPMENT.md with source structure and extension guide Covers directory layout, build process, and how to add new services, locales, flows, and modify the Lambda handler. * docs: remove services/README.md, consolidate in DEVELOPMENT.md * chore: use CSS/JS comments for build placeholders Avoids false editor errors from HTML comments inside style/script tags. * refactor: output built configurator.html to repo root Move build output from build/configurator.html back to the root so customers find it in the same place as before. Delete build/ directory. Updated: build.js, verify-build.js, build.test.js, generate_deploy_sh.js, all lint scripts, all workflows, all docs, .gitignore. * feat: add build-yaml.js, wire E2E to use generated YAML Add scripts/build-yaml.js that calls the same generateMainTemplate() to produce standalone YAML. Accepts --config JSON and --output path. E2E scope tests and deploy-single now generate their own YAML via build-yaml.js instead of deploying map2-auto-tagger-optimized.yaml. Each test bakes its specific config (MPE, VPC IDs, dates) directly. The standalone YAML and sync-check are kept for now — once E2E passes with the new path, they can be removed in a follow-up commit. * refactor: delete standalone YAML, wire all lint to generated output Delete map2-auto-tagger-optimized.yaml, sync-check.py, and .github/sync/ (canonical permissions file). All lint jobs now depend on build-configurator and use the generated configurator.yaml. One source of truth (src/), two build outputs (HTML + YAML), zero drift possible. * fix(ci): resolve duplicate 'with' key in lint.yml The download-artifact insertion collided with a checkout step that had fetch-depth: 0, producing two 'with:' blocks on the same step. * fix(ci): cfn-lint ignore warnings, fix generate_iam.py canonical source - cfn-lint: add -I W to ignore warnings (unused params expected with baked values in generated YAML) - generate_iam.py: load_canonical() now extracts permissions from configurator.yaml instead of the deleted tagging-permissions.txt * fix(ci): allow cfn-lint exit code 4 (warnings only) * fix: add missing DeadLetterConfig and UUID strip in peer detector Bug 1: AutoTaggerFunction was missing DeadLetterConfig pointing to EventDLQ. Pre-existing gap between standalone YAML and configurator, now fixed in template-main.js. Bug 2: UUID strip regex was lost during lambda-handler.py extraction. StackSet instance stacks have a UUID suffix that must be stripped before comparing MPE IDs in the peer detector.
1 parent 5e5c1d2 commit 5f9d14b

135 files changed

Lines changed: 13575 additions & 5058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ Brief description of the change and the problem it solves.
2020

2121
## CI Notes
2222
> **Layer 1 (lint)** runs immediately on every PR — ~1 min.
23-
> **Layer 2 (E2E)** runs when `map2-auto-tagger-optimized.yaml` or `configurator.html` changes — ~37 min across 7 AWS accounts. No AWS credentials needed.
23+
> **Layer 2 (E2E)** runs when `configurator.yaml` or `configurator.html` changes — ~37 min across 7 AWS accounts. No AWS credentials needed.
2424
> If Layer 2 fails, download `verification-report.json` from the Actions run for details.

.github/scripts/audit_handler_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from pathlib import Path
4444

4545
REPO_ROOT = Path(__file__).resolve().parents[2]
46-
TEMPLATE = REPO_ROOT / "map2-auto-tagger-optimized.yaml"
46+
TEMPLATE = REPO_ROOT / "configurator.yaml"
4747
RESOURCE_GROUPS = REPO_ROOT / ".github" / "scripts" / "resource_groups"
4848
BASELINE = REPO_ROOT / ".github" / "handler_coverage_baseline.txt"
4949

.github/scripts/deploy_stackset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Usage:
66
python3 deploy_stackset.py \
77
--stack-set-name map-auto-tagger-e2e-pr42 \
8-
--template map2-auto-tagger-optimized.yaml \
8+
--template configurator.yaml \
99
--mpe-id migTEST0000001 \
1010
--agreement-date 2024-01-01 \
1111
--accounts "111111111111,222222222222"

.github/scripts/generate_iam.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env python3
22
"""
33
generate_iam.py — derive the canonical IAM tagging-action list from the Lambda
4-
source. Used as a supplement to the hand-curated tagging-permissions.txt: if a
4+
source. Used to verify IAM completeness in the generated configurator.yaml: if a
55
new native-dispatch branch is added to the Lambda without the matching IAM
66
grant, this script surfaces the drift at CI time.
77
88
Methodology:
99
10-
1. Parse `map2-auto-tagger-optimized.yaml` for every `boto3.client('<svc>')`
10+
1. Parse `configurator.yaml` for every `boto3.client('<svc>')`
1111
and `get_service_client('<svc>')` call — these are the services whose
1212
native TagResource/AddTags APIs the Lambda invokes directly.
1313
@@ -23,7 +23,7 @@
2323
kept in the hand-curated canonical list.
2424
2525
4. Emit either the derived list (stdout) or compare it against
26-
`.github/sync/tagging-permissions.txt` and fail if the derived list has
26+
`src/js/services/ (per-service modules)` and fail if the derived list has
2727
an action not present in the canonical list.
2828
2929
Usage:
@@ -37,8 +37,8 @@
3737
from pathlib import Path
3838

3939
REPO_ROOT = Path(__file__).resolve().parents[2]
40-
YAML = REPO_ROOT / "map2-auto-tagger-optimized.yaml"
41-
CANONICAL = REPO_ROOT / ".github" / "sync" / "tagging-permissions.txt"
40+
YAML = REPO_ROOT / "configurator.yaml"
41+
# CANONICAL file removed — permissions now live in src/js/services/
4242

4343
# Map native-dispatch service prefix → list of IAM actions required at runtime.
4444
# Keep one row per service; when a new native branch is added to `do_tag`,
@@ -57,7 +57,7 @@
5757
# API Gateway tagging uses HTTP-verb-level IAM actions, NOT the modern
5858
# TagResource. v1 REST API uses PUT /tags/{arn}, PATCH /tags/{arn} for
5959
# certain operations; v2 HTTP API uses POST /tags/{arn}. See the comment
60-
# block in map2-auto-tagger-optimized.yaml's ServiceSpecificTagging policy.
60+
# block in configurator.yaml's ServiceSpecificTagging policy.
6161
"apigateway": ["apigateway:PUT", "apigateway:PATCH"],
6262
"apigatewayv2": ["apigateway:POST"],
6363
"autoscaling": ["autoscaling:CreateOrUpdateTags"],
@@ -117,8 +117,11 @@ def derive_required_actions(services: set[str]) -> set[str]:
117117

118118

119119
def load_canonical() -> set[str]:
120-
with CANONICAL.open() as f:
121-
return {line.strip() for line in f if line.strip() and not line.startswith("#")}
120+
"""Extract IAM actions from the generated configurator.yaml's IAM policy."""
121+
yaml_text = YAML.read_text()
122+
# Match all IAM action patterns in the YAML (service:Action format)
123+
import re
124+
return set(re.findall(r"[\w-]+:[\w]+", yaml_text))
122125

123126

124127
def main() -> int:
@@ -143,7 +146,7 @@ def main() -> int:
143146
print(f" - {action}")
144147
print(
145148
f"\nFix by adding each missing action to {CANONICAL.relative_to(REPO_ROOT)} "
146-
f"AND the matching row in map2-auto-tagger-optimized.yaml's ServiceSpecificTagging policy.",
149+
f"AND the matching row in configurator.yaml's ServiceSpecificTagging policy.",
147150
file=sys.stderr,
148151
)
149152
return 1

.github/scripts/lint_batchsize_floor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from pathlib import Path
3232

3333
ROOT = Path(__file__).resolve().parent.parent.parent
34-
YAML_FILE = ROOT / 'map2-auto-tagger-optimized.yaml'
34+
YAML_FILE = ROOT / 'configurator.yaml'
3535
HTML_FILE = ROOT / 'configurator.html'
3636

3737
MIN_BATCH_SIZE = 10

.github/scripts/lint_cfn_correctness.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
5. !Sub undefined template variables
3131
3232
Covers both sources of truth:
33-
- map2-auto-tagger-optimized.yaml (runtime)
33+
- configurator.yaml (runtime)
3434
- configurator.html (customer-generated inline template)
3535
3636
Exit codes: 0 all green; 1 drift detected.
@@ -44,7 +44,7 @@
4444
from typing import Iterable
4545

4646
ROOT = Path(__file__).resolve().parent.parent.parent
47-
YAML_FILE = ROOT / 'map2-auto-tagger-optimized.yaml'
47+
YAML_FILE = ROOT / 'configurator.yaml'
4848
HTML_FILE = ROOT / 'configurator.html'
4949

5050
# Worst-case expansion budgets. MpeId is validated by YAML AllowedPattern

.github/scripts/lint_event_prefixes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
statically, so we only enforce the direction we can prove.
2020
2121
Checks both source-of-truth files:
22-
- map2-auto-tagger-optimized.yaml (runtime template)
22+
- configurator.yaml (runtime template)
2323
- configurator.html (customer-generated template, inline)
2424
2525
Exit codes:
@@ -34,7 +34,7 @@
3434
from pathlib import Path
3535

3636
ROOT = Path(__file__).resolve().parent.parent.parent
37-
YAML_FILE = ROOT / 'map2-auto-tagger-optimized.yaml'
37+
YAML_FILE = ROOT / 'configurator.yaml'
3838
HTML_FILE = ROOT / 'configurator.html'
3939

4040

@@ -99,7 +99,7 @@ def main() -> int:
9999
print(
100100
"\nFix: add the missing verb(s) to the `- prefix:` block in the "
101101
"EventBridge rule (AutoTagEventRule.Properties.EventPattern.detail."
102-
"eventName). Both map2-auto-tagger-optimized.yaml and configurator."
102+
"eventName). Both configurator.yaml and configurator."
103103
"html must carry every verb used by an explicit `event_name == ...` "
104104
"handler, otherwise EventBridge drops the event before it reaches "
105105
"SQS (silent failure — no DLQ, no alarm)."

0 commit comments

Comments
 (0)