citomni/installer
最新稳定版本:v1.0.0.0
Composer 安装命令:
composer require citomni/installer
包简介
Deterministic scaffold materializer and lifecycle tool for CitOmni applications.
关键字:
README 文档
README
Official scaffold lifecycle tool for CitOmni applications.
citomni/installer is the package-owned scaffold materializer for the CitOmni ecosystem. It discovers scaffold manifests from installed Composer packages, renders package-owned scaffold files, writes the few files that must physically exist in the application layer, and tracks their state so future repairs and scaffold updates can be handled safely.
The package is deliberately narrow. It is not a framework runtime, not a web updater, not a Composer replacement, not a historical restore tool, and not a merge engine with a heroic cape and a questionable threat model.
In practical terms, citomni/installer lets CitOmni keep citomni/app-skeleton neutral and almost empty while allowing packages such as citomni/http and citomni/cli to own the runtime-near entrypoints and scaffold files that belong to them.
Highlights
- Official scaffold lifecycle tool for CitOmni with explicit ownership of scaffold discovery, rendering, planning, status, repair, sync, and state.
- Composer-native model where scaffold files come from the currently installed package versions, not from live GitHub fetches or mutable remote templates.
- Neutral app skeleton support so
citomni/app-skeletoncan remain a thin application container instead of a pile of mode-specific runtime files. - Conservative write behavior with no blind overwrites,
.newfiles for blocked upstream updates, confirmation before plain forced replacement, and backups before overwriting existing files. - Stateful managed-file sync using both raw stub checksums and rendered-file checksums to separate upstream scaffold drift from local changes.
- Bootstrap-independent CLI through
vendor/bin/citomni-installer, without requiringcitomni/cli,citomni/http, or a running CitOmni application. - Stable JSON output for project tooling, CI, deploy hooks, and other automation that needs machine-readable scaffold status.
What this package is
citomni/installer is the scaffold materialization and lifecycle package for CitOmni.
It handles the few package-owned files that cannot stay in /vendor/ because they must exist in the application tree to function. Typical examples are an HTTP front controller such as public/index.php or a CLI entrypoint such as bin/citomni.
The package works from installed Composer packages. A package that owns scaffold declares a manifest, and citomni/installer uses that manifest to plan and apply safe changes in the application layer.
This keeps the ownership model mechanical and easy to reason about:
- Runtime packages own their scaffold stubs.
- The application owns its local code, root files, and configuration after project creation.
- Composer owns dependency resolution and installed package versions.
citomni/installerowns the mechanics that materialize package-owned scaffold into the app.
No smoke, no mirrors, and preferably no wizard hat in production.
What this package provides
Scaffold discovery
- Discovery of installed Composer packages.
- Support for scaffold manifests declared through
extra.citomni.scaffold. - Convention fallback to
install/manifest.phpwhen no explicit Composer extra value exists. - Manifest validation, package-name validation, and manifest version validation.
- Deterministic path normalization and guard checks before any write operation.
Scaffold materialization
- Creation of missing scaffold files from package-owned stubs.
- Placeholder rendering using deterministic placeholder sources.
- Registration of rendered files in app-local installer state.
- Conservative handling of existing unknown files.
- Targeted package filtering through
--package=vendor/package.
Scaffold lifecycle commands
doctorfor read-only environment validation.statusfor read-only scaffold status.installfor first-time package scaffold materialization.repairfor recreating missing scaffold files from the currently installed package version.syncfor controlled updates of managed scaffold files.
Post-MVP may add diff for comparing local files against the current rendered upstream version.
Integration output
- Human-readable CLI output by default.
- JSON output for project tooling, CI, deployment tooling, and automation.
- Stable machine-readable status and reason values for scaffold drift, missing files, local changes, and blocked sync.
What this package owns
citomni/installer owns the lifecycle mechanics around package-owned scaffold.
That includes:
- The
vendor/bin/citomni-installerCLI entrypoint. - Discovery of scaffold manifests from installed Composer packages.
- Validation of scaffold manifests, source paths, target paths, and supported manifest versions.
- Placeholder resolution and simple placeholder rendering.
- Plan-first scaffold decisions.
- Safe write behavior for scaffold targets,
.newfiles, backups, and state. - App-local scaffold state format.
- Checksum semantics for raw stubs and rendered files.
- Read-only status and doctor diagnostics.
- JSON output intended for project tooling and other automation.
This package owns the mechanism, not the application. That distinction is the whole point.
What this package does not own
citomni/installer is intentionally not a general application generator or updater.
It does not own:
- HTTP runtime behavior.
- CLI runtime behavior.
- The contents of HTTP scaffold stubs.
- The contents of CLI scaffold stubs.
- Application business code.
- Application root-level files after
composer create-project. composer.jsonorcomposer.lockmutation.- Composer dependency resolution.
- Composer
requireorupdateexecution. - GitHub repository creation.
- Deployment profiles.
- Secrets management.
- Historical restoration of previous scaffold bytes.
- Automatic merging of local modifications.
- HTTP write access to the codebase.
Those responsibilities belong elsewhere. The installer stays small because that is where most of its safety comes from.
Relationship to other CitOmni packages
CitOmni separates project creation, dependency management, runtime packages, scaffold materialization, and project tooling into distinct layers.
Within that model:
citomni/app-skeletonprovides the neutral application container.- Composer installs and updates packages.
- Runtime packages such as
citomni/httpandcitomni/cliown their own scaffold stubs. citomni/installermaterializes package-owned scaffold into the application layer.- External project tooling may orchestrate richer workflows around project creation, repository setup, secrets, deployment, and registration.
A minimal HTTP application can therefore start from a neutral skeleton, require citomni/http, and then materialize only the HTTP-owned files needed by that mode.
A minimal CLI application can do the same with citomni/cli without dragging a public/ directory around like ceremonial luggage.
Scaffold ownership model
Package-owned scaffold should live in the package that owns the runtime function.
Examples:
vendor/citomni/http/install/manifest.php
vendor/citomni/http/install/scaffold/public/index.php.stub
vendor/citomni/http/install/scaffold/config/citomni_http_cfg.php.stub
vendor/citomni/http/install/scaffold/config/citomni_http_routes.php.stub
vendor/citomni/cli/install/manifest.php
vendor/citomni/cli/install/scaffold/bin/citomni.stub
vendor/citomni/cli/install/scaffold/config/citomni_cli_cfg.php.stub
Those stubs are the package-owned reference versions. The materialized files in the app are app-local runtime artifacts derived from those stubs.
Examples:
public/index.php
bin/citomni
config/citomni_http_cfg.php
config/citomni_cli_cfg.php
This duplication is intentional. The files have different lifecycles:
- The stub in
/vendor/follows the installed package version. - The materialized file in the app may be present, missing, clean, locally modified, or ready for sync.
Composer remains the source of truth for which upstream scaffold version is available.
Application skeleton model
citomni/app-skeleton should stay neutral and mode-free.
It may provide the application container and base structure, such as:
composer.json
.gitattributes
.gitignore
.htaccess
LICENSE
NOTICE
README.md
TRADEMARKS.md
bin/
config/
language/
src/
tests/
var/
It should not need to include complete HTTP or CLI runtime entrypoints as permanent root-owned files.
Examples:
public/index.phpcomes fromcitomni/http.bin/citomnicomes fromcitomni/cli.config/citomni_installer.phpmay be added by the app to provide versioned placeholder configuration when scaffold placeholders are needed.
After composer create-project, root-level files are app-owned. citomni/installer must not rewrite the app README, .gitignore, composer.json, or composer.lock during normal operation.
Requirements
- PHP 8.2+
- Composer autoloading
ext-jsonfor JSON output
citomni/installer is intentionally independent of citomni/cli and citomni/http. It must be able to run before either runtime package has been installed in the application.
citomni/kernel is not required for the installer CLI bootstrap unless a future implementation explicitly chooses that dependency.
Installation
In a new CitOmni application, citomni/installer is normally installed through citomni/app-skeleton.
composer create-project citomni/app-skeleton my-app
cd my-app
For an existing application, install the package explicitly:
composer require citomni/installer composer dump-autoload -o
The package provides its own CLI entrypoint:
vendor/bin/citomni-installer
No CitOmni provider registration is required for MVP usage. The installer is a CLI lifecycle tool, not a request-time runtime dependency.
Typical workflows
Add HTTP support
composer require citomni/http vendor/bin/citomni-installer install --package=citomni/http
If the installed scaffold uses placeholders such as {{ CITOMNI_ENVIRONMENT }}, provide them either through config/citomni_installer.php or through repeated --placeholder=KEY=VALUE options before running install.
vendor/bin/citomni-installer install --package=citomni/http --placeholder=CITOMNI_ENVIRONMENT=dev
Add CLI support
composer require citomni/cli vendor/bin/citomni-installer install --package=citomni/cli
Check scaffold status
vendor/bin/citomni-installer status vendor/bin/citomni-installer status --package=citomni/http vendor/bin/citomni-installer status --format=json
Update framework packages and sync scaffold
composer update citomni/http citomni/installer vendor/bin/citomni-installer status --package=citomni/http vendor/bin/citomni-installer sync --package=citomni/http
Repair missing files
vendor/bin/citomni-installer repair --package=citomni/http
repair is not historical restore. It recreates missing scaffold files from the currently installed package version.
Composer scripts
Applications may expose shorter commands through root-level Composer scripts.
Example:
{
"scripts": {
"citomni:status": "vendor/bin/citomni-installer status",
"citomni:install": "vendor/bin/citomni-installer install",
"citomni:sync": "vendor/bin/citomni-installer sync",
"citomni:repair": "vendor/bin/citomni-installer repair",
"citomni:doctor": "vendor/bin/citomni-installer doctor"
}
}
Usage:
composer citomni:status composer citomni:sync
These scripts belong to the application. citomni/installer must not rewrite composer.json to add or modify them.
Avoid wiring write-capable commands such as sync into automatic Composer hooks. Read-only hints are friendly. Surprise file mutation after dependency update is how a tool earns side-eye.
Commands
doctor
Runs read-only validation of the application environment.
Typical checks include:
- Application root detection.
- Presence of
vendor/. - Readability of Composer metadata.
- Discovery of scaffold manifests.
- Write access diagnostics for relevant app paths.
- State readability and format validation where relevant.
Examples:
vendor/bin/citomni-installer doctor vendor/bin/citomni-installer doctor --format=json
status
Shows scaffold status without changing files.
It reports whether known scaffold targets are missing, clean, locally modified, affected by upstream stub changes, affected by placeholder changes, or blocked from automatic sync.
Examples:
vendor/bin/citomni-installer status vendor/bin/citomni-installer status --package=citomni/http vendor/bin/citomni-installer status --format=json
install
Materializes scaffold for a newly installed package or runtime mode.
Default behavior:
- Creates missing files.
- Does not overwrite existing files.
- Registers created files in installer state.
- Stores checksum and placeholder metadata where relevant.
- Fails clearly on conflicts.
Example:
vendor/bin/citomni-installer install --package=citomni/http
Forced replacement:
vendor/bin/citomni-installer install --package=citomni/http --force vendor/bin/citomni-installer install --package=citomni/http --force=yes
--force allows existing scaffold targets to be overwritten, but asks for interactive confirmation before writing. --force=yes confirms the overwrite without prompting and is intended for scripts or explicitly confirmed manual runs. Forced replacements create backups before writing.
repair
Recreates missing scaffold files from the currently installed package stubs.
Default behavior:
- Writes only files that are missing.
- Does not overwrite existing files.
- Uses previously registered placeholder values when state exists.
- Updates the baseline for files it recreates.
- Reports
recreated_stub_driftwhen the recorded stub checksum differs from the currently installed package stub.
Example:
vendor/bin/citomni-installer repair --package=citomni/cli
sync
Updates managed scaffold files safely.
Default behavior:
- Creates missing scaffold files, including missing
managedandcreate-onlytargets. - Updates files that still match the previous rendered baseline.
- Does not overwrite locally modified files.
- Writes the new upstream version to
.newwhen a local modification blocks automatic sync. - Does not update existing
create-onlyfiles by default; forced replacement requires--forceor--force=yes.
Example:
vendor/bin/citomni-installer sync --package=citomni/http
Forced sync:
vendor/bin/citomni-installer sync public/index.php --package=citomni/http --force vendor/bin/citomni-installer sync public/index.php --package=citomni/http --force=yes
--force replaces the existing target after interactive confirmation and writes a backup first. --force=yes performs the same forced replacement without prompting. A positional target limits the run to that app-relative file; without a target, the command applies to the selected package scope.
Conflict example:
public/index.php changed locally and was not overwritten.
New upstream version written to public/index.php.new
Review public/index.php and public/index.php.new before merging manually.
Post-MVP may add a diff command for comparing local app files against the current rendered upstream scaffold.
Package manifest convention
Packages that provide scaffold should declare a scaffold manifest.
Composer extra is the primary discovery mechanism:
{
"extra": {
"citomni": {
"scaffold": "install/manifest.php"
}
}
}
If no explicit value exists, the installer may fall back to:
install/manifest.php
If both exist and point to different paths, the Composer extra value wins.
The manifest's scaffold source base is normally install/scaffold/.
Example manifest:
<?php declare(strict_types=1); return [ 'package' => 'citomni/http', 'version' => 1, 'files' => [ [ 'target' => 'public/index.php', 'source' => 'install/scaffold/public/index.php.stub', 'type' => 'entrypoint', 'policy' => 'managed', ], [ 'target' => 'config/citomni_http_cfg.php', 'source' => 'install/scaffold/config/citomni_http_cfg.php.stub', 'type' => 'config', 'policy' => 'create-only', ], [ 'target' => 'config/citomni_http_routes.php', 'source' => 'install/scaffold/config/citomni_http_routes.php.stub', 'type' => 'routes', 'policy' => 'create-only', ], ], ];
version is the manifest schema version, not the package semver.
Manifest fields
target
Relative path in the application where the scaffold file should be materialized.
Targets must be safe relative app paths. Absolute paths, parent traversal, invalid separators, and paths outside the application root must be rejected.
source
Relative path inside the package root pointing to the scaffold stub.
Sources must stay inside the package root. The installer must not follow a manifest into writing or reading arbitrary system files. Manifests are configuration, not a treasure map.
type
Diagnostic metadata describing what the file is.
Examples:
entrypointconfigroutesserver-config
type may be used for output, grouping, logging, JSON responses, and future read-only UI.
policy
The write behavior contract.
Supported MVP policies:
managedcreate-only
Write behavior is controlled by policy, not by type.
File policies
managed
Used for small framework-near files where the package owner may continue to ship updates.
Examples:
public/index.phpbin/citomni
Rules:
- May be updated automatically when the local file still matches the previous rendered baseline.
- Must not be overwritten when locally modified.
- Should store both
stub_checksumandrendered_checksumin state. - Should produce
.newwhen an upstream update is available but local changes block automatic sync.
create-only
Used for files that should normally be created once and then left to the application.
Examples:
config/citomni_http_cfg.phpconfig/citomni_http_routes.phpconfig/citomni_cli_cfg.php
Rules:
- Create if missing.
- Register in state.
- Do not update automatically.
- Do not treat upstream drift as an automatic update signal.
- Require explicit force or manual action for replacement.
Placeholder rendering
Scaffold stubs may use simple placeholders.
Examples:
{{APP_NAMESPACE}}
{{ APP_NAME }}
{{CITOMNI_ENVIRONMENT}}
{{ CITOMNI_ENVIRONMENT }}
Whitespace immediately inside the braces is ignored, but placeholder names themselves must still use uppercase keys matching [A-Z][A-Z0-9_]*.
MVP placeholder rules:
- Keep placeholders few and explicit.
- Do not use a general template engine.
- Do not use
eval. - Do not allow arbitrary PHP execution.
- Fail on unknown placeholders.
- Resolve placeholders from deterministic sources.
- Store the actual placeholder snapshot used for each rendered file in installer state.
When scaffold placeholders are needed, the application-level placeholder config should live here:
config/citomni_installer.php
Example:
<?php declare(strict_types=1); return [ 'placeholders' => [ 'APP_NAMESPACE' => 'App', 'APP_NAME' => 'My App', 'CITOMNI_ENVIRONMENT' => 'dev', ], ];
MVP placeholder priority:
- CLI options.
config/citomni_installer.php.
Future versions may add Composer metadata, existing CitOmni config, or explicit package defaults.
Example:
vendor/bin/citomni-installer install --package=citomni/http --placeholder=APP_NAMESPACE=App
APP_NAMESPACE should be explicit when Composer autoload configuration is ambiguous.
State and checksums
Installer state is stored in the application, not in the installer package, runtime package, or /vendor/.
Recommended location:
var/state/citomni/installer-scaffold.php
The state file stores enough information to determine whether a managed file is clean, locally modified, affected by upstream stub changes, or affected by placeholder changes.
For managed files, the installer uses two checksum concepts.
stub_checksum
Checksum of the raw scaffold stub before placeholder rendering.
Purpose:
- Identifies the upstream stub.
- Detects package-owned scaffold changes.
- Stays independent of application placeholder values.
rendered_checksum
Checksum of the rendered file that was written to the application.
Purpose:
- Detects local modifications.
- Allows safe automatic updates when the local file still matches the previous rendered baseline.
Package semver is useful metadata, but it must not drive sync decisions alone. A package can change without scaffold drift, and a dev checkout can change scaffold without a meaningful semver change. Bytes beat vibes.
Write safety model
Default behavior is conservative.
| Situation | Default behavior |
|---|---|
| Target is missing | Create file |
| Target exists and is unknown | Stop or write .new |
Target matches previous rendered_checksum |
Update if policy is managed |
| Target is locally modified | Do not overwrite |
Target is create-only and exists |
Do not touch unless explicitly forced |
| Target is outside app root | Fail |
| Source is missing in package | Fail |
| Manifest is invalid | Fail |
| State is unknown or unsafe | Fail without writing |
Plain --force asks for confirmation before destructive writes. --force=yes confirms the same destructive write without prompting. Both modes create backups before replacing existing files.
When forced replacement overwrites a file, the installer must create a backup first.
Recommended backup location:
var/backups/citomni-installer/<utc-timestamp>/path/to/file
Writes should be atomic where practical. Scaffold files, .new files, backups, and state should be written through a temporary file in the same directory and then renamed into place.
Exit codes
CLI exit codes should be stable so project tooling, CI, and deploy scripts can make deterministic decisions.
Exit codes:
| Code | Meaning |
|---|---|
0 |
OK, no problems |
1 |
General error |
2 |
Invalid usage or invalid arguments |
3 |
Drift found, no conflict |
4 |
Conflict or local changes requiring manual action |
5 |
Unsafe state or migration required |
6 |
IO or permission error |
Examples:
statusmay return3when an update is available but no local conflict exists.statusmay return4when local changes block sync.syncmay return4when it writes.newbecause a local file was modified.doctormay return5when the state format is unknown.
JSON output
All MVP commands support JSON output.
Example:
vendor/bin/citomni-installer status --format=json
JSON output should be stable enough for project tooling and automation. It includes package name, type, policy, target, status, reason, and action values that tooling can use to infer whether a file can be synced without conflict.
Interactive confirmation is not available in JSON mode. When a forced overwrite is required, automation must use --force=yes; plain --force --format=json returns an invalid-usage response instead of prompting.
Status, reason, and error values should distinguish at least:
- Missing target.
- Local modification.
- Upstream stub drift.
- Placeholder drift.
- Invalid manifest, usually as a top-level command error or
doctorcheck. - Unsafe state, usually as a top-level command error or
doctorcheck. - Permission issue, usually as a top-level command error or
doctorcheck.
Human output can be friendly. JSON output should be boring. Boring is a feature when another program has to parse it.
Tooling integration
External project tooling can use citomni/installer as the scaffold engine in a larger app-creation workflow.
A typical project-tooling flow may look like this:
Create application workspace
composer create-project citomni/app-skeleton <path>
composer require citomni/http and/or citomni/cli
vendor/bin/citomni-installer install --package=citomni/http
vendor/bin/citomni-installer install --package=citomni/cli
Create project repository
Push initial commit
Register the application in external tooling
Create deployment and secret placeholders
Project tooling should not need to know the internal contents of public/index.php, bin/citomni, HTTP config stubs, or CLI config stubs. It should ask the installer for status or ask the installer to materialize the package-owned scaffold.
Legacy applications
Legacy migration is not an MVP responsibility, but the state model should allow a future adopt command.
A possible future flow:
composer require citomni/installer vendor/bin/citomni-installer doctor vendor/bin/citomni-installer status vendor/bin/citomni-installer adopt
Important principles for legacy adoption:
- Do not assume an existing file is framework-owned.
- Render stubs before comparing templated files.
- Register clean matches differently from local modifications.
- Do not compare raw stub bytes directly against rendered app files.
Existing applications deserve caution. They may contain history, intentional edits, and the occasional fossil from a heroic Friday deploy.
HTTP UI
HTTP UI is not part of the MVP.
A future UI may be read-only and show status, diffs, and recommended commands. It should not write to the codebase from a web request, run Composer update, mutate /vendor/, or act as a web updater.
If remote write behavior is ever needed, it should be handled through an out-of-band runner, deploy hook, cron job, or explicit CLI process rather than direct HTTP request execution.
Runtime and architectural model
citomni/installer follows CitOmni's architectural discipline without requiring a full CitOmni runtime.
Recommended internal structure:
- CLI classes own argument parsing and output.
- Operations own the decision graph and return arrays.
- State classes own reading and writing installer state.
- Support classes may handle IO-oriented helper behavior.
- Utilities remain pure and side-effect free.
There is no repository layer in MVP because there is no SQL.
There is no HTTP controller in MVP because HTTP UI is not part of the first version.
There is no service-map singleton dependency in MVP because the installer must work before a CitOmni app runtime is bootable.
Performance notes
- The installer is not loaded in the HTTP request boot pipeline.
- The installer should keep its dependency footprint small.
- Scaffold discovery and status checks should be deterministic and file-based.
- Runtime packages should not need the installer during normal request handling.
- Production applications should still use optimized Composer autoloading and OPcache for runtime code.
Composer example:
{
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true,
"apcu-autoloader": true
}
}
Then run:
composer dump-autoload -o
Error handling philosophy
Fail fast.
citomni/installer should reject unsafe paths, unknown manifest versions, invalid state, missing sources, unknown placeholders, and ambiguous writes before touching the application tree.
When a file is locally modified, the correct default is not to be clever. The correct default is to stop, write .new where appropriate, and make the human decision obvious.
Silent fallback behavior feels nice until it quietly edits the wrong file. That is not the kind of magic CitOmni is trying to collect.
Contributing
- PHP 8.2+
- PSR-4
- Tabs for indentation
- K&R brace style
- Keep ownership boundaries sharp
- Keep write behavior conservative
- Keep installer independent of
citomni/cliandcitomni/http - Validate paths before IO
- Build plans before writing files
- Prefer explicit failure over hidden fallback behavior
- Do not introduce HTTP write paths in MVP
Coding & Documentation Conventions
All CitOmni projects follow the shared conventions documented here: CitOmni Coding & Documentation Conventions
License
CitOmni Installer is open-source under the MIT License. See LICENSE.
Trademark notice: "CitOmni" and the CitOmni logo are trademarks of Lars Grove Mortensen. Usage of the name or logo must follow the policy in NOTICE. Do not imply endorsement or affiliation without prior written permission.
Trademarks
"CitOmni" and the CitOmni logo are trademarks of Lars Grove Mortensen. You may make factual references to "CitOmni", but do not modify the marks, create confusingly similar logos, or imply sponsorship, endorsement, or affiliation without prior written permission. Do not register or use "citomni" or confusingly similar terms in company names, domains, social handles, or top-level vendor/package names. For details, see NOTICE.
Author
Developed by Lars Grove Mortensen (c) 2012-present.
CitOmni - low overhead, high performance, ready for anything.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-10