standard-library / sdk-application
Package info
gitlab.com/php-standard-library/release-packages/sdk-application
Type:standard-library-sdk
pkg:composer/standard-library/sdk-application
Requires
- php: ^8.5.0
- ext-sodium: *
- standard-library/cryptography: ^1.0.30
- standard-library/sdk: ^1.0.30
- symfony/filesystem: ^v8.0.4
- symfony/process: ^v8.0.4
README
Build and deployment tooling for applications built on top of the PHP Standard Library: assembling
a deployable build directory, a Deployer-style (releases/shared/current) rsync-over-ssh deploy, and
a temporary SSH key installer for CI. Config parsing uses symfony/yaml, process/filesystem handling
uses symfony/process and symfony/filesystem; deploy itself shells out directly to the
rsync/ssh/ssh-keygen binaries.
{
"require-dev": {
"standard-library/sdk-application": "^1.0"
}
}
Commands
| Command | Purpose |
|---|---|
bin/sdk app:build | Assemble a deployable build directory (composer install --no-dev, plus optional build steps). |
bin/sdk deploy:run <environment> | Rsync the configured source directory to the environment's host/path. |
bin/sdk deploy:set-temporary-ssh-key <environment> | Write a temporary SSH key/known_hosts entry from environment variables. |
All three are bin/sdk commands (see standard-library/sdk's README) — run any of them in Docker
with --docker (e.g. bin/sdk deploy:run production --docker); the deploy commands additionally
mount the host's ~/.ssh into the container, so a key written by deploy:set-temporary-ssh-key is
still there for a later deploy:run run.
Each command reads config-dev.yaml under the section keyed by its own fully qualified class name
(see standard-library/sdk's Console\Command base class). deploy:run and
deploy:set-temporary-ssh-key need the same per-environment data, so define it once via a YAML
anchor and merge it into both sections.
config-dev.yaml
'StandardLibrary\Sdk\Application\Console\Command\BuildCommand':
output: artifacts/build
dirs:
- src
- public
files:
- composer.json
- composer.lock
exclude:
- vendor/bin
steps:
- App\Build\FrontendBuildStep
.deploy: &deploy
production:
host: example.com
user: deploy
port: 22
path: /var/www/example
source: artifacts/build
exclude:
- .env.local
writable-dirs:
- var/cache
shared-dirs:
- var/uploads
shared-files:
- .env
keep-releases: 5
key:
host: "example.com ssh-ed25519 AAAA..."
public: "ssh-ed25519 AAAA... deploy@ci"
'StandardLibrary\Sdk\Application\Console\Command\DeployCommand':
<<: *deploy
'StandardLibrary\Sdk\Application\Console\Command\DeploySetTemporarySshKeyCommand':
<<: *deploy
build
outputis the directory (relative to the project root) the finished build is moved to; defaults toartifacts/build— matchingdeploy.<environment>.sourceabove.dirs/filesare optional. If both are empty,bin/sdk app:buildgit clones the project's own.gitinto the build directory (i.e. exactly the committed tree). If either is set, only the listed directories/files are extracted viagit archive, so uncommitted or gitignored files can never end up in the build even when only part of the project is selected.excludeis an optional list of paths (relative to the build directory) removed as the last step before the result is moved tooutput— aftercomposer installand allstepshave run, so it can also strip things they produced (e.g. dev tooling left behind undervendor/bin), not just files that were already there when the project was copied.stepsis an optional list of class names implementingStandardLibrary\Sdk\Application\Build\Step\BuildStep, run in order after the composer install. This is the extension point for things this package deliberately doesn't ship — frontend asset compilation, CMS-specific setup, etc. — supplied by another package on the classpath. Each step is instantiated with no constructor arguments and receives aBuildContext(project root, build directory, logger).
bin/sdk app:build
- Copies the project into a temporary build directory (see
dirs/filesabove). - Writes a
REVISIONfile containing the project's currentgit rev-parse HEAD. - Runs
composer install --no-dev --no-interactionin it. - Runs each configured
stepsentry. - Removes each configured
excludepath. - Atomically moves the result to
output(removing any previous build there first).
deploy
host,user,path,sourceare required.portdefaults to22.sourceis the local directory (relative to the project root) that gets rsynced;.gitis always excluded in addition to anyexcludeentries.pathis the deployment root, not the web-served directory — see the release layout below.writable-dirsarechmod -R a+w'd inside the new release after the sync.shared-dirs/shared-filespersist across deploys (uploads, logs,.env, ...) — see below.keep-releasesis how many past releases to retain; defaults to5.key.host/key.publicare only used bydeploy:set-temporary-ssh-key.
The <environment> argument tab-completes (via bin/sdk's shell completion — see standard-library/sdk's
README) to whatever environment keys are configured in each command's own section above.
bin/sdk deploy:run <environment>
Deployer-style releases/shared/current layout under path, not a direct in-place sync:
path/
releases/
20260801120000/ - one full rsync per deploy, named by timestamp
...
shared/ - persists across every deploy
current -> releases/20260801120000
Each run:
- Creates a new
path/releases/<timestamp>/directory and rsyncssourceinto it (rsync -avz --deleteover the givenport;.gitandexcludeentries are always skipped). - For each
shared-dirs/shared-filesentry: ensures it exists underpath/shared/(bootstrapping it from the freshly synced release's own copy on the very first deploy, if present), removes the copy that just got synced into the new release, and symlinks it frompath/shared/instead — so uploads, logs, or a server-side.envsurvive every subsequent deploy untouched. - Applies
writable-dirsinside the new release. - Atomically flips
path/currentto point at the new release (ln -sfn). - Prunes old releases beyond
keep-releases.
Because each deploy syncs into a brand-new directory and only swaps current at the very end, the
live site (whatever serves from path/current) is never left mid-sync — unlike a direct in-place
rsync.
bin/sdk deploy:set-temporary-ssh-key <environment>
Writes ~/.ssh/known_hosts (from key.host), ~/.ssh/id_rsa.pub (from key.public), and
~/.ssh/id_rsa (from the KEY_<ENVIRONMENT> environment variable, e.g. KEY_PRODUCTION) if they
don't already exist. If a PASSPHRASE_<ENVIRONMENT> environment variable is set, it re-encrypts the
private key with that passphrase via ssh-keygen.