diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index cc7695726bb..c49e1267a7a 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -181,10 +181,11 @@ func (u *User) UnmarshalJSON(data []byte) error { return nil } -// Repository https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version +// Repository https://docs.npmjs.com/cli/v11/configuring-npm/package-json#repository type Repository struct { - Type string `json:"type"` - URL string `json:"url"` + Type string `json:"type"` + URL string `json:"url"` + Directory string `json:"directory,omitempty"` } // PackageAttachment https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index 40c50de91f9..7474d6c9f4b 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -28,8 +28,9 @@ func TestParsePackage(t *testing.T) { data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA" integrity := "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==" repository := Repository{ - Type: "gitea", - URL: "http://localhost:3000/gitea/test.git", + Type: "gitea", + URL: "http://localhost:3000/gitea/test.git", + Directory: "packages/test-package", } t.Run("InvalidUpload", func(t *testing.T) { @@ -298,6 +299,7 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, "1.2.0", p.Metadata.Dependencies["package"]) assert.Equal(t, repository.Type, p.Metadata.Repository.Type) assert.Equal(t, repository.URL, p.Metadata.Repository.URL) + assert.Equal(t, repository.Directory, p.Metadata.Repository.Directory) }) t.Run("ValidLicenseMap", func(t *testing.T) { diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 9d05bb2a0b6..b78196bd5c9 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/emoji" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/htmlutil" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -223,6 +224,25 @@ func (ut *RenderUtils) MarkdownToHtml(input string) template.HTML { //nolint:rev return output } +// RenderPackageMarkdown renders package page Markdown so relative links resolve against the +// linked repository's default branch instead of the site root, falling back to plain rendering +// when there is no linked repository. pkgTreePath optionally roots links in a subdirectory +// (e.g. npm's repository.directory for monorepo packages). +func (ut *RenderUtils) RenderPackageMarkdown(input string, linkedRepo *repo.Repository, pkgTreePath ...string) template.HTML { + if linkedRepo == nil { + return `
` + ut.MarkdownToHtml(input) + `
` + } + rctx := renderhelper.NewRenderContextRepoFile(ut.ctx, linkedRepo, renderhelper.RepoFileOptions{ + CurrentRefSubURL: git.RefNameFromBranch(linkedRepo.DefaultBranch).RefWebLinkPath(), + CurrentTreePath: util.OptionalArg(pkgTreePath), + }) + output, err := markdown.RenderString(rctx, input) + if err != nil { + log.Error("RenderString: %v", err) + } + return `
` + output + `
` +} + func (ut *RenderUtils) RenderLabels(labels []*issues_model.Label, repoLink string, issue *issues_model.Issue) template.HTML { isPullRequest := issue != nil && issue.IsPull baseLink := fmt.Sprintf("%s/%s", repoLink, util.Iif(isPullRequest, "pulls", "issues")) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index de7768e91c3..61dcb4937f1 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -194,6 +194,38 @@ space

assert.Equal(t, expected, string(newTestRenderUtils(t).MarkdownToHtml(testInput()))) } +func TestRenderPackageMarkdown(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)() + mockRepo := &repo.Repository{ + ID: 1, OwnerName: "user13", Name: "repo11", DefaultBranch: "main", + Owner: &user_model.User{ID: 13, Name: "user13"}, + Units: []*repo.RepoUnit{}, + } + ut := newTestRenderUtils(t) + + t.Run("LinkedRepoWithDirectory", func(t *testing.T) { + rendered := ut.RenderPackageMarkdown("[docs](docs/getting-started.md)\n![logo](logo.png)", mockRepo, "pkg-subdir") + expected := `

docs +logo

+
` + assert.Equal(t, expected, strings.TrimSpace(string(rendered))) + }) + + t.Run("LinkedRepoWithEmptyDirectory", func(t *testing.T) { + rendered := ut.RenderPackageMarkdown("[docs](docs/getting-started.md)", mockRepo, "") + expected := `

docs

+
` + assert.Equal(t, expected, strings.TrimSpace(string(rendered))) + }) + + t.Run("UnlinkedRepo", func(t *testing.T) { + rendered := ut.RenderPackageMarkdown("[docs](docs/getting-started.md)", nil, "pkg-subdir") + expected := `

docs

+
` + assert.Equal(t, expected, strings.TrimSpace(string(rendered))) + }) +} + func TestRenderLabels(t *testing.T) { ut := newTestRenderUtils(t) label := &issues.Label{ID: 123, Name: "label-name", Color: "label-color"} diff --git a/templates/package/content/cargo.tmpl b/templates/package/content/cargo.tmpl index ebb23e86597..5c04ab8aae9 100644 --- a/templates/package/content/cargo.tmpl +++ b/templates/package/content/cargo.tmpl @@ -27,7 +27,7 @@ git-fetch-with-cli = true {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}

{{ctx.Locale.Tr "packages.about"}}

{{if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}}
{{end}} - {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Readme}}
{{end}} + {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Readme .PackageDescriptor.Repository}}
{{end}} {{end}} {{if .PackageDescriptor.Metadata.Dependencies}} diff --git a/templates/package/content/chef.tmpl b/templates/package/content/chef.tmpl index d1c17d36a49..0912aff792c 100644 --- a/templates/package/content/chef.tmpl +++ b/templates/package/content/chef.tmpl @@ -20,7 +20,7 @@

{{ctx.Locale.Tr "packages.about"}}

{{if .PackageDescriptor.Metadata.Description}}

{{.PackageDescriptor.Metadata.Description}}

{{end}} - {{if .PackageDescriptor.Metadata.LongDescription}}{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}{{end}} + {{if .PackageDescriptor.Metadata.LongDescription}}{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.LongDescription .PackageDescriptor.Repository}}{{end}}
{{end}} diff --git a/templates/package/content/composer.tmpl b/templates/package/content/composer.tmpl index 2e8cfb77ebc..5bbc19e9f25 100644 --- a/templates/package/content/composer.tmpl +++ b/templates/package/content/composer.tmpl @@ -25,7 +25,7 @@ {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Comments}}

{{ctx.Locale.Tr "packages.about"}}

{{if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}}
{{end}} - {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Readme}}
{{end}} + {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Readme .PackageDescriptor.Repository}}
{{end}} {{if .PackageDescriptor.Metadata.Comments}}
{{StringUtils.Join .PackageDescriptor.Metadata.Comments " "}}
{{end}} {{end}} diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl index 28bcf45ee87..89f4c940081 100644 --- a/templates/package/content/npm.tmpl +++ b/templates/package/content/npm.tmpl @@ -22,15 +22,8 @@ {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}

{{ctx.Locale.Tr "packages.about"}}

-
- {{if .PackageDescriptor.Metadata.Readme}} -
- {{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Readme}} -
- {{else if .PackageDescriptor.Metadata.Description}} - {{.PackageDescriptor.Metadata.Description}} - {{end}} -
+ {{if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}}
{{end}} + {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Readme .PackageDescriptor.Repository .PackageDescriptor.Metadata.Repository.Directory}}
{{end}} {{end}} {{if or .PackageDescriptor.Metadata.Dependencies .PackageDescriptor.Metadata.DevelopmentDependencies .PackageDescriptor.Metadata.PeerDependencies .PackageDescriptor.Metadata.OptionalDependencies}} diff --git a/templates/package/content/nuget.tmpl b/templates/package/content/nuget.tmpl index 7f874044cc1..fc9a715a28e 100644 --- a/templates/package/content/nuget.tmpl +++ b/templates/package/content/nuget.tmpl @@ -18,9 +18,9 @@ {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}

{{ctx.Locale.Tr "packages.about"}}

- {{if .PackageDescriptor.Metadata.Description}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Description}}
{{end}} - {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Readme}}
{{end}} - {{if .PackageDescriptor.Metadata.ReleaseNotes}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.ReleaseNotes}}
{{end}} + {{if .PackageDescriptor.Metadata.Description}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Description .PackageDescriptor.Repository}}
{{end}} + {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Readme .PackageDescriptor.Repository}}
{{end}} + {{if .PackageDescriptor.Metadata.ReleaseNotes}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Repository}}
{{end}} {{end}} {{if .PackageDescriptor.Metadata.Dependencies}} diff --git a/templates/package/content/pub.tmpl b/templates/package/content/pub.tmpl index 9eefcf71625..5dafa2db47e 100644 --- a/templates/package/content/pub.tmpl +++ b/templates/package/content/pub.tmpl @@ -14,6 +14,6 @@ {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}

{{ctx.Locale.Tr "packages.about"}}

{{if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}}
{{end}} - {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Readme}}
{{end}} + {{if .PackageDescriptor.Metadata.Readme}}
{{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Readme .PackageDescriptor.Repository}}
{{end}} {{end}} {{end}} diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl index 4afdc1b72a7..c570e60e6bf 100644 --- a/templates/package/content/pypi.tmpl +++ b/templates/package/content/pypi.tmpl @@ -16,9 +16,9 @@

{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}

{{if .PackageDescriptor.Metadata.LongDescription}} - {{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.LongDescription}} + {{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.LongDescription .PackageDescriptor.Repository}} {{else if .PackageDescriptor.Metadata.Description}} - {{ctx.RenderUtils.MarkdownToHtml .PackageDescriptor.Metadata.Description}} + {{ctx.RenderUtils.RenderPackageMarkdown .PackageDescriptor.Metadata.Description .PackageDescriptor.Repository}} {{end}}
{{end}} diff --git a/tests/integration/api_packages_npm_test.go b/tests/integration/api_packages_npm_test.go index 92f9b61081b..8fb892cc866 100644 --- a/tests/integration/api_packages_npm_test.go +++ b/tests/integration/api_packages_npm_test.go @@ -13,6 +13,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/packages/npm" @@ -20,6 +21,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestPackageNpm(t *testing.T) { @@ -39,6 +41,7 @@ func TestPackageNpm(t *testing.T) { packageBinPath := "./cli.sh" repoType := "gitea" repoURL := "http://localhost:3000/gitea/test.git" + repoDirectory := "package-subdir" data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA" @@ -67,8 +70,10 @@ func TestPackageNpm(t *testing.T) { }, "repository": { "type": "` + repoType + `", - "url": "` + repoURL + `" + "url": "` + repoURL + `", + "directory": "` + repoDirectory + `" }, + "readme": "[docs](docs/usage.md)\n![logo](logo.png)", "peerDependencies": { "tea": "2.x", "soy-milk": "1.2" @@ -282,6 +287,28 @@ func TestPackageNpm(t *testing.T) { } }) + t.Run("WebViewReadmeRepoLinks", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeNpm) + assert.NoError(t, err) + require.Len(t, pvs, 1) + + // link the package to a repository so README relative links resolve against + // repository files instead of the site root + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.NoError(t, packages.SetRepositoryLink(t.Context(), pvs[0].PackageID, repo.ID)) + + req := NewRequest(t, "GET", fmt.Sprintf("/%s/-/packages/npm/%s/%s", user.Name, url.PathEscape(packageName), packageVersion)). + AddBasicAuth(user.Name) + resp := MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + rendered, _ := doc.Find(".markup.markdown").Html() + assert.Equal(t, `

docs +logo

+`, rendered) + }) + t.Run("Delete", func(t *testing.T) { defer tests.PrintCurrentTest(t)()