Merge branch 'forgejo' into landingpagedetails
Some checks failed
Integration tests for the release process / release-simulation (push) Has been cancelled

This commit is contained in:
David Rotermund 2025-02-18 10:40:49 +00:00
commit 20c03184be
44 changed files with 4415 additions and 1056 deletions

View file

@ -28,7 +28,7 @@ jobs:
runs-on: docker
container:
image: data.forgejo.org/renovate/renovate:39.164.1
image: data.forgejo.org/renovate/renovate:39.171.2
steps:
- name: Load renovate repo cache

View file

@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.29.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@39.164.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
RENOVATE_NPM_PACKAGE ?= renovate@39.171.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
@ -127,6 +127,11 @@ FORGEJO_VERSION_API ?= ${FORGEJO_VERSION}
show-version-api:
@echo ${FORGEJO_VERSION_API}
# Strip binaries by default to reduce size, allow overriding for debugging
STRIP ?= 1
ifeq ($(STRIP),1)
LDFLAGS := $(LDFLAGS) -s -w
endif
LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(FORGEJO_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION_API)"
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
@ -827,7 +832,7 @@ check: test
.PHONY: install $(TAGS_PREREQ)
install: $(wildcard *.go)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
.PHONY: build
build: frontend backend
@ -855,13 +860,13 @@ merge-locales:
@echo "NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY"
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@
forgejo: $(EXECUTABLE)
ln -f $(EXECUTABLE) forgejo
static-executable: $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -o $(EXECUTABLE)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -o $(EXECUTABLE)
.PHONY: release
release: frontend generate release-linux release-copy release-compress vendor release-sources release-check

View file

@ -219,8 +219,13 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
return "", 0, fmt.Errorf("ParsePublicKey: %w", err)
}
pkeyType := pkey.Type()
if certPkey, ok := pkey.(*ssh.Certificate); ok {
pkeyType = certPkey.Key.Type()
}
// The ssh library can parse the key, so next we find out what key exactly we have.
switch pkey.Type() {
switch pkeyType {
case ssh.KeyAlgoDSA:
rawPub := struct {
Name string

View file

@ -35,6 +35,7 @@ func Test_SSHParsePublicKey(t *testing.T) {
{"ecdsa-384", false, "ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
{"ecdsa-sk", true, "ecdsa-sk", 256, "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment"},
{"ed25519-sk", true, "ed25519-sk", 256, "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIE7kM1R02+4ertDKGKEDcKG0s+2vyDDcIvceJ0Gqv5f1AAAABHNzaDo= nocomment"},
{"ed25519-cert-v01", true, "ed25519", 256, "ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIAlIAPlEj0mYQzQo8Ks0Nm/Ct8ceNkyJSf4DLuF5l7+5AAAAIEuWAoaBo2tT29/oMNnoDfdAPRCIdM2RGapKUhY4nDfLRgPQwfnRoc0AAAABAAAAcHZhdWx0LW9pZGMtNmRhYjdiZDgtNDg5YS00MDFkLTg3ZmItNjdjNTlhMDZkZDkxLTNjNTk2M2YyMGRmMDM3MDkyMzc1YmNiYmNiNzkxY2EyZWIxM2I0NGZhMzc2NTcwMWI0MjMwODU0MWFmNjhkNTgAAAALAAAAB2Zvcmdlam8AAAAAZ6/RUQAAAABn115vAAAAAAAAAAAAAAAAAAACFwAAAAdzc2gtcnNhAAAAAwEAAQAAAgEAySnM/TvD117GyKgOgMatDB2t+fCHORFaWVmH5SaadAzNJ2DfDAauRSLfnim1xdgAOMTzsPEEHH47zyYMjE85o2AiJxrfUBMw3O/7AbNc6+HyLr/txH4+vD9tWQknKnpVWM+3Z9wiHDcOdKRoXCmFZKJH1vxs16GNWjwbrfNiimv7Oi0fadgvTDKX603gpLTuVDXqs9eQFLCONptei86JYBAJqaHvg51k8YUCKt9WFqKAj7BJUWmrDvhv5VFMOsnZieJjqxkoxnpsQNlXfPzxK0vIpJofbYfWwscv/g9WZypHwO1ZR2PqzKm99YrSdr8w5256l0f44vsF0NSP0N7bDQEfYYnRGj8zWTYCBFD+uYF7AxIeaRUpZoTQO8MvCHOLMIDinNgEeCUvNA2v9zHl4BGq+PQjzUKAgJiKj0MZeiCDAmQ22g83ggQlB6BOrBb1fNa/S1cmTbGHQ2oAN358aqkmHVCBhPOyA2Rf65D2M2vzDlUdOsNDUIWAHk7GbwSNGDgcYfTWqtR5fTzp2MJovMh1dDUDXjOvojbhzjJtSy9+rzUYIv18aXdOitzVBgPMWdeVCZFZv4OKF+5MiqxQvedUvfiSjsdxZWLxyT1CJ88G3MzxNMS/Djm86T8h/Oa55bdvFtqpsLfvpIqq0pnXq1V/vF2j1MWwRB5z5Xh/HtEAAAIUAAAADHJzYS1zaGEyLTI1NgAAAgB2I2gzqemQl8/ETxtakALlm/2BpUcbhADcFWuoH6BCPnWHuTSwf3OayM6KXv1PQfL3YFRoi9Afrp8kVFL6DePsmKH+0BUEMz71sZ7v1ty7pwfzibItGnpTbQXhzbEiNYAFoz77rl7oaXF7pV6JNZhj3DVAB5gVA2oN5KRNVxijz+6uyuFJEw1HIl1C7GworvGwZcN7BThTEh3i72/Vntejy9Z8uGVjSFjS0rjRo2oXK1LKN0rVt66p3TmCWHouLkVnOTk0qrhLGlL2HVyo24OYHbkAAObD9b6aMDYlmluk6NsaiTKsSTsvMrbIbjtFQlh7nNyoPhZ0VMwaT1l10pDQ5uxWWZjKGIkz4xM1ZfpBszjJNPo+ivYQnTSjj9LwkbLAT9a/5LawSj80TGcLEMO+0eyPdJsP0wYmOVRFAZeRiBgwb3HrzcF6Wqr8icj1EjYkKSy9YFHGTnFBGknpdh3HGwghRXrCUwAnSM76db9pv4/qowT8LthtJ3dY5Epe0OJ1Tqm+q8bkGH4gB+7uqLSqM5pIHSKLp7lfHQBt1J6xa7H2saiweaWjU+QGTgQ2Lg+uUC5DXJrmm60CeFJ4BoGhUenDlgijbQpjH/l6330PbwefgjWtUK/pqaEA4lCoPyvJ+eF2DbYfPiAIBAFQnhVJJae4AH+XoCt29nb2j30ztg== nocomment"},
}
for _, tc := range testCases {

View file

@ -45,6 +45,7 @@
full_name: ' < U<se>r Tw<o > >< '
email: user2@example.com
keep_email_private: true
keep_pronouns_private: true
email_notifications_preference: enabled
passwd: ZogKvWdyEx:password
passwd_hash_algo: dummy
@ -350,6 +351,7 @@
full_name: User Ten
email: user10@example.com
keep_email_private: false
keep_pronouns_private: true
email_notifications_preference: enabled
passwd: ZogKvWdyEx:password
passwd_hash_algo: dummy

View file

@ -58,40 +58,42 @@ var migrations = []*Migration{
NewMigration("Add the `apply_to_admins` column to the `protected_branch` table", forgejo_v1_22.AddApplyToAdminsSetting),
// v9 -> v10
NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser),
// v11 -> v12
// v10 -> v11
NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue),
// v12 -> v13
// v11 -> v12
NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount),
// v13 -> v14
// v12 -> v13
NewMigration("Add `hide_archive_links` column to `release` table", AddHideArchiveLinksToRelease),
// v14 -> v15
// v13 -> v14
NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge),
// v15 -> v16
// v14 -> v15
NewMigration("Create the `federation_host` table", CreateFederationHostTable),
// v16 -> v17
// v15 -> v16
NewMigration("Create the `federated_user` table", CreateFederatedUserTable),
// v17 -> v18
// v16 -> v17
NewMigration("Add `normalized_federated_uri` column to `user` table", AddNormalizedFederatedURIToUser),
// v18 -> v19
// v17 -> v18
NewMigration("Create the `following_repo` table", CreateFollowingRepoTable),
// v19 -> v20
// v18 -> v19
NewMigration("Add external_url to attachment table", AddExternalURLColumnToAttachmentTable),
// v20 -> v21
// v19 -> v20
NewMigration("Creating Quota-related tables", CreateQuotaTables),
// v21 -> v22
// v20 -> v21
NewMigration("Add SSH keypair to `pull_mirror` table", AddSSHKeypairToPushMirror),
// v22 -> v23
// v21 -> v22
NewMigration("Add `legacy` to `web_authn_credential` table", AddLegacyToWebAuthnCredential),
// v23 -> v24
// v22 -> v23
NewMigration("Add `delete_branch_after_merge` to `auto_merge` table", AddDeleteBranchAfterMergeToAutoMerge),
// v24 -> v25
// v23 -> v24
NewMigration("Add `purpose` column to `forgejo_auth_token` table", AddPurposeToForgejoAuthToken),
// v25 -> v26
// v24 -> v25
NewMigration("Migrate `secret` column to store keying material", MigrateTwoFactorToKeying),
// v26 -> v27
// v25 -> v26
NewMigration("Add `hash_blake2b` column to `package_blob` table", AddHashBlake2bToPackageBlob),
// v27 -> v28
// v26 -> v27
NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect),
// v27 -> v28
NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser),
}
// GetCurrentDBVersion returns the current Forgejo database version.

View file

@ -0,0 +1,15 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgejo_migrations //nolint:revive
import "xorm.io/xorm"
func AddHidePronounsOptionToUser(x *xorm.Engine) error {
type User struct {
ID int64 `xorm:"pk autoincr"`
KeepPronounsPrivate bool `xorm:"NOT NULL DEFAULT false"`
}
return x.Sync(&User{})
}

View file

@ -154,6 +154,7 @@ type User struct {
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Theme string `xorm:"NOT NULL DEFAULT ''"`
KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
KeepPronounsPrivate bool `xorm:"NOT NULL DEFAULT false"`
EnableRepoUnitHints bool `xorm:"NOT NULL DEFAULT true"`
}
@ -500,6 +501,16 @@ func (u *User) GetCompleteName() string {
return u.Name
}
// GetPronouns returns an empty string, if the user has set to keep his
// pronouns private from non-logged in users, otherwise the pronouns
// are returned.
func (u *User) GetPronouns(signed bool) string {
if u.KeepPronounsPrivate && !signed {
return ""
}
return u.Pronouns
}
func gitSafeName(name string) string {
return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name))
}
@ -854,48 +865,46 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
// VerifyUserActiveCode verifies that the code is valid for the given purpose for this user.
// If delete is specified, the token will be deleted.
func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose, delete bool) (*User, error) {
func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose) (user *User, deleteToken func() error, err error) {
lookupKey, validator, found := strings.Cut(code, ":")
if !found {
return nil, nil
return nil, nil, nil
}
authToken, err := auth.FindAuthToken(ctx, lookupKey, purpose)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
return nil, nil
return nil, nil, nil
}
return nil, err
return nil, nil, err
}
if authToken.IsExpired() {
return nil, auth.DeleteAuthToken(ctx, authToken)
return nil, nil, auth.DeleteAuthToken(ctx, authToken)
}
rawValidator, err := hex.DecodeString(validator)
if err != nil {
return nil, err
return nil, nil, err
}
if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 {
return nil, errors.New("validator doesn't match")
return nil, nil, errors.New("validator doesn't match")
}
u, err := GetUserByID(ctx, authToken.UID)
if err != nil {
if IsErrUserNotExist(err) {
return nil, nil
return nil, nil, nil
}
return nil, err
return nil, nil, err
}
if delete {
if err := auth.DeleteAuthToken(ctx, authToken); err != nil {
return nil, err
}
deleteToken = func() error {
return auth.DeleteAuthToken(ctx, authToken)
}
return u, nil
return u, deleteToken, nil
}
// ValidateUser check if user is valid to insert / update into database

View file

@ -756,13 +756,13 @@ func TestVerifyUserAuthorizationToken(t *testing.T) {
assert.True(t, ok)
t.Run("Wrong purpose", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset, false)
u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset)
require.NoError(t, err)
assert.Nil(t, u)
})
t.Run("No delete", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, false)
u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation)
require.NoError(t, err)
assert.EqualValues(t, user.ID, u.ID)
@ -772,9 +772,10 @@ func TestVerifyUserAuthorizationToken(t *testing.T) {
})
t.Run("Delete", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, true)
u, deleteToken, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation)
require.NoError(t, err)
assert.EqualValues(t, user.ID, u.ID)
require.NoError(t, deleteToken())
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
require.ErrorIs(t, err, util.ErrNotExist)
@ -795,3 +796,42 @@ func TestGetInactiveUsers(t *testing.T) {
require.NoError(t, err)
require.Empty(t, users)
}
func TestPronounsPrivacy(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
t.Run("EmptyPronounsIfNoneSet", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user.Pronouns = ""
user.KeepPronounsPrivate = false
assert.Equal(t, "", user.GetPronouns(false))
})
t.Run("EmptyPronounsIfSetButPrivateAndNotLoggedIn", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user.Pronouns = "any"
user.KeepPronounsPrivate = true
assert.Equal(t, "", user.GetPronouns(false))
})
t.Run("ReturnPronounsIfSetAndNotPrivateAndNotLoggedIn", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user.Pronouns = "any"
user.KeepPronounsPrivate = false
assert.Equal(t, "any", user.GetPronouns(false))
})
t.Run("ReturnPronounsIfSetAndPrivateAndLoggedIn", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user.Pronouns = "any"
user.KeepPronounsPrivate = false
assert.Equal(t, "any", user.GetPronouns(true))
})
t.Run("ReturnPronounsIfSetAndNotPrivateAndLoggedIn", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user.Pronouns = "any"
user.KeepPronounsPrivate = true
assert.Equal(t, "any", user.GetPronouns(true))
})
}

View file

@ -1,4 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
@ -107,6 +108,9 @@ func getLicensePlaceholder(name string) *licensePlaceholder {
}
// Other special placeholders can be added here.
} else if name == "BSD-4-Clause" {
ret.Owner = append(ret.Owner, "COPYRIGHT HOLDER")
ret.Owner = append(ret.Owner, "the organization")
}
return ret
}

View file

@ -1,4 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
@ -170,6 +171,31 @@ Copyright (C) 2023 by Gitea teabot@gitea.io
...
... THE AUTHOR BE LIABLE FOR ...
`,
},
{
name: "BSD-4-Clause",
args: args{
name: "BSD-4-Clause",
values: &LicenseValues{Year: "2025", Owner: "Forgejo", Email: "hello@forgejo.org", Repo: "forgejo"},
origin: `
Copyright (c) <year> <owner>. All rights reserved.
... includes software developed by the organization.
... Neither the name of the copyright holder nor
... PROVIDED BY COPYRIGHT HOLDER "AS IS" ... NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE ...
`,
},
want: `
Copyright (c) 2025 Forgejo. All rights reserved.
... includes software developed by Forgejo.
... Neither the name of the copyright holder nor
... PROVIDED BY Forgejo "AS IS" ... NO EVENT SHALL Forgejo BE LIABLE ...
`,
},
}

View file

@ -84,6 +84,7 @@ type UserSettings struct {
EnableRepoUnitHints bool `json:"enable_repo_unit_hints"`
// Privacy
HideEmail bool `json:"hide_email"`
HidePronouns bool `json:"hide_pronouns"`
HideActivity bool `json:"hide_activity"`
}
@ -101,6 +102,7 @@ type UserSettingsOptions struct {
EnableRepoUnitHints *bool `json:"enable_repo_unit_hints"`
// Privacy
HideEmail *bool `json:"hide_email"`
HidePronouns *bool `json:"hide_pronouns"`
HideActivity *bool `json:"hide_activity"`
}

View file

@ -853,6 +853,8 @@ email_preference_set_success = Email preference has been set successfully.
add_openid_success = The new OpenID address has been added.
keep_email_private = Hide email address
keep_email_private_popup = Your email address will not be shown on your profile and will not be the default for commits made via the web interface, like file uploads, edits, and merge commits. Instead, a special address %s can be used to link commits to your account. This option will not affect existing commits.
keep_pronouns_private = Only show pronouns to authenticated users
keep_pronouns_private.description = This will hide your pronouns from visitors that are not logged in.
openid_desc = OpenID lets you delegate authentication to an external provider.
manage_ssh_keys = Manage SSH keys
@ -3935,4 +3937,4 @@ filepreview.lines = Lines %[1]d to %[2]d in %[3]s
filepreview.truncated = Preview has been truncated
[translation_meta]
test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :)
test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :)

View file

@ -134,6 +134,11 @@ new_repo.title = مخزن جدید
new_migrate.title = مهاجرت جدید
new_repo.link = مخزن جدید
filter = فیلتر
filter.is_archived = بایگانی شده
filter.public = عمومی
filter.private = خصوصی
[aria]
[heatmap]
@ -1987,6 +1992,8 @@ error.csv.too_large=نمی توان این فایل را رندر کرد زیر
error.csv.unexpected=نمی توان این فایل را رندر کرد زیرا حاوی یک کاراکتر غیرمنتظره در خط %d و ستون %d است.
error.csv.invalid_field_count=نمی توان این فایل را رندر کرد زیرا تعداد فیلدهای آن در خط %d اشتباه است.
milestones.filter_sort.name = نام
[graphs]
[org]
@ -2540,6 +2547,9 @@ notices.op=عملیات.
notices.delete_success=گزارش سیستم حذف شده است.
config_summary = چکیده
config_settings = تنظيمات
[action]
create_repo=مخزن ایجاد شده <a href="%s"> %s</a>
rename_repo=مخزن تغییر نام داد از <code>%[1]s</code> به <a href="%[2]s">%[3]s</a>
@ -2669,3 +2679,5 @@ executable_file = فایل اجرایی
normal_file = فایل معمولی
changed_filemode = %[1] ها ← %[2] ها
directory = پوشه
[search]

File diff suppressed because it is too large Load diff

View file

@ -1270,6 +1270,8 @@ topic.done=Kész
milestones.filter_sort.name = Név
[graphs]
[org]
@ -1684,6 +1686,9 @@ config.cache_item_ttl = Gyorsítótárelem TTL értéke
config.app_data_path = Alkalmazásadatok elérési útja
config_summary = Összefoglaló
config_settings = Beállítások
[action]
create_repo=létrehozott tárolót: <a href="%s"> %s</a>
rename_repo=átnevezte a(z) <code>%[1]s</code> tárolót <a href="%[2]s">%[3]s</a>-ra/re

View file

@ -84,15 +84,93 @@ concept_code_repository=Repositori
name=Nama
re_type = Konfirmasi Kata Sandi
webauthn_insert_key = Masukkan kunci keamanan anda
webauthn_sign_in = Tekan tombol pada kunci keamanan Anda. Jika kunci keamanan Anda tidak memiliki tombol, masukkan kembali.
webauthn_press_button = Silakan tekan tombol pada kunci keamanan Anda…
webauthn_use_twofa = Gunakan kode dua faktor dari telepon Anda
webauthn_error = Tidak dapat membaca kunci keamanan Anda.
webauthn_unsupported_browser = Browser Anda saat ini tidak mendukung WebAuthn.
webauthn_error_unknown = Terdapat kesalahan yang tidak diketahui. Mohon coba lagi.
webauthn_error_insecure = `WebAuthn hanya mendukung koneksi aman. Untuk pengujian melalui HTTP, Anda dapat menggunakan "localhost" atau "127.0.0.1"`
webauthn_error_unable_to_process = Server tidak dapat memproses permintaan Anda.
webauthn_error_duplicated = Kunci keamanan tidak diperbolehkan untuk permintaan ini. Pastikan bahwa kunci ini belum terdaftar sebelumnya.
webauthn_error_empty = Anda harus menetapkan nama untuk kunci ini.
webauthn_error_timeout = Waktu habis sebelum kunci Anda dapat dibaca. Mohon muat ulang halaman ini dan coba lagi.
new_project = Proyek Baru
new_project_column = Kolom Baru
ok = Oke
retry = Coba lagi
rerun = Jalankan ulang
rerun_all = Jalankan ulang semua job
remove_label_str = `Hapus item "%s"`
view = Tampilan
test = Pengujian
locked = Terkunci
copy = Salin
copy_url = Salin URL
copy_hash = Salin hash
copy_content = Salin konten
copy_branch = Salin nama branch
copy_success = Tersalin!
copy_error = Gagal menyalin
copy_type_unsupported = Tipe berkas ini tidak dapat disalin
error = Gangguan
error404 = Halaman yang akan kamu akses <strong>tidak dapat ditemukan</strong> atau <strong>kamu tidak memiliki akses </strong> untuk melihatnya.
go_back = Kembali
invalid_data = Data invalid: %v
never = Tidak Pernah
unknown = Tidak diketahui
rss_feed = Umpan Berita
pin = Sematkan
unpin = Lepas sematan
artifacts = Artefak
archived = Diarsipkan
concept_system_global = Global
concept_user_individual = Perorangan
show_full_screen = Tampilkan layar penuh
download_logs = Unduh Logs
confirm_delete_selected = Konfirmasi untuk menghapus semua item yang dipilih?
value = Nilai
filter = Saring
filter.is_archived = Diarsipkan
filter.not_archived = Tidak Diarsipkan
filter.public = Publik
filter.private = Pribadi
[aria]
navbar = Bar Navigasi
footer = Footer
footer.links = Tautan
[heatmap]
number_of_contributions_in_the_last_12_months = %s Kontribusi pada 12 bulan terakhir
less = Lebih sedikit
more = Lebih banyak
[editor]
buttons.heading.tooltip = Tambahkan heading
buttons.bold.tooltip = Tambahkan teks Tebal
buttons.italic.tooltip = Tambahkan teks Miring
buttons.quote.tooltip = Kutip teks
buttons.code.tooltip = Tambah Kode
buttons.link.tooltip = Tambahkan tautan
buttons.list.unordered.tooltip = Tambah daftar titik
buttons.list.ordered.tooltip = Tambah daftar angka
buttons.list.task.tooltip = Tambahkan daftar tugas
buttons.mention.tooltip = Tandai pengguna atau tim
buttons.ref.tooltip = Merujuk pada isu atau permintaan tarik
buttons.switch_to_legacy.tooltip = Gunakan editor versi lama
buttons.enable_monospace_font = Aktifkan font monospace
buttons.disable_monospace_font = Non-Aktifkan font monospace
[filter]
string.asc = A - Z
string.desc = Z - A
[error]
occurred = Terjadi kesalahan
not_found = Target tidak dapat ditemukan.
[startpage]
app_desc=Sebuah layanan hosting Git sendiri yang tanpa kesulitan
@ -124,6 +202,9 @@ require_sign_in_view=Harus Login Untuk Melihat Halaman
admin_password=Kata Sandi
admin_email=Alamat Email
email_title = Pengaturan email
smtp_from = Kirim Email Sebagai
[home]
uname_holder=Nama Pengguna atau Alamat Surel
password_holder=Kata Sandi
@ -141,6 +222,8 @@ show_private=Pribadi
issues.in_your_repos=Dalam repositori anda
show_archived = Diarsipkan
[explore]
repos=Repositori
users=Pengguna
@ -492,6 +575,8 @@ email_notifications.submit=Pasang Pengaturan Email
visibility.private=Pribadi
visibility.public = Publik
[repo]
owner=Pemilik
repo_name=Nama Repositori
@ -955,6 +1040,12 @@ branch.deleted_by=Dihapus oleh %s
desc.public = Publik
desc.archived = Diarsipkan
commitstatus.error = Gangguan
projects.new = Proyek Baru
milestones.filter_sort.name = Nama
[graphs]
[org]
@ -1009,6 +1100,8 @@ teams.delete_team_success=Tim sudah di hapus.
teams.repositories=Tim repositori
teams.search_repo_placeholder=Cari repositori…
settings.visibility.public = Publik
[admin]
dashboard=Dasbor
organizations=Organisasi
@ -1267,6 +1360,9 @@ notices.op=Op.
notices.delete_success=Laporan sistem telah dihapus.
config_settings = Pengaturan
users.list_status_filter.menu_text = Saring
[action]
create_repo=repositori dibuat <a href="%s">%s</a>
rename_repo=ganti nama gudang penyimpanan dari <code>%[1]s</code> ke <a href="%[2]s">%[3]s</a>
@ -1342,6 +1438,56 @@ runs.commit=Memperbuat
runs.no_matching_online_runner_helper = Tidak ada runner online yang cocok dengan label: %s
runs.actor = Aktor
runs.status = Status
runs.actors_no_select = Semua aktor
runs.status_no_select = Semua status
runs.no_results = Tidak ada hasil yang cocok.
runs.no_workflows = Belum ada alur kerja.
runs.no_runs = Alur kerja belum berjalan.
runs.empty_commit_message = (pesan commit kosong)
workflow.disable = Nonaktifkan Alur Kerja
workflow.enable = Aktifkan Alur Kerja
workflow.disabled = Alur kerja dinonaktifkan.
need_approval_desc = Butuh persetujuan untuk menjalankan alur kerja untuk pull request fork.
variables = Variabel
variables.creation = Tambah Variabel
variables.none = Belum ada variabel.
variables.deletion = Hapus variabel
variables.deletion.description = Menghapus variabel bersifat permanen dan tidak dapat dibatalkan. Lanjutkan?
variables.description = Variabel akan diteruskan ke beberapa tindakan dan tidak dapat dibaca sebaliknya.
variables.id_not_exist = Variabel dengan ID %d tidak ada.
variables.edit = Edit Variabel
variables.deletion.failed = Gagal menghapus variabel.
variables.deletion.success = Variabel telah dihapus.
variables.creation.failed = Gagal menambahkan variabel.
variables.creation.success = Variabel "%s" telah ditambahkan.
variables.update.failed = Gagal mengedit variabel.
variables.update.success = Variabel telah diedit.
[projects]
type-1.display_name = Proyek Individu
type-2.display_name = Proyek Repositori
type-3.display_name = Proyek Organisasi
[git.filemode]
changed_filemode = %[1]s → %[2]s
directory = Directory
normal_file = Normal file
executable_file = Executable file
symbolic_link = Symbolic link
submodule = Submodule
[search]
search = Cari...
type_tooltip = Tipe pencarian
fuzzy_tooltip = Termasuk juga hasil yang mendekati kata pencarian
exact_tooltip = Hanya menampilkan hasil yang cocok dengan istilah pencarian
repo_kind = Cari repo...
user_kind = Telusuri pengguna...
org_kind = Cari organisasi...
team_kind = Cari tim...
code_kind = Cari kode...
code_search_unavailable = Pencarian kode saat ini tidak tersedia. Silahkan hubungi administrator.
branch_kind = Cari cabang...

View file

@ -114,6 +114,10 @@ value=Gildi
sign_in_with_provider = Skrá inn með %s
enable_javascript = Þessi síða krefst JavaScript.
filter = Sía
filter.is_archived = Safnvistað
filter.public = Opinbert
[aria]
[heatmap]
@ -1118,6 +1122,8 @@ topic.done=Í lagi
milestones.filter_sort.name = Heiti
[graphs]
[org]
@ -1287,6 +1293,9 @@ notices.type_2=Verkefni
notices.desc=Lýsing
config_summary = Yfirlit
config_settings = Stillingar
[action]
create_issue=`opnaði vandamál <a href="%[1]s">%[3]s#%[2]s</a>`
reopen_issue=`enduropnaði vandamál <a href="%[1]s">%[3]s#%[2]s</a>`
@ -1371,3 +1380,5 @@ runs.commit=Framlag
[projects]
[git.filemode]
[search]

View file

@ -167,6 +167,8 @@ new_org.link = 新しい組織
test = テスト
error413 = 割り当て量を使い切りしました。
copy_path = パスをコピー
[aria]
navbar=ナビゲーションバー
footer=フッター
@ -3515,6 +3517,8 @@ config.app_slogan = インスタンスのスローガン
config.cache_test = テストキャッシュ
config.cache_test_failed = キャッシュの調査に失敗しました: %v.
[action]
create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
rename_repo=がリポジトリ名を <code>%[1]s</code> から <a href="%[2]s">%[3]s</a> へ変更しました
@ -3758,6 +3762,8 @@ rpm.repository.multiple_groups = このパッケージは複数のグループ
owner.settings.cargo.rebuild.no_index = 再構築できません、インデックスが初期化されていません。
npm.dependencies.bundle = バンドルされた依存関係
search_in_external_registry = %s で検索
[secrets]
secrets=シークレット
description=シークレットは特定のActionsに渡されます。 それ以外で読み出されることはありません。
@ -3875,11 +3881,15 @@ workflow.dispatch.invalid_input_type = 入力タイプ「%s」が無効です。
workflow.dispatch.warn_input_limit = 最初の %d 個の入力のみを表示します。
runs.no_job = ワークフローには少なくとも1つのジョブが含まれている必要があります
runs.expire_log_message = ログは古すぎるため消去されています。
[projects]
type-1.display_name=個人プロジェクト
type-2.display_name=リポジトリ プロジェクト
type-3.display_name=組織プロジェクト
deleted.display_name = 削除されたプロジェクト
[git.filemode]
changed_filemode=%[1]s → %[2]s
directory=ディレクトリ

View file

@ -376,7 +376,7 @@ allow_password_change=사용자에게 비밀번호 변경을 요청 (권장됨)
reset_password_mail_sent_prompt=확인 메일이 <b>%s</b>로 전송되었습니다. 받은 편지함으로 도착한 메일을 %s 안에 확인해서 비밀번호 찾기 절차를 완료하십시오.
active_your_account=계정 활성화
account_activated=계정이 활성화 되었습니다
prohibit_login =
prohibit_login =
resent_limit_prompt=활성화를 위한 이메일을 이미 전송했습니다. 3분 내로 이메일을 받지 못한 경우 재시도해주세요.
has_unconfirmed_mail=안녕하세요 %s, 이메일 주소(<b>%s</b>)가 확인되지 않았습니다. 확인 메일을 받으시지 못하겼거나 새로운 확인 메일이 필요하다면, 아래 버튼을 클릭해 재발송하실 수 있습니다.
resend_mail=여기를 눌러 확인 메일 재전송
@ -1425,6 +1425,8 @@ archive.title_date = 이 저장소는 %s에 보관처리되었습니다. 파일
milestones.filter_sort.name = 이름
[graphs]
[org]
@ -1797,6 +1799,9 @@ emails.filter_sort.name_reverse = 사용자명 (예약됨)
config.allow_dots_in_usernames = 사용자들이 마침표를 사용자명에 사용할 수 있도록 허가합니다. 이미 존재하는 계정에는 영향을 주지 않습니다.
config_summary = 요약
config_settings = 설정
[action]
create_repo=저장소를 만들었습니다. <a href="%s">%s</a>
rename_repo=저장소 이름을 <code>%[1]s에서</code>에서 <a href="%[2]s"> %[3]s</a>으로 변경함

View file

@ -101,6 +101,11 @@ concept_user_organization=සංවිධානය
name=නම
filter = පෙරහන
filter.is_archived = සංරක්ෂිත
filter.public = ප්‍රසිද්ධ
filter.private = පෞද්ගලික
[aria]
[heatmap]
@ -1913,6 +1918,8 @@ error.csv.too_large=එය ඉතා විශාල නිසා මෙම ග
error.csv.unexpected=%d පේළියේ සහ %dතීරුවේ අනපේක්ෂිත චරිතයක් අඩංගු බැවින් මෙම ගොනුව විදැහුම්කරණය කළ නොහැක.
error.csv.invalid_field_count=මෙම ගොනුව රේඛාවේ වැරදි ක්ෂේත්ර සංඛ්යාවක් ඇති බැවින් එය විදැහුම්කරණය කළ නොහැක %d.
milestones.filter_sort.name = නම
[graphs]
[org]
@ -2462,6 +2469,9 @@ notices.op=ඔප්.
notices.delete_success=පද්ධති දැන්වීම් මකා දමා ඇත.
config_summary = සාරාංශය
config_settings = සැකසුම්
[action]
create_repo=නිර්මිත ගබඩාව <a href="%s">%s</a>
rename_repo=<code>%[1]s</code> සිට <a href="%[2]s">%[3]s</a>දක්වා නම් කරන ලද ගබඩාව
@ -2554,3 +2564,5 @@ runs.commit=කැප
[git.filemode]
symbolic_link=සංකේතාත්මක සබැඳිය
[search]

View file

@ -142,6 +142,9 @@ name=Meno
value=Hodnota
issues = Problémy
filter.is_archived = Archivované
filter.private = Súkromný
[aria]
navbar=Navigačná lišta
footer=Päta
@ -1370,4 +1373,6 @@ runners.labels=Štítky
[projects]
[git.filemode]
symbolic_link=Symbolický odkaz
symbolic_link=Symbolický odkaz
[search]

View file

@ -342,6 +342,8 @@ app_slogan_helper = Oluşum sloganınızı giriniz. Devre dışı bırakmak içi
enable_update_checker_helper_forgejo = release.forgejo.org adresindeki TXT DNS kayıdı kullanılarak yeni Forgejo sürümleri düzenli olarak kontrol edilecektir.
allow_dots_in_usernames = Kullanıcı isimlerinde noktaya izin ver. Var olan kullanıcıları etkilemez.
smtp_from_invalid = `"E-posta Olarak Gönder" adresi geçersiz`
[home]
uname_holder=Kullanıcı adı veya e-posta adresi
password_holder=Parola
@ -663,6 +665,9 @@ admin_cannot_delete_self = Yöneticiyken kullanıcınızı silemezsiniz. Lütfen
username_error_no_dots = ` sadece alfanumerik karakterler ("0-9","a-z","A-Z"), tire ("-") ve alt tire ("-") içerebilir. Alfanumerik olmayan karakterlerle başlayamaz ve bitemez, ayrıca ardışık alfanumerik olmayan karakterler de kullanılamaz.`
unset_password = Oturum açma kullanıcısı parola belirlemedi.
unsupported_login_type = Oturum açma türü hesap silmeyi desteklemiyor.
[user]
change_avatar=Profil resmini değiştir…
joined_on=%s tarihinde katıldı
@ -2692,7 +2697,37 @@ activity.navbar.contributors = Katılımcılar
contributors.contribution_type.deletions = Çıkarmalar
settings.new_owner_blocked_doer = Yeni sahip sizi engelledi.
open_with_editor = %s ile aç
object_format = Nesne Biçimi
mirror_sync = eşitlendi
stars = Yıldızlar
desc.sha256 = SHA256
vendored = Sağlanmış
generated = Üretilmiş
editor.push_out_of_date = İtme eskimiş.
commits.search_branch = Bu Dal
issues.edit.already_changed = Konuya yapılan değişiklikler kaydedilemiyor. İçerik başka kullanıcı tarafından değiştirilmiş gözüküyor. Diğerlerinin değişikliklerinin üzerine yazmamak için lütfen sayfayı yenileyin ve tekrar düzenlemeye çalışın
pulls.edit.already_changed = Değişiklik isteğine yapılan değişiklikler kaydedilemiyor. İçerik başka kullanıcı tarafından değiştirilmiş gözüküyor. Diğerlerinin değişikliklerinin üzerine yazmamak için lütfen sayfayı yenileyin ve tekrar düzenlemeye çalışın
pulls.nothing_to_compare_have_tag = Seçili dal/etiket aynı.
pulls.fast_forward_only_merge_pull_request = Sadece ileri sarma
comments.edit.already_changed = Yoruma yapılan değişiklikler kaydedilemiyor. İçerik başka kullanıcı tarafından değiştirilmiş gözüküyor. Diğerlerinin değişikliklerinin üzerine yazmamak için lütfen sayfayı yenileyin ve tekrar düzenlemeye çalışın
milestones.filter_sort.name = Ad
activity.navbar.pulse = Eğilim
activity.navbar.code_frequency = Kod Frekansı
activity.navbar.recent_commits = Son İşlemeler
settings.mirror_settings.pushed_repository = İtilmiş depo
settings.ignore_stale_approvals = Eskimiş onayları yoksay
settings.ignore_stale_approvals_desc = Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir.
error.broken_git_hook = Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen <a target="_blank" rel="noreferrer" href="%s">belgelere</a> bakın, daha sonra durumu yenilemek için bazı işlemeler itin.
[graphs]
component_loading = %s yükleniyor...
component_loading_failed = %s yüklenemedi
component_loading_info = Bu biraz sürebilir…
component_failed_to_load = Beklenmedik bir hata oluştu.
code_frequency.what = kod frekansı
contributors.what = katkılar
recent_commits.what = son işlemeler
[org]
org_name_holder=Organizasyon Adı
@ -3346,6 +3381,23 @@ notices.op=İşlem
notices.delete_success=Sistem bildirimleri silindi.
self_check = Öz Denetim
config_summary = Özet
config_settings = Ayarlar
dashboard.sync_repo_tags = Etiketleri git verisinden veritabanına eşitle
emails.delete = E-postayı Sil
emails.delete_desc = Bu e-posta adresini silmek istediğinizden emin misiniz?
emails.deletion_success = E-posta adresi silindi.
emails.delete_primary_email_error = Ana e-posta adresini silemezsiniz.
config.cache_test = Önbelleği Sına
config.cache_test_failed = Önbelleğin incelenmesi başarısız oldu: %v.
config.cache_test_slow = Önbellek sınaması başarılı, ancak yanıt yavaş: %s.
config.cache_test_succeeded = Önbellek sınaması başarılı, %s sürede bir yanıt alındı.
config.open_with_editor_app_help = Klon menüsü için "Birlikte aç" düzenleyicileri. Boş bırakılırsa, varsayılan kullanılacaktır. Varsayılanı görmek için genişletin.
self_check.no_problem_found = Henüz bir sorun bulunmadı.
self_check.database_collation_mismatch = Veritabanının şu harmanlamayı kullanmasını bekle: %s
self_check.database_inconsistent_collation_columns = Veritabanı %s harmanlamasını kullanıyor, ancak bu sütunlar uyumsuz harmanlamalar kullanıyor. Bu beklenmedik sorunlar oluşturabilir.
[action]
create_repo=depo <a href="%s">%s</a> oluşturuldu
rename_repo=<code>%[1]s</code> olan depo adını <a href="%[2]s">%[3]s</a> buna çevirdi
@ -3586,6 +3638,9 @@ owner.settings.chef.title=Chef Kütüğü
owner.settings.chef.keypair=Anahtar çifti üret
owner.settings.chef.keypair.description=Chef kütüğünde kimlik doğrulaması için bir anahtar çifti gereklidir. Eğer daha önce bir anahtar çifti ürettiyseniz, yeni bir anahtar çifti üretmek eski anahtar çiftini ıskartaya çıkartacaktır.
npm.dependencies.bundle = Paketlenmiş Bağımlılıklar
rpm.repository.multiple_groups = Bu paket birçok grupta mevcut.
[secrets]
secrets=Gizlilikler
description=Gizlilikler belirli işlemlere aktarılacaktır, bunun dışında okunamaz.
@ -3693,6 +3748,10 @@ runs.no_workflows.documentation = Gitea İşlem'i hakkında daha fazla bilgi iç
variables.id_not_exist = %d kimlikli değişken mevcut değil.
runs.no_workflows.quick_start = Gitea İşlem'i nasıl başlatacağınızı bilmiyor musunuz? <a target="_blank" rel="noopener noreferrer" href="%s">Hızlı başlangıç rehberine</a> bakabilirsiniz.
runs.no_job_without_needs = İş akışı en azından bağımlılığı olmayan bir görev içermelidir.
runs.no_job = İş akışı en azından bir görev içermelidir
runs.expire_log_message = Günlükler, çok eski oldukları için temizlendiler.
[projects]
type-1.display_name=Kişisel Proje
type-2.display_name=Depo Projesi
@ -3732,3 +3791,5 @@ keyword_search_unavailable = Anahtar kelime ile arama şu anda kullanıma açık
fuzzy_tooltip = Arama terimine yakın olan eşleşmeleri dahil et
union_tooltip = Boşlukla ayrılmış anahtar kelime eşleşmelerini dahil et
exact_tooltip = Sadece arama terimiyle tam uyuşan sonuçları dahit et.
fuzzy = Bulanık
exact = Tam

View file

@ -134,6 +134,8 @@ copy_generic = 複製到剪貼簿
copy_url = 複製網址
copy_hash = 複製雜湊值
filter.private = 私有庫
[aria]
footer = 頁尾
footer.links = 連結
@ -791,6 +793,8 @@ settings.branches.update_default_branch = 更新預設分支
milestones.filter_sort.name = 組織名稱
[graphs]
[org]
@ -1076,6 +1080,8 @@ users = 使用者帳戶
defaulthooks = 預設 Webhook
config_settings = 組織設定
[action]
create_repo=建立了儲存庫 <a href="%s">%s</a>
rename_repo=重新命名儲存庫 <code>%[1]s</code> 為 <a href="%[2]s">%[3]s</a>
@ -1146,4 +1152,6 @@ runners.labels = 標籤
[projects]
[git.filemode]
[git.filemode]
[search]

View file

@ -2842,6 +2842,17 @@ release.summary_card_alt = 儲存庫 %[2]s 中名為「%[1]s」的發行摘要
error.broken_git_hook = 此儲存庫的 Git 鉤子似乎已損壞。請按照<a target="_blank" rel="noreferrer" href="%s">文件</a>修復它們,然後推送一些提交以重新整理狀態。
issues.reaction.alt_remove = 從留言中移除 %[1]s 的反應。
vendored = 已供應
settings.mirror_settings.docs.doc_link_pull_section = 文件中的「從遠端儲存庫拉取」部分。
settings.event_pull_request_review_request_desc = 合併請求審核請求或審核請求已移除。
settings.protect_status_check_patterns_desc = 輸入模式以指定其他分支在合併到受此規則保護的分支前必須通過的狀態檢查。每行指定一個模式,模式不得為空白。
settings.protect_invalid_status_check_pattern = 狀態檢查模式無效: 「%s」。
settings.ignore_stale_approvals_desc = 不計算在較舊提交上進行的核可(過時的審核)作為合併請求的核可數量。如果過時的審核已經被捨棄,則無關緊要。
settings.tags.protection.pattern.description = 您可以使用單一名稱或 glob 模式或正則表達式來匹配多個標籤。詳情請參閱 <a target="_blank" rel="noopener" href="%s">受保護標籤指南</a>。
settings.thread_id = 線程 ID
settings.archive.text = 封存儲存庫將使其完全變為唯讀。它將從儀表板中隱藏。沒有人(甚至包括您!)將能夠進行新的提交,或打開任何問題或合併請求。
diff.comment.add_line_comment = 新增行評論
[graphs]
component_loading = 載入中 %s…
code_frequency.what = 寫程式頻率
@ -3536,6 +3547,9 @@ users.admin.description = 授予此使用者透過網頁介面和 API 提供的
auths.tip.gitea = 註冊一個新的 OAuth2 應用程式。指南可在 %s 找到
monitor.queue.activeworkers = 活躍工作者
monitor.queue.settings.desc = 集區會根據工作者佇列的阻塞情況動態增長。
[action]
create_repo=建立了儲存庫 <a href="%s">%s</a>
rename_repo=重新命名儲存庫 <code>%[1]s</code> 為 <a href="%[2]s">%[3]s</a>

1629
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -85,7 +85,7 @@
"eslint-plugin-vue-scoped-css": "2.9.0",
"eslint-plugin-wc": "2.2.0",
"globals": "15.15.0",
"happy-dom": "16.8.1",
"happy-dom": "17.1.0",
"license-checker-rseidelsohn": "4.4.2",
"markdownlint-cli": "0.44.0",
"postcss-html": "1.8.0",

12
poetry.lock generated
View file

@ -44,13 +44,13 @@ files = [
[[package]]
name = "cssbeautifier"
version = "1.15.2"
version = "1.15.3"
description = "CSS unobfuscator and beautifier."
optional = false
python-versions = "*"
files = [
{file = "cssbeautifier-1.15.2-py3-none-any.whl", hash = "sha256:6daf7f6012ff2914092d675793a5a7f602ca36a10159d0cf5c119183004306e0"},
{file = "cssbeautifier-1.15.2.tar.gz", hash = "sha256:02d42ffa6aefaa87f18452b437dbb73f6b98f42e9a84759810dc58f7a6bfc26c"},
{file = "cssbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:0dcaf5ce197743a79b3a160b84ea58fcbd9e3e767c96df1171e428125b16d410"},
{file = "cssbeautifier-1.15.3.tar.gz", hash = "sha256:406b04d09e7d62c0be084fbfa2cba5126fe37359ea0d8d9f7b963a6354fc8303"},
]
[package.dependencies]
@ -115,13 +115,13 @@ files = [
[[package]]
name = "jsbeautifier"
version = "1.15.2"
version = "1.15.3"
description = "JavaScript unobfuscator and beautifier."
optional = false
python-versions = "*"
files = [
{file = "jsbeautifier-1.15.2-py3-none-any.whl", hash = "sha256:d599aed6dcb0d5431190e5ad7335900d5fdc67236082fe6b6d3fb61d568d7417"},
{file = "jsbeautifier-1.15.2.tar.gz", hash = "sha256:6aff11af2c6cb9a2ce135f33a5b223cf5ee676ab7ff5da0edac01e23734f5755"},
{file = "jsbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:b207a15ab7529eee4a35ae7790e9ec4e32a2b5026d51e2d0386c3a65e6ecfc91"},
{file = "jsbeautifier-1.15.3.tar.gz", hash = "sha256:5f1baf3d4ca6a615bb5417ee861b34b77609eeb12875555f8bbfabd9bf2f3457"},
]
[package.dependencies]

View file

@ -63,6 +63,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
Theme: optional.FromPtr(form.Theme),
DiffViewStyle: optional.FromPtr(form.DiffViewStyle),
KeepEmailPrivate: optional.FromPtr(form.HideEmail),
KeepPronounsPrivate: optional.FromPtr(form.HidePronouns),
KeepActivityPrivate: optional.FromPtr(form.HideActivity),
EnableRepoUnitHints: optional.FromPtr(form.EnableRepoUnitHints),
}

View file

@ -31,6 +31,7 @@ func AuthShared(ctx *context.Base, sessionStore auth_service.SessionStore, authM
ctx.Data["SignedUserID"] = ar.Doer.ID
ctx.Data["IsAdmin"] = ar.Doer.IsAdmin
} else {
ctx.Data["IsSigned"] = false
ctx.Data["SignedUserID"] = int64(0)
}
return ar, nil

View file

@ -62,7 +62,7 @@ func autoSignIn(ctx *context.Context) (bool, error) {
return false, nil
}
u, err := user_model.VerifyUserAuthorizationToken(ctx, authCookie, auth.LongTermAuthorization, false)
u, _, err := user_model.VerifyUserAuthorizationToken(ctx, authCookie, auth.LongTermAuthorization)
if err != nil {
return false, fmt.Errorf("VerifyUserAuthorizationToken: %w", err)
}
@ -672,7 +672,7 @@ func Activate(ctx *context.Context) {
return
}
user, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation, false)
user, deleteToken, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation)
if err != nil {
ctx.ServerError("VerifyUserAuthorizationToken", err)
return
@ -693,6 +693,11 @@ func Activate(ctx *context.Context) {
return
}
if err := deleteToken(); err != nil {
ctx.ServerError("deleteToken", err)
return
}
handleAccountActivation(ctx, user)
}
@ -741,7 +746,7 @@ func ActivatePost(ctx *context.Context) {
return
}
user, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation, true)
user, deleteToken, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.UserActivation)
if err != nil {
ctx.ServerError("VerifyUserAuthorizationToken", err)
return
@ -770,6 +775,11 @@ func ActivatePost(ctx *context.Context) {
}
}
if err := deleteToken(); err != nil {
ctx.ServerError("deleteToken", err)
return
}
handleAccountActivation(ctx, user)
}
@ -830,7 +840,7 @@ func ActivateEmail(ctx *context.Context) {
code := ctx.FormString("code")
emailStr := ctx.FormString("email")
u, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.EmailActivation(emailStr), true)
u, deleteToken, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.EmailActivation(emailStr))
if err != nil {
ctx.ServerError("VerifyUserAuthorizationToken", err)
return
@ -840,6 +850,11 @@ func ActivateEmail(ctx *context.Context) {
return
}
if err := deleteToken(); err != nil {
ctx.ServerError("deleteToken", err)
return
}
email, err := user_model.GetEmailAddressOfUser(ctx, emailStr, u.ID)
if err != nil {
ctx.ServerError("GetEmailAddressOfUser", err)

View file

@ -116,7 +116,7 @@ func commonResetPassword(ctx *context.Context, shouldDeleteToken bool) (*user_mo
}
// Fail early, don't frustrate the user
u, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.PasswordReset, shouldDeleteToken)
u, deleteToken, err := user_model.VerifyUserAuthorizationToken(ctx, code, auth.PasswordReset)
if err != nil {
ctx.ServerError("VerifyUserAuthorizationToken", err)
return nil, nil
@ -127,6 +127,13 @@ func commonResetPassword(ctx *context.Context, shouldDeleteToken bool) (*user_mo
return nil, nil
}
if shouldDeleteToken {
if err := deleteToken(); err != nil {
ctx.ServerError("deleteToken", err)
return nil, nil
}
}
twofa, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil {
if !auth.IsErrTwoFactorNotEnrolled(err) {

View file

@ -106,6 +106,7 @@ func ProfilePost(ctx *context.Context) {
Location: optional.Some(form.Location),
Visibility: optional.Some(form.Visibility),
KeepActivityPrivate: optional.Some(form.KeepActivityPrivate),
KeepPronounsPrivate: optional.Some(form.KeepPronounsPrivate),
}
if err := user_service.UpdateUser(ctx, ctx.Doer, opts); err != nil {
ctx.ServerError("UpdateUser", err)

View file

@ -57,7 +57,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
Created: user.CreatedUnix.AsTime(),
Restricted: user.IsRestricted,
Location: user.Location,
Pronouns: user.Pronouns,
Pronouns: user.GetPronouns(signed),
Website: user.Website,
Description: user.Description,
// counter's
@ -97,6 +97,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings {
Description: user.Description,
Theme: user.Theme,
HideEmail: user.KeepEmailPrivate,
HidePronouns: user.KeepPronounsPrivate,
HideActivity: user.KeepActivityPrivate,
DiffViewStyle: user.DiffViewStyle,
EnableRepoUnitHints: user.EnableRepoUnitHints,

View file

@ -224,6 +224,7 @@ type UpdateProfileForm struct {
Biography string `binding:"MaxSize(255)"`
Visibility structs.VisibleType
KeepActivityPrivate bool
KeepPronounsPrivate bool
}
// Validate validates the fields

View file

@ -40,6 +40,7 @@ type UpdateOptions struct {
SetLastLogin bool
RepoAdminChangeTeamAccess optional.Option[bool]
EnableRepoUnitHints optional.Option[bool]
KeepPronounsPrivate optional.Option[bool]
}
func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error {
@ -97,6 +98,12 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
cols = append(cols, "enable_repo_unit_hints")
}
if opts.KeepPronounsPrivate.Has() {
u.KeepPronounsPrivate = opts.KeepPronounsPrivate.Value()
cols = append(cols, "keep_pronouns_private")
}
if opts.AllowGitHook.Has() {
u.AllowGitHook = opts.AllowGitHook.Value()

View file

@ -72,7 +72,7 @@
<ul class="list">
{{if $hasArchiveLinks}}
<li>
<a class="archive-link tw-flex-1 flex-text-inline tw-font-bold" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow">
<a class="archive-link tw-flex-1 flex-text-inline tw-font-bold" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow" type="application/zip">
{{svg "octicon-file-zip" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)
</a>
<div class="tw-mr-1">
@ -83,7 +83,7 @@
</span>
</li>
<li class="{{if $hasReleaseAttachment}}start-gap{{end}}">
<a class="archive-link tw-flex-1 flex-text-inline tw-font-bold" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">
<a class="archive-link tw-flex-1 flex-text-inline tw-font-bold" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow" type="application/gzip">
{{svg "octicon-file-zip" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.source_code"}} (TAR.GZ)
</a>
<div class="tw-mr-1">

View file

@ -16,7 +16,7 @@
</div>
<div class="content tw-break-anywhere profile-avatar-name">
{{if .ContextUser.FullName}}<span class="header text center">{{.ContextUser.FullName}}</span>{{end}}
<span class="username text center">{{.ContextUser.Name}}{{if .ContextUser.Pronouns}} · {{.ContextUser.Pronouns}}{{end}} {{if .IsAdmin}}
<span class="username text center">{{.ContextUser.Name}} {{if .ContextUser.GetPronouns .IsSigned}} · {{.ContextUser.GetPronouns .IsSigned}}{{end}} {{if .IsAdmin}}
<a class="muted" href="{{AppSubUrl}}/admin/users/{{.ContextUser.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">
{{svg "octicon-gear" 18}}
</a>

View file

@ -27954,6 +27954,10 @@
"type": "boolean",
"x-go-name": "HideEmail"
},
"hide_pronouns": {
"type": "boolean",
"x-go-name": "HidePronouns"
},
"language": {
"type": "string",
"x-go-name": "Language"
@ -28006,6 +28010,10 @@
"type": "boolean",
"x-go-name": "HideEmail"
},
"hide_pronouns": {
"type": "boolean",
"x-go-name": "HidePronouns"
},
"language": {
"type": "string",
"x-go-name": "Language"

View file

@ -120,6 +120,12 @@
{{ctx.Locale.Tr "settings.keep_activity_private"}}
<span class="help">{{ctx.Locale.Tr "settings.keep_activity_private.description" (printf "/%s?tab=activity" .SignedUser.Name)}}</span>
</label>
<label>
<input name="keep_pronouns_private" type="checkbox" {{if .SignedUser.KeepPronounsPrivate}}checked{{end}}>
{{ctx.Locale.Tr "settings.keep_pronouns_private"}}
<span class="help">{{ctx.Locale.Tr "settings.keep_pronouns_private.description"}}</span>
</label>
</fieldset>
<button class="ui primary button">{{ctx.Locale.Tr "settings.update_profile"}}</button>

View file

@ -43,10 +43,17 @@ test('External Release Attachments', async ({page, isMobile}) => {
// Validate release page and click edit
await expect(page).toHaveURL('/user2/repo2/releases');
await expect(page.locator('.download[open] li')).toHaveCount(3);
await expect(page.locator('.download[open] li:nth-of-type(1)')).toContainText('Source code (ZIP)');
await expect(page.locator('.download[open] li:nth-of-type(1) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.');
await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.zip');
await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('type', 'application/zip');
await expect(page.locator('.download[open] li:nth-of-type(2)')).toContainText('Source code (TAR.GZ)');
await expect(page.locator('.download[open] li:nth-of-type(2) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.');
await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.tar.gz');
await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('type', 'application/gzip');
await expect(page.locator('.download[open] li:nth-of-type(3)')).toContainText('Test');
await expect(page.locator('.download[open] li:nth-of-type(3) a')).toHaveAttribute('href', 'https://forgejo.org/');
await save_visual(page);

View file

@ -438,8 +438,16 @@ func TestUserHints(t *testing.T) {
func TestUserPronouns(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
// user1 is admin, using user2 and user10 respectively instead.
// This is explicitly mentioned here because of the unconventional
// variable naming scheme.
firstUserSession := loginUser(t, "user2")
firstUserToken := getTokenForLoggedInUser(t, firstUserSession, auth_model.AccessTokenScopeWriteUser)
// This user has the HidePronouns setting enabled.
// Check the fixture!
secondUserSession := loginUser(t, "user10")
secondUserToken := getTokenForLoggedInUser(t, secondUserSession, auth_model.AccessTokenScopeWriteUser)
adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true})
adminSession := loginUser(t, adminUser.Name)
@ -449,8 +457,10 @@ func TestUserPronouns(t *testing.T) {
t.Run("user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/api/v1/user").AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
// secondUserToken was chosen arbitrarily and should have no impact.
// See next comment.
req := NewRequest(t, "GET", "/api/v1/user").AddTokenAuth(secondUserToken)
resp := firstUserSession.MakeRequest(t, req, http.StatusOK)
// We check the raw JSON, because we want to test the response, not
// what it decodes into. Contents doesn't matter, we're testing the
@ -468,16 +478,22 @@ func TestUserPronouns(t *testing.T) {
// what it decodes into. Contents doesn't matter, we're testing the
// presence only.
assert.Contains(t, resp.Body.String(), `"pronouns":`)
req = NewRequest(t, "GET", "/api/v1/users/user10")
resp = MakeRequest(t, req, http.StatusOK)
// Same deal here.
assert.Contains(t, resp.Body.String(), `"pronouns":`)
})
t.Run("user/settings", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Set pronouns first
// Set pronouns first for user2
pronouns := "they/them"
req := NewRequestWithJSON(t, "PATCH", "/api/v1/user/settings", &api.UserSettingsOptions{
Pronouns: &pronouns,
}).AddTokenAuth(token)
}).AddTokenAuth(firstUserToken)
resp := MakeRequest(t, req, http.StatusOK)
// Verify the response
@ -486,7 +502,7 @@ func TestUserPronouns(t *testing.T) {
assert.Equal(t, pronouns, user.Pronouns)
// Verify retrieving the settings again
req = NewRequest(t, "GET", "/api/v1/user/settings").AddTokenAuth(token)
req = NewRequest(t, "GET", "/api/v1/user/settings").AddTokenAuth(firstUserToken)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &user)
@ -497,22 +513,40 @@ func TestUserPronouns(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Set the pronouns for user2
pronouns := "she/her"
pronouns := "he/him"
req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{
Pronouns: &pronouns,
}).AddTokenAuth(adminToken)
resp := MakeRequest(t, req, http.StatusOK)
// Verify the API response
var user *api.User
DecodeJSON(t, resp, &user)
assert.Equal(t, pronouns, user.Pronouns)
var user2 *api.User
DecodeJSON(t, resp, &user2)
assert.Equal(t, pronouns, user2.Pronouns)
// Verify via user2 too
req = NewRequest(t, "GET", "/api/v1/user").AddTokenAuth(token)
// Verify via user2
req = NewRequest(t, "GET", "/api/v1/user").AddTokenAuth(firstUserToken)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &user)
assert.Equal(t, pronouns, user.Pronouns)
DecodeJSON(t, resp, &user2)
assert.Equal(t, pronouns, user2.Pronouns) // TODO: This fails for some reason
// Set the pronouns for user10
pronouns = "he/him"
req = NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user10", &api.EditUserOption{
Pronouns: &pronouns,
}).AddTokenAuth(adminToken)
resp = MakeRequest(t, req, http.StatusOK)
// Verify the API response
var user10 *api.User
DecodeJSON(t, resp, &user10)
assert.Equal(t, pronouns, user10.Pronouns)
// Verify via user10
req = NewRequest(t, "GET", "/api/v1/user").AddTokenAuth(secondUserToken)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &user10)
assert.Equal(t, pronouns, user10.Pronouns)
})
})
@ -520,10 +554,10 @@ func TestUserPronouns(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Set the pronouns to a known state via the API
pronouns := "she/her"
pronouns := "they/them"
req := NewRequestWithJSON(t, "PATCH", "/api/v1/user/settings", &api.UserSettingsOptions{
Pronouns: &pronouns,
}).AddTokenAuth(token)
}).AddTokenAuth(firstUserToken)
MakeRequest(t, req, http.StatusOK)
t.Run("profile view", func(t *testing.T) {
@ -534,14 +568,14 @@ func TestUserPronouns(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
userNameAndPronouns := strings.TrimSpace(htmlDoc.Find(".profile-avatar-name .username").Text())
assert.Contains(t, userNameAndPronouns, pronouns)
assert.NotContains(t, userNameAndPronouns, pronouns)
})
t.Run("settings", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/user/settings")
resp := session.MakeRequest(t, req, http.StatusOK)
resp := firstUserSession.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
// Check that the field is present
@ -550,12 +584,12 @@ func TestUserPronouns(t *testing.T) {
assert.Equal(t, pronouns, pronounField)
// Check that updating the field works
newPronouns := "they/them"
newPronouns := "she/her"
req = NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": GetCSRF(t, session, "/user/settings"),
"_csrf": GetCSRF(t, firstUserSession, "/user/settings"),
"pronouns": newPronouns,
})
session.MakeRequest(t, req, http.StatusSeeOther)
firstUserSession.MakeRequest(t, req, http.StatusSeeOther)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
assert.Equal(t, newPronouns, user2.Pronouns)
@ -886,11 +920,27 @@ func TestUserActivate(t *testing.T) {
assert.False(t, authToken.IsExpired())
assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator))
req = NewRequest(t, "POST", "/user/activate?code="+code)
session.MakeRequest(t, req, http.StatusOK)
t.Run("No password", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
unittest.AssertNotExistsBean(t, &auth_model.AuthorizationToken{ID: authToken.ID})
unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "doesnotexist", IsActive: true})
req = NewRequest(t, "POST", "/user/activate?code="+code)
session.MakeRequest(t, req, http.StatusOK)
unittest.AssertExistsIf(t, true, &auth_model.AuthorizationToken{ID: authToken.ID})
unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "doesnotexist"}, "is_active = false")
})
t.Run("With password", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req = NewRequestWithValues(t, "POST", "/user/activate?code="+code, map[string]string{
"password": "examplePassword!1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
unittest.AssertExistsIf(t, false, &auth_model.AuthorizationToken{ID: authToken.ID})
unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "doesnotexist"}, "is_active = true")
})
}
func TestUserPasswordReset(t *testing.T) {

View file

@ -132,16 +132,16 @@
}
},
"node_modules/@octokit/core": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.3.tgz",
"integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz",
"integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^5.0.0",
"@octokit/graphql": "^8.1.2",
"@octokit/request": "^9.1.4",
"@octokit/request-error": "^6.1.6",
"@octokit/request": "^9.2.1",
"@octokit/request-error": "^6.1.7",
"@octokit/types": "^13.6.2",
"before-after-hook": "^3.0.2",
"universal-user-agent": "^7.0.0"
@ -161,9 +161,9 @@
}
},
"node_modules/@octokit/core/node_modules/@octokit/endpoint": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz",
"integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==",
"version": "10.1.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz",
"integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -182,14 +182,14 @@
"peer": true
},
"node_modules/@octokit/core/node_modules/@octokit/request": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.0.tgz",
"integrity": "sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==",
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz",
"integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/endpoint": "^10.0.0",
"@octokit/request-error": "^6.0.1",
"@octokit/endpoint": "^10.1.3",
"@octokit/request-error": "^6.1.7",
"@octokit/types": "^13.6.2",
"fast-content-type-parse": "^2.0.0",
"universal-user-agent": "^7.0.2"
@ -199,9 +199,9 @@
}
},
"node_modules/@octokit/core/node_modules/@octokit/request-error": {
"version": "6.1.6",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz",
"integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==",
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
"integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -253,13 +253,13 @@
"license": "ISC"
},
"node_modules/@octokit/graphql": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.0.tgz",
"integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==",
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.1.tgz",
"integrity": "sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/request": "^9.1.4",
"@octokit/request": "^9.2.2",
"@octokit/types": "^13.8.0",
"universal-user-agent": "^7.0.0"
},
@ -268,9 +268,9 @@
}
},
"node_modules/@octokit/graphql/node_modules/@octokit/endpoint": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz",
"integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==",
"version": "10.1.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz",
"integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -289,14 +289,14 @@
"peer": true
},
"node_modules/@octokit/graphql/node_modules/@octokit/request": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.0.tgz",
"integrity": "sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==",
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz",
"integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/endpoint": "^10.0.0",
"@octokit/request-error": "^6.0.1",
"@octokit/endpoint": "^10.1.3",
"@octokit/request-error": "^6.1.7",
"@octokit/types": "^13.6.2",
"fast-content-type-parse": "^2.0.0",
"universal-user-agent": "^7.0.2"
@ -306,9 +306,9 @@
}
},
"node_modules/@octokit/graphql/node_modules/@octokit/request-error": {
"version": "6.1.6",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz",
"integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==",
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
"integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -477,6 +477,16 @@
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@types/expect": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz",
@ -484,9 +494,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.13.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
"integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
"version": "22.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
"integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
@ -1201,9 +1211,9 @@
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@ -1239,9 +1249,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001699",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz",
"integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==",
"version": "1.0.30001700",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz",
"integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==",
"funding": [
{
"type": "opencollective",
@ -1995,9 +2005,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.96",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz",
"integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==",
"version": "1.5.101",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz",
"integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@ -4967,18 +4977,18 @@
}
},
"node_modules/jackspeak": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
"integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jquery": {
@ -4988,14 +4998,14 @@
"license": "MIT"
},
"node_modules/js-beautify": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.2.tgz",
"integrity": "sha512-mcG6CHJxxih+EFAbd5NEBwrosIs6MoJmiNLFYN6kj5SeJMf7n29Ii/H4lt6zGTvmdB9AApuj5cs4zydjuLeqjw==",
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.3.tgz",
"integrity": "sha512-rKKGuyTxGNlyN4EQKWzNndzXpi0bOl8Gl8YQAW1as/oMz0XhD6sHJO1hTvoBDOSzKuJb9WkwoAb34FfdkKMv2A==",
"license": "MIT",
"dependencies": {
"config-chain": "^1.1.13",
"editorconfig": "^1.0.4",
"glob": "^11.0.0",
"glob": "^10.4.2",
"js-cookie": "^3.0.5",
"nopt": "^8.0.0"
},
@ -5018,38 +5028,35 @@
}
},
"node_modules/js-beautify/node_modules/glob": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^4.0.1",
"minimatch": "^10.0.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^2.0.0"
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/js-beautify/node_modules/minimatch": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": "20 || >=22"
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@ -5481,13 +5488,10 @@
}
},
"node_modules/lru-cache": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
"license": "ISC",
"engines": {
"node": "20 || >=22"
}
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/macos-release": {
"version": "2.5.1",
@ -6467,16 +6471,16 @@
}
},
"node_modules/path-scurry": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": "20 || >=22"
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"

View file

@ -30,7 +30,6 @@ const sfc = {
intervalID: null,
currentJobStepsStates: [],
artifacts: [],
onHoverRerunIndex: -1,
menuVisible: false,
isFullScreen: false,
timeVisible: {
@ -457,13 +456,13 @@ export function initRepositoryActionView() {
<div class="action-view-left">
<div class="job-group-section">
<div class="job-brief-list">
<a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
<a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id">
<div class="job-brief-item-left">
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
<span class="job-brief-name tw-mx-2 gt-ellipsis">{{ job.name }}</span>
</div>
<span class="job-brief-item-right">
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-3 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun"/>
<span class="step-summary-duration">{{ job.duration }}</span>
</span>
</a>