diff --git a/internal/api/controller/githook/post_receive.go b/internal/api/controller/githook/post_receive.go index efea428e3..326c887c0 100644 --- a/internal/api/controller/githook/post_receive.go +++ b/internal/api/controller/githook/post_receive.go @@ -12,8 +12,6 @@ import ( "github.com/harness/gitness/internal/auth" events "github.com/harness/gitness/internal/events/git" "github.com/harness/gitness/types" - - "github.com/rs/zerolog/log" ) const ( @@ -96,13 +94,28 @@ func (c *Controller) reportTagEvent(ctx context.Context, principalID int64, repoID int64, tagUpdate ReferenceUpdate) { switch { case tagUpdate.Old == types.NilSHA: - // TODO + c.gitReporter.TagCreated(ctx, &events.TagCreatedPayload{ + RepoID: repoID, + PrincipalID: principalID, + Ref: tagUpdate.Ref, + SHA: tagUpdate.New, + }) case tagUpdate.New == types.NilSHA: - // TODO + c.gitReporter.TagDeleted(ctx, &events.TagDeletedPayload{ + RepoID: repoID, + PrincipalID: principalID, + Ref: tagUpdate.Ref, + SHA: tagUpdate.Old, + }) default: - // TODO + c.gitReporter.TagUpdated(ctx, &events.TagUpdatedPayload{ + RepoID: repoID, + PrincipalID: principalID, + Ref: tagUpdate.Ref, + OldSHA: tagUpdate.Old, + NewSHA: tagUpdate.New, + // tags can only be force updated! + Forced: true, + }) } - - log.Ctx(ctx).Info().Msgf("received a change for tag '%s' in repo %d by principal %d (SHA: %s -> %s)'", - tagUpdate.Ref, repoID, principalID, tagUpdate.Old, tagUpdate.New) } diff --git a/internal/events/git/branch.go b/internal/events/git/branch.go new file mode 100644 index 000000000..7ad9751dd --- /dev/null +++ b/internal/events/git/branch.go @@ -0,0 +1,84 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package events + +import ( + "context" + + "github.com/harness/gitness/events" + + "github.com/rs/zerolog/log" +) + +const BranchCreatedEvent events.EventType = "branchcreated" + +type BranchCreatedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` +} + +func (r *Reporter) BranchCreated(ctx context.Context, payload *BranchCreatedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchCreatedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send branch created event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported branch created event with id '%s'", eventID) +} + +func (r *Reader) RegisterBranchCreated(fn func(context.Context, *events.Event[*BranchCreatedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, BranchCreatedEvent, fn) +} + +const BranchUpdatedEvent events.EventType = "branchupdated" + +type BranchUpdatedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + OldSHA string `json:"old_sha"` + NewSHA string `json:"new_sha"` + // Forced bool `json:"forced"` TODO: data not available yet. +} + +func (r *Reporter) BranchUpdated(ctx context.Context, payload *BranchUpdatedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchUpdatedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send branch updated event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported branch updated event with id '%s'", eventID) +} + +func (r *Reader) RegisterBranchUpdated(fn func(context.Context, *events.Event[*BranchUpdatedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, BranchUpdatedEvent, fn) +} + +const BranchDeletedEvent events.EventType = "branchdeleted" + +type BranchDeletedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` +} + +func (r *Reporter) BranchDeleted(ctx context.Context, payload *BranchDeletedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchDeletedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send branch deleted event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported branch deleted event with id '%s'", eventID) +} + +func (r *Reader) RegisterBranchDeleted(fn func(context.Context, *events.Event[*BranchDeletedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, BranchDeletedEvent, fn) +} diff --git a/internal/events/git/branch_created.go b/internal/events/git/branch_created.go deleted file mode 100644 index 7fad1d08a..000000000 --- a/internal/events/git/branch_created.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 Harness Inc. All rights reserved. -// Use of this source code is governed by the Polyform Free Trial License -// that can be found in the LICENSE.md file for this repository. - -package events - -import ( - "context" - - "github.com/harness/gitness/events" - - "github.com/rs/zerolog/log" -) - -const BranchCreatedEvent events.EventType = "branchcreated" - -type BranchCreatedPayload struct { - RepoID int64 `json:"repo_id"` - PrincipalID int64 `json:"principal_id"` - Ref string `json:"ref"` - SHA string `json:"sha"` -} - -func (r *Reporter) BranchCreated(ctx context.Context, payload *BranchCreatedPayload) { - eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchCreatedEvent, payload) - if err != nil { - log.Ctx(ctx).Err(err).Msgf("failed to send branch created event") - return - } - - log.Ctx(ctx).Debug().Msgf("reported branch created event with id '%s'", eventID) -} - -func (r *Reader) RegisterBranchCreated(fn func(context.Context, *events.Event[*BranchCreatedPayload]) error) error { - return events.ReaderRegisterEvent(r.innerReader, BranchCreatedEvent, fn) -} diff --git a/internal/events/git/branch_deleted.go b/internal/events/git/branch_deleted.go deleted file mode 100644 index 6b37d41bf..000000000 --- a/internal/events/git/branch_deleted.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 Harness Inc. All rights reserved. -// Use of this source code is governed by the Polyform Free Trial License -// that can be found in the LICENSE.md file for this repository. - -package events - -import ( - "context" - - "github.com/harness/gitness/events" - - "github.com/rs/zerolog/log" -) - -const BranchDeletedEvent events.EventType = "branchdeleted" - -type BranchDeletedPayload struct { - RepoID int64 `json:"repo_id"` - PrincipalID int64 `json:"principal_id"` - Ref string `json:"ref"` - SHA string `json:"sha"` -} - -func (r *Reporter) BranchDeleted(ctx context.Context, payload *BranchDeletedPayload) { - eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchDeletedEvent, payload) - if err != nil { - log.Ctx(ctx).Err(err).Msgf("failed to send branch deleted event") - return - } - - log.Ctx(ctx).Debug().Msgf("reported branch deleted event with id '%s'", eventID) -} - -func (r *Reader) RegisterBranchDeleted(fn func(context.Context, *events.Event[*BranchDeletedPayload]) error) error { - return events.ReaderRegisterEvent(r.innerReader, BranchDeletedEvent, fn) -} diff --git a/internal/events/git/branch_updated.go b/internal/events/git/branch_updated.go deleted file mode 100644 index 983681ede..000000000 --- a/internal/events/git/branch_updated.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2022 Harness Inc. All rights reserved. -// Use of this source code is governed by the Polyform Free Trial License -// that can be found in the LICENSE.md file for this repository. - -package events - -import ( - "context" - - "github.com/harness/gitness/events" - - "github.com/rs/zerolog/log" -) - -const BranchUpdatedEvent events.EventType = "branchupdated" - -type BranchUpdatedPayload struct { - RepoID int64 `json:"repo_id"` - PrincipalID int64 `json:"principal_id"` - Ref string `json:"ref"` - OldSHA string `json:"old_sha"` - NewSHA string `json:"new_sha"` - // Forced bool `json:"forced"` TODO: data not available yet. -} - -func (r *Reporter) BranchUpdated(ctx context.Context, payload *BranchUpdatedPayload) { - eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchUpdatedEvent, payload) - if err != nil { - log.Ctx(ctx).Err(err).Msgf("failed to send branch updated event") - return - } - - log.Ctx(ctx).Debug().Msgf("reported branch updated event with id '%s'", eventID) -} - -func (r *Reader) RegisterBranchUpdated(fn func(context.Context, *events.Event[*BranchUpdatedPayload]) error) error { - return events.ReaderRegisterEvent(r.innerReader, BranchUpdatedEvent, fn) -} diff --git a/internal/events/git/tag.go b/internal/events/git/tag.go new file mode 100644 index 000000000..89452eb18 --- /dev/null +++ b/internal/events/git/tag.go @@ -0,0 +1,84 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package events + +import ( + "context" + + "github.com/harness/gitness/events" + + "github.com/rs/zerolog/log" +) + +const TagCreatedEvent events.EventType = "tagcreated" + +type TagCreatedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` +} + +func (r *Reporter) TagCreated(ctx context.Context, payload *TagCreatedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagCreatedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send tag created event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported tag created event with id '%s'", eventID) +} + +func (r *Reader) RegisterTagCreated(fn func(context.Context, *events.Event[*TagCreatedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, TagCreatedEvent, fn) +} + +const TagUpdatedEvent events.EventType = "tagupdated" + +type TagUpdatedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + OldSHA string `json:"old_sha"` + NewSHA string `json:"new_sha"` + Forced bool `json:"forced"` +} + +func (r *Reporter) TagUpdated(ctx context.Context, payload *TagUpdatedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagUpdatedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send tag updated event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported tag updated event with id '%s'", eventID) +} + +func (r *Reader) RegisterTagUpdated(fn func(context.Context, *events.Event[*TagUpdatedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, TagUpdatedEvent, fn) +} + +const TagDeletedEvent events.EventType = "tagdeleted" + +type TagDeletedPayload struct { + RepoID int64 `json:"repo_id"` + PrincipalID int64 `json:"principal_id"` + Ref string `json:"ref"` + SHA string `json:"sha"` +} + +func (r *Reporter) TagDeleted(ctx context.Context, payload *TagDeletedPayload) { + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagDeletedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send tag deleted event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported tag deleted event with id '%s'", eventID) +} + +func (r *Reader) RegisterTagDeleted(fn func(context.Context, *events.Event[*TagDeletedPayload]) error) error { + return events.ReaderRegisterEvent(r.innerReader, TagDeletedEvent, fn) +} diff --git a/internal/webhook/branch.go b/internal/webhook/branch.go index be5b9fb81..a5eb93555 100644 --- a/internal/webhook/branch.go +++ b/internal/webhook/branch.go @@ -17,7 +17,7 @@ import ( ) // BranchBody describes the body of Branch related webhook triggers. -// NOTE: Use a single payload format to make it easier for consumers! +// NOTE: Use a single payload format (and keep it similar to TagBody) to make it easier for consumers! // TODO: move in separate package for small import? type BranchBody struct { Trigger enum.WebhookTrigger `json:"trigger"` diff --git a/internal/webhook/server.go b/internal/webhook/server.go index 7b88fa870..cc0d37ef6 100644 --- a/internal/webhook/server.go +++ b/internal/webhook/server.go @@ -75,6 +75,10 @@ func NewServer(ctx context.Context, config Config, _ = r.RegisterBranchUpdated(server.handleEventBranchUpdated) _ = r.RegisterBranchDeleted(server.handleEventBranchDeleted) + _ = r.RegisterTagCreated(server.handleEventTagCreated) + _ = r.RegisterTagUpdated(server.handleEventTagUpdated) + _ = r.RegisterTagDeleted(server.handleEventTagDeleted) + return nil }) if err != nil { diff --git a/internal/webhook/tag.go b/internal/webhook/tag.go new file mode 100644 index 000000000..18d596f43 --- /dev/null +++ b/internal/webhook/tag.go @@ -0,0 +1,82 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package webhook + +import ( + "context" + + "github.com/harness/gitness/events" + gitevents "github.com/harness/gitness/internal/events/git" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// TagBody describes the body of Tag related webhook triggers. +// NOTE: Use a single payload format (and keep it similar to BranchBody) to make it easier for consumers! +// TODO: move in separate package for small import? +type TagBody struct { + Trigger enum.WebhookTrigger `json:"trigger"` + Repo RepositoryInfo `json:"repo"` + Principal PrincipalInfo `json:"principal"` + Ref string `json:"ref"` + Before string `json:"before"` + After string `json:"after"` + Forced bool `json:"forced"` // tags can only be force-updated, include to be explicit. +} + +// handleEventTagCreated handles tag created events +// and triggers tag created webhooks for the source repo. +func (s *Server) handleEventTagCreated(ctx context.Context, + event *events.Event[*gitevents.TagCreatedPayload]) error { + return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagCreated, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) (any, error) { + return &TagBody{ + Trigger: enum.WebhookTriggerTagCreated, + Repo: repositoryInfoFrom(*repo, s.urlProvider), + Principal: principalInfoFrom(*principal), + Ref: event.Payload.Ref, + Before: types.NilSHA, + After: event.Payload.SHA, + }, nil + }) +} + +// handleEventTagUpdated handles tag updated events +// and triggers tag updated webhooks for the source repo. +func (s *Server) handleEventTagUpdated(ctx context.Context, + event *events.Event[*gitevents.TagUpdatedPayload]) error { + return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagUpdated, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) (any, error) { + return &TagBody{ + Trigger: enum.WebhookTriggerTagUpdated, + Repo: repositoryInfoFrom(*repo, s.urlProvider), + Principal: principalInfoFrom(*principal), + Ref: event.Payload.Ref, + Before: event.Payload.OldSHA, + After: event.Payload.NewSHA, + Forced: event.Payload.Forced, + }, nil + }) +} + +// handleEventTagDeleted handles tag deleted events +// and triggers tag deleted webhooks for the source repo. +func (s *Server) handleEventTagDeleted(ctx context.Context, + event *events.Event[*gitevents.TagDeletedPayload]) error { + return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagDeleted, + event.ID, event.Payload.RepoID, event.Payload.PrincipalID, + func(repo *types.Repository, principal *types.Principal) (any, error) { + return &TagBody{ + Trigger: enum.WebhookTriggerTagDeleted, + Repo: repositoryInfoFrom(*repo, s.urlProvider), + Principal: principalInfoFrom(*principal), + Ref: event.Payload.Ref, + Before: event.Payload.SHA, + After: types.NilSHA, + }, nil + }) +} diff --git a/types/enum/webhook.go b/types/enum/webhook.go index 6744e72df..f1a61d2ea 100644 --- a/types/enum/webhook.go +++ b/types/enum/webhook.go @@ -45,7 +45,6 @@ func GetAllWebhookExecutionResults() []WebhookExecutionResult { } // WebhookTrigger defines the different types of webhook triggers available. -// NOTE: For now we keep a small list - will be extended later on once we decided on a final set of triggers. type WebhookTrigger string const ( @@ -55,6 +54,13 @@ const ( WebhookTriggerBranchUpdated WebhookTrigger = "branch_updated" // WebhookTriggerBranchDeleted gets triggered when a branch gets deleted. WebhookTriggerBranchDeleted WebhookTrigger = "branch_deleted" + + // WebhookTriggerTagCreated gets triggered when a tag gets created. + WebhookTriggerTagCreated WebhookTrigger = "tag_created" + // WebhookTriggerTagUpdated gets triggered when a tag gets updated. + WebhookTriggerTagUpdated WebhookTrigger = "tag_updated" + // WebhookTriggerTagDeleted gets triggered when a tag gets deleted. + WebhookTriggerTagDeleted WebhookTrigger = "tag_deleted" ) func GetAllWebhookTriggers() []WebhookTrigger { @@ -62,6 +68,9 @@ func GetAllWebhookTriggers() []WebhookTrigger { WebhookTriggerBranchCreated, WebhookTriggerBranchUpdated, WebhookTriggerBranchDeleted, + WebhookTriggerTagCreated, + WebhookTriggerTagUpdated, + WebhookTriggerTagDeleted, } } diff --git a/web/src/services/code/index.tsx b/web/src/services/code/index.tsx index 6b8b6dee3..5c7dc61c8 100644 --- a/web/src/services/code/index.tsx +++ b/web/src/services/code/index.tsx @@ -208,7 +208,13 @@ export interface OpenapiWebhookExecutionType { export type OpenapiWebhookParent = 'repo' | 'space' -export type OpenapiWebhookTrigger = 'branch_created' | 'branch_updated' | 'branch_deleted' +export type OpenapiWebhookTrigger = + | 'branch_created' + | 'branch_updated' + | 'branch_deleted' + | 'tag_created' + | 'tag_updated' + | 'tag_deleted' export interface OpenapiWebhookType { created?: number diff --git a/web/src/services/code/swagger.yaml b/web/src/services/code/swagger.yaml index d54dbfb87..c3360f303 100644 --- a/web/src/services/code/swagger.yaml +++ b/web/src/services/code/swagger.yaml @@ -3406,6 +3406,9 @@ components: - branch_created - branch_updated - branch_deleted + - tag_created + - tag_updated + - tag_deleted type: string OpenapiWebhookType: properties: