hyperi-ci 2.2.0__tar.gz → 2.2.1__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.
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/CHANGELOG.md +9 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/PKG-INFO +1 -1
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/STATE.md +31 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/TODO.md +46 -4
- hyperi_ci-2.2.1/VERSION +1 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/ARCHITECTURE.md +17 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/cli.py +13 -1
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/gh.py +6 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/init.py +8 -1
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/watch.py +31 -19
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/integration/test_rust_build_optimize.py +8 -5
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_init.py +17 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_watch.py +36 -0
- hyperi_ci-2.2.0/VERSION +0 -1
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/.ai-version +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/doco.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/load.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/review.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/save.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/setup-claude.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/simplify.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/commands/standards.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/UNIVERSAL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/ai-conduct.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/ansible.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/bash.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/chars-policy.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/ci.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/clickhouse-sql.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/code-header.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/code-style.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/config-and-logging.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/cpp.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/design-principles.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/dfe-metrics.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/docker.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/error-handling.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/git.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/golang.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/issue-management.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/k8s.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/licensing.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/mocks-policy.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/pki.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/python.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/repo-naming.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/rust.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/security.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/sql-clickhouse.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/terraform.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/testing.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/typescript.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/rules/universal.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/settings.json +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/ai-common/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/ai-guidelines/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/bleeding-edge/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/ci-check/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/ci-logs/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/ci-watch/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/deps/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/docs-audit/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/documentation/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/python/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/release/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/standards/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/test-review/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.claude/skills/verification/SKILL.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.gitattributes +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/actions/predict-version/action.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/_release-tail.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/ci.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/go-ci.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/python-ci.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/rust-ci.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.github/workflows/ts-ci.yml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.gitignore +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.gitmodules +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.hyperi-ci.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.mcp.json +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/.releaserc.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/AI-TRAINING-POLICY.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/CLAUDE.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/LICENSE +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/README.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/config/commit-types.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/config/org.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/config/runners.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/config/secrets-access.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/config/versions.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/ARC-RUNNERS.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/CI-LESSONS.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/DESIGN.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/JFROG-MIGRATION.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/MIGRATION-GUIDE.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/PGO-WORKLOAD-GUIDE.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/deployment-contract.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/migration/deployment-contract-tier3.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/rust.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/docs/typescript.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/pyproject.toml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/renovate.json +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/robots.txt +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/scripts/pre-commit-versions.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/scripts/setup-rust-dev.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/scripts/sync-secrets-access.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/scripts/update-versions.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/common.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/defaults.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/native-deps/golang.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/native-deps/python.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/native-deps/rust.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/native-deps/typescript.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/org.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/toolchains/gcc.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config/toolchains/llvm.yaml +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/config.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/binary_stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/compose.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/detect.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/labels.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/manifest.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/registry.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/container/templates.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/cli.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/contract.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/detect.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/registry.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/scaffold.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/deployment/stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/detect.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/dispatch.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/install_deps.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/_build_common.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/golang/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/golang/build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/golang/publish.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/golang/quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/golang/test.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/python/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/python/build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/python/publish.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/python/quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/python/test.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/quality_common.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/optimize.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/pgo.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/publish.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/rust/test.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/_common.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/install_deps.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/publish.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/languages/typescript/test.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/logs.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/migrate.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/native_deps.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/publish/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/publish/binaries.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/publish/dispatch.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/publish_binaries.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/push.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/quality/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/quality/commit_validation.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/quality/gitleaks.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/release.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/trigger.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/src/hyperi_ci/upgrade.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/README.md +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/grpc-server.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/http-server.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/kafka-consumer.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/kafka-producer.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/templates/pgo-workload/multi-protocol.sh +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/integration/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/__init__.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_contract.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_detect_tier.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_emit_artefacts_cli.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_registry_cascade.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_scaffold.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/deployment/test_stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_cli.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_commit_validation.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_common.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_config.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_binary_stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_build.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_compose.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_detect.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_labels.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_manifest.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_registry.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_stage.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_container_templates.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_detect.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_dispatch_alias.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_gh.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_migrate.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_native_deps.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_publish.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_push.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_rust_optimize.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_rust_pgo.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_rust_quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_typescript_quality.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_upgrade.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/tests/unit/test_workflow_consistency.py +0 -0
- {hyperi_ci-2.2.0 → hyperi_ci-2.2.1}/uv.lock +0 -0
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [2.2.1](https://github.com/hyperi-io/hyperi-ci/compare/v2.2.0...v2.2.1) (2026-05-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **init:** scaffold ci.yml with tag input on workflow_dispatch ([e6f7470](https://github.com/hyperi-io/hyperi-ci/commit/e6f7470c827ee8013db5ae2ed9b0af27c0226d93))
|
|
7
|
+
* **test:** align spike-channel allocator test with current jemalloc-everywhere policy ([9de19e4](https://github.com/hyperi-io/hyperi-ci/commit/9de19e4a3d897b44caf0c586f5548443c1b8015d))
|
|
8
|
+
* **watch:** accept --repo flag for cross-repo run watching ([378dea3](https://github.com/hyperi-io/hyperi-ci/commit/378dea37b86f7d3c3ca508233c57a9058ee28cdb))
|
|
9
|
+
|
|
1
10
|
# [2.2.0](https://github.com/hyperi-io/hyperi-ci/compare/v2.1.6...v2.2.0) (2026-05-08)
|
|
2
11
|
|
|
3
12
|
|
|
@@ -157,6 +157,16 @@ PR review and release-worthy pushes run them. The gate is computed
|
|
|
157
157
|
ONCE in the `plan` job and consumed by every downstream job; do not
|
|
158
158
|
re-implement the condition string elsewhere — `tests/unit/test_workflow_consistency.py` enforces this.
|
|
159
159
|
|
|
160
|
+
## CI bug-log convention (cross-repo)
|
|
161
|
+
|
|
162
|
+
CI bugs and fixes surfaced in consumer repos are logged under
|
|
163
|
+
`<consumer>/docs/superpowers/plans/<date>-ci-<topic>.md` (gitignored
|
|
164
|
+
local-only) plus a one-line entry in that consumer's `TODO.md`.
|
|
165
|
+
This is the SSoT location for CI fixes across the org — when canary
|
|
166
|
+
runs surface bugs, look there for the resolution status. The
|
|
167
|
+
hyperi-ci rollout doc references back to those plans so the loop
|
|
168
|
+
is closeable.
|
|
169
|
+
|
|
160
170
|
## Architecture
|
|
161
171
|
|
|
162
172
|
See `docs/DESIGN.md` for full architecture documentation.
|
|
@@ -332,6 +342,27 @@ See `docs/MIGRATION-GUIDE.md` for migrating projects from v1 to v2.
|
|
|
332
342
|
- **dfe-protocol-sdk** — plugin system removed
|
|
333
343
|
- **dfe-receiver-plugin-syslog** — syslog is built-in transport
|
|
334
344
|
|
|
345
|
+
## Future direction (aspirational)
|
|
346
|
+
|
|
347
|
+
Today: GitHub for git hosting, GitHub Actions for CI. Likely move when
|
|
348
|
+
budget and time allow:
|
|
349
|
+
|
|
350
|
+
- **Codeberg** for git hosting — reduce single-vendor lock-in to GitHub.
|
|
351
|
+
- **Buildkite** for CI — stronger pipeline ergonomics, self-hosted
|
|
352
|
+
runners without ARC's K8s overhead.
|
|
353
|
+
|
|
354
|
+
Design implications today:
|
|
355
|
+
|
|
356
|
+
- CI logic stays in the `hyperi-ci` Python CLI, not embedded in
|
|
357
|
+
workflow YAML. Buildkite (or any successor) calls the same CLI;
|
|
358
|
+
only the runner glue changes.
|
|
359
|
+
- Workflows stay thin — plan job + gates + handler dispatch.
|
|
360
|
+
- Avoid hard dependencies on GitHub-only features in handler code
|
|
361
|
+
(Actions-specific matrix syntax, GHCR-only auth flows).
|
|
362
|
+
|
|
363
|
+
Not on the near-term roadmap; recorded so we don't accidentally make
|
|
364
|
+
choices that paint us into the GitHub-Actions corner.
|
|
365
|
+
|
|
335
366
|
## Licensing
|
|
336
367
|
|
|
337
368
|
Proprietary — HYPERI PTY LIMITED.
|
|
@@ -20,9 +20,14 @@ This is the **single source of truth** for all tasks and progress.
|
|
|
20
20
|
|
|
21
21
|
**Canaries (no SEP fields — fix bugs found, don't dismiss):**
|
|
22
22
|
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
- [ ] **Canary
|
|
23
|
+
- [x] **Canary 0: hyperi-ci's own `test-projects/`** — integration tests green; one stale spike-allocator test fixed (commit `a331a0b`).
|
|
24
|
+
- [x] **Canary 1a: dfe-loader chore-skip** — [run 25531398891](https://github.com/hyperi-io/dfe-loader/actions/runs/25531398891) — 16 seconds total, plan ✅, every other job skipped. **Killer feature validated.**
|
|
25
|
+
- [ ] **Canary 1b: dfe-loader publish-dispatch** — [run 25531808212](https://github.com/hyperi-io/dfe-loader/actions/runs/25531808212) in flight as of 2026-05-08T01:40Z. Plan/Quality/Test green; both Build (linux-amd64, linux-arm64) jobs in_progress. BOLT/PGO; expect 30-45 min.
|
|
26
|
+
- [x] **Canary 2a: dfe-engine chore-skip** — [run 25531462469](https://github.com/hyperi-io/dfe-engine/actions/runs/25531462469) — 33 seconds, all heavy jobs skipped. **Gate validated.**
|
|
27
|
+
- [ ] **Canary 2b: dfe-engine publish-dispatch** — failed at quality stage on real pre-existing CVEs (pytest, cryptography). NOT a gate regression. Handed back to dfe-engine maintainers via [`dfe-engine/docs/superpowers/plans/2026-05-08-ci-canary-publish-fix.md`](../../dfe-engine/docs/superpowers/plans/2026-05-08-ci-canary-publish-fix.md). Blocked on merge coordination (Derek + Kaz have WIP on `feat/transport-filter-helpers`).
|
|
28
|
+
- [ ] **Canary 3 (added 2026-05-08): hyperi-pylib publish-dispatch** — preferred Python canary now: clean lib, no parallel WIP. User to drive.
|
|
29
|
+
|
|
30
|
+
**Ordering update 2026-05-08:** Python canary order is hyperi-pylib FIRST (clean), then dfe-engine after merge coordination. dfe-engine is the most complex Python case but its current branch state makes it a poor first canary.
|
|
26
31
|
|
|
27
32
|
**Done means:**
|
|
28
33
|
|
|
@@ -32,7 +37,44 @@ This is the **single source of truth** for all tasks and progress.
|
|
|
32
37
|
- A `Publish: true` commit on each canary still runs the full pipeline and ships to the right registries
|
|
33
38
|
- v2.2.0+ of hyperi-ci is published to PyPI
|
|
34
39
|
|
|
35
|
-
**Bugs discovered during rollout (no SEP fields):**
|
|
40
|
+
**Bugs discovered during rollout (no SEP fields):**
|
|
41
|
+
|
|
42
|
+
- [x] `tests/integration/test_rust_build_optimize.py::test_spike_channel_default_uses_system_allocator` encoded the old "spike → system allocator" policy; current code (and standards/rules/RUST.md Allocator Policy) says jemalloc at every channel. Fixed in commit `a331a0b`.
|
|
43
|
+
- [x] `_release-tail.yml` expects a single `build-dist-*` artifact containing both `dist/` AND `ci-tmp/`. The plan's draft proposed splitting into separate artifacts which would have broken release-tail's download step. Combined-upload pattern preserved.
|
|
44
|
+
- [x] `ts-ci.yml` had a duplicate `setup:` job key (silent YAML override) and `test:` job missing `needs:`/`if:`. Fixed during Task 8.
|
|
45
|
+
- [x] `go-ci.yml` ran quality + test unconditionally before any gate decision (setup ran AFTER them). Fixed during Task 9.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### Follow-up: dedicated CI test repos (next plan)
|
|
50
|
+
|
|
51
|
+
**Why:** in-repo `test-projects/` fixtures validate the CLI, but cannot exercise GitHub-side workflow behaviour (chore-skip, semantic-release tagging, publish flow). Real validation needs separate repos that push commits and trigger real CI runs.
|
|
52
|
+
|
|
53
|
+
**Proposed minimum:**
|
|
54
|
+
|
|
55
|
+
- `hyperi-io/ci-test-rust-app` — single-crate binary (chore-skip, R2 + GH Release path)
|
|
56
|
+
- `hyperi-io/ci-test-rust-workspace` — 3-crate workspace (workspace version stamp)
|
|
57
|
+
- `hyperi-io/ci-test-python-pypi` — Python lib for PyPI (sdist clean)
|
|
58
|
+
- `hyperi-io/ci-test-python-app` — Python container app (Tier-3 deployment-contract path)
|
|
59
|
+
- `hyperi-io/ci-test-ts-app` — TypeScript bundle (npm publish)
|
|
60
|
+
- `hyperi-io/ci-test-go-app` — Go binary (cross-compile, GH Release)
|
|
61
|
+
|
|
62
|
+
**Status:** scoped only — needs its own plan written. Track separately when we pick this up.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Follow-up: deployment artefact distribution (next plan)
|
|
67
|
+
|
|
68
|
+
**Why:** `hyperi-ci run generate` produces `argocd-application.yaml`, `chart/` (Helm), `Dockerfile.runtime`, `container-manifest.json`. These get uploaded as a CI artifact and then... dropped on the floor. `_release-tail.yml` consumes only the container-manifest. ArgoCD operators have no consumable distribution path.
|
|
69
|
+
|
|
70
|
+
**Proposed:**
|
|
71
|
+
|
|
72
|
+
- Helm `chart/` → push to GHCR as OCI artifact (`oci://ghcr.io/hyperi-io/charts/<app>:vX.Y.Z`); ArgoCD ≥2.6 reads OCI natively
|
|
73
|
+
- `argocd-application.yaml` → PR into a `hyperi-io/gitops` cluster-config repo via GitHub App with `contents:write`
|
|
74
|
+
- Versioning: chart version === app version === `next-version` from plan output
|
|
75
|
+
- NOT R2 — Helm and ArgoCD don't read from R2; would need to reinvent chart-museum
|
|
76
|
+
|
|
77
|
+
**Status:** scoped only — needs its own plan written. Out of scope for the KISS consolidation.
|
|
36
78
|
|
|
37
79
|
---
|
|
38
80
|
|
hyperi_ci-2.2.1/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.2.1
|
|
@@ -154,6 +154,23 @@ workflows.
|
|
|
154
154
|
- ["GitHub Actions Is Slowly Killing Your Engineering Team"](https://www.iankduncan.com/engineering/2026-02-05-github-actions-killing-your-team/) — explicit warning against generic-abstraction-layer reusable workflows
|
|
155
155
|
- [Composite-action path resolution discussion #26245](https://github.com/orgs/community/discussions/26245) — confirms the cross-repo `./` problem is unsolved as of May 2026
|
|
156
156
|
|
|
157
|
+
## Future portability (aspirational)
|
|
158
|
+
|
|
159
|
+
We may move from GitHub + GitHub Actions to Codeberg (git) +
|
|
160
|
+
Buildkite (CI) when budget and time allow. Not on the near-term
|
|
161
|
+
roadmap, but it shapes design today:
|
|
162
|
+
|
|
163
|
+
- The `hyperi-ci` Python CLI owns the work — quality, test, build,
|
|
164
|
+
publish. The reusable workflows are thin runner glue around it.
|
|
165
|
+
Porting to Buildkite means rewriting the glue, not the CLI.
|
|
166
|
+
- Handler code (`src/hyperi_ci/languages/<lang>/*.py`) avoids
|
|
167
|
+
GitHub-only assumptions: no `${{ ... }}` template syntax leaking
|
|
168
|
+
into Python, no GHCR-only auth flows hardcoded in publish handlers.
|
|
169
|
+
- The plan job + gate-output pattern translates directly to
|
|
170
|
+
Buildkite's dynamic pipelines.
|
|
171
|
+
|
|
172
|
+
The shared CI tool keeps the cost of switching CI vendors bounded.
|
|
173
|
+
|
|
157
174
|
## Pre-2026-05-08 archaeology
|
|
158
175
|
|
|
159
176
|
Before this consolidation, `_setup.yml` existed as a shared first-job
|
|
@@ -424,11 +424,23 @@ def watch(
|
|
|
424
424
|
int,
|
|
425
425
|
typer.Option("--interval", "-i", help="Initial poll interval in seconds"),
|
|
426
426
|
] = 30,
|
|
427
|
+
repo: Annotated[
|
|
428
|
+
str | None,
|
|
429
|
+
typer.Option(
|
|
430
|
+
"--repo",
|
|
431
|
+
"-R",
|
|
432
|
+
help=(
|
|
433
|
+
"Target repo as owner/name (e.g. hyperi-io/dfe-loader). "
|
|
434
|
+
"Defaults to the cwd's git remote — set this when watching "
|
|
435
|
+
"a run in a different repo than your cwd."
|
|
436
|
+
),
|
|
437
|
+
),
|
|
438
|
+
] = None,
|
|
427
439
|
) -> None:
|
|
428
440
|
"""Watch a GitHub Actions run to completion."""
|
|
429
441
|
from hyperi_ci.watch import watch_run
|
|
430
442
|
|
|
431
|
-
rc = watch_run(run_id=run_id, timeout=timeout, interval=interval)
|
|
443
|
+
rc = watch_run(run_id=run_id, timeout=timeout, interval=interval, repo=repo)
|
|
432
444
|
raise typer.Exit(rc)
|
|
433
445
|
|
|
434
446
|
|
|
@@ -91,18 +91,24 @@ def gh_json(
|
|
|
91
91
|
def get_latest_run(
|
|
92
92
|
branch: str | None = None,
|
|
93
93
|
workflow: str | None = None,
|
|
94
|
+
repo: str | None = None,
|
|
94
95
|
) -> dict | None:
|
|
95
96
|
"""Find the most recent workflow run.
|
|
96
97
|
|
|
97
98
|
Args:
|
|
98
99
|
branch: Filter by branch name.
|
|
99
100
|
workflow: Filter by workflow filename.
|
|
101
|
+
repo: Optional ``owner/name`` — when set, queries this repo
|
|
102
|
+
instead of the cwd's git remote. Use this when looking up
|
|
103
|
+
runs in a different repo than your cwd.
|
|
100
104
|
|
|
101
105
|
Returns:
|
|
102
106
|
Dict with run info, or None if no runs found.
|
|
103
107
|
|
|
104
108
|
"""
|
|
105
109
|
args = ["run", "list", "--limit", "1"]
|
|
110
|
+
if repo:
|
|
111
|
+
args.extend(["--repo", repo])
|
|
106
112
|
if branch:
|
|
107
113
|
args.extend(["--branch", branch])
|
|
108
114
|
if workflow:
|
|
@@ -261,15 +261,22 @@ def _render_workflow(
|
|
|
261
261
|
" pull_request:\n"
|
|
262
262
|
" branches: [main]\n"
|
|
263
263
|
" workflow_dispatch:\n"
|
|
264
|
+
" inputs:\n"
|
|
265
|
+
" tag:\n"
|
|
266
|
+
" type: string\n"
|
|
267
|
+
" required: true\n"
|
|
268
|
+
' description: "Tag to publish (e.g. v1.3.0)"\n'
|
|
264
269
|
"\n"
|
|
265
270
|
"jobs:\n"
|
|
266
271
|
" ci:\n"
|
|
267
272
|
f" uses: {_CI_REPO}/.github/workflows/"
|
|
268
273
|
f"{workflow_file}@{_WORKFLOW_REF}\n"
|
|
274
|
+
" with:\n"
|
|
275
|
+
" tag: ${{ inputs.tag || '' }}\n"
|
|
269
276
|
)
|
|
270
277
|
|
|
271
278
|
if publish_target != "internal":
|
|
272
|
-
base += f"
|
|
279
|
+
base += f" publish-target: {publish_target}\n"
|
|
273
280
|
|
|
274
281
|
base += " secrets: inherit\n"
|
|
275
282
|
return base
|
|
@@ -67,11 +67,16 @@ def _poll_interval(base: int, attempt: int) -> float:
|
|
|
67
67
|
return min(base * (1.5 ** min(attempt - 1, 4)), 120.0)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def _get_run_status(run_id: str) -> dict | None:
|
|
70
|
+
def _get_run_status(run_id: str, repo: str | None = None) -> dict | None:
|
|
71
71
|
"""Fetch current run status.
|
|
72
72
|
|
|
73
73
|
Args:
|
|
74
74
|
run_id: Workflow run ID.
|
|
75
|
+
repo: Optional ``owner/name`` — pass this when watching a run
|
|
76
|
+
in a different repo than the current working directory.
|
|
77
|
+
``gh run view`` defaults to the cwd's git remote and
|
|
78
|
+
silently 404s when the run isn't there, which the watch
|
|
79
|
+
loop misreads as transient network failure.
|
|
75
80
|
|
|
76
81
|
Returns:
|
|
77
82
|
Dict with status/conclusion/jobs, or None on transient error.
|
|
@@ -82,26 +87,28 @@ def _get_run_status(run_id: str) -> dict | None:
|
|
|
82
87
|
`_MAX_CONSECUTIVE_FETCH_FAILURES`.
|
|
83
88
|
|
|
84
89
|
"""
|
|
90
|
+
args = [
|
|
91
|
+
"run",
|
|
92
|
+
"view",
|
|
93
|
+
run_id,
|
|
94
|
+
"--json",
|
|
95
|
+
"status,conclusion,jobs,url,workflowName,headBranch",
|
|
96
|
+
]
|
|
97
|
+
if repo:
|
|
98
|
+
args.extend(["--repo", repo])
|
|
85
99
|
try:
|
|
86
|
-
result = gh_run(
|
|
87
|
-
[
|
|
88
|
-
"run",
|
|
89
|
-
"view",
|
|
90
|
-
run_id,
|
|
91
|
-
"--json",
|
|
92
|
-
"status,conclusion,jobs,url,workflowName,headBranch",
|
|
93
|
-
]
|
|
94
|
-
)
|
|
100
|
+
result = gh_run(args)
|
|
95
101
|
return json.loads(result.stdout)
|
|
96
102
|
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
|
97
103
|
return None
|
|
98
104
|
|
|
99
105
|
|
|
100
|
-
def _resume_command(run_id: str, timeout: int) -> str:
|
|
106
|
+
def _resume_command(run_id: str, timeout: int, repo: str | None = None) -> str:
|
|
101
107
|
"""Format a copy-pasteable resume command for the user."""
|
|
108
|
+
repo_arg = f" --repo {repo}" if repo else ""
|
|
102
109
|
if timeout == 0:
|
|
103
|
-
return f"hyperi-ci watch {run_id} --timeout 0"
|
|
104
|
-
return f"hyperi-ci watch {run_id} --timeout {timeout}"
|
|
110
|
+
return f"hyperi-ci watch {run_id}{repo_arg} --timeout 0"
|
|
111
|
+
return f"hyperi-ci watch {run_id}{repo_arg} --timeout {timeout}"
|
|
105
112
|
|
|
106
113
|
|
|
107
114
|
def _print_summary(run_data: dict) -> None:
|
|
@@ -146,6 +153,7 @@ def watch_run(
|
|
|
146
153
|
run_id: str | None = None,
|
|
147
154
|
timeout: int = _DEFAULT_TIMEOUT,
|
|
148
155
|
interval: int = 30,
|
|
156
|
+
repo: str | None = None,
|
|
149
157
|
) -> int:
|
|
150
158
|
"""Watch a GitHub Actions run to completion.
|
|
151
159
|
|
|
@@ -155,6 +163,9 @@ def watch_run(
|
|
|
155
163
|
(poll until the run reaches a terminal state). Default is
|
|
156
164
|
sized for Tier 2 Rust builds (3600 s = 60 min).
|
|
157
165
|
interval: Base poll interval in seconds.
|
|
166
|
+
repo: Optional ``owner/name`` — when set, all gh calls target
|
|
167
|
+
this repo instead of the cwd's git remote. Use this when
|
|
168
|
+
watching a run in a different repo than your cwd.
|
|
158
169
|
|
|
159
170
|
Returns:
|
|
160
171
|
Exit code: 0=success, 1=failed/cancelled/unreachable, 2=timeout.
|
|
@@ -170,16 +181,17 @@ def watch_run(
|
|
|
170
181
|
return 1
|
|
171
182
|
|
|
172
183
|
info(f"Finding latest run on {branch}...")
|
|
173
|
-
latest = get_latest_run(branch=branch)
|
|
184
|
+
latest = get_latest_run(branch=branch, repo=repo)
|
|
174
185
|
if not latest:
|
|
175
186
|
error(f"No runs found on {branch}")
|
|
176
187
|
return 1
|
|
177
188
|
run_id = str(latest["databaseId"])
|
|
178
189
|
|
|
190
|
+
repo_label = f" in {repo}" if repo else ""
|
|
179
191
|
if timeout == 0:
|
|
180
|
-
info(f"Watching run {run_id} (no timeout)")
|
|
192
|
+
info(f"Watching run {run_id}{repo_label} (no timeout)")
|
|
181
193
|
else:
|
|
182
|
-
info(f"Watching run {run_id} (timeout: {timeout}s)")
|
|
194
|
+
info(f"Watching run {run_id}{repo_label} (timeout: {timeout}s)")
|
|
183
195
|
|
|
184
196
|
# `deadline = None` disables the timeout check entirely.
|
|
185
197
|
deadline: float | None = None if timeout == 0 else time.monotonic() + timeout
|
|
@@ -192,7 +204,7 @@ def watch_run(
|
|
|
192
204
|
now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
193
205
|
info(f" [{now}] polling (attempt {attempt})...")
|
|
194
206
|
|
|
195
|
-
run_data = _get_run_status(run_id)
|
|
207
|
+
run_data = _get_run_status(run_id, repo=repo)
|
|
196
208
|
if not run_data:
|
|
197
209
|
consecutive_failures += 1
|
|
198
210
|
if consecutive_failures >= _MAX_CONSECUTIVE_FETCH_FAILURES:
|
|
@@ -200,7 +212,7 @@ def watch_run(
|
|
|
200
212
|
f" Failed to fetch run status "
|
|
201
213
|
f"{consecutive_failures} times in a row — giving up. "
|
|
202
214
|
f"Last known status: {last_known_status}. "
|
|
203
|
-
f"Resume: {_resume_command(run_id, timeout)}"
|
|
215
|
+
f"Resume: {_resume_command(run_id, timeout, repo=repo)}"
|
|
204
216
|
)
|
|
205
217
|
return 1
|
|
206
218
|
warn(
|
|
@@ -232,7 +244,7 @@ def watch_run(
|
|
|
232
244
|
# in progress) or investigate (stuck / silently failing).
|
|
233
245
|
error(
|
|
234
246
|
f"Timeout after {timeout} seconds — run still {last_known_status}. "
|
|
235
|
-
f"Resume: {_resume_command(run_id, timeout)} "
|
|
247
|
+
f"Resume: {_resume_command(run_id, timeout, repo=repo)} "
|
|
236
248
|
f"(or use --timeout 0 to disable timeout)"
|
|
237
249
|
)
|
|
238
250
|
return 2
|
|
@@ -309,22 +309,25 @@ class TestChannelToBinaryFlow:
|
|
|
309
309
|
binary = tmp_path / "target" / "release" / "fixture-bin"
|
|
310
310
|
assert _nm_has_symbol(binary, "je_")
|
|
311
311
|
|
|
312
|
-
def
|
|
313
|
-
#
|
|
312
|
+
def test_spike_channel_default_uses_jemalloc(self, tmp_path) -> None:
|
|
313
|
+
# standards/rules/RUST.md "Allocator Policy" — DFE Rust binaries
|
|
314
|
+
# use jemalloc at EVERY channel for tooling consistency
|
|
315
|
+
# (jeprof works on every binary from day one). spike still gets
|
|
316
|
+
# thin LTO, but allocator is jemalloc.
|
|
314
317
|
_write_fixture_crate(
|
|
315
318
|
tmp_path,
|
|
316
|
-
with_jemalloc_feature=True,
|
|
319
|
+
with_jemalloc_feature=True,
|
|
317
320
|
wire_global_allocator=True,
|
|
318
321
|
)
|
|
319
322
|
|
|
320
323
|
profile = resolve_optimization_profile("spike", None)
|
|
321
|
-
assert profile.allocator == "
|
|
324
|
+
assert profile.allocator == "jemalloc"
|
|
322
325
|
|
|
323
326
|
result = _run_cargo_build(tmp_path, profile)
|
|
324
327
|
assert result.returncode == 0
|
|
325
328
|
|
|
326
329
|
binary = tmp_path / "target" / "release" / "fixture-bin"
|
|
327
|
-
assert
|
|
330
|
+
assert _nm_has_symbol(binary, "je_")
|
|
328
331
|
|
|
329
332
|
|
|
330
333
|
@pytest.mark.skipif(not CARGO_AVAILABLE, reason="cargo not installed")
|
|
@@ -55,6 +55,23 @@ class TestRenderTemplates:
|
|
|
55
55
|
content = _render_workflow("my-project", "rust-ci.yml")
|
|
56
56
|
assert "secrets: inherit" in content
|
|
57
57
|
|
|
58
|
+
def test_workflow_dispatch_accepts_tag_input(self) -> None:
|
|
59
|
+
# `hyperi-ci publish vX.Y.Z` calls `gh workflow run ci.yml -f
|
|
60
|
+
# tag=vX.Y.Z`. Without a `tag` input on workflow_dispatch the
|
|
61
|
+
# GitHub API returns 422 "Unexpected inputs provided" and
|
|
62
|
+
# publish silently fails. Every scaffolded ci.yml must accept
|
|
63
|
+
# the tag input AND forward it to the language workflow.
|
|
64
|
+
for workflow_file in ("python-ci.yml", "rust-ci.yml", "ts-ci.yml", "go-ci.yml"):
|
|
65
|
+
content = _render_workflow("my-project", workflow_file)
|
|
66
|
+
assert "workflow_dispatch:" in content
|
|
67
|
+
assert "inputs:" in content, (
|
|
68
|
+
f"{workflow_file}: missing workflow_dispatch inputs"
|
|
69
|
+
)
|
|
70
|
+
assert "tag:" in content, f"{workflow_file}: missing tag input"
|
|
71
|
+
assert "tag: ${{ inputs.tag" in content, (
|
|
72
|
+
f"{workflow_file}: tag input not forwarded to language workflow"
|
|
73
|
+
)
|
|
74
|
+
|
|
58
75
|
|
|
59
76
|
class TestDetectPythonBuildType:
|
|
60
77
|
"""Python build type detection from pyproject.toml."""
|
|
@@ -16,6 +16,7 @@ import pytest
|
|
|
16
16
|
from hyperi_ci.watch import (
|
|
17
17
|
_DEFAULT_TIMEOUT,
|
|
18
18
|
_MAX_CONSECUTIVE_FETCH_FAILURES,
|
|
19
|
+
_get_run_status,
|
|
19
20
|
_poll_interval,
|
|
20
21
|
_resume_command,
|
|
21
22
|
watch_run,
|
|
@@ -57,6 +58,41 @@ class TestResumeCommand:
|
|
|
57
58
|
def test_with_zero_timeout(self) -> None:
|
|
58
59
|
assert _resume_command("12345", 0) == "hyperi-ci watch 12345 --timeout 0"
|
|
59
60
|
|
|
61
|
+
def test_with_repo(self) -> None:
|
|
62
|
+
# When watching a run in a different repo than the cwd, the
|
|
63
|
+
# resume hint must include --repo so the user can copy-paste
|
|
64
|
+
# without re-deriving the repo from somewhere.
|
|
65
|
+
assert (
|
|
66
|
+
_resume_command("12345", 3600, repo="hyperi-io/dfe-loader")
|
|
67
|
+
== "hyperi-ci watch 12345 --repo hyperi-io/dfe-loader --timeout 3600"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def test_with_repo_and_zero_timeout(self) -> None:
|
|
71
|
+
assert (
|
|
72
|
+
_resume_command("12345", 0, repo="hyperi-io/dfe-loader")
|
|
73
|
+
== "hyperi-ci watch 12345 --repo hyperi-io/dfe-loader --timeout 0"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TestGetRunStatusRepo:
|
|
78
|
+
"""`_get_run_status` must forward --repo to gh when set, so watching
|
|
79
|
+
a run in a different repo than cwd doesn't 404 on every poll."""
|
|
80
|
+
|
|
81
|
+
def test_no_repo_omits_flag(self) -> None:
|
|
82
|
+
with patch("hyperi_ci.watch.gh_run") as mock_gh:
|
|
83
|
+
mock_gh.return_value.stdout = '{"status": "in_progress"}'
|
|
84
|
+
_get_run_status("12345")
|
|
85
|
+
args = mock_gh.call_args[0][0]
|
|
86
|
+
assert "--repo" not in args
|
|
87
|
+
|
|
88
|
+
def test_repo_set_appends_flag(self) -> None:
|
|
89
|
+
with patch("hyperi_ci.watch.gh_run") as mock_gh:
|
|
90
|
+
mock_gh.return_value.stdout = '{"status": "in_progress"}'
|
|
91
|
+
_get_run_status("12345", repo="hyperi-io/dfe-loader")
|
|
92
|
+
args = mock_gh.call_args[0][0]
|
|
93
|
+
assert "--repo" in args
|
|
94
|
+
assert args[args.index("--repo") + 1] == "hyperi-io/dfe-loader"
|
|
95
|
+
|
|
60
96
|
|
|
61
97
|
class TestDefaultTimeout:
|
|
62
98
|
"""Default timeout is sized for Tier 2 Rust builds."""
|
hyperi_ci-2.2.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.2.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|