Use deploy-deployer.yml when a repository has a Deployer setup.
jobs:
deploy:
uses: sympress/workflows/.github/workflows/deploy-deployer.yml@2ed4e3bca97fae7cda4d08b176199e2a47a9503e
with:
environment: production
deployment_directory: deployment
secrets:
GITHUB_USER_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
COMPOSER_AUTH_JSON: ${{ secrets.COMPOSER_AUTH_JSON }}
DEPLOY_HOSTNAME: ${{ secrets.DEPLOY_HOSTNAME }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}The project and deployment directory must each commit a valid composer.lock.
Their installs validate the lock and use --no-dev --no-scripts --no-plugins.
Private Composer authentication is available only during those fetch steps.
Private Git dependencies can use the separate read-only INSTALL_SSH_KEY and
verified INSTALL_SSH_KNOWN_HOSTS secrets. The production deployment key is loaded
after dependency installation and authorized builds, immediately before deployment.
Node installs require a selected npm, pnpm, or Yarn lockfile and disable lifecycle
scripts. Yarn 1 uses --frozen-lockfile --ignore-scripts; Yarn 2 uses --immutable --skip-builds; Yarn 3 and 4 use --immutable --mode=skip-build. Builds run in a
separate step without install tokens or an SSH agent. Any required Composer
post-install/plugin work must be explicitly moved to an authorized credential-free
build step before adopting this workflow.
The dependency artifact includes downloaded Composer archives and a sanitized Git object cache for source-only private packages. Authentication files, Git remotes, hooks and external object alternates are excluded. Restored mirrors have only a minimal bare-repository configuration, objects and refs. Composer reinstallation in the build step uses this cache with networking disabled; no install secret or SSH agent is available. A missing cached package fails the build instead of requesting credentials. The final release artifact excludes these download caches.
WireGuard is supported through the WIREGUARD_CONFIGURATION secret.
Deployments are bound to the requested GitHub environment and serialized per
environment. SSH_KNOWN_HOSTS must contain verified host keys; empty or malformed
files fail. Host verification is strict. The legacy allow_ssh_keyscan and
allow_unpinned_node_install inputs remain accepted for caller compatibility,
but cannot enable deployment host scans or unlocked installs.
Custom deploy_command values require allow_custom_deploy_command: true.
Keep the default command for normal deployments. A gated custom shell command
receives the validated environment and verbosity as positional $1 and $2.
Invalid inputs, any nonzero Deployer or tee status, cancellation, and missing or
nonnumeric result output fail the job. Cleanup runs even when deployment fails.
Outputs exposed to caller workflows:
| Output | Description |
|---|---|
deploy_exit_code |
Deployer process exit code. |
deploy_reason |
Bounded generic result without raw log content. |
deploy_warnings |
Retained compatibility output; empty. |
deploy_log_excerpt |
Retained compatibility output; empty. |
The workflow uses isolated fetch, audit, build, verification and deploy jobs. dependencies installs the project and
Deployer locks with scripts and plugins disabled and fetch-only credentials.
audit restores that immutable dependency artifact without running build code,
audits Composer and Node dependencies, and records lock hashes, the installed
production inventory and CycloneDX SBOM in a separate artifact.
build restores the dependency artifact and runs application build scripts without
any operator secrets or deployment environment. deploy checks out the exact
tested caller commit into a separate trusted source directory and installs its
deployment lock with scripts/plugins disabled and fetch-only authentication.
It verifies the recipe, regular Deployer proxy and dependency paths before loading
SSH or WireGuard credentials. Artifact files cannot overwrite this checkout.
The built archive is downloaded by its exact ID and verified before extraction
into a new directory outside the executable workspace. Secret files, authentication configuration,
VCS metadata and private caches are excluded. Internal relative symlinks retain
pnpm's dependency resolution; absolute or external links and links or hardlinks
to private files fail packaging. Restores use Python's data extraction filter.
Before signing or deployment, verify runs on a fresh runner and compares release
metadata with the independently uploaded audit artifact. A changed lockfile,
missing or different Composer package inventory, or altered SBOM stops the run.
Build-provided executables and PATH changes are never used for this verification.
The SBOM inventories audited dependency inputs; generated application code is
outside that statement. Non-npm JavaScript inventories retain an explicit limitation.
Deployment recipes run from the trusted checkout. Use the absolute
SYMPRESS_RELEASE_DIRECTORY environment variable as the upload source, for example
upload(getenv('SYMPRESS_RELEASE_DIRECTORY') . '/', '{{release_path}}').
Treat files in that directory as upload data: do not include PHP files from it,
load its Composer autoloader, or run its local commands with deployment credentials.
Remote application PHP execution remains part of the deployment recipe's scope.
For standalone Deployer usage, a recipe may fall back to its usual local source
when this environment variable is absent.
Modern Yarn uses a workspace-local cache so PnP dependencies survive the job
boundary. The dependency artifact replaces .yarnrc.yml with only public
nodeLinker, enableGlobalCache, yarnPath and cacheFolder settings. Yarn paths
must remain inside the workspace; registry and authentication settings are
excluded, and the original configuration is unchanged. The release artifact
omits this configuration and node_modules after the build.
The default SSH transport requires GITHUB_USER_SSH_KEY at runtime.
Set use_ssh: false only for Deployer recipes using local or non-SSH hosts.
The repository's local Deployer fixture exercises these jobs and
checks that build output reaches deployment while inert credential files do not.
Its build also tampers with the artifact recipe, verification script and both
Deployer binary paths. Deployment succeeds using freshly installed trusted tools
and trusted verification code that reads the built payload as data.
The build audits the locked production Composer dependencies and rejects high or
critical Node advisories before packaging. Every archive includes
sympress-sbom.cdx.json in CycloneDX 1.6 format, derived from installed Composer
packages. Installed development packages fail the build. npm lockfile v2/v3
production inputs are included; Yarn/pnpm JavaScript inventories are explicitly
marked incomplete in the SBOM. Review those inventories separately before a release.
The build exports the SHA-256 of the final archive. Deployment compares the downloaded bytes with this job output before opening the tar file. The checksum is not read from a file supplied by the archive.
For public production repositories, enable both provenance and SBOM attestations:
permissions:
contents: read
actions: read
attestations: write
id-token: write
jobs:
deploy:
uses: SymPress/workflows/.github/workflows/deploy-deployer.yml@REVIEWED_40_CHARACTER_COMMIT
with:
artifact_attestation: true
attestation_signer_digest: REVIEWED_40_CHARACTER_COMMITReplace both placeholders with the same reviewed workflow commit. A separate job
signs the archive's exact digest without deployment credentials. Deployment uses
gh attestation verify and requires the reusable signer workflow, its full commit,
the caller's source commit/ref, and a GitHub-hosted runner. Verification failure
stops extraction and deployment. No additional human approver is required by this
workflow. Consumers may opt out only when their hosting plan cannot provide
attestations; archive digest and audit gates still apply.
GitHub requires Enterprise Cloud for attestations of private repositories. This workflow does not publish private packages or grant them to public consumers. See GitHub's attestation action and the verification command.