Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .claude/commands/krci-ai/go-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# /go-dev Command

CRITICAL: Carefully read the YAML agent definition below. Immediately activate the Go Developer persona by following the activation instructions, and remain in this persona until you receive an explicit command to exit.

```yaml
agent:
identity:
name: "Go Developer"
id: go-developer-v1
version: "1.0.0"
description: "Go developer for Go code implementation/debugging. Redirects requirements→PM/PO, architecture→architect, other languages→dev agents."
role: "Go Developer"
goal: "Implement clean, efficient Go code within Go dev scope"
icon: "💻"

activation_prompt:
- Greet the user with your name and role, inform of available commands, then HALT to await instruction
- Offer to help with tasks but wait for explicit user confirmation
- Always show tasks as numbered options list
- IMPORTANT!!! ALWAYS execute instructions from the customization field below
- Only execute tasks when user explicitly requests them
- NEVER validate unused commands or proceed with broken references
- CRITICAL!!! Before running a task, resolve and load all paths in the task's YAML frontmatter `dependencies` under {project_root}/.krci-ai/{agents,tasks,data,templates}/**/*.md. If any file is missing, report exact path(s) and HALT until the user resolves or explicitly authorizes continuation.

principles:
- "SCOPE: Go code implementation + Go code reviews. Redirect requirements→PM/PO, architecture→architect, other languages→dev."
- "CRITICAL OUTPUT FORMATTING: When generating documents from templates, you will encounter XML-style tags like `<instructions>` or `<key_risks>`. These tags are internal metadata for your guidance ONLY and MUST NEVER be included in the final Markdown output presented to the user. Your final output must be clean, human-readable Markdown containing only headings, paragraphs, lists, and other standard elements."
- "Write clean, readable Go code following established patterns"
- "Test thoroughly with comprehensive coverage"
- "Document clearly for maintainability"
- "Handle errors gracefully and provide meaningful feedback"

customization: ""

commands:
help: "Show available commands"
chat: "(Default) Development consultation and code assistance"
implement-new-cr: "Implement Kubernetes Custom Resource"
review-code: "Review code for best practices"
exit: "Exit Go Developer persona and return to normal mode"

tasks:
- ./.krci-ai/tasks/go-dev-implement-new-cr.md
- ./.krci-ai/tasks/go-dev-review-code.md
```
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,5 @@ tags
.idea/*
# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
.DS_Store

.claude/settings.local.json
30 changes: 6 additions & 24 deletions .krci-ai/tasks/go-dev-implement-new-cr.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,15 @@ dependencies:

This guide provides a comprehensive prompt for LLM to implement a new Kubernetes Custom Resource.

## Prerequisites
## Instructions

<prerequisites>
IMPORTANT: Before starting implementation, you must read and fully understand the following documentation:
<instructions>
BEFORE ANY IMPLEMENTATION confirm you have read and fully understand [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) to apply ALL Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices. Ensure dependencies declared in the YAML frontmatter are readable before proceeding.

1. [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) - Apply ALL the Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices defined in this document.
</prerequisites>
CRITICAL FIRST STEP: You MUST run the `make operator-sdk create api` command first to scaffold the proper structure before manually creating any files. See Step 1.0 in the Implementation Steps below for detailed instructions.

## ⚠️ CRITICAL FIRST STEP

<critical_first_step>
BEFORE ANY IMPLEMENTATION: You MUST run the `make operator-sdk create api` command first to scaffold the proper structure. See Step 1.0 below for detailed instructions on how to do this.

DO NOT manually create files before running this command!
</critical_first_step>

## Overview

<overview>
You are tasked with implementing a new Kubernetes Custom Resource for the `your-operator` project. This operator follows the chain of responsibility pattern for handling reconciliation logic.
</overview>

## Implementation Steps

<implementation_steps>
Follow the [Operator SDK Tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/) as the foundation for implementing your controller.
</implementation_steps>
You are tasked with implementing a new Kubernetes Custom Resource for the `your-operator` project following the chain of responsibility pattern for handling reconciliation logic. Follow the [Operator SDK Tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/) as the foundation for implementing your controller.
</instructions>

### 1 Scaffold API and Controller

Expand Down
22 changes: 5 additions & 17 deletions .krci-ai/tasks/go-dev-review-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,14 @@ dependencies:

You are an expert Go developer and Kubernetes operator specialist tasked with reviewing Go code for quality, best practices, and adherence to standards.

## Prerequisites
## Instructions

<prerequisites>
IMPORTANT: Before starting your review, you must read and fully understand the following documentation:
<instructions>
Confirm you have read and fully understand [Go Coding Standards](./.krci-ai/data/go-coding-standards.md) to apply ALL Go development standards, best practices, naming conventions, error handling patterns, testing guidelines, and security practices. Read [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) to apply ALL Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices. Ensure dependencies declared in the YAML frontmatter are readable before proceeding. Your review must be based on the standards and practices outlined in these documents.

1. Read [Go Coding Standards](./.krci-ai/data/go-coding-standards.md) - Apply ALL the Go development standards, best practices, naming conventions, error handling patterns, testing guidelines, and security practices defined in this document.

2. Read [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) - Apply ALL the Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices defined in this document.

Your review must be based on the standards and practices outlined in these documents. Do not proceed without reading them first.
</prerequisites>

## Review Approach

<review_approach>
1. Analyze the code against all standards and practices from the required documentation
2. Identify violations of the established guidelines
3. Provide specific, actionable feedback with clear examples and references to the documentation
</review_approach>
Analyze the code against all standards and practices from the required documentation. Identify violations of the established guidelines. Provide specific, actionable feedback with clear examples and references to the documentation.

</instructions>
## Review Output Format

<review_output_format>
Expand Down
4 changes: 3 additions & 1 deletion controllers/codebase/service/chain/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/epam/edp-codebase-operator/v2/controllers/codebase/service/chain/handler"
"github.com/epam/edp-codebase-operator/v2/pkg/gerrit"
"github.com/epam/edp-codebase-operator/v2/pkg/git"
gitlabci "github.com/epam/edp-codebase-operator/v2/pkg/gitlab"
"github.com/epam/edp-codebase-operator/v2/pkg/gitprovider"
)

Expand All @@ -20,10 +21,11 @@ func MakeChain(ctx context.Context, c client.Client) handler.CodebaseHandler {

ch := &chain{}
gp := &git.GitProvider{}
gitlabCIManager := gitlabci.NewManager(c)

ch.Use(
NewPutGitWebRepoUrl(c),
NewPutProject(c, gp, &gerrit.SSHGerritClient{}, gitprovider.NewGitProjectProvider),
NewPutProject(c, gp, &gerrit.SSHGerritClient{}, gitprovider.NewGitProjectProvider, gitlabCIManager),
NewPutWebHook(c, resty.New()),
NewPutDeployConfigs(c, gp),
NewPutDefaultCodeBaseBranch(c),
Expand Down
32 changes: 31 additions & 1 deletion controllers/codebase/service/chain/put_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1"
"github.com/epam/edp-codebase-operator/v2/pkg/gerrit"
"github.com/epam/edp-codebase-operator/v2/pkg/git"
gitlabci "github.com/epam/edp-codebase-operator/v2/pkg/gitlab"
"github.com/epam/edp-codebase-operator/v2/pkg/gitprovider"
"github.com/epam/edp-codebase-operator/v2/pkg/util"
)
Expand All @@ -26,6 +27,7 @@ type PutProject struct {
git git.Git
gerrit gerrit.Client
gitProjectProvider func(gitServer *codebaseApi.GitServer, token string) (gitprovider.GitProjectProvider, error)
gitlabCIManager gitlabci.Manager
}

var (
Expand All @@ -38,8 +40,9 @@ func NewPutProject(
g git.Git,
gerritProvider gerrit.Client,
gitProjectProvider func(gitServer *codebaseApi.GitServer, token string) (gitprovider.GitProjectProvider, error),
gitlabCIManager gitlabci.Manager,
) *PutProject {
return &PutProject{client: c, git: g, gerrit: gerritProvider, gitProjectProvider: gitProjectProvider}
return &PutProject{client: c, git: g, gerrit: gerritProvider, gitProjectProvider: gitProjectProvider, gitlabCIManager: gitlabCIManager}
}

func (h *PutProject) ServeRequest(ctx context.Context, codebase *codebaseApi.Codebase) error {
Expand Down Expand Up @@ -149,6 +152,13 @@ func (h *PutProject) createProject(
return err
}

// Inject GitLab CI configuration if needed
if codebase.Spec.CiTool == util.CIGitLab {
if err := h.injectGitLabCIConfig(ctx, codebase, workDir); err != nil {
return fmt.Errorf("failed to inject GitLab CI config: %w", err)
}
}

err = h.setDefaultBranch(ctx, gitServer, codebase, gitProviderToken, privateSSHKey)
if err != nil {
return err
Expand Down Expand Up @@ -498,6 +508,26 @@ func (h *PutProject) notEmptyProjectProvisioning(ctx context.Context, codebase *
return nil
}

// injectGitLabCIConfig injects GitLab CI configuration.
func (h *PutProject) injectGitLabCIConfig(
ctx context.Context,
codebase *codebaseApi.Codebase,
workDir string,
) error {
log := ctrl.LoggerFrom(ctx)

log.Info("Injecting GitLab CI configuration")

// Use the GitLab CI manager to create the config file
if err := h.gitlabCIManager.InjectGitLabCIConfig(ctx, codebase, workDir); err != nil {
return fmt.Errorf("failed to create GitLab CI config: %w", err)
}

log.Info("GitLab CI configuration injected successfully")

return nil
}

func setFailedFields(c *codebaseApi.Codebase, a codebaseApi.ActionType, message string) {
// Set WebHookRef from WebHookID for backward compatibility.
webHookRef := c.Status.WebHookRef
Expand Down
76 changes: 76 additions & 0 deletions controllers/codebase/service/chain/put_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
gerritmocks "github.com/epam/edp-codebase-operator/v2/pkg/gerrit/mocks"
"github.com/epam/edp-codebase-operator/v2/pkg/git"
gitmocks "github.com/epam/edp-codebase-operator/v2/pkg/git/mocks"
gitlabci "github.com/epam/edp-codebase-operator/v2/pkg/gitlab"
"github.com/epam/edp-codebase-operator/v2/pkg/gitprovider"
gitprovidermock "github.com/epam/edp-codebase-operator/v2/pkg/gitprovider/mocks"
"github.com/epam/edp-codebase-operator/v2/pkg/util"
Expand Down Expand Up @@ -1228,6 +1229,80 @@ func TestPutProject_ServeRequest(t *testing.T) {
assert.Equal(t, util.ProjectTemplatesPushedStatus, status.Git)
},
},
{
name: "gitlab with gitlab ci - should inject gitlab ci config successfully",
codebase: &codebaseApi.Codebase{
ObjectMeta: metav1.ObjectMeta{
Name: "java-app",
Namespace: defaultNs,
},
Spec: codebaseApi.CodebaseSpec{
Strategy: codebaseApi.Create,
GitServer: gitlabGitServer.Name,
GitUrlPath: "/owner/java-repo",
DefaultBranch: "master",
Lang: "java",
BuildTool: "maven",
CiTool: util.CIGitLab,
},
},
objects: []client.Object{
gitlabGitServer,
gitlabGitServerSecret,
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "gitlab-ci-java-maven",
Namespace: defaultNs,
},
Data: map[string]string{
".gitlab-ci.yml": "variables:\n CODEBASE_NAME: \"{{.CodebaseName}}\"\ninclude:\n - component: $CI_SERVER_FQDN/kuberocketci/ci-java17-mvn/build@0.1.1",
},
},
},
gitClient: func(t *testing.T) git.Git {
mock := gitmocks.NewMockGit(t)

mock.On("CheckPermissions", testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(true).
On("CloneRepository", testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(nil).
On("Init", testify.Anything).
Return(nil).
On("CommitChanges", testify.Anything, testify.Anything).
Return(nil).
On("GetCurrentBranchName", testify.Anything).
Return("feature", nil).
On("Checkout", testify.Anything, testify.Anything, testify.Anything, testify.Anything, false).
Return(nil).
On("AddRemoteLink", testify.Anything, testify.Anything).
Return(nil).
On("PushChanges", testify.Anything, testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(nil)

return mock
},
gerritClient: func(t *testing.T) gerrit.Client {
return gerritmocks.NewMockClient(t)
},
gitProvider: func(t *testing.T) func(gitServer *codebaseApi.GitServer, token string) (gitprovider.GitProjectProvider, error) {
mock := gitprovidermock.NewMockGitProjectProvider(t)

mock.On("ProjectExists", testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(false, nil).
On("CreateProject", testify.Anything, testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(nil).
On("SetDefaultBranch", testify.Anything, testify.Anything, testify.Anything, testify.Anything, testify.Anything).
Return(nil)

return func(gitServer *codebaseApi.GitServer, token string) (gitprovider.GitProjectProvider, error) {
return mock, nil
}
},
wantErr: require.NoError,
wantStatus: func(t *testing.T, status *codebaseApi.CodebaseStatus) {
assert.Equal(t, util.ProjectPushedStatus, status.Git)
},
},
}

for _, tt := range tests {
Expand All @@ -1247,6 +1322,7 @@ func TestPutProject_ServeRequest(t *testing.T) {
tt.gitClient(t),
tt.gerritClient(t),
tt.gitProvider(t),
gitlabci.NewManager(k8sClient),
)

err := h.ServeRequest(ctrl.LoggerInto(context.Background(), logr.Discard()), tt.codebase)
Expand Down
62 changes: 62 additions & 0 deletions docs/gitlabci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# GitLab CI ConfigMap Templates

This directory contains example ConfigMaps for GitLab CI template injection. These ConfigMaps replace the hard-coded component URLs in the GitLab CI manager, allowing for runtime updates without operator rebuilds.

## ConfigMap Naming Convention

ConfigMaps follow a predictable naming pattern:

- Specific: `gitlab-ci-{language}-{buildtool}` (e.g., `gitlab-ci-java-maven`)
- Language fallback: `gitlab-ci-{language}` (e.g., `gitlab-ci-java`)
- Default fallback: `gitlab-ci-default`

## Template Structure

Each ConfigMap contains a `.gitlab-ci.yml` template with:

- Workflow rules for merge requests, protected branches, and semantic version tags
- Global variables customizable per project type
- Component inclusion with conditional pipeline execution
- Template substitution using `{{.CodebaseName}}` placeholder

## Deployment

Apply the ConfigMaps to your cluster:

```bash
kubectl apply -f gitlab-ci-java-maven.yaml
kubectl apply -f gitlab-ci-go.yaml
kubectl apply -f gitlab-ci-default.yaml
```

## Version Updates

To update CI component versions, simply patch the ConfigMap:

```bash
# Update Java Maven CI component from 0.1.1 to 0.1.2
kubectl patch configmap gitlab-ci-java-maven -n krci --type='merge' -p='
{
"data": {
".gitlab-ci.yml": "... updated template with @0.1.2 ..."
}
}'
```

## Fallback Hierarchy

The GitLab CI manager uses this fallback hierarchy:

1. Specific ConfigMap: `gitlab-ci-java-maven`
2. Language ConfigMap: `gitlab-ci-java`
3. Default ConfigMap: `gitlab-ci-default`

This ensures graceful handling of unsupported language/build tool combinations.

## Benefits

- No operator rebuilds for CI component updates
- Runtime template updates via ConfigMap changes
- Kubernetes-native configuration management
- Version control of CI templates outside operator
- Easy customization per environment/organization
Loading