Skip to content

feat(core): ExecJob console-height / console-width for TTY sizing (#235) - #701

Merged
CybotTM merged 2 commits into
mainfrom
feat/235-execjob-console-size
May 17, 2026
Merged

feat(core): ExecJob console-height / console-width for TTY sizing (#235)#701
CybotTM merged 2 commits into
mainfrom
feat/235-execjob-console-size

Conversation

@CybotTM

@CybotTM CybotTM commented May 17, 2026

Copy link
Copy Markdown
Member

Summary

Operators can now configure the initial pseudo-TTY console size for job-exec jobs via two new fields:

```ini
[job-exec "my-job"]
tty = true
console-height = 24
console-width = 80
```

Useful for jobs that render TUIs, tables, or formatted text — applications that expect a specific terminal geometry (`htop`, `vim`, formatted reports) now render correctly instead of relying on Docker's default console size.

Closes #235.

Backward compatibility

  • Default behavior unchanged: existing configs without `console-height` / `console-width` produce nil `ConsoleSize` on the Docker call (Docker daemon picks the default).
  • Only honored when `tty = true`; otherwise the Docker daemon silently ignores the size.
  • Requires Docker API v1.42+ (Docker Engine 20.10+, released 2020-12). Ofelia's auto-negotiation handles older daemons gracefully — the field is just ignored.

Tests (5 new)

  • `ConsoleSize_NilWhenUnset` — pre-Add ConsoleSize support for TTY-based ExecJobs #235 default preserved when both dimensions are zero.
  • `ConsoleSize_HeightWidthOrder` — pins the `[height, width]` order Docker expects (swapping would produce silently-wrong TUI geometry).
  • `ConsoleSize_PartialPopulates` — setting only one dimension still produces a non-nil `ConsoleSize` (partial config is operator-meaningful).
  • `RunWithStreams_PropagatesConsoleSize` — end-to-end via mock provider, asserts the value reaches `domain.ExecConfig`.
  • `RunWithStreams_DefaultsToNilConsoleSize` — legacy "Docker default" behavior preserved when fields are unset.

Test plan

  • `go test ./...` passes (full repo, 14 packages, ~58s)
  • `golangci-lint run` clean
  • `go vet ./...` clean
  • CI green

Copilot AI review requested due to automatic review settings May 17, 2026 10:24
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests labels May 17, 2026
@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

github-actions[bot]
github-actions Bot previously approved these changes May 17, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@github-actions

Copy link
Copy Markdown

✅ Mutation Testing Results

Mutation Score: 100.00% (threshold: 60%)

✨ Good job! Mutation score meets the threshold.

What is mutation testing?

Mutation testing measures test quality by introducing small changes (mutations) to the code and checking if tests detect them. A higher score means better test effectiveness.

  • Killed mutants: Tests caught the mutation (good!)
  • Survived mutants: Tests missed the mutation (needs improvement)

@codecov

codecov Bot commented May 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.08%. Comparing base (44d79cb) to head (eaf7527).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #701      +/-   ##
==========================================
- Coverage   88.10%   88.08%   -0.03%     
==========================================
  Files          89       89              
  Lines       11497    11504       +7     
==========================================
+ Hits        10130    10133       +3     
- Misses       1115     1117       +2     
- Partials      252      254       +2     
Flag Coverage Δ
integration 88.08% <100.00%> (-0.03%) ⬇️
unittests 85.43% <100.00%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds operator-configurable initial pseudo-TTY console size (console-height / console-width) to job-exec, plumbed through domain.ExecConfig to Docker's ContainerExecCreate.ConsoleSize. Defaults preserve pre-fix behavior (nil → Docker default).

Changes:

  • Add ConsoleHeight / ConsoleWidth fields on ExecJob and a consoleSize() helper that returns nil when both are zero, populating ExecConfig.ConsoleSize on both Run and RunWithStreams paths.
  • Extend domain.ExecConfig with ConsoleSize *[2]uint and forward it through the Docker adapter to containertypes.ExecOptions.
  • Add unit/integration tests pinning [height, width] order, partial-population semantics, and the nil-default backward-compat path; CHANGELOG entry under Added.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
core/execjob.go New ConsoleHeight/ConsoleWidth fields, consoleSize() helper, and wiring into both exec config builders.
core/domain/exec.go Adds ConsoleSize *[2]uint to the domain ExecConfig.
core/adapters/docker/exec.go Propagates config.ConsoleSize to containertypes.ExecOptions.
core/execjob_console_size_test.go New tests covering nil default, [height,width] ordering, partial population, and end-to-end propagation.
CHANGELOG.md Documents the new fields under Added.

Comment thread core/execjob.go
Comment thread core/execjob.go

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the ability to set the initial pseudo-TTY console size for job-exec jobs via new console-height and console-width configuration fields. The changes include updates to the ExecJob and ExecConfig structures, the Docker adapter to pass these dimensions to the Docker API, and a new test suite to ensure correct behavior and backward compatibility. I have no feedback to provide.

CybotTM added a commit that referenced this pull request May 17, 2026
Code reviewer "Important" + test engineer "High" findings applied.

Test engineer High: the adapter-level SDK boundary at
core/adapters/docker/exec.go:64 (the `ConsoleSize: config.ConsoleSize`
line in the containertypes.ExecOptions struct literal) had no test
coverage. A refactor that dropped it would silently break #235 and
all 5 core-level tests would still pass because they only exercise
the mock provider. Added two adapter tests using an httptest server
to capture the SDK request body:

- TestExecServiceAdapter_Create_PropagatesConsoleSize asserts the
  ConsoleSize value reaches the daemon's /containers/{id}/exec POST
  body with the correct [height, width] order.
- TestExecServiceAdapter_Create_OmitsConsoleSizeWhenNil pins the
  JSON `omitempty` contract: a nil ConsoleSize must NOT appear in
  the request body, otherwise Docker would treat {0,0} as an
  explicit-default override.

Test engineer Medium / code reviewer Suggestion: pin the
TTY-vs-ConsoleSize ownership contract — Ofelia owns the wire-the-
value layer; the Docker daemon owns the "honor it only when TTY is
true" layer. New test
TestExecJob_RunWithStreams_PropagatesConsoleSizeEvenWhenTTYFalse
ensures a future well-meaning patch can't add `if j.TTY { ... }` and
silently swallow operator intent for non-TTY jobs.

Code reviewer Important: documentation gap. docs/jobs.md and
docs/CONFIGURATION.md didn't mention the new console-height /
console-width fields, so INI/labels operators couldn't discover them.

- docs/jobs.md: added two entries next to the existing `tty` parameter
  in the `exec` section. Each documents the TTY-gating, Docker API
  version floor (v1.42+), and the independent-dimension contract.
- docs/CONFIGURATION.md: added one-line examples in the job-exec INI
  block with inline comments naming the issue.

Code reviewer Suggestion: clarified the consoleSize() doc-comment to
note that partial values like {40, 0} are intentionally forwarded
(daemon honors the 40 and uses its default for the zero dimension);
only "both zero" collapses to nil.

Deferred:

- TTY-mismatch validation warning (code reviewer Suggestion). The
  field godoc already calls out the daemon's silent-ignore behavior;
  a load-time warning would touch config validator and is scope creep
  for a small Docker-field addition.
- Upper-bound sanity check (code reviewer Suggestion). Docker
  validates daemon-side; uint type already prevents negatives.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@CybotTM

CybotTM commented May 17, 2026

Copy link
Copy Markdown
Member Author

Multi-axis review summary (code-reviewer + test-engineer + Copilot)

All findings applied across one fixup commit (86bc573). 0 unresolved threads.

Applied — Important / High

  • Adapter-level SDK boundary test (test-engineer High) — added two TestExecServiceAdapter_Create_* tests using httptest to capture the request body. The containertypes.ExecOptions.ConsoleSize line in exec.go:64 was previously the only untested layer; a refactor dropping it would have passed all 5 core-level tests because they only exercise the mock provider.
  • Docs gap (code-reviewer Important + Copilot) — console-height / console-width now documented in docs/jobs.md (exec Parameters section, next to tty) and docs/CONFIGURATION.md (job-exec INI block).

Applied — Medium

  • TTY=false propagation test (test-engineer + code-reviewer) — TestExecJob_RunWithStreams_PropagatesConsoleSizeEvenWhenTTYFalse pins that Ofelia owns the wire-the-value layer; the Docker daemon owns the TTY-gating. Prevents a future well-meaning if j.TTY { ... } from silently swallowing operator intent.

Applied — Doc/Suggestion

  • consoleSize() partial-values comment (code-reviewer Suggestion) — notes that {40, 0} is intentionally forwarded so the daemon honors the 40 and uses its default for the zero dimension; only "both zero" collapses to nil.

Deferred — with rationale on threads

  • TTY-mismatch validation warning (code-reviewer + Copilot Suggestion) — would require plumbing through to config_validate.go's validator chain and a breaking-vs-warning behavioral decision. Scope creep for a small Docker-field addition. The field godoc already documents the daemon-silently-ignores contract, and the new TTY=false test pins it.
  • Upper-bound sanity check (code-reviewer Suggestion) — Docker validates daemon-side; uint type already prevents negatives.

Final stats: 7 tests (5 core + 2 adapter), 2 doc files updated, CHANGELOG entry. All 2 inline AI threads replied + resolved.

CybotTM added 2 commits May 17, 2026 12:35
Operators can now configure the initial pseudo-TTY console size for
job-exec jobs via two new fields:

  [job-exec "my-job"]
  tty = true
  console-height = 24
  console-width  = 80

Useful for jobs that render TUIs, tables, or formatted text —
applications that expect a specific terminal geometry (htop, vim,
formatted reports) now render correctly instead of relying on Docker's
default console size. Closes #235.

Implementation:

- New ExecJob.ConsoleHeight + ConsoleWidth (uint, mapstructure-tagged)
  hash:"true" so config changes trigger reload.
- New ExecJob.consoleSize() helper returns *[2]uint{height, width} or
  nil when both dimensions are zero — matching the Docker SDK contract
  (nil = "use Docker's default", any populated value is sent verbatim).
- New domain.ExecConfig.ConsoleSize *[2]uint plumbing.
- Adapter wires through to containertypes.ExecOptions.ConsoleSize
  (Docker SDK 28.5.2, field added in API v1.42 / Docker 20.10).

Tests (5 new, all in core/execjob_console_size_test.go):

- ConsoleSize_NilWhenUnset: pre-#235 default behavior preserved when
  both dimensions are zero.
- ConsoleSize_HeightWidthOrder: pins the [height, width] order Docker
  expects — swapping would produce silently-wrong TUI geometry.
- ConsoleSize_PartialPopulates: setting only one dimension still
  produces a non-nil ConsoleSize (partial config is operator-
  meaningful).
- RunWithStreams_PropagatesConsoleSize: end-to-end via mock provider
  asserting ConsoleSize arrives at domain.ExecConfig.
- RunWithStreams_DefaultsToNilConsoleSize: a job without the new
  fields still produces nil ConsoleSize, preserving the legacy
  "Docker default" behavior.

Backward compat: existing configs without console-height/console-width
behave exactly as before. Only honored when tty = true; otherwise the
Docker daemon silently ignores the size (verified by Docker API spec).

CHANGELOG entry under [Unreleased] ### Added.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Code reviewer "Important" + test engineer "High" findings applied.

Test engineer High: the adapter-level SDK boundary at
core/adapters/docker/exec.go:64 (the `ConsoleSize: config.ConsoleSize`
line in the containertypes.ExecOptions struct literal) had no test
coverage. A refactor that dropped it would silently break #235 and
all 5 core-level tests would still pass because they only exercise
the mock provider. Added two adapter tests using an httptest server
to capture the SDK request body:

- TestExecServiceAdapter_Create_PropagatesConsoleSize asserts the
  ConsoleSize value reaches the daemon's /containers/{id}/exec POST
  body with the correct [height, width] order.
- TestExecServiceAdapter_Create_OmitsConsoleSizeWhenNil pins the
  JSON `omitempty` contract: a nil ConsoleSize must NOT appear in
  the request body, otherwise Docker would treat {0,0} as an
  explicit-default override.

Test engineer Medium / code reviewer Suggestion: pin the
TTY-vs-ConsoleSize ownership contract — Ofelia owns the wire-the-
value layer; the Docker daemon owns the "honor it only when TTY is
true" layer. New test
TestExecJob_RunWithStreams_PropagatesConsoleSizeEvenWhenTTYFalse
ensures a future well-meaning patch can't add `if j.TTY { ... }` and
silently swallow operator intent for non-TTY jobs.

Code reviewer Important: documentation gap. docs/jobs.md and
docs/CONFIGURATION.md didn't mention the new console-height /
console-width fields, so INI/labels operators couldn't discover them.

- docs/jobs.md: added two entries next to the existing `tty` parameter
  in the `exec` section. Each documents the TTY-gating, Docker API
  version floor (v1.42+), and the independent-dimension contract.
- docs/CONFIGURATION.md: added one-line examples in the job-exec INI
  block with inline comments naming the issue.

Code reviewer Suggestion: clarified the consoleSize() doc-comment to
note that partial values like {40, 0} are intentionally forwarded
(daemon honors the 40 and uses its default for the zero dimension);
only "both zero" collapses to nil.

Deferred:

- TTY-mismatch validation warning (code reviewer Suggestion). The
  field godoc already calls out the daemon's silent-ignore behavior;
  a load-time warning would touch config validator and is scope creep
  for a small Docker-field addition.
- Upper-bound sanity check (code reviewer Suggestion). Docker
  validates daemon-side; uint type already prevents negatives.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@CybotTM
CybotTM force-pushed the feat/235-execjob-console-size branch from 86bc573 to eaf7527 Compare May 17, 2026 10:36
@sonarqubecloud

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@CybotTM
CybotTM added this pull request to the merge queue May 17, 2026
Merged via the queue into main with commit e92b41f May 17, 2026
27 checks passed
@CybotTM
CybotTM deleted the feat/235-execjob-console-size branch May 17, 2026 10:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ConsoleSize support for TTY-based ExecJobs

2 participants