Skip to content

EPMDEDP-17346: feat: Add krci project build command - #40

Merged
SergK merged 7 commits into
mainfrom
feat/EPMDEDP-17346-project-build
Sep 17, 2026
Merged

SergK merged 7 commits into
mainfrom
feat/EPMDEDP-17346-project-build

Conversation

@SergK

@SergK SergK commented Sep 17, 2026 •

Copy link
Copy Markdown
Member

Closes #39. CLI half of the feature; the portal half landed in KubeRocketCI/krci-portal#403 and is required on the target portal.

One command reproduces the Portal Build button. krci project build <name> [--branch <b>] [--param k=v]... [--dry-run] [-o table|json|yaml] starts the build pipeline of a project branch. The pipeline, its params, labels, service account and TriggerTemplate are resolved server-side from the project and branch, so the caller never names a Tekton pipeline, a CodebaseBranch or a TriggerTemplate. --branch defaults to the project's default branch. Output is the pipelinerun start row, JSON envelope and dry-run rendering, so a run is immediately traceable with krci pipelinerun list --project <name>.

Every failure is a specific message. The portal answers with a stable reason; the CLI maps each one to a terminal message that names the project and, when given, the branch. When --branch is omitted the message says the default branch of project '<name>', because the portal does not echo resource names.

Situation Message
Project missing project '<name>' not found
Branch missing branch '<b>' of project '<name>' not found
Branch not created ... is not ready (status must be 'created'); check: krci project get <name>
Build already active a build is already running for ...; check: krci pipelinerun list --project <name> --status running
Project builds via GitLab CI ... builds via GitLab CI; 'krci project build' does not support it yet, trigger it from the portal
No build pipeline, ambiguous branch, missing git server or template platform-configuration message, exit 1
Portal predates the route portal has no endpoint for this command (...); upgrade the portal
Resource list truncated portal could not safely evaluate the build state ...; retry or contact an operator

Managed params are rejected before any request. git-source-url, git-source-revision, targetBranch, CODEBASE_NAME, CODEBASEBRANCH_NAME, gitfullrepositoryname, changeNumber, patchsetNumber are derived from the project and branch and cannot be overridden with --param. The Go list is pinned by a test to the x-krci-managed-params extension in the vendored spec, so drift from the portal fails make test. COMMIT_MESSAGE, COMMIT_MESSAGE_PATTERN and pipeline-specific params stay overridable. Raw control remains available through krci pipelinerun start; there is no --label flag on project build.

Input validation mirrors the portal contract. The project name is validated with the portal's own pattern and 253-char cap (new cmdutil.ValidateK8sName; existing validators either accept dots or cap at 63). --branch is bounded the same way; an empty value equals omitting the flag. A test pins the pattern, name cap and branch bounds to the vendored spec.

Build guard semantics are documented, not promised. The portal refuses while any build run of the branch is still active, stricter than the Portal button's latest-run check. It is list-then-create, not a platform guarantee. --dry-run renders even when the branch is not ready or a build is running.

Shared render and error paths. The row, dry-run, JSON and stderr-note glue that pipelinerun start owned moved to pkg/cmd/internal/pipelinerun and is used by both commands; pipelinerun start output is byte-identical (golden tests). Fastify's route-not-found body is now recognised in the shared response checker, so every verb reports upgrade the portal on an older portal instead of a misleading not-found. The 4xx/5xx tail both checkers duplicated is one function.

Vendored spec. internal/portal/openapi/spec.json gains the POST /v1/pipelineruns/build operation, byte-identical to the portal document, and make generate reproduces the committed client. The operation was spliced in rather than the file replaced: the portal document and the vendored spec have drifted in both directions (SCA and Sonar operations exist only here, custom-runs only there, start declares different statuses). Reconciling that is a separate ticket.

Docs and e2e. docs/project.md gains the project build section (flags, managed params, dry-run deltas, guard semantics, full error table pinned to the code by a test); docs/pipelinerun.md and README.md point project builds at the new verb. e2e/project/test-cases.md gains PROJ-B-01..13 (help, validation, dry-run, create, reason mapping with and without --branch).

Verification. make build, go vet, go test -race ./... green; zero lint findings in changed packages (make lint still fails only on a pre-existing staticcheck finding in the SCA tests, unrelated). Live against a portal running the merged portal change, with a GitLab-hosted library project: dry-run rendered the fully resolved manifest; managed-param, unknown-project and unknown-branch rejections behaved as tabled with no run created; a non-managed --param merged; a real build was created and succeeded; a second call while it ran was refused with the default-branch wording, and with the branch wording when --branch was passed; dry-run still rendered during the run; the run appeared in pipelinerun list --project. Against a portal without the route, project build reports upgrade the portal and every existing verb is unchanged.

Out of scope. GitLab CI projects (no REST trigger yet). Building a specific commit (--revision). krci project security. e2e rows PROJ-B-07..13 need the configured e2e portal on a release that includes the portal change.

@SergK SergK changed the title Feat/epmdedp 17346 project build EPMDEDP-17346: feat: Add krci project build command Sep 17, 2026
@SergK

SergK commented Sep 17, 2026

Copy link
Copy Markdown
Member Author

/ok-to-test

@SergK
SergK force-pushed the feat/EPMDEDP-17346-project-build branch 2 times, most recently from f582822 to c081d1b Compare September 17, 2026 15:12
… sca tests

Two sca tests dereference a flag pointer right after a nil guard that ends
in a fatal call. The pinned linter, running on Go 1.26, no longer treats
that fatal call as terminating and reports a possible nil dereference, so
`make lint` fails on every commit, main included.

A shared test helper returns the usage text and fails the test when the
flag is missing, with no dereference on the nil path. Both tests read the
usage through it.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
…helper

Four command groups carried their own copy of the same name validator, and
three of them capped names at 63 characters while their message promised
253. The portal applies one rule to every user-typed resource name:
lowercase alphanumerics and hyphens, no dots, at most 253 characters.

One shared validator now implements that rule. Each command passes its own
argument placeholder at the call site; the per-group wrappers that only
forwarded a placeholder are removed, along with the tests that repeated the
boundary cases through them. The unused subdomain check is removed.

Names of 64 to 253 characters are accepted everywhere, matching the portal.
Nothing that passed before is rejected. Docs and e2e contracts carry the
unified wording.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
…mand groups

Go's internal-package rule limited the pipelinerun helpers to the
pipelinerun verbs, so a command in another group could not reuse the row
and dry-run renderers, the output and dry-run validation, or the schema
version. The package moves next to the other cross-group helpers, and the
start renderers move into it behind a single entry point that writes the
dry-run manifest, the JSON envelope, or the table row with its post-create
note.

Only that entry point is exported; the renderers stay package-private.
Golden tests pin the table row, YAML, and JSON envelope byte-for-byte, so
`pipelinerun start` output is unchanged.

Two helpers every command group re-typed are now shared: a generic test
runner that executes a command and returns its validated options, and a
single empty-table-cell placeholder used by the pipelinerun row and the
discovery cells.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
The start endpoint mapped status codes and reason tags with a hand-written
switch, and every endpoint with stable reason tags would have repeated it.
The portal client now owns that mapping once: a per-endpoint table pairs
each reason tag with its sentinel and message format, and one function
applies the same order for every endpoint: a known reason, then a 404 as
either a missing route or a missing resource, then admission-class 4xx and
5xx, then the generic status mapping.

The generic mapping gains the 403 case, so every service reports
"permission denied" instead of a raw status line. A 404 whose body is the
web framework's route-not-found response maps to a distinct "upgrade the
portal" error for every service. The not-found-only error constructor was
an alias of the general one and is removed.

The malformed TriggerTemplate message drops its "platform rejected
request:" prefix, matching every other synthesised message; the error
still matches its sentinel. A test pins the start error table in the docs
to what the mapping renders.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
…peration

`krci project build` needs a typed client for POST /rest/v1/pipelineruns/build,
which exists in the portal spec only after the build procedure landed there.

The portal document is not a superset of the vendored spec: it lacks the
sonar and sca operations the CLI generates clients from, and it inlines the
start response schemas the vendored document keeps as components. A
wholesale copy would delete working client code, so the build operation is
spliced in verbatim, byte-identical to the portal document, and the client
regenerated. Every existing type and method is untouched.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
`project build` needs a typed caller for POST /rest/v1/pipelineruns/build
and a translation of the portal's stable reason tags into messages a user
can act on. The service maps its thirteen reason tags through the shared
table, including the reserved-parameter rejection, which names the
offending parameter. The portal never echoes resource names, so every
message is built from the request; with --branch omitted the resolved
default branch is unknown and the message says "the default branch".

The reserved parameter set and the branch length bound mirror the request
schema the portal publishes, so the command can reject them before any
network call.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
Building a project branch from the terminal meant knowing the Tekton
pipeline name, the branch resource name, and the full parameter table,
then reproducing them by hand with `krci pipelinerun start`; the resulting
run carried none of the identity labels, so it was invisible to
`pipelinerun list --project`. `krci project build <name>` reproduces the
portal's Build button instead: the portal resolves the pipeline,
parameters, labels, and service account from the project and the branch.

Parameters derived from project identity and source are rejected locally,
before any network call, with the name of the offending parameter; --label
is not offered because labels are what the command resolves. --dry-run
renders the manifest even when the branch is not ready or a build is
running, so the manifest can be inspected without a create. One test pins
the reserved-parameter set, the branch bound, and the codebase name rule to
the vendored document; two more pin the docs error table and the
reserved-parameter list to what the service renders and rejects.

Signed-off-by: Sergiy Kulanov <sergiy_kulanov@epam.com>
@SergK
SergK force-pushed the feat/EPMDEDP-17346-project-build branch from c081d1b to b8db007 Compare September 17, 2026 15:49
@SergK
SergK merged commit 09106f4 into main Sep 17, 2026
2 checks passed
@SergK
SergK deleted the feat/EPMDEDP-17346-project-build branch September 17, 2026 15:52
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.

Add high-level "project build" command

1 participant