controlm-chain-admin 0.1.0__tar.gz → 0.1.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.
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/bootstrap/SKILL.md +40 -13
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/BACKLOG.md +7 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CHANGELOG.md +49 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/DESIGN.md +36 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/PKG-INFO +8 -5
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/README.md +7 -4
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/__init__.py +1 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/cli.py +14 -4
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/fixes.py +59 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/guide.py +4 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/intake.py +170 -20
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/refactor.py +17 -7
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/validate.py +64 -4
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/intake.py +1 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/schemas.py +8 -2
- controlm_chain_admin-0.1.2/src/controlm_chain_admin/web/static/assets/index-M3NaeRfY.js +28 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/index.html +1 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_receive.py +41 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake_envs.py +1 -1
- controlm_chain_admin-0.1.2/tests/unit/test_intake_values_only.py +196 -0
- controlm_chain_admin-0.1.2/tests/unit/test_validate_similar.py +222 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/openapi.snapshot.json +13 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_intake.py +19 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/scripts/dump_fixtures.py +21 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/intake/ImportPage.test.tsx +64 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/intake/ImportPage.tsx +40 -4
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/types.gen.ts +13 -1
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake-full.json +1 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake-groups.json +1 -0
- controlm_chain_admin-0.1.2/web/src/test/fixtures/intake-values.json +253 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake.json +1 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/index-DYc-B93N.js +0 -28
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/ATTRIBUTION.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/fastapi-api/SKILL.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/oss-repo-standards/SKILL.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/oss-repo-standards/check_repo.sh +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/python-core-tdd/SKILL.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/react-frontend/SKILL.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/verify/SKILL.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/verify/verify.sh +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.editorconfig +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/FUNDING.yml +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/workflows/ci.yml +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/workflows/release.yml +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.gitignore +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.pre-commit-config.yaml +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CLAUDE.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CODE_OF_CONDUCT.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CONTRIBUTING.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/LICENSE +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/SECURITY.md +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/pyproject.toml +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/__main__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/attribute_groups.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/commit.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/compare.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/constants.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/errors.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/graph.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/history.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/inspect.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/jsonmodel.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/letter.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/links.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/membership.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/model.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/ops.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/overrides.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/package.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/relations.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/repo.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/selection.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/take.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/workspace.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/xmlio.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/py.typed +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/app.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/deps.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/errors.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/changes.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/environments.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/folders.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/history.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/jobs.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/package.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/repo.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/table.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/validate.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/workspace.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/serve.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/settings.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/graph-CA0VXlpl.js +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/index-BA0sPihz.css +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/react-DB7G9u3x.js +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/conftest.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_compare_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_constants_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_dedupe_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_environments.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_export_file.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_fixes_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_import_export.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_package_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_refactor_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_roundtrip.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_table_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_validate_real.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/anomalies.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/envexports.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/gitrepo.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/rawxml.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/samples.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_attribute_groups.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_commands.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_commit.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_compare.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_distinct_filter.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_env.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_inspect.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_package.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_refactor.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_release.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_resolve.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_serve.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_commit.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_compare.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants_ops.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants_overview.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_dedupe.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_edit_ops.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_env_rule.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_fixes.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_graph.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_history.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_inspect.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake_groups.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_jsonmodel.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_letter.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_links.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_links_truth.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_main_module.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_membership.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_overrides.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_package.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_packaging.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_rawxml.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_receive.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_refactor.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_relations.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_repo_ops.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_selection.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_take.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_validate.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_validate_names.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_workspace.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_xmlio.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/__init__.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/conftest.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_commit.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_compare.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_edit.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_environments.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_package.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_take.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_workspace.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_openapi.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_static_ui.py +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/.prettierrc +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/eslint.config.js +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/index.html +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/package-lock.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/package.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/app/App.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/DiffView.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/EffectsTable.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/ui.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/chain-table/ChainTablePage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/chain-table/ChainTablePage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/ChangeProvider.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/ChangeProvider.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/TokenForm.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/changes-context.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitButton.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitButton.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitDialog.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/branch-name.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/CommitPicker.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/ComparePage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/ComparePage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/CopyMarkdownButton.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/PackagePanel.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/TakeBar.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/refs.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/take-context.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/AttributeForm.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/InlineAction.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RawJsonEditor.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RelationsEditor.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RelationsEditor.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/VariablesEditor.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/editors.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/labels.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/environments/EnvironmentsPage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/environments/EnvironmentsPage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/ExceptionsPanel.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/ExceptionsPanel.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/exception-count.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderGraph.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderPage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderPage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/flow.test.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/flow.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/job-detail/JobPage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/job-detail/JobPage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/repo/RepoBadge.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/repo/RepoBadge.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/FixOptions.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ResolvePanel.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ValidatePage.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ValidatePage.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/BranchButton.test.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/BranchButton.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/ProtectedBanner.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/ReadOnlyNotice.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/WorkspaceDialog.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/plural.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/use-workspace-action.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/index.css +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/client.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/queries.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/session.test.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/session.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/layout.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/utils.test.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/utils.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/main.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/compare-selected.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/compare.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/environments.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folder-P77_CHAIN.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folder-P77_INGEST_DAILY.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folders.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/git.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/job-P77_CHAIN-P77_B.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/job-P77_INGEST_DAILY-P77_INGEST_LOAD.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/package.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/pending.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/preview.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/repo.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/table.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/validate.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/workspace-main.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/workspace.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/render.tsx +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/server.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/setup.ts +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/tsconfig.json +0 -0
- {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/vite.config.ts +0 -0
|
@@ -68,25 +68,32 @@ git add -A && git commit -m "chore: production baseline"
|
|
|
68
68
|
This first commit is the only one made on `main` by hand: from now on `main` mirrors
|
|
69
69
|
production and every change goes through a release.
|
|
70
70
|
|
|
71
|
-
### 3. The other environments, one release
|
|
71
|
+
### 3. The values of the other environments, one release
|
|
72
|
+
|
|
73
|
+
The full exports of the other environments also carry the releases that are installed there
|
|
74
|
+
but not in production yet. Take only their *values* from them (machines, users, mails, the
|
|
75
|
+
environment's name in texts, variables): the jobs stay as production has them, and the
|
|
76
|
+
releases come later from their deliveries (step 6).
|
|
72
77
|
|
|
73
78
|
```bash
|
|
74
|
-
ctmchain release start
|
|
75
|
-
ctmchain receive ../exports/uat/<uat>.xml --
|
|
79
|
+
ctmchain release start environments
|
|
80
|
+
ctmchain receive ../exports/uat/<uat>.xml --values-only # preview: the decisions
|
|
76
81
|
```
|
|
77
82
|
|
|
78
|
-
A full export of an environment other than production leaves production alone by default.
|
|
79
83
|
Read the decisions with the person, by section, before `--apply`:
|
|
80
84
|
|
|
81
|
-
- **groups** (
|
|
85
|
+
- **groups** (a machine, user, mail or variable, or the environment's name in a text): a new
|
|
82
86
|
constant with a value per environment. Give them readable names with
|
|
83
|
-
`--choose 'group:...=new:<name>'`, or in the *Import* page of `ctmchain serve
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
environment
|
|
87
|
+
`--choose 'group:...=new:<name>'`, or in the *Import* page of `ctmchain serve` (box *Only
|
|
88
|
+
the values of <env>*).
|
|
89
|
+
- everything else is left out by default: changes of jobs (**differences**, **children**),
|
|
90
|
+
jobs the environment lacks (**removed**: kept) and folders or jobs only it has
|
|
91
|
+
(**added**: left out). A difference the person knows is the environment's own (a job that
|
|
92
|
+
is Dummy there, another command line) may be made an **exception** instead.
|
|
87
93
|
|
|
88
|
-
Then `--apply`, `ctmchain export --env uat
|
|
89
|
-
|
|
94
|
+
Then `--apply`, look at `ctmchain export --env uat`, and commit. Same for bench and test (they
|
|
95
|
+
only learn the values of the constants that exist by now). Production is untouched:
|
|
96
|
+
`ctmchain export --env prod --jobnames original` is still byte for byte the prod export.
|
|
90
97
|
|
|
91
98
|
### 4. The conventions agreed for these chains
|
|
92
99
|
|
|
@@ -104,13 +111,33 @@ and test. Receiving the same export again shows what still differs.
|
|
|
104
111
|
environment, a letter in place of the application's first letter): not fixed on the way
|
|
105
112
|
in. `ctmchain validate` lists them with suggested fixes, to correct in later releases.
|
|
106
113
|
|
|
107
|
-
### 5. Close the
|
|
114
|
+
### 5. Close the environments release
|
|
108
115
|
|
|
109
116
|
```bash
|
|
110
|
-
ctmchain release installed prod # nothing to install:
|
|
117
|
+
ctmchain release installed prod # nothing to install: it only describes the environments
|
|
111
118
|
ctmchain release close
|
|
112
119
|
```
|
|
113
120
|
|
|
121
|
+
### 6. The releases already on their way, from their deliveries
|
|
122
|
+
|
|
123
|
+
For each release installed somewhere but not in production yet, ask, numbered: its name,
|
|
124
|
+
the delivery XML of each of its parts, and the environments it is installed in. Then, from
|
|
125
|
+
`main`, one release each:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
ctmchain release start <name>
|
|
129
|
+
ctmchain receive ../exports/deliveries/<name>.xml --stat # a delivery: its differences
|
|
130
|
+
ctmchain receive ../exports/deliveries/<name>.xml --apply # are the release's changes
|
|
131
|
+
ctmchain commit -m "feat: release <name>"
|
|
132
|
+
ctmchain release installed uat # each environment it is in
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
A folder two releases both changed comes whole in each delivery: when the first one is closed
|
|
136
|
+
into `main`, *Update from main* in the other merges them. Check each release with
|
|
137
|
+
`ctmchain receive ../exports/<env>/<env>.xml` (preview only) on the release branch: what is
|
|
138
|
+
left is what differs for another reason (often conditions whose names differ per
|
|
139
|
+
environment).
|
|
140
|
+
|
|
114
141
|
Then the everyday flow of the data repository's own `CLAUDE.md` (written by `init`):
|
|
115
142
|
deliveries through `ctmchain receive`, a full prod export now and then to catch up with
|
|
116
143
|
production, releases installed environment by environment.
|
|
@@ -213,6 +213,13 @@ or addresses anywhere in this public repository.
|
|
|
213
213
|
take` and the boxes of the *Compare* page; one attribute, variable, condition, group, job,
|
|
214
214
|
folder, condition name, constant value or exception at a time, conflicts value by value.
|
|
215
215
|
13. **PyPI release 0.1.0 — DONE (2026-11-03)**: Trusted Publishing from the `v0.1.0` tag.
|
|
216
|
+
14. **Similar condition names — DONE (2026-11-03), released in 0.1.1**, asked by the author:
|
|
217
|
+
`validate` reports names that differ only by a copy's suffix or by case (`similar-names`)
|
|
218
|
+
and offers to number them (`number-similar`: `…_OK1`, `…_OK2`).
|
|
219
|
+
15. **Only the values of an environment — DONE (2026-11-03), released in 0.1.2**, asked by
|
|
220
|
+
the author for the company (uat and bench carry releases production does not have yet):
|
|
221
|
+
`receive --values-only`, `skip` for added folders and jobs, *adopt* decisions; the
|
|
222
|
+
`bootstrap` skill builds the releases on their way from their deliveries.
|
|
216
223
|
|
|
217
224
|
Later, unordered: `envs` variants of sub-elements; second-environment import with learning; `diff`
|
|
218
225
|
of environments and XML files with `xmldiffreport`; mapping editor; upstream graph explorer
|
|
@@ -6,6 +6,52 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.2] - 2026-11-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `receive --values-only` and the *Only the values of <env>* box of the Import page: an export
|
|
14
|
+
of an environment whose jobs carry releases production does not have yet gives its
|
|
15
|
+
machines, users, mails, variables and its name in texts (new constants or their values) and
|
|
16
|
+
nothing else by default (differences and children ignored, jobs and folders it lacks kept,
|
|
17
|
+
folders and jobs only it has left out).
|
|
18
|
+
- *added* decisions have the option `skip` ("Leave out"): the folder or job does not enter the
|
|
19
|
+
repository.
|
|
20
|
+
- *adopt* decisions: where the repository still has production's value of a constant and the
|
|
21
|
+
export has the constant (a job no earlier export of that environment had), use the
|
|
22
|
+
constant; production does not change. Also inside texts (`[P77][PROD]` and
|
|
23
|
+
`@{env.subject}`).
|
|
24
|
+
- The `bootstrap` skill takes only the values from the full exports, then builds the releases
|
|
25
|
+
on their way from their deliveries; the data repository's `CLAUDE.md` mentions
|
|
26
|
+
`--values-only`.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- A job numbered by `--dedupe-jobnames` (`X2`) that a full export of another environment names
|
|
31
|
+
otherwise lost its original JOBNAME when kept: production's export changed.
|
|
32
|
+
- Values taken inside the children of a job (a mail of an ON block) were put back when the
|
|
33
|
+
job's children were kept as they were.
|
|
34
|
+
|
|
35
|
+
## [0.1.1] - 2026-11-03
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `validate` reports `similar-names` (warning): conditions whose names differ only by the
|
|
40
|
+
suffix a copy of a chain appended (`…_OK-U26`, `…_OK-U26-U31`) or by case (`_OK`, `_Ok`).
|
|
41
|
+
The fix `number-similar` gives them the common name with a number (`…_OK1`, `…_OK2`, …) in
|
|
42
|
+
every job that uses them; a condition with an end outside the repository keeps its name.
|
|
43
|
+
Names made of their own jobs (`A-TO-LOAD-S1`, `A-TO-LOAD-S2`) are not reported.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- A numbered standard name (`A-B_OK2`, `A-TO-B2`) is a standard name for `validate`.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Resolving several findings at once failed ("a batch cannot contain another batch") when a
|
|
52
|
+
fix was itself several changes (numbering repeated JOBNAMEs or similar names): a batch now
|
|
53
|
+
runs the changes of a batch inside it in its place.
|
|
54
|
+
|
|
9
55
|
## [0.1.0] - 2026-11-03
|
|
10
56
|
|
|
11
57
|
First release on PyPI.
|
|
@@ -182,5 +228,7 @@ First release on PyPI.
|
|
|
182
228
|
- On Windows, unknown `/api/...` paths and missing assets were answered with the app instead of
|
|
183
229
|
404 (the static files handler sees `\` separators there).
|
|
184
230
|
|
|
185
|
-
[Unreleased]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.
|
|
231
|
+
[Unreleased]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.2...HEAD
|
|
232
|
+
[0.1.2]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.1...v0.1.2
|
|
233
|
+
[0.1.1]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.0...v0.1.1
|
|
186
234
|
[0.1.0]: https://github.com/bilouro/controlm-chain-admin/releases/tag/v0.1.0
|
|
@@ -287,6 +287,27 @@ still exports prod byte for byte; uat comes out identical in 257 of 329 folders
|
|
|
287
287
|
production has them, listed by `validate`). Sub-element variants (an ON block for one
|
|
288
288
|
environment only) remain open.
|
|
289
289
|
|
|
290
|
+
**Only the values of an environment** (2026-11-03, asked by the author for the company, where
|
|
291
|
+
the full exports of uat and bench carry releases production does not have yet):
|
|
292
|
+
`receive --values-only` (the *Only the values of <env>* box of the Import page) takes from an
|
|
293
|
+
export what makes a constant — machines, users, mails, variables, the environment's name in a
|
|
294
|
+
text — and leaves everything else out by default: differences and children ignored, jobs it
|
|
295
|
+
lacks kept (*removed*: `keep`), folders it lacks kept (*absent*: `keep`), folders and jobs only
|
|
296
|
+
it has left out (*added*: `skip`, also offered in any full export; with values only it is a
|
|
297
|
+
decision in a delivery too); a repeated difference of another attribute is ignored rather than
|
|
298
|
+
made exceptions. The releases then come from their deliveries, one release branch each (the
|
|
299
|
+
`bootstrap` skill, step 6). Two decisions came with it, for every receive: *adopt* (one per
|
|
300
|
+
constant) — a job no earlier export of the environment had still has production's value of a
|
|
301
|
+
constant where this export has the constant, in the whole value or inside a text
|
|
302
|
+
(`[P77][PROD]` for `[P77][@{env.subject}]`): using the constant leaves production as it was, so
|
|
303
|
+
it is the default (it replaces the exceptions a full export made there); and values taken
|
|
304
|
+
inside children whose shape is kept (a release's new condition on the job that sends the mail)
|
|
305
|
+
stay taken. A job numbered by the deduplication (`P77_F2`) that another environment names
|
|
306
|
+
otherwise keeps its original JOBNAME when it is kept. On the real exports: prod first, then
|
|
307
|
+
uat and bench with values only, give 42 + 15 constants, no exception, prod byte for byte, uat
|
|
308
|
+
identical in 234 of 329 folders (the rest are its releases and its own folders) and bench in
|
|
309
|
+
287 of 341.
|
|
310
|
+
|
|
290
311
|
## 6. Import
|
|
291
312
|
|
|
292
313
|
- Lossless by default: re-importing the XML produced by `export` yields no changes
|
|
@@ -363,6 +384,20 @@ environment only) remain open.
|
|
|
363
384
|
back (`restore-word`). A MEMNAME that merely differs from its JOBNAME is not reported: on the
|
|
364
385
|
real chain 647 of them are script names. On the uat data: 39 replaced words, 4 letters in
|
|
365
386
|
place of the P, 1 foreign letter, 2 foreign resources (107 before `leading_only`).
|
|
387
|
+
- Similar names (implemented 2026-11-03, asked by the author: "detecta nomes similares no
|
|
388
|
+
validate e sugere nomes como nome1, nome2"): `similar-names` (warning, one finding per
|
|
389
|
+
family) groups the conditions whose names are equal once the suffixes a copy appended
|
|
390
|
+
(`-<letter><digits>`, repeated: `…_OK-U26-U31`) are removed and case is ignored; a family
|
|
391
|
+
whose every name is made of its own producer and consumer is left out (`A-TO-LOAD-S1`,
|
|
392
|
+
`A-TO-LOAD-S2` only look alike because the jobs do). The fix `number-similar` renames them,
|
|
393
|
+
shortest name first, to the stem of the first with a number (`…_OK1`, `…_OK2`; a number
|
|
394
|
+
another condition has is skipped) in every job that uses them; a condition with an end
|
|
395
|
+
outside the repository keeps its name, as a job outside may use it. `validate` accepts a
|
|
396
|
+
numbered standard name (`A-B_OK2`, `A-TO-B2`), so a numbered copy that still embeds the
|
|
397
|
+
original's jobs shows as `stale-name`, with its standard name offered. On the real chain:
|
|
398
|
+
51 families, 115 conditions; one family is left after the fix (its other members only have
|
|
399
|
+
a producer). Fixes that are several changes may now be resolved together: a `batch` runs a
|
|
400
|
+
batch inside it in its place.
|
|
366
401
|
- `diff A B`: plain `git diff` of the base is the first-class review tool. For XML↔XML semantic
|
|
367
402
|
comparison the tool depends on `xmldiffreport` (key-aligned, N-way, Markdown/HTML report).
|
|
368
403
|
- `show --envs <node>`: the node resolved in every environment, side by side, differences
|
|
@@ -686,7 +721,7 @@ the first import.
|
|
|
686
721
|
Quality
|
|
687
722
|
- `validate [--env <env>] [--strict]` — graph, naming, overlay and resolution checks; exit code = highest severity.
|
|
688
723
|
- `resolve [CODE=KIND...] [--apply] [--stat]` — implemented: fixes for the findings (see §8); without arguments, the fix kinds per code.
|
|
689
|
-
- `receive <xml> [--env ENV] [--full|--partial] [--choose ID=OPTION ...] [--apply] [--stat] [--format text|json]` — implemented: an export of one environment (some folders or all) taken into the checked-out release (§5): the decisions with their defaults, then the change set. A group's id holds `=`: `--choose 'group:RUN_AS=a>b=new:user.batch_user_proc'`.
|
|
724
|
+
- `receive <xml> [--env ENV] [--full|--partial] [--values-only] [--choose ID=OPTION ...] [--apply] [--stat] [--format text|json]` — implemented: an export of one environment (some folders or all) taken into the checked-out release (§5): the decisions with their defaults, then the change set. `--values-only`: the environment's values and nothing else by default. A group's id holds `=`: `--choose 'group:RUN_AS=a>b=new:user.batch_user_proc'`.
|
|
690
725
|
- `env learn <xml> [--env ENV] [--only PREFIX ...] [--apply]` — implemented: only the values of the constants in that environment (`--only user.` keeps some constants, e.g. when the export is anonymised).
|
|
691
726
|
- `set-exception <env> <node> [--set ATTR=VALUE] [--unset ATTR] [--var %%NAME=VALUE] [--drop NAME]` — implemented: an exception of one environment for one job or folder (§5), previewed, `--apply` writes `envs/<env>/overrides.json`.
|
|
692
727
|
- `rename-constant <old> <new>` — implemented: placeholders, `chain.json`, environment values and exceptions follow.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: controlm-chain-admin
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Manage BMC Control-M job chains as a git-friendly JSON repository: XML import/export, links between jobs, environment-neutral base, validation and refactorings.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bilouro/controlm-chain-admin
|
|
6
6
|
Project-URL: Documentation, https://github.com/bilouro/controlm-chain-admin#readme
|
|
@@ -85,8 +85,9 @@ export into:
|
|
|
85
85
|
other branch (one attribute, one job without its folder, one condition) can be taken into
|
|
86
86
|
yours, conflicts shown value by value.
|
|
87
87
|
- **Validation and refactorings** that the Control-M UI does not offer: dangling or stale
|
|
88
|
-
conditions, chains broken by renames,
|
|
89
|
-
|
|
88
|
+
conditions, chains broken by renames, condition names that differ only by a copy's suffix
|
|
89
|
+
or by case (numbered as the fix), renaming a sub-application across a folder, moving a job,
|
|
90
|
+
inserting a job in a chain.
|
|
90
91
|
- **Byte-identical export.** Untouched definitions export exactly as they were imported.
|
|
91
92
|
|
|
92
93
|
## Install
|
|
@@ -111,8 +112,9 @@ python -m venv .venv && .venv/bin/pip install -e ".[dev,web]"
|
|
|
111
112
|
```
|
|
112
113
|
|
|
113
114
|
The `bootstrap` skill (`.claude/skills/bootstrap/SKILL.md`) takes it from there, for a person
|
|
114
|
-
or Claude Code: the gate, then a new chain repository
|
|
115
|
-
|
|
115
|
+
or Claude Code: the gate, then a new chain repository: production first, the values of the
|
|
116
|
+
other environments from their full exports, then the releases on their way from their
|
|
117
|
+
deliveries.
|
|
116
118
|
|
|
117
119
|
## Quickstart
|
|
118
120
|
|
|
@@ -136,6 +138,7 @@ ctmchain package main release/2026-11 --env uat --repo ./my-chains # import fil
|
|
|
136
138
|
ctmchain release installed uat --repo ./my-chains # tag uat/2026-11-1
|
|
137
139
|
ctmchain receive delivery.xml --repo ./my-chains # a team's folders, made neutral
|
|
138
140
|
ctmchain receive uat_full.xml --full --repo ./my-chains # all of uat: constants, what only uat has
|
|
141
|
+
ctmchain receive uat_full.xml --values-only --repo ./my-chains # only uat's machines, users, mails
|
|
139
142
|
ctmchain take release/2026-10 --list --repo ./my-chains # what another release changed
|
|
140
143
|
ctmchain take release/2026-10 --item job:P77_CHAIN/P77_D --repo ./my-chains # one job of it; --apply
|
|
141
144
|
ctmchain release close --repo ./my-chains # once installed in prod
|
|
@@ -39,8 +39,9 @@ export into:
|
|
|
39
39
|
other branch (one attribute, one job without its folder, one condition) can be taken into
|
|
40
40
|
yours, conflicts shown value by value.
|
|
41
41
|
- **Validation and refactorings** that the Control-M UI does not offer: dangling or stale
|
|
42
|
-
conditions, chains broken by renames,
|
|
43
|
-
|
|
42
|
+
conditions, chains broken by renames, condition names that differ only by a copy's suffix
|
|
43
|
+
or by case (numbered as the fix), renaming a sub-application across a folder, moving a job,
|
|
44
|
+
inserting a job in a chain.
|
|
44
45
|
- **Byte-identical export.** Untouched definitions export exactly as they were imported.
|
|
45
46
|
|
|
46
47
|
## Install
|
|
@@ -65,8 +66,9 @@ python -m venv .venv && .venv/bin/pip install -e ".[dev,web]"
|
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
The `bootstrap` skill (`.claude/skills/bootstrap/SKILL.md`) takes it from there, for a person
|
|
68
|
-
or Claude Code: the gate, then a new chain repository
|
|
69
|
-
|
|
69
|
+
or Claude Code: the gate, then a new chain repository: production first, the values of the
|
|
70
|
+
other environments from their full exports, then the releases on their way from their
|
|
71
|
+
deliveries.
|
|
70
72
|
|
|
71
73
|
## Quickstart
|
|
72
74
|
|
|
@@ -90,6 +92,7 @@ ctmchain package main release/2026-11 --env uat --repo ./my-chains # import fil
|
|
|
90
92
|
ctmchain release installed uat --repo ./my-chains # tag uat/2026-11-1
|
|
91
93
|
ctmchain receive delivery.xml --repo ./my-chains # a team's folders, made neutral
|
|
92
94
|
ctmchain receive uat_full.xml --full --repo ./my-chains # all of uat: constants, what only uat has
|
|
95
|
+
ctmchain receive uat_full.xml --values-only --repo ./my-chains # only uat's machines, users, mails
|
|
93
96
|
ctmchain take release/2026-10 --list --repo ./my-chains # what another release changed
|
|
94
97
|
ctmchain take release/2026-10 --item job:P77_CHAIN/P77_D --repo ./my-chains # one job of it; --apply
|
|
95
98
|
ctmchain release close --repo ./my-chains # once installed in prod
|
|
@@ -253,6 +253,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
253
253
|
dest="full",
|
|
254
254
|
help="some folders only, e.g. a delivery",
|
|
255
255
|
)
|
|
256
|
+
rec.add_argument(
|
|
257
|
+
"--values-only",
|
|
258
|
+
action="store_true",
|
|
259
|
+
help="take the values of the environment (machines, users, mails, its name in texts) "
|
|
260
|
+
"and leave the rest out by default: for an environment with releases production has not",
|
|
261
|
+
)
|
|
256
262
|
rec.add_argument(
|
|
257
263
|
"--choose",
|
|
258
264
|
action="append",
|
|
@@ -564,8 +570,8 @@ def cmd_env_help(args: argparse.Namespace) -> int:
|
|
|
564
570
|
|
|
565
571
|
_SHOWN_PER_KIND = 20
|
|
566
572
|
_DECISION_ORDER = (
|
|
567
|
-
"learn", "set-value", "group", "constant", "unmatched", "value", "children",
|
|
568
|
-
"added", "absent",
|
|
573
|
+
"learn", "set-value", "adopt", "group", "constant", "unmatched", "value", "children",
|
|
574
|
+
"removed", "added", "absent",
|
|
569
575
|
) # fmt: skip
|
|
570
576
|
|
|
571
577
|
|
|
@@ -581,6 +587,9 @@ def _decision_line(decision: intake_mod.Decision) -> str:
|
|
|
581
587
|
return f"{head} {decision.constant}: {decision.expected} -> {decision.incoming}{uses}"
|
|
582
588
|
if decision.kind in ("value", "constant"):
|
|
583
589
|
return f"{head} {decision.current} -> {decision.incoming}"
|
|
590
|
+
if decision.kind == "adopt":
|
|
591
|
+
uses = _plural(decision.count, "use")
|
|
592
|
+
return f"{head} {decision.incoming} where the repository has {decision.current} ({uses})"
|
|
584
593
|
if decision.kind == "unmatched":
|
|
585
594
|
uses = _plural(decision.count, "use")
|
|
586
595
|
return (
|
|
@@ -607,6 +616,7 @@ def _print_intake(intake: intake_mod.Intake) -> None:
|
|
|
607
616
|
f"Folders: {len(intake.folders)} ({statuses['new']} new, {statuses['changed']} changed, "
|
|
608
617
|
f"{statuses['same']} unchanged)"
|
|
609
618
|
+ (", full export" if intake.full else ", some folders only" if intake.partial else "")
|
|
619
|
+
+ (", values only" if intake.values_only else "")
|
|
610
620
|
)
|
|
611
621
|
if intake.decisions:
|
|
612
622
|
print(
|
|
@@ -630,12 +640,12 @@ def cmd_receive(args: argparse.Namespace) -> int:
|
|
|
630
640
|
repo = load_repo(args.repo)
|
|
631
641
|
data = args.xml.read_bytes()
|
|
632
642
|
choices = _pairs(args.choose, "ID=OPTION", last=True) # ids may hold "=", options do not
|
|
633
|
-
intake = intake_mod.analyze(repo, data, args.env, args.full)
|
|
643
|
+
intake = intake_mod.analyze(repo, data, args.env, args.full, args.values_only)
|
|
634
644
|
if args.format == "text":
|
|
635
645
|
_print_intake(intake)
|
|
636
646
|
if args.apply:
|
|
637
647
|
workspace_mod.ensure_editable(repo)
|
|
638
|
-
change = intake_mod.receive(repo, data, intake.env, choices, intake.full)
|
|
648
|
+
change = intake_mod.receive(repo, data, intake.env, choices, intake.full, args.values_only)
|
|
639
649
|
if args.format == "json":
|
|
640
650
|
result = refactor_mod.apply(repo, change) if args.apply and change.edits else None
|
|
641
651
|
body = {"intake": intake.to_json(), "change": change.to_json(), "applied": bool(result)}
|
{controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/fixes.py
RENAMED
|
@@ -26,10 +26,12 @@ from controlm_chain_admin.core.repo import ChainRepo, read_json
|
|
|
26
26
|
from controlm_chain_admin.core.validate import (
|
|
27
27
|
Finding,
|
|
28
28
|
NameRules,
|
|
29
|
+
copy_stem,
|
|
29
30
|
embeds_endpoints,
|
|
30
31
|
parse_condition_name,
|
|
31
32
|
replaced_words,
|
|
32
33
|
resources,
|
|
34
|
+
similar_names,
|
|
33
35
|
validate,
|
|
34
36
|
)
|
|
35
37
|
|
|
@@ -101,6 +103,7 @@ class _Index:
|
|
|
101
103
|
jobs: dict[str, list[str]] # folder -> job ids
|
|
102
104
|
repo: ChainRepo | None = None
|
|
103
105
|
names: NameRules | None = None
|
|
106
|
+
similar: dict[str, list[Link]] = field(default_factory=dict) # stem -> similar conditions
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
def _index(repo: ChainRepo) -> _Index:
|
|
@@ -114,7 +117,16 @@ def _index(repo: ChainRepo) -> _Index:
|
|
|
114
117
|
for job in ids:
|
|
115
118
|
by_name.setdefault(job.split(DUPLICATE_SEPARATOR)[0], []).append(NodeRef(folder, job))
|
|
116
119
|
used = {link.name: link.id for link in links.values()}
|
|
117
|
-
return _Index(
|
|
120
|
+
return _Index(
|
|
121
|
+
links,
|
|
122
|
+
standard_names(links),
|
|
123
|
+
used,
|
|
124
|
+
by_name,
|
|
125
|
+
jobs,
|
|
126
|
+
repo,
|
|
127
|
+
NameRules.of(repo),
|
|
128
|
+
similar_names(links),
|
|
129
|
+
)
|
|
118
130
|
|
|
119
131
|
|
|
120
132
|
def _waiting(finding: Finding, link: Link, index: _Index) -> list[Fix]:
|
|
@@ -277,6 +289,50 @@ def _numbering(finding: Finding, index: _Index) -> list[Fix]:
|
|
|
277
289
|
]
|
|
278
290
|
|
|
279
291
|
|
|
292
|
+
def _numbering_similar(finding: Finding, index: _Index) -> list[Fix]:
|
|
293
|
+
stem, family = finding.name, index.similar.get(finding.name, [])
|
|
294
|
+
kept = [link for link in family if link.external]
|
|
295
|
+
taken = {name.upper() for name in index.used}
|
|
296
|
+
renames: list[tuple[Link, str]] = []
|
|
297
|
+
number = 1
|
|
298
|
+
for link in (link for link in family if not link.external):
|
|
299
|
+
while True:
|
|
300
|
+
name = f"{stem}{number}"
|
|
301
|
+
if copy_stem(name) != name: # the stem ends with -<letter>: keep the number apart
|
|
302
|
+
name = f"{stem}_{number}"
|
|
303
|
+
number += 1
|
|
304
|
+
if name.upper() not in taken:
|
|
305
|
+
break
|
|
306
|
+
renames.append((link, name))
|
|
307
|
+
if not renames:
|
|
308
|
+
return []
|
|
309
|
+
op, params = _operation(
|
|
310
|
+
[("rename-link", {"link": link.id, "name": name}) for link, name in renames]
|
|
311
|
+
)
|
|
312
|
+
explanation = (
|
|
313
|
+
f"Gives each condition the common name with a number at the end ({stem}1, {stem}2…), "
|
|
314
|
+
"in every job that uses it, so that no two names differ only by a suffix or by case."
|
|
315
|
+
)
|
|
316
|
+
explanation += "".join(
|
|
317
|
+
f" {link.name} keeps its name: a job outside this repository may use it." for link in kept
|
|
318
|
+
)
|
|
319
|
+
return [
|
|
320
|
+
Fix(
|
|
321
|
+
"number-similar",
|
|
322
|
+
"Number the similar names",
|
|
323
|
+
"; ".join(f"rename {link.name} -> {name}" for link, name in renames),
|
|
324
|
+
explanation,
|
|
325
|
+
op,
|
|
326
|
+
params,
|
|
327
|
+
tuple(
|
|
328
|
+
Effect(str(node), "change", "condition name", link.name, name)
|
|
329
|
+
for link, name in renames
|
|
330
|
+
for node in _users(link)
|
|
331
|
+
),
|
|
332
|
+
)
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
|
|
280
336
|
def _rename_to(link: Link, name: str, index: _Index, kind: str, title: str, why: str) -> list[Fix]:
|
|
281
337
|
if name == link.name or index.used.get(name, link.id) != link.id:
|
|
282
338
|
return [] # nothing to do, or the name belongs to another link
|
|
@@ -367,6 +423,8 @@ def _name_fixes(finding: Finding, index: _Index) -> list[Fix]:
|
|
|
367
423
|
def _fixes(finding: Finding, index: _Index) -> list[Fix]:
|
|
368
424
|
if finding.code == "duplicate-jobname":
|
|
369
425
|
return _numbering(finding, index)
|
|
426
|
+
if finding.code == "similar-names":
|
|
427
|
+
return _numbering_similar(finding, index)
|
|
370
428
|
if finding.code in (
|
|
371
429
|
"foreign-letter",
|
|
372
430
|
"letter-for-p",
|
{controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/guide.py
RENAMED
|
@@ -23,7 +23,9 @@ Environments: {environments}. Production is {production}: `{trunk}` mirrors it.
|
|
|
23
23
|
it) and closed into `{trunk}` once {production} has it (`ctmchain release close`).
|
|
24
24
|
3. Exports come in through `ctmchain receive <xml>` or the *Import* page: preview, decide,
|
|
25
25
|
`--apply`, commit. A full export of an environment other than {production} leaves
|
|
26
|
-
{production} alone by default; a delivery (some folders) changes the release.
|
|
26
|
+
{production} alone by default; a delivery (some folders) changes the release. When that
|
|
27
|
+
environment has releases {production} has not, take only its values (`--values-only`, box
|
|
28
|
+
*Only the values* of the *Import* page): the releases come from their deliveries.
|
|
27
29
|
4. Never hand-edit `index.json` (derived) or `meta/` (volatile attributes, export header);
|
|
28
30
|
edit through `ctmchain` or the web UI, which preview every change.
|
|
29
31
|
5. Exports are big: read them with scripts and counts, never whole.
|
|
@@ -45,6 +47,7 @@ Environments: {environments}. Production is {production}: `{trunk}` mirrors it.
|
|
|
45
47
|
ctmchain release status # branch, releases, what may be edited
|
|
46
48
|
ctmchain receive exports/uat/delivery.xml # preview the decisions of an export
|
|
47
49
|
ctmchain receive exports/uat/delivery.xml --apply
|
|
50
|
+
ctmchain receive exports/uat/full.xml --values-only # machines, users, mails of uat only
|
|
48
51
|
ctmchain take release/<other> --list # what another release changed, item by item
|
|
49
52
|
ctmchain validate # broken chains, dirty names, suggested fixes
|
|
50
53
|
ctmchain export --env uat -o uat.xml
|