typelevel / typelevel/sbt-typelevel

Public hook for appending commands to the "Check headers and formatting" lint step

Open
#888 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
185
Forks
62
PR merge metrics
No merged PRs in 30d

Description

Problem

sbt-typelevel's generated Test job emits a step named "Check headers and formatting" that runs sbt commands like:

sbt '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck

Plugins and projects that contribute additional lint-style checks (custom formatters, schema validators, license linters, etc.) want to fold their check into this same step so it runs alongside the existing ones — same matrix cell, same conditions, same "lint failures show up here" semantics. There's no first-class way to do that today; the only option is a fragile rewrite of githubWorkflowBuild:

ThisBuild / githubWorkflowBuild ~= {
  _.map {
    case step: WorkflowStep.Sbt if step.name == Some("Check headers and formatting") =>
      step.withCommands(step.commands :+ "myLintCheckAll")
    case other => other
  }
}

This is nine lines per plugin, repeats the magic step-name string at every call site, and breaks if upstream renames the step or splits it. It also doesn't compose: two plugins both rewriting githubWorkflowBuild need to be careful about order and not stomping each other.

Proposed shape

A public setting that's just a list of extra commands appended into the existing lint step:

val tlCiLintCommands = settingKey[Seq[String]](
  "Additional sbt commands appended to the 'Check headers and formatting' step"
)

// existing default rendering becomes something like:
WorkflowStep.Sbt(
  List(
    s"$$ $${{ matrix.scala }}",
    "headerCheckAll",
    "scalafmtCheckAll",
    "project /",
    "scalafmtSbtCheck",
  ) ++ tlCiLintCommands.value,
  name = Some("Check headers and formatting"),
  cond = ...,
)

User code becomes one line:

ThisBuild / tlCiLintCommands += "smithyFmtCheckAll"

…and plugins can opt projects in by default:

override def projectSettings = Seq(
  tlCiLintCommands += "smithyFmtCheckAll",
)

+='d sequences compose naturally across plugins and project settings, so there's no ordering footgun.

Why it's worth doing

  • Removes a copy-paste recipe from documentation. We currently tell SmithyFormatPlugin users (in polyvariant/smithy-trait-codegen-scala) to write the nine-line githubWorkflowBuild ~= { … } snippet by hand — every reader has to understand step matching, WorkflowStep.Sbt, and withCommands just to bolt one task onto CI.
  • Plays nicely with autoplugins. A plugin that wants to opt projects into a check on CI can do so transparently via projectSettings, with users' explicit additions composing on top.
  • The current pattern is brittle to upstream changes (step name, step splitting) — a named setting decouples user code from the rendering layout.

Happy to send a PR if the shape lands.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the githubWorkflowBuild rendering and the WorkflowStep.Sbt command construction described in the issue. Add the public tlCiLintCommands setting so its values are appended to the existing "Check headers and formatting" commands, then verify that += additions compose and remain in the generated Test job.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
build-system, ci-cd
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.