Skip to content

Commit

Permalink
Merge remote-tracking branch 'giteaofficial/main'
Browse files Browse the repository at this point in the history
* giteaofficial/main:
  Fix line number width in code preview (go-gitea#31307)
  Delete legacy cookie before setting new cookie (go-gitea#31306)
  [skip ci] Updated translations via Crowdin
  Use `querySelector` over alternative DOM methods (go-gitea#31280)
  Remove jQuery `.text()` (go-gitea#30506)
  [skip ci] Updated translations via Crowdin
  Remove sub-path from container registry realm (go-gitea#31293)
  Fix some URLs whose sub-path is missing (go-gitea#31289)
  • Loading branch information
zjjhot committed Jun 11, 2024
2 parents 77ff67f + 397930d commit 17f77f3
Show file tree
Hide file tree
Showing 64 changed files with 476 additions and 365 deletions.
6 changes: 3 additions & 3 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ rules:
jquery/no-sizzle: [2]
jquery/no-slide: [2]
jquery/no-submit: [2]
jquery/no-text: [0]
jquery/no-text: [2]
jquery/no-toggle: [2]
jquery/no-trigger: [0]
jquery/no-trim: [2]
Expand Down Expand Up @@ -477,7 +477,7 @@ rules:
no-jquery/no-slide: [2]
no-jquery/no-sub: [2]
no-jquery/no-support: [2]
no-jquery/no-text: [0]
no-jquery/no-text: [2]
no-jquery/no-trigger: [0]
no-jquery/no-trim: [2]
no-jquery/no-type: [2]
Expand Down Expand Up @@ -798,7 +798,7 @@ rules:
unicorn/prefer-object-has-own: [0]
unicorn/prefer-optional-catch-binding: [2]
unicorn/prefer-prototype-methods: [0]
unicorn/prefer-query-selector: [0]
unicorn/prefer-query-selector: [2]
unicorn/prefer-reflect-apply: [0]
unicorn/prefer-regexp-test: [2]
unicorn/prefer-set-has: [0]
Expand Down
5 changes: 0 additions & 5 deletions modules/setting/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package setting
import (
"fmt"
"math"
"net/url"
"os"
"path/filepath"

Expand All @@ -19,7 +18,6 @@ var (
Storage *Storage
Enabled bool
ChunkedUploadPath string
RegistryHost string

LimitTotalOwnerCount int64
LimitTotalOwnerSize int64
Expand Down Expand Up @@ -66,9 +64,6 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
return err
}

appURL, _ := url.Parse(AppURL)
Packages.RegistryHost = appURL.Host

Packages.ChunkedUploadPath = filepath.ToSlash(sec.Key("CHUNKED_UPLOAD_PATH").MustString("tmp/package-upload"))
if !filepath.IsAbs(Packages.ChunkedUploadPath) {
Packages.ChunkedUploadPath = filepath.ToSlash(filepath.Join(AppDataPath, Packages.ChunkedUploadPath))
Expand Down
6 changes: 4 additions & 2 deletions modules/test/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ func IsNormalPageCompleted(s string) bool {
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
}

func MockVariableValue[T any](p *T, v T) (reset func()) {
func MockVariableValue[T any](p *T, v ...T) (reset func()) {
old := *p
*p = v
if len(v) > 0 {
*p = v[0]
}
return func() { *p = old }
}
8 changes: 4 additions & 4 deletions modules/web/middleware/cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func GetSiteCookie(req *http.Request, name string) string {

// SetSiteCookie returns given cookie value from request header.
func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
deleteLegacySiteCookie(resp, name)
cookie := &http.Cookie{
Name: name,
Value: url.QueryEscape(value),
Expand All @@ -46,10 +50,6 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
SameSite: setting.SessionConfig.SameSite,
}
resp.Header().Add("Set-Cookie", cookie.String())
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
deleteLegacySiteCookie(resp, name)
}

// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
Expand Down
122 changes: 116 additions & 6 deletions options/locale/locale_fr-FR.ini

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions options/locale/locale_ja-JP.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,7 @@ commitstatus.success=成功
ext_issues=外部イシューへのアクセス
ext_issues.desc=外部のイシュートラッカーへのリンク。

projects.desc=プロジェクトでイシューとプルリクエストを管理します。
projects.description=説明 (オプション)
projects.description_placeholder=説明
projects.create=プロジェクトを作成
Expand Down Expand Up @@ -1552,7 +1553,9 @@ issues.no_content=説明はありません。
issues.close=イシューをクローズ
issues.comment_pull_merged_at=がコミット %[1]s を %[2]s にマージ %[3]s
issues.comment_manually_pull_merged_at=がコミット %[1]s を %[2]s に手動マージ %[3]s
issues.close_comment_issue=コメントしてクローズ
issues.reopen_issue=再オープンする
issues.reopen_comment_issue=コメントして再オープン
issues.create_comment=コメントする
issues.comment.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、コメントの作成や編集はできません。
issues.closed_at=`がイシューをクローズ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
Expand Down Expand Up @@ -3412,6 +3415,7 @@ error.unit_not_allowed=このセクションへのアクセスが許可されて
title=パッケージ
desc=リポジトリ パッケージを管理します。
empty=パッケージはまだありません。
no_metadata=メタデータがありません。
empty.documentation=パッケージレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
empty.repo=パッケージはアップロード済みで、ここに表示されていないですか? <a href="%[1]s">パッケージ設定</a>を開いて、パッケージをこのリポジトリにリンクしてください。
registry.documentation=%sレジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a> を参照してください。
Expand Down Expand Up @@ -3634,6 +3638,7 @@ runs.pushed_by=pushed by
runs.invalid_workflow_helper=ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s
runs.no_matching_online_runner_helper=ラベルに一致するオンラインのランナーが見つかりません: %s
runs.no_job_without_needs=ワークフローには依存関係のないジョブが少なくとも1つ含まれている必要があります。
runs.no_job=ワークフローには少なくとも1つのジョブが含まれている必要があります
runs.actor=アクター
runs.status=ステータス
runs.actors_no_select=すべてのアクター
Expand Down
6 changes: 3 additions & 3 deletions routers/api/packages/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
}

func apiUnauthorizedError(ctx *context.Context) {
// TODO: it doesn't seem quite right but it doesn't really cause problem at the moment.
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed, ideally.
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+httplib.GuessCurrentAppURL(ctx)+`v2/token",service="container_registry",scope="*"`)
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed
realmURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), setting.AppSubURL+"/") + "/v2/token"
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+realmURL+`",service="container_registry",scope="*"`)
apiErrorDefined(ctx, errUnauthorized)
}

Expand Down
8 changes: 7 additions & 1 deletion routers/web/user/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package user

import (
"net/http"
"net/url"

"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
Expand All @@ -15,6 +16,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
Expand Down Expand Up @@ -178,7 +180,11 @@ func ViewPackageVersion(ctx *context.Context) {

switch pd.Package.Type {
case packages_model.TypeContainer:
ctx.Data["RegistryHost"] = setting.Packages.RegistryHost
registryAppURL, err := url.Parse(httplib.GuessCurrentAppURL(ctx))
if err != nil {
registryAppURL, _ = url.Parse(setting.AppURL)
}
ctx.Data["RegistryHost"] = registryAppURL.Host
case packages_model.TypeAlpine:
branches := make(container.Set[string])
repositories := make(container.Set[string])
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/packages/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{{ctx.Locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
{{ctx.Locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
<div class="ui right">
<form method="post" action="/admin/packages/cleanup">
<form method="post" action="{{AppSubUrl}}/admin/packages/cleanup">
{{.CsrfTokenHtml}}
<button class="ui primary tiny button">{{ctx.Locale.Tr "admin.packages.cleanup"}}</button>
</form>
Expand Down
2 changes: 1 addition & 1 deletion templates/devtest/fetch-action.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<div><label><input name="check" type="checkbox"> check</label></div>
<div><button name="btn">submit post</button></div>
</form>
<form method="post" action="/no-such-uri" class="form-fetch-action">
<form method="post" action="no-such-uri" class="form-fetch-action">
<div class="tw-py-8">bad action url</div>
<div><button name="btn">submit test</button></div>
</form>
Expand Down
6 changes: 3 additions & 3 deletions templates/repo/editor/commit_form.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<div class="quick-pull-choice js-quick-pull-choice">
<div class="field">
<div class="ui radio checkbox {{if not .CanCommitToBranch.CanCommitToBranch}}disabled{{end}}">
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" button_text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
<label>
{{svg "octicon-git-commit"}}
{{ctx.Locale.Tr "repo.editor.commit_directly_to_this_branch" .BranchName}}
Expand All @@ -43,9 +43,9 @@
<div class="field">
<div class="ui radio checkbox">
{{if .CanCreatePullRequest}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" button_text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{else}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" button_text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{end}}
<label>
{{svg "octicon-git-pull-request"}}
Expand Down
8 changes: 4 additions & 4 deletions templates/repo/settings/collaboration.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
<div class="flex-item-trailing">
<div class="flex-text-block">
{{svg "octicon-shield-lock"}}
<div class="ui inline dropdown access-mode" data-url="{{$.Link}}/access_mode" data-uid="{{.ID}}" data-last-value="{{printf "%d" .Collaboration.Mode}}">
<div class="ui dropdown custom access-mode" data-url="{{$.Link}}/access_mode" data-uid="{{.ID}}" data-last-value="{{.Collaboration.Mode}}">
<div class="text">{{if eq .Collaboration.Mode 1}}{{ctx.Locale.Tr "repo.settings.collaboration.read"}}{{else if eq .Collaboration.Mode 2}}{{ctx.Locale.Tr "repo.settings.collaboration.write"}}{{else if eq .Collaboration.Mode 3}}{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}{{else}}{{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}{{end}}</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}" data-value="3">{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}</div>
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.write"}}" data-value="2">{{ctx.Locale.Tr "repo.settings.collaboration.write"}}</div>
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.read"}}" data-value="1">{{ctx.Locale.Tr "repo.settings.collaboration.read"}}</div>
<div class="item" data-value="3">{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}</div>
<div class="item" data-value="2">{{ctx.Locale.Tr "repo.settings.collaboration.write"}}</div>
<div class="item" data-value="1">{{ctx.Locale.Tr "repo.settings.collaboration.read"}}</div>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/user/settings/applications.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
{{ctx.Locale.Tr "settings.select_permissions"}}
</summary>
<p class="activity meta">
<i>{{ctx.Locale.Tr "settings.access_token_desc" (`href="/api/swagger" target="_blank"`|SafeHTML) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}}</i>
<i>{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}}</i>
</p>
<div class="scoped-access-token-mount">
<scoped-access-token-selector
Expand Down
12 changes: 9 additions & 3 deletions tests/integration/api_packages_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ func TestPackageContainer(t *testing.T) {
Token string `json:"token"`
}

authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`}
defaultAuthenticateValues := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`}

t.Run("Anonymous", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
resp := MakeRequest(t, req, http.StatusUnauthorized)

assert.ElementsMatch(t, authenticate, resp.Header().Values("WWW-Authenticate"))
assert.ElementsMatch(t, defaultAuthenticateValues, resp.Header().Values("WWW-Authenticate"))

req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
resp = MakeRequest(t, req, http.StatusOK)
Expand All @@ -115,6 +115,12 @@ func TestPackageContainer(t *testing.T) {

req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
MakeRequest(t, req, http.StatusUnauthorized)

defer test.MockVariableValue(&setting.AppURL, "https://domain:8443/sub-path/")()
defer test.MockVariableValue(&setting.AppSubURL, "/sub-path")()
req = NewRequest(t, "GET", "/v2")
resp = MakeRequest(t, req, http.StatusUnauthorized)
assert.Equal(t, `Bearer realm="https://domain:8443/v2/token",service="container_registry",scope="*"`, resp.Header().Get("WWW-Authenticate"))
})

t.Run("User", func(t *testing.T) {
Expand All @@ -123,7 +129,7 @@ func TestPackageContainer(t *testing.T) {
req := NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
resp := MakeRequest(t, req, http.StatusUnauthorized)

assert.ElementsMatch(t, authenticate, resp.Header().Values("WWW-Authenticate"))
assert.ElementsMatch(t, defaultAuthenticateValues, resp.Header().Values("WWW-Authenticate"))

req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL)).
AddBasicAuth(user.Name)
Expand Down
2 changes: 0 additions & 2 deletions web_src/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,6 @@ overflow-menu .ui.label {
padding: 0 8px;
text-align: right !important;
color: var(--color-text-light-2);
width: 1%;
font-family: var(--fonts-monospace);
}

.lines-num span.bottom-line::after {
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/components/DashboardRepoList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const sfc = {
},
mounted() {
const el = document.getElementById('dashboard-repo-list');
const el = document.querySelector('#dashboard-repo-list');
this.changeReposFilter(this.reposFilter);
$(el).find('.dropdown').dropdown();
nextTick(() => {
Expand Down Expand Up @@ -330,7 +330,7 @@ const sfc = {
};
export function initDashboardRepoList() {
const el = document.getElementById('dashboard-repo-list');
const el = document.querySelector('#dashboard-repo-list');
if (el) {
createApp(sfc).mount(el);
}
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/components/DiffCommitSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {GET} from '../modules/fetch.js';
export default {
components: {SvgIcon},
data: () => {
const el = document.getElementById('diff-commit-select');
const el = document.querySelector('#diff-commit-select');
return {
menuVisible: false,
isLoading: false,
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/components/DiffFileList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export default {
return {store: diffTreeStore()};
},
mounted() {
document.getElementById('show-file-list-btn').addEventListener('click', this.toggleFileList);
document.querySelector('#show-file-list-btn').addEventListener('click', this.toggleFileList);
},
unmounted() {
document.getElementById('show-file-list-btn').removeEventListener('click', this.toggleFileList);
document.querySelector('#show-file-list-btn').removeEventListener('click', this.toggleFileList);
},
methods: {
toggleFileList() {
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/components/DiffFileTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
updateState(visible) {
const btn = document.querySelector('.diff-toggle-file-tree-button');
const [toShow, toHide] = btn.querySelectorAll('.icon');
const tree = document.getElementById('diff-file-tree');
const tree = document.querySelector('#diff-file-tree');
const newTooltip = btn.getAttribute(visible ? 'data-hide-text' : 'data-show-text');
btn.setAttribute('data-tooltip-content', newTooltip);
toggleElem(tree, visible);
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/components/RepoActionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ const sfc = {
export default sfc;
export function initRepositoryActionView() {
const el = document.getElementById('repo-action-view');
const el = document.querySelector('#repo-action-view');
if (!el) return;
// TODO: the parent element's full height doesn't work well now,
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/components/RepoActivityTopAuthors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const sfc = {
};
export function initRepoActivityTopAuthorsChart() {
const el = document.getElementById('repo-activity-top-authors-chart');
const el = document.querySelector('#repo-activity-top-authors-chart');
if (el) {
createApp(sfc).mount(el);
}
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/components/RepoBranchTagSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const sfc = {
this.isViewBranch = false;
this.$refs.dropdownRefName.textContent = item.name;
if (this.setAction) {
document.getElementById(this.branchForm)?.setAttribute('action', url);
document.querySelector(`#${this.branchForm}`)?.setAttribute('action', url);
} else {
$(`#${this.branchForm} input[name="refURL"]`).val(url);
}
Expand Down
12 changes: 6 additions & 6 deletions web_src/js/components/ScopedAccessTokenSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,25 @@ const sfc = {
},
mounted() {
document.getElementById('scoped-access-submit').addEventListener('click', this.onClickSubmit);
document.querySelector('#scoped-access-submit').addEventListener('click', this.onClickSubmit);
},
unmounted() {
document.getElementById('scoped-access-submit').removeEventListener('click', this.onClickSubmit);
document.querySelector('#scoped-access-submit').removeEventListener('click', this.onClickSubmit);
},
methods: {
onClickSubmit(e) {
e.preventDefault();
const warningEl = document.getElementById('scoped-access-warning');
const warningEl = document.querySelector('#scoped-access-warning');
// check that at least one scope has been selected
for (const el of document.getElementsByClassName('access-token-select')) {
for (const el of document.querySelectorAll('.access-token-select')) {
if (el.value) {
// Hide the error if it was visible from previous attempt.
hideElem(warningEl);
// Submit the form.
document.getElementById('scoped-access-form').submit();
document.querySelector('#scoped-access-form').submit();
// Don't show the warning.
return;
}
Expand All @@ -78,7 +78,7 @@ export default sfc;
* Initialize category toggle sections
*/
export function initScopedAccessTokenCategories() {
for (const el of document.getElementsByClassName('scoped-access-token-mount')) {
for (const el of document.querySelectorAll('.scoped-access-token-mount')) {
createApp({})
.component('scoped-access-token-selector', sfc)
.mount(el);
Expand Down
Loading

0 comments on commit 17f77f3

Please sign in to comment.