Support Repo Migration APIs (create repo, import PRs, webhooks, and branch rules) (#2158)

* update migrator mdoule
* added formatted forbidden error helper
* delete work sum
* check branch rule identifier check
* match migrator data contract for webhooks
* self review
* self review
* self review, code cleaning
* resolve conflicts
* updated import api calls
* resolve conflicts
* lint
* bypass pre-receive block ref update on push if it's migrator push
* migrate controller and apis
* pull main
* add migrate routes to terminate path prefix apis
* move apis to migrate pkg
* Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into atmsn/import_meta
* import branch rules API
* wip import webhooks
This commit is contained in:
Atefeh Mohseni Ejiyeh 2024-08-07 23:40:09 +00:00 committed by Harness
parent 91291b8b27
commit cef6708a1e
38 changed files with 1611 additions and 108 deletions

View File

@ -62,8 +62,10 @@ func (c *Controller) PostReceive(
// as the branch could be different than the configured default value.
c.handleEmptyRepoPush(ctx, repo, in.PostReceiveInput, &out)
// report ref events (best effort)
c.reportReferenceEvents(ctx, rgit, repo, in.PrincipalID, in.PostReceiveInput)
// report ref events if repo is in an active state (best effort)
if repo.State == enum.RepoStateActive {
c.reportReferenceEvents(ctx, rgit, repo, in.PrincipalID, in.PostReceiveInput)
}
// handle branch updates related to PRs - best effort
c.handlePRMessaging(ctx, repo, in.PostReceiveInput, &out)

View File

@ -67,13 +67,13 @@ func (c *Controller) PreReceive(
}
// For external calls (git pushes) block modification of pullreq references.
if !in.Internal && c.blockPullReqRefUpdate(refUpdates) {
if !in.Internal && c.blockPullReqRefUpdate(refUpdates, repo.State) {
output.Error = ptr.String(usererror.ErrPullReqRefsCantBeModified.Error())
return output, nil
}
// For internal calls - through the application interface (API) - no need to verify protection rules.
if !in.Internal {
if !in.Internal && repo.State == enum.RepoStateActive {
// TODO: use store.PrincipalInfoCache once we abstracted principals.
principal, err := c.principalStore.Find(ctx, in.PrincipalID)
if err != nil {
@ -109,7 +109,11 @@ func (c *Controller) PreReceive(
return output, nil
}
func (c *Controller) blockPullReqRefUpdate(refUpdates changedRefs) bool {
func (c *Controller) blockPullReqRefUpdate(refUpdates changedRefs, state enum.RepoState) bool {
if state == enum.RepoStateMigrateGitPush {
return false
}
fn := func(ref string) bool {
return strings.HasPrefix(ref, gitReferenceNamePullReq)
}

View File

@ -15,21 +15,88 @@
package migrate
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/publicaccess"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
type Controller struct {
authorizer authz.Authorizer
principalStore store.PrincipalStore
authorizer authz.Authorizer
publicAccess publicaccess.Service
git git.Interface
urlProvider url.Provider
pullreqImporter *migrate.PullReq
ruleImporter *migrate.Rule
webhookImporter *migrate.Webhook
resourceLimiter limiter.ResourceLimiter
auditService audit.Service
identifierCheck check.RepoIdentifier
tx dbtx.Transactor
spaceStore store.SpaceStore
repoStore store.RepoStore
}
func NewController(
authorizer authz.Authorizer,
principalStore store.PrincipalStore,
publicAccess publicaccess.Service,
git git.Interface,
urlProvider url.Provider,
pullreqImporter *migrate.PullReq,
ruleImporter *migrate.Rule,
webhookImporter *migrate.Webhook,
resourceLimiter limiter.ResourceLimiter,
auditService audit.Service,
identifierCheck check.RepoIdentifier,
tx dbtx.Transactor,
spaceStore store.SpaceStore,
repoStore store.RepoStore,
) *Controller {
return &Controller{
authorizer: authorizer,
principalStore: principalStore,
authorizer: authorizer,
publicAccess: publicAccess,
git: git,
urlProvider: urlProvider,
pullreqImporter: pullreqImporter,
ruleImporter: ruleImporter,
webhookImporter: webhookImporter,
resourceLimiter: resourceLimiter,
auditService: auditService,
identifierCheck: identifierCheck,
tx: tx,
spaceStore: spaceStore,
repoStore: repoStore,
}
}
func (c *Controller) getRepoCheckAccess(ctx context.Context,
session *auth.Session, repoRef string, reqPermission enum.Permission) (*types.Repository, error) {
if repoRef == "" {
return nil, usererror.BadRequest("A valid repository reference must be provided.")
}
repo, err := c.repoStore.FindByRef(ctx, repoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repo: %w", err)
}
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, reqPermission); err != nil {
return nil, fmt.Errorf("failed to verify authorization: %w", err)
}
return repo, nil
}

View File

@ -0,0 +1,206 @@
// 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 migrate
import (
"context"
"database/sql"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
repoCtrl "github.com/harness/gitness/app/api/controller/repo"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/bootstrap"
"github.com/harness/gitness/app/githook"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
type CreateRepoInput struct {
ParentRef string `json:"parent_ref"`
Identifier string `json:"identifier"`
DefaultBranch string `json:"default_branch"`
IsPublic bool `json:"is_public"`
}
func (c *Controller) CreateRepo(
ctx context.Context,
session *auth.Session,
in *CreateRepoInput,
) (*repoCtrl.RepositoryOutput, error) {
if err := c.sanitizeCreateRepoInput(in); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
parentSpace, err := c.spaceCheckAuth(ctx, session, in.ParentRef)
if err != nil {
return nil, fmt.Errorf("failed to check auth in parent '%s': %w", in.ParentRef, err)
}
// generate envars (add everything githook CLI needs for execution)
envVars, err := githook.GenerateEnvironmentVariables(
ctx,
c.urlProvider.GetInternalAPIURL(ctx),
0,
session.Principal.ID,
true,
true,
)
if err != nil {
return nil, fmt.Errorf("failed to generate git hook environment variables: %w", err)
}
actor := &git.Identity{
Name: session.Principal.DisplayName,
Email: session.Principal.Email,
}
committer := bootstrap.NewSystemServiceSession().Principal
now := time.Now()
gitResp, err := c.git.CreateRepository(ctx, &git.CreateRepositoryParams{
Actor: *actor,
EnvVars: envVars,
DefaultBranch: in.DefaultBranch,
Files: nil,
Author: actor,
AuthorDate: &now,
Committer: &git.Identity{
Name: committer.DisplayName,
Email: committer.Email,
},
CommitterDate: &now,
})
if err != nil {
return nil, fmt.Errorf("failed to create git repository: %w", err)
}
var repo *types.Repository
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
if err := c.resourceLimiter.RepoCount(ctx, parentSpace.ID, 1); err != nil {
return fmt.Errorf("resource limit exceeded: %w", limiter.ErrMaxNumReposReached)
}
// lock the space for update during repo creation to prevent racing conditions with space soft delete.
parentSpace, err = c.spaceStore.FindForUpdate(ctx, parentSpace.ID)
if err != nil {
return fmt.Errorf("failed to find the parent space: %w", err)
}
repo = &types.Repository{
Version: 0,
ParentID: parentSpace.ID,
Identifier: in.Identifier,
GitUID: gitResp.UID,
CreatedBy: session.Principal.ID,
Created: now.UnixMilli(),
Updated: now.UnixMilli(),
DefaultBranch: in.DefaultBranch,
IsEmpty: true,
State: enum.RepoStateMigrateGitPush,
}
return c.repoStore.Create(ctx, repo)
}, sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil {
// TODO: best effort cleanup
return nil, fmt.Errorf("failed to create a repo on db: %w", err)
}
repo.GitURL = c.urlProvider.GenerateGITCloneURL(ctx, repo.Path)
repo.GitSSHURL = c.urlProvider.GenerateGITCloneSSHURL(ctx, repo.Path)
isPublicAccessSupported, err := c.publicAccess.IsPublicAccessSupported(ctx, parentSpace.Path)
if err != nil {
return nil, fmt.Errorf(
"failed to check if public access is supported for parent space %s: %w",
parentSpace.Path,
err,
)
}
isRepoPublic := in.IsPublic
if !isPublicAccessSupported {
log.Debug().Msgf("public access is not supported, create migrating repo %s as private instead", repo.Identifier)
isRepoPublic = false
}
err = c.publicAccess.Set(ctx, enum.PublicResourceTypeRepo, repo.Path, isRepoPublic)
if err != nil {
return nil, fmt.Errorf("failed to set repo access mode: %w", err)
}
err = c.auditService.Log(ctx,
session.Principal,
audit.NewResource(audit.ResourceTypeRepository, repo.Identifier),
audit.ActionCreated,
paths.Parent(repo.Path),
audit.WithNewObject(audit.RepositoryObject{
Repository: *repo,
IsPublic: isRepoPublic,
}),
audit.WithData("created by", "migrator"),
)
if err != nil {
log.Warn().Msgf("failed to insert audit log for import repository operation: %s", err)
}
return &repoCtrl.RepositoryOutput{
Repository: *repo,
IsPublic: isRepoPublic,
}, nil
}
func (c *Controller) spaceCheckAuth(
ctx context.Context,
session *auth.Session,
parentRef string,
) (*types.Space, error) {
space, err := c.spaceStore.FindByRef(ctx, parentRef)
if err != nil {
return nil, fmt.Errorf("parent space not found: %w", err)
}
// create is a special case - check permission without specific resource
scope := &types.Scope{SpacePath: space.Path}
resource := &types.Resource{
Type: enum.ResourceTypeRepo,
Identifier: "",
}
err = apiauth.Check(ctx, c.authorizer, session, scope, resource, enum.PermissionRepoEdit)
if err != nil {
return nil, fmt.Errorf("auth check failed: %w", err)
}
return space, nil
}
func (c *Controller) sanitizeCreateRepoInput(in *CreateRepoInput) error {
if err := repoCtrl.ValidateParentRef(in.ParentRef); err != nil {
return err
}
if err := c.identifierCheck(in.Identifier); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,19 @@
// 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 migrate
import "github.com/harness/gitness/errors"
var errInvalidRepoState = errors.PreconditionFailed("Repository data can't be imported at this point")

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type PullreqsInput struct {
PullRequestData []*migrate.ExternalPullRequest `json:"pull_request_data"`
}
func (c *Controller) PullRequests(
ctx context.Context,
session *auth.Session,
repoRef string,
in *PullreqsInput,
) ([]*types.PullReq, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if repo.State != enum.RepoStateMigrateDataImport {
return nil, fmt.Errorf("repo state is %s want %s: %w",
repo.State, enum.RepoStateMigrateDataImport, errInvalidRepoState)
}
pullreqs, err := c.pullreqImporter.Import(ctx, session.Principal, repo, in.PullRequestData)
if err != nil {
return nil, fmt.Errorf("failed to import pull requests and/or comments: %w", err)
}
return pullreqs, nil
}

View File

@ -0,0 +1,54 @@
// 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 migrate
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type RulesInput struct {
Rules []*migrate.ExternalRule `json:"rules"`
Type migrate.ExternalRuleType `json:"type"`
}
func (c *Controller) Rules(
ctx context.Context,
session *auth.Session,
repoRef string,
in *RulesInput,
) ([]*types.Rule, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if repo.State != enum.RepoStateMigrateDataImport {
return nil, fmt.Errorf("repo state is %s want %s: %w",
repo.State, enum.RepoStateMigrateDataImport, errInvalidRepoState)
}
rulesOut, err := c.ruleImporter.Import(ctx, session.Principal, repo, in.Type, in.Rules)
if err != nil {
return nil, fmt.Errorf("failed to import rules: %w", err)
}
return rulesOut, nil
}

View File

@ -0,0 +1,71 @@
// 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 migrate
import (
"context"
"fmt"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
var validTransitions = map[enum.RepoState][]enum.RepoState{
enum.RepoStateActive: {enum.RepoStateMigrateDataImport},
enum.RepoStateMigrateDataImport: {enum.RepoStateActive},
enum.RepoStateMigrateGitPush: {enum.RepoStateActive, enum.RepoStateMigrateDataImport},
}
type UpdateStateInput struct {
State enum.RepoState `json:"state"`
}
func (c *Controller) UpdateRepoState(
ctx context.Context,
session *auth.Session,
repoRef string,
in *UpdateStateInput,
) (*types.Repository, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if !stateTransitionValid(repo, in.State) {
return nil, usererror.BadRequestf("Changing repo state from %s to %s is not allowed.", repo.State, in.State)
}
repo, err = c.repoStore.UpdateOptLock(ctx, repo, func(r *types.Repository) error {
r.State = in.State
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to update the repo state: %w", err)
}
return repo, nil
}
func stateTransitionValid(repo *types.Repository, newState enum.RepoState) bool {
for _, validState := range validTransitions[repo.State] {
if validState == newState {
return true
}
}
return false
}

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type WebhooksInput struct {
Webhooks []*migrate.ExternalWebhook `json:"hooks"`
}
func (c *Controller) Webhooks(
ctx context.Context,
session *auth.Session,
repoRef string,
in *WebhooksInput,
) ([]*types.Webhook, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if repo.State != enum.RepoStateMigrateDataImport {
return nil, fmt.Errorf("repo state is %s want %s: %w",
repo.State, enum.RepoStateMigrateDataImport, errInvalidRepoState)
}
hookOut, err := c.webhookImporter.Import(ctx, session.Principal, repo, in.Webhooks)
if err != nil {
return nil, fmt.Errorf("failed to import webhooks: %w", err)
}
return hookOut, nil
}

View File

@ -15,8 +15,16 @@
package migrate
import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/publicaccess"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types/check"
"github.com/google/wire"
)
@ -28,10 +36,32 @@ var WireSet = wire.NewSet(
func ProvideController(
authorizer authz.Authorizer,
principalStore store.PrincipalStore,
publicAccess publicaccess.Service,
rpcClient git.Interface,
urlProvider url.Provider,
pullreqImporter *migrate.PullReq,
ruleImporter *migrate.Rule,
webhookImporter *migrate.Webhook,
resourceLimiter limiter.ResourceLimiter,
auditService audit.Service,
identifierCheck check.RepoIdentifier,
tx dbtx.Transactor,
spaceStore store.SpaceStore,
repoStore store.RepoStore,
) *Controller {
return NewController(
authorizer,
principalStore,
publicAccess,
rpcClient,
urlProvider,
pullreqImporter,
ruleImporter,
webhookImporter,
resourceLimiter,
auditService,
identifierCheck,
tx,
spaceStore,
repoStore,
)
}

View File

@ -26,9 +26,9 @@ import (
pullreqevents "github.com/harness/gitness/app/events/pullreq"
"github.com/harness/gitness/app/services/codecomments"
"github.com/harness/gitness/app/services/codeowners"
"github.com/harness/gitness/app/services/importer"
"github.com/harness/gitness/app/services/label"
locker "github.com/harness/gitness/app/services/locker"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/pullreq"
"github.com/harness/gitness/app/sse"
@ -66,7 +66,7 @@ type Controller struct {
sseStreamer sse.Streamer
codeOwners *codeowners.Service
locker *locker.Locker
importer *importer.PullReq
importer *migrate.PullReq
labelSvc *label.Service
}
@ -93,7 +93,7 @@ func NewController(
sseStreamer sse.Streamer,
codeowners *codeowners.Service,
locker *locker.Locker,
importer *importer.PullReq,
importer *migrate.PullReq,
labelSvc *label.Service,
) *Controller {
return &Controller{

View File

@ -19,9 +19,9 @@ import (
pullreqevents "github.com/harness/gitness/app/events/pullreq"
"github.com/harness/gitness/app/services/codecomments"
"github.com/harness/gitness/app/services/codeowners"
"github.com/harness/gitness/app/services/importer"
"github.com/harness/gitness/app/services/label"
"github.com/harness/gitness/app/services/locker"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/pullreq"
"github.com/harness/gitness/app/sse"
@ -48,7 +48,7 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer
checkStore store.CheckStore,
rpcClient git.Interface, eventReporter *pullreqevents.Reporter, codeCommentMigrator *codecomments.Migrator,
pullreqService *pullreq.Service, ruleManager *protection.Manager, sseStreamer sse.Streamer,
codeOwners *codeowners.Service, locker *locker.Locker, importer *importer.PullReq,
codeOwners *codeowners.Service, locker *locker.Locker, importer *migrate.PullReq,
labelSvc *label.Service,
) *Controller {
return NewController(tx, urlProvider, authorizer,

View File

@ -203,7 +203,7 @@ func (c *Controller) getRepoCheckAccessForGit(
)
}
func (c *Controller) validateParentRef(parentRef string) error {
func ValidateParentRef(parentRef string) error {
parentRefAsID, err := strconv.ParseInt(parentRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(parentRef)) == 0) {
return errRepositoryRequiresParent

View File

@ -208,7 +208,7 @@ func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
in.Identifier = in.UID
}
if err := c.validateParentRef(in.ParentRef); err != nil {
if err := ValidateParentRef(in.ParentRef); err != nil {
return err
}

View File

@ -123,7 +123,7 @@ func (c *Controller) sanitizeImportInput(in *ImportInput) error {
in.Identifier = in.UID
}
if err := c.validateParentRef(in.ParentRef); err != nil {
if err := ValidateParentRef(in.ParentRef); err != nil {
return err
}

View File

@ -32,8 +32,8 @@ const (
var ErrInternalWebhookOperationNotAllowed = usererror.Forbidden("changes to internal webhooks are not allowed")
// checkURL validates the url of a webhook.
func checkURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool) error {
// CheckURL validates the url of a webhook.
func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool) error {
// check URL
if len(rawURL) > webhookMaxURLLength {
return check.NewValidationErrorf("The URL of a webhook can be at most %d characters long.",
@ -84,8 +84,8 @@ func checkSecret(secret string) error {
return nil
}
// checkTriggers validates the triggers of a webhook.
func checkTriggers(triggers []enum.WebhookTrigger) error {
// CheckTriggers validates the triggers of a webhook.
func CheckTriggers(triggers []enum.WebhookTrigger) error {
// ignore duplicates here, should be deduplicated later
for _, trigger := range triggers {
if _, ok := trigger.Sanitize(); !ok {
@ -96,8 +96,8 @@ func checkTriggers(triggers []enum.WebhookTrigger) error {
return nil
}
// deduplicateTriggers de-duplicates the triggers provided by the user.
func deduplicateTriggers(in []enum.WebhookTrigger) []enum.WebhookTrigger {
// DeduplicateTriggers de-duplicates the triggers provided by the user.
func DeduplicateTriggers(in []enum.WebhookTrigger) []enum.WebhookTrigger {
if len(in) == 0 {
return []enum.WebhookTrigger{}
}
@ -114,3 +114,11 @@ func deduplicateTriggers(in []enum.WebhookTrigger) []enum.WebhookTrigger {
return out
}
func ConvertTriggers(vals []string) []enum.WebhookTrigger {
res := make([]enum.WebhookTrigger, len(vals))
for i := range vals {
res[i] = enum.WebhookTrigger(vals[i])
}
return res
}

View File

@ -92,7 +92,7 @@ func (c *Controller) Create(
Secret: string(encryptedSecret),
Enabled: in.Enabled,
Insecure: in.Insecure,
Triggers: deduplicateTriggers(in.Triggers),
Triggers: DeduplicateTriggers(in.Triggers),
LatestExecutionResult: nil,
}
@ -149,13 +149,13 @@ func sanitizeCreateInput(in *CreateInput, allowLoopback bool, allowPrivateNetwor
if err := check.Description(in.Description); err != nil {
return err
}
if err := checkURL(in.URL, allowLoopback, allowPrivateNetwork); err != nil {
if err := CheckURL(in.URL, allowLoopback, allowPrivateNetwork); err != nil {
return err
}
if err := checkSecret(in.Secret); err != nil {
return err
}
if err := checkTriggers(in.Triggers); err != nil { //nolint:revive
if err := CheckTriggers(in.Triggers); err != nil { //nolint:revive
return err
}

View File

@ -93,7 +93,7 @@ func (c *Controller) Update(
hook.Insecure = *in.Insecure
}
if in.Triggers != nil {
hook.Triggers = deduplicateTriggers(in.Triggers)
hook.Triggers = DeduplicateTriggers(in.Triggers)
}
if err = c.webhookStore.Update(ctx, hook); err != nil {
@ -125,7 +125,7 @@ func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwor
}
}
if in.URL != nil {
if err := checkURL(*in.URL, allowLoopback, allowPrivateNetwork); err != nil {
if err := CheckURL(*in.URL, allowLoopback, allowPrivateNetwork); err != nil {
return err
}
}
@ -135,7 +135,7 @@ func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwor
}
}
if in.Triggers != nil {
if err := checkTriggers(in.Triggers); err != nil {
if err := CheckTriggers(in.Triggers); err != nil {
return err
}
}

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 migrate
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
// HandleCreateRepo handles API that create an empty repo ready for migration.
func HandleCreateRepo(migCtrl *migrate.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
in := new(migrate.CreateRepoInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
repo, err := migCtrl.CreateRepo(ctx, session, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusCreated, repo)
}
}

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
// HandlePullRequests handles API that imports pull requests to a repository.
func HandlePullRequests(migCtrl *migrate.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
in := new(migrate.PullreqsInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
pullReqs, err := migCtrl.PullRequests(ctx, session, repoRef, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusCreated, pullReqs)
}
}

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
// HandleRules handles API that imports protection rule(s) to a repository.
func HandleRules(migCtrl *migrate.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
in := new(migrate.RulesInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
rules, err := migCtrl.Rules(ctx, session, repoRef, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusCreated, rules)
}
}

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
// HandleUpdateRepoState handles API that updates the repository state.
func HandleUpdateRepoState(migCtrl *migrate.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
in := new(migrate.UpdateStateInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
repo, err := migCtrl.UpdateRepoState(ctx, session, repoRef, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusOK, repo)
}
}

View File

@ -0,0 +1,53 @@
// 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 migrate
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
)
// HandleWebhooks returns a http.HandlerFunc that import webhooks.
func HandleWebhooks(migCtrl *migrate.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
repoRef, err := request.GetRepoRefFromPath(r)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
in := new(migrate.WebhooksInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return
}
hooks, err := migCtrl.Webhooks(ctx, session, repoRef, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return
}
render.JSON(w, http.StatusCreated, hooks)
}
}

View File

@ -43,6 +43,11 @@ func Forbidden(ctx context.Context, w http.ResponseWriter) {
UserError(ctx, w, usererror.ErrForbidden)
}
// Forbiddenf writes the json-encoded message with a forbidden error.
func Forbiddenf(ctx context.Context, w http.ResponseWriter, format string, args ...interface{}) {
UserError(ctx, w, usererror.Newf(http.StatusForbidden, format, args...))
}
// BadRequest writes the json-encoded message for a bad request error.
func BadRequest(ctx context.Context, w http.ResponseWriter) {
UserError(ctx, w, usererror.ErrBadRequest)

View File

@ -52,6 +52,7 @@ import (
handlerinfraProvider "github.com/harness/gitness/app/api/handler/infraprovider"
handlerkeywordsearch "github.com/harness/gitness/app/api/handler/keywordsearch"
handlerlogs "github.com/harness/gitness/app/api/handler/logs"
handlermigrate "github.com/harness/gitness/app/api/handler/migrate"
handlerpipeline "github.com/harness/gitness/app/api/handler/pipeline"
handlerplugin "github.com/harness/gitness/app/api/handler/plugin"
handlerprincipal "github.com/harness/gitness/app/api/handler/principal"
@ -92,7 +93,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/secrets/", "/v1/connectors", "/v1/templates/step", "/v1/templates/stage",
"/v1/gitspaces", "/v1/infraproviders", "/v1/migrate/repos"}
)
// NewAPIHandler returns a new APIHandler.
@ -658,6 +660,7 @@ func SetupRules(r chi.Router, repoCtrl *repo.Controller) {
r.Route("/rules", func(r chi.Router) {
r.Post("/", handlerrepo.HandleRuleCreate(repoCtrl))
r.Get("/", handlerrepo.HandleRuleList(repoCtrl))
r.Route(fmt.Sprintf("/{%s}", request.PathParamRuleIdentifier), func(r chi.Router) {
r.Patch("/", handlerrepo.HandleRuleUpdate(repoCtrl))
r.Delete("/", handlerrepo.HandleRuleDelete(repoCtrl))
@ -813,12 +816,16 @@ func setupAccountWithAuth(r chi.Router, userCtrl *user.Controller, config *types
r.Post("/logout", account.HandleLogout(userCtrl, cookieName))
}
func setupMigrate(r chi.Router, ctrl *migrate.Controller) {
func setupMigrate(r chi.Router, migCtrl *migrate.Controller) {
r.Route("/migrate", func(r chi.Router) {
SetupMigrateRoutes(r, ctrl)
r.Route("/repos", func(r chi.Router) {
r.Post("/", handlermigrate.HandleCreateRepo(migCtrl))
r.Route(fmt.Sprintf("/{%s}", request.PathParamRepoRef), func(r chi.Router) {
r.Patch("/update-state", handlermigrate.HandleUpdateRepoState(migCtrl))
r.Post("/pullreqs", handlermigrate.HandlePullRequests(migCtrl))
r.Post("/webhooks", handlermigrate.HandleWebhooks(migCtrl))
r.Post("/rules", handlermigrate.HandleRules(migCtrl))
})
})
})
}
func SetupMigrateRoutes(_ chi.Router, _ *migrate.Controller) {
// add migrate routes with spaces
}

View File

@ -32,7 +32,6 @@ import (
var WireSet = wire.NewSet(
ProvideRepoImporter,
ProvidePullReqImporter,
)
func ProvideRepoImporter(
@ -74,25 +73,3 @@ func ProvideRepoImporter(
return importer, nil
}
func ProvidePullReqImporter(
urlProvider url.Provider,
git git.Interface,
principalStore store.PrincipalStore,
repoStore store.RepoStore,
pullReqStore store.PullReqStore,
pullReqActStore store.PullReqActivityStore,
tx dbtx.Transactor,
) *PullReq {
importer := &PullReq{
urlProvider: urlProvider,
git: git,
principalStore: principalStore,
repoStore: repoStore,
pullReqStore: pullReqStore,
pullReqActStore: pullReqActStore,
tx: tx,
}
return importer
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package importer
package migrate
import (
"context"
@ -27,6 +27,7 @@ import (
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/parser"
gitness_store "github.com/harness/gitness/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
@ -34,7 +35,7 @@ import (
"github.com/rs/zerolog/log"
)
// PullReq is pull request importer.
// PullReq is pull request migrate.
type PullReq struct {
urlProvider url.Provider
git git.Interface
@ -45,6 +46,26 @@ type PullReq struct {
tx dbtx.Transactor
}
func NewPullReq(
urlProvider url.Provider,
git git.Interface,
principalStore store.PrincipalStore,
repoStore store.RepoStore,
pullReqStore store.PullReqStore,
pullReqActStore store.PullReqActivityStore,
tx dbtx.Transactor,
) *PullReq {
return &PullReq{
urlProvider: urlProvider,
git: git,
principalStore: principalStore,
repoStore: repoStore,
pullReqStore: pullReqStore,
pullReqActStore: pullReqActStore,
tx: tx,
}
}
type repoImportState struct {
git git.Interface
readParams git.ReadParams
@ -52,29 +73,30 @@ type repoImportState struct {
pullReqActivityStore store.PullReqActivityStore
branchCheck map[string]*git.Branch
principals map[string]*types.Principal
unknownEmails map[int]map[string]bool
migrator types.Principal
}
// Import load provided pull requests in go-scm format and imports them.
//
//nolint:gocognit
func (importer PullReq) Import(
func (migrate PullReq) Import(
ctx context.Context,
migrator types.Principal,
repo *types.Repository,
extPullReqs []*ExternalPullRequest,
) ([]*types.PullReq, error) {
if repo.State != enum.RepoStateMigrateDataImport {
return nil, errors.PreconditionFailed("Repository data can't be imported at this point")
}
readParams := git.ReadParams{RepoUID: repo.GitUID}
repoState := repoImportState{
git: importer.git,
git: migrate.git,
readParams: readParams,
principalStore: importer.principalStore,
pullReqActivityStore: importer.pullReqActStore,
principalStore: migrate.principalStore,
pullReqActivityStore: migrate.pullReqActStore,
branchCheck: map[string]*git.Branch{},
principals: map[string]*types.Principal{},
unknownEmails: map[int]map[string]bool{},
migrator: migrator,
}
pullReqUnique := map[int]struct{}{}
@ -103,13 +125,13 @@ func (importer PullReq) Import(
return nil, nil
}
err := importer.tx.WithTx(ctx, func(ctx context.Context) error {
err := migrate.tx.WithTx(ctx, func(ctx context.Context) error {
var deltaOpen, deltaClosed, deltaMerged int
var maxNumber int64
// Store the pull request objects and the comments.
for _, pullReq := range pullReqs {
if err := importer.pullReqStore.Create(ctx, pullReq); err != nil {
if err := migrate.pullReqStore.Create(ctx, pullReq); err != nil {
return fmt.Errorf("failed to import the pull request %d: %w", pullReq.Number, err)
}
@ -131,18 +153,28 @@ func (importer PullReq) Import(
return fmt.Errorf("failed to import pull request comments: %w", err)
}
// Add a comment if any principal (PR author or commenter) were replaced by the fallback migrator principal
if prUnknownEmails, ok := repoState.unknownEmails[int(pullReq.Number)]; ok && len(prUnknownEmails) != 0 {
infoComment, err := repoState.createInfoComment(ctx, repo, pullReq)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to add an informational comment for replacing non-existing users")
} else {
comments = append(comments, infoComment)
}
}
if len(comments) == 0 { // no need to update the pull request object in the DB if there are no comments.
continue
}
if err := importer.pullReqStore.Update(ctx, pullReq); err != nil {
if err := migrate.pullReqStore.Update(ctx, pullReq); err != nil {
return fmt.Errorf("failed to update pull request after importing of the comments: %w", err)
}
}
// Update the repository
repoUpdate, err := importer.repoStore.Find(ctx, repo.ID)
repoUpdate, err := migrate.repoStore.Find(ctx, repo.ID)
if err != nil {
return fmt.Errorf("failed to fetch repo in pull request import: %w", err)
}
@ -155,7 +187,7 @@ func (importer PullReq) Import(
repoUpdate.NumClosedPulls += deltaClosed
repoUpdate.NumMergedPulls += deltaMerged
if err := importer.repoStore.Update(ctx, repoUpdate); err != nil {
if err := migrate.repoStore.Update(ctx, repoUpdate); err != nil {
return fmt.Errorf("failed to update repo in pull request import: %w", err)
}
@ -181,7 +213,7 @@ func (r *repoImportState) convertPullReq(
Int("pullreq.number", extPullReq.Number).
Logger()
author, err := r.getPrincipalByEmail(ctx, extPullReq.Author.Email)
author, err := r.getPrincipalByEmail(ctx, extPullReq.Author.Email, extPullReq.Number, false)
if err != nil {
return nil, fmt.Errorf("failed to get pull request author: %w", err)
}
@ -200,7 +232,7 @@ func (r *repoImportState) convertPullReq(
Edited: updatedAt,
Closed: nil,
State: enum.PullReqStateOpen,
IsDraft: false,
IsDraft: extPullReq.Draft,
CommentCount: 0,
UnresolvedCount: 0,
Title: extPullReq.Title,
@ -239,14 +271,16 @@ func (r *repoImportState) convertPullReq(
pr.MergeCheckStatus = enum.MergeCheckStatusMergeable
pr.SourceSHA = extPullReq.Head.SHA
pr.MergeTargetSHA = &extPullReq.Base.SHA
pr.MergeBaseSHA = extPullReq.Head.SHA // Don't have the real value. Set the value to SourceSHA.
pr.MergeSHA = nil // Don't have this.
pr.MergeBaseSHA = extPullReq.Base.SHA
pr.MergeSHA = nil // Don't have this.
pr.MergeConflicts = nil
case enum.PullReqStateClosed:
// For closed PR's it's not important to verify existence of branches and commits.
// If these don't exist the PR will be impossible to open.
pr.SourceSHA = extPullReq.Head.SHA
pr.MergeTargetSHA = &extPullReq.Base.SHA
pr.MergeBaseSHA = extPullReq.Base.SHA
pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked
pr.MergeSHA = nil
pr.MergeConflicts = nil
@ -353,11 +387,10 @@ func (r *repoImportState) createComment(
order, subOrder, replySeq int,
extComment *ExternalComment,
) (*types.PullReqActivity, error) {
commenter, err := r.getPrincipalByEmail(ctx, extComment.Author.Email)
commenter, err := r.getPrincipalByEmail(ctx, extComment.Author.Email, int(pullReq.Number), false)
if err != nil {
return nil, fmt.Errorf("failed to get comment ID=%d author: %w", extComment.ID, err)
}
commentedAt := extComment.Created.UnixMilli()
// Mark comments as resolved if the PR is merged, otherwise they are unresolved.
@ -440,17 +473,87 @@ func (r *repoImportState) createComment(
return comment, nil
}
func (r *repoImportState) getPrincipalByEmail(ctx context.Context, emailAddress string) (*types.Principal, error) {
// createInfoComment creates an informational comment on the PR
// if any of the principals were replaced with the migrator.
func (r *repoImportState) createInfoComment(
ctx context.Context,
repo *types.Repository,
pullReq *types.PullReq,
) (*types.PullReqActivity, error) {
var unknownEmails []string
for email := range r.unknownEmails[int(pullReq.Number)] {
unknownEmails = append(unknownEmails, email)
}
now := time.Now().UnixMilli()
text := fmt.Sprintf(InfoCommentMessage, r.migrator.UID, strings.Join(unknownEmails, ", "))
comment := &types.PullReqActivity{
CreatedBy: r.migrator.ID,
Created: now,
Updated: now,
Deleted: nil,
ParentID: nil,
RepoID: repo.ID,
PullReqID: pullReq.ID,
Order: pullReq.ActivitySeq + 1,
SubOrder: 0,
ReplySeq: 0,
Type: enum.PullReqActivityTypeComment,
Kind: enum.PullReqActivityKindComment,
Text: text,
PayloadRaw: json.RawMessage("{}"),
Metadata: nil,
ResolvedBy: &r.migrator.ID,
Resolved: &now,
CodeComment: nil,
Mentions: nil,
}
if err := r.pullReqActivityStore.Create(ctx, comment); err != nil {
return nil, fmt.Errorf("failed to store the info comment author: %w", err)
}
pullReq.ActivitySeq++
pullReq.CommentCount++
return comment, nil
}
func (r *repoImportState) getPrincipalByEmail(
ctx context.Context,
emailAddress string,
prNumber int,
strict bool,
) (*types.Principal, error) {
if principal, exists := r.principals[emailAddress]; exists {
return principal, nil
}
principal, err := r.principalStore.FindByEmail(ctx, emailAddress)
if err != nil {
if err != nil && !errors.Is(err, gitness_store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to load principal by email: %w", err)
}
return principal, nil
if err == nil {
r.principals[emailAddress] = principal
return principal, nil
}
if strict {
return nil, fmt.Errorf(
"could not find principal by email %s and automatic replacing unknown prinicapls is disabled: %w",
emailAddress, err)
}
// ignore not found emails if is not strict
if _, exists := r.unknownEmails[prNumber]; !exists {
r.unknownEmails[prNumber] = make(map[string]bool, 0)
}
if _, ok := r.unknownEmails[prNumber][emailAddress]; !ok && len(r.unknownEmails[prNumber]) < MaxNumberOfUnknownEmails {
r.unknownEmails[prNumber][emailAddress] = true
}
return &r.migrator, nil
}
func timestampMillis(t time.Time, def int64) int64 {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package importer
package migrate
import (
"testing"

View File

@ -0,0 +1,32 @@
// 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 migrate
import migratetypes "github.com/harness/harness-migrate/types"
type ExternalPullRequest = migratetypes.PullRequestData
type ExternalComment = migratetypes.Comment
type externalCommentThread struct {
TopLevel ExternalComment
Replies []ExternalComment
}
const (
InfoCommentMessage = "This pull request has been imported. Non-existent users who were originally listed " +
"as the pull request author or commenter have been replaced by the principal '%s' which performed the migration.\n" +
"Unknown emails: %v"
MaxNumberOfUnknownEmails = 500 // limit keeping unknown users to avoid info comment text exceed ~1000 characters
)

View File

@ -0,0 +1,178 @@
// 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 migrate
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/errors"
gitness_store "github.com/harness/gitness/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
migratetypes "github.com/harness/harness-migrate/types"
"github.com/rs/zerolog/log"
)
type Rule struct {
ruleStore store.RuleStore
principalStore store.PrincipalStore
tx dbtx.Transactor
DefDeserializationMap map[migratetypes.RuleType]definitionDeserializer
PatternDeserializationMap map[migratetypes.RuleType]patternDeserializer
}
func NewRule(
ruleStore store.RuleStore,
tx dbtx.Transactor,
principalStore store.PrincipalStore,
) *Rule {
rule := &Rule{
ruleStore: ruleStore,
principalStore: principalStore,
tx: tx,
DefDeserializationMap: make(map[ExternalRuleType]definitionDeserializer),
PatternDeserializationMap: make(map[ExternalRuleType]patternDeserializer),
}
rule.registerDeserializers(principalStore)
return rule
}
func (migrate Rule) Import(
ctx context.Context,
migrator types.Principal,
repo *types.Repository,
typ ExternalRuleType,
extRules []*ExternalRule,
) ([]*types.Rule, error) {
rules := make([]*types.Rule, len(extRules))
for i, extRule := range extRules {
if err := check.Identifier(extRule.Identifier); err != nil {
return nil, fmt.Errorf("branch rule identifier '%s' is invalid: %w", extRule.Identifier, err)
}
def, err := migrate.DefDeserializationMap[typ](ctx, string(extRule.Definition))
if err != nil {
return nil, fmt.Errorf("failed to deserialize rule definition: %w", err)
}
if err = def.Sanitize(); err != nil {
return nil, fmt.Errorf("provided rule definition is invalid: %w", err)
}
definitionJSON, err := json.Marshal(def)
if err != nil {
return nil, fmt.Errorf("failed to marshal rule definition: %w", err)
}
pattern, err := migrate.PatternDeserializationMap[typ](ctx, string(extRule.Pattern))
if err != nil {
return nil, fmt.Errorf("failed to deserialize rule pattern: %w", err)
}
if err = pattern.Validate(); err != nil {
return nil, fmt.Errorf("provided rule pattern is invalid: %w", err)
}
now := time.Now().UnixMilli()
r := &types.Rule{
CreatedBy: migrator.ID,
Created: now,
Updated: now,
RepoID: &repo.ID,
SpaceID: nil,
Type: protection.TypeBranch,
State: enum.RuleStateActive,
Identifier: extRule.Identifier,
Pattern: pattern.JSON(),
Definition: json.RawMessage(definitionJSON),
}
rules[i] = r
}
err := migrate.tx.WithTx(ctx, func(ctx context.Context) error {
for _, rule := range rules {
err := migrate.ruleStore.Create(ctx, rule)
if err != nil {
return fmt.Errorf("failed to create branch rule: %w", err)
}
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to store external branch rules: %w", err)
}
return rules, nil
}
func mapToBranchRules(
ctx context.Context,
rule ExternalDefinition,
principalStore store.PrincipalStore,
) (*protection.Branch, error) {
// map users
var userIDs []int64
for _, email := range rule.Bypass.UserEmails {
principal, err := principalStore.FindByEmail(ctx, email)
if err != nil && !errors.Is(err, gitness_store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to find principal by email for '%s': %w", email, err)
}
if errors.Is(err, gitness_store.ErrResourceNotFound) {
log.Ctx(ctx).Warn().Msgf("skipping principal '%s' on bypass list", email)
continue
}
userIDs = append(userIDs, principal.ID)
}
return &protection.Branch{
Bypass: protection.DefBypass{
UserIDs: userIDs,
RepoOwners: rule.Bypass.RepoOwners,
},
PullReq: protection.DefPullReq{
Approvals: protection.DefApprovals(rule.PullReq.Approvals),
Comments: protection.DefComments(rule.PullReq.Comments),
StatusChecks: protection.DefStatusChecks(rule.PullReq.StatusChecks),
Merge: protection.DefMerge{
StrategiesAllowed: convertMergeMethods(rule.PullReq.Merge.StrategiesAllowed),
DeleteBranch: rule.PullReq.Merge.DeleteBranch,
},
},
Lifecycle: protection.DefLifecycle(rule.Lifecycle),
}, nil
}
func convertMergeMethods(vals []string) []enum.MergeMethod {
res := make([]enum.MergeMethod, len(vals))
for i := range vals {
res[i] = enum.MergeMethod(vals[i])
}
return res
}

View File

@ -0,0 +1,82 @@
// 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 migrate
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/store"
migratetypes "github.com/harness/harness-migrate/types"
)
const ExternalRuleTypeBranch = migratetypes.RuleTypeBranch
type (
ExternalRuleType = migratetypes.RuleType
ExternalRule = migratetypes.Rule
ExternalDefinition = migratetypes.Definition
ExternalBranchPattern = migratetypes.BranchPattern
definitionDeserializer func(context.Context, string) (protection.Definition, error)
patternDeserializer func(context.Context, string) (*protection.Pattern, error)
)
func (migrate *Rule) registerDeserializers(principalStore store.PrincipalStore) {
// banch rules definition deserializer
migrate.DefDeserializationMap[ExternalRuleTypeBranch] = func(
ctx context.Context,
rawDef string,
) (protection.Definition, error) {
// deserialize string into external branch rule type
var extrDef ExternalDefinition
decoder := json.NewDecoder(bytes.NewReader([]byte(rawDef)))
if err := decoder.Decode(&extrDef); err != nil {
return nil, fmt.Errorf("failed to decode external branch rule definition: %w", err)
}
rule, err := mapToBranchRules(ctx, extrDef, principalStore)
if err != nil {
return nil, fmt.Errorf("failed to map external branch rule definition to internal: %w", err)
}
return rule, nil
}
// branch rules pattern deserializer
migrate.PatternDeserializationMap[ExternalRuleTypeBranch] = func(
_ context.Context,
rawDef string,
) (*protection.Pattern, error) {
var extrPattern ExternalBranchPattern
decoder := json.NewDecoder(bytes.NewReader([]byte(rawDef)))
if err := decoder.Decode(&extrPattern); err != nil {
return nil, fmt.Errorf("failed to decode external branch rule pattern: %w", err)
}
return &protection.Pattern{
Default: extrPattern.Default,
Include: extrPattern.Include,
Exclude: extrPattern.Exclude,
}, nil
}
}

View File

@ -0,0 +1,129 @@
// 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 migrate
import (
"context"
"fmt"
"time"
webhookpkg "github.com/harness/gitness/app/api/controller/webhook"
"github.com/harness/gitness/app/services/webhook"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// Webhook is webhook migrate.
type Webhook struct {
// webhook configs
allowLoopback bool
allowPrivateNetwork bool
tx dbtx.Transactor
webhookStore store.WebhookStore
}
func NewWebhook(
config webhook.Config,
tx dbtx.Transactor,
webhookStore store.WebhookStore,
) *Webhook {
return &Webhook{
allowLoopback: config.AllowLoopback,
allowPrivateNetwork: config.AllowPrivateNetwork,
tx: tx,
webhookStore: webhookStore,
}
}
func (migrate Webhook) Import(
ctx context.Context,
migrator types.Principal,
repo *types.Repository,
extWebhooks []*ExternalWebhook,
) ([]*types.Webhook, error) {
now := time.Now().UnixMilli()
hooks := make([]*types.Webhook, len(extWebhooks))
// sanitize and convert webhooks
for i, whook := range extWebhooks {
triggers := webhookpkg.ConvertTriggers(whook.Events)
err := sanitizeWebhook(whook, triggers, migrate.allowLoopback, migrate.allowPrivateNetwork)
if err != nil {
return nil, fmt.Errorf("failed to sanitize external webhook input: %w", err)
}
// create new webhook object
hook := &types.Webhook{
ID: 0, // the ID will be populated in the data layer
Version: 0, // the Version will be populated in the data layer
CreatedBy: migrator.ID,
Created: now,
Updated: now,
ParentID: repo.ID,
ParentType: enum.WebhookParentRepo,
// user input
Identifier: whook.Identifier,
DisplayName: whook.Identifier,
URL: whook.Target,
Enabled: whook.Active,
Insecure: whook.SkipVerify,
Triggers: webhookpkg.DeduplicateTriggers(triggers),
LatestExecutionResult: nil,
}
hooks[i] = hook
}
err := migrate.tx.WithTx(ctx, func(ctx context.Context) error {
for _, hook := range hooks {
err := migrate.webhookStore.Create(ctx, hook)
if err != nil {
return fmt.Errorf("failed to store webhook: %w", err)
}
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to store external webhooks: %w", err)
}
return hooks, nil
}
func sanitizeWebhook(
in *ExternalWebhook,
triggers []enum.WebhookTrigger,
allowLoopback bool,
allowPrivateNetwork bool,
) error {
if err := check.Identifier(in.Identifier); err != nil {
return err
}
if err := webhookpkg.CheckURL(in.Target, allowLoopback, allowPrivateNetwork); err != nil {
return err
}
if err := webhookpkg.CheckTriggers(triggers); err != nil { //nolint:revive
return err
}
return nil
}

View File

@ -12,16 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package importer
package migrate
import (
migratetypes "github.com/harness/harness-migrate/types"
import migratetypes "github.com/harness/harness-migrate/types"
type (
ExternalWebhook = migratetypes.Hook
)
type ExternalPullRequest = migratetypes.PullRequestData
type ExternalComment = migratetypes.Comment
type externalCommentThread struct {
TopLevel ExternalComment
Replies []ExternalComment
}

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 migrate
import (
"github.com/harness/gitness/app/services/webhook"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store/database/dbtx"
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvidePullReqImporter,
ProvideRuleImporter,
ProvideWebhookImporter,
)
func ProvidePullReqImporter(
urlProvider url.Provider,
git git.Interface,
principalStore store.PrincipalStore,
repoStore store.RepoStore,
pullReqStore store.PullReqStore,
pullReqActStore store.PullReqActivityStore,
tx dbtx.Transactor,
) *PullReq {
return NewPullReq(urlProvider, git, principalStore, repoStore, pullReqStore, pullReqActStore, tx)
}
func ProvideRuleImporter(
ruleStore store.RuleStore,
tx dbtx.Transactor,
principalStore store.PrincipalStore,
) *Rule {
return NewRule(ruleStore, tx, principalStore)
}
func ProvideWebhookImporter(
config webhook.Config,
tx dbtx.Transactor,
webhookStore store.WebhookStore,
) *Webhook {
return NewWebhook(config, tx, webhookStore)
}

View File

@ -74,6 +74,7 @@ import (
svclabel "github.com/harness/gitness/app/services/label"
locker "github.com/harness/gitness/app/services/locker"
"github.com/harness/gitness/app/services/metric"
migrateservice "github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/app/services/protection"
@ -203,6 +204,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
plugin.WireSet,
resolver.WireSet,
importer.WireSet,
migrateservice.WireSet,
canceler.WireSet,
exporter.WireSet,
metric.WireSet,

View File

@ -18,7 +18,7 @@ import (
keywordsearch2 "github.com/harness/gitness/app/api/controller/keywordsearch"
"github.com/harness/gitness/app/api/controller/limiter"
logs2 "github.com/harness/gitness/app/api/controller/logs"
"github.com/harness/gitness/app/api/controller/migrate"
migrate2 "github.com/harness/gitness/app/api/controller/migrate"
"github.com/harness/gitness/app/api/controller/pipeline"
"github.com/harness/gitness/app/api/controller/plugin"
"github.com/harness/gitness/app/api/controller/principal"
@ -75,6 +75,7 @@ import (
"github.com/harness/gitness/app/services/label"
"github.com/harness/gitness/app/services/locker"
"github.com/harness/gitness/app/services/metric"
"github.com/harness/gitness/app/services/migrate"
"github.com/harness/gitness/app/services/notification"
"github.com/harness/gitness/app/services/notification/mailer"
"github.com/harness/gitness/app/services/protection"
@ -302,7 +303,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
if err != nil {
return nil, err
}
pullReq := importer.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor)
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)
webhookConfig := server.ProvideWebhookConfig(config)
webhookStore := database.ProvideWebhookStore(db)
@ -366,7 +367,9 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter4, orchestratorConfig, vsCode, vsCodeWeb)
gitspaceEventStore := database.ProvideGitspaceEventStore(db)
gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter4, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore, gitspaceService)
migrateController := migrate.ProvideController(authorizer, principalStore)
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)
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)
serverServer := server2.ProvideServer(config, routerRouter)

25
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/drone/funcmap v0.0.0-20240227160611-7e19e9cd5a1c
github.com/drone/go-convert v0.0.0-20240307072510-6bd371c65e61
github.com/drone/go-generate v1.1.0
github.com/drone/go-scm v1.38.2
github.com/drone/go-scm v1.38.4
github.com/drone/runner-go v1.12.0
github.com/drone/spec v0.0.0-20230920145636-3827abdce961
github.com/fatih/color v1.17.0
@ -36,7 +36,7 @@ require (
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
github.com/gotidy/ptr v1.4.0
github.com/guregu/null v4.0.0+incompatible
github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8
github.com/harness/harness-migrate v0.21.1-0.20240804180936-b1de602aa8e7
github.com/hashicorp/go-multierror v1.1.1
github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1
@ -80,6 +80,10 @@ require (
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
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/antonmedv/expr v1.15.5 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
@ -90,15 +94,21 @@ require (
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/charmbracelet/lipgloss v0.12.1 // indirect
github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
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/emirpasic/gods v1.18.1 // 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
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gitleaks/go-gitdiff v0.9.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/go-git/go-git/v5 v5.5.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -107,11 +117,15 @@ require (
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/h2non/filetype v1.1.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jackc/pgx/v4 v4.12.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/muesli/termenv v0.15.2 // indirect
@ -121,6 +135,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
@ -129,12 +144,17 @@ require (
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/schollz/progressbar/v3 v3.13.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
@ -150,6 +170,7 @@ require (
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

93
go.sum
View File

@ -34,14 +34,23 @@ 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.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
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=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -60,6 +69,7 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -86,6 +96,7 @@ github.com/bradrydzewski/spec v1.0.2 h1:spQ8nLjsV4IFieEqHAnob5S4Vfx+0EQG/L91mVVr
github.com/bradrydzewski/spec v1.0.2/go.mod h1:ZrrxQBqUvBoqAaH9ClsskjE3LPMHra+NbKdqDIQqRNQ=
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
@ -100,6 +111,9 @@ github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@ -117,6 +131,7 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -133,6 +148,8 @@ github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ
github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE=
github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
@ -163,6 +180,8 @@ github.com/drone/go-generate v1.1.0 h1:JoWB4emGS/hlIaKNlcI1Amjvm5ajub1lqocLHMSwu
github.com/drone/go-generate v1.1.0/go.mod h1:rnOS/ww//Mz5XM88u6+xT3Bd2ftJdYAYi1k2vtGUkUQ=
github.com/drone/go-scm v1.38.2 h1:u8fEuz2vbcn5y93YPiT0nkcmQKKAxNuL08N0xuHCLTY=
github.com/drone/go-scm v1.38.2/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw=
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/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc=
@ -173,6 +192,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -198,12 +219,21 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gitleaks/go-gitdiff v0.9.0 h1:SHAU2l0ZBEo8g82EeFewhVy81sb7JCxW76oSPtR/Nqg=
github.com/gitleaks/go-gitdiff v0.9.0/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE=
github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw=
github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
@ -328,6 +358,10 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8 h1:X0/Nk9aCkIKoF3kavxZcr9EtfARSBqnJK33c59m+xmw=
github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8/go.mod h1:HiUX39ZfhBGY+OkEEN6JpglbodNJlbzayOt/l3nkk6w=
github.com/harness/harness-migrate v0.21.1-0.20240801225636-542cd216f84e h1:Do+0drtv74/mOkXo07mj9JMIn6jy6dDlK6FrRW7pgpI=
github.com/harness/harness-migrate v0.21.1-0.20240801225636-542cd216f84e/go.mod h1:DSqjRv/0GKxBTkqgayOOX/rIktw9KTIBMxJUvHdZK4Q=
github.com/harness/harness-migrate v0.21.1-0.20240804180936-b1de602aa8e7 h1:hvQPC9k0V4tSYccVLqYFbT7HPH9kzwRqcInfbK6z7ag=
github.com/harness/harness-migrate v0.21.1-0.20240804180936-b1de602aa8e7/go.mod h1:hlyMG5yMpTUesICvSYz61mInT+dMGN7HdTujv+RHcgU=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -359,6 +393,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
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/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
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=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
@ -419,6 +455,9 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@ -435,8 +474,11 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@ -444,6 +486,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -475,6 +518,7 @@ github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3eu
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -491,10 +535,12 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@ -503,6 +549,8 @@ github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxU
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
@ -536,6 +584,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
@ -573,6 +622,8 @@ github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzk
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -612,6 +663,7 @@ github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -633,10 +685,13 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY=
github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@ -646,8 +701,11 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -695,8 +753,6 @@ github.com/swaggest/jsonschema-go v0.3.72 h1:IHaGlR1bdBUBPfhe4tfacN2TGAPKENEGiNy
github.com/swaggest/jsonschema-go v0.3.72/go.mod h1:OrGyEoVqpfSFJ4Am4V/FQcQ3mlEC1vVeleA+5ggbVW4=
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/openapi-go v0.2.53 h1:lWHKgC9IN48nBYxvuBrmAVJgki/1xsrGZWaWJnOLenE=
github.com/swaggest/openapi-go v0.2.53/go.mod h1:2Q7NpuG9NgpGeTaNOo852GSR6cCzSP4IznA9DNdUTQw=
github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I=
github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg=
github.com/swaggest/swgui v1.8.1 h1:OLcigpoelY0spbpvp6WvBt0I1z+E9egMQlUeEKya+zU=
@ -709,6 +765,11 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/vearutop/statigz v1.4.0 h1:RQL0KG3j/uyA/PFpHeZ/L6l2ta920/MxlOAIGEOuwmU=
github.com/vearutop/statigz v1.4.0/go.mod h1:LYTolBLiz9oJISwiVKnOQoIwhO1LWX1A7OECawGS8XE=
github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d/go.mod h1:mb5taDqMnJiZNRQ3+02W2IFG+oEz1+dTuCXkp4jpkfo=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
@ -749,8 +810,6 @@ go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lI
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.starlark.net v0.0.0-20231121155337-90ade8b19d09 h1:hzy3LFnSN8kuQK8h9tHl4ndF6UruMj47OqwqsS+/Ai4=
go.starlark.net v0.0.0-20231121155337-90ade8b19d09/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
go.starlark.net v0.0.0-20240705175910-70002002b310 h1:tEAOMoNmN2MqVNi0MMEWpTtPI4YNCXgxmAGtuv3mST0=
go.starlark.net v0.0.0-20240705175910-70002002b310/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -782,6 +841,10 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
@ -825,7 +888,10 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
@ -872,14 +938,24 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -890,6 +966,9 @@ golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
@ -902,6 +981,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
@ -993,6 +1073,8 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
@ -1008,14 +1090,17 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=