feat: [ML-302]: Framework For Harness Intelligence APIs (#2547)

* feat: [ML-302]: Framework For Harness Intelligence APIs
pull/3545/head
Yogesh Chauhan 2024-08-20 22:05:29 +00:00 committed by Harness
parent fcfa540f7c
commit 59614e73f4
60 changed files with 2521 additions and 125 deletions

View File

@ -0,0 +1,59 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) GetAnalysis(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
executionNum int64,
) (*types.AnalyseExecutionOutput, error) {
repo, err := c.repoStore.FindByRef(ctx, repoRef)
if err != nil {
return nil, usererror.BadRequestf("failed to find repo %s", repoRef)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, repo.Path, pipelineIdentifier, enum.PermissionPipelineView)
if err != nil {
return nil, usererror.Forbidden(fmt.Sprintf("not allowed to view pipeline %s", pipelineIdentifier))
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, usererror.BadRequestf("failed to find pipeline: %s", pipelineIdentifier)
}
execution, err := c.executionStore.FindByNumber(ctx, pipeline.ID, executionNum)
if err != nil {
return nil, usererror.BadRequestf("failed to find execution %d", executionNum)
}
if execution.Status == enum.CIStatusSuccess {
return nil, usererror.BadRequestf("execution %d is not a failed execution", executionNum)
}
return &types.AnalyseExecutionOutput{Yaml: "a:1"}, nil
}

View File

@ -0,0 +1,45 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/aiagent"
"github.com/harness/gitness/app/store"
)
type Controller struct {
authorizer authz.Authorizer
pipeline *aiagent.HarnessIntelligence
repoStore store.RepoStore
pipelineStore store.PipelineStore
executionStore store.ExecutionStore
}
func NewController(
authorizer authz.Authorizer,
pipeline *aiagent.HarnessIntelligence,
repoStore store.RepoStore,
pipelineStore store.PipelineStore,
executionStore store.ExecutionStore,
) *Controller {
return &Controller{
authorizer: authorizer,
pipeline: pipeline,
repoStore: repoStore,
pipelineStore: pipelineStore,
executionStore: executionStore,
}
}

View File

@ -0,0 +1,48 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"context"
"fmt"
"github.com/harness/gitness/types"
)
type GeneratePipelineInput struct {
Prompt string `json:"prompt"`
RepoRef string `json:"repo_ref"`
}
type GeneratePipelineOutput struct {
Yaml string `json:"yaml"`
}
func (c *Controller) GeneratePipeline(
ctx context.Context,
in *GeneratePipelineInput,
) (*GeneratePipelineOutput, error) {
generateRequest := &types.PipelineGenerateRequest{
Prompt: in.Prompt,
RepoRef: in.RepoRef,
}
output, err := c.pipeline.Generate(ctx, generateRequest)
if err != nil {
return nil, fmt.Errorf("generate pipeline: %w", err)
}
return &GeneratePipelineOutput{
Yaml: output.YAML,
}, nil
}

View File

@ -0,0 +1,42 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"context"
"fmt"
"github.com/harness/gitness/types"
)
type SuggestPipelineInput struct {
RepoRef string `json:"repo_ref"`
Pipeline string `json:"pipeline"`
}
func (c *Controller) SuggestPipeline(
ctx context.Context,
in *SuggestPipelineInput,
) (*types.PipelineSuggestionsResponse, error) {
suggestionRequest := &types.PipelineSuggestionsRequest{
RepoRef: in.RepoRef,
Pipeline: in.Pipeline,
}
output, err := c.pipeline.Suggest(ctx, suggestionRequest)
if err != nil {
return nil, fmt.Errorf("suggest pipeline: %w", err)
}
return output, nil
}

View File

@ -0,0 +1,44 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"context"
"fmt"
"github.com/harness/gitness/types"
)
type UpdatePipelineInput struct {
Prompt string `json:"prompt"`
RepoRef string `json:"repo_ref"`
Pipeline string `json:"pipeline"`
}
func (c *Controller) UpdatePipeline(
ctx context.Context,
in *UpdatePipelineInput,
) (string, error) {
generateRequest := &types.PipelineUpdateRequest{
Prompt: in.Prompt,
RepoRef: in.RepoRef,
Pipeline: in.Pipeline,
}
output, err := c.pipeline.Update(ctx, generateRequest)
if err != nil {
return "", fmt.Errorf("update pipeline: %w", err)
}
return output.YAML, nil
}

View File

@ -0,0 +1,44 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/aiagent"
"github.com/harness/gitness/app/store"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideController,
)
func ProvideController(
authorizer authz.Authorizer,
aiagentPipeline *aiagent.HarnessIntelligence,
repoStore store.RepoStore,
pipelineStore store.PipelineStore,
executionStore store.ExecutionStore,
) *Controller {
return NewController(
authorizer,
aiagentPipeline,
repoStore,
pipelineStore,
executionStore,
)
}

View File

@ -0,0 +1,31 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"github.com/harness/gitness/app/services/capabilities"
)
type Controller struct {
Capabilities *capabilities.Registry
}
func NewController(
capabilities *capabilities.Registry,
) *Controller {
return &Controller{
Capabilities: capabilities,
}
}

View File

@ -0,0 +1,77 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"context"
"fmt"
"github.com/harness/gitness/types/capabilities"
)
type ContextID string
type RunCapabilitiesRequest struct {
ConversationRaw string `json:"conversation_raw"`
ConversationID ContextID `json:"conversation_id"`
CapabilitiesToRun []CapabilityRunRequest `json:"capabilities_to_run"`
}
type CapabilityRunRequest struct {
CallID string `json:"call_id"`
Type capabilities.Type `json:"type"`
Input capabilities.Input `json:"input"`
}
type CapabilityExecution struct {
Type capabilities.Type `json:"capability_id"`
Result capabilities.Output `json:"result"`
ReturnToUser bool `json:"return_to_user"`
}
func (c CapabilityExecution) GetType() capabilities.AIContextPayloadType {
return "other"
}
type CapabilityRunResponse struct {
CapabilitiesRan []CapabilityExecution `json:"capabilities_ran"`
}
func (c *Controller) RunCapabilities(ctx context.Context, req *RunCapabilitiesRequest) (*CapabilityRunResponse, error) {
capOut := new(CapabilityRunResponse)
capOut.CapabilitiesRan = []CapabilityExecution{}
for _, value := range req.CapabilitiesToRun {
if !c.Capabilities.Exists(value.Type) {
return nil, fmt.Errorf("capability %s does not exist", value.Type)
}
resp, err := c.Capabilities.Execute(ctx, value.Type, value.Input)
if err != nil {
return nil, err
}
returnToUser, err := c.Capabilities.ReturnToUser(value.Type)
if err != nil {
return nil, err
}
capOut.CapabilitiesRan = append(capOut.CapabilitiesRan, CapabilityExecution{
Type: value.Type,
Result: resp,
ReturnToUser: returnToUser,
})
}
return capOut, nil
}

View File

@ -0,0 +1,35 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"github.com/harness/gitness/app/services/capabilities"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideController,
)
func ProvideController(
capabilities *capabilities.Registry,
) *Controller {
return NewController(
capabilities,
)
}

View File

@ -0,0 +1,59 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) GetAnalysis(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
executionNum int64,
) (*types.AnalyseExecutionOutput, error) {
repo, err := c.repoStore.FindByRef(ctx, repoRef)
if err != nil {
return nil, usererror.BadRequestf("failed to find repo %s", repoRef)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, repo.Path, pipelineIdentifier, enum.PermissionPipelineView)
if err != nil {
return nil, usererror.Forbidden(fmt.Sprintf("not allowed to view pipeline %s", pipelineIdentifier))
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, usererror.BadRequestf("failed to find pipeline: %s", pipelineIdentifier)
}
execution, err := c.executionStore.FindByNumber(ctx, pipeline.ID, executionNum)
if err != nil {
return nil, usererror.BadRequestf("failed to find execution %d", executionNum)
}
if execution.Status == enum.CIStatusSuccess {
return nil, usererror.BadRequestf("execution %d is not a failed execution", executionNum)
}
return &types.AnalyseExecutionOutput{Yaml: "a:1"}, nil
}

View File

@ -16,6 +16,7 @@ package pipeline
import (
"github.com/harness/gitness/app/auth/authz"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/app/store"
)
@ -25,6 +26,7 @@ type Controller struct {
triggerStore store.TriggerStore
authorizer authz.Authorizer
pipelineStore store.PipelineStore
reporter events.Reporter
}
func NewController(
@ -32,11 +34,13 @@ func NewController(
repoStore store.RepoStore,
triggerStore store.TriggerStore,
pipelineStore store.PipelineStore,
reporter events.Reporter,
) *Controller {
return &Controller{
repoStore: repoStore,
triggerStore: triggerStore,
authorizer: authorizer,
pipelineStore: pipelineStore,
reporter: reporter,
}
}

View File

@ -23,6 +23,7 @@ import (
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
@ -107,6 +108,9 @@ func (c *Controller) Create(
log.Ctx(ctx).Err(err).Msg("failed to create auto trigger on pipeline creation")
}
// send pipeline create event
c.reporter.Created(ctx, &events.CreatedPayload{PipelineID: pipeline.ID, RepoID: pipeline.RepoID})
return pipeline, nil
}

View File

@ -21,6 +21,7 @@ import (
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
@ -60,7 +61,7 @@ func (c *Controller) Update(
return nil, fmt.Errorf("failed to find pipeline: %w", err)
}
return c.pipelineStore.UpdateOptLock(ctx, pipeline, func(pipeline *types.Pipeline) error {
updated, err := c.pipelineStore.UpdateOptLock(ctx, pipeline, func(pipeline *types.Pipeline) error {
if in.Identifier != nil {
pipeline.Identifier = *in.Identifier
}
@ -76,6 +77,11 @@ func (c *Controller) Update(
return nil
})
// send pipeline update event
c.reporter.Updated(ctx, &events.UpdatedPayload{PipelineID: pipeline.ID, RepoID: pipeline.RepoID})
return updated, err
}
func (c *Controller) sanitizeUpdateInput(in *UpdateInput) error {

View File

@ -16,6 +16,7 @@ package pipeline
import (
"github.com/harness/gitness/app/auth/authz"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/app/store"
"github.com/google/wire"
@ -31,11 +32,13 @@ func ProvideController(
triggerStore store.TriggerStore,
authorizer authz.Authorizer,
pipelineStore store.PipelineStore,
reporter *events.Reporter,
) *Controller {
return NewController(
authorizer,
repoStore,
triggerStore,
pipelineStore,
*reporter,
)
}

View File

@ -0,0 +1,64 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package space
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListPipelines lists the pipelines in a space.
func (c *Controller) ListPipelines(
ctx context.Context,
session *auth.Session,
spaceRef string,
filter types.ListQueryFilter,
) ([]*types.Pipeline, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, 0, fmt.Errorf("failed to find space: %w", err)
}
if err := apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionPipelineView); err != nil {
return nil, 0, fmt.Errorf("access check failed: %w", err)
}
var count int64
var pipelines []*types.Pipeline
err = c.tx.WithTx(ctx, func(ctx context.Context) (err error) {
count, err = c.pipelineStore.CountInSpace(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to count pipelines in space: %w", err)
}
pipelines, err = c.pipelineStore.ListInSpace(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to list pipelines in space: %w", err)
}
return
}, dbtx.TxDefaultReadOnly)
if err != nil {
return pipelines, count, fmt.Errorf("failed to list pipelines in space: %w", err)
}
return pipelines, count, nil
}

View File

@ -0,0 +1,65 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/types"
)
func HandleAnalyse(aiagentCtrl *aiagent.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
in := new(types.AnalyseExecutionInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
pipelineIdentifier := in.PipelineIdentifier
if pipelineIdentifier == "" {
render.TranslatedUserError(ctx, w, usererror.BadRequest("pipeline_identifier is required"))
return
}
repoRef := in.RepoRef
if repoRef == "" {
render.TranslatedUserError(ctx, w, usererror.BadRequest("repo_ref is required"))
return
}
executionNumber := in.ExecutionNum
if executionNumber < 1 {
render.TranslatedUserError(ctx, w, usererror.BadRequest("execution_number must be greater than 0"))
return
}
// fetch stored analysis for the given execution, if any
analysis, err := aiagentCtrl.GetAnalysis(ctx, session, repoRef, pipelineIdentifier, in.ExecutionNum)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusCreated, analysis)
}
}

View File

@ -0,0 +1,46 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/render"
)
func HandleGeneratePipeline(aiagentCtrl *aiagent.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
in := new(aiagent.GeneratePipelineInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
pipeline, err := aiagentCtrl.GeneratePipeline(ctx, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
w.Header().Set("Content-Type", "text/yaml; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(pipeline.Yaml))
}
}

View File

@ -0,0 +1,44 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/render"
)
func HandleSuggestPipelines(aiagentCtrl *aiagent.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
in := new(aiagent.SuggestPipelineInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
suggestions, err := aiagentCtrl.SuggestPipeline(ctx, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusOK, suggestions)
}
}

View File

@ -0,0 +1,47 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/render"
)
func HandleUpdatePipeline(aiagentCtrl *aiagent.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// TODO Question: how come we decode body here vs putting that logic in the request package?
in := new(aiagent.UpdatePipelineInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
pipeline, err := aiagentCtrl.UpdatePipeline(ctx, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
w.Header().Set("Content-Type", "text/yaml; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(pipeline))
}
}

View File

@ -0,0 +1,89 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"encoding/json"
"io"
"net/http"
"github.com/harness/gitness/app/api/controller/capabilities"
"github.com/harness/gitness/app/api/render"
capabilitiesservice "github.com/harness/gitness/app/services/capabilities"
capabilities2 "github.com/harness/gitness/types/capabilities"
)
func HandleRunCapabilities(capabilitiesCtrl *capabilities.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
data, err := io.ReadAll(r.Body)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
in, err := UnmarshalRunCapabilitiesRequest(capabilitiesCtrl.Capabilities, data)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
resp, err := capabilitiesCtrl.RunCapabilities(ctx, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusOK, resp)
}
}
// UnmarshalRunCapabilitiesRequest We need this method in order to unmarshall
// the request. We cannot do this in a generic way, because we use the
// capability type in order to deserialize it appropriately (see DeserializeInput,
// as used in this function).
func UnmarshalRunCapabilitiesRequest(cr *capabilitiesservice.Registry,
data []byte) (*capabilities.RunCapabilitiesRequest, error) {
r := &capabilities.RunCapabilitiesRequest{}
type rawCapability struct {
CallID string `json:"call_id"`
Type capabilities2.Type `json:"type"`
Input json.RawMessage
}
var rawBase struct {
ConversationRaw string `json:"conversation_raw"`
ConversationID capabilities.ContextID `json:"conversation_id"`
CapabilitiesToRun []rawCapability `json:"capabilities_to_run"`
}
err := json.Unmarshal(data, &rawBase)
if err != nil {
return nil, err
}
r.CapabilitiesToRun = make([]capabilities.CapabilityRunRequest, 0)
r.ConversationRaw = rawBase.ConversationRaw
r.ConversationID = rawBase.ConversationID
for _, raw := range rawBase.CapabilitiesToRun {
capabilityRequest := new(capabilities.CapabilityRunRequest)
capabilityRequest.CallID = raw.CallID
capabilityRequest.Type = raw.Type
capabilityRequest.Input, err = capabilitiesservice.DeserializeInput(cr, raw.Type, raw.Input)
if err != nil {
return nil, err
}
r.CapabilitiesToRun = append(r.CapabilitiesToRun, *capabilityRequest)
}
return r, nil
}

View File

@ -0,0 +1,44 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package space
import (
"net/http"
"github.com/harness/gitness/app/api/controller/space"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
func HandleListPipelines(spaceCtrl *space.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
spaceRef, err := request.GetSpaceRefFromPath(r)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
filter := request.ParseListQueryFilterFromRequest(r)
repos, totalCount, err := spaceCtrl.ListPipelines(ctx, session, spaceRef, filter)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
render.JSON(w, http.StatusOK, repos)
}
}

View File

@ -0,0 +1,45 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const CreatedEvent events.EventType = "created"
type CreatedPayload struct {
PipelineID int64 `json:"pipeline_id"`
RepoID int64 `json:"repo_id"`
}
func (r *Reporter) Created(ctx context.Context, payload *CreatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, CreatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send pipeline created event")
return
}
log.Ctx(ctx).Debug().Msgf("reported pipeline created event with id '%s'", eventID)
}
func (r *Reader) RegisterCreated(fn events.HandlerFunc[*CreatedPayload],
opts ...events.HandlerOption) error {
return events.ReaderRegisterEvent(r.innerReader, CreatedEvent, fn, opts...)
}

View File

@ -0,0 +1,20 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
const (
// category defines the event category used for this package.
category = "pipeline"
)

View File

@ -0,0 +1,48 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
const ExecutedEvent events.EventType = "executed"
type ExecutedPayload struct {
PipelineID int64 `json:"pipeline_id"`
RepoID int64 `json:"repo_id"`
ExecutionNum int64 `json:"execution_number"`
Status enum.CIStatus `json:"status"`
}
func (r *Reporter) Executed(ctx context.Context, payload *ExecutedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, ExecutedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send pipeline executed event")
return
}
log.Ctx(ctx).Debug().Msgf("reported pipeline executed event with id '%s'", eventID)
}
func (r *Reader) RegisterExecuted(fn events.HandlerFunc[*ExecutedPayload],
opts ...events.HandlerOption) error {
return events.ReaderRegisterEvent(r.innerReader, ExecutedEvent, fn, opts...)
}

View File

@ -0,0 +1,38 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import "github.com/harness/gitness/events"
func NewReaderFactory(eventsSystem *events.System) (*events.ReaderFactory[*Reader], error) {
readerFactoryFunc := func(innerReader *events.GenericReader) (*Reader, error) {
return &Reader{
innerReader: innerReader,
}, nil
}
return events.NewReaderFactory(eventsSystem, category, readerFactoryFunc)
}
// Reader is the event reader for this package.
// It exposes typesafe event registration methods for all events by this package.
// NOTE: Event registration methods are in the event's dedicated file.
type Reader struct {
innerReader *events.GenericReader
}
func (r *Reader) Configure(opts ...events.ReaderOption) {
r.innerReader.Configure(opts...)
}

View File

@ -0,0 +1,39 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import (
"errors"
"github.com/harness/gitness/events"
)
// Reporter is the event reporter for this package.
// It exposes typesafe send methods for all events of this package.
// NOTE: Event send methods are in the event's dedicated file.
type Reporter struct {
innerReporter *events.GenericReporter
}
func NewReporter(eventsSystem *events.System) (*Reporter, error) {
innerReporter, err := events.NewReporter(eventsSystem, category)
if err != nil {
return nil, errors.New("failed to create new GenericReporter from event system")
}
return &Reporter{
innerReporter: innerReporter,
}, nil
}

View File

@ -0,0 +1,45 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const UpdatedEvent events.EventType = "updated"
type UpdatedPayload struct {
PipelineID int64 `json:"pipeline_id"`
RepoID int64 `json:"repo_id"`
}
func (r *Reporter) Updated(ctx context.Context, payload *UpdatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, UpdatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send pipeline updated event")
return
}
log.Ctx(ctx).Debug().Msgf("reported pipeline update event with id '%s'", eventID)
}
func (r *Reader) RegisterUpdated(fn events.HandlerFunc[*UpdatedPayload],
opts ...events.HandlerOption) error {
return events.ReaderRegisterEvent(r.innerReader, UpdatedEvent, fn, opts...)
}

View File

@ -0,0 +1,35 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package events
import (
"github.com/harness/gitness/events"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideReaderFactory,
ProvideReporter,
)
func ProvideReaderFactory(eventsSystem *events.System) (*events.ReaderFactory[*Reader], error) {
return NewReaderFactory(eventsSystem)
}
func ProvideReporter(eventsSystem *events.System) (*Reporter, error) {
return NewReporter(eventsSystem)
}

View File

@ -163,8 +163,8 @@ func Parse(
// Since we want to maintain compatibility with drone, the older format
// needs to be maintained (even if the variables do not exist in gitness).
func mapBuild(v *types.Execution, vm *jsonnet.VM) {
vm.ExtVar(build+"event", v.Event)
vm.ExtVar(build+"action", v.Action)
vm.ExtVar(build+"event", string(v.Event))
vm.ExtVar(build+"action", string(v.Action))
vm.ExtVar(build+"environment", v.Deploy)
vm.ExtVar(build+"link", v.Link)
vm.ExtVar(build+"branch", v.Target)

View File

@ -177,8 +177,8 @@ func ConvertToDroneBuild(execution *types.Execution) *drone.Build {
Parent: execution.Parent,
Status: string(execution.Status),
Error: execution.Error,
Event: execution.Event,
Action: execution.Action,
Event: string(execution.Event),
Action: string(execution.Action),
Link: execution.Link,
Timestamp: execution.Timestamp,
Title: execution.Title,

View File

@ -23,6 +23,7 @@ import (
"time"
"github.com/harness/gitness/app/bootstrap"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/app/jwt"
"github.com/harness/gitness/app/pipeline/converter"
"github.com/harness/gitness/app/pipeline/file"
@ -152,6 +153,8 @@ type Manager struct {
// Webhook store.WebhookSender
publicAccess publicaccess.Service
// events reporter
reporter events.Reporter
}
func New(
@ -172,6 +175,7 @@ func New(
stepStore store.StepStore,
userStore store.PrincipalStore,
publicAccess publicaccess.Service,
reporter events.Reporter,
) *Manager {
return &Manager{
Config: config,
@ -191,6 +195,7 @@ func New(
Steps: stepStore,
Users: userStore,
publicAccess: publicAccess,
reporter: reporter,
}
}
@ -481,6 +486,7 @@ func (m *Manager) AfterStage(_ context.Context, stage *types.Stage) error {
Scheduler: m.Scheduler,
Steps: m.Steps,
Stages: m.Stages,
Reporter: m.reporter,
}
return t.do(noContext, stage)
}

View File

@ -20,6 +20,7 @@ import (
"strings"
"time"
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/app/pipeline/checks"
"github.com/harness/gitness/app/pipeline/scheduler"
"github.com/harness/gitness/app/sse"
@ -43,6 +44,7 @@ type teardown struct {
Repos store.RepoStore
Steps store.StepStore
Stages store.StageStore
Reporter events.Reporter
}
//nolint:gocognit // refactor if needed.
@ -174,6 +176,9 @@ func (t *teardown) do(ctx context.Context, stage *types.Stage) error {
Msg("manager: could not publish execution completed event")
}
// send pipeline execution status
t.reportExecutionCompleted(ctx, execution)
pipeline, err := t.Pipelines.Find(ctx, execution.PipelineID)
if err != nil {
log.Error().Err(err).Msg("manager: cannot find pipeline")
@ -364,3 +369,12 @@ func (t *teardown) resync(ctx context.Context, stage *types.Stage) error {
stage.Stopped = updated.Stopped
return nil
}
func (t *teardown) reportExecutionCompleted(ctx context.Context, execution *types.Execution) {
t.Reporter.Executed(ctx, &events.ExecutedPayload{
PipelineID: execution.PipelineID,
RepoID: execution.RepoID,
ExecutionNum: execution.Number,
Status: execution.Status,
})
}

View File

@ -15,6 +15,7 @@
package manager
import (
events "github.com/harness/gitness/app/events/pipeline"
"github.com/harness/gitness/app/pipeline/converter"
"github.com/harness/gitness/app/pipeline/file"
"github.com/harness/gitness/app/pipeline/scheduler"
@ -54,9 +55,11 @@ func ProvideExecutionManager(
stepStore store.StepStore,
userStore store.PrincipalStore,
publicAccess publicaccess.Service,
reporter *events.Reporter,
) ExecutionManager {
return New(config, executionStore, pipelineStore, urlProvider, sseStreamer, fileService, converterService,
logStore, logStream, checkStore, repoStore, scheduler, secretStore, stageStore, stepStore, userStore, publicAccess)
logStore, logStream, checkStore, repoStore, scheduler, secretStore,
stageStore, stepStore, userStore, publicAccess, *reporter)
}
// ProvideExecutionClient provides a client implementation to interact with the execution manager.

View File

@ -150,7 +150,7 @@ func (t *triggerer) Trigger(
}
}()
event := string(base.Action.GetTriggerEvent())
event := base.Action.GetTriggerEvent()
repo, err := t.repoStore.Find(ctx, pipeline.RepoID)
if err != nil {
@ -178,7 +178,7 @@ func (t *triggerer) Trigger(
Parent: base.Parent,
Status: enum.CIStatusPending,
Event: event,
Action: string(base.Action),
Action: base.Action,
Link: base.Link,
// Timestamp: base.Timestamp,
Title: trunc(base.Title, 2000),
@ -254,7 +254,7 @@ func (t *triggerer) Trigger(
switch {
case skipBranch(pipeline, base.Target):
log.Info().Str("pipeline", name).Msg("trigger: skipping pipeline, does not match branch")
case skipEvent(pipeline, event):
case skipEvent(pipeline, string(event)):
log.Info().Str("pipeline", name).Msg("trigger: skipping pipeline, does not match event")
case skipAction(pipeline, string(base.Action)):
log.Info().Str("pipeline", name).Msg("trigger: skipping pipeline, does not match action")
@ -557,8 +557,8 @@ func (t *triggerer) createExecutionWithError(
Parent: base.Parent,
Status: enum.CIStatusError,
Error: message,
Event: string(base.Action.GetTriggerEvent()),
Action: string(base.Action),
Event: base.Action.GetTriggerEvent(),
Action: base.Action,
Link: base.Link,
Title: base.Title,
Message: base.Message,

View File

@ -19,6 +19,8 @@ import (
"fmt"
"net/http"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/controller/capabilities"
"github.com/harness/gitness/app/api/controller/check"
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
@ -44,6 +46,8 @@ import (
"github.com/harness/gitness/app/api/controller/user"
"github.com/harness/gitness/app/api/controller/webhook"
"github.com/harness/gitness/app/api/handler/account"
handleraiagent "github.com/harness/gitness/app/api/handler/aiagent"
handlercapabilities "github.com/harness/gitness/app/api/handler/capabilities"
handlercheck "github.com/harness/gitness/app/api/handler/check"
handlerconnector "github.com/harness/gitness/app/api/handler/connector"
handlerexecution "github.com/harness/gitness/app/api/handler/execution"
@ -93,8 +97,8 @@ import (
var (
// terminatedPathPrefixesAPI is the list of prefixes that will require resolving terminated paths.
terminatedPathPrefixesAPI = []string{"/v1/spaces/", "/v1/repos/",
"/v1/secrets/", "/v1/connectors", "/v1/templates/step", "/v1/templates/stage",
"/v1/gitspaces", "/v1/infraproviders", "/v1/migrate/repos"}
"/v1/secrets/", "/v1/connectors", "/v1/templates/step", "/v1/templates/stage", "/v1/gitspaces", "/v1/infraproviders",
"/v1/migrate/repos", "/v1/pipelines"}
)
// NewAPIHandler returns a new APIHandler.
@ -127,6 +131,8 @@ func NewAPIHandler(
infraProviderCtrl *infraprovider.Controller,
migrateCtrl *migrate.Controller,
gitspaceCtrl *gitspace.Controller,
aiagentCtrl *aiagent.Controller,
capabilitiesCtrl *capabilities.Controller,
) http.Handler {
// Use go-chi router for inner routing.
r := chi.NewRouter()
@ -159,7 +165,7 @@ func NewAPIHandler(
setupRoutesV1WithAuth(r, appCtx, config, repoCtrl, repoSettingsCtrl, executionCtrl, triggerCtrl, logCtrl,
pipelineCtrl, connectorCtrl, templateCtrl, pluginCtrl, secretCtrl, spaceCtrl, pullreqCtrl,
webhookCtrl, githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, uploadCtrl,
searchCtrl, gitspaceCtrl, infraProviderCtrl, migrateCtrl)
searchCtrl, gitspaceCtrl, infraProviderCtrl, migrateCtrl, aiagentCtrl, capabilitiesCtrl)
})
})
@ -208,6 +214,8 @@ func setupRoutesV1WithAuth(r chi.Router,
gitspaceCtrl *gitspace.Controller,
infraProviderCtrl *infraprovider.Controller,
migrateCtrl *migrate.Controller,
aiagentCtrl *aiagent.Controller,
capabilitiesCtrl *capabilities.Controller,
) {
setupAccountWithAuth(r, userCtrl, config)
setupSpaces(r, appCtx, spaceCtrl)
@ -216,6 +224,7 @@ func setupRoutesV1WithAuth(r chi.Router,
setupConnectors(r, connectorCtrl)
setupTemplates(r, templateCtrl)
setupSecrets(r, secretCtrl)
setupAiAgent(r, aiagentCtrl, capabilitiesCtrl)
setupUser(r, userCtrl)
setupServiceAccounts(r, saCtrl)
setupPrincipals(r, principalCtrl)
@ -253,6 +262,7 @@ func setupSpaces(
r.Post("/import", handlerspace.HandleImportRepositories(spaceCtrl))
r.Post("/move", handlerspace.HandleMove(spaceCtrl))
r.Get("/spaces", handlerspace.HandleListSpaces(spaceCtrl))
r.Get("/pipelines", handlerspace.HandleListPipelines(spaceCtrl))
r.Get("/repos", handlerspace.HandleListRepos(spaceCtrl))
r.Get("/service-accounts", handlerspace.HandleListServiceAccounts(spaceCtrl))
r.Get("/secrets", handlerspace.HandleListSecrets(spaceCtrl))
@ -504,6 +514,16 @@ func setupTemplates(
})
}
func setupAiAgent(r chi.Router, aiagentCtrl *aiagent.Controller, capabilitiesCtrl *capabilities.Controller) {
r.Route("/harness-intelligence", func(r chi.Router) {
r.Post("/generate-pipeline", handleraiagent.HandleGeneratePipeline(aiagentCtrl))
r.Post("/update-pipeline", handleraiagent.HandleUpdatePipeline(aiagentCtrl))
r.Post("/capabilities", handlercapabilities.HandleRunCapabilities(capabilitiesCtrl))
r.Post("/suggest-pipeline", handleraiagent.HandleSuggestPipelines(aiagentCtrl))
r.Post("/analyse-execution", handleraiagent.HandleAnalyse(aiagentCtrl))
})
}
func setupSecrets(r chi.Router, secretCtrl *secret.Controller) {
r.Route("/secrets", func(r chi.Router) {
// Create takes path and parentId via body, not uri

View File

@ -18,6 +18,8 @@ import (
"context"
"strings"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/controller/capabilities"
"github.com/harness/gitness/app/api/controller/check"
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
@ -100,6 +102,8 @@ func ProvideRouter(
infraProviderCtrl *infraprovider.Controller,
gitspaceCtrl *gitspace.Controller,
migrateCtrl *migrate.Controller,
aiagentCtrl *aiagent.Controller,
capabilitiesCtrl *capabilities.Controller,
urlProvider url.Provider,
openapi openapi.Service,
) *Router {
@ -117,7 +121,7 @@ func ProvideRouter(
authenticator, repoCtrl, repoSettingsCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
secretCtrl, triggerCtrl, connectorCtrl, templateCtrl, pluginCtrl, pullreqCtrl, webhookCtrl,
githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl,
infraProviderCtrl, migrateCtrl, gitspaceCtrl)
infraProviderCtrl, migrateCtrl, gitspaceCtrl, aiagentCtrl, capabilitiesCtrl)
routers[1] = NewAPIRouter(apiHandler)
webHandler := NewWebHandler(config, authenticator, openapi)

View File

@ -0,0 +1,200 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
"context"
"fmt"
capabilitiesctrl "github.com/harness/gitness/app/api/controller/capabilities"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/capabilities"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/genai"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
capabilitytypes "github.com/harness/gitness/types/capabilities"
"github.com/google/uuid"
)
type HarnessIntelligence struct {
repoStore store.RepoStore
git git.Interface
authorizer authz.Authorizer
cr *capabilities.Registry
cc *capabilitiesctrl.Controller
}
type Pipeline interface {
Generate(ctx context.Context, req *types.PipelineGenerateRequest) (*types.PipelineGenerateResponse, error)
}
func capabilityResponseToChatContext(
ranCapabilities *capabilitiesctrl.CapabilityRunResponse) []capabilitytypes.AIContext {
var aiContexts []capabilitytypes.AIContext
for _, value := range ranCapabilities.CapabilitiesRan {
aiContext := capabilitytypes.AIContext{
Type: capabilitytypes.AIContextPayloadType("other"),
Payload: value.Result,
Name: string(value.Type),
}
aiContexts = append(aiContexts, aiContext)
}
return aiContexts
}
func (s *HarnessIntelligence) Generate(
ctx context.Context, req *types.PipelineGenerateRequest) (*types.PipelineGenerateResponse, error) {
if req.RepoRef == "" {
return nil, fmt.Errorf("no repo ref specified")
}
// do permission check on repo here?
repo, err := s.repoStore.FindByRef(ctx, req.RepoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repo by ref: %w", err)
}
conversationID := uuid.New()
chatRequest := &genai.ChatRequest{
Prompt: req.Prompt,
ConversationID: conversationID.String(),
ConversationRaw: "",
Context: genai.GenerateAIContext(
genai.RepoRef{
Ref: repo.Path,
},
),
Capabilities: s.cr.Capabilities(),
}
resp, err := s.CapabilitiesLoop(ctx, chatRequest)
if err != nil {
return nil, err
}
var yaml string
for _, value := range resp.Context {
out, ok := value.Payload.(*capabilities.ReturnPipelineYamlOutput)
if ok {
yaml = out.Yaml
}
}
return &types.PipelineGenerateResponse{
YAML: yaml,
}, nil
}
// TODO fix naming
type PipelineYaml struct {
Yaml string `yaml:"yaml"`
}
// CapabilitiesLoop TODO: this should be replaced with an async model for Harness Enterprise, but remain for gitness.
func (s *HarnessIntelligence) CapabilitiesLoop(
ctx context.Context, req *genai.ChatRequest) (*genai.ChatRequest, error) {
returnToUser := false
for !returnToUser {
capToRun, err := genai.CallAIFoundation(ctx, s.cr, req)
if err != nil {
return nil, fmt.Errorf("failed to call local chat: %w", err)
}
resp, err := s.cc.RunCapabilities(ctx, capToRun)
if err != nil {
return nil, fmt.Errorf("failed to run capabilities: %w", err)
}
prevChatRequest := req
req = &genai.ChatRequest{
Prompt: "",
ConversationID: prevChatRequest.ConversationID,
ConversationRaw: capToRun.ConversationRaw,
Context: capabilityResponseToChatContext(resp),
Capabilities: s.cr.Capabilities(),
}
for _, value := range resp.CapabilitiesRan {
if value.ReturnToUser {
returnToUser = true
}
}
}
return req, nil
}
func (s *HarnessIntelligence) Update(
ctx context.Context,
req *types.PipelineUpdateRequest) (*types.PipelineUpdateResponse, error) {
if req.RepoRef == "" {
return nil, fmt.Errorf("no repo ref specified")
}
// do permission check on repo here?
repo, err := s.repoStore.FindByRef(ctx, req.RepoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repo by ref: %w", err)
}
conversationID := uuid.New()
chatRequest := &genai.ChatRequest{
Prompt: req.Prompt,
ConversationID: conversationID.String(),
ConversationRaw: "",
Context: genai.GenerateAIContext(
genai.RepoRef{
Ref: repo.Path,
},
genai.PipelineContext{
Yaml: req.Pipeline,
},
),
Capabilities: s.cr.Capabilities(),
}
resp, err := s.CapabilitiesLoop(ctx, chatRequest)
if err != nil {
return nil, err
}
var yaml string
for _, value := range resp.Context {
out, ok := value.Payload.(*capabilities.ReturnPipelineYamlOutput)
if ok {
yaml = out.Yaml
}
}
updateResponse := &types.PipelineUpdateResponse{
YAML: yaml,
}
return updateResponse, nil
}
func (s *HarnessIntelligence) Suggest(
_ context.Context,
_ *types.PipelineSuggestionsRequest) (*types.PipelineSuggestionsResponse, error) {
return &types.PipelineSuggestionsResponse{
Suggestions: []types.Suggestion{
{
ID: "add-testing-stage",
Prompt: "add a testing stage",
UserSuggestion: "You should follow best practices by adding a testing stage",
Suggestion: "kind: pipeline\nstages:\n - steps:\n - name: build\n " +
"timeout: 10m\n run:\n script: go build\n - run:\n " +
"script: go test\n on-failure:\n errors: all\n action: ignore",
},
},
}, nil
}

View File

@ -0,0 +1,45 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aiagent
import (
capabilitiesctrl "github.com/harness/gitness/app/api/controller/capabilities"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/capabilities"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvideAiAgent,
)
func ProvideAiAgent(
repoStore store.RepoStore,
git git.Interface,
authorizer authz.Authorizer,
cr *capabilities.Registry,
cc *capabilitiesctrl.Controller,
) (*HarnessIntelligence, error) {
return &HarnessIntelligence{
repoStore,
git,
authorizer,
cr,
cc,
}, nil
}

View File

@ -0,0 +1,124 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"context"
"encoding/json"
"fmt"
"github.com/harness/gitness/types/capabilities"
)
// newLogic This function helps us adhere to the same function definition, across different capabilities
// Each capability takes in an input and returns an output, by using this function, each capability can have its own
// input and output definition.
func newLogic[T capabilities.Input, U capabilities.Output](
logic func(ctx context.Context, input T) (U, error)) capabilities.Logic {
return func(ctx context.Context, input capabilities.Input) (capabilities.Output, error) {
myInput, ok := input.(T)
if !ok {
return nil, fmt.Errorf("invalid input type")
}
return logic(ctx, myInput)
}
}
func NewRegistry() *Registry {
return &Registry{
capabilities: make(map[capabilities.Type]capabilities.Capability),
}
}
type Registry struct {
capabilities map[capabilities.Type]capabilities.Capability
}
func (r *Registry) register(c capabilities.Capability) error {
if _, ok := r.capabilities[c.Type]; ok {
return fmt.Errorf("capability %q already registered", c.Type)
}
r.capabilities[c.Type] = c
return nil
}
func (r *Registry) Exists(t capabilities.Type) bool {
_, ok := r.capabilities[t]
return ok
}
func (r *Registry) ReturnToUser(t capabilities.Type) (bool, error) {
c, ok := r.capabilities[t]
if !ok {
return false, fmt.Errorf("unknown capability type %q", t)
}
return c.ReturnToUser, nil
}
func (r *Registry) Get(t capabilities.Type) (capabilities.Capability, bool) {
c, ok := r.capabilities[t]
return c, ok
}
func (r *Registry) Execute(
ctx context.Context, t capabilities.Type, in capabilities.Input) (capabilities.Output, error) {
c, ok := r.Get(t)
if !ok {
return nil, fmt.Errorf("unknown capability type %q", t)
}
out, err := c.Logic(ctx, in)
if err != nil {
return nil, fmt.Errorf("failed execution: %w", err)
}
return out, nil
}
func DeserializeInput(cr *Registry, t capabilities.Type, raw json.RawMessage) (capabilities.Input, error) {
capability, ok := cr.Get(t)
if !ok {
return nil, fmt.Errorf("unknown type: %s", t)
}
input := capability.NewInput()
err := json.Unmarshal(raw, input)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal input: %w", err)
}
return input, nil
}
func (r *Registry) Capabilities() []capabilities.CapabilityReference {
var capabilitiesList []capabilities.CapabilityReference
for _, capability := range r.capabilities {
c := capabilities.CapabilityReference{
Type: capability.Type,
Version: capability.Version,
}
capabilitiesList = append(capabilitiesList, c)
}
return capabilitiesList
}
type RepoRef struct {
Ref string `json:"ref"`
}
func (RepoRef) IsCapabilityOutput() {}

View File

@ -0,0 +1,150 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"bytes"
"context"
"fmt"
"io"
"unicode/utf8"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types/capabilities"
"github.com/harness/gitness/types/check"
"github.com/rs/zerolog/log"
)
const (
// maxGetContentFileSize specifies the maximum number of bytes a file content response contains.
// If a file is any larger, the content is truncated.
maxGetContentFileSize = 1 << 22 // 4 MB
)
var GetFileType capabilities.Type = "get_file"
var GetFileVersion capabilities.Version = "1.0.0"
type GetFileInput struct {
RepoREF string `json:"repo_ref"`
GitREF string `json:"git_ref"`
Path string `json:"path"`
}
func (GetFileInput) IsCapabilityInput() {}
type GetFileOutput struct {
Content string `json:"content"`
}
func (GetFileOutput) IsCapabilityOutput() {}
func (GetFileOutput) GetName() string {
return string(GetFileType)
}
const AIContextPayloadTypeGetFile capabilities.AIContextPayloadType = "other"
func (GetFileOutput) GetType() capabilities.AIContextPayloadType {
return AIContextPayloadTypeGetFile
}
func (r *Registry) RegisterGetFileCapability(
logic func(ctx context.Context, input *GetFileInput) (*GetFileOutput, error),
) error {
return r.register(
capabilities.Capability{
Type: GetFileType,
NewInput: func() capabilities.Input { return &GetFileInput{} },
Logic: newLogic(logic),
},
)
}
func GetFile(
repoStore store.RepoStore,
gitI git.Interface) func(ctx context.Context, input *GetFileInput) (*GetFileOutput, error) {
return func(ctx context.Context, input *GetFileInput) (*GetFileOutput, error) {
if input.RepoREF == "" {
return nil, check.NewValidationError("repo_ref is required")
}
if input.Path == "" {
return nil, check.NewValidationError("path is required")
}
repo, err := repoStore.FindByRef(ctx, input.RepoREF)
if err != nil {
return nil, fmt.Errorf("failed to find repo %q: %w", input.RepoREF, err)
}
// set gitRef to default branch in case an empty reference was provided
gitRef := input.GitREF
if gitRef == "" {
gitRef = repo.DefaultBranch
}
readParams := git.CreateReadParams(repo)
node, err := gitI.GetTreeNode(ctx, &git.GetTreeNodeParams{
ReadParams: readParams,
GitREF: gitRef,
Path: input.Path,
IncludeLatestCommit: false,
})
if err != nil {
return nil, fmt.Errorf("failed to read tree node: %w", err)
}
// Todo: Handle symlinks
return getContent(ctx, gitI, readParams, node)
}
}
func getContent(
ctx context.Context,
gitI git.Interface,
readParams git.ReadParams,
node *git.GetTreeNodeOutput) (*GetFileOutput, error) {
output, err := gitI.GetBlob(ctx, &git.GetBlobParams{
ReadParams: readParams,
SHA: node.Node.SHA,
SizeLimit: maxGetContentFileSize,
})
if err != nil {
return nil, fmt.Errorf("failed to get file content: %w", err)
}
defer func() {
if err := output.Content.Close(); err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf("failed to close blob content reader.")
}
}()
content, err := io.ReadAll(output.Content)
if err != nil {
return nil, fmt.Errorf("failed to read blob content: %w", err)
}
// throw error for binary file
if i := bytes.IndexByte(content, '\x00'); i > 0 {
if !utf8.Valid(content[:i]) {
return nil, check.NewValidationError("file content is not valid UTF-8")
}
}
return &GetFileOutput{
Content: string(content),
}, nil
}

View File

@ -0,0 +1,124 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"context"
"fmt"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/capabilities"
"github.com/harness/gitness/types/check"
)
var ListFilesType capabilities.Type = "list_files"
var ListFilesVersion capabilities.Version = "0"
type ListFilesInput struct {
RepoREF string `json:"repo_ref"`
GitRef string `json:"git_ref"`
Path string `json:"path"`
}
func (ListFilesInput) IsCapabilityInput() {}
type ListFilesOutput struct {
Files []string `json:"files"`
Directories []string `json:"directories"`
}
func (ListFilesOutput) IsCapabilityOutput() {}
const AIContextPayloadTypeListFiles capabilities.AIContextPayloadType = "other"
func (ListFilesOutput) GetType() capabilities.AIContextPayloadType {
return AIContextPayloadTypeListFiles
}
func (ListFilesOutput) GetName() string {
return string(ListFilesType)
}
func (r *Registry) RegisterListFilesCapability(
logic func(ctx context.Context, input *ListFilesInput) (*ListFilesOutput, error),
) error {
return r.register(
capabilities.Capability{
Type: ListFilesType,
NewInput: func() capabilities.Input { return &ListFilesInput{} },
Logic: newLogic(logic),
Version: ListFilesVersion,
ReturnToUser: false,
},
)
}
func ListFiles(
repoStore store.RepoStore,
gitI git.Interface) func(
ctx context.Context,
input *ListFilesInput) (*ListFilesOutput, error) {
return func(ctx context.Context, input *ListFilesInput) (*ListFilesOutput, error) {
if input.RepoREF == "" {
return nil, check.NewValidationError("repo_ref is required")
}
repo, err := repoStore.FindByRef(ctx, input.RepoREF)
if err != nil {
return nil, fmt.Errorf("failed to find repo %q: %w", input.RepoREF, err)
}
// set gitRef to default branch in case an empty reference was provided
gitRef := input.GitRef
if gitRef == "" {
gitRef = repo.DefaultBranch
}
return listFiles(ctx, gitI, repo, gitRef, input.Path)
}
}
func listFiles(
ctx context.Context,
gitI git.Interface,
repo *types.Repository,
gitRef string,
path string) (*ListFilesOutput, error) {
files := make([]string, 0)
directories := make([]string, 0)
tree, err := gitI.ListTreeNodes(ctx, &git.ListTreeNodeParams{
ReadParams: git.CreateReadParams(repo),
GitREF: gitRef,
Path: path,
IncludeLatestCommit: false,
})
if err != nil {
return nil, fmt.Errorf("failed to list tree nodes: %w", err)
}
for _, v := range tree.Nodes {
if v.Type == git.TreeNodeTypeBlob {
files = append(files, v.Path)
} else if v.Type == git.TreeNodeTypeTree {
directories = append(directories, v.Path)
}
}
return &ListFilesOutput{
Files: files,
Directories: directories,
}, nil
}

View File

@ -0,0 +1,69 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"context"
"github.com/harness/gitness/types/capabilities"
)
var ReturnPipelineYamlType capabilities.Type = "return_pipeline_yaml"
var ReturnPipelineYamlVersion capabilities.Version = "0"
type ReturnPipelineYamlInput struct {
Yaml string `json:"pipeline_yaml"`
}
func (ReturnPipelineYamlInput) IsCapabilityInput() {}
type ReturnPipelineYamlOutput struct {
Yaml string `json:"pipeline_yaml"`
}
func (ReturnPipelineYamlOutput) IsCapabilityOutput() {}
const AIContextPayloadTypeReturnPipelineYaml capabilities.AIContextPayloadType = "other"
func (ReturnPipelineYamlOutput) GetType() capabilities.AIContextPayloadType {
return AIContextPayloadTypeReturnPipelineYaml
}
func (ReturnPipelineYamlOutput) GetName() string {
return string(ReturnPipelineYamlType)
}
func (r *Registry) RegisterReturnPipelineYamlCapability(
logic func(ctx context.Context, input *ReturnPipelineYamlInput) (*ReturnPipelineYamlOutput, error),
) error {
return r.register(
capabilities.Capability{
Type: ReturnPipelineYamlType,
NewInput: func() capabilities.Input { return &ReturnPipelineYamlInput{} },
Logic: newLogic(logic),
Version: ReturnPipelineYamlVersion,
ReturnToUser: true,
},
)
}
// ReturnPipelineYaml could take in, eg repoStore store.RepoStore, git git.Interface, as arguments.
func ReturnPipelineYaml() func(ctx context.Context, input *ReturnPipelineYamlInput) (*ReturnPipelineYamlOutput, error) {
return func(_ context.Context, input *ReturnPipelineYamlInput) (*ReturnPipelineYamlOutput, error) {
return &ReturnPipelineYamlOutput{
Yaml: input.Yaml,
}, nil
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import (
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvideCapabilities,
)
func panicOnErr(err error) {
if err != nil {
panic(err)
}
}
func ProvideCapabilities(repoStore store.RepoStore, git git.Interface) (*Registry, error) {
registry := NewRegistry()
panicOnErr(registry.RegisterListFilesCapability(ListFiles(repoStore, git)))
panicOnErr(registry.RegisterGetFileCapability(GetFile(repoStore, git)))
panicOnErr(registry.RegisterReturnPipelineYamlCapability(ReturnPipelineYaml()))
return registry, nil
}

View File

@ -698,6 +698,12 @@ type (
// IncrementSeqNum increments the sequence number of the pipeline
IncrementSeqNum(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error)
// ListInSpace lists pipelines in a particular space.
ListInSpace(ctx context.Context, spaceID int64, filter types.ListQueryFilter) ([]*types.Pipeline, error)
// CountInSpace counts pipelines in a particular space.
CountInSpace(ctx context.Context, spaceID int64, filter types.ListQueryFilter) (int64, error)
}
SecretStore interface {

View File

@ -55,8 +55,8 @@ type execution struct {
Parent int64 `db:"execution_parent"`
Status enum.CIStatus `db:"execution_status"`
Error string `db:"execution_error"`
Event string `db:"execution_event"`
Action string `db:"execution_action"`
Event enum.TriggerEvent `db:"execution_event"`
Action enum.TriggerAction `db:"execution_action"`
Link string `db:"execution_link"`
Timestamp int64 `db:"execution_timestamp"`
Title string `db:"execution_title"`

View File

@ -216,6 +216,75 @@ func (s *pipelineStore) List(
return dst, nil
}
// ListInSpace lists all the pipelines for a space.
func (s *pipelineStore) ListInSpace(
ctx context.Context,
spaceID int64,
filter types.ListQueryFilter,
) ([]*types.Pipeline, error) {
const pipelineWithRepoColumns = pipelineColumns + `
,repositories.repo_id
,repositories.repo_uid
`
stmt := database.Builder.
Select(pipelineWithRepoColumns).
From("pipelines").
InnerJoin("repositories ON pipelines.pipeline_repo_id = repositories.repo_id").
Where("repositories.repo_parent_id = ?", fmt.Sprint(spaceID))
if filter.Query != "" {
stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
}
stmt = stmt.Limit(database.Limit(filter.Size))
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
sql, args, err := stmt.ToSql()
if err != nil {
return nil, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
dst := []*pipelineRepoJoin{}
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "Failed executing custom list query")
}
return convertPipelineRepoJoins(dst), nil
}
// CountInSpace counts the number of pipelines in a space.
func (s *pipelineStore) CountInSpace(
ctx context.Context,
spaceID int64,
filter types.ListQueryFilter,
) (int64, error) {
stmt := database.Builder.
Select("count(*)").
From("pipelines").
InnerJoin("repositories ON pipelines.pipeline_repo_id = repositories.repo_id").
Where("repositories.repo_parent_id = ?", fmt.Sprint(spaceID))
if filter.Query != "" {
stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
}
var count int64
sql, args, err := stmt.ToSql()
if err != nil {
return count, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
err = db.QueryRowContext(ctx, sql, args...).Scan(&count)
if err != nil {
return 0, database.ProcessSQLErrorf(ctx, err, "Failed executing count query")
}
return count, nil
}
// ListLatest lists all the pipelines under a repository with information
// about the latest build if available.
func (s *pipelineStore) ListLatest(

View File

@ -68,7 +68,7 @@ func convertPipelineJoin(join *pipelineExecutionJoin) *types.Pipeline {
ID: join.ID.Int64,
PipelineID: join.PipelineID.Int64,
RepoID: join.RepoID.Int64,
Action: join.Action.String,
Action: enum.TriggerAction(join.Action.String),
Trigger: join.Trigger.String,
Number: join.Number.Int64,
After: join.After.String,
@ -92,3 +92,27 @@ func convertPipelineJoin(join *pipelineExecutionJoin) *types.Pipeline {
}
return ret
}
type pipelineRepoJoin struct {
*types.Pipeline
RepoID sql.NullInt64 `db:"repo_id"`
RepoUID sql.NullString `db:"repo_uid"`
}
func convertPipelineRepoJoins(rows []*pipelineRepoJoin) []*types.Pipeline {
pipelines := []*types.Pipeline{}
for _, k := range rows {
pipeline := convertPipelineRepoJoin(k)
pipelines = append(pipelines, pipeline)
}
return pipelines
}
func convertPipelineRepoJoin(join *pipelineRepoJoin) *types.Pipeline {
ret := join.Pipeline
if !join.RepoID.Valid {
return ret
}
ret.RepoUID = join.RepoUID.String
return ret
}

View File

@ -10,6 +10,8 @@ package main
import (
"context"
"github.com/harness/gitness/app/api/controller/aiagent"
"github.com/harness/gitness/app/api/controller/capabilities"
checkcontroller "github.com/harness/gitness/app/api/controller/check"
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
@ -43,6 +45,7 @@ import (
gitevents "github.com/harness/gitness/app/events/git"
gitspaceevents "github.com/harness/gitness/app/events/gitspace"
gitspaceinfraevents "github.com/harness/gitness/app/events/gitspaceinfra"
pipelineevents "github.com/harness/gitness/app/events/pipeline"
pullreqevents "github.com/harness/gitness/app/events/pullreq"
repoevents "github.com/harness/gitness/app/events/repo"
infrastructure "github.com/harness/gitness/app/gitspace/infrastructure"
@ -64,6 +67,8 @@ import (
"github.com/harness/gitness/app/router"
"github.com/harness/gitness/app/server"
"github.com/harness/gitness/app/services"
aiagentservice "github.com/harness/gitness/app/services/aiagent"
capabilitiesservice "github.com/harness/gitness/app/services/capabilities"
"github.com/harness/gitness/app/services/cleanup"
"github.com/harness/gitness/app/services/codecomments"
"github.com/harness/gitness/app/services/codeowners"
@ -153,6 +158,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
infrastructure.WireSet,
infraproviderpkg.WireSet,
gitspaceevents.WireSet,
pipelineevents.WireSet,
infraproviderCtrl.WireSet,
gitspaceCtrl.WireSet,
gitevents.WireSet,
@ -240,6 +246,10 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
cliserver.ProvideGitspaceInfraProvisionerConfig,
cliserver.ProvideIDEVSCodeConfig,
instrument.WireSet,
aiagentservice.WireSet,
aiagent.WireSet,
capabilities.WireSet,
capabilitiesservice.WireSet,
)
return &cliserver.System{}, nil
}

View File

@ -9,6 +9,8 @@ package main
import (
"context"
aiagent2 "github.com/harness/gitness/app/api/controller/aiagent"
capabilities2 "github.com/harness/gitness/app/api/controller/capabilities"
check2 "github.com/harness/gitness/app/api/controller/check"
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
@ -39,10 +41,11 @@ import (
"github.com/harness/gitness/app/auth/authn"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/bootstrap"
events5 "github.com/harness/gitness/app/events/git"
events6 "github.com/harness/gitness/app/events/gitspace"
events6 "github.com/harness/gitness/app/events/git"
events7 "github.com/harness/gitness/app/events/gitspace"
events3 "github.com/harness/gitness/app/events/gitspaceinfra"
events4 "github.com/harness/gitness/app/events/pullreq"
events4 "github.com/harness/gitness/app/events/pipeline"
events5 "github.com/harness/gitness/app/events/pullreq"
events2 "github.com/harness/gitness/app/events/repo"
"github.com/harness/gitness/app/gitspace/infrastructure"
"github.com/harness/gitness/app/gitspace/logutil"
@ -63,6 +66,8 @@ import (
"github.com/harness/gitness/app/router"
server2 "github.com/harness/gitness/app/server"
"github.com/harness/gitness/app/services"
"github.com/harness/gitness/app/services/aiagent"
"github.com/harness/gitness/app/services/capabilities"
"github.com/harness/gitness/app/services/cleanup"
"github.com/harness/gitness/app/services/codecomments"
"github.com/harness/gitness/app/services/codeowners"
@ -275,7 +280,11 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
infraproviderService := infraprovider2.ProvideInfraProvider(transactor, infraProviderResourceStore, infraProviderConfigStore, infraProviderTemplateStore, factory, spaceStore)
gitspaceService := gitspace.ProvideGitspace(transactor, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, infraproviderService)
spaceController := space.ProvideController(config, transactor, provider, streamer, spaceIdentifier, authorizer, spacePathStore, pipelineStore, secretStore, connectorStore, templateStore, spaceStore, repoStore, principalStore, repoController, membershipStore, repository, exporterRepository, resourceLimiter, publicaccessService, auditService, gitspaceService, labelService, instrumentService)
pipelineController := pipeline.ProvideController(repoStore, triggerStore, authorizer, pipelineStore)
reporter2, err := events4.ProvideReporter(eventsSystem)
if err != nil {
return nil, err
}
pipelineController := pipeline.ProvideController(repoStore, triggerStore, authorizer, pipelineStore, reporter2)
secretController := secret.ProvideController(encrypter, secretStore, authorizer, spaceStore)
triggerController := trigger.ProvideController(authorizer, triggerStore, pipelineStore, repoStore)
connectorController := connector.ProvideController(connectorStore, authorizer, spaceStore)
@ -287,27 +296,27 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
pullReqReviewStore := database.ProvidePullReqReviewStore(db)
pullReqReviewerStore := database.ProvidePullReqReviewerStore(db, principalInfoCache)
pullReqFileViewStore := database.ProvidePullReqFileViewStore(db)
reporter2, err := events4.ProvideReporter(eventsSystem)
reporter3, err := events5.ProvideReporter(eventsSystem)
if err != nil {
return nil, err
}
migrator := codecomments.ProvideMigrator(gitInterface)
readerFactory, err := events5.ProvideReaderFactory(eventsSystem)
readerFactory, err := events6.ProvideReaderFactory(eventsSystem)
if err != nil {
return nil, err
}
eventsReaderFactory, err := events4.ProvideReaderFactory(eventsSystem)
eventsReaderFactory, err := events5.ProvideReaderFactory(eventsSystem)
if err != nil {
return nil, err
}
repoGitInfoView := database.ProvideRepoGitInfoView(db)
repoGitInfoCache := cache.ProvideRepoGitInfoCache(repoGitInfoView)
pullreqService, err := pullreq.ProvideService(ctx, config, readerFactory, eventsReaderFactory, reporter2, gitInterface, repoGitInfoCache, repoStore, pullReqStore, pullReqActivityStore, principalInfoCache, codeCommentView, migrator, pullReqFileViewStore, pubSub, provider, streamer)
pullreqService, err := pullreq.ProvideService(ctx, config, readerFactory, eventsReaderFactory, reporter3, gitInterface, repoGitInfoCache, repoStore, pullReqStore, pullReqActivityStore, principalInfoCache, codeCommentView, migrator, pullReqFileViewStore, pubSub, provider, streamer)
if err != nil {
return nil, err
}
pullReq := migrate.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor)
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, reporter2, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService, instrumentService)
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, reporter3, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService, instrumentService)
webhookConfig := server.ProvideWebhookConfig(config)
webhookStore := database.ProvideWebhookStore(db)
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
@ -316,7 +325,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
return nil, err
}
webhookController := webhook2.ProvideController(webhookConfig, authorizer, webhookStore, webhookExecutionStore, repoStore, webhookService, encrypter)
reporter3, err := events5.ProvideReporter(eventsSystem)
reporter4, err := events6.ProvideReporter(eventsSystem)
if err != nil {
return nil, err
}
@ -332,7 +341,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
if err != nil {
return nil, err
}
githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter3, reporter, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, settingsService, preReceiveExtender, updateExtender, postReceiveExtender)
githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter4, reporter, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, settingsService, preReceiveExtender, updateExtender, postReceiveExtender)
serviceaccountController := serviceaccount.NewController(principalUID, authorizer, principalStore, spaceStore, repoStore, tokenStore)
principalController := principal.ProvideController(principalStore, authorizer)
v := check2.ProvideCheckSanitizers()
@ -350,7 +359,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
searcher := keywordsearch.ProvideSearcher(localIndexSearcher)
keywordsearchController := keywordsearch2.ProvideController(authorizer, searcher, repoController, spaceController)
infraproviderController := infraprovider3.ProvideController(authorizer, spaceStore, infraproviderService)
reporter4, err := events6.ProvideReporter(eventsSystem)
reporter5, err := events7.ProvideReporter(eventsSystem)
if err != nil {
return nil, err
}
@ -370,18 +379,28 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig)
passwordResolver := secret2.ProvidePasswordResolver()
resolverFactory := secret2.ProvideResolverFactory(passwordResolver)
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter4, orchestratorConfig, vsCode, vsCodeWeb, resolverFactory)
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter5, orchestratorConfig, vsCode, vsCodeWeb, resolverFactory)
gitspaceEventStore := database.ProvideGitspaceEventStore(db)
gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter4, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore, gitspaceService)
gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter5, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore, gitspaceService)
rule := migrate.ProvideRuleImporter(ruleStore, transactor, principalStore)
migrateWebhook := migrate.ProvideWebhookImporter(webhookConfig, transactor, webhookStore)
migrateController := migrate2.ProvideController(authorizer, publicaccessService, gitInterface, provider, pullReq, rule, migrateWebhook, resourceLimiter, auditService, repoIdentifier, transactor, spaceStore, repoStore)
registry, err := capabilities.ProvideCapabilities(repoStore, gitInterface)
if err != nil {
return nil, err
}
capabilitiesController := capabilities2.ProvideController(registry)
harnessIntelligence, err := aiagent.ProvideAiAgent(repoStore, gitInterface, authorizer, registry, capabilitiesController)
if err != nil {
return nil, err
}
aiagentController := aiagent2.ProvideController(authorizer, harnessIntelligence, repoStore, pipelineStore, executionStore)
openapiService := openapi.ProvideOpenAPIService()
routerRouter := router.ProvideRouter(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController, provider, openapiService)
routerRouter := router.ProvideRouter(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController, aiagentController, capabilitiesController, provider, openapiService)
serverServer := server2.ProvideServer(config, routerRouter)
publickeyService := publickey.ProvidePublicKey(publicKeyStore, principalInfoCache)
sshServer := ssh.ProvideServer(config, publickeyService, repoController)
executionManager := manager.ProvideExecutionManager(config, executionStore, pipelineStore, provider, streamer, fileService, converterService, logStore, logStream, checkStore, repoStore, schedulerScheduler, secretStore, stageStore, stepStore, principalStore, publicaccessService)
executionManager := manager.ProvideExecutionManager(config, executionStore, pipelineStore, provider, streamer, fileService, converterService, logStore, logStream, checkStore, repoStore, schedulerScheduler, secretStore, stageStore, stepStore, principalStore, publicaccessService, reporter2)
client := manager.ProvideExecutionClient(executionManager, provider, config)
resolverManager := resolver.ProvideResolver(config, pluginStore, templateStore, executionStore, repoStore)
runtimeRunner, err := runner.ProvideExecutionRunner(config, client, resolverManager)
@ -428,7 +447,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
return nil, err
}
gitspaceeventConfig := server.ProvideGitspaceEventConfig(config)
readerFactory3, err := events6.ProvideReaderFactory(eventsSystem)
readerFactory3, err := events7.ProvideReaderFactory(eventsSystem)
if err != nil {
return nil, err
}
@ -440,7 +459,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
if err != nil {
return nil, err
}
gitspaceinfraeventService, err := gitspaceinfraevent.ProvideService(ctx, gitspaceeventConfig, readerFactory4, orchestratorOrchestrator, gitspaceService, reporter4)
gitspaceinfraeventService, err := gitspaceinfraevent.ProvideService(ctx, gitspaceeventConfig, readerFactory4, orchestratorOrchestrator, gitspaceService, reporter5)
if err != nil {
return nil, err
}

114
genai/genai.go Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package genai
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"os"
capabilitiesctrl "github.com/harness/gitness/app/api/controller/capabilities"
capabilitieshandler "github.com/harness/gitness/app/api/handler/capabilities"
"github.com/harness/gitness/app/services/capabilities"
capabilities2 "github.com/harness/gitness/types/capabilities"
)
const (
AIFoundationServiceToken = "AI_FOUNDATION_SERVICE_TOKEN"
)
func GenerateAIContext(payloads ...capabilities2.AIContextPayload) []capabilities2.AIContext {
out := make([]capabilities2.AIContext, len(payloads))
for i := range payloads {
out[i] = capabilities2.AIContext{
Type: payloads[i].GetType(),
Payload: payloads[i],
Name: payloads[i].GetName(),
}
}
return out
}
type PipelineContext struct {
Yaml string `json:"yaml"`
}
func (c PipelineContext) GetName() string {
return "pipeline_context"
}
const AIContextPayloadTypePipelineContext capabilities2.AIContextPayloadType = "other"
func (PipelineContext) GetType() capabilities2.AIContextPayloadType {
return AIContextPayloadTypePipelineContext
}
type RepoRef struct {
Ref string `json:"ref"`
}
func (r RepoRef) GetName() string {
return "repo_ref"
}
const AIContextPayloadTypeRepoRef capabilities2.AIContextPayloadType = "other"
func (RepoRef) GetType() capabilities2.AIContextPayloadType {
return AIContextPayloadTypeRepoRef
}
type ChatRequest struct {
Prompt string `json:"prompt"`
ConversationID string `json:"conversation_id"`
ConversationRaw string `json:"conversation_raw"`
Context []capabilities2.AIContext `json:"context"`
Capabilities []capabilities2.CapabilityReference `json:"capabilities"`
}
func CallAIFoundation(ctx context.Context, cr *capabilities.Registry,
req *ChatRequest) (*capabilitiesctrl.RunCapabilitiesRequest, error) {
url := "http://localhost:8000/chat/gitness"
jsonData, err := json.Marshal(req)
if err != nil {
return nil, err
}
newReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
newReq.Header.Add("Authorization", "Bearer "+os.Getenv(AIFoundationServiceToken))
client := http.DefaultClient
resp, err := client.Do(newReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
in, err := capabilitieshandler.UnmarshalRunCapabilitiesRequest(cr, data)
if err != nil {
return nil, err
}
return in, nil
}

23
go.mod
View File

@ -15,8 +15,8 @@ require (
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240815103043-c6c3a3e33ce3
github.com/drone/drone-go v1.7.1
github.com/drone/drone-yaml v1.2.3
github.com/drone/funcmap v0.0.0-20240227160611-7e19e9cd5a1c
github.com/drone/go-convert v0.0.0-20240307072510-6bd371c65e61
github.com/drone/funcmap v0.0.0-20190918184546-d4ef6e88376d
github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522
github.com/drone/go-scm v1.38.4
github.com/drone/runner-go v1.12.0
@ -77,7 +77,6 @@ require (
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.1.12 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d // indirect
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
@ -94,7 +93,6 @@ require (
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/drone/envsubst v1.0.3 // indirect
github.com/drone/signal v1.0.0 // indirect
github.com/fatih/semgroup v1.2.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
@ -161,30 +159,31 @@ require (
)
require (
cloud.google.com/go/profiler v0.4.1
github.com/Microsoft/go-winio v0.6.2 // indirect
cloud.google.com/go/profiler v0.3.1
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c // indirect
github.com/google/subcommands v1.2.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/swaggest/jsonschema-go v0.3.72
github.com/swaggest/refl v1.3.0 // indirect
github.com/swaggest/jsonschema-go v0.3.40
github.com/swaggest/refl v1.1.0 // indirect
github.com/vearutop/statigz v1.4.0 // indirect
github.com/yuin/goldmark v1.7.4
github.com/yuin/goldmark v1.4.13
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/genproto v0.0.0-20240723171418-e6d459c13d2a // indirect
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)

60
go.sum
View File

@ -10,10 +10,10 @@ cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJ
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/iam v1.1.12 h1:JixGLimRrNGcxvJEQ8+clfLxPlbeZA6MuRJ+qJNQ5Xw=
cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg=
cloud.google.com/go/longrunning v0.5.10 h1:eB/BniENNRKhjz/xgiillrdcH3G74TGSl3BXinGlI7E=
cloud.google.com/go/longrunning v0.5.10/go.mod h1:tljz5guTr5oc/qhlUjBlk7UAIFMOGuPNxkNDZXlLics=
cloud.google.com/go/profiler v0.4.1 h1:Q7+lOvikTGMJ/IAWocpYYGit4SIIoILmVZfEEWTORSY=
cloud.google.com/go/profiler v0.4.1/go.mod h1:LBrtEX6nbvhv1w/e5CPZmX9ajGG9BGLtGbv56Tg4SHs=
cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k=
cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c=
cloud.google.com/go/profiler v0.3.1 h1:b5got9Be9Ia0HVvyt7PavWxXEht15B9lWnigdvHtxOc=
cloud.google.com/go/profiler v0.3.1/go.mod h1:GsG14VnmcMFQ9b+kq71wh3EKMZr3WRMgLzNiFRpW7tE=
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
@ -21,7 +21,6 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
docker.io/go-docker v1.0.0/go.mod h1:7tiAn5a0LFmjbPDbyTPOaTTOuG1ZRNXdPA6RvKY+fpY=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng=
github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3cARGAK9CfW3HoxCy1a0G4TKrdiKke8ftOMEOHyySYs=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY=
@ -35,8 +34,8 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@ -48,8 +47,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@ -79,8 +78,8 @@ github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQ
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bool64/dev v0.2.35 h1:M17TLsO/pV2J7PYI/gpe3Ua26ETkzZGb+dC06eoMqlk=
github.com/bool64/dev v0.2.35/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/dev v0.2.32 h1:DRZtloaoH1Igky3zphaUHV9+SLIV2H3lsf78JsJHFg0=
github.com/bool64/dev v0.2.32/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs=
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
@ -144,8 +143,6 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240725142717-515d467f7b29 h1:7k3uDEBK1FcVDb8qFF7P2bWyimsrdcnzs4ZfX02WqqI=
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240725142717-515d467f7b29/go.mod h1:kkQh3P4hJp82vjLTju1RkMvnbY+7cImwaoRH3lExnWA=
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240815103043-c6c3a3e33ce3 h1:NnP4ingWdiSNvY5NRit3jrd+kS6j+9Lg2EWfGQqXzTQ=
github.com/drone-runners/drone-runner-docker v1.8.4-0.20240815103043-c6c3a3e33ce3/go.mod h1:kkQh3P4hJp82vjLTju1RkMvnbY+7cImwaoRH3lExnWA=
github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw=
@ -156,17 +153,16 @@ github.com/drone/drone-yaml v1.2.3/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcej
github.com/drone/envsubst v1.0.2/go.mod h1:bkZbnc/2vh1M12Ecn7EYScpI4YGYU0etwLJICOWi8Z0=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/drone/funcmap v0.0.0-20240227160611-7e19e9cd5a1c h1:R4T5Tv7YUrKmw+tBPDvdaz5g7+EElGNIZKY0MWRI3yU=
github.com/drone/funcmap v0.0.0-20240227160611-7e19e9cd5a1c/go.mod h1:nDRkX7PHq+p39AD5/usv3KZMerxZTYU/9rfLS5IDspU=
github.com/drone/go-convert v0.0.0-20240307072510-6bd371c65e61 h1:1mhmsVKAjApVVD0nmyW62SLD8XTc2h/7Hhll7CtOvzY=
github.com/drone/go-convert v0.0.0-20240307072510-6bd371c65e61/go.mod h1:PyCDcuAhGF6W0VJ6qMmlM47dsSyGv/zDiMqeJxMFuGM=
github.com/drone/funcmap v0.0.0-20190918184546-d4ef6e88376d h1:/IO7UVVu191Jc0DajV4cDVoO+91cuppvgxg2MZl+AXI=
github.com/drone/funcmap v0.0.0-20190918184546-d4ef6e88376d/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E=
github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635 h1:qQX+U2iEm4X2FcmBzxZwZgz8gLpUTa6lBB1vBBCV9Oo=
github.com/drone/go-convert v0.0.0-20230919093251-7104c3bcc635/go.mod h1:PyCDcuAhGF6W0VJ6qMmlM47dsSyGv/zDiMqeJxMFuGM=
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522 h1:i3EfRpr/eYifK9w0ninT3xHAthkS4NTQjLX0/zDIsy4=
github.com/drone/go-generate v0.0.0-20230920014042-6085ee5c9522/go.mod h1:eTfy716efMJgVvk/ZkRvitaXY2UuytfqDjxclFMeLdQ=
github.com/drone/go-scm v1.38.4 h1:KW+znh2tg3tJwbiFfzhjZQ2gbyasJ213V7hZ00QaVpc=
github.com/drone/go-scm v1.38.4/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
github.com/drone/runner-go v1.12.0 h1:zUjDj9ylsJ4n4Mvy4znddq/Z4EBzcUXzTltpzokKtgs=
github.com/drone/runner-go v1.12.0/go.mod h1:vu4pPPYDoeN6vdYQAY01GGGsAIW4aLganJNaa8Fx8zE=
github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI=
github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc=
github.com/drone/spec v0.0.0-20230920145636-3827abdce961 h1:aUWrLS2ghyxIpDICpZOV50V1x7JLM3U80UQDQxMKT54=
github.com/drone/spec v0.0.0-20230920145636-3827abdce961/go.mod h1:KyQZA9qwuscbbM7yTrtZg25Wammoc5GKwaRem8kDA5k=
@ -285,8 +281,8 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 h1:ssNFCCVmib/GQSzx3uCWyfMgOamLGWuGqlMS77Y1m3Y=
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c h1:lvddKcYTQ545ADhBujtIJmqQrZBDsGo7XIMbAQe/sNY=
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
@ -358,8 +354,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
@ -691,14 +687,14 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08Yu
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
github.com/swaggest/jsonschema-go v0.3.72 h1:IHaGlR1bdBUBPfhe4tfacN2TGAPKENEGiNyNzvnVHv4=
github.com/swaggest/jsonschema-go v0.3.72/go.mod h1:OrGyEoVqpfSFJ4Am4V/FQcQ3mlEC1vVeleA+5ggbVW4=
github.com/swaggest/assertjson v1.7.0 h1:SKw5Rn0LQs6UvmGrIdaKQbMR1R3ncXm5KNon+QJ7jtw=
github.com/swaggest/assertjson v1.7.0/go.mod h1:vxMJMehbSVJd+dDWFCKv3QRZKNTpy/ktZKTz9LOEDng=
github.com/swaggest/jsonschema-go v0.3.40 h1:9EqQ9RvtdW69xfYODmyEKWOSZ12x5eiK+wGD2EVh/L4=
github.com/swaggest/jsonschema-go v0.3.40/go.mod h1:ipIOmoFP64QyRUgyPyU/P9tayq2m2TlvUhyZHrhe3S4=
github.com/swaggest/openapi-go v0.2.23 h1:DYUezSTyw180z1bL51wUnalYYbTMwHBjp1Itvji8/rs=
github.com/swaggest/openapi-go v0.2.23/go.mod h1:T1Koc6EAFAvnCI1MUqOOPDniqGzZy6dOiHtA/j54k14=
github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I=
github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg=
github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc=
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
github.com/swaggest/swgui v1.8.1 h1:OLcigpoelY0spbpvp6WvBt0I1z+E9egMQlUeEKya+zU=
github.com/swaggest/swgui v1.8.1/go.mod h1:YBaAVAwS3ndfvdtW8A4yWDJpge+W57y+8kW+f/DqZtU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -716,9 +712,8 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/zricethezav/gitleaks/v8 v8.18.5-0.20240614204812-26f34692fac6 h1:UL8vBvxILAVsruyxIGMskACYzOk57nR8aq6dpZLR3KQ=
github.com/zricethezav/gitleaks/v8 v8.18.5-0.20240614204812-26f34692fac6/go.mod h1:3EFYK+ZNDHPNQinyZTVGHG7/sFsApEZ9DrCGA1AP63M=
@ -954,8 +949,8 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240723171418-e6d459c13d2a h1:hPbLwHFm59QoSKUT0uGaL19YN4U9W5lY4+iNXlUBNj0=
google.golang.org/genproto v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:+7gIV7FP6jBo5hiY2lsWA//NkNORQVj0J1Isc/4HzR4=
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
google.golang.org/genproto/googleapis/api v0.0.0-20240723171418-e6d459c13d2a h1:YIa/rzVqMEokBkPtydCkx1VLmv3An1Uw7w1P1m6EhOY=
google.golang.org/genproto/googleapis/api v0.0.0-20240723171418-e6d459c13d2a/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240723171418-e6d459c13d2a h1:hqK4+jJZXCU4pW7jsAdGOVFIfLHQeV7LaizZKnZ84HI=
@ -1027,7 +1022,6 @@ k8s.io/apimachinery v0.0.0-20181201231028-18a5ff3097b4/go.mod h1:ccL7Eh7zubPUSh9
k8s.io/client-go v9.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View File

@ -0,0 +1,61 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package capabilities
import "context"
type AIContextPayload interface {
GetType() AIContextPayloadType
GetName() string
}
type AIContextPayloadType string
type AIContext struct {
Type AIContextPayloadType `json:"type"`
Payload AIContextPayload `json:"payload"`
Name string `json:"name"`
}
type CapabilityReference struct {
Type Type `json:"type"`
Version Version `json:"version"`
}
type Type string
type Version string
type Capabilities struct {
Capabilities []Capability `json:"capabilities"`
}
type Capability struct {
Type Type `json:"type"`
NewInput func() Input `json:"-"`
Logic Logic `json:"-"`
Version Version `json:"version"`
ReturnToUser bool `json:"return_to_user"`
}
type Logic func(ctx context.Context, input Input) (Output, error)
type Input interface {
IsCapabilityInput()
}
type Output interface {
IsCapabilityOutput()
GetType() AIContextPayloadType
GetName() string
}

View File

@ -36,6 +36,21 @@ const (
CIStatusError CIStatus = "error"
)
// Enum returns all possible CIStatus values.
func (CIStatus) Enum() []interface{} {
return toInterfaceSlice(ciStatuses)
}
// Sanitize validates and returns a sanitized CIStatus value.
func (status CIStatus) Sanitize() (CIStatus, bool) {
return Sanitize(status, GetAllCIStatuses)
}
// GetAllCIStatuses returns all possible CIStatus values and a default value.
func GetAllCIStatuses() ([]CIStatus, CIStatus) {
return ciStatuses, CIStatusPending
}
func (status CIStatus) ConvertToCheckStatus() CheckStatus {
if status == CIStatusPending || status == CIStatusWaitingOnDeps {
return CheckStatusPending
@ -87,3 +102,17 @@ func (status CIStatus) IsFailed() bool {
status == CIStatusKilled ||
status == CIStatusError
}
// List of all CIStatus values.
var ciStatuses = sortEnum([]CIStatus{
CIStatusSkipped,
CIStatusBlocked,
CIStatusDeclined,
CIStatusWaitingOnDeps,
CIStatusPending,
CIStatusRunning,
CIStatusSuccess,
CIStatusFailure,
CIStatusKilled,
CIStatusError,
})

View File

@ -37,9 +37,9 @@ const (
// TriggerActionPullReqBranchUpdated gets triggered when a pull request source branch gets updated.
TriggerActionPullReqBranchUpdated TriggerAction = "pullreq_branch_updated"
// TriggerActionPullReqClosed gets triggered when a pull request is closed.
TriggerActionPullReqClosed = "pullreq_closed"
TriggerActionPullReqClosed TriggerAction = "pullreq_closed"
// TriggerActionPullReqMerged gets triggered when a pull request is merged.
TriggerActionPullReqMerged = "pullreq_merged"
TriggerActionPullReqMerged TriggerAction = "pullreq_merged"
)
func (TriggerAction) Enum() []interface{} { return toInterfaceSlice(triggerActions) }

View File

@ -19,9 +19,33 @@ type TriggerEvent string
// Hook event constants.
const (
TriggerEventCron = "cron"
TriggerEventManual = "manual"
TriggerEventPush = "push"
TriggerEventPullRequest = "pull_request"
TriggerEventTag = "tag"
TriggerEventCron TriggerEvent = "cron"
TriggerEventManual TriggerEvent = "manual"
TriggerEventPush TriggerEvent = "push"
TriggerEventPullRequest TriggerEvent = "pull_request"
TriggerEventTag TriggerEvent = "tag"
)
// Enum returns all possible TriggerEvent values.
func (TriggerEvent) Enum() []interface{} {
return toInterfaceSlice(triggerEvents)
}
// Sanitize validates and returns a sanitized TriggerEvent value.
func (event TriggerEvent) Sanitize() (TriggerEvent, bool) {
return Sanitize(event, GetAllTriggerEvents)
}
// GetAllTriggerEvents returns all possible TriggerEvent values and a default value.
func GetAllTriggerEvents() ([]TriggerEvent, TriggerEvent) {
return triggerEvents, TriggerEventManual
}
// List of all TriggerEvent values.
var triggerEvents = sortEnum([]TriggerEvent{
TriggerEventCron,
TriggerEventManual,
TriggerEventPush,
TriggerEventPullRequest,
TriggerEventTag,
})

View File

@ -18,41 +18,41 @@ import "github.com/harness/gitness/types/enum"
// Execution represents an instance of a pipeline execution.
type Execution struct {
ID int64 `json:"-"`
PipelineID int64 `json:"pipeline_id"`
CreatedBy int64 `json:"created_by"`
RepoID int64 `json:"repo_id"`
Trigger string `json:"trigger,omitempty"`
Number int64 `json:"number"`
Parent int64 `json:"parent,omitempty"`
Status enum.CIStatus `json:"status"`
Error string `json:"error,omitempty"`
Event string `json:"event,omitempty"`
Action string `json:"action,omitempty"`
Link string `json:"link,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Title string `json:"title,omitempty"`
Message string `json:"message,omitempty"`
Before string `json:"before,omitempty"`
After string `json:"after,omitempty"`
Ref string `json:"ref,omitempty"`
Fork string `json:"source_repo,omitempty"`
Source string `json:"source,omitempty"`
Target string `json:"target,omitempty"`
Author string `json:"author_login,omitempty"`
AuthorName string `json:"author_name,omitempty"`
AuthorEmail string `json:"author_email,omitempty"`
AuthorAvatar string `json:"author_avatar,omitempty"`
Sender string `json:"sender,omitempty"`
Params map[string]string `json:"params,omitempty"`
Cron string `json:"cron,omitempty"`
Deploy string `json:"deploy_to,omitempty"`
DeployID int64 `json:"deploy_id,omitempty"`
Debug bool `json:"debug,omitempty"`
Started int64 `json:"started,omitempty"`
Finished int64 `json:"finished,omitempty"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Version int64 `json:"-"`
Stages []*Stage `json:"stages,omitempty"`
ID int64 `json:"-"`
PipelineID int64 `json:"pipeline_id"`
CreatedBy int64 `json:"created_by"`
RepoID int64 `json:"repo_id"`
Trigger string `json:"trigger,omitempty"`
Number int64 `json:"number"`
Parent int64 `json:"parent,omitempty"`
Status enum.CIStatus `json:"status"`
Error string `json:"error,omitempty"`
Event enum.TriggerEvent `json:"event,omitempty"`
Action enum.TriggerAction `json:"action,omitempty"`
Link string `json:"link,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Title string `json:"title,omitempty"`
Message string `json:"message,omitempty"`
Before string `json:"before,omitempty"`
After string `json:"after,omitempty"`
Ref string `json:"ref,omitempty"`
Fork string `json:"source_repo,omitempty"`
Source string `json:"source,omitempty"`
Target string `json:"target,omitempty"`
Author string `json:"author_login,omitempty"`
AuthorName string `json:"author_name,omitempty"`
AuthorEmail string `json:"author_email,omitempty"`
AuthorAvatar string `json:"author_avatar,omitempty"`
Sender string `json:"sender,omitempty"`
Params map[string]string `json:"params,omitempty"`
Cron string `json:"cron,omitempty"`
Deploy string `json:"deploy_to,omitempty"`
DeployID int64 `json:"deploy_id,omitempty"`
Debug bool `json:"debug,omitempty"`
Started int64 `json:"started,omitempty"`
Finished int64 `json:"finished,omitempty"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Version int64 `json:"-"`
Stages []*Stage `json:"stages,omitempty"`
}

View File

@ -0,0 +1,25 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
type AnalyseExecutionInput struct {
RepoRef string `json:"repo_ref"`
PipelineIdentifier string `json:"pipeline_identifier"`
ExecutionNum int64 `json:"execution_number"`
}
type AnalyseExecutionOutput struct {
Yaml string `json:"yaml"`
}

View File

@ -32,6 +32,9 @@ type Pipeline struct {
Execution *Execution `db:"-" json:"execution,omitempty"`
Updated int64 `db:"pipeline_updated" json:"updated"`
Version int64 `db:"pipeline_version" json:"-"`
// Repo specific information not stored with pipelines
RepoUID string `db:"-" json:"repo_uid,omitempty"`
}
// TODO [CODE-1363]: remove after identifier migration.

50
types/suggestions.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
type PipelineSuggestionsRequest struct {
RepoRef string
Pipeline string
}
type PipelineGenerateRequest struct {
Prompt string
RepoRef string
}
type PipelineUpdateRequest struct {
Prompt string
RepoRef string
Pipeline string
}
type PipelineGenerateResponse struct {
YAML string
}
type PipelineUpdateResponse struct {
YAML string
}
type Suggestion struct {
ID string
Prompt string
UserSuggestion string
Suggestion string
}
type PipelineSuggestionsResponse struct {
Suggestions []Suggestion
}