Merge branch 'main' into main

This commit is contained in:
RedCocoon 2024-10-30 23:21:47 +08:00 committed by GitHub
commit abc5d12590
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 77 additions and 47 deletions

View File

@ -324,6 +324,10 @@ RUN_USER = ; git
;; Maximum number of locks returned per page ;; Maximum number of locks returned per page
;LFS_LOCKS_PAGING_NUM = 50 ;LFS_LOCKS_PAGING_NUM = 50
;; ;;
;; When clients make lfs batch requests, reject them if there are more pointers than this number
;; zero means 'unlimited'
;LFS_MAX_BATCH_SIZE = 0
;;
;; Allow graceful restarts using SIGHUP to fork ;; Allow graceful restarts using SIGHUP to fork
;ALLOW_GRACEFUL_RESTARTS = true ;ALLOW_GRACEFUL_RESTARTS = true
;; ;;
@ -2638,6 +2642,10 @@ LEVEL = Info
;; override the azure blob base path if storage type is azureblob ;; override the azure blob base path if storage type is azureblob
;AZURE_BLOB_BASE_PATH = lfs/ ;AZURE_BLOB_BASE_PATH = lfs/
;[lfs_client]
;; When mirroring an upstream lfs endpoint, limit the number of pointers in each batch request to this number
;BATCH_SIZE = 20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; settings for packages, will override storage setting ;; settings for packages, will override storage setting

View File

@ -16,10 +16,9 @@ import (
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"
) )
const httpBatchSize = 20
// HTTPClient is used to communicate with the LFS server // HTTPClient is used to communicate with the LFS server
// https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md // https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md
type HTTPClient struct { type HTTPClient struct {
@ -30,7 +29,7 @@ type HTTPClient struct {
// BatchSize returns the preferred size of batchs to process // BatchSize returns the preferred size of batchs to process
func (c *HTTPClient) BatchSize() int { func (c *HTTPClient) BatchSize() int {
return httpBatchSize return setting.LFSClient.BatchSize
} }
func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient { func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient {

View File

@ -10,7 +10,10 @@ import (
"code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/generate"
) )
// LFS represents the configuration for Git LFS // LFS represents the server-side configuration for Git LFS.
// Ideally these options should be in a section like "[lfs_server]",
// but they are in "[server]" section due to historical reasons.
// Could be refactored in the future while keeping backwards compatibility.
var LFS = struct { var LFS = struct {
StartServer bool `ini:"LFS_START_SERVER"` StartServer bool `ini:"LFS_START_SERVER"`
AllowPureSSH bool `ini:"LFS_ALLOW_PURE_SSH"` AllowPureSSH bool `ini:"LFS_ALLOW_PURE_SSH"`
@ -18,15 +21,21 @@ var LFS = struct {
HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"` HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"` MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"` LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
MaxBatchSize int `ini:"LFS_MAX_BATCH_SIZE"`
Storage *Storage Storage *Storage
}{} }{}
// LFSClient represents configuration for Gitea's LFS clients, for example: mirroring upstream Git LFS
var LFSClient = struct {
BatchSize int `ini:"BATCH_SIZE"`
}{}
func loadLFSFrom(rootCfg ConfigProvider) error { func loadLFSFrom(rootCfg ConfigProvider) error {
mustMapSetting(rootCfg, "lfs_client", &LFSClient)
mustMapSetting(rootCfg, "server", &LFS)
sec := rootCfg.Section("server") sec := rootCfg.Section("server")
if err := sec.MapTo(&LFS); err != nil {
return fmt.Errorf("failed to map LFS settings: %v", err)
}
lfsSec, _ := rootCfg.GetSection("lfs") lfsSec, _ := rootCfg.GetSection("lfs")
@ -53,6 +62,10 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
LFS.LocksPagingNum = 50 LFS.LocksPagingNum = 50
} }
if LFSClient.BatchSize < 1 {
LFSClient.BatchSize = 20
}
LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour) LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour)
if !LFS.StartServer || !InstallLock { if !LFS.StartServer || !InstallLock {

View File

@ -99,3 +99,19 @@ STORAGE_TYPE = minio
assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
} }
func Test_LFSClientServerConfigs(t *testing.T) {
iniStr := `
[server]
LFS_MAX_BATCH_SIZE = 100
[lfs_client]
# will default to 20
BATCH_SIZE = 0
`
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, 100, LFS.MaxBatchSize)
assert.EqualValues(t, 20, LFSClient.BatchSize)
}

16
package-lock.json generated
View File

@ -47,7 +47,6 @@
"sortablejs": "1.15.2", "sortablejs": "1.15.2",
"swagger-ui-dist": "5.17.14", "swagger-ui-dist": "5.17.14",
"tailwindcss": "3.4.10", "tailwindcss": "3.4.10",
"temporal-polyfill": "0.2.5",
"throttle-debounce": "5.0.2", "throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
@ -14748,21 +14747,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/temporal-polyfill": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.5.tgz",
"integrity": "sha512-ye47xp8Cb0nDguAhrrDS1JT1SzwEV9e26sSsrWzVu+yPZ7LzceEcH0i2gci9jWfOfSCCgM3Qv5nOYShVUUFUXA==",
"license": "MIT",
"dependencies": {
"temporal-spec": "^0.2.4"
}
},
"node_modules/temporal-spec": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz",
"integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==",
"license": "ISC"
},
"node_modules/terser": { "node_modules/terser": {
"version": "5.31.6", "version": "5.31.6",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz",

View File

@ -46,7 +46,6 @@
"sortablejs": "1.15.2", "sortablejs": "1.15.2",
"swagger-ui-dist": "5.17.14", "swagger-ui-dist": "5.17.14",
"tailwindcss": "3.4.10", "tailwindcss": "3.4.10",
"temporal-polyfill": "0.2.5",
"throttle-debounce": "5.0.2", "throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",

View File

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
@ -39,7 +40,7 @@ func Organizations(ctx *context.Context) {
) )
sortOrder := ctx.FormString("sort") sortOrder := ctx.FormString("sort")
if sortOrder == "" { if sortOrder == "" {
sortOrder = "newest" sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
ctx.SetFormString("sort", sortOrder) ctx.SetFormString("sort", sortOrder)
} }

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/sitemap"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
@ -149,7 +150,7 @@ func Users(ctx *context.Context) {
) )
sortOrder := ctx.FormString("sort") sortOrder := ctx.FormString("sort")
if sortOrder == "" { if sortOrder == "" {
sortOrder = "newest" sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
ctx.SetFormString("sort", sortOrder) ctx.SetFormString("sort", sortOrder)
} }

View File

@ -179,6 +179,11 @@ func BatchHandler(ctx *context.Context) {
return return
} }
if setting.LFS.MaxBatchSize != 0 && len(br.Objects) > setting.LFS.MaxBatchSize {
writeStatus(ctx, http.StatusRequestEntityTooLarge)
return
}
contentStore := lfs_module.NewContentStore() contentStore := lfs_module.NewContentStore()
var responseObjects []*lfs_module.ObjectResponse var responseObjects []*lfs_module.ObjectResponse

View File

@ -102,7 +102,7 @@
{{$sameBase := ne $.BaseName $.HeadUserName}} {{$sameBase := ne $.BaseName $.HeadUserName}}
{{$differentBranch := ne . $.HeadBranch}} {{$differentBranch := ne . $.HeadBranch}}
{{if or $sameBase $differentBranch}} {{if or $sameBase $differentBranch}}
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-branch="{{.}}">{{$.BaseName}}{{if $.HeadRepo}}/{{$.HeadRepo}}{{end}}:{{.}}</div> <div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-branch="{{.}}">{{$.BaseName}}:{{.}}</div>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>

View File

@ -17,7 +17,7 @@ const colors = ref({
const activityTopAuthors = window.config.pageData.repoActivityTopAuthors || []; const activityTopAuthors = window.config.pageData.repoActivityTopAuthors || [];
const graphPoints = computed(() => { const graphPoints = computed(() => {
return activityTopAuthors.value.map((item) => { return activityTopAuthors.map((item) => {
return { return {
value: item.commits, value: item.commits,
label: item.name, label: item.name,
@ -26,7 +26,7 @@ const graphPoints = computed(() => {
}); });
const graphAuthors = computed(() => { const graphAuthors = computed(() => {
return activityTopAuthors.value.map((item, idx) => { return activityTopAuthors.map((item, idx) => {
return { return {
position: idx + 1, position: idx + 1,
...item, ...item,
@ -35,7 +35,7 @@ const graphAuthors = computed(() => {
}); });
const graphWidth = computed(() => { const graphWidth = computed(() => {
return activityTopAuthors.value.length * 40; return activityTopAuthors.length * 40;
}); });
const styleElement = ref<HTMLElement | null>(null); const styleElement = ref<HTMLElement | null>(null);

View File

@ -7,9 +7,15 @@ test('toAbsoluteLocaleDate', () => {
day: 'numeric', day: 'numeric',
})).toEqual('March 15, 2024'); })).toEqual('March 15, 2024');
expect(toAbsoluteLocaleDate('2024-03-15', 'de-DE', { expect(toAbsoluteLocaleDate('2024-03-15T01:02:03', 'de-DE', {
year: 'numeric', year: 'numeric',
month: 'long', month: 'long',
day: 'numeric', day: 'numeric',
})).toEqual('15. März 2024'); })).toEqual('15. März 2024');
expect(toAbsoluteLocaleDate('12345-03-15 01:02:03', '', {
year: 'numeric',
month: 'short',
day: 'numeric',
})).toEqual('Mar 15, 12345');
}); });

View File

@ -1,30 +1,28 @@
import {Temporal} from 'temporal-polyfill'; export function toAbsoluteLocaleDate(date: string, lang: string, opts: Intl.DateTimeFormatOptions) {
return new Date(date).toLocaleString(lang || [], opts);
export function toAbsoluteLocaleDate(dateStr, lang, opts) {
return Temporal.PlainDate.from(dateStr).toLocaleString(lang ?? [], opts);
} }
window.customElements.define('absolute-date', class extends HTMLElement { window.customElements.define('absolute-date', class extends HTMLElement {
static observedAttributes = ['date', 'year', 'month', 'weekday', 'day']; static observedAttributes = ['date', 'year', 'month', 'weekday', 'day'];
initialized = false;
update = () => { update = () => {
const year = this.getAttribute('year') ?? ''; const opt: Intl.DateTimeFormatOptions = {};
const month = this.getAttribute('month') ?? ''; for (const attr of ['year', 'month', 'weekday', 'day']) {
const weekday = this.getAttribute('weekday') ?? ''; if (this.getAttribute(attr)) opt[attr] = this.getAttribute(attr);
const day = this.getAttribute('day') ?? ''; }
const lang = this.closest('[lang]')?.getAttribute('lang') || const lang = this.closest('[lang]')?.getAttribute('lang') ||
this.ownerDocument.documentElement.getAttribute('lang') || ''; this.ownerDocument.documentElement.getAttribute('lang') || '';
// only use the first 10 characters, e.g. the `yyyy-mm-dd` part // only use the date part, it is guaranteed to be in ISO format (YYYY-MM-DDTHH:mm:ss.sssZ)
const dateStr = this.getAttribute('date').substring(0, 10); let date = this.getAttribute('date');
let dateSep = date.indexOf('T');
dateSep = dateSep === -1 ? date.indexOf(' ') : dateSep;
date = dateSep === -1 ? date : date.substring(0, dateSep);
if (!this.shadowRoot) this.attachShadow({mode: 'open'}); if (!this.shadowRoot) this.attachShadow({mode: 'open'});
this.shadowRoot.textContent = toAbsoluteLocaleDate(dateStr, lang, { this.shadowRoot.textContent = toAbsoluteLocaleDate(date, lang, opt);
...(year && {year}),
...(month && {month}),
...(weekday && {weekday}),
...(day && {day}),
});
}; };
attributeChangedCallback(_name, oldValue, newValue) { attributeChangedCallback(_name, oldValue, newValue) {