@empire-builder-kit/containers 0.0.1-alpha.10
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.
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/executors.json +3 -0
- package/dist/generators/service/files-go/Dockerfile.template +11 -0
- package/dist/generators/service/files-go/README.md.template +11 -0
- package/dist/generators/service/files-go/cmd/main.go.template +34 -0
- package/dist/generators/service/files-go/docs/contracts.md.template +9 -0
- package/dist/generators/service/files-go/go.mod.template +3 -0
- package/dist/generators/service/files-go/infra/service.ts.template +59 -0
- package/dist/generators/service/files-python/Dockerfile.template +12 -0
- package/dist/generators/service/files-python/README.md.template +11 -0
- package/dist/generators/service/files-python/docs/contracts.md.template +9 -0
- package/dist/generators/service/files-python/infra/service.ts.template +68 -0
- package/dist/generators/service/files-python/pyproject.toml.template +15 -0
- package/dist/generators/service/files-python/src/main.py.template +20 -0
- package/dist/generators/service/files-rust/Cargo.toml.template +16 -0
- package/dist/generators/service/files-rust/Dockerfile.template +12 -0
- package/dist/generators/service/files-rust/README.md.template +11 -0
- package/dist/generators/service/files-rust/docs/contracts.md.template +9 -0
- package/dist/generators/service/files-rust/infra/service.ts.template +59 -0
- package/dist/generators/service/files-rust/src/main.rs.template +29 -0
- package/dist/generators/service/files-typescript/Dockerfile.template +30 -0
- package/dist/generators/service/files-typescript/README.md.template +24 -0
- package/dist/generators/service/files-typescript/docs/architecture.md.template +18 -0
- package/dist/generators/service/files-typescript/docs/contracts.md.template +20 -0
- package/dist/generators/service/files-typescript/docs/operations.md.template +24 -0
- package/dist/generators/service/files-typescript/eslint.config.mjs.template +3 -0
- package/dist/generators/service/files-typescript/infra/service.ts.template +64 -0
- package/dist/generators/service/files-typescript/package.json.template +7 -0
- package/dist/generators/service/files-typescript/src/index.ts.template +2 -0
- package/dist/generators/service/files-typescript/src/main.ts.template +60 -0
- package/dist/generators/service/files-typescript/src/service.spec.ts.template +49 -0
- package/dist/generators/service/files-typescript/src/service.ts.template +57 -0
- package/dist/generators/service/files-typescript/tsconfig.app.json.template +12 -0
- package/dist/generators/service/files-typescript/tsconfig.json.template +7 -0
- package/dist/generators/service/files-typescript/tsconfig.spec.json.template +7 -0
- package/dist/generators/service/files-typescript/vitest.config.mts.template +17 -0
- package/dist/generators/service/schema.d.ts +8 -0
- package/dist/generators/service/schema.json +40 -0
- package/dist/generators/service/service.d.ts +5 -0
- package/dist/generators/service/service.d.ts.map +1 -0
- package/dist/generators/service/service.js +198 -0
- package/dist/generators/task/files-go/Dockerfile.template +10 -0
- package/dist/generators/task/files-go/README.md.template +11 -0
- package/dist/generators/task/files-go/cmd/main.go.template +22 -0
- package/dist/generators/task/files-go/docs/contracts.md.template +9 -0
- package/dist/generators/task/files-go/go.mod.template +3 -0
- package/dist/generators/task/files-go/infra/task.ts.template +38 -0
- package/dist/generators/task/files-python/Dockerfile.template +10 -0
- package/dist/generators/task/files-python/README.md.template +11 -0
- package/dist/generators/task/files-python/docs/contracts.md.template +9 -0
- package/dist/generators/task/files-python/infra/task.ts.template +38 -0
- package/dist/generators/task/files-python/pyproject.toml.template +12 -0
- package/dist/generators/task/files-python/src/main.py.template +26 -0
- package/dist/generators/task/files-rust/Cargo.toml.template +15 -0
- package/dist/generators/task/files-rust/Dockerfile.template +11 -0
- package/dist/generators/task/files-rust/README.md.template +11 -0
- package/dist/generators/task/files-rust/docs/contracts.md.template +9 -0
- package/dist/generators/task/files-rust/infra/task.ts.template +38 -0
- package/dist/generators/task/files-rust/src/main.rs.template +12 -0
- package/dist/generators/task/files-typescript/Dockerfile.template +25 -0
- package/dist/generators/task/files-typescript/README.md.template +24 -0
- package/dist/generators/task/files-typescript/docs/architecture.md.template +18 -0
- package/dist/generators/task/files-typescript/docs/contracts.md.template +20 -0
- package/dist/generators/task/files-typescript/docs/operations.md.template +24 -0
- package/dist/generators/task/files-typescript/eslint.config.mjs.template +3 -0
- package/dist/generators/task/files-typescript/infra/task.ts.template +35 -0
- package/dist/generators/task/files-typescript/package.json.template +7 -0
- package/dist/generators/task/files-typescript/src/index.ts.template +6 -0
- package/dist/generators/task/files-typescript/src/main.ts.template +45 -0
- package/dist/generators/task/files-typescript/src/task.spec.ts.template +66 -0
- package/dist/generators/task/files-typescript/src/task.ts.template +71 -0
- package/dist/generators/task/files-typescript/tsconfig.app.json.template +13 -0
- package/dist/generators/task/files-typescript/tsconfig.json.template +19 -0
- package/dist/generators/task/files-typescript/tsconfig.spec.json.template +11 -0
- package/dist/generators/task/files-typescript/vitest.config.mts.template +9 -0
- package/dist/generators/task/schema.d.ts +8 -0
- package/dist/generators/task/schema.json +40 -0
- package/dist/generators/task/task.d.ts +5 -0
- package/dist/generators/task/task.d.ts.map +1 -0
- package/dist/generators/task/task.js +198 -0
- package/dist/generators.json +14 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/executors.json +3 -0
- package/generators.json +14 -0
- package/package.json +93 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""<%= projectTitle %> — task runner for the <%= app %> app group."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
logging.basicConfig(
|
|
9
|
+
stream=sys.stdout,
|
|
10
|
+
level=logging.INFO,
|
|
11
|
+
format="%(message)s",
|
|
12
|
+
)
|
|
13
|
+
logger = logging.getLogger("<%= projectName %>")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
trigger = os.environ.get("TASK_TRIGGER", "manual")
|
|
18
|
+
logger.info(json.dumps({"msg": "task started", "trigger": trigger, "task": "<%= projectName %>"}))
|
|
19
|
+
|
|
20
|
+
# Task logic goes here
|
|
21
|
+
|
|
22
|
+
logger.info(json.dumps({"msg": "task completed successfully"}))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "<%= projectName %>"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[[bin]]
|
|
7
|
+
name = "task"
|
|
8
|
+
path = "src/main.rs"
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
serde = { version = "1", features = ["derive"] }
|
|
12
|
+
serde_json = "1"
|
|
13
|
+
tokio = { version = "1", features = ["full"] }
|
|
14
|
+
tracing = "0.1"
|
|
15
|
+
tracing-subscriber = { version = "0.3", features = ["json"] }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
FROM rust:1-bookworm AS builder
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY Cargo.toml Cargo.lock* ./
|
|
4
|
+
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
|
|
5
|
+
COPY src/ src/
|
|
6
|
+
RUN cargo build --release
|
|
7
|
+
|
|
8
|
+
FROM debian:bookworm-slim
|
|
9
|
+
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
|
10
|
+
COPY --from=builder /app/target/release/task /usr/local/bin/task
|
|
11
|
+
CMD ["task"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# <%= projectTitle %>
|
|
2
|
+
|
|
3
|
+
Rust task for the `<%= app %>` app group.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `pnpm nx run <%= projectName %>:build` — compile release binary
|
|
8
|
+
- `pnpm nx run <%= projectName %>:dev` — run locally
|
|
9
|
+
- `pnpm nx run <%= projectName %>:test` — run tests
|
|
10
|
+
- `pnpm nx run <%= projectName %>:lint` — run clippy
|
|
11
|
+
- `pnpm nx run <%= projectName %>:docker-build` — build container image
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface Register<%= className %>TaskArgs {
|
|
2
|
+
cluster: sst.aws.Cluster;
|
|
3
|
+
environment?: Record<string, string>;
|
|
4
|
+
link?: any[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// ECS task (not a long-running service) — runs once per trigger, no health
|
|
8
|
+
// endpoint required. Runtime-agnostic SST wrapper; the Dockerfile decides
|
|
9
|
+
// whether the workload is Rust, Go, Python, or Node.
|
|
10
|
+
export function register<%= className %>Task({
|
|
11
|
+
cluster,
|
|
12
|
+
environment = {},
|
|
13
|
+
link = [],
|
|
14
|
+
}: Register<%= className %>TaskArgs) {
|
|
15
|
+
const task = new sst.aws.Task('<%= projectName %>', {
|
|
16
|
+
cluster,
|
|
17
|
+
image: {
|
|
18
|
+
context: './apps/task/<%= name %>',
|
|
19
|
+
dockerfile: 'Dockerfile',
|
|
20
|
+
},
|
|
21
|
+
dev: {
|
|
22
|
+
command: 'pnpm nx run <%= projectName %>:dev',
|
|
23
|
+
directory: '../../..',
|
|
24
|
+
},
|
|
25
|
+
environment: {
|
|
26
|
+
TASK_NAME: '<%= className %>Task',
|
|
27
|
+
TASK_VERSION: '0.0.1',
|
|
28
|
+
TASK_TRIGGER: 'manual',
|
|
29
|
+
...environment,
|
|
30
|
+
},
|
|
31
|
+
link,
|
|
32
|
+
logging: {
|
|
33
|
+
retention: '1 month',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return { task };
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
use std::env;
|
|
2
|
+
|
|
3
|
+
#[tokio::main]
|
|
4
|
+
async fn main() {
|
|
5
|
+
tracing_subscriber::fmt().json().init();
|
|
6
|
+
|
|
7
|
+
let trigger = env::var("TASK_TRIGGER").unwrap_or_else(|_| "manual".to_string());
|
|
8
|
+
tracing::info!(trigger = %trigger, task = "<%= projectName %>", "task started");
|
|
9
|
+
|
|
10
|
+
// Task logic goes here
|
|
11
|
+
tracing::info!("task completed successfully");
|
|
12
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
|
|
3
|
+
FROM node:22-bookworm-slim AS build
|
|
4
|
+
WORKDIR /workspace
|
|
5
|
+
|
|
6
|
+
ENV CI=true
|
|
7
|
+
|
|
8
|
+
RUN corepack enable
|
|
9
|
+
|
|
10
|
+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml nx.json tsconfig.base.json tsconfig.json eslint.config.mjs ./
|
|
11
|
+
COPY packages ./packages
|
|
12
|
+
COPY tools ./tools
|
|
13
|
+
|
|
14
|
+
RUN pnpm install --frozen-lockfile
|
|
15
|
+
RUN pnpm nx run <%= projectName %>:build
|
|
16
|
+
|
|
17
|
+
FROM node:22-bookworm-slim AS runtime
|
|
18
|
+
WORKDIR /app
|
|
19
|
+
|
|
20
|
+
ENV NODE_ENV=production
|
|
21
|
+
ENV TASK_NAME=<%= className %>Task
|
|
22
|
+
|
|
23
|
+
COPY --from=build /workspace/dist/<%= projectRoot %>/ ./
|
|
24
|
+
|
|
25
|
+
CMD ["node", "main.js"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# <%= projectTitle %>
|
|
2
|
+
|
|
3
|
+
TypeScript-first backend container task for the `<%= appName %>` app group.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package provides a first mergeable ECS-style task slice for scheduled, queue-driven, and ad hoc operational work without forcing a broader multi-language preset decision yet.
|
|
8
|
+
|
|
9
|
+
## Nx Targets
|
|
10
|
+
|
|
11
|
+
- `pnpm nx run <%= projectName %>:build`
|
|
12
|
+
- `pnpm nx run <%= projectName %>:lint`
|
|
13
|
+
- `pnpm nx run <%= projectName %>:typecheck`
|
|
14
|
+
- `pnpm nx run <%= projectName %>:test`
|
|
15
|
+
- `pnpm nx run <%= projectName %>:dev`
|
|
16
|
+
- `pnpm nx run <%= projectName %>:docker-build`
|
|
17
|
+
|
|
18
|
+
## Current Conventions
|
|
19
|
+
|
|
20
|
+
- keep batch and worker logic in `src/`
|
|
21
|
+
- treat `src/main.ts` as the command entrypoint for local and container runs
|
|
22
|
+
- prefer structured JSON logs so ECS, EventBridge, and CloudWatch parsing stay predictable
|
|
23
|
+
- keep SST composition scaffolding in `infra/task.ts` so remote execution and local proxying use the same contract
|
|
24
|
+
- treat this package as app-group-internal until a supported SDK or API contract is published
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# <%= projectTitle %> Architecture
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
`<%= projectName %>` is the first TypeScript-first container task slice for `<%= appName %>`. It gives the app group a batch-oriented workload under `packages/<%= appName %>/apps/task` without introducing language-preset sprawl in the first milestone.
|
|
6
|
+
|
|
7
|
+
## Runtime Shape
|
|
8
|
+
|
|
9
|
+
- `src/main.ts` runs the task entrypoint and emits structured lifecycle logs
|
|
10
|
+
- `src/task.ts` keeps config, result creation, and task behavior testable
|
|
11
|
+
- `TASK_TRIGGER` provides a simple starting contract for schedule, queue, or manual execution context
|
|
12
|
+
- `infra/task.ts` captures the recommended SST `Cluster` + `Task` composition seam and `sst dev` proxy command contract
|
|
13
|
+
|
|
14
|
+
## Current Boundaries
|
|
15
|
+
|
|
16
|
+
- keep scheduled, queue-driven, and ad hoc operational work here
|
|
17
|
+
- publish cross-app contracts through the owning app group's SDK instead of importing internals directly
|
|
18
|
+
- compose this workload into the owning app group's root `infra/` entrypoint instead of letting runtime code instantiate SST resources ad hoc
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# <%= projectTitle %> Contracts
|
|
2
|
+
|
|
3
|
+
## Local Development
|
|
4
|
+
|
|
5
|
+
- `pnpm nx run <%= projectName %>:dev` is the canonical local command for this workload.
|
|
6
|
+
- `infra/task.ts` passes the same command to SST via `dev.command` so `sst dev` can proxy remote task runs back to local code.
|
|
7
|
+
- `TASK_NAME`, `TASK_VERSION`, and `TASK_TRIGGER` are the supported starter env overrides.
|
|
8
|
+
|
|
9
|
+
## Infra Composition
|
|
10
|
+
|
|
11
|
+
- import `register<%= className %>Task` from `apps/task/infra/task` into the owning app group's root `infra/` entrypoint
|
|
12
|
+
- provide an existing `sst.aws.Cluster` from app-group infrastructure instead of constructing a cluster inside runtime code
|
|
13
|
+
- pass linked SST resources through `link` so runtime code can consume them through `Resource.*`
|
|
14
|
+
- use `TASK_TRIGGER` as the starter trigger contract for local runs, EventBridge invocations, or task SDK calls
|
|
15
|
+
|
|
16
|
+
## Deployment Defaults
|
|
17
|
+
|
|
18
|
+
- image build context stays rooted at `packages/<%= appName %>/apps/task`
|
|
19
|
+
- local dev runs the same command SST uses when proxying task executions
|
|
20
|
+
- logging defaults to a one-month CloudWatch retention window until app-specific requirements are known
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# <%= projectTitle %> Operations
|
|
2
|
+
|
|
3
|
+
## Local Verification
|
|
4
|
+
|
|
5
|
+
- `pnpm nx run <%= projectName %>:build`
|
|
6
|
+
- `pnpm nx run <%= projectName %>:test`
|
|
7
|
+
- `pnpm nx run <%= projectName %>:dev`
|
|
8
|
+
- `node dist/<%= projectRoot %>/main.js`
|
|
9
|
+
|
|
10
|
+
Set `TASK_NAME`, `TASK_VERSION`, or `TASK_TRIGGER` in the environment to override the defaults exposed by the starter task.
|
|
11
|
+
|
|
12
|
+
`<%= projectName %>:dev` uses `@swc-node/register` so SST can proxy remote task invocations back to the same local command during `sst dev`.
|
|
13
|
+
|
|
14
|
+
## Container Workflow
|
|
15
|
+
|
|
16
|
+
- `pnpm nx run <%= projectName %>:docker-build`
|
|
17
|
+
- image build runs the workspace install plus `pnpm nx run <%= projectName %>:build`
|
|
18
|
+
- run the image with `docker run --rm -e TASK_TRIGGER=schedule <%= projectName %>` to exercise the starter command path
|
|
19
|
+
- `infra/task.ts` keeps the SST `Task` image path, linked-resource surface, and local proxy command aligned
|
|
20
|
+
|
|
21
|
+
## Next Steps
|
|
22
|
+
|
|
23
|
+
- replace the starter task body with app-specific batch or worker logic
|
|
24
|
+
- import `register<%= className %>Task` into the owning app group's `infra/` entrypoint when the workload is ready to deploy
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface Register<%= className %>TaskArgs {
|
|
2
|
+
cluster: sst.aws.Cluster;
|
|
3
|
+
environment?: Record<string, string>;
|
|
4
|
+
link?: any[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function register<%= className %>Task({
|
|
8
|
+
cluster,
|
|
9
|
+
environment = {},
|
|
10
|
+
link = [],
|
|
11
|
+
}: Register<%= className %>TaskArgs) {
|
|
12
|
+
const task = new sst.aws.Task('<%= projectName %>', {
|
|
13
|
+
cluster,
|
|
14
|
+
image: {
|
|
15
|
+
context: './apps/task/<%= name %>',
|
|
16
|
+
dockerfile: 'Dockerfile',
|
|
17
|
+
},
|
|
18
|
+
dev: {
|
|
19
|
+
command: 'pnpm nx run <%= projectName %>:dev',
|
|
20
|
+
directory: '../../..',
|
|
21
|
+
},
|
|
22
|
+
environment: {
|
|
23
|
+
TASK_NAME: '<%= className %>Task',
|
|
24
|
+
TASK_VERSION: '0.0.1',
|
|
25
|
+
TASK_TRIGGER: 'manual',
|
|
26
|
+
...environment,
|
|
27
|
+
},
|
|
28
|
+
link,
|
|
29
|
+
logging: {
|
|
30
|
+
retention: '1 month',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return { task };
|
|
35
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { formatLogEvent, readTaskConfig, runTask, type TaskInput } from './task.js';
|
|
2
|
+
|
|
3
|
+
function readTaskInput(env: NodeJS.ProcessEnv = process.env): TaskInput {
|
|
4
|
+
const trigger = env.TASK_TRIGGER?.trim();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
trigger: trigger && trigger.length > 0 ? trigger : 'manual',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
const config = readTaskConfig();
|
|
13
|
+
const input = readTaskInput();
|
|
14
|
+
|
|
15
|
+
console.log(
|
|
16
|
+
formatLogEvent(config, 'task.started', {
|
|
17
|
+
trigger: input.trigger,
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const result = await runTask(config, input);
|
|
22
|
+
|
|
23
|
+
console.log(
|
|
24
|
+
formatLogEvent(config, 'task.completed', {
|
|
25
|
+
status: result.status,
|
|
26
|
+
trigger: result.trigger,
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
console.log(JSON.stringify(result));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void main().catch((error: unknown) => {
|
|
34
|
+
const message = error instanceof Error ? error.message : 'Unknown task failure';
|
|
35
|
+
|
|
36
|
+
console.error(
|
|
37
|
+
JSON.stringify({
|
|
38
|
+
level: 'error',
|
|
39
|
+
message: 'task.failed',
|
|
40
|
+
error: message,
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createTaskResult, formatLogEvent, readTaskConfig, runTask } from './task.js';
|
|
3
|
+
|
|
4
|
+
describe('task helpers', () => {
|
|
5
|
+
it('falls back to task defaults', () => {
|
|
6
|
+
expect(readTaskConfig({})).toEqual({
|
|
7
|
+
taskName: '<%= className %>Task',
|
|
8
|
+
version: '0.0.1',
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('builds a stable task result', () => {
|
|
13
|
+
expect(
|
|
14
|
+
createTaskResult(
|
|
15
|
+
{
|
|
16
|
+
taskName: '<%= className %>Task',
|
|
17
|
+
version: '1.2.3',
|
|
18
|
+
},
|
|
19
|
+
{ trigger: 'schedule' }
|
|
20
|
+
)
|
|
21
|
+
).toEqual({
|
|
22
|
+
status: 'ok',
|
|
23
|
+
task: '<%= className %>Task',
|
|
24
|
+
trigger: 'schedule',
|
|
25
|
+
version: '1.2.3',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('formats structured log output', () => {
|
|
30
|
+
expect(
|
|
31
|
+
formatLogEvent(
|
|
32
|
+
{
|
|
33
|
+
taskName: '<%= className %>Task',
|
|
34
|
+
version: '1.2.3',
|
|
35
|
+
},
|
|
36
|
+
'task.started',
|
|
37
|
+
{ trigger: 'manual' }
|
|
38
|
+
)
|
|
39
|
+
).toBe(
|
|
40
|
+
JSON.stringify({
|
|
41
|
+
level: 'info',
|
|
42
|
+
message: 'task.started',
|
|
43
|
+
task: '<%= className %>Task',
|
|
44
|
+
version: '1.2.3',
|
|
45
|
+
trigger: 'manual',
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('runs the starter task', async () => {
|
|
51
|
+
await expect(
|
|
52
|
+
runTask(
|
|
53
|
+
{
|
|
54
|
+
taskName: '<%= className %>Task',
|
|
55
|
+
version: '1.2.3',
|
|
56
|
+
},
|
|
57
|
+
{ trigger: 'queue' }
|
|
58
|
+
)
|
|
59
|
+
).resolves.toEqual({
|
|
60
|
+
status: 'ok',
|
|
61
|
+
task: '<%= className %>Task',
|
|
62
|
+
trigger: 'queue',
|
|
63
|
+
version: '1.2.3',
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface TaskConfig {
|
|
2
|
+
taskName: string;
|
|
3
|
+
version: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface TaskInput {
|
|
7
|
+
trigger: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TaskResult {
|
|
11
|
+
status: 'ok';
|
|
12
|
+
task: string;
|
|
13
|
+
trigger: string;
|
|
14
|
+
version: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LogContext {
|
|
18
|
+
[key: string]: string | number | boolean | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DEFAULT_TASK_NAME = '<%= className %>Task';
|
|
22
|
+
const DEFAULT_TASK_VERSION = '0.0.1';
|
|
23
|
+
|
|
24
|
+
function readOptionalEnv(
|
|
25
|
+
env: NodeJS.ProcessEnv,
|
|
26
|
+
key: string,
|
|
27
|
+
fallback: string
|
|
28
|
+
): string {
|
|
29
|
+
const value = env[key]?.trim();
|
|
30
|
+
return value && value.length > 0 ? value : fallback;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function readTaskConfig(env: NodeJS.ProcessEnv = process.env): TaskConfig {
|
|
34
|
+
return {
|
|
35
|
+
taskName: readOptionalEnv(env, 'TASK_NAME', DEFAULT_TASK_NAME),
|
|
36
|
+
version: readOptionalEnv(env, 'TASK_VERSION', DEFAULT_TASK_VERSION),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createTaskResult(
|
|
41
|
+
config: TaskConfig,
|
|
42
|
+
input: TaskInput
|
|
43
|
+
): TaskResult {
|
|
44
|
+
return {
|
|
45
|
+
status: 'ok',
|
|
46
|
+
task: config.taskName,
|
|
47
|
+
trigger: input.trigger,
|
|
48
|
+
version: config.version,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function runTask(
|
|
53
|
+
config: TaskConfig,
|
|
54
|
+
input: TaskInput
|
|
55
|
+
): Promise<TaskResult> {
|
|
56
|
+
return createTaskResult(config, input);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function formatLogEvent(
|
|
60
|
+
config: TaskConfig,
|
|
61
|
+
message: string,
|
|
62
|
+
context: LogContext = {}
|
|
63
|
+
): string {
|
|
64
|
+
return JSON.stringify({
|
|
65
|
+
level: 'info',
|
|
66
|
+
message,
|
|
67
|
+
task: config.taskName,
|
|
68
|
+
version: config.version,
|
|
69
|
+
...context,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"composite": true,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"tsBuildInfoFile": "./dist/tsconfig.app.tsbuildinfo"
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*.ts"],
|
|
12
|
+
"exclude": ["src/**/*.spec.ts"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "<%= offsetFromRoot %>tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "nodenext",
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"target": "ES2022",
|
|
7
|
+
"types": ["node", "vitest/globals"]
|
|
8
|
+
},
|
|
9
|
+
"files": [],
|
|
10
|
+
"include": [],
|
|
11
|
+
"references": [
|
|
12
|
+
{
|
|
13
|
+
"path": "./tsconfig.app.json"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "./tsconfig.spec.json"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"composite": true,
|
|
5
|
+
"module": "nodenext",
|
|
6
|
+
"moduleResolution": "nodenext",
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"tsBuildInfoFile": "./dist/tsconfig.spec.tsbuildinfo"
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.spec.ts", "src/**/*.test.ts"]
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"$id": "Task",
|
|
4
|
+
"title": "Generate an EBK container task",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Name for the task within the app group.",
|
|
10
|
+
"$default": { "$source": "argv", "index": 0 },
|
|
11
|
+
"x-prompt": "What name should the task have?"
|
|
12
|
+
},
|
|
13
|
+
"app": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Owning Empire Builder Kit app-group name.",
|
|
16
|
+
"x-prompt": "Which app group should receive the task?"
|
|
17
|
+
},
|
|
18
|
+
"language": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Programming language for the task.",
|
|
21
|
+
"enum": ["typescript", "rust", "go", "python"],
|
|
22
|
+
"default": "typescript"
|
|
23
|
+
},
|
|
24
|
+
"directory": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Base directory for app groups.",
|
|
27
|
+
"default": "packages"
|
|
28
|
+
},
|
|
29
|
+
"tags": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Comma-separated additional tags to apply to the project."
|
|
32
|
+
},
|
|
33
|
+
"skipFormat": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"description": "Skip formatting files.",
|
|
36
|
+
"default": false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["name", "app"]
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../../src/generators/task/task.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,IAAI,EACL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AA+M/C,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,iBA6C3E;AAED,eAAe,aAAa,CAAC"}
|