Add alternate_object_dirs support to githook handlers (#1141)

pull/3490/head
Johannes Batzill 2024-03-26 23:52:49 +00:00 committed by Harness
parent 96f35b6e01
commit e96ab4159b
23 changed files with 179 additions and 83 deletions

View File

@ -26,40 +26,15 @@ import (
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ServerHookOutput represents the output of server hook api calls.
// TODO: support non-error messages (once we need it).
type ServerHookOutput struct {
// Error contains the user facing error (like "branch is protected", ...).
Error *string `json:"error,omitempty"`
}
// ReferenceUpdate represents an update of a git reference.
type ReferenceUpdate struct {
// Ref is the full name of the reference that got updated.
Ref string `json:"ref"`
// Old is the old commmit hash (before the update).
Old string `json:"old"`
// New is the new commit hash (after the update).
New string `json:"new"`
}
// BaseInput contains the base input for any githook api call.
type BaseInput struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
}
type Controller struct {
authorizer authz.Authorizer
principalStore store.PrincipalStore
repoStore store.RepoStore
gitReporter *eventsgit.Reporter
git git.Interface
pullreqStore store.PullReqStore
urlProvider url.Provider
protectionManager *protection.Manager
@ -74,7 +49,6 @@ func NewController(
principalStore store.PrincipalStore,
repoStore store.RepoStore,
gitReporter *eventsgit.Reporter,
git git.Interface,
pullreqStore store.PullReqStore,
urlProvider url.Provider,
protectionManager *protection.Manager,
@ -89,7 +63,6 @@ func NewController(
principalStore: principalStore,
repoStore: repoStore,
gitReporter: gitReporter,
git: git,
pullreqStore: pullreqStore,
urlProvider: urlProvider,
protectionManager: protectionManager,

View File

@ -25,6 +25,7 @@ import (
type PreReceiveExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookPreReceiveInput,
@ -35,6 +36,7 @@ type PreReceiveExtender interface {
type UpdateExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookUpdateInput,
@ -45,6 +47,7 @@ type UpdateExtender interface {
type PostReceiveExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookPostReceiveInput,
@ -61,6 +64,7 @@ func NewPreReceiveExtender() PreReceiveExtender {
func (NoOpPreReceiveExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookPreReceiveInput,
@ -78,6 +82,7 @@ func NewUpdateExtender() UpdateExtender {
func (NoOpUpdateExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookUpdateInput,
@ -95,6 +100,7 @@ func NewPostReceiveExtender() PostReceiveExtender {
func (NoOpPostReceiveExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.Repository,
types.GithookPostReceiveInput,

View File

@ -0,0 +1,30 @@
// 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 githook
import (
"context"
"github.com/harness/gitness/git"
)
// RestrictedGIT is a git client that is restricted to a subset of operations of git.Interface
// which can be executed on quarantine data that is part of git-hooks (e.g. pre-receive, update, ..)
// and don't alter the repo (so only read operations).
// NOTE: While it doesn't apply to all git-hooks (e.g. post-receive), we still use the interface across the board
// to "soft enforce" no write operations being executed as part of githooks.
type RestrictedGIT interface {
IsAncestor(ctx context.Context, params git.IsAncestorParams) (git.IsAncestorOutput, error)
}

View File

@ -43,6 +43,7 @@ const (
// PostReceive executes the post-receive hook for a git repository.
func (c *Controller) PostReceive(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookPostReceiveInput,
) (hook.Output, error) {
@ -52,7 +53,7 @@ func (c *Controller) PostReceive(
}
// report ref events (best effort)
c.reportReferenceEvents(ctx, repo, in.PrincipalID, in.PostReceiveInput)
c.reportReferenceEvents(ctx, rgit, repo, in.PrincipalID, in.PostReceiveInput)
// create output object and have following messages fill its messages
out := hook.Output{}
@ -60,7 +61,7 @@ func (c *Controller) PostReceive(
// handle branch updates related to PRs - best effort
c.handlePRMessaging(ctx, repo, in.PostReceiveInput, &out)
err = c.postReceiveExtender.Extend(ctx, session, repo, in, &out)
err = c.postReceiveExtender.Extend(ctx, rgit, session, repo, in, &out)
if out.Error != nil {
return out, nil
}
@ -76,6 +77,7 @@ func (c *Controller) PostReceive(
// TODO: in the future we might want to think about propagating errors so user is aware of events not being triggered.
func (c *Controller) reportReferenceEvents(
ctx context.Context,
rgit RestrictedGIT,
repo *types.Repository,
principalID int64,
in hook.PostReceiveInput,
@ -83,7 +85,7 @@ func (c *Controller) reportReferenceEvents(
for _, refUpdate := range in.RefUpdates {
switch {
case strings.HasPrefix(refUpdate.Ref, gitReferenceNamePrefixBranch):
c.reportBranchEvent(ctx, repo, principalID, refUpdate)
c.reportBranchEvent(ctx, rgit, repo, principalID, in.Environment, refUpdate)
case strings.HasPrefix(refUpdate.Ref, gitReferenceNamePrefixTag):
c.reportTagEvent(ctx, repo, principalID, refUpdate)
default:
@ -94,8 +96,10 @@ func (c *Controller) reportReferenceEvents(
func (c *Controller) reportBranchEvent(
ctx context.Context,
rgit RestrictedGIT,
repo *types.Repository,
principalID int64,
env hook.Environment,
branchUpdate hook.ReferenceUpdate,
) {
switch {
@ -114,8 +118,11 @@ func (c *Controller) reportBranchEvent(
SHA: branchUpdate.Old.String(),
})
default:
result, err := c.git.IsAncestor(ctx, git.IsAncestorParams{
ReadParams: git.ReadParams{RepoUID: repo.GitUID},
result, err := rgit.IsAncestor(ctx, git.IsAncestorParams{
ReadParams: git.ReadParams{
RepoUID: repo.GitUID,
AlternateObjectDirs: env.AlternateObjectDirs,
},
AncestorCommitSHA: branchUpdate.Old,
DescendantCommitSHA: branchUpdate.New,
})

View File

@ -33,10 +33,9 @@ import (
)
// PreReceive executes the pre-receive hook for a git repository.
//
//nolint:revive // not yet fully implemented
func (c *Controller) PreReceive(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookPreReceiveInput,
) (hook.Output, error) {
@ -87,7 +86,7 @@ func (c *Controller) PreReceive(
return hook.Output{}, fmt.Errorf("failed to check protection rules: %w", err)
}
err = c.preReceiveExtender.Extend(ctx, session, repo, in, &output)
err = c.preReceiveExtender.Extend(ctx, rgit, session, repo, in, &output)
if output.Error != nil {
return output, nil
}

View File

@ -25,10 +25,9 @@ import (
)
// Update executes the update hook for a git repository.
//
//nolint:revive // not yet implemented
func (c *Controller) Update(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookUpdateInput,
) (hook.Output, error) {
@ -39,7 +38,7 @@ func (c *Controller) Update(
output := hook.Output{}
err = c.updateExtender.Extend(ctx, session, repo, in, &output)
err = c.updateExtender.Extend(ctx, rgit, session, repo, in, &output)
if output.Error != nil {
return output, nil
}

View File

@ -21,11 +21,15 @@ import (
controllergithook "github.com/harness/gitness/app/api/controller/githook"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
)
// HandlePostReceive returns a handler function that handles post-receive git hooks.
func HandlePostReceive(githookCtrl *controllergithook.Controller) http.HandlerFunc {
func HandlePostReceive(
githookCtrl *controllergithook.Controller,
git git.Interface,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
@ -37,7 +41,8 @@ func HandlePostReceive(githookCtrl *controllergithook.Controller) http.HandlerFu
return
}
out, err := githookCtrl.PostReceive(ctx, session, in)
// gitness doesn't require any custom git connector.
out, err := githookCtrl.PostReceive(ctx, git, session, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return

View File

@ -21,11 +21,15 @@ import (
controllergithook "github.com/harness/gitness/app/api/controller/githook"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
)
// HandlePreReceive returns a handler function that handles pre-receive git hooks.
func HandlePreReceive(githookCtrl *controllergithook.Controller) http.HandlerFunc {
func HandlePreReceive(
githookCtrl *controllergithook.Controller,
git git.Interface,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
@ -37,7 +41,8 @@ func HandlePreReceive(githookCtrl *controllergithook.Controller) http.HandlerFun
return
}
out, err := githookCtrl.PreReceive(ctx, session, in)
// gitness doesn't require any custom git connector.
out, err := githookCtrl.PreReceive(ctx, git, session, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return

View File

@ -18,14 +18,18 @@ import (
"encoding/json"
"net/http"
githookcontroller "github.com/harness/gitness/app/api/controller/githook"
controllergithook "github.com/harness/gitness/app/api/controller/githook"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
)
// HandleUpdate returns a handler function that handles update git hooks.
func HandleUpdate(githookCtrl *githookcontroller.Controller) http.HandlerFunc {
func HandleUpdate(
githookCtrl *controllergithook.Controller,
git git.Interface,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
@ -37,7 +41,8 @@ func HandleUpdate(githookCtrl *githookcontroller.Controller) http.HandlerFunc {
return
}
out, err := githookCtrl.Update(ctx, session, in)
// gitness doesn't require any custom git connector.
out, err := githookCtrl.Update(ctx, git, session, in)
if err != nil {
render.TranslatedUserError(ctx, w, err)
return

View File

@ -20,6 +20,7 @@ import (
"fmt"
"github.com/harness/gitness/app/api/controller/githook"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
@ -27,9 +28,13 @@ import (
"github.com/rs/zerolog/log"
)
var _ hook.ClientFactory = (*ControllerClientFactory)(nil)
var _ hook.Client = (*ControllerClient)(nil)
// ControllerClientFactory creates clients that directly call the controller to execute githooks.
type ControllerClientFactory struct {
githookCtrl *githook.Controller
git git.Interface
}
func (f *ControllerClientFactory) NewClient(_ context.Context, envVars map[string]string) (hook.Client, error) {
@ -48,8 +53,9 @@ func (f *ControllerClientFactory) NewClient(_ context.Context, envVars map[strin
}
return &ControllerClient{
baseInput: getInputBaseFromPayload(payload),
baseInput: GetInputBaseFromPayload(payload),
githookCtrl: f.githookCtrl,
git: f.git,
}, nil
}
@ -57,6 +63,7 @@ func (f *ControllerClientFactory) NewClient(_ context.Context, envVars map[strin
type ControllerClient struct {
baseInput types.GithookInputBase
githookCtrl *githook.Controller
git githook.RestrictedGIT
}
func (c *ControllerClient) PreReceive(
@ -67,7 +74,8 @@ func (c *ControllerClient) PreReceive(
out, err := c.githookCtrl.PreReceive(
ctx,
nil, // TODO: update once githooks are auth protected
c.git, // gitness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookPreReceiveInput{
GithookInputBase: c.baseInput,
PreReceiveInput: in,
@ -88,7 +96,8 @@ func (c *ControllerClient) Update(
out, err := c.githookCtrl.Update(
ctx,
nil, // TODO: update once githooks are auth protected
c.git, // gitness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookUpdateInput{
GithookInputBase: c.baseInput,
UpdateInput: in,
@ -109,7 +118,8 @@ func (c *ControllerClient) PostReceive(
out, err := c.githookCtrl.PostReceive(
ctx,
nil, // TODO: update once githooks are auth protected
c.git, // gitness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookPostReceiveInput{
GithookInputBase: c.baseInput,
PostReceiveInput: in,

View File

@ -77,7 +77,7 @@ func NewRestClient(
httpClient: http.DefaultClient,
baseURL: strings.TrimRight(payload.BaseURL, "/"),
requestID: payload.RequestID,
baseInput: getInputBaseFromPayload(payload),
baseInput: GetInputBaseFromPayload(payload),
}
}

View File

@ -49,7 +49,7 @@ func (p Payload) Validate() error {
return nil
}
func getInputBaseFromPayload(p Payload) types.GithookInputBase {
func GetInputBaseFromPayload(p Payload) types.GithookInputBase {
return types.GithookInputBase{
RepoID: p.RepoID,
PrincipalID: p.PrincipalID,

View File

@ -61,7 +61,6 @@ func ProvideController(
principalStore,
repoStore,
gitReporter,
git,
pullreqStore,
urlProvider,
protectionManager,
@ -74,6 +73,7 @@ func ProvideController(
// TODO: improve wiring if possible
if fct, ok := githookFactory.(*ControllerClientFactory); ok {
fct.githookCtrl = ctrl
fct.git = git
}
return ctrl

View File

@ -70,6 +70,7 @@ import (
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/app/auth/authn"
"github.com/harness/gitness/app/githook"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
@ -108,6 +109,7 @@ func NewAPIHandler(
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
githookCtrl *controllergithook.Controller,
git git.Interface,
saCtrl *serviceaccount.Controller,
userCtrl *user.Controller,
principalCtrl principal.Controller,
@ -139,7 +141,7 @@ func NewAPIHandler(
r.Route("/v1", func(r chi.Router) {
setupRoutesV1(r, appCtx, config, repoCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,
connectorCtrl, templateCtrl, pluginCtrl, secretCtrl, spaceCtrl, pullreqCtrl,
webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, uploadCtrl,
webhookCtrl, githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, uploadCtrl,
searchCtrl)
})
@ -177,6 +179,7 @@ func setupRoutesV1(r chi.Router,
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
githookCtrl *controllergithook.Controller,
git git.Interface,
saCtrl *serviceaccount.Controller,
userCtrl *user.Controller,
principalCtrl principal.Controller,
@ -194,7 +197,7 @@ func setupRoutesV1(r chi.Router,
setupUser(r, userCtrl)
setupServiceAccounts(r, saCtrl)
setupPrincipals(r, principalCtrl)
setupInternal(r, githookCtrl)
setupInternal(r, githookCtrl, git)
setupAdmin(r, userCtrl)
setupAccount(r, userCtrl, sysCtrl, config)
setupSystem(r, config, sysCtrl)
@ -470,17 +473,17 @@ func setupTriggers(
})
}
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller) {
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller, git git.Interface) {
r.Route("/internal", func(r chi.Router) {
SetupGitHooks(r, githookCtrl)
SetupGitHooks(r, githookCtrl, git)
})
}
func SetupGitHooks(r chi.Router, githookCtrl *controllergithook.Controller) {
func SetupGitHooks(r chi.Router, githookCtrl *controllergithook.Controller, git git.Interface) {
r.Route("/git-hooks", func(r chi.Router) {
r.Post("/"+githook.HTTPRequestPathPreReceive, handlergithook.HandlePreReceive(githookCtrl))
r.Post("/"+githook.HTTPRequestPathUpdate, handlergithook.HandleUpdate(githookCtrl))
r.Post("/"+githook.HTTPRequestPathPostReceive, handlergithook.HandlePostReceive(githookCtrl))
r.Post("/"+githook.HTTPRequestPathPreReceive, handlergithook.HandlePreReceive(githookCtrl, git))
r.Post("/"+githook.HTTPRequestPathUpdate, handlergithook.HandleUpdate(githookCtrl, git))
r.Post("/"+githook.HTTPRequestPathPostReceive, handlergithook.HandlePostReceive(githookCtrl, git))
})
}

View File

@ -41,6 +41,7 @@ import (
"github.com/harness/gitness/app/api/openapi"
"github.com/harness/gitness/app/auth/authn"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/git"
"github.com/harness/gitness/types"
"github.com/google/wire"
@ -103,6 +104,7 @@ func ProvideAPIHandler(
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
githookCtrl *githook.Controller,
git git.Interface,
saCtrl *serviceaccount.Controller,
userCtrl *user.Controller,
principalCtrl principal.Controller,
@ -114,7 +116,7 @@ func ProvideAPIHandler(
return NewAPIHandler(appCtx, config,
authenticator, repoCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
secretCtrl, triggerCtrl, connectorCtrl, templateCtrl, pluginCtrl, pullreqCtrl, webhookCtrl,
githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl)
githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl)
}
func ProvideWebHandler(config *types.Config, openapi openapi.Service) WebHandler {

View File

@ -291,7 +291,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
uploadController := upload.ProvideController(authorizer, repoStore, blobStore)
searcher := keywordsearch.ProvideSearcher(localIndexSearcher)
keywordsearchController := keywordsearch2.ProvideController(authorizer, searcher, repoController, spaceController)
apiHandler := router.ProvideAPIHandler(ctx, config, authenticator, repoController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController)
apiHandler := router.ProvideAPIHandler(ctx, config, authenticator, repoController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController)
gitHandler := router.ProvideGitHandler(provider, authenticator, repoController)
openapiService := openapi.ProvideOpenAPIService()
webHandler := router.ProvideWebHandler(config, openapiService)

View File

@ -183,15 +183,12 @@ func (g *Git) RawDiff(
cmd := command.New("diff",
command.WithFlag("-M"),
command.WithFlag("--full-index"),
command.WithAlternateObjectDirs(alternates...),
)
if mergeBase {
cmd.Add(command.WithFlag("--merge-base"))
}
if len(alternates) > 0 {
cmd.Add(command.WithAlternateObjectDirs(alternates...))
}
perFileDiffRequired := false
paths := make([]string, 0, len(files))
if len(files) > 0 {

View File

@ -79,7 +79,9 @@ func (g *Git) GetMergeBase(
func (g *Git) IsAncestor(
ctx context.Context,
repoPath string,
ancestorCommitSHA, descendantCommitSHA sha.SHA,
alternates []string,
ancestorCommitSHA,
descendantCommitSHA sha.SHA,
) (bool, error) {
if repoPath == "" {
return false, ErrRepositoryPathEmpty
@ -88,6 +90,7 @@ func (g *Git) IsAncestor(
cmd := command.New("merge-base",
command.WithFlag("--is-ancestor"),
command.WithArg(ancestorCommitSHA.String(), descendantCommitSHA.String()),
command.WithAlternateObjectDirs(alternates...),
)
err := cmd.Run(ctx, command.WithDir(repoPath))

View File

@ -342,6 +342,9 @@ func (g *Git) updateRefWithHooks(
New: newValue,
},
},
Environment: hook.Environment{
// TODO: Update once we properly copy quarantine objects only after pre-receive.
},
})
if err != nil {
return fmt.Errorf("pre-receive call failed with: %w", err)
@ -378,6 +381,7 @@ func (g *Git) updateRefWithHooks(
New: newValue,
},
},
Environment: hook.Environment{},
})
if err != nil {
return fmt.Errorf("post-receive call failed with: %w", err)

View File

@ -24,6 +24,7 @@ import (
"strings"
"time"
"github.com/harness/gitness/git/command"
"github.com/harness/gitness/git/sha"
)
@ -49,8 +50,16 @@ func (c *CLICore) PreReceive(ctx context.Context) error {
return fmt.Errorf("failed to read updated references from std in: %w", err)
}
alternateObjDirs, err := getAlternateObjectDirsFromEnv()
if err != nil {
return fmt.Errorf("failed to read alternate object dirs from env: %w", err)
}
in := PreReceiveInput{
RefUpdates: refUpdates,
Environment: Environment{
AlternateObjectDirs: alternateObjDirs,
},
}
out, err := c.client.PreReceive(ctx, in)
@ -60,12 +69,20 @@ func (c *CLICore) PreReceive(ctx context.Context) error {
// Update executes the update git hook.
func (c *CLICore) Update(ctx context.Context, ref string, oldSHA string, newSHA string) error {
alternateObjDirs, err := getAlternateObjectDirsFromEnv()
if err != nil {
return fmt.Errorf("failed to read alternate object dirs from env: %w", err)
}
in := UpdateInput{
RefUpdate: ReferenceUpdate{
Ref: ref,
Old: sha.Must(oldSHA),
New: sha.Must(newSHA),
},
Environment: Environment{
AlternateObjectDirs: alternateObjDirs,
},
}
out, err := c.client.Update(ctx, in)
@ -82,6 +99,9 @@ func (c *CLICore) PostReceive(ctx context.Context) error {
in := PostReceiveInput{
RefUpdates: refUpdates,
Environment: Environment{
AlternateObjectDirs: nil, // all objects are in main objects folder at this point
},
}
out, err := c.client.PostReceive(ctx, in)
@ -149,3 +169,16 @@ func getUpdatedReferencesFromStdIn() ([]ReferenceUpdate, error) {
return updatedRefs, nil
}
// getAlternateObjectDirsFromEnv returns the alternate object directories that have to be used
// to be able to preemptively access the quarantined objects created by a write operation.
// NOTE: The temp dir of a write operation is it's main object dir,
// which is the one that read operations have to use as alternate object dir.
func getAlternateObjectDirsFromEnv() ([]string, error) {
tmpDir, err := getRequiredEnvironmentVariable(command.GitObjectDir)
if err != nil {
return nil, err
}
return []string{tmpDir}, nil
}

View File

@ -18,7 +18,6 @@ import (
"bytes"
"encoding/base64"
"encoding/gob"
"errors"
"fmt"
"os"
)
@ -28,11 +27,6 @@ const (
envNamePayload = "GIT_HOOK_PAYLOAD"
)
var (
// ErrEnvVarNotFound is an error that is returned in case the environment variable isn't found.
ErrEnvVarNotFound = errors.New("environment variable not found")
)
// GenerateEnvironmentVariables generates the environment variables that should be used when calling git
// to ensure the payload will be available to the githook cli.
func GenerateEnvironmentVariables(payload any) (map[string]string, error) {
@ -60,7 +54,7 @@ func LoadPayloadFromMap[T any](envVars map[string]string) (T, error) {
// retrieve payload from environment variables
payloadBase64, ok := envVars[envNamePayload]
if !ok {
return payload, ErrEnvVarNotFound
return payload, fmt.Errorf("environment variable %q not found", envNamePayload)
}
return decodePayload[T](payloadBase64)
@ -71,7 +65,7 @@ func LoadPayloadFromEnvironment[T any]() (T, error) {
var payload T
// retrieve payload from environment variables
payloadBase64, err := getEnvironmentVariable(envNamePayload)
payloadBase64, err := getRequiredEnvironmentVariable(envNamePayload)
if err != nil {
return payload, fmt.Errorf("failed to load payload from environment variables: %w", err)
}
@ -97,14 +91,14 @@ func decodePayload[T any](encodedPayload string) (T, error) {
return payload, nil
}
func getEnvironmentVariable(name string) (string, error) {
func getRequiredEnvironmentVariable(name string) (string, error) {
val, ok := os.LookupEnv(name)
if !ok {
return "", ErrEnvVarNotFound
return "", fmt.Errorf("environment variable %q not found", name)
}
if val == "" {
return "", fmt.Errorf("'%s' found in env but it's empty", name)
return "", fmt.Errorf("environment variable %q found but it's empty", name)
}
return val, nil

View File

@ -35,20 +35,35 @@ type ReferenceUpdate struct {
New sha.SHA `json:"new"`
}
// PostReceiveInput represents the input of the post-receive git hook.
type PostReceiveInput struct {
// RefUpdates contains all references that got updated as part of the git operation.
RefUpdates []ReferenceUpdate `json:"ref_updates"`
// Environment contains the information required to access a specific git environment.
type Environment struct {
// AlternateObjectDirs contains any alternate object dirs required to access all objects of an operation.
AlternateObjectDirs []string `json:"alternate_object_dirs,omitempty"`
}
// PreReceiveInput represents the input of the pre-receive git hook.
type PreReceiveInput struct {
// Environment contains the information required to access the git environment.
Environment Environment `json:"environment"`
// RefUpdates contains all references that are being updated as part of the git operation.
RefUpdates []ReferenceUpdate `json:"ref_updates"`
}
// UpdateInput represents the input of the update git hook.
type UpdateInput struct {
// Environment contains the information required to access the git environment.
Environment Environment `json:"environment"`
// RefUpdate contains information about the reference that is being updated.
RefUpdate ReferenceUpdate `json:"ref_update"`
}
// PostReceiveInput represents the input of the post-receive git hook.
type PostReceiveInput struct {
// Environment contains the information required to access the git environment.
Environment Environment `json:"environment"`
// RefUpdates contains all references that got updated as part of the git operation.
RefUpdates []ReferenceUpdate `json:"ref_updates"`
}

View File

@ -391,7 +391,13 @@ func (s *Service) IsAncestor(
) (IsAncestorOutput, error) {
repoPath := getFullPathForRepo(s.reposRoot, params.RepoUID)
result, err := s.git.IsAncestor(ctx, repoPath, params.AncestorCommitSHA, params.DescendantCommitSHA)
result, err := s.git.IsAncestor(
ctx,
repoPath,
params.AlternateObjectDirs,
params.AncestorCommitSHA,
params.DescendantCommitSHA,
)
if err != nil {
return IsAncestorOutput{}, err
}