Calm Deployment with Vyriy Scripts
Deployment is part of architecture.
Not only because deployment moves code into an environment, but because deployment defines how a system changes.
A deployment flow can be calm and explicit:
- Build or publish required artifacts.
- Deploy infrastructure and application resources.
- Smoke-test the deployed runtime from the lowest useful level upward.
- Run post-deploy hooks that prepare the environment for real testing or use.
Or it can be chaotic:
- one large script that does everything
- hidden retries in random places
- unclear failure messages
- duplicated shell logic across projects
- aggressive polling against remote systems
- public URL checks without lower-level runtime validation
- too much concurrency by accident
@vyriy/scripts exists to support the first style.
It is a small, composable deployment and verification layer for project automation.
It is not intended to be a full deployment framework.
The goal is lower cognitive load: each script should say what operational boundary it verifies or changes.
A script layer, not a deployment framework
@vyriy/scripts is built around script factories.
Each factory focuses on one concrete operational task:
- deploy infrastructure
- publish a Docker image
- invoke a Lambda smoke check
- call a healthcheck endpoint
- resolve an API URL for custom checks
- verify a static MFE distribution
- verify a static website through its sitemap
- run post-deploy webhooks
The shared process behavior belongs to @vyriy/script.
That wrapper is responsible for process-level concerns:
- logging
- timeout handling
- predictable process execution
- clear failure reporting
This separation matters.
A deployment script should not become a hidden pipeline engine.
It should do one operational thing and report clearly when that thing fails.
The larger pipeline can then compose these scripts differently for different project types:
- libraries
- static websites
- micro-frontends
- APIs
- services
- Fargate workloads
- serverless applications
That is calm architecture at the automation level.
Deployment as a sequence of small steps
A good deployment flow should be readable as a sequence.
For a service or API project, the shape may be:
- Publish a Docker image when the service uses Fargate.
- Deploy infrastructure and application resources.
- Smoke-test the runtime directly.
- Smoke-test the HTTP boundary.
- Run project-specific API checks.
- Run post-deploy hooks.
For a static website or MFE project, the shape may be:
- Deploy infrastructure and static assets.
- Verify static delivery.
- Run post-deploy hooks only when the environment needs preparation.
The important point is not that every project has exactly the same flow.
The important point is that every step has a clear reason to exist.
A calm deployment should answer:
- What did we publish?
- What did we deploy?
- What runtime boundary did we verify?
- What public boundary did we verify?
- What environment preparation happened after deploy?
- Where exactly did the flow fail?
deploy: AWS CDK deployment
deploy is the infrastructure deployment script.
It runs AWS CDK in CI-oriented mode:
- synth
- diff
- deploy
It also writes CDK output data to:
cdk.out/cdk-outputs.json
Other scripts can read this file through the shared CDK output helper.
That makes deployment outputs reusable without copy-pasting values between scripts.
Use deploy when a project deploys CloudFormation resources through AWS CDK.
Conceptually, it owns one responsibility:
Turn the desired infrastructure state into deployed AWS resources and expose deployment outputs for later steps.
It should not also perform every smoke-test, warm every cache, or run every custom preparation step.
Those belong to later scripts.
That separation keeps the flow readable.
docker and kaniko: image publishing
Some projects need container images before infrastructure deployment.
For example, a service or API may run through AWS Fargate tasks. In that case, the image needs to be published to ECR before the task definition points to it.
@vyriy/scripts supports two image publishing paths:
dockerkaniko
docker uses Docker Buildx.
It is useful when the CI environment can run Docker and image builds are allowed directly in the runner.
kaniko uses the Kaniko executor.
It is useful in container-native CI environments where Docker-in-Docker is not desirable.
Both scripts are deployment-adjacent.
They do not deploy the infrastructure themselves. They prepare artifacts that deployment can use.
That distinction matters.
Publishing an image and deploying infrastructure are different operational boundaries.
A calm flow keeps them separate.
Smoke testing from the lowest useful level upward
API verification should start as close to the runtime as possible and then move outward.
A typical API smoke order can look like this:
import { api, healthcheck, lambda } from '@vyriy/scripts';
await lambda('my-function');
await healthcheck();
await api(async (url) => {
await fetch(`${url}healthcheck`);
await fetch(`${url}content/preview`);
});
Each step verifies a different boundary.
lambda: direct runtime smoke testing
lambda invokes a Lambda function directly with the shared smoke payload.
This is the first useful check when an API is backed by Lambda.
It proves that the function:
- exists
- can be invoked
- can load its dependencies
- can run inside the deployed runtime
- returns the expected smoke response
This check is intentionally lower than HTTP.
If direct Lambda invocation fails, the problem is probably inside the function package, runtime configuration, permissions, environment variables, or dependencies.
That is valuable information.
A public HTTP check would only tell us that the API does not work.
A direct runtime check tells us that the runtime itself does not work.
healthcheck: standard HTTP boundary
healthcheck calls the standard API Gateway healthcheck endpoint.
This proves a different boundary:
- API Gateway is deployed
- the route is reachable
- the integration works
- the basic API wrapper responds
- the deployed HTTP layer can reach the runtime
This should usually run after lambda.
If lambda passes but healthcheck fails, the function is alive, but the HTTP boundary may be broken.
That narrows the problem.
The goal of calm smoke testing is not only to detect failure.
It is to make failure easier to understand.
api: custom API smoke scenarios
api resolves the API Gateway base URL and passes it to a callback.
Use it for project-specific API smoke scenarios:
await api(async (url) => {
await fetch(`${url}healthcheck`);
await fetch(`${url}content/preview`);
await fetch(`${url}profile/demo`);
});
This script is intentionally flexible.
The shared layer resolves the deployed base URL. The project decides what behavior matters.
Use api when a project needs broader checks than a single healthcheck:
- several endpoints
- domain-specific behavior
- preview content
- public API flows
- lightweight integration checks
This keeps the framework small while still giving projects room to define meaningful confidence checks.
Static smoke testing
Static delivery has different boundaries from APIs.
@vyriy/scripts separates two common static verification cases:
mfesite
mfe: static micro-frontend distribution
mfe checks a static micro-frontend distribution.
It verifies the distribution URL and key files such as:
index.htmlindex.js
Use mfe for widget or application bundle distributions where deployment confidence is mostly about whether the bundle is reachable.
The goal is simple:
The deployed micro-frontend entry points should be available from the static distribution.
This is useful for OpenMFE-style widgets, static application bundles, or small frontend distributions.
site: complete static website verification
site checks a complete static website.
It verifies:
- the site URL
robots.txtsitemap.xml- every page URL listed by the sitemap
- sitemap index files when they exist
Use site when sitemap coverage is part of deployment confidence.
For a static website, the question is not only "is the homepage reachable?"
The better question is:
Are the important published URLs reachable after deployment?
Following the sitemap gives the script a practical source of truth.
It can verify the pages that the site itself exposes to crawlers and external systems.
This makes site useful for documentation sites, blogs, marketing sites, static SSR/SSG output, and other public websites.
Local parallelism and calm static checks
A static site smoke-test can be careful without being slow.
The useful pattern is:
- sequential across pages
- local parallelism inside one page
- retry around temporary CDN or propagation issues
- pause between page checks
Conceptually:
import { pause } from '@vyriy/pause';
import { recursive } from '@vyriy/recursive';
import { request } from '@vyriy/request';
import { retry } from '@vyriy/retry';
await recursive(async (page) => {
await retry(async () => {
await Promise.all([
request(page.url),
...page.assets.map((asset) => request(asset.url)),
]);
}, retryOptions);
await pause(500);
}, pages);
This pattern follows the same functional calm architecture idea:
- pages are checked one by one
- independent resources for the current page can be checked in parallel
- temporary failures are retried
- the script does not aggressively hammer the deployed site
The result is a smoke-test that is predictable and friendly to remote systems.
webhooks: post-deploy preparation
Deployment does not always mean the environment is ready.
Sometimes an environment needs post-deploy preparation:
- generate translations
- build derived content
- warm caches
- start Fargate tasks
- trigger background jobs
- prepare preview data
- call internal indexing endpoints
That is where webhooks is useful.
import { webhooks } from '@vyriy/scripts';
await webhooks(['webhooks/translations', 'webhooks/content']);
This step should happen after deployment and smoke checks, when the runtime is already reachable.
It turns the environment from "deployed" into "ready for testing."
This is also a calm architecture decision.
Instead of hiding post-deploy behavior inside deployment itself, the flow names it directly.
Deploy. Verify. Prepare.
Each phase has a clear reason to exist.
Practical API/service deployment shape
A service or API project may use this shape:
import { api, deploy, docker, healthcheck, lambda, webhooks } from '@vyriy/scripts';
await docker();
await deploy();
await lambda('my-function');
await healthcheck();
await api(async (url) => {
await fetch(`${url}healthcheck`);
await fetch(`${url}content/preview`);
});
await webhooks([
'webhooks/translations',
'webhooks/content',
]);
The operational story is clear:
- Publish the container image.
- Deploy infrastructure and application resources.
- Verify the Lambda runtime directly.
- Verify the HTTP healthcheck boundary.
- Verify project-specific API behavior.
- Prepare generated data or async workloads.
Every step has a narrow responsibility.
Every failure points to a smaller area.
Practical static website or MFE deployment shape
A static website or MFE project may use this shape:
import { deploy, mfe, site, webhooks } from '@vyriy/scripts';
await deploy();
await site();
// or:
// await mfe();
await webhooks([
'webhooks/content',
]);
For a complete website, site gives stronger confidence by following the sitemap.
For a micro-frontend distribution, mfe focuses on the static entry points.
webhooks should only run when the environment needs post-deploy preparation.
The flow stays explicit.
No hidden steps. No universal pipeline pretending every project is the same.
Why narrow scripts matter
A broad deployment framework can feel convenient at first.
But as projects grow, hidden behavior becomes expensive.
A script that "does everything" often becomes hard to answer:
- Did it publish the image?
- Did it deploy CDK?
- Did it run smoke-tests?
- Which URL did it check?
- Did it retry?
- Did it call post-deploy hooks?
- Which part failed?
- Can I reuse only one step locally?
Narrow scripts avoid that problem.
Each script has one operational responsibility.
The pipeline composes them.
This makes automation easier to change.
For example:
- A Lambda API can run
lambda,healthcheck, andapi. - A Fargate service can publish with
dockerorkaniko, then run HTTP checks. - A static website can run
site. - A micro-frontend can run
mfe. - A feature environment can add
webhooks. - A local developer can run only the step they need.
That is the practical value of composability.
Design rules for adding new scripts
When adding new scripts to @vyriy/scripts, they should follow the existing model:
- build on
@vyriy/script - have one clear operational responsibility
- be useful in CI/CD and local automation
- prefer AWS/serverless-first behavior when infrastructure choices are open
- expose a practical TypeScript API
- keep configuration explicit
- document the script with a real example
- fail with useful context
A new script should not hide a large pipeline behind a small name.
It should represent a clear operational boundary.
Good examples:
- verify a queue consumer
- warm a cache endpoint
- publish a package
- check an S3 object
- invoke a Step Functions smoke execution
- verify an EventBridge route
- run a small post-deploy preparation hook
Weak examples:
doEverythingdeployAndTestAndPreparemagicProductionRelease- one script that behaves differently based on too many hidden conventions
Calm automation should be boring in the best way.
Calm deployment is explicit deployment
The goal of @vyriy/scripts is not to replace CI/CD.
The goal is to make project automation easier to compose inside CI/CD.
CI should still define when things run.
Scripts should define what each operational step means.
That separation keeps the system readable:
- CI controls orchestration.
@vyriy/scriptcontrols process behavior.@vyriy/scriptsprovides reusable operational steps.- Project code composes the steps that match its deployment shape.
This is a calm boundary.
It avoids both extremes:
- too much shell duplication in CI files
- too much hidden behavior inside one deployment framework
Small scripts create calm deployments
Calm deployment is not created by one universal command.
It is created by small scripts that make the flow easier to understand and operate.
deploy owns CDK deployment.
docker and kaniko own image publishing.
lambda owns direct runtime smoke testing.
healthcheck owns the standard HTTP healthcheck.
api owns custom API verification.
mfe owns static MFE distribution checks.
site owns static website verification through the sitemap.
webhooks owns post-deploy preparation.
None of these scripts needs to be impressive alone.
Together, they create a deployment flow that is explicit, reusable, and calm.
Build or publish artifacts. Deploy infrastructure. Verify the runtime. Verify the public boundary. Prepare the environment.
One step at a time.
That is calm deployment with Vyriy scripts.