Skip to content

feat(cli): add compile command to emit GitHub/GitLab CI YAML from a single TS plan - #35

Merged
ThePlenkov merged 1 commit into
mainfrom
devin/1786550146-compile-ci-targets
Aug 13, 2026
Merged

ThePlenkov merged 1 commit into
mainfrom
devin/1786550146-compile-ci-targets

Conversation

@ThePlenkov

@ThePlenkov ThePlenkov commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds sverka compile --target github|gitlab so the same sverka.config.ts can be turned into either GitHub Actions or GitLab CI YAML through the existing canonical Plan IR.

What changed

  • @sverka/sdk now builds the canonical Plan IR during planning:
    • PlanResult gains plan: Plan
    • new Sverka.toPlan() and top-level toPlan() return the validated Plan IR directly
  • @sverka/cli depends on @sverka/compiler-github and @sverka/compiler-gitlab
  • new sverka compile command:
    • --target github|gitlab (required)
    • --output <path> / -o <path> to write the YAML to a file
    • defaults to YAML on stdout
    • supports --format json for tool integration
  • tests added in packages/cli/src/__tests__/compile.test.ts for both targets and --output

Example

sverka compile --target github --output .github/workflows/sverka.yml
sverka compile --target gitlab --output .gitlab-ci.yml

This keeps the thin-wrapper compiler design from ADR-004: the generated CI config runs sverka execute inside the CI runner, using the same Plan derivation as local runs.

Verification

  • bunx nx run-many --target=typecheck --all passes
  • bunx nx run-many --target=lint --all passes
  • bunx nx run-many --target=test --all passes

@codeant-ai

codeant-ai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 0e11711 Aug 12, 2026 · 15:59 16:02

@baz-reviewer

baz-reviewer Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Merger

Waiting for CI and review to complete.

Commit 698f38d · Updated 2026-08-13 16:07 UTC

Review this PR on Baz | Customize your next review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bff05b0a-5105-4500-8495-a06d17c5db75

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a CLI compile command for GitHub Actions and GitLab CI.
    • Supports output to the terminal or a specified file path, with human-readable and JSON formats.
    • Added SDK support for generating and accessing canonical execution plans.
    • Compilation results now include the generated plan.
  • Bug Fixes

    • Invalid compilation targets now return a clear usage error.

Walkthrough

The SDK now exposes canonical plans through toPlan and PlanResult. The CLI adds a compile command that generates GitHub Actions or GitLab CI YAML, writes optional output files, and validates targets.

Changes

Plan compilation

Layer / File(s) Summary
Canonical plan API
packages/sdk/src/types.ts, packages/sdk/src/sverka.ts, packages/sdk/src/index.ts, packages/sdk/src/__tests__/public-api.test.ts
The SDK adds toPlan methods and the canonical plan field on PlanResult. The public API test types the plan fixture with Plan.
Plan generation and validation
packages/sdk/src/sverka.ts
Configured workflows and auto-discovery convert operations into named host-executor plans and reject empty plans.
CLI compilation flow
packages/cli/package.json, packages/cli/src/commands/compile.ts, packages/cli/src/main.ts, packages/cli/src/__tests__/compile.test.ts
The CLI registers compile, supports GitHub and GitLab targets, emits text or JSON output, writes files with recursive directory creation, and tests successful, file-output, and invalid-target cases.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as compileCommand
  participant SDK as Sverka.toPlan
  participant GitHub as `@sverka/compiler-github`
  participant GitLab as `@sverka/compiler-gitlab`
  participant Output as stdout_or_output_file
  CLI->>SDK: Build canonical Plan
  alt target is github
    CLI->>GitHub: Compile Plan to GitHub Actions YAML
    GitHub-->>CLI: Return YAML
  else target is gitlab
    CLI->>GitLab: Compile Plan to GitLab CI YAML
    GitLab-->>CLI: Return YAML
  end
  CLI->>Output: Write or emit YAML
Loading

Possibly related PRs

Suggested labels: baz: needs review

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding a CLI compile command that emits GitHub or GitLab CI YAML from a TypeScript plan.
Description check ✅ Passed The description directly explains the CLI command, SDK Plan changes, compiler dependencies, tests, examples, and verification steps.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1786550146-compile-ci-targets

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 12, 2026

@amazon-q-developer amazon-q-developer 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.

This PR successfully adds the compile command functionality to transform Sverka workflows into CI YAML for GitHub Actions and GitLab CI. The implementation follows existing patterns and includes appropriate validation and testing. The code is ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 13 complexity · 3 duplication

Metric Results
Complexity 13
Duplication 3

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@ThePlenkov ThePlenkov changed the title test feat(cli): add compile command to emit GitHub/GitLab CI YAML from a single TS plan Aug 12, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add sverka compile for GitHub/GitLab targets via canonical Plan IR

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Add toPlan() to SDK to expose canonical Plan IR from resolved operations.
• Add sverka compile --target github|gitlab [--output] to emit CI YAML (or JSON).
• Add CLI tests covering GitHub/GitLab compilation, file output, and invalid targets.
Diagram

graph TD
  A["CLI main.ts"] --> B["compile command"] --> C["SDK createSverka"] --> D["SDK toPlan()"] --> E{Target}
  E -->|"github"| F["compiler-github"] --> H["YAML string"]
  E -->|"gitlab"| G["compiler-gitlab"] --> H
  H --> I{Output}
  I -->|"stdout"| J["writer stdout"]
  I -->|"--output"| K["write file"]
  subgraph Legend
    direction LR
    _cli["CLI"] ~~~ _sdk["SDK"] ~~~ _dec{"Decision"} ~~~ _io["I/O"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Plugin-based target registry (extensible compilers)
  • ➕ Avoids hardcoding targets in CLI; enables third-party CI targets
  • ➕ Targets can be added without changing CLI dispatch logic
  • ➖ More upfront abstraction and API design (plugin discovery, versioning)
  • ➖ Adds complexity for a small initial set of targets
2. Keep `plan()` only; derive Plan IR externally
  • ➕ Avoids expanding SDK API surface (toPlan, PlanResult.plan)
  • ➕ Keeps planner/executor responsibilities clearer
  • ➖ Duplicates Plan construction/validation in CLI or compilers
  • ➖ Makes Plan IR access inconsistent across consumers

Recommendation: Current approach is appropriate for the near term: exposing toPlan() and embedding plan into PlanResult creates a single canonical Plan IR for multiple downstream compilers. If additional targets are expected soon (beyond GitHub/GitLab), consider evolving the CLI’s target selection into a registry/plugin mechanism to avoid repeated conditional wiring.

Files changed (9) +267 / -3

Enhancement (5) +167 / -3
compile.tsImplement 'sverka compile' to emit GitHub/GitLab YAML +81/-0

Implement 'sverka compile' to emit GitHub/GitLab YAML

• Adds a new compile subcommand that builds a canonical Plan via 'createSverka(...).toPlan()', compiles it using the appropriate target compiler, and writes YAML to stdout or a file. Supports JSON-formatted output envelopes when '--format json' is selected.

packages/cli/src/commands/compile.ts

main.tsRegister 'compile' subcommand and dispatch to compiler +38/-0

Register 'compile' subcommand and dispatch to compiler

• Wires a new 'compile' yargs command with '--target' (github|gitlab) and optional '--output/-o', then dispatches to 'compileCommand' alongside existing commands.

packages/cli/src/main.ts

index.tsExport 'toPlan' from SDK entrypoint +1/-1

Export 'toPlan' from SDK entrypoint

• Exposes 'toPlan' as part of the SDK public API alongside 'createSverka', 'plan', and 'execute'.

packages/sdk/src/index.ts

sverka.tsAdd canonical Plan construction/validation and 'toPlan' API +43/-2

Add canonical Plan construction/validation and 'toPlan' API

• Adds 'Sverka.toPlan()' and a top-level 'toPlan()' convenience function that return the canonical Plan IR. Updates planning to also return 'plan' within 'PlanResult', building it from operations with host executor defaults and validating the Plan when operations are present.

packages/sdk/src/sverka.ts

types.tsExtend SDK types to include 'Plan' and 'PlanResult.plan' +4/-0

Extend SDK types to include 'Plan' and 'PlanResult.plan'

• Imports 'Plan' from '@sverka/ir', adds 'toPlan()' to the 'Sverka' interface, and extends 'PlanResult' to always return the canonical Plan IR in a new 'plan' field.

packages/sdk/src/types.ts

Tests (2) +96 / -0
compile.test.tsAdd CLI tests for 'compile' command behavior +94/-0

Add CLI tests for 'compile' command behavior

• Introduces Vitest coverage for compiling a sample 'sverka.config.ts' to GitHub Actions and GitLab CI YAML. Verifies '--output' writes a file and unsupported targets return a usage error exit code.

packages/cli/src/tests/compile.test.ts

public-api.test.tsUpdate SDK public API type checks for Plan exposure +2/-0

Update SDK public API type checks for Plan exposure

• Extends the public API compile-time test to include the 'Plan' type and validates that 'PlanResult' now includes a 'plan' field.

packages/sdk/src/tests/public-api.test.ts

Other (2) +4 / -0
bun.lockAdd CLI workspace deps for GitHub/GitLab compilers +2/-0

Add CLI workspace deps for GitHub/GitLab compilers

• Updates the lockfile to include '@sverka/compiler-github' and '@sverka/compiler-gitlab' as CLI dependencies, ensuring they are available at runtime.

bun.lock

package.jsonWire compiler packages into CLI dependencies +2/-0

Wire compiler packages into CLI dependencies

• Adds '@sverka/compiler-github' and '@sverka/compiler-gitlab' to the CLI package dependencies so the new 'compile' command can emit CI YAML for both targets.

packages/cli/package.json

Comment thread packages/cli/src/commands/compile.ts

@codacy-production codacy-production 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.

Pull Request Overview

This PR introduces the compile command to the CLI and extends the SDK to support workflow plan generation. Although Codacy indicates the PR is up to standards, there are critical logic errors: specifically, packages/sdk/src/sverka.ts is missing required imports for Plan and validatePlan, which will cause runtime failures. Furthermore, the Plan type is not exported from the SDK's public API, breaking compatibility for external consumers.

From a requirements perspective, while most acceptance criteria are met, there is a total lack of integration tests for the --format json output option in the CLI. The file packages/sdk/src/sverka.ts is flagged as both complex and uncovered by tests, making the logic duplication identified there particularly risky. These issues should be addressed before merging.

About this PR

  • The implementation of --format json lacks corresponding integration tests in compile.test.ts. Please add test cases to verify the JSON structure for both terminal output and file-based output.
  • Please update the PR title to reflect the implementation of the CLI compile command and SDK IR exposure.

Test suggestions

  • Compile a workflow to GitHub Actions YAML and output to stdout
  • Compile a workflow to GitLab CI YAML and output to stdout
  • Save compiled YAML to a file path and verify parent directory creation
  • Verify usage error is returned for an unsupported target (e.g., 'azure')
  • Verify JSON output format when compiling to stdout includes the generated YAML
  • Verify JSON output format when saving to a file includes the target path
  • Verify SDK toPlan() correctly synthesizes and validates the workflow IR
  • Unit tests for uncovered logic in complex file packages/sdk/src/sverka.ts
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Verify JSON output format when compiling to stdout includes the generated YAML
2. Verify JSON output format when saving to a file includes the target path
3. Unit tests for uncovered logic in complex file `packages/sdk/src/sverka.ts`

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread packages/sdk/src/types.ts
Comment thread packages/sdk/src/sverka.ts
Comment thread packages/sdk/src/sverka.ts
Comment thread packages/cli/src/commands/compile.ts
Comment thread packages/sdk/src/sverka.ts
Comment thread packages/sdk/src/sverka.ts
@qodo-code-review

qodo-code-review Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Invalid Plan on empty ops ✗ Dismissed 🐞 Bug ≡ Correctness
Description
toPlan() / PlanResult.plan can return a Plan with operations: [] because plan validation is
skipped when no operations are discovered. This violates @sverka/ir's validatePlan() rule that
operations must be non-empty, so downstream consumers that assume schema-valid Plan IR can break or
fail validation unexpectedly.
Code

packages/sdk/src/sverka.ts[R122-125]

+  if (operations.length > 0) {
+    const validation = validatePlan(plan);
+    if (!validation.valid) {
+      throw new SdkError(
Relevance

●●● Strong

Correctness: returning schema-invalid Plan can break CLI/compilers; team has emphasized plan/IR
validation hardening.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The SDK’s new plan-building path skips validation when operations.length === 0, yet the IR
validator explicitly rejects empty operations. Auto-discovery is tested to produce `operations =
[], so the newly exposed PlanResult.plan / toPlan()` can be invalid in normal usage, and CLI
compile consumes toPlan() directly.

packages/sdk/src/sverka.ts[110-132]
packages/ir/src/validate.ts[111-120]
packages/sdk/src/tests/plan-mode.test.ts[19-28]
packages/cli/src/commands/compile.ts[43-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The SDK now exposes a canonical `Plan` via `PlanResult.plan` and `toPlan()`, but it can return a Plan that fails `validatePlan()` when `operations.length === 0` because validation is skipped. This creates an API-contract mismatch: callers receive a `Plan` type that is not valid Plan IR.

### Issue Context
- Auto-discovery can legitimately return `operations = []` (tests cover this), so this is a reachable scenario.
- `@sverka/ir` validator treats empty operations as invalid (`EMPTY_OPERATIONS`).
- `packages/cli` `compile` uses `toPlan()` directly.

### Fix approach (pick one and implement consistently)
**Option A (recommended): represent “no plan” explicitly**
1. Change `PlanResult.plan` to `Plan | null` (or make it optional).
2. In `doPlan()`, set `plan: null` when `operations.length === 0`.
3. In `toPlan()`, throw an `SdkError` (e.g. `CONFIG_NOT_FOUND`/`EXECUTION_FAILED`) if the computed plan is null / operations empty.
4. In CLI `compile`, surface a clear usage/runtime error when `toPlan()` cannot produce a plan.
5. Update/extend tests to cover `toPlan()` empty-ops behavior.

**Option B: allow empty plans in IR**
1. Update `@sverka/ir` validation rules to allow `operations: []` (remove/relax rule 3).
2. Remove the `operations.length > 0` validation guard so returned plans are always validated.

### Fix Focus Areas
- packages/sdk/src/types.ts[47-56]
- packages/sdk/src/sverka.ts[72-133]
- packages/ir/src/validate.ts[111-120]
- packages/cli/src/commands/compile.ts[43-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 8 rules
Review mode: ⚖️ Balanced: This adds a new CLI compilation path across multiple files and exposes SDK plan behavior, with several independent output, path, parsing, and plan-generation interactions that warrant a complete single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/sdk/src/sverka.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sdk/src/sverka.ts`:
- Around line 122-130: Update the plan validation flow in toPlan so validatePlan
is invoked even when operations is empty; remove the operations.length
conditional and preserve the existing SdkError handling for invalid plans.
Ensure empty auto-discovered plans are rejected before being returned to
compileCommand.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e625ceb1-1d42-41f0-9e28-e6abdf5fc1f9

📥 Commits

Reviewing files that changed from the base of the PR and between db46bfe and 0e11711.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • packages/cli/package.json
  • packages/cli/src/__tests__/compile.test.ts
  • packages/cli/src/commands/compile.ts
  • packages/cli/src/main.ts
  • packages/sdk/src/__tests__/public-api.test.ts
  • packages/sdk/src/index.ts
  • packages/sdk/src/sverka.ts
  • packages/sdk/src/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Codacy Static Code Analysis
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: - No any: Use unknown and narrow. Strict TypeScript.

  • Error handling: Custom error classes per package.

Files:

  • packages/sdk/src/__tests__/public-api.test.ts
  • packages/sdk/src/index.ts
  • packages/cli/src/main.ts
  • packages/cli/src/commands/compile.ts
  • packages/cli/src/__tests__/compile.test.ts
  • packages/sdk/src/types.ts
  • packages/sdk/src/sverka.ts
**/src/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

  • Public API: Everything public is exported from src/index.ts.

Files:

  • packages/sdk/src/index.ts
🪛 GitHub Check: SonarCloud Code Analysis
packages/cli/src/commands/compile.ts

[warning] 67-67: 'If' statement should not be the only statement in 'else' block

See more on https://sonarcloud.io/project/issues?id=sverka-dev_sverka&issues=AZ_2s_NtrIcTy-_n40Fj&open=AZ_2s_NtrIcTy-_n40Fj&pullRequest=35

🔇 Additional comments (8)
packages/cli/package.json (1)

25-26: LGTM!

packages/cli/src/commands/compile.ts (1)

1-81: LGTM!

packages/cli/src/main.ts (1)

15-15: LGTM!

Also applies to: 69-83, 115-115, 146-147, 192-208

packages/cli/src/__tests__/compile.test.ts (1)

1-94: LGTM!

packages/sdk/src/types.ts (1)

5-5: LGTM!

Also applies to: 42-42, 54-55

packages/sdk/src/sverka.ts (1)

44-46: LGTM!

Also applies to: 58-62

packages/sdk/src/index.ts (1)

100-100: LGTM!

packages/sdk/src/__tests__/public-api.test.ts (1)

10-10: LGTM!

Also applies to: 54-54

Comment thread packages/sdk/src/sverka.ts
@coderabbitai coderabbitai Bot mentioned this pull request Aug 13, 2026
5 tasks done
@ThePlenkov
ThePlenkov force-pushed the devin/1786550146-compile-ci-targets branch from 0e11711 to bcf86bb Compare August 13, 2026 16:01
- Add toPlan() to SDK to build canonical Plan IR from sverka.config.ts.
- Wire compiler-github and compiler-gitlab into CLI deps.
- Add compile command: sverka compile --target github|gitlab [--output <path>].
- Output YAML to stdout or file, with JSON format support.
- Add CLI tests for github/gitlab output and --output.

Relates-to: sverka design - compile single TS plan to multiple CI targets.
Co-Authored-By: Petr Plenkov <petr.plenkov@gmail.com>
@ThePlenkov
ThePlenkov force-pushed the devin/1786550146-compile-ci-targets branch from bcf86bb to 698f38d Compare August 13, 2026 16:07
@sonarqubecloud

Copy link
Copy Markdown

@ThePlenkov
ThePlenkov merged commit ecdee79 into main Aug 13, 2026
8 of 9 checks passed
@ThePlenkov
ThePlenkov deleted the devin/1786550146-compile-ci-targets branch August 13, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant