Skip to content

Jobs - #866

Open
ndeloof wants to merge 6 commits into
compose-spec:mainfrom
ndeloof:jobs
Open

Jobs#866
ndeloof wants to merge 6 commits into
compose-spec:mainfrom
ndeloof:jobs

Conversation

@ndeloof

@ndeloof ndeloof commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

implements compose-spec/compose-spec#642

The container configuration is extracted as a layered specification — ContainerSpec (anything that runs a container: services, jobs, pre_start init containers) / WorkloadSpec (services and jobs: build, depends_on, expose, healthcheck, ports, stdin_open, tty) / service-only attributes. pre_start hooks carry the full container specification (#656), and the canonicalization/path-resolution/interpolation registries follow the same layering (which also fixes interpolation casts never applying to jobs).


Suggested review order — the history reads linearly, no commit revisits an earlier one's design:

  1. test(loader): golden-file harness — pins today's loader output before anything moves;
  2. refactor(types): extract ContainerSpec and WorkloadSpec — the layering, no behavior change;
  3. feat: pre_start hooks carry the full container specification — hooks become a ContainerSpec (add compose2nix to README #656);
  4. feat: introduce the jobs top-level element — jobs with triggers (schedule + tri-state manual), full pipeline support;
  5. feat: introduce WithSelectedJob — project narrowing for manual job execution;
  6. feat: resolve pre_start inheritance at load time — the model carries resolved hooks, idempotently across config round-trips (relies on the strictly-identical-entry merge dedup of override: sequence merge ignores strictly-identical duplicate entries #924, now on main).

@ndeloof
ndeloof requested a review from glours April 17, 2026 07:46
@ndeloof ndeloof mentioned this pull request Apr 17, 2026
@github-actions github-actions Bot added the Stale label Jun 17, 2026
@github-actions github-actions Bot closed this Jun 28, 2026
@ndeloof ndeloof reopened this Jul 30, 2026
@ndeloof
ndeloof force-pushed the jobs branch 7 times, most recently from ccbf84f to efe70a9 Compare July 31, 2026 06:40
@github-actions github-actions Bot removed the Stale label Aug 2, 2026
@ndeloof
ndeloof force-pushed the jobs branch 3 times, most recently from 0619779 to 61e7729 Compare August 19, 2026 13:16
ndeloof added a commit to docker/compose that referenced this pull request Aug 19, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 19, 2026
…go#866)

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 19, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 20, 2026
…go#866)

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 20, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 20, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 20, 2026
…go#866)

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 20, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 26, 2026
…go#866)

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 26, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 31, 2026
…go#866)

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
ndeloof added a commit to docker/compose that referenced this pull request Aug 31, 2026
compose-spec/compose-go#866 layers the service configuration:
ContainerSpec (anything running a container), WorkloadSpec (services
and jobs) and service-only attributes, with pre_start hooks carrying
the full container specification (PreStartHook) while exec hooks
(post_start/pre_stop) lose the fields they never should have had.

Promoted field access is source-compatible; what this adapts is:

- composite literals setting moved fields, wrapped into the embedded
  ContainerSpec/WorkloadSpec (production code and tests, rewritten
  mechanically with an AST tool, long lines re-split by hand)
- pre_start hook handling typed against PreStartHook
- the api test asserting exec-hook images are not collected now states
  the property is enforced by construction: ServiceHook has no image

No behavior change.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
@ndeloof
ndeloof force-pushed the jobs branch 4 times, most recently from 5b95b9e to 5079f50 Compare August 31, 2026 08:45
Load representative compose files and compare the fully-loaded project
against committed golden JSON/YAML outputs (UPDATE_GOLDEN=1 regenerates
them). This pins the observable loader behavior before the container
specification gets restructured in the next commits.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
…onfig

Layer the container configuration:

- ContainerSpec holds the attributes shared by anything that runs a
  container (runtime configuration: image, command, resources, mounts,
  networking, ...)
- WorkloadSpec holds the attributes meaningful for orchestrated
  workloads: build, depends_on, expose, healthcheck, ports, stdin_open,
  tty
- ServiceConfig embeds both and keeps the service-only attributes
  (deploy, develop, scale, container_name, links, hooks, ...)

The JSON schema mirrors this layering with container_spec and
workload_spec $defs composed into the service definition, and the
interpolation type-cast and canonicalization registries are organized by
the same layers.

No behavior change: promoted fields keep the exact same yaml/json tags,
and the golden files only reflect the new attribute ordering in
marshalled output. This layering is the ground for reusing the container
specification beyond services (jobs, pre_start init containers).

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Implements compose-spec#656: a pre_start hook is an init container run to
completion before the service starts, and now accepts the complete
container specification instead of a restricted command/image subset.
PreStartHook embeds ContainerSpec (plus per_replica); attributes not
declared on the hook are inherited from the parent service (image via
normalization, as before).

The schema, path resolution, canonicalization and interpolation casts
reuse the container_spec layer for services.*.pre_start.*, so hook
attributes behave exactly as their service counterparts. Exec hooks
(post_start/pre_stop) are unchanged: they run inside the service
container and keep rejecting container-level attributes.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Implements compose-spec#642: jobs are containers that run to completion,
triggered manually or on schedule. JobConfig composes the same
ContainerSpec and WorkloadSpec layers as services, plus job-only
triggers:

- schedule, a list of cron/timezone/concurrency/missed_fires entries
  (a plain crontab string canonicalizes into a schedule object);
- manual, a tri-state *bool: unset keeps the spec default — any job can
  be triggered by an explicit run command — while an explicit false
  forbids manual execution, meant for scheduled jobs whose
  out-of-schedule execution would be harmful. manual and schedule
  combine (anyOf in the trigger schema).

Jobs go through the full loading pipeline exactly as services do:
merge/override, extends, include, environment resolution, profiles,
normalization, path resolution, defaults — implemented by iterating the
existing per-service logic over both keys. Registries organized by
specification layer (interpolation casts, canonicalization, omitempty)
get the jobs.* prefixes, so container_spec attribute handling is
declared once and applies wherever a container is declared.

Consistency rules: service and job names share a single namespace so a
name always resolves to exactly one of them; a service cannot depend on
a job; a job can depend on services and other jobs.

Per-attribute loader tests cover every container_spec/workload_spec
attribute declared on a job.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
WithSelectedJob returns a Project narrowed to the services the named
job depends on, enabling the job's profile when it is declared under an
inactive one. The job itself is not added to Services: it is up to the
runtime to create its container when triggered.

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
The pre_start hook specification used to be completed by the runtime
(docker/compose merged the service's container specification under the
hook's, through this module's own merge machinery). The resolution now
happens during loading, in the normalization phase: the loaded model
carries hooks already completed with every inherited attribute, so any
consumer — compose config included — sees the specification a hook
actually runs with.

The merge follows the compose rules, applied post-canonicalization by
wrapping the hook as a service entry so the per-attribute mergers
match: the hook's declarations win on conflicts, collections merge,
and the sequence merge's strictly-identical-entry dedup makes the
resolution idempotent — loading an already-resolved model resolves to
the same hooks, so config round-trips stay stable.

The inherited side is filtered to container-spec attributes (derived
from the ContainerSpec type by reflection, so the filter cannot drift
from the model), minus volumes: mounts inherit at runtime through
volumes_from, the only mechanism able to share the parent's anonymous
and image volumes. This subsumes the image-only inheritance of
compose-spec#647 (inheritPreStartImage).

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant