feat: [CODE-3500] add Git LFS support to the repository setting (#3672)

This commit is contained in:
Atefeh Mohseni Ejiyeh 2025-04-16 15:59:23 +00:00 committed by Harness
parent b8603e7f07
commit df94dca3a7
20 changed files with 217 additions and 26 deletions

View File

@ -62,7 +62,18 @@ func (c *Controller) processObjects(
return fmt.Errorf("failed to check settings for principal committer match: %w", err) return fmt.Errorf("failed to check settings for principal committer match: %w", err)
} }
if sizeLimit == 0 && !principalCommitterMatch { gitLFSEnabled, err := settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyGitLFSEnabled,
settings.DefaultGitLFSEnabled,
)
if err != nil {
return fmt.Errorf("failed to check settings for Git LFS enabled: %w", err)
}
if sizeLimit == 0 && !principalCommitterMatch && !gitLFSEnabled {
return nil return nil
} }
@ -85,7 +96,9 @@ func (c *Controller) processObjects(
} }
} }
if gitLFSEnabled {
preReceiveObjsIn.FindLFSPointersParams = &git.FindLFSPointersParams{} preReceiveObjsIn.FindLFSPointersParams = &git.FindLFSPointersParams{}
}
preReceiveObjsOut, err := c.git.ProcessPreReceiveObjects( preReceiveObjsOut, err := c.git.ProcessPreReceiveObjects(
ctx, ctx,

View File

@ -18,8 +18,10 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authn" "github.com/harness/gitness/app/auth/authn"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/token" "github.com/harness/gitness/app/token"
) )
@ -28,6 +30,26 @@ func (c *Controller) Authenticate(
session *auth.Session, session *auth.Session,
repoRef string, repoRef string,
) (*AuthenticateResponse, error) { ) (*AuthenticateResponse, error) {
repo, err := c.repoFinder.FindByRef(ctx, repoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repository: %w", err)
}
gitLFSEnabled, err := settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyGitLFSEnabled,
settings.DefaultGitLFSEnabled,
)
if err != nil {
return nil, fmt.Errorf("failed to check settings for Git LFS enabled: %w", err)
}
if !gitLFSEnabled {
return nil, usererror.ErrGitLFSDisabled
}
jwt, err := c.remoteAuth.GenerateToken(ctx, session.Principal.ID, session.Principal.Type, repoRef) jwt, err := c.remoteAuth.GenerateToken(ctx, session.Principal.ID, session.Principal.Type, repoRef)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate auth token: %w", err) return nil, fmt.Errorf("failed to generate auth token: %w", err)

View File

@ -24,6 +24,7 @@ import (
"github.com/harness/gitness/app/auth/authz" "github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/remoteauth" "github.com/harness/gitness/app/services/remoteauth"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url" "github.com/harness/gitness/app/url"
"github.com/harness/gitness/blob" "github.com/harness/gitness/blob"
@ -43,6 +44,7 @@ type Controller struct {
blobStore blob.Store blobStore blob.Store
remoteAuth remoteauth.Service remoteAuth remoteauth.Service
urlProvider url.Provider urlProvider url.Provider
settings *settings.Service
} }
func NewController( func NewController(
@ -53,6 +55,7 @@ func NewController(
blobStore blob.Store, blobStore blob.Store,
remoteAuth remoteauth.Service, remoteAuth remoteauth.Service,
urlProvider url.Provider, urlProvider url.Provider,
settings *settings.Service,
) *Controller { ) *Controller {
return &Controller{ return &Controller{
authorizer: authorizer, authorizer: authorizer,
@ -62,10 +65,11 @@ func NewController(
blobStore: blobStore, blobStore: blobStore,
remoteAuth: remoteAuth, remoteAuth: remoteAuth,
urlProvider: urlProvider, urlProvider: urlProvider,
settings: settings,
} }
} }
func (c *Controller) getRepoCheckAccess( func (c *Controller) getRepoCheckAccessAndSetting(
ctx context.Context, ctx context.Context,
session *auth.Session, session *auth.Session,
repoRef string, repoRef string,
@ -89,6 +93,21 @@ func (c *Controller) getRepoCheckAccess(
return nil, fmt.Errorf("access check failed: %w", err) return nil, fmt.Errorf("access check failed: %w", err)
} }
gitLFSEnabled, err := settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyGitLFSEnabled,
settings.DefaultGitLFSEnabled,
)
if err != nil {
return nil, fmt.Errorf("failed to check settings for Git LFS enabled: %w", err)
}
if !gitLFSEnabled {
return nil, usererror.ErrGitLFSDisabled
}
return repo, nil return repo, nil
} }

View File

@ -41,7 +41,7 @@ func (c *Controller) Download(ctx context.Context,
repoRef string, repoRef string,
oid string, oid string,
) (*Content, error) { ) (*Content, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) repo, err := c.getRepoCheckAccessAndSetting(ctx, session, repoRef, enum.PermissionRepoView)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err) return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
} }

View File

@ -37,7 +37,7 @@ func (c *Controller) LFSTransfer(ctx context.Context,
reqPermission = enum.PermissionRepoPush reqPermission = enum.PermissionRepoPush
} }
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, reqPermission) repo, err := c.getRepoCheckAccessAndSetting(ctx, session, repoRef, reqPermission)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -39,7 +39,7 @@ func (c *Controller) Upload(ctx context.Context,
pointer Pointer, pointer Pointer,
file io.Reader, file io.Reader,
) (*UploadOut, error) { ) (*UploadOut, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush) repo, err := c.getRepoCheckAccessAndSetting(ctx, session, repoRef, enum.PermissionRepoPush)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err) return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
} }

View File

@ -18,6 +18,7 @@ import (
"github.com/harness/gitness/app/auth/authz" "github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/remoteauth" "github.com/harness/gitness/app/services/remoteauth"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url" "github.com/harness/gitness/app/url"
"github.com/harness/gitness/blob" "github.com/harness/gitness/blob"
@ -37,6 +38,16 @@ func ProvideController(
blobStore blob.Store, blobStore blob.Store,
remoteAuth remoteauth.Service, remoteAuth remoteauth.Service,
urlProvider url.Provider, urlProvider url.Provider,
settings *settings.Service,
) *Controller { ) *Controller {
return NewController(authorizer, repoFinder, principalStore, lfsStore, blobStore, remoteAuth, urlProvider) return NewController(
authorizer,
repoFinder,
principalStore,
lfsStore,
blobStore,
remoteAuth,
urlProvider,
settings,
)
} }

View File

@ -22,9 +22,12 @@ import (
"github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git" "github.com/harness/gitness/git"
"github.com/harness/gitness/git/parser" "github.com/harness/gitness/git/parser"
"github.com/harness/gitness/git/sha" "github.com/harness/gitness/git/sha"
gitness_store "github.com/harness/gitness/store"
"github.com/harness/gitness/types/enum" "github.com/harness/gitness/types/enum"
) )
@ -89,32 +92,52 @@ func (c *Controller) Raw(ctx context.Context,
return nil, fmt.Errorf("failed to read blob: %w", err) return nil, fmt.Errorf("failed to read blob: %w", err)
} }
// check if blob is an LFS pointer gitLFSEnabled, err := settings.RepoGet(
content, err := io.ReadAll(io.LimitReader(blobReader.Content, parser.LfsPointerMaxSize)) ctx,
c.settings,
repo.ID,
settings.KeyGitLFSEnabled,
settings.DefaultGitLFSEnabled,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read LFS file content: %w", err) return nil, fmt.Errorf("failed to check settings for Git LFS enabled: %w", err)
} }
lfsInfo, ok := parser.IsLFSPointer(ctx, content, blobReader.Size) if !gitLFSEnabled {
if !ok {
return &RawContent{ return &RawContent{
Data: &multiReadCloser{ Data: blobReader.Content,
Reader: io.MultiReader(bytes.NewBuffer(content), blobReader.Content),
closeFunc: blobReader.Content.Close,
},
Size: blobReader.ContentSize, Size: blobReader.ContentSize,
SHA: blobReader.SHA, SHA: blobReader.SHA,
}, nil }, nil
} }
file, err := c.lfsCtrl.DownloadNoAuth(ctx, repo.ID, lfsInfo.OID) // check if blob is an LFS pointer
headerContent, err := io.ReadAll(io.LimitReader(blobReader.Content, parser.LfsPointerMaxSize))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to download LFS file: %w", err) return nil, fmt.Errorf("failed to read content: %w", err)
} }
lfsInfo, ok := parser.IsLFSPointer(ctx, headerContent, blobReader.Size)
if ok {
lfsContent, err := c.lfsCtrl.DownloadNoAuth(ctx, repo.ID, lfsInfo.OID)
if err == nil {
return &RawContent{ return &RawContent{
Data: file, Data: lfsContent,
Size: lfsInfo.Size, Size: lfsInfo.Size,
SHA: blobReader.SHA, SHA: blobReader.SHA,
}, nil }, nil
}
if !errors.Is(err, gitness_store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to download LFS file: %w", err)
}
}
return &RawContent{
Data: &multiReadCloser{
Reader: io.MultiReader(bytes.NewBuffer(headerContent), blobReader.Content),
closeFunc: blobReader.Content.Close,
},
Size: blobReader.ContentSize,
SHA: blobReader.SHA,
}, nil
} }

View File

@ -23,17 +23,20 @@ import (
// GeneralSettings represent the general repository settings as exposed externally. // GeneralSettings represent the general repository settings as exposed externally.
type GeneralSettings struct { type GeneralSettings struct {
FileSizeLimit *int64 `json:"file_size_limit" yaml:"file_size_limit"` FileSizeLimit *int64 `json:"file_size_limit" yaml:"file_size_limit"`
GitLFSEnabled *bool `json:"git_lfs_enabled" yaml:"git_lfs_enabled"`
} }
func GetDefaultGeneralSettings() *GeneralSettings { func GetDefaultGeneralSettings() *GeneralSettings {
return &GeneralSettings{ return &GeneralSettings{
FileSizeLimit: ptr.Int64(settings.DefaultFileSizeLimit), FileSizeLimit: ptr.Int64(settings.DefaultFileSizeLimit),
GitLFSEnabled: ptr.Bool(settings.DefaultGitLFSEnabled),
} }
} }
func GetGeneralSettingsMappings(s *GeneralSettings) []settings.SettingHandler { func GetGeneralSettingsMappings(s *GeneralSettings) []settings.SettingHandler {
return []settings.SettingHandler{ return []settings.SettingHandler{
settings.Mapping(settings.KeyFileSizeLimit, s.FileSizeLimit), settings.Mapping(settings.KeyFileSizeLimit, s.FileSizeLimit),
settings.Mapping(settings.KeyGitLFSEnabled, s.GitLFSEnabled),
} }
} }
@ -46,5 +49,13 @@ func GetGeneralSettingsAsKeyValues(s *GeneralSettings) []settings.KeyValue {
Value: s.FileSizeLimit, Value: s.FileSizeLimit,
}) })
} }
if s.GitLFSEnabled != nil {
kvs = append(kvs, settings.KeyValue{
Key: settings.KeyGitLFSEnabled,
Value: s.GitLFSEnabled,
})
}
return kvs return kvs
} }

View File

@ -92,6 +92,9 @@ var (
// ErrEmptyRepoNeedsBranch is returned if no branch found on the githook post receieve for empty repositories. // ErrEmptyRepoNeedsBranch is returned if no branch found on the githook post receieve for empty repositories.
ErrEmptyRepoNeedsBranch = New(http.StatusBadRequest, ErrEmptyRepoNeedsBranch = New(http.StatusBadRequest,
"Pushing to an empty repository requires at least one branch with commits.") "Pushing to an empty repository requires at least one branch with commits.")
// ErrGitLFSDisabled is returned if the Git LFS is disabled but LFS endpoint is requested.
ErrGitLFSDisabled = New(http.StatusBadRequest, "Git LFS is disabled")
) )
// Error represents a json-encoded API error. // Error represents a json-encoded API error.

View File

@ -30,6 +30,7 @@ import (
"github.com/harness/gitness/app/services/keywordsearch" "github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/publicaccess" "github.com/harness/gitness/app/services/publicaccess"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/sse" "github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
gitnessurl "github.com/harness/gitness/app/url" gitnessurl "github.com/harness/gitness/app/url"
@ -72,6 +73,7 @@ type Repository struct {
publicAccess publicaccess.Service publicAccess publicaccess.Service
eventReporter *repoevents.Reporter eventReporter *repoevents.Reporter
auditService audit.Service auditService audit.Service
settings *settings.Service
} }
var _ job.Handler = (*Repository)(nil) var _ job.Handler = (*Repository)(nil)
@ -295,6 +297,11 @@ func (r *Repository) Handle(ctx context.Context, data string, _ job.ProgressRepo
} }
} }
// revert this when import fetches LFS objects
if err := r.settings.RepoSet(ctx, repo.ID, settings.KeyGitLFSEnabled, false); err != nil {
log.Warn().Err(err).Msg("failed to disable Git LFS in repository settings")
}
log.Info().Msg("create git repository") log.Info().Msg("create git repository")
gitUID, err := r.createGitRepository(ctx, &systemPrincipal, repo.ID) gitUID, err := r.createGitRepository(ctx, &systemPrincipal, repo.ID)

View File

@ -19,6 +19,7 @@ import (
"github.com/harness/gitness/app/services/keywordsearch" "github.com/harness/gitness/app/services/keywordsearch"
"github.com/harness/gitness/app/services/publicaccess" "github.com/harness/gitness/app/services/publicaccess"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/sse" "github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url" "github.com/harness/gitness/app/url"
@ -53,6 +54,7 @@ func ProvideRepoImporter(
publicAccess publicaccess.Service, publicAccess publicaccess.Service,
eventReporter *repoevents.Reporter, eventReporter *repoevents.Reporter,
auditService audit.Service, auditService audit.Service,
settings *settings.Service,
) (*Repository, error) { ) (*Repository, error) {
importer := &Repository{ importer := &Repository{
defaultBranch: config.Git.DefaultBranch, defaultBranch: config.Git.DefaultBranch,
@ -70,6 +72,7 @@ func ProvideRepoImporter(
publicAccess: publicAccess, publicAccess: publicAccess,
eventReporter: eventReporter, eventReporter: eventReporter,
auditService: auditService, auditService: auditService,
settings: settings,
} }
err := executor.Register(jobType, importer) err := executor.Register(jobType, importer)

View File

@ -26,4 +26,6 @@ var (
DefaultInstallID = string("") DefaultInstallID = string("")
KeyPrincipalCommitterMatch Key = "principal_committer_match" KeyPrincipalCommitterMatch Key = "principal_committer_match"
DefaultPrincipalCommitterMatch = false DefaultPrincipalCommitterMatch = false
KeyGitLFSEnabled Key = "git_lfs_enabled"
DefaultGitLFSEnabled = true
) )

View File

@ -260,7 +260,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
return nil, err return nil, err
} }
auditService := audit.ProvideAuditService() auditService := audit.ProvideAuditService()
repository, err := importer.ProvideRepoImporter(config, provider, gitInterface, transactor, repoStore, pipelineStore, triggerStore, repoFinder, encrypter, jobScheduler, executor, streamer, indexer, publicaccessService, eventsReporter, auditService) repository, err := importer.ProvideRepoImporter(config, provider, gitInterface, transactor, repoStore, pipelineStore, triggerStore, repoFinder, encrypter, jobScheduler, executor, streamer, indexer, publicaccessService, eventsReporter, auditService, settingsService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -296,7 +296,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
return nil, err return nil, err
} }
remoteauthService := remoteauth.ProvideRemoteAuth(tokenStore, principalStore) remoteauthService := remoteauth.ProvideRemoteAuth(tokenStore, principalStore)
lfsController := lfs.ProvideController(authorizer, repoFinder, principalStore, lfsObjectStore, blobStore, remoteauthService, provider) lfsController := lfs.ProvideController(authorizer, repoFinder, principalStore, lfsObjectStore, blobStore, remoteauthService, provider, settingsService)
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, executionStore, ruleStore, checkStore, pullReqStore, settingsService, principalInfoCache, protectionManager, gitInterface, spaceFinder, repoFinder, repository, codeownersService, eventsReporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService, labelService, instrumentService, userGroupStore, searchService, rulesService, streamer, lfsController) repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, executionStore, ruleStore, checkStore, pullReqStore, settingsService, principalInfoCache, protectionManager, gitInterface, spaceFinder, repoFinder, repository, codeownersService, eventsReporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService, labelService, instrumentService, userGroupStore, searchService, rulesService, streamer, lfsController)
reposettingsController := reposettings.ProvideController(authorizer, repoFinder, settingsService, auditService) reposettingsController := reposettings.ProvideController(authorizer, repoFinder, settingsService, auditService)
stageStore := database.ProvideStageStore(db) stageStore := database.ProvideStageStore(db)

View File

@ -464,6 +464,9 @@ export interface StringsMap {
findOrCreateBranch: string findOrCreateBranch: string
firstTimeTitle: string firstTimeTitle: string
general: string general: string
'generalSetting.features': string
'generalSetting.gitLFSEnable': string
'generalSetting.gitLFSEnableDesc': string
generate: string generate: string
generateCloneCred: string generateCloneCred: string
generateCloneText: string generateCloneText: string

View File

@ -397,6 +397,10 @@ prReview:
targetBranchChange: '{user} changed the target branch from {old} to {new}' targetBranchChange: '{user} changed the target branch from {old} to {new}'
webhookListingContent: 'create,delete,deployment ...' webhookListingContent: 'create,delete,deployment ...'
general: 'General' general: 'General'
generalSetting:
features: Features
gitLFSEnable: Git Large File Storage (LFS)
gitLFSEnableDesc: Manage large files using Git LFS.
webhooks: 'Webhooks' webhooks: 'Webhooks'
open: Open open: Open
merged: Merged merged: Merged

View File

@ -32,7 +32,7 @@ import cx from 'classnames'
import { Color, FontVariation, Intent } from '@harnessio/design-system' import { Color, FontVariation, Intent } from '@harnessio/design-system'
import { Icon } from '@harnessio/icons' import { Icon } from '@harnessio/icons'
import { noop } from 'lodash-es' import { noop } from 'lodash-es'
import { useMutate } from 'restful-react' import { useGet, useMutate } from 'restful-react'
import { Render } from 'react-jsx-match' import { Render } from 'react-jsx-match'
import { ACCESS_MODES, getErrorMessage, permissionProps, voidFn } from 'utils/Utils' import { ACCESS_MODES, getErrorMessage, permissionProps, voidFn } from 'utils/Utils'
import { useStrings } from 'framework/strings' import { useStrings } from 'framework/strings'
@ -64,7 +64,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
const [defaultBranch, setDefaultBranch] = useState(ACCESS_MODES.VIEW) const [defaultBranch, setDefaultBranch] = useState(ACCESS_MODES.VIEW)
const { openModal: openDefaultBranchModal } = useDefaultBranchModal({ currentGitRef, setDefaultBranch, refetch }) const { openModal: openDefaultBranchModal } = useDefaultBranchModal({ currentGitRef, setDefaultBranch, refetch })
const { showError, showSuccess } = useToaster() const { showError, showSuccess } = useToaster()
const { standalone, hooks } = useAppContext() const { standalone, hooks, routingId } = useAppContext()
const space = useGetSpaceParam() const space = useGetSpaceParam()
const { allowPublicResourceCreation } = usePublicResourceConfig() const { allowPublicResourceCreation } = usePublicResourceConfig()
const { getString } = useStrings() const { getString } = useStrings()
@ -83,6 +83,17 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
path: `/api/v1/repos/${repoMetadata?.path}/+/public-access` path: `/api/v1/repos/${repoMetadata?.path}/+/public-access`
}) })
const { data: generalSettingsData, refetch: refetchSettings } = useGet({
path: `/api/v1/repos/${repoMetadata?.path}/+/settings/general`,
queryParams: { routingId: routingId }
})
const { mutate: updateGeneralSettings } = useMutate({
verb: 'PATCH',
path: `/api/v1/repos/${repoMetadata?.path}/+/settings/general`,
queryParams: { routingId: routingId }
})
const permEditResult = hooks?.usePermissionTranslate?.( const permEditResult = hooks?.usePermissionTranslate?.(
{ {
resource: { resource: {
@ -178,12 +189,14 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
return ( return (
<Formik <Formik
enableReinitialize
formName="repoGeneralSettings" formName="repoGeneralSettings"
initialValues={{ initialValues={{
name: repoMetadata?.identifier, name: repoMetadata?.identifier,
desc: repoMetadata?.description, desc: repoMetadata?.description,
defaultBranch: repoMetadata?.default_branch, defaultBranch: repoMetadata?.default_branch,
isPublic: currRepoVisibility isPublic: currRepoVisibility,
gitLFSEnabled: generalSettingsData?.git_lfs_enabled ?? true
}} }}
onSubmit={voidFn(mutate)}> onSubmit={voidFn(mutate)}>
{formik => { {formik => {
@ -412,6 +425,56 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
</Layout.Horizontal> </Layout.Horizontal>
</Container> </Container>
</Render> </Render>
<Container padding="medium" margin={{ bottom: 'medium' }} className={css.generalContainer}>
<Layout.Horizontal padding={{ bottom: 'medium' }}>
<Container className={css.label}>
<Text color={Color.GREY_600} className={css.textSize} margin={{ top: 'medium' }}>
{getString('generalSetting.features')}
</Text>
</Container>
<Layout.Vertical spacing="small" padding={{ button: 'small', top: 'small' }}>
<Container className={css.content}>
<Layout.Horizontal flex={{ alignItems: 'center' }} spacing={'small'}>
<FormInput.Toggle
{...permissionProps(permEditResult, standalone)}
key={'gitLFSEnabled'}
style={{ margin: '0px' }}
label=""
name="gitLFSEnabled"
/>
<Text color={Color.GREY_800} className={css.featureText}>
{getString('generalSetting.gitLFSEnable')}
</Text>
<Text color={Color.GREY_500} className={css.featureText}>
{getString('generalSetting.gitLFSEnableDesc')}
</Text>
</Layout.Horizontal>
<Layout.Horizontal className={css.buttonContainer}>
{generalSettingsData?.git_lfs_enabled !== formik.values.gitLFSEnabled ? (
<Button
margin={{ top: 'medium' }}
type="submit"
text={getString('save')}
variation={ButtonVariation.PRIMARY}
size={ButtonSize.SMALL}
onClick={() => {
updateGeneralSettings({ git_lfs_enabled: formik.values.gitLFSEnabled })
.then(() => {
showSuccess(getString('repoUpdate'))
refetchSettings()
})
.catch(err => {
showError(getErrorMessage(err))
})
}}
{...permissionProps(permEditResult, standalone)}
/>
) : null}
</Layout.Horizontal>
</Container>
</Layout.Vertical>
</Layout.Horizontal>
</Container>
<Container padding="medium" margin={{ bottom: 'medium' }} className={css.generalContainer}> <Container padding="medium" margin={{ bottom: 'medium' }} className={css.generalContainer}>
<Layout.Horizontal padding={{ bottom: 'medium' }}> <Layout.Horizontal padding={{ bottom: 'medium' }}>
<Container className={css.label}> <Container className={css.label}>

View File

@ -117,6 +117,13 @@
text-transform: capitalize; text-transform: capitalize;
} }
.featureText {
font-size: 12px !important;
font-style: normal !important;
font-weight: 300 !important;
white-space: nowrap !important;
}
.dividerContainer { .dividerContainer {
opacity: 0.2; opacity: 0.2;
height: 1px; height: 1px;

View File

@ -26,6 +26,7 @@ export declare const descText: string
export declare const dialogContainer: string export declare const dialogContainer: string
export declare const dividerContainer: string export declare const dividerContainer: string
export declare const editContainer: string export declare const editContainer: string
export declare const featureText: string
export declare const generalContainer: string export declare const generalContainer: string
export declare const headerContainer: string export declare const headerContainer: string
export declare const iconContainer: string export declare const iconContainer: string

View File

@ -48,7 +48,6 @@ export default function RepositorySettings() {
gitRef: normalizeGitRef(gitRef) as string, gitRef: normalizeGitRef(gitRef) as string,
resourcePath resourcePath
}) })
useDisableCodeMainLinks(!!isRepositoryEmpty) useDisableCodeMainLinks(!!isRepositoryEmpty)
const tabListArray = [ const tabListArray = [
{ {