From 6da802744630e98d341a4b958103464de28a7152 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 27 Apr 2026 19:33:10 +0200 Subject: [PATCH 01/32] Fix inconsistent disabled styling on logged-out repo header buttons (#37406) Make the watch, star, and fork buttons in the repo header consistent for logged-out users: - Apply the same look to all three buttons (number labels included), instead of only the action button being grayed. - Clicking any of them while logged out now leads to the login page (with a redirect back) instead of being inert. - Split the per-button markup out of `header.tmpl` into a dedicated `templates/repo/header/` folder (`fork.tmpl`, `star.tmpl`, `watch.tmpl`). --------- Co-authored-by: Claude (Opus 4.7) Co-authored-by: wxiaoguang --- routers/web/repo/star.go | 2 +- routers/web/repo/watch.go | 2 +- templates/repo/header.tmpl | 53 ++----------------- templates/repo/header/fork.tmpl | 47 ++++++++++++++++ .../{star_unstar.tmpl => header/star.tmpl} | 6 +-- .../{watch_unwatch.tmpl => header/watch.tmpl} | 8 +-- 6 files changed, 59 insertions(+), 59 deletions(-) create mode 100644 templates/repo/header/fork.tmpl rename templates/repo/{star_unstar.tmpl => header/star.tmpl} (84%) rename templates/repo/{watch_unwatch.tmpl => header/watch.tmpl} (76%) diff --git a/routers/web/repo/star.go b/routers/web/repo/star.go index 8cfbfefdf1..c93c877d63 100644 --- a/routers/web/repo/star.go +++ b/routers/web/repo/star.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/services/context" ) -const tplStarUnstar templates.TplName = "repo/star_unstar" +const tplStarUnstar templates.TplName = "repo/header/star" func ActionStar(ctx *context.Context) { err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, ctx.PathParam("action") == "star") diff --git a/routers/web/repo/watch.go b/routers/web/repo/watch.go index a7fbfc168b..616e1ee89c 100644 --- a/routers/web/repo/watch.go +++ b/routers/web/repo/watch.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/services/context" ) -const tplWatchUnwatch templates.TplName = "repo/watch_unwatch" +const tplWatchUnwatch templates.TplName = "repo/header/watch" func ActionWatch(ctx *context.Context) { err := repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, ctx.PathParam("action") == "watch") diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 9ed74d50bc..9fbe6a9a11 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -57,59 +57,12 @@ {{svg "octicon-rss" 16}} {{end}} - {{template "repo/watch_unwatch" $}} + {{template "repo/header/watch" $}} {{if not $.DisableStars}} - {{template "repo/star_unstar" $}} + {{template "repo/header/star" $}} {{end}} {{if and (not .IsEmpty) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}} - - + {{template "repo/header/fork" $}} {{end}} {{end}} diff --git a/templates/repo/header/fork.tmpl b/templates/repo/header/fork.tmpl new file mode 100644 index 0000000000..e14851f068 --- /dev/null +++ b/templates/repo/header/fork.tmpl @@ -0,0 +1,47 @@ +{{$canNotForkOwn := and $.IsSigned (not $.CanSignedUserFork) (not $.UserAndOrgForks)}} + +{{if $.ShowForkModal}} + +{{end}} diff --git a/templates/repo/star_unstar.tmpl b/templates/repo/header/star.tmpl similarity index 84% rename from templates/repo/star_unstar.tmpl rename to templates/repo/header/star.tmpl index 7e4c61aa28..d1762bc004 100644 --- a/templates/repo/star_unstar.tmpl +++ b/templates/repo/header/star.tmpl @@ -1,18 +1,18 @@
{{$buttonText := ctx.Locale.Tr "repo.star"}} {{if $.IsStaringRepo}}{{$buttonText = ctx.Locale.Tr "repo.unstar"}}{{end}} - + {{CountFmt .Repository.NumStars}} diff --git a/templates/repo/watch_unwatch.tmpl b/templates/repo/header/watch.tmpl similarity index 76% rename from templates/repo/watch_unwatch.tmpl rename to templates/repo/header/watch.tmpl index 1d6daac252..a00fd01c80 100644 --- a/templates/repo/watch_unwatch.tmpl +++ b/templates/repo/header/watch.tmpl @@ -1,19 +1,19 @@
{{$buttonText := ctx.Locale.Tr "repo.watch"}} {{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}} - - + + {{CountFmt .Repository.NumWatches}}
From 89d358d8a7b386f4b8a4acbdec04523d17d59b5f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 28 Apr 2026 07:08:50 +0800 Subject: [PATCH 02/32] Fix script error alert (#37458) After using CSP nonce, the "onerror" doesn't work anymore. Change it to use a global variable to detect Also help users like #37379 to catch errors more easily. Co-authored-by: Lunny Xiao --- services/context/context_template.go | 23 ++--------------------- templates/base/footer.tmpl | 3 +++ web_src/js/globals.d.ts | 1 + web_src/js/index.ts | 2 ++ web_src/js/vitest.setup.ts | 1 + 5 files changed, 9 insertions(+), 21 deletions(-) diff --git a/services/context/context_template.go b/services/context/context_template.go index b63aaf4c3c..0f083d097e 100644 --- a/services/context/context_template.go +++ b/services/context/context_template.go @@ -5,13 +5,11 @@ package context import ( "context" - "fmt" "html" "html/template" "net/http" "strconv" "strings" - "sync" "time" "code.gitea.io/gitea/modules/httplib" @@ -91,31 +89,14 @@ func (c TemplateContext) AppFullLink(link ...string) template.URL { return template.URL(s + "/" + strings.TrimPrefix(link[0], "/")) } -var globalVars = sync.OnceValue(func() (ret struct { - scriptImportRemainingPart string -}, -) { - // add onerror handler to alert users when the script fails to load: - // * for end users: there were many users reporting that "UI doesn't work", actually they made mistakes in their config - // * for developers: help them to remember to run "make watch-frontend" to build frontend assets - // the message will be directly put in the onerror JS code's string - onScriptErrorPrompt := `Please make sure the asset files can be accessed.` - if !setting.IsProd { - onScriptErrorPrompt += `\n\nFor development, run: make watch-frontend.` - } - onScriptErrorJS := fmt.Sprintf(`alert('Failed to load asset file from ' + this.src + '. %s')`, onScriptErrorPrompt) - ret.scriptImportRemainingPart = `onerror="` + html.EscapeString(onScriptErrorJS) + `">` - return ret -}) - func (c TemplateContext) ScriptImport(path string, typ ...string) template.HTML { if len(typ) > 0 { if typ[0] == "module" { - return template.HTML(``) } panic("unsupported script type: " + typ[0]) } - return template.HTML(``) } func (c TemplateContext) CspScriptNonce() (ret string) { diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 5a218bb62a..b7443345ad 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -11,5 +11,8 @@ {{template "base/footer_content" .}} {{ctx.ScriptImport "js/index.js" "module"}} {{template "custom/footer" .}} + diff --git a/web_src/js/globals.d.ts b/web_src/js/globals.d.ts index bd9fd410b8..5398d407d1 100644 --- a/web_src/js/globals.d.ts +++ b/web_src/js/globals.d.ts @@ -53,6 +53,7 @@ interface Window { enableTimeTracking: boolean, mermaidMaxSourceCharacters: number, i18n: Record, + frontendInited: boolean, }, $: JQueryStatic, jQuery: JQueryStatic, diff --git a/web_src/js/index.ts b/web_src/js/index.ts index d6457a1326..cb2b56a5bd 100644 --- a/web_src/js/index.ts +++ b/web_src/js/index.ts @@ -171,3 +171,5 @@ const initDur = performance.now() - initStartTime; if (initDur > 500) { console.error(`slow init functions took ${initDur.toFixed(3)}ms`); } + +window.config.frontendInited = true; diff --git a/web_src/js/vitest.setup.ts b/web_src/js/vitest.setup.ts index 229c99cceb..190196cabd 100644 --- a/web_src/js/vitest.setup.ts +++ b/web_src/js/vitest.setup.ts @@ -12,6 +12,7 @@ window.config = { enableTimeTracking: true, mermaidMaxSourceCharacters: 5000, i18n: {}, + frontendInited: false, }; window.testModules = {}; From 4952a48b4eb1b1e2394908a8fca811ed3e187455 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 28 Apr 2026 07:30:27 +0800 Subject: [PATCH 03/32] Clean up org pages layout (#37445) 1. Fix overview sidebar regression 2. Remove unnecessary classes and styles 3. Fix "org invite" page --- routers/web/org/teams.go | 2 ++ templates/org/header.tmpl | 36 +++++++++++++++++++------------- templates/org/home.tmpl | 19 ++++++++++------- templates/org/team/invite.tmpl | 20 +++++++----------- templates/org/team/sidebar.tmpl | 20 +++++++++--------- web_src/css/index.css | 1 - web_src/css/org.css | 37 --------------------------------- web_src/css/shared/repoorg.css | 18 ---------------- 8 files changed, 52 insertions(+), 101 deletions(-) delete mode 100644 web_src/css/shared/repoorg.css diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 10803c9fbf..7312478299 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -609,6 +609,8 @@ func DeleteTeam(ctx *context.Context) { // TeamInvite renders the team invite page func TeamInvite(ctx *context.Context) { invite, org, team, inviter, err := getTeamInviteFromContext(ctx) + // TODO: to quickly debug the UI, can uncomment this (don't worry, it won't pass CI lint) + // invite, org, team, inviter, err = &org_model.TeamInvite{}, &org_model.Organization{}, &org_model.Team{}, ctx.Doer, nil if err != nil { if org_model.IsErrTeamInviteNotFound(err) { ctx.NotFound(err) diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 83ae95a1ae..4f9e54b610 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -1,13 +1,13 @@ -
- {{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}} -
-
- {{.Org.DisplayName}} - +
+
{{ctx.AvatarUtils.Avatar .Org 100}}
+
+
+
+ {{.Org.DisplayName}} {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} - - +
+
- {{if .RenderedDescription}}
{{.RenderedDescription}}
{{end}} -
- {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}}
{{end}} - {{if .Org.Website}}
{{end}} - {{if .IsSigned}} - {{if .Org.Email}}
{{svg "octicon-mail"}} {{.Org.Email}}
{{end}} + {{if .RenderedDescription}} +
{{.RenderedDescription}}
+ {{end}} +
+ {{if .Org.Location}} +
{{svg "octicon-location"}} {{.Org.Location}}
+ {{end}} + {{if .Org.Website}} +
{{svg "octicon-link"}} {{.Org.Website}}
+ {{end}} + {{if and .IsSigned .Org.Email}} +
{{svg "octicon-mail"}} {{.Org.Email}}
{{end}}
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 7132743d0c..12b41c3e94 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -55,11 +55,12 @@ {{end}} {{if .NumMembers}} -

- {{ctx.Locale.Tr "org.members"}} +

+ {{ctx.Locale.Tr "org.members"}} {{.NumMembers}} {{svg "octicon-chevron-right"}}

-
+ {{/* gap 8px below is specifically chosen to make sure a full line of avatars can exactly fit the segment width */}} +
{{range $memberUser := .OrgOverviewMembers}} {{if or $.IsOrganizationMember (call $.IsPublicMember $memberUser.ID)}} {{template "shared/user/avatarlink" dict "user" $memberUser "size" 32 "tooltip" true}} @@ -68,20 +69,22 @@
{{end}} {{if .IsOrganizationMember}} -
- {{ctx.Locale.Tr "org.teams"}} +
+ {{ctx.Locale.Tr "org.teams"}} {{.Org.NumTeams}} {{svg "octicon-chevron-right"}}
-
+
+ {{if .IsOrganizationOwner}}
diff --git a/templates/org/team/invite.tmpl b/templates/org/team/invite.tmpl index 14a97ae659..d591414efc 100644 --- a/templates/org/team/invite.tmpl +++ b/templates/org/team/invite.tmpl @@ -1,18 +1,14 @@ {{template "base/head" .}} -
+
{{template "base/alert" .}} -
-
- {{ctx.AvatarUtils.Avatar .Organization 140}} -
-
-
{{ctx.Locale.Tr "org.teams.invite.title" .Team.Name .Organization.Name}}
-
{{ctx.Locale.Tr "org.teams.invite.by" .Inviter.Name}}
-
{{ctx.Locale.Tr "org.teams.invite.description"}}
-
-
-
+
+
+
{{ctx.AvatarUtils.Avatar .Organization 140}}
+
{{ctx.Locale.Tr "org.teams.invite.title" .Team.Name .Organization.Name}}
+
{{ctx.Locale.Tr "org.teams.invite.by" .Inviter.Name}}
+
{{ctx.Locale.Tr "org.teams.invite.description"}}
+
diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 1487c280de..1036e886da 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -15,21 +15,21 @@ {{end}}
-
-
+ +
{{if .Team.Description}} {{.Team.Description}} {{else}} {{ctx.Locale.Tr "org.teams.no_desc"}} {{end}}
- {{if eq .Team.LowerName "owners"}} -
+ +
+ {{/* TODO: old indent is kept to make diff changes minimal, can be reformatted in the future */}} + {{if eq .Team.LowerName "owners"}}

{{ctx.Locale.Tr "org.teams.owners_permission_desc"}}

{{ctx.Locale.Tr "org.teams.owners_permission_suggestion"}}

-
- {{else}} -
+ {{else}}

{{ctx.Locale.Tr "org.team_access_desc"}}

    {{if .Team.IncludesAllRepositories}} @@ -75,9 +75,9 @@ {{end}} -
- {{end}} -
+ {{end}} +
+ {{if .IsOrganizationOwner}}
{{svg "octicon-gear"}} {{ctx.Locale.Tr "org.teams.settings"}} diff --git a/web_src/css/index.css b/web_src/css/index.css index c23e3e1c19..d7f57e324b 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -37,7 +37,6 @@ @import "./shared/flex-list.css"; @import "./shared/milestone.css"; -@import "./shared/repoorg.css"; @import "./shared/settings.css"; @import "./features/dropzone.css"; diff --git a/web_src/css/org.css b/web_src/css/org.css index b54a21ac6e..6de9ebd51e 100644 --- a/web_src/css/org.css +++ b/web_src/css/org.css @@ -1,23 +1,3 @@ -.organization .head .ui.header .ui.right { - margin-top: 5px; -} - -.page-content.organization .org-avatar { - margin-right: 15px; -} - -.page-content.organization #org-info .ui.header { - display: flex; - align-items: center; - font-size: 36px; - margin-bottom: 0; -} - -.page-content.organization #org-info .desc { - font-size: 16px; - margin-bottom: 10px; -} - .page-content.organization .team-item-box > .team-item-header { min-height: 50px; /* the header sometimes contains a mini button, sometimes not, so we set a min-height to make sure the layout is consistent */ } @@ -30,20 +10,3 @@ white-space: nowrap; color: var(--color-text-light-3); } - -.organization.invite .ui.avatar { - width: 100%; - height: 100%; -} - -.organization.teams .detail .item { - padding: 10px 15px; -} - -.organization.teams .detail .item:not(:last-child) { - border-bottom: 1px solid var(--color-secondary); -} - -.org-team-navbar .active.item { - background: var(--color-box-body) !important; -} diff --git a/web_src/css/shared/repoorg.css b/web_src/css/shared/repoorg.css deleted file mode 100644 index 5573ae47b8..0000000000 --- a/web_src/css/shared/repoorg.css +++ /dev/null @@ -1,18 +0,0 @@ -.repository .head .ui.header .text, -.organization .head .ui.header .text { - vertical-align: middle; - font-size: 1.6rem; - margin-left: 15px; -} - -.repository .ui.tabs.container, -.organization .ui.tabs.container { - margin-top: 14px; - margin-bottom: 0; -} - -.repository .head .ui.header .org-visibility .label, -.organization .head .ui.header .org-visibility .label { - margin-left: 5px; - margin-top: 5px; -} From c6ffbfe0d2aabadfd27ec1de984a52f571e270c7 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 28 Apr 2026 08:34:17 +0800 Subject: [PATCH 04/32] Rename CurrentRefPath to CurrentRefSubURL (#37453) Fix a TODO Co-authored-by: Nicolas --- models/renderhelper/repo_comment.go | 4 ++-- models/renderhelper/repo_comment_test.go | 4 ++-- models/renderhelper/repo_file.go | 15 +++++++-------- models/renderhelper/repo_file_test.go | 16 ++++++++-------- models/renderhelper/repo_wiki.go | 8 ++++---- routers/common/markup.go | 4 ++-- routers/web/org/home.go | 2 +- routers/web/repo/commit.go | 3 +-- routers/web/repo/render.go | 4 ++-- routers/web/repo/view_file.go | 4 ++-- routers/web/repo/view_readme.go | 4 ++-- routers/web/user/profile.go | 2 +- 12 files changed, 34 insertions(+), 36 deletions(-) diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go index ae0fbf0abd..d1c587671b 100644 --- a/models/renderhelper/repo_comment.go +++ b/models/renderhelper/repo_comment.go @@ -34,7 +34,7 @@ func (r *RepoComment) ResolveLink(link, preferLinkType string) string { case markup.LinkTypeRoot: return r.ctx.ResolveLinkRoot(link) default: - return r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link) + return r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefSubURL, link) } } @@ -43,7 +43,7 @@ var _ markup.RenderHelper = (*RepoComment)(nil) type RepoCommentOptions struct { DeprecatedRepoName string // it is only a patch for the non-standard "markup" api DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api - CurrentRefPath string // eg: "branch/main" or "commit/11223344" + CurrentRefSubURL string // eg: "branch/main" or "commit/11223344" FootnoteContextID string // the extra context ID for footnotes, used to avoid conflicts with other footnotes in the same page } diff --git a/models/renderhelper/repo_comment_test.go b/models/renderhelper/repo_comment_test.go index 3b13bff73c..1443f8b3c0 100644 --- a/models/renderhelper/repo_comment_test.go +++ b/models/renderhelper/repo_comment_test.go @@ -54,8 +54,8 @@ func TestRepoComment(t *testing.T) { `, rendered) }) - t.Run("WithCurrentRefPath", func(t *testing.T) { - rctx := NewRenderContextRepoComment(t.Context(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}). + t.Run("WithCurrentRefSubURL", func(t *testing.T) { + rctx := NewRenderContextRepoComment(t.Context(), repo1, RepoCommentOptions{CurrentRefSubURL: "/commit/1234"}). WithMarkupType(markdown.MarkupName) // the ref path is only used to render commit message: a commit message is rendered at the commit page with its commit ID path diff --git a/models/renderhelper/repo_file.go b/models/renderhelper/repo_file.go index 5d0bfd6c80..d9aa71b727 100644 --- a/models/renderhelper/repo_file.go +++ b/models/renderhelper/repo_file.go @@ -35,11 +35,11 @@ func (r *RepoFile) ResolveLink(link, preferLinkType string) (finalLink string) { case markup.LinkTypeRoot: finalLink = r.ctx.ResolveLinkRoot(link) case markup.LinkTypeRaw: - finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link) + finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefSubURL), r.opts.CurrentTreePath, link) case markup.LinkTypeMedia: - finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link) + finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefSubURL), r.opts.CurrentTreePath, link) default: - finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link) + finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefSubURL), r.opts.CurrentTreePath, link) } return finalLink } @@ -50,8 +50,8 @@ type RepoFileOptions struct { DeprecatedRepoName string // it is only a patch for the non-standard "markup" api DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api - CurrentRefPath string // eg: "branch/main", it is a sub URL path escaped by callers, TODO: rename to CurrentRefSubURL - CurrentTreePath string // eg: "path/to/file" in the repo, it is the tree path without URL path escaping + CurrentRefSubURL string // eg: "branch/main" or "branch/my%20branch", it is a sub URL path escaped by callers + CurrentTreePath string // eg: "path/to/file" in the repo, it is the tree path without URL path escaping } func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository, opts ...RepoFileOptions) *markup.RenderContext { @@ -71,9 +71,8 @@ func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository, }) } // External render's iframe needs this to generate correct links - // TODO: maybe need to make it access "CurrentRefPath" directly (but impossible at the moment due to cycle-import) - // CurrentRefPath is already path-escaped by callers - rctx.RenderOptions.Metas["RefTypeNameSubURL"] = helper.opts.CurrentRefPath + // TODO: maybe need to make it access "CurrentRefSubURL" directly (but impossible at the moment due to cycle-import) + rctx.RenderOptions.Metas["RefTypeNameSubURL"] = helper.opts.CurrentRefSubURL rctx = rctx.WithHelper(helper).WithEnableHeadingIDGeneration(true) return rctx } diff --git a/models/renderhelper/repo_file_test.go b/models/renderhelper/repo_file_test.go index 3b48efba3a..72d98efc66 100644 --- a/models/renderhelper/repo_file_test.go +++ b/models/renderhelper/repo_file_test.go @@ -36,7 +36,7 @@ func TestRepoFile(t *testing.T) { }) t.Run("AbsoluteAndRelative", func(t *testing.T) { - rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}). + rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefSubURL: "branch/main"}). WithMarkupType(markdown.MarkupName) rendered, err := markup.RenderString(rctx, ` [/test](/test) @@ -53,8 +53,8 @@ func TestRepoFile(t *testing.T) { `, rendered) }) - t.Run("WithCurrentRefPath", func(t *testing.T) { - rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}). + t.Run("WithCurrentRefSubURL", func(t *testing.T) { + rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefSubURL: "/commit/1234"}). WithMarkupType(markdown.MarkupName) rendered, err := markup.RenderString(rctx, ` [/test](/test) @@ -66,10 +66,10 @@ func TestRepoFile(t *testing.T) { `, rendered) }) - t.Run("WithCurrentRefPathByTag", func(t *testing.T) { + t.Run("WithCurrentRefSubURLByTag", func(t *testing.T) { rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{ - CurrentRefPath: "/commit/1234", - CurrentTreePath: "my-dir", + CurrentRefSubURL: "/commit/1234", + CurrentTreePath: "my-dir", }). WithMarkupType(markdown.MarkupName) rendered, err := markup.RenderString(rctx, ` @@ -89,8 +89,8 @@ func TestRepoFileOrgMode(t *testing.T) { t.Run("Links", func(t *testing.T) { rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{ - CurrentRefPath: "/commit/1234", - CurrentTreePath: "my-dir", + CurrentRefSubURL: "/commit/1234", + CurrentTreePath: "my-dir", }).WithRelativePath("my-dir/a.org") rendered, err := markup.RenderString(rctx, ` diff --git a/models/renderhelper/repo_wiki.go b/models/renderhelper/repo_wiki.go index 218b1e4a67..61e2b570e5 100644 --- a/models/renderhelper/repo_wiki.go +++ b/models/renderhelper/repo_wiki.go @@ -36,9 +36,9 @@ func (r *RepoWiki) ResolveLink(link, preferLinkType string) (finalLink string) { case markup.LinkTypeRoot: finalLink = r.ctx.ResolveLinkRoot(link) case markup.LinkTypeMedia, markup.LinkTypeRaw: - finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki/raw", r.opts.currentRefPath), r.opts.currentTreePath, link) + finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki/raw", r.opts.currentRefSubURL), r.opts.currentTreePath, link) default: - finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefPath), r.opts.currentTreePath, link) + finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefSubURL), r.opts.currentTreePath, link) } return finalLink } @@ -50,8 +50,8 @@ type RepoWikiOptions struct { DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api // these options are not used at the moment because Wiki doesn't support sub-path, nor branch - currentRefPath string // eg: "branch/main" - currentTreePath string // eg: "path/to/file" in the repo + currentRefSubURL string // eg: "branch/main" + currentTreePath string // eg: "path/to/file" in the repo } func NewRenderContextRepoWiki(ctx context.Context, repo *repo_model.Repository, opts ...RepoWikiOptions) *markup.RenderContext { diff --git a/routers/common/markup.go b/routers/common/markup.go index 35b1b21f6a..05f48c7902 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -71,7 +71,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur case "gfm": // legacy mode rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{ DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName, - CurrentRefPath: refPath, CurrentTreePath: treePath, + CurrentRefSubURL: refPath, CurrentTreePath: treePath, }) rctx = rctx.WithMarkupType(markdown.MarkupName) case "comment": @@ -87,7 +87,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur case "file": rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{ DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName, - CurrentRefPath: refPath, CurrentTreePath: treePath, + CurrentRefSubURL: refPath, CurrentTreePath: treePath, }) rctx = rctx.WithMarkupType("").WithRelativePath(filePath) // render the repo file content by its extension default: diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 262b001e6a..56475c47f0 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -182,7 +182,7 @@ func prepareOrgProfileReadme(ctx *context.Context, prepareResult *shared_user.Pr } rctx := renderhelper.NewRenderContextRepoFile(ctx, profileRepo, renderhelper.RepoFileOptions{ - CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileRepo.DefaultBranch)), + CurrentRefSubURL: path.Join("branch", util.PathEscapeSegments(profileRepo.DefaultBranch)), }) ctx.Data["ProfileReadmeContent"], err = markdown.RenderString(rctx, readmeBytes) if err != nil { diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 736a2dff00..c118f41381 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -9,7 +9,6 @@ import ( "fmt" "html/template" "net/http" - "path" "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" @@ -409,7 +408,7 @@ func Diff(ctx *context.Context) { if err == nil { ctx.Data["NoteCommit"] = note.Commit ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit) - rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{CurrentRefPath: path.Join("commit", util.PathEscapeSegments(commitID))}) + rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{CurrentRefSubURL: "commit/" + util.PathEscapeSegments(commitID)}) htmlMessage := template.HTML(template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{})))) ctx.Data["NoteRendered"], err = markup.PostProcessCommitMessage(rctx, htmlMessage) if err != nil { diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index ace871a9f1..4a68c96aaa 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -40,8 +40,8 @@ func RenderFile(ctx *context.Context) { defer blobReader.Close() rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.RefTypeNameSubURL(), - CurrentTreePath: path.Dir(ctx.Repo.TreePath), + CurrentRefSubURL: ctx.Repo.RefTypeNameSubURL(), + CurrentTreePath: path.Dir(ctx.Repo.TreePath), }).WithRelativePath(ctx.Repo.TreePath).WithStandalonePage(markup.StandalonePageOptions{ CurrentWebTheme: ctx.TemplateContext.CurrentWebTheme(), RenderQueryString: ctx.Req.URL.RawQuery, diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index 8d7721103a..f6c97e83bb 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -59,8 +59,8 @@ func prepareFileViewLfsAttrs(ctx *context.Context) (*attribute.Attributes, bool) func handleFileViewRenderMarkup(ctx *context.Context, prefetchBuf []byte, utf8Reader io.Reader) bool { rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.RefTypeNameSubURL(), - CurrentTreePath: path.Dir(ctx.Repo.TreePath), + CurrentRefSubURL: ctx.Repo.RefTypeNameSubURL(), + CurrentTreePath: path.Dir(ctx.Repo.TreePath), }).WithRelativePath(ctx.Repo.TreePath) renderer := rctx.DetectMarkupRenderer(prefetchBuf) diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go index 25e1f87806..79ca9efc36 100644 --- a/routers/web/repo/view_readme.go +++ b/routers/web/repo/view_readme.go @@ -190,8 +190,8 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{ - CurrentRefPath: ctx.Repo.RefTypeNameSubURL(), - CurrentTreePath: path.Dir(readmeFullPath), + CurrentRefSubURL: ctx.Repo.RefTypeNameSubURL(), + CurrentTreePath: path.Dir(readmeFullPath), }).WithRelativePath(readmeFullPath) renderer := rctx.DetectMarkupRenderer(buf) if renderer != nil { diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index faf2f442a2..b1d00520c2 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -251,7 +251,7 @@ func prepareUserProfileTabData(ctx *context.Context, profileDbRepo *repo_model.R log.Error("failed to GetBlobContent: %v", err) } else { rctx := renderhelper.NewRenderContextRepoFile(ctx, profileDbRepo, renderhelper.RepoFileOptions{ - CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), + CurrentRefSubURL: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), }) if profileContent, err := markdown.RenderString(rctx, bytes); err != nil { log.Error("failed to RenderString: %v", err) From 596a8868d741da01ee0520d1196459b444dc4699 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 28 Apr 2026 01:04:43 +0000 Subject: [PATCH 05/32] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_zh-CN.json b/options/locale/locale_zh-CN.json index 0bab3a0010..5960e554b7 100644 --- a/options/locale/locale_zh-CN.json +++ b/options/locale/locale_zh-CN.json @@ -2474,7 +2474,7 @@ "repo.settings.tags.protection.allowed.noone": "无", "repo.settings.tags.protection.create": "保护 Git 标签", "repo.settings.tags.protection.none": "没有受保护的 Git 标签。", - "repo.settings.tags.protection.pattern.description": "您可以使用单个名称或 glob 表达式匹配或正则表达式来匹配多个 Git 标签。了解详情请访问 保护标签指南。", + "repo.settings.tags.protection.pattern.description": "您可以使用单个名称或 glob 表达式匹配或正则表达式来匹配多个 Git 标签。欲了解详情请访问 保护标签指南。", "repo.settings.bot_token": "Bot 令牌", "repo.settings.chat_id": "聊天 ID", "repo.settings.thread_id": "线程 ID", @@ -3099,11 +3099,11 @@ "admin.packages.size": "大小", "admin.packages.published": "已发布", "admin.defaulthooks": "默认 Web 钩子", - "admin.defaulthooks.desc": "当某些 Gitea 事件触发时,Web 钩子自动向服务器发出 HTTP POST 请求。这里定义的 Web 钩子是默认配置,将被复制到所有新的仓库中。详情请访问 Web 钩子指南。", + "admin.defaulthooks.desc": "当某些 Gitea 事件触发时,Web 钩子自动向服务器发出 HTTP POST 请求。这里定义的 Web 钩子是默认配置,将被复制到所有新的仓库中。欲了解详情请访问 Web 钩子指南。", "admin.defaulthooks.add_webhook": "添加默认 Web 钩子", "admin.defaulthooks.update_webhook": "更新默认 Web 钩子", "admin.systemhooks": "系统 Web 钩子", - "admin.systemhooks.desc": "当某些 Gitea 事件触发时,Web 钩子自动向服务器发出 HTTP POST 请求。这里定义的 Web 钩子将作用于系统上的所有仓库,所以请考虑这可能带来的任何性能影响。了解详情请访问 Web 钩子指南。", + "admin.systemhooks.desc": "当某些 Gitea 事件触发时,Web 钩子自动向服务器发出 HTTP POST 请求。这里定义的 Web 钩子将作用于系统上的所有仓库,所以请考虑这可能带来的任何性能影响。欲了解详情请访问 Web 钩子指南。", "admin.systemhooks.add_webhook": "添加系统 Web 钩子", "admin.systemhooks.update_webhook": "更新系统 Web 钩子", "admin.auths.auth_manage_panel": "认证源管理", From 15b23f037d8e7bf8d945a7fecb0ebb9cc4ab43c8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 28 Apr 2026 09:29:09 +0800 Subject: [PATCH 06/32] Fix attachment Content-Security-Policy (#37455) See the comments. Others are not changed, only added a new rule for medias: `serveHeaderCspMedia` --------- Co-authored-by: Giteabot --- modules/httplib/serve.go | 51 +++++++++++++++++++++++++---------- modules/httplib/serve_test.go | 27 +++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go index 8abf6f1887..6c2fe9b0d6 100644 --- a/modules/httplib/serve.go +++ b/modules/httplib/serve.go @@ -37,6 +37,42 @@ type ServeHeaderOptions struct { LastModified time.Time } +const ( + // Disable JS execution on the same origin, since we serve the file from the same origin as Gitea server. + // This rule can be relaxed in the future as long as it is properly sandboxed. + // "style-src" is for SVG inline styles (from Display SVG files as images instead of text #14101) + serveHeaderCspDefault = "default-src 'none'; style-src 'unsafe-inline'; sandbox" + + // No sandbox attribute for PDF as it breaks rendering in at least Safari. + // This should generally be safe as scripts inside PDF can not escape the PDF document. + // See https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion. + // HINT: PDF-RENDER-SANDBOX: PDF won't render in sandboxed context + serveHeaderCspPdf = "default-src 'none'; style-src 'unsafe-inline'" + + // For audios and videos, actually it doesn't really need CSP (just like Gitea <= 1.25) + serveHeaderCspAudioVideo = "" +) + +func serveSetHeaderContentRelated(w http.ResponseWriter, contentType string) { + header := w.Header() + contentType = util.IfZero(contentType, typesniffer.MimeTypeApplicationOctetStream) + header.Set("Content-Type", contentType) + header.Set("X-Content-Type-Options", "nosniff") + + csp := serveHeaderCspDefault + if strings.HasPrefix(contentType, "application/pdf") { + csp = serveHeaderCspPdf + } + if strings.HasPrefix(contentType, "video/") || strings.HasPrefix(contentType, "audio/") { + csp = serveHeaderCspAudioVideo + } + if csp != "" { + header.Set("Content-Security-Policy", csp) + } else { + header.Del("Content-Security-Policy") + } +} + // ServeSetHeaders sets necessary content serve headers func ServeSetHeaders(w http.ResponseWriter, opts ServeHeaderOptions) { header := w.Header() @@ -46,24 +82,11 @@ func ServeSetHeaders(w http.ResponseWriter, opts ServeHeaderOptions) { w.Header().Add(gzhttp.HeaderNoCompression, "1") } - contentType := util.IfZero(opts.ContentType, typesniffer.MimeTypeApplicationOctetStream) - header.Set("Content-Type", contentType) - header.Set("X-Content-Type-Options", "nosniff") + serveSetHeaderContentRelated(w, opts.ContentType) if opts.ContentLength != nil { header.Set("Content-Length", strconv.FormatInt(*opts.ContentLength, 10)) } - - // Disable script execution of HTML/SVG files, since we serve the file from the same origin as Gitea server - header.Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - if strings.Contains(contentType, "application/pdf") { - // no sandbox attribute for PDF as it breaks rendering in at least safari. this - // should generally be safe as scripts inside PDF can not escape the PDF document - // see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion - // HINT: PDF-RENDER-SANDBOX: PDF won't render in sandboxed context - header.Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'") - } - if opts.Filename != "" && opts.ContentDisposition != "" { header.Set("Content-Disposition", encodeContentDisposition(opts.ContentDisposition, path.Base(opts.Filename))) header.Set("Access-Control-Expose-Headers", "Content-Disposition") diff --git a/modules/httplib/serve_test.go b/modules/httplib/serve_test.go index 38cf4c197f..2a245300b0 100644 --- a/modules/httplib/serve_test.go +++ b/modules/httplib/serve_test.go @@ -12,6 +12,8 @@ import ( "strings" "testing" + "code.gitea.io/gitea/modules/typesniffer" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -106,3 +108,28 @@ func TestServeUserContentByFile(t *testing.T) { test(t, http.StatusPartialContent, data[1:]) }) } + +func TestServeSetHeaderContentRelated(t *testing.T) { + cases := []struct { + contentType string + csp string + }{ + {"", serveHeaderCspDefault}, + {"any", serveHeaderCspDefault}, + {"application/pdf", serveHeaderCspPdf}, + {"application/pdf; other", serveHeaderCspPdf}, + {"audio/mp4", serveHeaderCspAudioVideo}, + {"video/ogg; other", serveHeaderCspAudioVideo}, + {typesniffer.MimeTypeImageSvg, serveHeaderCspDefault}, + } + for _, c := range cases { + w := httptest.NewRecorder() + serveSetHeaderContentRelated(w, c.contentType) + csp := w.Header().Get("Content-Security-Policy") + assert.Equal(t, c.csp, csp, "content-type: %s", c.contentType) + assert.Equal(t, "nosniff", w.Header().Get("X-Content-Type-Options")) // it should always be there + } + + // make sure sandboxed + require.Contains(t, serveHeaderCspDefault, "; sandbox") +} From c8e67799b2fa6eb3fa0a087608a0241f6265a5e4 Mon Sep 17 00:00:00 2001 From: Rayan Salhab Date: Tue, 28 Apr 2026 04:58:04 +0300 Subject: [PATCH 07/32] Fix scheduled action panic with null event payload (#37459) This fixes the scheduled action panic when an event payload is JSON `null` by initializing the payload map before adding `schedule`. It also adds regression coverage for the null-payload case. Fixes #37447. Testing: - `go test -tags 'sqlite sqlite_unlock_notify' ./services/actions -run '^TestWithScheduleInEventPayload$' -count=1` - Local note: this agent ran the command as root with a temporary `GITEA_TEST_CONF=custom/conf/app-test-root.ini` file that only set `I_AM_BEING_UNSAFE_RUNNING_AS_ROOT = true`. Authorship: cyphercodes; AI assistance disclosed: Hermes Agent (GPT-5.5). --------- Co-authored-by: cyphercodes Co-authored-by: Hermes Agent (GPT-5.5) Co-authored-by: Nicolas Co-authored-by: silverwind Co-authored-by: Claude (Opus 4.7) Co-authored-by: Giteabot --- services/actions/schedule_tasks.go | 18 +++++++++++++----- services/actions/schedule_tasks_test.go | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index b2dc3f9840..3a0dff490a 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -132,14 +132,22 @@ func CreateScheduleTask(ctx context.Context, spec *actions_model.ActionScheduleS } func withScheduleInEventPayload(eventPayload, schedule string) string { - if schedule == "" || eventPayload == "" { + if schedule == "" { return eventPayload } - event := map[string]any{} - if err := json.Unmarshal([]byte(eventPayload), &event); err != nil { - log.Error("withScheduleInEventPayload: unmarshal: %v", err) - return eventPayload + // eventPayload originates from json.Marshal(input.Payload) in handleSchedules, + // so a nil payload is stored as the literal "null" and pre-existing rows may be + // empty. Both cases start from a fresh map so the schedule field can still be set. + var event map[string]any + if eventPayload != "" { + if err := json.Unmarshal([]byte(eventPayload), &event); err != nil { + log.Error("withScheduleInEventPayload: unmarshal: %v", err) + return eventPayload + } + } + if event == nil { + event = map[string]any{} } event["schedule"] = schedule diff --git a/services/actions/schedule_tasks_test.go b/services/actions/schedule_tasks_test.go index 770b842623..f2c7e656e6 100644 --- a/services/actions/schedule_tasks_test.go +++ b/services/actions/schedule_tasks_test.go @@ -22,9 +22,20 @@ func TestWithScheduleInEventPayload(t *testing.T) { assert.Equal(t, "refs/heads/main", event["ref"]) }) - t.Run("keeps empty payload", func(t *testing.T) { + t.Run("adds schedule to null payload", func(t *testing.T) { + updated := withScheduleInEventPayload("null", "37 12 5 1 2") + + event := map[string]any{} + assert.NoError(t, json.Unmarshal([]byte(updated), &event)) + assert.Equal(t, "37 12 5 1 2", event["schedule"]) + }) + + t.Run("adds schedule to empty payload", func(t *testing.T) { updated := withScheduleInEventPayload("", "37 12 5 1 2") - assert.Empty(t, updated) + + event := map[string]any{} + assert.NoError(t, json.Unmarshal([]byte(updated), &event)) + assert.Equal(t, "37 12 5 1 2", event["schedule"]) }) t.Run("keeps payload when schedule empty", func(t *testing.T) { From 8bf51da65fe1167802678c02639ee11c20c2a815 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 28 Apr 2026 12:36:39 +0800 Subject: [PATCH 08/32] Refactor pull request view (4) (#37451) Use JSON attribute instead of inline script --------- Co-authored-by: Nicolas --- routers/web/repo/issue_view.go | 70 +-------- routers/web/repo/pull.go | 13 +- routers/web/repo/pull_merge_form.go | 147 ++++++++++++++++++ .../issue/view_content/pull_merge_box.tmpl | 97 +----------- .../view_content/pull_merge_instruction.tmpl | 34 ++-- .../js/components/PullRequestMergeForm.vue | 6 +- web_src/js/features/repo-issue-pull.ts | 24 +-- 7 files changed, 194 insertions(+), 197 deletions(-) create mode 100644 routers/web/repo/pull_merge_form.go diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go index af13a1156e..852b880ab0 100644 --- a/routers/web/repo/issue_view.go +++ b/routers/web/repo/issue_view.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" - pull_model "code.gitea.io/gitea/models/pull" "code.gitea.io/gitea/models/renderhelper" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -826,6 +825,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * panic("impossible, issue must be the same") } + pull := issue.PullRequest data := &pullMergeBoxData{} prInfo.MergeBoxData = data @@ -834,14 +834,12 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * statusCheckData = &pullCommitStatusCheckData{} // make the following logic easier, no need to keep checking "nil" } - pull := issue.PullRequest canDelete := false allowMerge := false canWriteToHeadRepo := false pull_service.StartPullRequestCheckOnView(ctx, pull) - ctx.Data["GetCommitMessages"] = "" if !prInfo.IsPullRequestBroken { var err error ctx.Data["UpdateAllowed"], ctx.Data["UpdateByRebaseAllowed"], err = pull_service.IsUserAllowedToUpdate(ctx, pull, ctx.Doer) @@ -849,7 +847,6 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * ctx.ServerError("IsUserAllowedToUpdate", err) return } - ctx.Data["GetCommitMessages"] = pull_service.GetSquashMergeCommitMessages(ctx, pull) } if pull.IsFilesConflicted() { @@ -903,59 +900,11 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * } } - data.ReloadingInterval = util.Iif(pull != nil && pull.IsChecking(), 2000, 0) - ctx.Data["CanWriteToHeadRepo"] = canWriteToHeadRepo - ctx.Data["ShowMergeInstructions"] = canWriteToHeadRepo + data.ReloadingInterval = util.Iif(pull.IsChecking(), 2000, 0) + data.ShowMergeInstructions = canWriteToHeadRepo + data.ShowPullCommands = pull.HeadRepo != nil && !pull.HasMerged && !issue.IsClosed ctx.Data["AllowMerge"] = allowMerge - prUnit, err := issue.Repo.GetUnit(ctx, unit.TypePullRequests) - if err != nil { - ctx.ServerError("GetUnit", err) - return - } - prConfig := prUnit.PullRequestsConfig() - - ctx.Data["AutodetectManualMerge"] = prConfig.AutodetectManualMerge - - var mergeStyle repo_model.MergeStyle - // Check correct values and select default - if ms, ok := ctx.Data["MergeStyle"].(repo_model.MergeStyle); !ok || - !prConfig.IsMergeStyleAllowed(ms) { - if prConfig.IsMergeStyleAllowed(prConfig.DefaultMergeStyle) && !ok { - mergeStyle = prConfig.DefaultMergeStyle - } else if prConfig.AllowMerge { - mergeStyle = repo_model.MergeStyleMerge - } else if prConfig.AllowRebase { - mergeStyle = repo_model.MergeStyleRebase - } else if prConfig.AllowRebaseMerge { - mergeStyle = repo_model.MergeStyleRebaseMerge - } else if prConfig.AllowSquash { - mergeStyle = repo_model.MergeStyleSquash - } else if prConfig.AllowFastForwardOnly { - mergeStyle = repo_model.MergeStyleFastForwardOnly - } else if prConfig.AllowManualMerge { - mergeStyle = repo_model.MergeStyleManuallyMerged - } - } - - ctx.Data["MergeStyle"] = mergeStyle - - defaultMergeMessage, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle) - if err != nil { - ctx.ServerError("GetDefaultMergeMessage", err) - return - } - ctx.Data["DefaultMergeMessage"] = defaultMergeMessage - ctx.Data["DefaultMergeBody"] = defaultMergeBody - - defaultSquashMergeMessage, defaultSquashMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash) - if err != nil { - ctx.ServerError("GetDefaultSquashMergeMessage", err) - return - } - ctx.Data["DefaultSquashMergeMessage"] = defaultSquashMergeMessage - ctx.Data["DefaultSquashMergeBody"] = defaultSquashMergeBody - pb := prInfo.ProtectedBranchRule if pb != nil { pb.Repo = pull.BaseRepo @@ -995,6 +944,9 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * return } + prConfig := issue.Repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + data.AutodetectManualMerge = prConfig.AutodetectManualMerge + stillCanManualMerge := func() bool { if pull.HasMerged || issue.IsClosed || !ctx.IsSigned { return false @@ -1007,13 +959,6 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * ctx.Data["StillCanManualMerge"] = stillCanManualMerge() - // Check if there is a pending pr merge - ctx.Data["HasPendingPullRequestMerge"], ctx.Data["PendingPullRequestMerge"], err = pull_model.GetScheduledMergeByPullID(ctx, pull.ID) - if err != nil { - ctx.ServerError("GetScheduledMergeByPullID", err) - return - } - enableStatusCheck := pb != nil && pb.EnableStatusCheck ctx.Data["EnableStatusCheck"] = enableStatusCheck @@ -1043,6 +988,7 @@ func (prInfo *pullRequestViewInfo) prepareMergeBox(ctx *context.Context, issue * (!data.requireSigned || data.willSign) // signing requirement is satisfied ctx.Data["PullMergeBoxData"] = prInfo.MergeBoxData + prInfo.prepareMergeBoxFormProps(ctx) } func prepareIssueViewContent(ctx *context.Context, issue *issues_model.Issue) { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c532cbba22..7b31a26d6f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -164,12 +164,13 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { func (prInfo *pullRequestViewInfo) setTemplateDataMergeTarget(ctx *context.Context) { pull := prInfo.issue.PullRequest if ctx.Repo.Owner.Name == pull.MustHeadUserName(ctx) { - ctx.Data["HeadTarget"] = pull.HeadBranch + prInfo.headTarget = pull.HeadBranch } else if pull.HeadRepo == nil { - ctx.Data["HeadTarget"] = ctx.Locale.Tr("repo.pull.deleted_branch", pull.HeadBranch) + prInfo.headTarget = ctx.Locale.TrString("repo.pull.deleted_branch", pull.HeadBranch) } else { - ctx.Data["HeadTarget"] = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch + prInfo.headTarget = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch } + ctx.Data["HeadTarget"] = prInfo.headTarget ctx.Data["BaseTarget"] = pull.BaseBranch headBranchLink := "" if pull.Flow == issues_model.PullRequestFlowGithub { @@ -268,6 +269,11 @@ type pullMergeBoxData struct { HasOverridableBlockers bool CanMergeNow bool + MergeFormProps map[string]any + ShowPullCommands bool + ShowMergeInstructions bool + AutodetectManualMerge bool + // don't expose unneeded fields to templates, need more refactoring changes hasStatusCheckBlocker bool isPullBranchDeletable bool @@ -289,6 +295,7 @@ type pullRequestViewInfo struct { IsPullRequestBroken bool HeadBranchCommitID string + headTarget string // for display purpose only CompareInfo git_service.CompareInfo ProtectedBranchRule *git_model.ProtectedBranch diff --git a/routers/web/repo/pull_merge_form.go b/routers/web/repo/pull_merge_form.go new file mode 100644 index 0000000000..b390fd6934 --- /dev/null +++ b/routers/web/repo/pull_merge_form.go @@ -0,0 +1,147 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "html/template" + + pull_model "code.gitea.io/gitea/models/pull" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/services/context" + pull_service "code.gitea.io/gitea/services/pull" +) + +func (prInfo *pullRequestViewInfo) prepareMergeBoxFormProps(ctx *context.Context) { + pull := prInfo.issue.PullRequest + prConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + + // Check correct values and select default + var mergeStyle repo_model.MergeStyle + if prConfig.IsMergeStyleAllowed(prConfig.DefaultMergeStyle) { + mergeStyle = prConfig.DefaultMergeStyle + } else if prConfig.AllowMerge { + mergeStyle = repo_model.MergeStyleMerge + } else if prConfig.AllowRebase { + mergeStyle = repo_model.MergeStyleRebase + } else if prConfig.AllowRebaseMerge { + mergeStyle = repo_model.MergeStyleRebaseMerge + } else if prConfig.AllowSquash { + mergeStyle = repo_model.MergeStyleSquash + } else if prConfig.AllowFastForwardOnly { + mergeStyle = repo_model.MergeStyleFastForwardOnly + } else if prConfig.AllowManualMerge { + mergeStyle = repo_model.MergeStyleManuallyMerged + } + if mergeStyle == "" { + return + } + + // Check if there is a pending pr merge + hasPendingPullRequestMerge, pendingPullRequestMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID) + if err != nil { + ctx.ServerError("GetScheduledMergeByPullID", err) + return + } + + var hasPendingPullRequestMergeTip template.HTML + if hasPendingPullRequestMerge { + createdPRMergeStr := templates.TimeSince(pendingPullRequestMerge.CreatedUnix) + hasPendingPullRequestMergeTip = ctx.Locale.Tr("repo.pulls.auto_merge_has_pending_schedule", pendingPullRequestMerge.Doer.Name, createdPRMergeStr) + } + + defaultMergeTitle, defaultMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, mergeStyle) + if err != nil { + ctx.ServerError("GetDefaultMergeMessage", err) + return + } + defaultSquashMergeTitle, defaultSquashMergeBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pull, repo_model.MergeStyleSquash) + if err != nil { + ctx.ServerError("GetDefaultSquashMergeMessage", err) + return + } + + var defaultSquashMergeCommitMessages string + if !prInfo.IsPullRequestBroken { + defaultSquashMergeCommitMessages = pull_service.GetSquashMergeCommitMessages(ctx, pull) + } + + allOverridableChecksOk := !prInfo.MergeBoxData.HasOverridableBlockers + prInfo.MergeBoxData.MergeFormProps = map[string]any{ + "baseLink": prInfo.issue.Link(), + "textCancel": ctx.Locale.Tr("cancel"), + "textDeleteBranch": ctx.Locale.Tr("repo.branch.delete", prInfo.headTarget), + "textAutoMergeButtonWhenSucceed": ctx.Locale.Tr("repo.pulls.auto_merge_button_when_succeed"), + "textAutoMergeWhenSucceed": ctx.Locale.Tr("repo.pulls.auto_merge_when_succeed"), + "textAutoMergeCancelSchedule": ctx.Locale.Tr("repo.pulls.auto_merge_cancel_schedule"), + "textClearMergeMessage": ctx.Locale.Tr("repo.pulls.clear_merge_message"), + "textClearMergeMessageHint": ctx.Locale.Tr("repo.pulls.clear_merge_message_hint"), + "textMergeCommitId": ctx.Locale.Tr("repo.pulls.merge_commit_id"), + + "canMergeNow": prInfo.MergeBoxData.CanMergeNow, + "allOverridableChecksOk": allOverridableChecksOk, + "emptyCommit": pull.IsEmpty(), + "pullHeadCommitID": prInfo.CompareInfo.HeadCommitID, + "isPullBranchDeletable": prInfo.MergeBoxData.isPullBranchDeletable, + "defaultMergeStyle": mergeStyle, + "defaultDeleteBranchAfterMerge": prConfig.DefaultDeleteBranchAfterMerge, + "mergeMessageFieldPlaceHolder": ctx.Locale.Tr("repo.editor.commit_message_desc"), + "defaultMergeMessage": defaultMergeBody, + + "hasPendingPullRequestMerge": hasPendingPullRequestMerge, + "hasPendingPullRequestMergeTip": hasPendingPullRequestMergeTip, + } + + // if this pr can be merged now, then hide the auto merge + generalHideAutoMerge := prInfo.MergeBoxData.CanMergeNow && allOverridableChecksOk + + prInfo.MergeBoxData.MergeFormProps["mergeStyles"] = []any{ + map[string]any{ + "name": "merge", + "allowed": prConfig.AllowMerge, + "textDoMerge": ctx.Locale.Tr("repo.pulls.merge_pull_request"), + "mergeTitleFieldText": defaultMergeTitle, + "mergeMessageFieldText": defaultMergeBody, + "hideAutoMerge": generalHideAutoMerge, + }, + map[string]any{ + "name": "rebase", + "allowed": prConfig.AllowRebase, + "textDoMerge": ctx.Locale.Tr("repo.pulls.rebase_merge_pull_request"), + "hideMergeMessageTexts": true, + "hideAutoMerge": generalHideAutoMerge, + }, + map[string]any{ + "name": "rebase-merge", + "allowed": prConfig.AllowRebaseMerge, + "textDoMerge": ctx.Locale.Tr("repo.pulls.rebase_merge_commit_pull_request"), + "mergeTitleFieldText": defaultMergeTitle, + "mergeMessageFieldText": defaultMergeBody, + "hideAutoMerge": generalHideAutoMerge, + }, + map[string]any{ + "name": "squash", + "allowed": prConfig.AllowSquash, + "textDoMerge": ctx.Locale.Tr("repo.pulls.squash_merge_pull_request"), + "mergeTitleFieldText": defaultSquashMergeTitle, + "mergeMessageFieldText": defaultSquashMergeCommitMessages + defaultSquashMergeBody, + "hideAutoMerge": generalHideAutoMerge, + }, + map[string]any{ + "name": "fast-forward-only", + "allowed": prConfig.AllowFastForwardOnly && pull.CommitsBehind == 0, + "textDoMerge": ctx.Locale.Tr("repo.pulls.fast_forward_only_merge_pull_request"), + "hideMergeMessageTexts": true, + "hideAutoMerge": generalHideAutoMerge, + }, + map[string]any{ + "name": "manually-merged", + "allowed": prConfig.AllowManualMerge, + "textDoMerge": ctx.Locale.Tr("repo.pulls.merge_manually"), + "hideMergeMessageTexts": true, + "hideAutoMerge": true, + }, + } +} diff --git a/templates/repo/issue/view_content/pull_merge_box.tmpl b/templates/repo/issue/view_content/pull_merge_box.tmpl index 84d703debc..768d3b3119 100644 --- a/templates/repo/issue/view_content/pull_merge_box.tmpl +++ b/templates/repo/issue/view_content/pull_merge_box.tmpl @@ -208,100 +208,11 @@ {{end}} {{if .AllowMerge}} {{/* user is allowed to merge */}} - {{$prUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePullRequests}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} - {{$hasPendingPullRequestMergeTip := ""}} - {{if .HasPendingPullRequestMerge}} - {{$createdPRMergeStr := DateUtils.TimeSince .PendingPullRequestMerge.CreatedUnix}} - {{$hasPendingPullRequestMergeTip = ctx.Locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}} - {{end}} + {{if $data.MergeFormProps}}
- - {{$showGeneralMergeForm = true}} {{/* The merge form is a Vue component. After mounted, it has a button for choosing merge style, so make it have min-height to avoid layout shifting */}} -
+
{{else}} {{/* no merge style was set in repo setting: not or ($prUnit.PullRequestsConfig.AllowMerge ...) */}}
@@ -396,8 +307,8 @@ {{end}} - {{if and .Issue.PullRequest.HeadRepo (not .Issue.PullRequest.HasMerged) (not .Issue.IsClosed)}} - {{template "repo/issue/view_content/pull_merge_instruction" dict "PullRequest" .Issue.PullRequest "ShowMergeInstructions" .ShowMergeInstructions}} + {{if $data.ShowPullCommands}} + {{template "repo/issue/view_content/pull_merge_instruction" dict "PullRequest" .Issue.PullRequest "MergeBoxData" $data}} {{end}}
diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index b52333466d..ad85c00450 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -1,57 +1,59 @@ +{{$data := $.MergeBoxData}} +{{$pull := $.PullRequest}}
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_hint"}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_title"}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_desc"}}
- {{$localBranch := .PullRequest.HeadBranch}} - {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}} - {{$localBranch = print .PullRequest.HeadRepo.OwnerName "-" .PullRequest.HeadBranch}} + {{$localBranch := $pull.HeadBranch}} + {{if ne $pull.HeadRepo.ID $pull.BaseRepo.ID}} + {{$localBranch = print $pull.HeadRepo.OwnerName "-" $pull.HeadBranch}} {{end}}
{{$gitRemoteName := ctx.RootData.SystemConfig.Repository.GitGuideRemoteName.Value ctx}} - {{if eq .PullRequest.Flow 0}} -
git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}{{ctx.AppFullLink .PullRequest.HeadRepo.Link}}{{else}}{{$gitRemoteName}}{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}
+ {{if eq $pull.Flow 0}} +
git fetch -u {{if ne $pull.HeadRepo.ID $pull.BaseRepo.ID}}{{ctx.AppFullLink $pull.HeadRepo.Link}}{{else}}{{$gitRemoteName}}{{end}} {{$pull.HeadBranch}}:{{$localBranch}}
{{else}} -
git fetch -u {{$gitRemoteName}} {{.PullRequest.GetGitHeadRefName}}:{{$localBranch}}
+
git fetch -u {{$gitRemoteName}} {{$pull.GetGitHeadRefName}}:{{$localBranch}}
{{end}}
git checkout {{$localBranch}}
- {{if .ShowMergeInstructions}} + {{if $data.ShowMergeInstructions}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}} - {{if not .AutodetectManualMerge}} + {{if not $data.AutodetectManualMerge}}
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_warning"}}
{{end}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge --no-ff {{$localBranch}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge --ff-only {{$localBranch}}
git checkout {{$localBranch}}
-
git rebase {{.PullRequest.BaseBranch}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git rebase {{$pull.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge --no-ff {{$localBranch}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge --squash {{$localBranch}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge --ff-only {{$localBranch}}
-
git checkout {{.PullRequest.BaseBranch}}
+
git checkout {{$pull.BaseBranch}}
git merge {{$localBranch}}
-
git push {{$gitRemoteName}} {{.PullRequest.BaseBranch}}
+
git push {{$gitRemoteName}} {{$pull.BaseBranch}}
{{end}}
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index 7ae8fbfdaf..d2835fe163 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -3,9 +3,11 @@ import {computed, onMounted, onUnmounted, shallowRef, watch} from 'vue'; import {SvgIcon} from '../svg.ts'; import {toggleElem} from '../utils/dom.ts'; -const {pageData} = window.config; +const props = defineProps<{ + mergeFormProps: any, // TODO: this is a huge object, need to be refactored in the future +}>(); -const mergeForm = pageData.pullRequestMergeForm!; +const mergeForm = props.mergeFormProps; const mergeTitleFieldValue = shallowRef(''); const mergeMessageFieldValue = shallowRef(''); diff --git a/web_src/js/features/repo-issue-pull.ts b/web_src/js/features/repo-issue-pull.ts index 4e148b3ec0..33e072a1a5 100644 --- a/web_src/js/features/repo-issue-pull.ts +++ b/web_src/js/features/repo-issue-pull.ts @@ -63,27 +63,10 @@ async function initRepoPullRequestMergeForm(box: HTMLElement) { const el = box.querySelector('#pull-request-merge-form'); if (!el) return; + const data = JSON.parse(el.getAttribute('data-merge-form-props')!); const {default: PullRequestMergeForm} = await import('../components/PullRequestMergeForm.vue'); - const view = createApp(PullRequestMergeForm); - view.mount(el); -} - -function executeScripts(elem: Element) { - // find any existing nonce value from the current page and apply it to the new script - const scriptNonce = document.querySelector('script[nonce]')!.getAttribute('nonce')!; - for (const oldScript of elem.querySelectorAll('script')) { - // TODO: that's the only way to load the data for the merge form. In the future - // we need to completely decouple the page data and embedded script - // eslint-disable-next-line github/no-dynamic-script-tag - const newScript = document.createElement('script'); - for (const attr of oldScript.attributes) { - if (attr.name === 'type' && attr.value === 'module') continue; - newScript.setAttribute(attr.name, attr.value); - } - newScript.setAttribute('nonce', scriptNonce); - newScript.text = oldScript.text; - document.body.append(newScript); - } + const view = createApp(PullRequestMergeForm, {mergeFormProps: data}); + view.mount(el); // TODO: can unmount when reloaded? } export function initRepoPullMergeBox(el: HTMLElement) { @@ -124,7 +107,6 @@ export function initRepoPullMergeBox(el: HTMLElement) { } document.removeEventListener('visibilitychange', onVisibilityChange); const newElem = createElementFromHTML(await resp.text()); - executeScripts(newElem); el.replaceWith(newElem); }; From fedc9dc993f0f14866fa4dcbe57c6ba8d90a180e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 17:26:08 +0200 Subject: [PATCH 09/32] FIX: URL sanitization to handle schemeless credentials (#37440) Fixes #37435 --------- Co-authored-by: silverwind Co-authored-by: Claude (Opus 4.7) Co-authored-by: wxiaoguang Co-authored-by: Giteabot --- modules/git/gitcmd/command.go | 6 +- modules/git/gitcmd/command_test.go | 5 +- modules/util/sanitize.go | 122 +++++++++++++++++++++-------- modules/util/sanitize_test.go | 53 ++++++++++--- services/migrations/migrate.go | 2 +- 5 files changed, 139 insertions(+), 49 deletions(-) diff --git a/modules/git/gitcmd/command.go b/modules/git/gitcmd/command.go index e9b51802fe..ee447dfd03 100644 --- a/modules/git/gitcmd/command.go +++ b/modules/git/gitcmd/command.go @@ -57,14 +57,12 @@ type Command struct { } func logArgSanitize(arg string) string { - if strings.Contains(arg, "://") && strings.Contains(arg, "@") { - return util.SanitizeCredentialURLs(arg) - } else if filepath.IsAbs(arg) { + if filepath.IsAbs(arg) { base := filepath.Base(arg) dir := filepath.Dir(arg) return ".../" + filepath.Join(filepath.Base(dir), base) } - return arg + return util.SanitizeCredentialURLs(arg) } func (c *Command) LogString() string { diff --git a/modules/git/gitcmd/command_test.go b/modules/git/gitcmd/command_test.go index 19ec02b808..1a9bfe7d75 100644 --- a/modules/git/gitcmd/command_test.go +++ b/modules/git/gitcmd/command_test.go @@ -109,7 +109,10 @@ func TestCommandString(t *testing.T) { assert.Equal(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString()) cmd = NewCommand("url: https://a:b@c/", "/root/dir-a/dir-b") - assert.Equal(t, cmd.prog+` "url: https://sanitized-credential@c/" .../dir-a/dir-b`, cmd.LogString()) + assert.Equal(t, cmd.prog+` "url: https://(masked)@c/" .../dir-a/dir-b`, cmd.LogString()) + + cmd = NewCommand("url: a:b@c/", "/root/dir-a/dir-b") + assert.Equal(t, cmd.prog+` "url: (masked)@c/" .../dir-a/dir-b`, cmd.LogString()) } func TestRunStdError(t *testing.T) { diff --git a/modules/util/sanitize.go b/modules/util/sanitize.go index 0dd8b342a2..88ca34788f 100644 --- a/modules/util/sanitize.go +++ b/modules/util/sanitize.go @@ -5,7 +5,8 @@ package util import ( "bytes" - "unicode" + "net" + "strings" ) type sanitizedError struct { @@ -25,48 +26,103 @@ func SanitizeErrorCredentialURLs(err error) error { return sanitizedError{err: err} } -const userPlaceholder = "sanitized-credential" - var schemeSep = []byte("://") -// SanitizeCredentialURLs remove all credentials in URLs (starting with "scheme://") for the input string: "https://user:pass@domain.com" => "https://sanitized-credential@domain.com" +const userInfoPlaceholder = "(masked)" + +// SanitizeCredentialURLs remove all credentials in URLs for the input string: +// * "https://userinfo@domain.com" => "https://***@domain.com" +// * "user:pass@domain.com" => "***@domain.com" +// "***" is a magic string internally used, doesn't guarantee to be anything. func SanitizeCredentialURLs(s string) string { + sepColPos := strings.Index(s, ":") + if sepColPos == -1 { + return s // fast path: no colon, unlikely contain any URL credential + } + sepAtPos := strings.Index(s[sepColPos+1:], "@") + for sepAtPos == -1 { + return s // fast path: no "@" after colon, unlikely contain any URL credential + } + sepAtPos += sepColPos + 1 + + res := make([]byte, 0, len(s)+len(userInfoPlaceholder)) // a best guess to avoid too many re-allocations bs := UnsafeStringToBytes(s) - schemeSepPos := bytes.Index(bs, schemeSep) - if schemeSepPos == -1 || bytes.IndexByte(bs[schemeSepPos:], '@') == -1 { - return s // fast return if there is no URL scheme or no userinfo - } - out := make([]byte, 0, len(bs)+len(userPlaceholder)) - for schemeSepPos != -1 { - schemeSepPos += 3 // skip the "://" - sepAtPos := -1 // the possible '@' position: "https://foo@[^here]host" - sepEndPos := schemeSepPos // the possible end position: "The https://host[^here] in log for test" - sepLoop: - for ; sepEndPos < len(bs); sepEndPos++ { - c := bs[sepEndPos] - if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') { - continue - } + for { + // left part (before "@") is likely to be the "userinfo" (single username, or "username:password") + leftPos := sepAtPos - 1 + leftLoop: + for leftPos >= 0 { + c := bs[leftPos] switch c { - case '@': - sepAtPos = sepEndPos case '-', '.', '_', '~', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '%': - continue // due to RFC 3986, userinfo can contain - . _ ~ ! $ & ' ( ) * + , ; = : and any percent-encoded chars + // RFC 3986, userinfo can contain - . _ ~ ! $ & ' ( ) * + , ; = : and any percent-encoded chars default: - break sepLoop // if it is an invalid char for URL (eg: space, '/', and others), stop the loop + valid := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' + if !valid { + break leftLoop + } } + leftPos-- } - // if there is '@', and the string is like "s://u@h", then hide the "u" part - if sepAtPos != -1 && (schemeSepPos >= 4 && unicode.IsLetter(rune(bs[schemeSepPos-4]))) && sepAtPos-schemeSepPos > 0 && sepEndPos-sepAtPos > 0 { - out = append(out, bs[:schemeSepPos]...) - out = append(out, userPlaceholder...) - out = append(out, bs[sepAtPos:sepEndPos]...) + // left pos should point to the beginning of the left part, this pos is always valid in the buffer + leftPos++ + + // right part is likely to be the host (domain name, ip address) + rightPos := sepAtPos + 1 + rightLoop: + for rightPos < len(bs) { + c := bs[rightPos] + switch c { + case '.', '-': + // valid host char + case '[': + // ipv6 begin + if rightPos != sepAtPos+1 { + break rightLoop + } + case ']': + // ipv6 end + rightPos++ + break rightLoop + default: + valid := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' + if bs[sepAtPos+1] == '[' { + // ipv6 host + valid = 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' || '0' <= c && c <= '9' || c == ':' + } + if !valid { + break rightLoop + } + } + rightPos++ + } + + leading, leftPart, rightPart := bs[:leftPos], bs[leftPos:sepAtPos], bs[sepAtPos+1:rightPos] + + // Either: + // * git log message: "user:pass@host" (it contains a colon in userinfo), ignore "git@host" pattern + // * http like URL: "https://userinfo@host.com" (it has "://" before the userinfo) + needSanitize := bytes.IndexByte(leftPart, ':') >= 0 || bytes.HasSuffix(leading, schemeSep) + needSanitize = needSanitize && len(leftPart) > 0 && len(rightPart) > 0 + // TODO: can also do more checks for right part + // for example: ipv6 quick check + if needSanitize && rightPart[0] == '[' { + needSanitize = rightPart[len(rightPart)-1] == ']' && net.ParseIP(UnsafeBytesToString(rightPart[1:len(rightPart)-1])) != nil + } + if needSanitize { + res = append(res, leading...) + res = append(res, userInfoPlaceholder...) + res = append(res, '@') + res = append(res, rightPart...) } else { - out = append(out, bs[:sepEndPos]...) + res = append(res, bs[:rightPos]...) + } + bs = bs[rightPos:] + sepAtPos = bytes.IndexByte(bs, '@') + if sepAtPos == -1 { + break } - bs = bs[sepEndPos:] - schemeSepPos = bytes.Index(bs, schemeSep) } - out = append(out, bs...) - return UnsafeBytesToString(out) + res = append(res, bs...) + return UnsafeBytesToString(res) } diff --git a/modules/util/sanitize_test.go b/modules/util/sanitize_test.go index 0bcfd45ca4..c1a80016f8 100644 --- a/modules/util/sanitize_test.go +++ b/modules/util/sanitize_test.go @@ -13,7 +13,7 @@ import ( func TestSanitizeErrorCredentialURLs(t *testing.T) { err := errors.New("error with https://a@b.com") se := SanitizeErrorCredentialURLs(err) - assert.Equal(t, "error with https://"+userPlaceholder+"@b.com", se.Error()) + assert.Equal(t, "error with https://"+userInfoPlaceholder+"@b.com", se.Error()) } func TestSanitizeCredentialURLs(t *testing.T) { @@ -27,15 +27,35 @@ func TestSanitizeCredentialURLs(t *testing.T) { }, { "https://mytoken@github.com/go-gitea/test_repo.git", - "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + "https://" + userInfoPlaceholder + "@github.com/go-gitea/test_repo.git", }, { "https://user:password@github.com/go-gitea/test_repo.git", - "https://" + userPlaceholder + "@github.com/go-gitea/test_repo.git", + "https://" + userInfoPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + { + "https://user:password@[::]/go-gitea/test_repo.git", + "https://" + userInfoPlaceholder + "@[::]/go-gitea/test_repo.git", + }, + { + "https://user:password@[2001:db8::1]:8080/go-gitea/test_repo.git", + "https://" + userInfoPlaceholder + "@[2001:db8::1]:8080/go-gitea/test_repo.git", + }, + { + "see https://u:p@[::1]/x and https://u2:p2@h2", + "see https://" + userInfoPlaceholder + "@[::1]/x and https://" + userInfoPlaceholder + "@h2", + }, + { + "https://user:secret@[unclosed-ipv6", + "https://user:secret@[unclosed-ipv6", + }, + { + "https://user:secret@[invalid-ipv6]", + "https://user:secret@[invalid-ipv6]", }, { "ftp://x@", - "ftp://" + userPlaceholder + "@", + "ftp://x@", }, { "ftp://x/@", @@ -43,27 +63,40 @@ func TestSanitizeCredentialURLs(t *testing.T) { }, { "ftp://u@x/@", // test multiple @ chars - "ftp://" + userPlaceholder + "@x/@", + "ftp://" + userInfoPlaceholder + "@x/@", }, { "😊ftp://u@x😊", // test unicode - "😊ftp://" + userPlaceholder + "@x😊", + "😊ftp://" + userInfoPlaceholder + "@x😊", }, { "://@", "://@", }, { - "//u:p@h", // do not process URLs without explicit scheme, they are not treated as "valid" URLs because there is no scheme context in string "//u:p@h", + "//" + userInfoPlaceholder + "@h", }, { - "s://u@h", // the minimal pattern to be sanitized - "s://" + userPlaceholder + "@h", + "s://u@h", + "s://" + userInfoPlaceholder + "@h", }, { "URLs in log https://u:b@h and https://u:b@h:80/, with https://h.com and u@h.com", - "URLs in log https://" + userPlaceholder + "@h and https://" + userPlaceholder + "@h:80/, with https://h.com and u@h.com", + "URLs in log https://" + userInfoPlaceholder + "@h and https://" + userInfoPlaceholder + "@h:80/, with https://h.com and u@h.com", + }, + { + "fatal: unable to look up username:token@github.com (port 9418)", + "fatal: unable to look up " + userInfoPlaceholder + "@github.com (port 9418)", + }, + { + "git failed for user:token@github.com/go-gitea/test_repo.git", + "git failed for " + userInfoPlaceholder + "@github.com/go-gitea/test_repo.git", + }, + { + // SSH-form git URL ("git@host:path") must not let a later credential URL through + "failed remote git@github.com:foo, retried via https://user:tok@github.com/foo", + "failed remote git@github.com:foo, retried via https://" + userInfoPlaceholder + "@github.com/foo", }, } diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index 99f8dba92f..0cdd96496d 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -218,7 +218,7 @@ func migrateRepository(ctx context.Context, doer *user_model.User, downloader ba // We don't actually need to check the OriginalURL as it isn't used anywhere } - log.Trace("migrating git data from %s", repo.CloneURL) + log.Trace("migrating git data from %s", util.SanitizeCredentialURLs(repo.CloneURL)) messenger("repo.migrate.migrating_git") if err = uploader.CreateRepo(ctx, repo, opts); err != nil { return err From deec2b0929c5a7badd74df8bcb767a8cce51e33f Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 28 Apr 2026 23:03:50 +0200 Subject: [PATCH 10/32] Fix compare dropdown for branches without common history (#37470) --- modules/gitrepo/compare_test.go | 32 +++++ modules/gitrepo/merge.go | 6 +- options/locale/locale_en-US.json | 1 + routers/web/repo/compare.go | 197 ++++++++++-------------------- routers/web/repo/pull.go | 31 ++--- services/git/compare.go | 11 +- templates/repo/diff/compare.tmpl | 1 + tests/integration/compare_test.go | 35 ++++++ 8 files changed, 160 insertions(+), 154 deletions(-) diff --git a/modules/gitrepo/compare_test.go b/modules/gitrepo/compare_test.go index 2d2af0934d..91ee32bed5 100644 --- a/modules/gitrepo/compare_test.go +++ b/modules/gitrepo/compare_test.go @@ -4,9 +4,15 @@ package gitrepo import ( + "path/filepath" + "strings" "testing" + "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type mockRepository struct { @@ -17,6 +23,32 @@ func (r *mockRepository) RelativePath() string { return r.path } +func TestMergeBaseNoCommonHistory(t *testing.T) { + repoDir := filepath.Join(t.TempDir(), "repo.git") + require.NoError(t, gitcmd.NewCommand("init").AddDynamicArguments(repoDir).Run(t.Context())) + _, _, runErr := gitcmd.NewCommand("fast-import").WithDir(repoDir).WithStdinBytes([]byte(strings.TrimSpace(` +commit refs/heads/branch1 +committer User 1714310400 +0000 +data 12 +First commit +M 100644 inline file1.txt +data 12 +Hello from 1 + +commit refs/heads/branch2 +committer User 1714310400 +0000 +data 13 +Second commit +M 100644 inline file2.txt +data 12 +Hello from 2 +`))).RunStdString(t.Context()) + require.NoError(t, runErr) + mergeBase, err := MergeBase(t.Context(), &mockRepository{path: repoDir}, "branch1", "branch2") + assert.Empty(t, mergeBase) + assert.ErrorIs(t, err, util.ErrNotExist) +} + func TestRepoGetDivergingCommits(t *testing.T) { repo := &mockRepository{path: "repo1_bare"} do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2") diff --git a/modules/gitrepo/merge.go b/modules/gitrepo/merge.go index 8d58e21c8d..5198392949 100644 --- a/modules/gitrepo/merge.go +++ b/modules/gitrepo/merge.go @@ -9,13 +9,17 @@ import ( "strings" "code.gitea.io/gitea/modules/git/gitcmd" + "code.gitea.io/gitea/modules/util" ) // MergeBase checks and returns merge base of two commits. func MergeBase(ctx context.Context, repo Repository, baseCommitID, headCommitID string) (string, error) { - mergeBase, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base"). + mergeBase, stderr, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base"). AddDashesAndList(baseCommitID, headCommitID)) if err != nil { + if gitcmd.IsErrorExitCode(err, 1) && strings.TrimSpace(stderr) == "" { + return "", util.NewNotExistErrorf("merge-base for %s and %s doesn't exist", baseCommitID, headCommitID) + } return "", fmt.Errorf("get merge-base of %s and %s failed: %w", baseCommitID, headCommitID, err) } return strings.TrimSpace(mergeBase), nil diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 6281ff8f54..2ee983ae18 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -1784,6 +1784,7 @@ "repo.pulls.review_only_possible_for_full_diff": "Review is only possible when viewing the full diff", "repo.pulls.filter_changes_by_commit": "Filter by commit", "repo.pulls.nothing_to_compare": "These branches are equal. There is no need to create a pull request.", + "repo.pulls.no_common_history": "These branches do not share a common merge base. Select a different base or compare branch.", "repo.pulls.nothing_to_compare_have_tag": "The selected branches/tags are equal.", "repo.pulls.nothing_to_compare_and_allow_empty_pr": "These branches are equal. This PR will be empty.", "repo.pulls.has_pull_request": "A pull request between these branches already exists: %[2]s#%[3]d", diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 7598ce561c..f37c9ef2d1 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -189,8 +189,8 @@ func setCsvCompareContext(ctx *context.Context) { } } -// ParseCompareInfo parse compare info between two commit for preparing comparing references -func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { +// parseCompareInfo parse compare info between two commit for preparing comparing references +func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { baseRepo := ctx.Repo.Repository fileOnly := ctx.FormBool("file-only") @@ -199,47 +199,29 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { // remove the check when we support compare with carets if compareReq.BaseOriRefSuffix != "" { - ctx.HTTPError(http.StatusBadRequest, "Unsupported comparison syntax: ref with suffix") - return nil + return nil, util.NewInvalidArgumentErrorf("unsupported comparison syntax: ref with suffix") } // 2 get repository and owner for head headOwner, headRepo, err := common.GetHeadOwnerAndRepo(ctx, baseRepo, compareReq) - switch { - case errors.Is(err, util.ErrInvalidArgument): - ctx.HTTPError(http.StatusBadRequest, err.Error()) - return nil - case errors.Is(err, util.ErrNotExist): - ctx.NotFound(nil) - return nil - case err != nil: - ctx.ServerError("GetHeadOwnerAndRepo", err) - return nil + if err != nil { + return nil, err } - isSameRepo := baseRepo.ID == headRepo.ID - // 3 permission check // base repository's code unit read permission check has been done on web.go permBase := ctx.Repo.Permission // If we're not merging from the same repo: + isSameRepo := baseRepo.ID == headRepo.ID if !isSameRepo { // Assert ctx.Doer has permission to read headRepo's codes permHead, err := access_model.GetDoerRepoPermission(ctx, headRepo, ctx.Doer) if err != nil { - ctx.ServerError("GetDoerRepoPermission", err) - return nil + return nil, err } if !permHead.CanRead(unit.TypeCode) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v", - ctx.Doer, - headRepo, - permHead) - } - ctx.NotFound(nil) - return nil + return nil, util.NewNotExistErrorf("") // permission: no error message for end users } ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode) } @@ -250,24 +232,17 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName) if baseRef == "" { - ctx.NotFound(nil) - return nil + return nil, util.NewNotExistErrorf("no base ref: %s", baseRefName) } - var headGitRepo *git.Repository - if isSameRepo { - headGitRepo = ctx.Repo.GitRepo - } else { - headGitRepo, err = gitrepo.OpenRepository(ctx, headRepo) - if err != nil { - ctx.ServerError("OpenRepository", err) - return nil - } - defer headGitRepo.Close() + headGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, headRepo) + if err != nil { + ctx.ServerError("OpenRepository", err) + return nil, err } + headRef := headGitRepo.UnstableGuessRefByShortName(headRefName) if headRef == "" { - ctx.NotFound(nil) - return nil + return nil, util.NewNotExistErrorf("no head ref: %s", headRefName) } ctx.Data["BaseName"] = baseRepo.OwnerName @@ -291,12 +266,9 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { var rootRepo *repo_model.Repository if baseRepo.IsFork { err = baseRepo.GetBaseRepo(ctx) - if err != nil { - if !repo_model.IsErrRepoNotExist(err) { - ctx.ServerError("Unable to find root repo", err) - return nil - } - } else { + if err != nil && !repo_model.IsErrRepoNotExist(err) { + return nil, err + } else if err == nil { rootRepo = baseRepo.BaseRepo } } @@ -313,42 +285,10 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { } } - has := headRepo != nil - // 3. If the base is a forked from "RootRepo" and the owner of - // the "RootRepo" is the :headUser - set headRepo to that - if !has && rootRepo != nil && rootRepo.OwnerID == headOwner.ID { - headRepo = rootRepo - has = true - } - - // 4. If the ctx.Doer has their own fork of the baseRepo and the headUser is the ctx.Doer - // set the headRepo to the ownFork - if !has && ownForkRepo != nil && ownForkRepo.OwnerID == headOwner.ID { - headRepo = ownForkRepo - has = true - } - - // 5. If the headOwner has a fork of the baseRepo - use that - if !has { - headRepo = repo_model.GetForkedRepo(ctx, headOwner.ID, baseRepo.ID) - has = headRepo != nil - } - - // 6. If the baseRepo is a fork and the headUser has a fork of that use that - if !has && baseRepo.IsFork { - headRepo = repo_model.GetForkedRepo(ctx, headOwner.ID, baseRepo.ForkID) - has = headRepo != nil - } - - // 7. Otherwise if we're not the same repo and haven't found a repo give up - if !isSameRepo && !has { - ctx.Data["PageIsComparePull"] = false - } - ctx.Data["HeadRepo"] = headRepo ctx.Data["BaseCompareRepo"] = ctx.Repo.Repository - // If we have a rootRepo and it's different from: + // If we have a rootRepo, and it's different from: // 1. the computed base // 2. the computed head // then get the branches of it @@ -361,17 +301,15 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) if err != nil { - ctx.ServerError("GetBranchesForRepo", err) - return nil + return nil, err } - ctx.Data["RootRepoBranches"] = branches ctx.Data["RootRepoTags"] = tags } } } - // If we have a ownForkRepo and it's different from: + // If we have a ownForkRepo, and it's different from: // 1. The computed base // 2. The computed head // 3. The rootRepo (if we have one) @@ -386,8 +324,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, ownForkRepo) if err != nil { - ctx.ServerError("GetBranchesForRepo", err) - return nil + return nil, err } ctx.Data["OwnForkRepoBranches"] = branches ctx.Data["OwnForkRepoTags"] = tags @@ -395,33 +332,21 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { } } - // Treat as pull request if both references are branches - if ctx.Data["PageIsComparePull"] == nil { - ctx.Data["PageIsComparePull"] = baseRef.IsBranch() && headRef.IsBranch() && permBase.CanReadIssuesOrPulls(true) - } - - if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v", - ctx.Doer, - baseRepo, - permBase) - } - ctx.NotFound(nil) - return nil - } - compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { - ctx.ServerError("GetCompareInfo", err) - return nil + return nil, err } + + // Treat as pull request if both references are branches + allowCreatePullRequest := baseRef.IsBranch() && headRef.IsBranch() && permBase.CanReadIssuesOrPulls(true) + allowCreatePullRequest = allowCreatePullRequest && compareInfo.MergeBase != "" + ctx.Data["PageIsComparePull"] = allowCreatePullRequest if compareReq.DirectComparison() { ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID } else { ctx.Data["BeforeCommitID"] = compareInfo.MergeBase } - return &compareInfo + return &compareInfo, nil } func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { @@ -454,12 +379,11 @@ func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*g return title, content } -// PrepareCompareDiff renders compare diff page -func PrepareCompareDiff( - ctx *context.Context, - ci *git_service.CompareInfo, - whitespaceBehavior gitcmd.TrustedCmdArgs, -) (nothingToCompare bool) { +// prepareCompareDiff renders compare diff page. TODO: need to refactor it and other "compare diff" related functions together +func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, whitespaceBehavior gitcmd.TrustedCmdArgs) (nothingToCompare bool) { + if ci.MergeBase == "" { + return true + } repo := ctx.Repo.Repository headCommitID := ci.HeadCommitID @@ -568,9 +492,6 @@ func PrepareCompareDiff( ctx.Data["CommitCount"] = len(commits) ctx.Data["title"], ctx.Data["content"] = prepareNewPullRequestTitleContent(ci, commits) - ctx.Data["Username"] = ci.HeadRepo.OwnerName - ctx.Data["Reponame"] = ci.HeadRepo.Name - setCompareContext(ctx, beforeCommit, headCommit, ci.HeadRepo.OwnerName, repo.Name) return false @@ -594,16 +515,24 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor // CompareDiff show different from one commit to another commit func CompareDiff(ctx *context.Context) { - ci := ParseCompareInfo(ctx) + ci, err := parseCompareInfo(ctx) if ctx.Written() { return } + if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) { + ctx.NotFound(nil) + return + } else if err != nil { + ctx.ServerError("ParseCompareInfo", err) + return + } ctx.Data["PageIsViewCode"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["CompareInfo"] = ci - nothingToCompare := PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) + // TODO: need to refactor "prepare compare" related functions together + nothingToCompare := prepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) if ctx.Written() { return } @@ -621,16 +550,13 @@ func CompareDiff(ctx *context.Context) { return } - headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ - RepoID: ci.HeadRepo.ID, - ListOptions: db.ListOptionsAll, - IsDeletedBranch: optional.Some(false), - }) + headBranches, headTags, err := getBranchesAndTagsForRepo(ctx, ci.HeadRepo) if err != nil { - ctx.ServerError("GetBranches", err) + ctx.ServerError("GetBranchesAndTagsForRepo", err) return } ctx.Data["HeadBranches"] = headBranches + ctx.Data["HeadTags"] = headTags // For compare repo branches PrepareBranchList(ctx) @@ -638,13 +564,20 @@ func CompareDiff(ctx *context.Context) { return } - headTags, err := repo_model.GetTagNamesByRepoID(ctx, ci.HeadRepo.ID) - if err != nil { - ctx.ServerError("GetTagNamesByRepoID", err) - return + if ci.MergeBase != "" { + prepareCreatePullRequestPage(ctx, ci, nothingToCompare) + if ctx.Written() { + return + } + } else { + ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) + ctx.Data["PageIsComparePull"] = false + ctx.Data["CommitCount"] = 0 } - ctx.Data["HeadTags"] = headTags + ctx.HTML(http.StatusOK, tplCompare) +} +func prepareCreatePullRequestPage(ctx *context.Context, ci *git_service.CompareInfo, nothingToCompare bool) { if ctx.Data["PageIsComparePull"] == true { pr, err := issues_model.GetUnmergedPullRequest(ctx, ci.HeadRepo.ID, ctx.Repo.Repository.ID, ci.HeadRef.ShortName(), ci.BaseRef.ShortName(), issues_model.PullRequestFlowGithub) if err != nil { @@ -685,7 +618,7 @@ func CompareDiff(ctx *context.Context) { if content, ok := ctx.Data["content"].(string); ok && content != "" { // If a template content is set, prepend the "content". In this case that's only // applicable if you have one commit to compare and that commit has a message. - // In that case the commit message will be prepend to the template body. + // In that case the commit message will be prepended to the template body. if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" { // Re-use the same key as that's prioritized over the "content" key. // Add two new lines between the content to ensure there's always at least @@ -713,14 +646,8 @@ func CompareDiff(ctx *context.Context) { ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWrite(unit.TypePullRequests) - if unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypePullRequests); err == nil { - config := unit.PullRequestsConfig() - ctx.Data["AllowMaintainerEdit"] = config.DefaultAllowMaintainerEdit - } else { - ctx.Data["AllowMaintainerEdit"] = false - } - - ctx.HTML(http.StatusOK, tplCompare) + prConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + ctx.Data["AllowMaintainerEdit"] = prConfig.DefaultAllowMaintainerEdit } // attachCommentsToLines attaches comments to their corresponding diff lines diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 7b31a26d6f..805fa6f419 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1281,24 +1281,26 @@ func PullsNewRedirect(ctx *context.Context) { // CompareAndPullRequestPost response for creating pull request func CompareAndPullRequestPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CreateIssueForm) - ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") - ctx.Data["PageIsComparePull"] = true - ctx.Data["IsDiffCompare"] = true - ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes - ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled - upload.AddUploadContext(ctx, "comment") - ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWrite(unit.TypePullRequests) + repo := ctx.Repo.Repository - var ( - repo = ctx.Repo.Repository - attachments []string - ) - - ci := ParseCompareInfo(ctx) + ci, err := parseCompareInfo(ctx) if ctx.Written() { return } - + if errors.Is(err, util.ErrNotExist) { + ctx.JSONErrorNotFound() + return + } else if errors.Is(err, util.ErrInvalidArgument) { + ctx.JSONError(err.Error()) + return + } else if err != nil { + ctx.ServerError("ParseCompareInfo", err) + return + } + if ci.MergeBase == "" { + ctx.JSONError(ctx.Tr("repo.pulls.no_common_history")) + return + } validateRet := ValidateRepoMetasForNewIssue(ctx, *form, true) if ctx.Written() { return @@ -1306,6 +1308,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { labelIDs, assigneeIDs, milestoneID, projectID := validateRet.LabelIDs, validateRet.AssigneeIDs, validateRet.MilestoneID, validateRet.ProjectID + var attachments []string if setting.Attachment.Enabled { attachments = form.Files } diff --git a/services/git/compare.go b/services/git/compare.go index a8c2980112..40a0eaac08 100644 --- a/services/git/compare.go +++ b/services/git/compare.go @@ -45,6 +45,7 @@ func (ci *CompareInfo) DirectComparison() bool { // GetCompareInfo generates and returns compare information between base and head branches of repositories. // It does its best to fill the fields as many as it can. +// MergeBase can be empty if the base and head are unrelated. func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Repository, headGitRepo *git.Repository, baseRef, headRef git.RefName, directComparison, fileOnly bool) (compareInfo CompareInfo, err error) { baseCommitID, err1 := gitrepo.GetFullCommitID(ctx, baseRepo, baseRef.String()) headCommitID, err2 := gitrepo.GetFullCommitID(ctx, headRepo, headRef.String()) @@ -75,13 +76,17 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito if !directComparison { compareInfo.MergeBase, err = gitrepo.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID) - if err != nil { + if err != nil && !errors.Is(err, util.ErrNotExist) { return compareInfo, fmt.Errorf("MergeBase: %w", err) } } else { compareInfo.MergeBase = compareInfo.BaseCommitID } + if compareInfo.MergeBase == "" { + return compareInfo, nil + } + // We have a common base - therefore we know that ... should work if !fileOnly { // In git log/rev-list, the "..." syntax represents the symmetric difference between two references, @@ -92,12 +97,10 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito if err != nil { return compareInfo, fmt.Errorf("ShowPrettyFormatLogToList: %w", err) } - } else { - compareInfo.Commits = []*git.Commit{} } // Count number of changed files. - // This probably should be removed as we need to use shortstat elsewhere + // TODO: This probably should be removed as we need to use shortstat elsewhere // Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly compareInfo.NumFiles, err = headGitRepo.GetDiffNumChangedFiles(compareInfo.BaseCommitID, compareInfo.HeadCommitID, directComparison) return compareInfo, err diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index afd44f26a4..9ed4b73174 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -13,6 +13,7 @@ {{ctx.Locale.Tr "action.compare_commits_general"}} {{end}} + {{template "base/alert" .}} {{$BaseCompareName := $.Repository.FullName -}} {{$HeadCompareName := $.HeadRepo.FullName -}} {{$OwnForkCompareName := "" -}} diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 3f0034c86b..5e50fcf043 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -10,13 +10,16 @@ import ( "strings" "testing" + 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/git/gitcmd" "code.gitea.io/gitea/modules/test" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCompareTag(t *testing.T) { @@ -124,6 +127,38 @@ func TestCompareBranches(t *testing.T) { inspectCompare(t, htmlDoc, diffCount, diffChanges) } +func TestCompareBranchesNoCommonMergeBase(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user2.ID, Name: "repo1"}) + + repoPath := repo_model.RepoPath(user2.Name, repo1.Name) + _, _, runErr := gitcmd.NewCommand("fast-import").WithDir(repoPath).WithStdinBytes([]byte(strings.TrimSpace(` +commit refs/heads/unrelated-history +committer User 1714310400 +0000 +data 13 +Second commit +M 100644 inline file2.txt +data 12 +Hello from 2 +`))).RunStdString(t.Context()) + require.NoError(t, runErr) + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo1/compare/master...unrelated-history") + resp := session.MakeRequest(t, req, http.StatusOK) + body := resp.Body.String() + htmlDoc := NewHTMLParser(t, resp.Body) + + selection := htmlDoc.doc.Find(".ui.dropdown.select-branch") + assert.Lenf(t, selection.Nodes, 2, "The template has changed") + assert.Contains(t, body, "These branches do not share a common merge base") + assert.Equal(t, 1, htmlDoc.doc.Find(`a.item[href="/user2/repo1/compare/master...unrelated-history"]`).Length()) + assert.Equal(t, 1, htmlDoc.doc.Find(`a.item[href="/user2/repo1/compare/master...master"]`).Length()) + assert.Equal(t, 0, htmlDoc.doc.Find(".pullrequest-form").Length()) +} + func TestCompareCodeExpand(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) From 0ba862cb9779a6c720f5031c4838427ddf90f86f Mon Sep 17 00:00:00 2001 From: 0xGREG <28388707+0xGREG@users.noreply.github.com> Date: Tue, 28 Apr 2026 23:33:20 +0200 Subject: [PATCH 11/32] Add DEFAULT_TITLE_SOURCE setting for pull request title default behavior (#37465) Adds a new `DEFAULT_TITLE_SOURCE` option under `[repository.pull-request]` with three values: - `first-commit` (default): uses the oldest commit summary, current behavior since v1.26 - `auto`: normalizes branch name as title for multi-commit PRs (just like GitHub), use commit summary for single-commit PRs Closes: #37463 Co-authored-by: silverwind Co-authored-by: Claude (Opus 4.7) Co-authored-by: wxiaoguang Co-authored-by: Giteabot Co-authored-by: Nicolas --- custom/conf/app.example.ini | 5 +++ modules/setting/repository.go | 9 +++++ routers/web/repo/compare.go | 45 ++++++++++++++++++++-- routers/web/repo/compare_test.go | 66 ++++++++++++++++++++++++-------- 4 files changed, 106 insertions(+), 19 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 97af5fa5fb..4245957191 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1169,6 +1169,11 @@ LEVEL = Info ;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo. ;RETARGET_CHILDREN_ON_MERGE = true ;; +;; Default source for the pull request title when opening a new PR. +;; "first-commit" uses the oldest commit's summary. +;; "auto" uses commit's summary if the PR only has one commit, normalizes the branch name if multiple commits. +;DEFAULT_TITLE_SOURCE = first-commit +;; ;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated. ;; Use "-1" to always check all pull requests (old behavior). Use "0" to always delay the checks. ;DELAY_CHECK_FOR_INACTIVE_DAYS = 7 diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 9195b7ee50..a8bc91c089 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -18,6 +18,12 @@ const ( RepoCreatingPublic = "public" ) +// enumerates the values for [repository.pull-request] DEFAULT_TITLE_SOURCE +const ( + RepoPRTitleSourceFirstCommit = "first-commit" + RepoPRTitleSourceAuto = "auto" +) + // ItemsPerPage maximum items per page in forks, watchers and stars of a repo const ItemsPerPage = 40 @@ -89,6 +95,7 @@ var ( RetargetChildrenOnMerge bool DelayCheckForInactiveDays int DefaultDeleteBranchAfterMerge bool + DefaultTitleSource string } `ini:"repository.pull-request"` // Issue Setting @@ -213,6 +220,7 @@ var ( RetargetChildrenOnMerge bool DelayCheckForInactiveDays int DefaultDeleteBranchAfterMerge bool + DefaultTitleSource string }{ WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, // Same as GitHub. See @@ -229,6 +237,7 @@ var ( AddCoCommitterTrailers: true, RetargetChildrenOnMerge: true, DelayCheckForInactiveDays: 7, + DefaultTitleSource: RepoPRTitleSourceFirstCommit, }, // Issue settings diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index f37c9ef2d1..174cb1be22 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -13,6 +13,7 @@ import ( "path/filepath" "sort" "strings" + "unicode" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -349,13 +350,46 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { return &compareInfo, nil } -func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses) (title, content string) { - title = ci.HeadRef.ShortName() +// autoTitleFromBranchName humanizes a branch name into a PR title. +func autoTitleFromBranchName(name string) string { + var buf strings.Builder + var prevIsSpace bool + runes := []rune(name) + for i, r := range runes { + isSpace := unicode.IsSpace(r) + if r == '-' || r == '_' || isSpace { + if !prevIsSpace { + buf.WriteRune(' ') + } + prevIsSpace = true + continue + } + if !prevIsSpace && unicode.IsUpper(r) { + needSpace := i > 0 && unicode.IsLower(runes[i-1]) || i < len(runes)-1 && unicode.IsLower(runes[i+1]) + if needSpace { + buf.WriteRune(' ') + } + } + buf.WriteRune(unicode.ToLower(r)) + prevIsSpace = isSpace + } + out := strings.TrimSpace(buf.String()) + if out == "" { + return out + } + outRunes := []rune(out) + outRunes[0] = unicode.ToUpper(outRunes[0]) + return string(outRunes) +} - if len(commits) > 0 { +func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*git_model.SignCommitWithStatuses, defaultTitleSource string) (title, content string) { + useFirstCommitAsTitle := len(commits) == 1 || (defaultTitleSource == setting.RepoPRTitleSourceFirstCommit && len(commits) > 0) + if useFirstCommitAsTitle { // the "commits" are from "ShowPrettyFormatLogToList", which is ordered from newest to oldest, here take the oldest one c := commits[len(commits)-1] title = strings.TrimSpace(c.UserCommit.Summary()) + } else { + title = autoTitleFromBranchName(ci.HeadRef.ShortName()) } if len(commits) == 1 { @@ -491,7 +525,10 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white ctx.Data["Commits"] = commits ctx.Data["CommitCount"] = len(commits) - ctx.Data["title"], ctx.Data["content"] = prepareNewPullRequestTitleContent(ci, commits) + ctx.Data["title"], ctx.Data["content"] = prepareNewPullRequestTitleContent(ci, commits, setting.Repository.PullRequest.DefaultTitleSource) + ctx.Data["Username"] = ci.HeadRepo.OwnerName + ctx.Data["Reponame"] = ci.HeadRepo.Name + setCompareContext(ctx, beforeCommit, headCommit, ci.HeadRepo.OwnerName, repo.Name) return false diff --git a/routers/web/repo/compare_test.go b/routers/web/repo/compare_test.go index 700aba8821..63b0f287e5 100644 --- a/routers/web/repo/compare_test.go +++ b/routers/web/repo/compare_test.go @@ -13,6 +13,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/setting" git_service "code.gitea.io/gitea/services/git" "code.gitea.io/gitea/services/gitdiff" @@ -61,31 +62,66 @@ func TestNewPullRequestTitleContent(t *testing.T) { } } - title, content := prepareNewPullRequestTitleContent(ci, nil) - assert.Equal(t, "head-branch", title) + // no commit + title, content := prepareNewPullRequestTitleContent(ci, nil, setting.RepoPRTitleSourceAuto) + assert.Equal(t, "Head branch", title) assert.Empty(t, content) - title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("title-only")}) - assert.Equal(t, "title-only", title) + title, content = prepareNewPullRequestTitleContent(ci, nil, setting.RepoPRTitleSourceFirstCommit) + assert.Equal(t, "Head branch", title) assert.Empty(t, content) - title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("title-" + strings.Repeat("a", 255))}) - assert.Equal(t, "title-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…", title) - assert.Equal(t, "…aaaaaaaaa\n", content) - - title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("title\nbody")}) - assert.Equal(t, "title", title) + // single commit + title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("single-commit-title\nbody")}, setting.RepoPRTitleSourceAuto) + assert.Equal(t, "single-commit-title", title) assert.Equal(t, "body", content) - title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("a\xf0\xf0\xf0\nb\xf0\xf0\xf0")}) - assert.Equal(t, "a?", title) // FIXME: GIT-COMMIT-MESSAGE-ENCODING: "title" doesn't use the same charset converting logic as "content" - assert.Equal(t, "b"+string(utf8.RuneError)+string(utf8.RuneError), content) + title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("single-commit-title\nbody")}, setting.RepoPRTitleSourceFirstCommit) + assert.Equal(t, "single-commit-title", title) + assert.Equal(t, "body", content) - title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{ + // multiple commits + commits := []*git_model.SignCommitWithStatuses{ // ordered from newest to oldest mockCommit("title2\nbody2"), mockCommit("title1\nbody1"), - }) + } + title, content = prepareNewPullRequestTitleContent(ci, commits, setting.RepoPRTitleSourceAuto) + assert.Equal(t, "Head branch", title) + assert.Empty(t, content) + + title, content = prepareNewPullRequestTitleContent(ci, commits, setting.RepoPRTitleSourceFirstCommit) assert.Equal(t, "title1", title) assert.Empty(t, content) + + // title string handling + title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("title-" + strings.Repeat("a", 255))}, setting.RepoPRTitleSourceFirstCommit) + assert.Equal(t, "title-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…", title) + assert.Equal(t, "…aaaaaaaaa\n", content) + + title, content = prepareNewPullRequestTitleContent(ci, []*git_model.SignCommitWithStatuses{mockCommit("a\xf0\xf0\xf0\nb\xf0\xf0\xf0")}, setting.RepoPRTitleSourceFirstCommit) + assert.Equal(t, "a?", title) // FIXME: GIT-COMMIT-MESSAGE-ENCODING: "title" doesn't use the same charset converting logic as "content" + assert.Equal(t, "b"+string(utf8.RuneError)+string(utf8.RuneError), content) +} + +func TestAutoTitleFromBranchName(t *testing.T) { + cases := []struct { + branch string + want string + }{ + {"fix/the-bug", "Fix/the bug"}, + {"Already-Capitalized", "Already capitalized"}, + {"ALL-CAPS-BRANCH", "All caps branch"}, + {"FixHTMLBug", "Fix html bug"}, + {"MixedCase-Name", "Mixed case name"}, + {"fooBar-baz", "Foo bar baz"}, + {"foo/BAR", "Foo/bar"}, + {"_leading-underscore", "Leading underscore"}, + {"CamelCase", "Camel case"}, + {"foo--double-dash", "Foo double dash"}, + {"123-fix", "123 fix"}, + } + for _, c := range cases { + assert.Equal(t, c.want, autoTitleFromBranchName(c.branch), "branch: %q", c.branch) + } } From 18762c7748b15fceed619c822942c6b3546e3d48 Mon Sep 17 00:00:00 2001 From: Myers Carpenter Date: Wed, 29 Apr 2026 04:39:43 -0400 Subject: [PATCH 12/32] Batch-load related data in actions run, job, and task API endpoints (#37032) Avoid per-item DB queries in ListRuns, ListJobs, and ListActionTasks by batch-loading trigger users, repositories, and task attributes before the conversion loop. Remove ReferencesGitRepo from the /actions route group since no task/run endpoints use it. Added tests for these endpoints as well. --------- Signed-off-by: wxiaoguang Co-authored-by: wxiaoguang Co-authored-by: silverwind Co-authored-by: Claude (Opus 4.7) --- models/actions/run.go | 27 ++--- models/actions/run_attempt.go | 4 - models/actions/run_job.go | 4 - models/actions/run_job_list.go | 4 +- models/actions/run_list.go | 43 ++++--- models/actions/task.go | 3 - models/repo/repo.go | 5 +- routers/api/v1/api.go | 2 +- routers/api/v1/repo/action.go | 15 ++- routers/api/v1/shared/action.go | 48 +++++--- services/actions/notifier.go | 3 +- services/convert/action_test.go | 4 +- services/convert/convert.go | 45 ++++--- services/webhook/notifier.go | 3 +- tests/integration/api_actions_run_test.go | 139 +++++++++++++++------- 15 files changed, 214 insertions(+), 135 deletions(-) diff --git a/models/actions/run.go b/models/actions/run.go index b4f4d00171..a44b0ff343 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -71,11 +71,11 @@ func init() { db.RegisterModel(new(ActionRunIndex)) } -func (run *ActionRun) HTMLURL() string { +func (run *ActionRun) HTMLURL(ctxOpt ...context.Context) string { if run.Repo == nil { return "" } - return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(), run.ID) + return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(ctxOpt...), run.ID) } func (run *ActionRun) Link() string { @@ -120,11 +120,7 @@ func (run *ActionRun) RefTooltip() string { } // LoadAttributes load Repo TriggerUser if not loaded -func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) { - if run == nil { - return nil - } - +func (run *ActionRun) LoadAttributes(ctx context.Context) error { if err := run.LoadRepo(ctx); err != nil { return err } @@ -133,18 +129,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) { return err } - if run.TriggerUser == nil { - run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID) - if err != nil { - return err - } - } + return run.LoadTriggerUser(ctx) +} - return nil +func (run *ActionRun) LoadTriggerUser(ctx context.Context) (err error) { + if run.TriggerUser != nil { + return nil + } + run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID) + return err } func (run *ActionRun) LoadRepo(ctx context.Context) error { - if run == nil || run.Repo != nil { + if run.Repo != nil { return nil } diff --git a/models/actions/run_attempt.go b/models/actions/run_attempt.go index 8ef2ddf00a..857247b068 100644 --- a/models/actions/run_attempt.go +++ b/models/actions/run_attempt.go @@ -51,10 +51,6 @@ func (attempt *ActionRunAttempt) Duration() time.Duration { } func (attempt *ActionRunAttempt) LoadAttributes(ctx context.Context) (err error) { - if attempt == nil { - return nil - } - if attempt.Run == nil { run, err := GetRunByRepoAndID(ctx, attempt.RepoID, attempt.RunID) if err != nil { diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 0921329997..f0d41ef4b4 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -120,10 +120,6 @@ func (job *ActionRunJob) LoadRepo(ctx context.Context) error { // LoadAttributes load Run if not loaded func (job *ActionRunJob) LoadAttributes(ctx context.Context) error { - if job == nil { - return nil - } - if err := job.LoadRun(ctx); err != nil { return err } diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index e06b6beb9e..db7554593d 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -56,8 +56,10 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error { return err } for _, j := range jobs { - if j.RunID > 0 && j.Run == nil { + if j.Run == nil { j.Run = runs[j.RunID] + } + if j.Run != nil { j.Run.Repo = j.Repo } } diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 82dc97f3e5..0a0840648d 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -7,6 +7,7 @@ import ( "context" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/translation" @@ -17,27 +18,39 @@ import ( type RunList []*ActionRun -// GetUserIDs returns a slice of user's id -func (runs RunList) GetUserIDs() []int64 { - return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { - return run.TriggerUserID, true - }) -} - func (runs RunList) LoadTriggerUser(ctx context.Context) error { - userIDs := runs.GetUserIDs() + userIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { + return run.TriggerUserID, run.TriggerUser == nil + }) users := make(map[int64]*user_model.User, len(userIDs)) if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { return err } for _, run := range runs { - if run.TriggerUserID == user_model.ActionsUserID { - run.TriggerUser = user_model.NewActionsUser() - } else { - run.TriggerUser = users[run.TriggerUserID] - if run.TriggerUser == nil { - run.TriggerUser = user_model.NewGhostUser() - } + if run.TriggerUser != nil { + continue + } + run.TriggerUser = users[run.TriggerUserID] + if run.TriggerUserID < 0 { + run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, run.TriggerUserID) + } else if run.TriggerUser == nil { + run.TriggerUserID, run.TriggerUser, _ = user_model.GetPossibleUserByID(ctx, user_model.GhostUserID) + } + } + return nil +} + +func (runs RunList) LoadRepos(ctx context.Context) error { + repoIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { + return run.RepoID, run.Repo == nil + }) + repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs) + if err != nil { + return err + } + for _, run := range runs { + if run.Repo == nil { + run.Repo = repos[run.RepoID] } } return nil diff --git a/models/actions/task.go b/models/actions/task.go index 016f91a7bb..7a97eadc79 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -125,9 +125,6 @@ func (task *ActionTask) LoadJob(ctx context.Context) error { // LoadAttributes load Job Steps if not loaded func (task *ActionTask) LoadAttributes(ctx context.Context) error { - if task == nil { - return nil - } if err := task.LoadJob(ctx); err != nil { return err } diff --git a/models/repo/repo.go b/models/repo/repo.go index 25207cc28b..7814bb4876 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -376,8 +376,9 @@ func (repo *Repository) CommitLink(commitID string) (result string) { } // APIURL returns the repository API URL -func (repo *Repository) APIURL() string { - return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name) +func (repo *Repository) APIURL(ctxOpt ...context.Context) string { + ctx := util.OptionalArg(ctxOpt, context.TODO()) + return httplib.MakeAbsoluteURL(ctx, setting.AppSubURL+"/api/v1/repos/"+url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name)) } // GetCommitsCountCacheKey returns cache key used for commits count caching. diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 633aa77430..a8bfa0965e 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1272,7 +1272,7 @@ func Routes() *web.Router { m.Delete("", reqRepoWriter(unit.TypeActions), repo.DeleteArtifact) }) m.Get("/artifacts/{artifact_id}/zip", repo.DownloadArtifact) - }, reqRepoReader(unit.TypeActions), context.ReferencesGitRepo(true)) + }, reqRepoReader(unit.TypeActions)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 8a0be250da..1d38cc2f53 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -848,6 +848,12 @@ func ListActionTasks(ctx *context.APIContext) { res := new(api.ActionTaskResponse) res.TotalCount = total + taskList := actions_model.TaskList(tasks) + if err := taskList.LoadAttributes(ctx); err != nil { + ctx.APIErrorInternal(err) + return + } + res.Entries = make([]*api.ActionTask, len(tasks)) for i := range tasks { convertedTask, err := convert.ToActionTask(ctx, tasks[i]) @@ -859,7 +865,7 @@ func ListActionTasks(ctx *context.APIContext) { } ctx.SetLinkHeader(total, listOptions.PageSize) - ctx.SetTotalCountHeader(total) // Duplicates api response field but it's better to set it for consistency + ctx.SetTotalCountHeader(total) // Duplicates api response field, but it's better to set it for consistency ctx.JSON(http.StatusOK, &res) } @@ -1155,6 +1161,7 @@ func getCurrentRepoActionRunByID(ctx *context.APIContext) *actions_model.ActionR ctx.APIErrorInternal(err) return nil } + run.Repo = ctx.Repo.Repository return run } @@ -1226,7 +1233,7 @@ func GetWorkflowRun(ctx *context.APIContext) { return } - convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, nil) + convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil) if err != nil { ctx.APIErrorInternal(err) return @@ -1275,7 +1282,7 @@ func GetWorkflowRunAttempt(ctx *context.APIContext) { return } - convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, attempt) + convertedRun, err := convert.ToActionWorkflowRun(ctx, run, attempt) if err != nil { ctx.APIErrorInternal(err) return @@ -1330,7 +1337,7 @@ func RerunWorkflowRun(ctx *context.APIContext) { return } - convertedRun, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, run, nil) + convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/shared/action.go b/routers/api/v1/shared/action.go index 1b12023d7a..6f0c024843 100644 --- a/routers/api/v1/shared/action.go +++ b/routers/api/v1/shared/action.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" @@ -62,6 +63,12 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64, runAttemptI res := new(api.ActionWorkflowJobsResponse) res.TotalCount = total + jobList := actions_model.ActionJobList(jobs) + if err := jobList.LoadAttributes(ctx, true); err != nil { + ctx.APIErrorInternal(err) + return + } + res.Entries = make([]*api.ActionWorkflowJob, len(jobs)) isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID @@ -70,11 +77,11 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64, runAttemptI if isRepoLevel { repository = ctx.Repo.Repository } else { - repository, err = repo_model.GetRepositoryByID(ctx, jobs[i].RepoID) - if err != nil { - ctx.APIErrorInternal(err) + if jobs[i].Run == nil || jobs[i].Run.Repo == nil { + ctx.APIErrorInternal(fmt.Errorf("job %d is missing its run or repository", jobs[i].ID)) return } + repository = jobs[i].Run.Repo } convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, repository, nil, jobs[i]) @@ -169,21 +176,28 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) { res := new(api.ActionWorkflowRunsResponse) res.TotalCount = total - res.Entries = make([]*api.ActionWorkflowRun, len(runs)) - isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID - for i := range runs { - var repository *repo_model.Repository - if isRepoLevel { - repository = ctx.Repo.Repository - } else { - repository, err = repo_model.GetRepositoryByID(ctx, runs[i].RepoID) - if err != nil { - ctx.APIErrorInternal(err) - return - } - } + runList := actions_model.RunList(runs) + if err := runList.LoadTriggerUser(ctx); err != nil { + ctx.APIErrorInternal(err) + return + } - convertedRun, err := convert.ToActionWorkflowRun(ctx, repository, runs[i], nil) + if err := runList.LoadRepos(ctx); err != nil { + ctx.APIErrorInternal(err) + return + } + repos := repo_model.RepositoryList(container.FilterSlice(runs, func(r *actions_model.ActionRun) (*repo_model.Repository, bool) { + return r.Repo, r.Repo != nil + })) + if err := repos.LoadOwners(ctx); err != nil { + ctx.APIErrorInternal(err) + return + } + + res.Entries = make([]*api.ActionWorkflowRun, len(runs)) + for i := range runs { + // TODO: load run attempts in batch + convertedRun, err := convert.ToActionWorkflowRun(ctx, runs[i], nil) if err != nil { ctx.APIErrorInternal(err) return diff --git a/services/actions/notifier.go b/services/actions/notifier.go index c3b2003b3c..4b2e87afad 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -815,7 +815,8 @@ func (n *actionsNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *rep log.Error("GetActionWorkflow: %v", err) return } - convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil) + run.Repo = repo + convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil) if err != nil { log.Error("ToActionWorkflowRun: %v", err) return diff --git a/services/convert/action_test.go b/services/convert/action_test.go index 9ecb4a2ca6..9efc0e36a8 100644 --- a/services/convert/action_test.go +++ b/services/convert/action_test.go @@ -115,12 +115,12 @@ func TestToActionWorkflowRun_UsesTriggerEvent(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 803}) - + run.Repo = repo // Scheduled runs keep Event as the registration event (push) and use TriggerEvent as the real trigger. run.Event = "push" run.TriggerEvent = "schedule" - apiRun, err := ToActionWorkflowRun(t.Context(), repo, run, nil) + apiRun, err := ToActionWorkflowRun(t.Context(), run, nil) require.NoError(t, err) assert.Equal(t, "schedule", apiRun.Event) } diff --git a/services/convert/convert.go b/services/convert/convert.go index 29f46d37ad..b254d29785 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -222,14 +223,18 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag { } } -// ToActionTask convert a actions_model.ActionTask to an api.ActionTask +// ToActionTask convert an actions_model.ActionTask to an api.ActionTask func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) { - if err := t.LoadAttributes(ctx); err != nil { + // don't need Steps here, only need to load job and its run + if err := t.LoadJob(ctx); err != nil { + return nil, err + } + if err := t.Job.LoadRun(ctx); err != nil { + return nil, err + } + if err := t.Job.Run.LoadRepo(ctx); err != nil { return nil, err } - - url := strings.TrimSuffix(setting.AppURL, "/") + t.GetRunLink() - return &api.ActionTask{ ID: t.ID, Name: t.Job.Name, @@ -240,23 +245,25 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action DisplayTitle: t.Job.Run.Title, Status: t.Status.String(), WorkflowID: t.Job.Run.WorkflowID, - URL: url, + URL: httplib.MakeAbsoluteURL(ctx, t.Job.Run.Link()), CreatedAt: t.Created.AsLocalTime(), UpdatedAt: t.Updated.AsLocalTime(), RunStartedAt: t.Started.AsLocalTime(), }, nil } -func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (*api.ActionWorkflowRun, error) { - if err := run.LoadAttributes(ctx); err != nil { +func ToActionWorkflowRun(ctx context.Context, run *actions_model.ActionRun, attempt *actions_model.ActionRunAttempt) (_ *api.ActionWorkflowRun, err error) { + if err := run.LoadRepo(ctx); err != nil { + return nil, err + } + if err := run.LoadTriggerUser(ctx); err != nil { return nil, err } if attempt == nil { - if latestAttempt, has, err := run.GetLatestAttempt(ctx); err != nil { + attempt, _, err = run.GetLatestAttempt(ctx) + if err != nil { return nil, err - } else if has { - attempt = latestAttempt } } @@ -272,6 +279,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run * var previousAttemptURL *string if attempt != nil { + attempt.Run = run if err := attempt.LoadAttributes(ctx); err != nil { return nil, err } @@ -281,16 +289,15 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run * completedAt = attempt.Stopped.AsLocalTime() triggerUser = attempt.TriggerUser if attempt.Attempt > 1 { - url := fmt.Sprintf("%s/actions/runs/%d/attempts/%d", repo.APIURL(), run.ID, attempt.Attempt-1) - previousAttemptURL = &url + previousAttemptURL = new(fmt.Sprintf("%s/actions/runs/%d/attempts/%d", run.Repo.APIURL(ctx), run.ID, attempt.Attempt-1)) } } return &api.ActionWorkflowRun{ ID: run.ID, - URL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), run.ID), + URL: fmt.Sprintf("%s/actions/runs/%d", run.Repo.APIURL(ctx), run.ID), PreviousAttemptURL: previousAttemptURL, - HTMLURL: run.HTMLURL(), + HTMLURL: run.HTMLURL(ctx), RunNumber: run.Index, RunAttempt: runAttempt, StartedAt: startedAt, @@ -302,7 +309,7 @@ func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run * Status: status, Conclusion: conclusion, Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref), - Repository: ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}), + Repository: ToRepo(ctx, run.Repo, access_model.Permission{AccessMode: perm.AccessModeNone}), TriggerActor: ToUser(ctx, triggerUser, nil), Actor: ToUser(ctx, actor, nil), }, nil @@ -400,11 +407,11 @@ func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task return &api.ActionWorkflowJob{ ID: job.ID, // missing api endpoint for this location - URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID), - HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), job.ID), + URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(ctx), job.ID), + HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(ctx), job.ID), RunID: job.RunID, // Missing api endpoint for this location, artifacts are available under a nested url - RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID), + RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(ctx), job.RunID), Name: job.Name, Labels: job.RunsOn, RunAttempt: job.Attempt, diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index d2575e9931..7627935a32 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -1043,7 +1043,8 @@ func (*webhookNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_ return } - convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run, nil) + run.Repo = repo + convertedRun, err := convert.ToActionWorkflowRun(ctx, run, nil) if err != nil { log.Error("ToActionWorkflowRun: %v", err) return diff --git a/tests/integration/api_actions_run_test.go b/tests/integration/api_actions_run_test.go index e2aeef7ec0..22fa6ba2ea 100644 --- a/tests/integration/api_actions_run_test.go +++ b/tests/integration/api_actions_run_test.go @@ -15,17 +15,35 @@ import ( 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/json" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestAPIActionsGetWorkflowRun(t *testing.T) { +func TestAPIActionsWorkflowRun(t *testing.T) { defer prepareTestEnvActionsArtifacts(t)() + t.Run("GetWorkflowRun", testAPIActionsGetWorkflowRun) + t.Run("GetWorkflowJob", testAPIActionsGetWorkflowJob) + t.Run("ListUserWorkflows", testAPIActionsListUserWorkflows) + t.Run("ListRepoWorkflows", testAPIActionsListRepoWorkflows) + t.Run("DeleteRunCheckPermission", testAPIActionsDeleteRunCheckPermission) + t.Run("DeleteRunRunning", testAPIActionsDeleteRunRunning) + t.Run("DeleteRunGeneral", testAPIActionsDeleteRunGeneral) + t.Run("RerunWorkflowRun", func(t *testing.T) { + defer tests.PrepareTestEnv(t)() + testAPIActionsRerunWorkflowRun(t) + }) + t.Run("RerunWorkflowJob", func(t *testing.T) { + defer tests.PrepareTestEnv(t)() + testAPIActionsRerunWorkflowJob(t) + }) +} + +func testAPIActionsGetWorkflowRun(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, user.Name) @@ -56,13 +74,9 @@ func TestAPIActionsGetWorkflowRun(t *testing.T) { }) require.NoError(t, err) - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs", repo.FullName())). - AddTokenAuth(token) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs", repo.FullName())).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - - var jobList api.ActionWorkflowJobsResponse - err = json.Unmarshal(resp.Body.Bytes(), &jobList) - require.NoError(t, err) + jobList := DecodeJSON(t, resp, &api.ActionWorkflowJobsResponse{}) job198Idx := slices.IndexFunc(jobList.Entries, func(job *api.ActionWorkflowJob) bool { return job.ID == 198 }) require.NotEqual(t, -1, job198Idx, "expected to find job 198 in run 795 jobs list") @@ -72,9 +86,7 @@ func TestAPIActionsGetWorkflowRun(t *testing.T) { }) } -func TestAPIActionsGetWorkflowJob(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsGetWorkflowJob(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, user.Name) @@ -91,9 +103,7 @@ func TestAPIActionsGetWorkflowJob(t *testing.T) { MakeRequest(t, req, http.StatusNotFound) } -func TestAPIActionsDeleteRunCheckPermission(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsDeleteRunCheckPermission(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, user.Name) @@ -101,9 +111,7 @@ func TestAPIActionsDeleteRunCheckPermission(t *testing.T) { testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound) } -func TestAPIActionsDeleteRun(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsDeleteRunGeneral(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, user.Name) @@ -118,9 +126,7 @@ func TestAPIActionsDeleteRun(t *testing.T) { testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound) } -func TestAPIActionsDeleteRunRunning(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsDeleteRunRunning(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, user.Name) @@ -138,22 +144,17 @@ func testAPIActionsDeleteRun(t *testing.T, repo *repo_model.Repository, token st } func testAPIActionsDeleteRunListArtifacts(t *testing.T, repo *repo_model.Repository, token string, artifacts int) { - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/artifacts", repo.FullName())). - AddTokenAuth(token) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/artifacts", repo.FullName())).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - var listResp api.ActionArtifactsResponse - err := json.Unmarshal(resp.Body.Bytes(), &listResp) - assert.NoError(t, err) + listResp := DecodeJSON(t, resp, &api.ActionArtifactsResponse{}) assert.Len(t, listResp.Entries, artifacts) } func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository, token string, expected bool) { - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/tasks", repo.FullName())). - AddTokenAuth(token) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/tasks", repo.FullName())).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - var listResp api.ActionTaskResponse - err := json.Unmarshal(resp.Body.Bytes(), &listResp) - assert.NoError(t, err) + listResp := DecodeJSON(t, resp, &api.ActionTaskResponse{}) + findTask1 := false findTask2 := false for _, entry := range listResp.Entries { @@ -170,9 +171,7 @@ func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository, assert.Equal(t, expected, findTask2) } -func TestAPIActionsRerunWorkflowRun(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsRerunWorkflowRun(t *testing.T) { t.Run("NotDone", func(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -192,13 +191,10 @@ func TestAPIActionsRerunWorkflowRun(t *testing.T) { readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) t.Run("Success", func(t *testing.T) { - req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/rerun", repo.FullName())). - AddTokenAuth(writeToken) + req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/rerun", repo.FullName())).AddTokenAuth(writeToken) resp := MakeRequest(t, req, http.StatusCreated) + rerunResp := DecodeJSON(t, resp, &api.ActionWorkflowRun{}) - var rerunResp api.ActionWorkflowRun - err := json.Unmarshal(resp.Body.Bytes(), &rerunResp) - require.NoError(t, err) assert.Equal(t, int64(795), rerunResp.ID) assert.Equal(t, "queued", rerunResp.Status) assert.Equal(t, "c2d72f548424103f01ee1dc02889c1e2bff816b0", rerunResp.HeadSha) @@ -236,9 +232,7 @@ func TestAPIActionsRerunWorkflowRun(t *testing.T) { }) } -func TestAPIActionsRerunWorkflowJob(t *testing.T) { - defer prepareTestEnvActionsArtifacts(t)() - +func testAPIActionsRerunWorkflowJob(t *testing.T) { t.Run("NotDone", func(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -258,13 +252,10 @@ func TestAPIActionsRerunWorkflowJob(t *testing.T) { readToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) t.Run("Success", func(t *testing.T) { - req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs/199/rerun", repo.FullName())). - AddTokenAuth(writeToken) + req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/jobs/199/rerun", repo.FullName())).AddTokenAuth(writeToken) resp := MakeRequest(t, req, http.StatusCreated) + rerunResp := DecodeJSON(t, resp, &api.ActionWorkflowJob{}) - var rerunResp api.ActionWorkflowJob - err := json.Unmarshal(resp.Body.Bytes(), &rerunResp) - require.NoError(t, err) job199Rerun := getLatestAttemptJobByTemplateJobID(t, 795, 199) assert.Equal(t, job199Rerun.ID, rerunResp.ID) assert.Equal(t, "queued", rerunResp.Status) @@ -301,3 +292,59 @@ func TestAPIActionsRerunWorkflowJob(t *testing.T) { MakeRequest(t, req, http.StatusNotFound) }) } + +func testAPIActionsListUserWorkflows(t *testing.T) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) + + t.Run("Runs", func(t *testing.T) { + req := NewRequest(t, "GET", "/api/v1/user/actions/runs").AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + runs := DecodeJSON(t, resp, &api.ActionWorkflowRunsResponse{}) + + assert.Positive(t, runs.TotalCount) + assert.NotEmpty(t, runs.Entries) + + for _, run := range runs.Entries { + assert.NotEmpty(t, run.DisplayTitle, "display_title should be populated") + assert.NotNil(t, run.Repository, "repository should be populated via batch loading") + assert.NotEmpty(t, run.Repository.FullName, "repository full_name should be populated") + assert.NotNil(t, run.TriggerActor, "trigger_actor should be populated via batch loading") + } + }) + + t.Run("Jobs", func(t *testing.T) { + req := NewRequest(t, "GET", "/api/v1/user/actions/jobs").AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + jobs := DecodeJSON(t, resp, &api.ActionWorkflowJobsResponse{}) + + assert.Positive(t, jobs.TotalCount) + assert.NotEmpty(t, jobs.Entries) + + for _, job := range jobs.Entries { + assert.NotEmpty(t, job.Name, "job name should be populated") + assert.NotEmpty(t, job.HTMLURL, "html_url should be populated via batch-loaded repo") + } + }) +} + +func testAPIActionsListRepoWorkflows(t *testing.T) { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs", repo.FullName())).AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + runs := DecodeJSON(t, resp, &api.ActionWorkflowRunsResponse{}) + + assert.Positive(t, runs.TotalCount) + assert.NotEmpty(t, runs.Entries) + + for _, run := range runs.Entries { + assert.NotNil(t, run.Repository, "repository should be populated from ctx.Repo") + assert.Equal(t, repo.FullName(), run.Repository.FullName, "repository full_name should match") + assert.NotNil(t, run.TriggerActor, "trigger_actor should be populated") + } +} From 9e031eb3df8ee857c0048205d583eab9d3b0d611 Mon Sep 17 00:00:00 2001 From: Myers Carpenter Date: Wed, 29 Apr 2026 08:47:52 -0400 Subject: [PATCH 13/32] Serve OpenAPI 3.0 spec at /openapi.v1.json (#37038) Add a build-time conversion step that transforms the existing Swagger 2.0 spec into an OpenAPI 3.0 spec. The OAS3 spec is served alongside the existing Swagger 2.0 spec, enabling API clients that require OAS3 to generate code directly from Gitea's API. This is not to be an answer to how gitea handles OAS3 long term, but a way to use what we have to move a step forward. --------- Signed-off-by: wxiaoguang Co-authored-by: Claude (Opus 4.7) Co-authored-by: silverwind Co-authored-by: wxiaoguang --- .editorconfig | 2 +- Makefile | 20 +- assets/go-licenses.json | 40 + build/generate-openapi.go | 97 + build/openapi3gen/convert.go | 281 + build/openapi3gen/convert_test.go | 170 + build/openapi3gen/enumscan.go | 188 + build/openapi3gen/enumscan_test.go | 239 + go.mod | 8 + go.sum | 18 + models/user/user.go | 1 + modules/structs/admin_user.go | 4 +- modules/structs/org.go | 8 +- modules/structs/org_team.go | 15 +- modules/structs/repo.go | 15 +- modules/structs/repo_collaborator.go | 30 +- modules/structs/user.go | 2 +- modules/structs/visible_type.go | 11 + routers/api/v1/admin/org.go | 2 +- routers/api/v1/admin/user.go | 4 +- routers/api/v1/org/org.go | 4 +- routers/api/v1/org/team.go | 8 +- routers/api/v1/repo/collaborators.go | 2 +- routers/api/v1/repo/repo.go | 2 +- routers/web/swagger_json.go | 7 + routers/web/web.go | 1 + services/convert/convert.go | 4 +- services/convert/repository.go | 2 +- services/convert/user.go | 4 +- services/convert/user_test.go | 4 +- templates/swagger/v1_json.tmpl | 59 +- templates/swagger/v1_openapi3_json.tmpl | 33451 ++++++++++++++++ .../api_helper_for_declarative_test.go | 6 +- tests/integration/api_org_test.go | 8 +- .../integration/api_repo_collaborator_test.go | 16 +- tests/integration/api_repo_teams_test.go | 4 +- tests/integration/api_team_test.go | 32 +- tests/integration/api_user_search_test.go | 4 +- tests/integration/user_test.go | 26 +- 39 files changed, 34700 insertions(+), 99 deletions(-) create mode 100644 build/generate-openapi.go create mode 100644 build/openapi3gen/convert.go create mode 100644 build/openapi3gen/convert_test.go create mode 100644 build/openapi3gen/enumscan.go create mode 100644 build/openapi3gen/enumscan_test.go create mode 100644 templates/swagger/v1_openapi3_json.tmpl diff --git a/.editorconfig b/.editorconfig index bf1cf757cc..703a834818 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,7 +18,7 @@ indent_style = tab [templates/custom/*.tmpl] insert_final_newline = false -[templates/swagger/v1_json.tmpl] +[templates/swagger/*_json.tmpl] indent_style = space insert_final_newline = false diff --git a/Makefile b/Makefile index ae053a8368..f85d042279 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,7 @@ ESLINT_CONCURRENCY ?= 2 SWAGGER_SPEC := templates/swagger/v1_json.tmpl SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json SWAGGER_EXCLUDE := code.gitea.io/sdk +OPENAPI3_SPEC := templates/swagger/v1_openapi3_json.tmpl TEST_MYSQL_HOST ?= mysql:3306 TEST_MYSQL_DBNAME ?= testgitea @@ -233,7 +234,7 @@ TAGS_PREREQ := $(TAGS_EVIDENCE) endif .PHONY: generate-swagger -generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments +generate-swagger: $(SWAGGER_SPEC) $(OPENAPI3_SPEC) ## generate the swagger spec from code comments $(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT) $(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' @@ -255,6 +256,21 @@ swagger-validate: ## check if the swagger spec is valid $(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' @$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath +.PHONY: generate-openapi3 +generate-openapi3: $(OPENAPI3_SPEC) ## generate the OpenAPI 3.0 spec from the Swagger 2.0 spec + +$(OPENAPI3_SPEC): $(SWAGGER_SPEC) build/generate-openapi.go $(wildcard build/openapi3gen/*.go) + $(GO) run build/generate-openapi.go + +.PHONY: openapi3-check +openapi3-check: generate-openapi3 + @diff=$$(git diff --color=always '$(OPENAPI3_SPEC)'); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make generate-openapi3' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + .PHONY: checks checks: checks-frontend checks-backend ## run various consistency checks @@ -262,7 +278,7 @@ checks: checks-frontend checks-backend ## run various consistency checks checks-frontend: lockfile-check svg-check ## check frontend files .PHONY: checks-backend -checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check ## check backend files +checks-backend: tidy-check swagger-check openapi3-check fmt-check swagger-validate security-check ## check backend files .PHONY: lint lint: lint-frontend lint-backend lint-spell ## lint everything diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 61bf76702c..26c7d6a0d8 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -489,6 +489,11 @@ "path": "github.com/fxamacker/cbor/v2/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2019-present Faye Amacker\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." }, + { + "name": "github.com/getkin/kin-openapi", + "path": "github.com/getkin/kin-openapi/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2017-2018 the project authors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/git-lfs/pktline", "path": "github.com/git-lfs/pktline/LICENSE.md", @@ -554,6 +559,16 @@ "path": "github.com/go-ldap/ldap/v3/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2024 go-ldap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, + { + "name": "github.com/go-openapi/jsonpointer", + "path": "github.com/go-openapi/jsonpointer/LICENSE", + "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, + { + "name": "github.com/go-openapi/swag", + "path": "github.com/go-openapi/swag/LICENSE", + "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, { "name": "github.com/go-redsync/redsync/v4", "path": "github.com/go-redsync/redsync/v4/LICENSE", @@ -929,6 +944,11 @@ "path": "github.com/modern-go/reflect2/LICENSE", "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, + { + "name": "github.com/mohae/deepcopy", + "path": "github.com/mohae/deepcopy/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Joel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/mschoch/smat", "path": "github.com/mschoch/smat/LICENSE", @@ -954,6 +974,16 @@ "path": "github.com/nwaples/rardecode/v2/LICENSE", "licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, + { + "name": "github.com/oasdiff/yaml", + "path": "github.com/oasdiff/yaml/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Sam Ghods\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\nCopyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "github.com/oasdiff/yaml3", + "path": "github.com/oasdiff/yaml3/LICENSE", + "licenseText": "\nThis project is covered by two different licenses: MIT and Apache.\n\n#### MIT License ####\n\nThe following files were ported to Go from C files of libyaml, and thus\nare still covered by their original MIT license, with the additional\ncopyright staring in 2011 when the project was ported over:\n\n apic.go emitterc.go parserc.go readerc.go scannerc.go\n writerc.go yamlh.go yamlprivateh.go\n\nCopyright (c) 2006-2010 Kirill Simonov\nCopyright (c) 2006-2011 Kirill Simonov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### Apache License ###\n\nAll the remaining project files are covered by the Apache license:\n\nCopyright (c) 2011-2019 Canonical Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n" + }, { "name": "github.com/olekukonko/cat", "path": "github.com/olekukonko/cat/LICENSE", @@ -994,6 +1024,11 @@ "path": "github.com/opencontainers/image-spec/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n Copyright 2016 The Linux Foundation.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, + { + "name": "github.com/perimeterx/marshmallow", + "path": "github.com/perimeterx/marshmallow/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2022 PerimeterX\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/philhofer/fwd", "path": "github.com/philhofer/fwd/LICENSE.md", @@ -1169,6 +1204,11 @@ "path": "github.com/wneessen/go-mail/smtp/LICENSE", "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." }, + { + "name": "github.com/woodsbury/decimal128", + "path": "github.com/woodsbury/decimal128/LICENCE", + "licenseText": "BSD Zero Clause License\n\nCopyright (c) 2022 Wade Smith\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n" + }, { "name": "github.com/x448/float16", "path": "github.com/x448/float16/LICENSE", diff --git a/build/generate-openapi.go b/build/generate-openapi.go new file mode 100644 index 0000000000..7b37a5bbba --- /dev/null +++ b/build/generate-openapi.go @@ -0,0 +1,97 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// generate-openapi converts Gitea's Swagger 2.0 spec into an OpenAPI 3.0 spec. +// +// Gitea generates a Swagger 2.0 spec from code annotations (make generate-swagger). +// This tool converts it to OAS3 so that SDK generators and tools that require +// OAS3 (e.g. progenitor for Rust) can consume it directly. The conversion also +// deduplicates inline enum definitions into named schema components, producing +// cleaner SDK output with proper enum types instead of anonymous strings. +// +// Run: go run build/generate-openapi.go +// Output: templates/swagger/v1_openapi3_json.tmpl + +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "regexp" + "sort" + "strings" + + "code.gitea.io/gitea/build/openapi3gen" + + "github.com/getkin/kin-openapi/openapi3" +) + +const ( + swaggerSpecPath = "templates/swagger/v1_json.tmpl" + openapi3OutPath = "templates/swagger/v1_openapi3_json.tmpl" + + appSubUrlVar = "{{.SwaggerAppSubUrl}}" + appVerVar = "{{.SwaggerAppVer}}" + + appSubUrlPlaceholder = "GITEA_APP_SUB_URL_PLACEHOLDER" + appVerPlaceholder = "0.0.0-gitea-placeholder" +) + +var ( + appSubUrlRe = regexp.MustCompile(regexp.QuoteMeta(appSubUrlVar)) + appVerRe = regexp.MustCompile(regexp.QuoteMeta(appVerVar)) + + enumScanDirs = []string{ + "modules/structs", + "modules/commitstatus", + } +) + +func main() { + astEnumMap, err := openapi3gen.ScanSwaggerEnumTypes(enumScanDirs) + if err != nil { + log.Fatalf("scanning swagger:enum annotations: %v", err) + } + names := make([]string, 0, len(astEnumMap)) + for _, n := range astEnumMap { + names = append(names, n) + } + sort.Strings(names) + fmt.Fprintf(os.Stderr, "discovered %d swagger:enum types: %s\n", len(names), strings.Join(names, ", ")) + + data, err := os.ReadFile(swaggerSpecPath) + if err != nil { + log.Fatalf("reading swagger spec: %v", err) + } + + cleaned := appSubUrlRe.ReplaceAll(data, []byte(appSubUrlPlaceholder)) + cleaned = appVerRe.ReplaceAll(cleaned, []byte(appVerPlaceholder)) + + oas3, err := openapi3gen.Convert(cleaned, astEnumMap) + if err != nil { + log.Fatalf("converting to openapi 3.0: %v", err) + } + + oas3.Servers = openapi3.Servers{ + {URL: appSubUrlPlaceholder + "/api/v1"}, + } + + out, err := json.MarshalIndent(oas3, "", " ") + if err != nil { + log.Fatalf("marshaling openapi 3.0: %v", err) + } + + result := strings.ReplaceAll(string(out), appSubUrlPlaceholder, appSubUrlVar) + result = strings.ReplaceAll(result, appVerPlaceholder, appVerVar) + result = strings.TrimSpace(result) + + if err := os.WriteFile(openapi3OutPath, []byte(result), 0o644); err != nil { + log.Fatalf("writing openapi 3.0 spec: %v", err) + } + + fmt.Printf("Generated %s\n", openapi3OutPath) +} diff --git a/build/openapi3gen/convert.go b/build/openapi3gen/convert.go new file mode 100644 index 0000000000..04587b3303 --- /dev/null +++ b/build/openapi3gen/convert.go @@ -0,0 +1,281 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "fmt" + "regexp" + "strings" + + "code.gitea.io/gitea/modules/json" + + "github.com/getkin/kin-openapi/openapi2" + "github.com/getkin/kin-openapi/openapi2conv" + "github.com/getkin/kin-openapi/openapi3" +) + +// rxDeprecated matches "deprecated" as a word at the start of a description +// or preceded by whitespace/punctuation that indicates a leading marker (e.g. +// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases +// like "not deprecated" or "previously deprecated, now supported". +var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`) + +// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying +// Gitea-specific post-processing: file-schema fixups, URI formats, +// deprecated flags, and shared-enum extraction. +// +// astEnumMap is a value-set-key → Go-type-name map (built by +// ScanSwaggerEnumTypes). If a shared enum in the spec has no entry in the +// map, Convert returns an error — no fallback naming. +func Convert(swaggerJSON []byte, astEnumMap map[string]string) (*openapi3.T, error) { + var swagger2 openapi2.T + if err := json.Unmarshal(swaggerJSON, &swagger2); err != nil { + return nil, fmt.Errorf("parsing swagger 2.0: %w", err) + } + + oas3, err := openapi2conv.ToV3(&swagger2) + if err != nil { + return nil, fmt.Errorf("converting to openapi 3.0: %w", err) + } + + fixFileSchemas(oas3) + addURIFormats(oas3) + addDeprecatedFlags(oas3) + if err := extractSharedEnums(oas3, astEnumMap); err != nil { + return nil, err + } + return oas3, nil +} + +func fixFileSchemas(doc *openapi3.T) { + for _, pathItem := range doc.Paths.Map() { + for _, op := range []*openapi3.Operation{ + pathItem.Get, pathItem.Post, pathItem.Put, pathItem.Patch, + pathItem.Delete, pathItem.Head, pathItem.Options, pathItem.Trace, + } { + if op == nil { + continue + } + for _, resp := range op.Responses.Map() { + if resp.Value == nil { + continue + } + for _, mediaType := range resp.Value.Content { + fixSchema(mediaType.Schema) + } + } + if op.RequestBody != nil && op.RequestBody.Value != nil { + for _, mediaType := range op.RequestBody.Value.Content { + fixSchema(mediaType.Schema) + } + } + } + } +} + +// fixSchema rewrites any "type: file" schemas to the OAS3 equivalent +// (type: string, format: binary), recursing into Properties, Items, and +// AllOf/OneOf/AnyOf/Not branches. $ref nodes are skipped so shared schemas +// are rewritten exactly once when visited through their declaration. +func fixSchema(ref *openapi3.SchemaRef) { + if ref == nil || ref.Value == nil || ref.Ref != "" { + return + } + s := ref.Value + if s.Type.Is("file") { + s.Type = &openapi3.Types{"string"} + s.Format = "binary" + } + for _, p := range s.Properties { + fixSchema(p) + } + fixSchema(s.Items) + for _, sub := range s.AllOf { + fixSchema(sub) + } + for _, sub := range s.OneOf { + fixSchema(sub) + } + for _, sub := range s.AnyOf { + fixSchema(sub) + } + fixSchema(s.Not) +} + +// addURIFormats sets format: uri on string properties whose names indicate +// they hold URLs. This information is lost in Swagger 2.0 but is valuable +// for code generators. +func addURIFormats(doc *openapi3.T) { + if doc.Components == nil { + return + } + for _, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for propName, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + prop := propRef.Value + if !prop.Type.Is("string") || prop.Format != "" { + continue + } + if isURLProperty(propName) { + prop.Format = "uri" + } + } + } +} + +func isURLProperty(name string) bool { + if strings.HasSuffix(name, "_url") { + return true + } + switch name { + case "url", "html_url", "clone_url": + return true + } + return false +} + +// addDeprecatedFlags sets deprecated: true on schema properties whose +// description starts with a "deprecated" marker (e.g. "Deprecated: true" +// or "deprecated (use X instead)"). Does not match negated phrases. +func addDeprecatedFlags(doc *openapi3.T) { + if doc.Components == nil { + return + } + for _, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for _, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + if rxDeprecated.MatchString(propRef.Value.Description) { + propRef.Value.Deprecated = true + } + } + } +} + +type enumUsage struct { + schemaName string + propName string + propRef *openapi3.SchemaRef + inItems bool +} + +// extractSharedEnums finds identical enum arrays used by multiple schema +// properties, creates a standalone named schema for each, and replaces +// the inline enums with $ref pointers. +// +// If the derived enum name collides with an existing component schema, or +// no // swagger:enum annotation matches the value set, generation aborts +// with an actionable error — there are no silent fallbacks. +func extractSharedEnums(doc *openapi3.T, astEnumMap map[string]string) error { + if doc.Components == nil { + return nil + } + + enumGroups := map[string][]enumUsage{} + + for schemaName, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for propName, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + if len(propRef.Value.Enum) > 1 && propRef.Value.Type.Is("string") { + key := EnumKey(propRef.Value.Enum) + enumGroups[key] = append(enumGroups[key], enumUsage{schemaName, propName, propRef, false}) + } + if propRef.Value.Type.Is("array") && propRef.Value.Items != nil && + propRef.Value.Items.Value != nil && propRef.Value.Items.Ref == "" && + len(propRef.Value.Items.Value.Enum) > 1 && propRef.Value.Items.Value.Type.Is("string") { + key := EnumKey(propRef.Value.Items.Value.Enum) + enumGroups[key] = append(enumGroups[key], enumUsage{schemaName, propName, propRef, true}) + } + } + } + + for key, usages := range enumGroups { + if len(usages) < 2 { + continue + } + + enumName, err := deriveEnumName(key, usages, astEnumMap) + if err != nil { + return err + } + if _, exists := doc.Components.Schemas[enumName]; exists { + return fmt.Errorf("enum name collision: %s already exists as a component schema", enumName) + } + + var enumValues []any + if usages[0].inItems { + enumValues = usages[0].propRef.Value.Items.Value.Enum + } else { + enumValues = usages[0].propRef.Value.Enum + } + + doc.Components.Schemas[enumName] = &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: enumValues, + }, + } + + ref := "#/components/schemas/" + enumName + + for _, usage := range usages { + if usage.inItems { + usage.propRef.Value.Items = &openapi3.SchemaRef{Ref: ref} + } else { + old := usage.propRef.Value + if old.Description == "" && !old.Deprecated && old.Format == "" { + usage.propRef.Ref = ref + usage.propRef.Value = nil + } else { + usage.propRef.Value = &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + {Ref: ref}, + }, + Description: old.Description, + Deprecated: old.Deprecated, + Format: old.Format, + } + } + } + } + } + return nil +} + +// deriveEnumName looks up a shared enum's Go type name from astEnumMap by +// value-set key. If no annotation matches, returns an error identifying the +// offending properties and the fix. +func deriveEnumName(key string, usages []enumUsage, astEnumMap map[string]string) (string, error) { + if name, ok := astEnumMap[key]; ok { + return name, nil + } + + props := map[string]bool{} + for _, u := range usages { + props[fmt.Sprintf("%s.%s", u.schemaName, u.propName)] = true + } + propList := make([]string, 0, len(props)) + for p := range props { + propList = append(propList, p) + } + return "", fmt.Errorf( + "no swagger:enum annotation matches value-set %q used by %d properties: %v; "+ + "fix by adding a named string type with // swagger:enum to modules/structs or modules/commitstatus", + key, len(usages), propList, + ) +} diff --git a/build/openapi3gen/convert_test.go b/build/openapi3gen/convert_test.go new file mode 100644 index 0000000000..a9a715e6c2 --- /dev/null +++ b/build/openapi3gen/convert_test.go @@ -0,0 +1,170 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "strings" + "testing" + + "github.com/getkin/kin-openapi/openapi3" +) + +func TestDeriveEnumName_hit(t *testing.T) { + key := EnumKey([]any{"red", "green", "blue"}) + astMap := map[string]string{key: "Color"} + usages := []enumUsage{{schemaName: "Paint", propName: "color"}} + got, err := deriveEnumName(key, usages, astMap) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != "Color" { + t.Fatalf("got %q, want %q", got, "Color") + } +} + +func TestDeriveEnumName_miss(t *testing.T) { + key := EnumKey([]any{"x", "y"}) + usages := []enumUsage{{schemaName: "Thing", propName: "kind"}} + _, err := deriveEnumName(key, usages, map[string]string{}) + if err == nil { + t.Fatal("expected miss error, got nil") + } + msg := err.Error() + if !strings.Contains(msg, "Thing.kind") { + t.Fatalf("error %q should list the missing usage", msg) + } + if !strings.Contains(msg, "swagger:enum") { + t.Fatalf("error %q should hint at the fix", msg) + } +} + +func TestExtractSharedEnums_usesASTMap(t *testing.T) { + doc := &openapi3.T{ + Components: &openapi3.Components{ + Schemas: openapi3.Schemas{ + "A": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green", "blue"}, + }}, + }, + }}, + "B": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green", "blue"}, + }}, + }, + }}, + }, + }, + } + astMap := map[string]string{EnumKey([]any{"red", "green", "blue"}): "Color"} + if err := extractSharedEnums(doc, astMap); err != nil { + t.Fatalf("extractSharedEnums: %v", err) + } + if _, ok := doc.Components.Schemas["Color"]; !ok { + t.Fatalf("expected Color schema to be extracted") + } +} + +func TestFixFileSchemas_recursesIntoNested(t *testing.T) { + fileType := func() *openapi3.SchemaRef { + return &openapi3.SchemaRef{Value: &openapi3.Schema{Type: &openapi3.Types{"file"}}} + } + doc := &openapi3.T{ + Paths: openapi3.NewPaths(), + } + doc.Paths.Set("/upload", &openapi3.PathItem{ + Post: &openapi3.Operation{ + RequestBody: &openapi3.RequestBodyRef{ + Value: &openapi3.RequestBody{ + Content: openapi3.Content{ + "multipart/form-data": { + Schema: &openapi3.SchemaRef{Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "attachment": fileType(), + "items": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"array"}, + Items: fileType(), + }}, + "alt": {Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{fileType()}, + }}, + "one": {Value: &openapi3.Schema{ + OneOf: openapi3.SchemaRefs{fileType()}, + }}, + "any": {Value: &openapi3.Schema{ + AnyOf: openapi3.SchemaRefs{fileType()}, + }}, + "not": {Value: &openapi3.Schema{ + Not: fileType(), + }}, + }, + }}, + }, + }, + }, + }, + Responses: openapi3.NewResponses(), + }, + }) + + fixFileSchemas(doc) + + props := doc.Paths.Value("/upload").Post.RequestBody.Value.Content["multipart/form-data"].Schema.Value.Properties + if !props["attachment"].Value.Type.Is("string") || props["attachment"].Value.Format != "binary" { + t.Errorf("nested property not fixed: %+v", props["attachment"].Value) + } + if !props["items"].Value.Items.Value.Type.Is("string") || props["items"].Value.Items.Value.Format != "binary" { + t.Errorf("array items not fixed: %+v", props["items"].Value.Items.Value) + } + if !props["alt"].Value.AllOf[0].Value.Type.Is("string") || props["alt"].Value.AllOf[0].Value.Format != "binary" { + t.Errorf("allOf branch not fixed: %+v", props["alt"].Value.AllOf[0].Value) + } + if !props["one"].Value.OneOf[0].Value.Type.Is("string") || props["one"].Value.OneOf[0].Value.Format != "binary" { + t.Errorf("oneOf branch not fixed: %+v", props["one"].Value.OneOf[0].Value) + } + if !props["any"].Value.AnyOf[0].Value.Type.Is("string") || props["any"].Value.AnyOf[0].Value.Format != "binary" { + t.Errorf("anyOf branch not fixed: %+v", props["any"].Value.AnyOf[0].Value) + } + if !props["not"].Value.Not.Value.Type.Is("string") || props["not"].Value.Not.Value.Format != "binary" { + t.Errorf("not branch not fixed: %+v", props["not"].Value.Not.Value) + } +} + +func TestExtractSharedEnums_missReturnsError(t *testing.T) { + doc := &openapi3.T{ + Components: &openapi3.Components{ + Schemas: openapi3.Schemas{ + "A": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green"}, + }}, + }, + }}, + "B": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green"}, + }}, + }, + }}, + }, + }, + } + if err := extractSharedEnums(doc, map[string]string{}); err == nil { + t.Fatal("expected miss error") + } +} diff --git a/build/openapi3gen/enumscan.go b/build/openapi3gen/enumscan.go new file mode 100644 index 0000000000..dd11620549 --- /dev/null +++ b/build/openapi3gen/enumscan.go @@ -0,0 +1,188 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// Package openapi3gen converts Gitea's Swagger 2.0 spec to an OpenAPI 3.0 +// spec. It discovers Go enum type names by scanning swagger:enum annotations +// in the source tree, then names extracted shared-enum schemas accordingly. +package openapi3gen + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" +) + +// EnumKey returns a canonical key for a set of enum values: values are +// stringified, sorted, and joined with "|". Used to match enum value sets +// across spec properties and scanned Go type declarations. +func EnumKey(values []any) string { + strs := make([]string, len(values)) + for i, v := range values { + strs[i] = fmt.Sprintf("%v", v) + } + sort.Strings(strs) + return strings.Join(strs, "|") +} + +var rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`) + +// ScanSwaggerEnumTypes walks .go files under each dir and returns a map from +// a canonical value-set key (see EnumKey) to the Go type name declared with +// // swagger:enum TypeName. +// +// Returns an error on parse failure, on an annotation for a type whose +// constants can't be extracted, or on value-set collisions between two +// different enum types. +func ScanSwaggerEnumTypes(dirs []string) (map[string]string, error) { + fset := token.NewFileSet() + parsed := []*ast.File{} + + for _, dir := range dirs { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("reading %s: %w", dir, err) + } + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { + continue + } + if strings.HasSuffix(entry.Name(), "_test.go") { + continue + } + path := filepath.Join(dir, entry.Name()) + file, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + return nil, fmt.Errorf("%s: %w", path, err) + } + parsed = append(parsed, file) + } + } + + enumTypes := map[string]string{} // typeName → "" (presence marker) + enumValues := map[string][]any{} // typeName → values + + // Pass 1: collect every // swagger:enum TypeName declaration. + for _, file := range parsed { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + if err := collectEnumType(gd, enumTypes); err != nil { + return nil, fmt.Errorf("%s: %w", fset.Position(gd.Pos()).Filename, err) + } + } + } + + // Pass 2: collect const values; now every annotated type is visible. + for _, file := range parsed { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.CONST { + continue + } + collectEnumValues(gd, enumTypes, enumValues) + } + } + + result := map[string]string{} + for typeName := range enumTypes { + values, ok := enumValues[typeName] + if !ok || len(values) == 0 { + return nil, fmt.Errorf("swagger:enum %s has no const block with typed string values", typeName) + } + key := EnumKey(values) + if existing, ok := result[key]; ok && existing != typeName { + return nil, fmt.Errorf("swagger:enum value-set collision: %s and %s both use %q", existing, typeName, key) + } + result[key] = typeName + } + return result, nil +} + +// collectEnumType scans a `type` GenDecl for // swagger:enum annotations, +// handling both the lone form (`// swagger:enum Foo\n type Foo string`) +// where the comment group is attached to the GenDecl, and the grouped form: +// +// type ( +// // swagger:enum Foo +// Foo string +// ) +// +// where the comment group is attached to each TypeSpec. Caveat: Go's parser +// only attaches a CommentGroup when it is immediately adjacent to the decl. +// A blank line (not a `//` continuation line) between the comment and the +// declaration drops the Doc, so annotations MUST sit directly above their +// type. All current annotated files obey this — the rule is noted here so +// a future edit that inserts a blank line fails fast rather than silently. +func collectEnumType(gd *ast.GenDecl, enumTypes map[string]string) error { + if err := registerEnumAnnotation(gd.Doc, gd.Specs, enumTypes); err != nil { + return err + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok || ts.Doc == nil { + continue + } + if err := registerEnumAnnotation(ts.Doc, []ast.Spec{ts}, enumTypes); err != nil { + return err + } + } + return nil +} + +func registerEnumAnnotation(doc *ast.CommentGroup, specs []ast.Spec, enumTypes map[string]string) error { + if doc == nil { + return nil + } + matches := rxSwaggerEnum.FindStringSubmatch(doc.Text()) + if len(matches) < 2 { + return nil + } + annotated := matches[1] + for _, spec := range specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + if ts.Name.Name == annotated { + enumTypes[annotated] = "" + return nil + } + } + return fmt.Errorf("swagger:enum %s: no type declaration with that name in the same decl group; check for a typo", annotated) +} + +func collectEnumValues(gd *ast.GenDecl, enumTypes map[string]string, enumValues map[string][]any) { + for _, spec := range gd.Specs { + vs, ok := spec.(*ast.ValueSpec) + if !ok || vs.Type == nil { + continue + } + ident, ok := vs.Type.(*ast.Ident) + if !ok { + continue + } + if _, isEnum := enumTypes[ident.Name]; !isEnum { + continue + } + for _, val := range vs.Values { + lit, ok := val.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + continue + } + unquoted, err := strconv.Unquote(lit.Value) + if err != nil { + continue + } + enumValues[ident.Name] = append(enumValues[ident.Name], unquoted) + } + } +} diff --git a/build/openapi3gen/enumscan_test.go b/build/openapi3gen/enumscan_test.go new file mode 100644 index 0000000000..2e5fe99db0 --- /dev/null +++ b/build/openapi3gen/enumscan_test.go @@ -0,0 +1,239 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestEnumKey_sortsAndJoins(t *testing.T) { + key := EnumKey([]any{"b", "a", "c"}) + if key != "a|b|c" { + t.Fatalf("EnumKey = %q, want %q", key, "a|b|c") + } +} + +func TestEnumKey_handlesNonStringValues(t *testing.T) { + key := EnumKey([]any{2, 1, 3}) + if key != "1|2|3" { + t.Fatalf("EnumKey = %q, want %q", key, "1|2|3") + } +} + +func TestScanSwaggerEnumTypes_basic(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// Color is a primary color. +// swagger:enum Color +type Color string + +const ( + ColorRed Color = "red" + ColorGreen Color = "green" + ColorBlue Color = "blue" +) +` + if err := os.WriteFile(filepath.Join(dir, "color.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"red", "green", "blue"}) + if got[wantKey] != "Color" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color") + } +} + +func TestScanSwaggerEnumTypes_orphanAnnotation(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Sttype +type StateType string + +const ( + StateOpen StateType = "open" +) +` + if err := os.WriteFile(filepath.Join(dir, "typo.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected error for annotation referencing a non-matching type name") + } + if !strings.Contains(err.Error(), "Sttype") { + t.Fatalf("error %q should mention the typo'd name Sttype", err.Error()) + } +} + +func TestScanSwaggerEnumTypes_collision(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Alpha +type Alpha string +const ( + AlphaX Alpha = "x" + AlphaY Alpha = "y" +) + +// swagger:enum Beta +type Beta string +const ( + BetaX Beta = "x" + BetaY Beta = "y" +) +` + if err := os.WriteFile(filepath.Join(dir, "dup.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected collision error, got nil") + } + msg := err.Error() + if !strings.Contains(msg, "Alpha") || !strings.Contains(msg, "Beta") { + t.Fatalf("error %q should mention both Alpha and Beta", msg) + } +} + +func TestScanSwaggerEnumTypes_parseFailure(t *testing.T) { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, "bad.go"), []byte("package fixture\nfunc Foo() {"), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected parse error, got nil") + } +} + +func TestScanSwaggerEnumTypes_annotationWithoutConsts(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Lonely +type Lonely string +` + if err := os.WriteFile(filepath.Join(dir, "lonely.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected error for annotation without consts") + } + if !strings.Contains(err.Error(), "Lonely") { + t.Fatalf("error %q should mention Lonely", err.Error()) + } +} + +func TestScanSwaggerEnumTypes_constsAndTypeInDifferentFiles(t *testing.T) { + dir := t.TempDir() + // Name ordering: `a_consts.go` < `b_type.go`, so readdir returns consts first. + // Old single-pass scanner would miss the values; two-pass must not. + constsSrc := `package fixture + +const ( + HueA Hue = "a" + HueB Hue = "b" +) +` + typeSrc := `package fixture + +// swagger:enum Hue +type Hue string +` + if err := os.WriteFile(filepath.Join(dir, "a_consts.go"), []byte(constsSrc), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "b_type.go"), []byte(typeSrc), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"a", "b"}) + if got[wantKey] != "Hue" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Hue") + } +} + +func TestScanSwaggerEnumTypes_constsBeforeType(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +const ( + ShadeDark Shade = "dark" + ShadeLight Shade = "light" +) + +// swagger:enum Shade +type Shade string +` + if err := os.WriteFile(filepath.Join(dir, "shade.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"dark", "light"}) + if got[wantKey] != "Shade" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade") + } +} + +func TestScanSwaggerEnumTypes_groupedTypeDecl(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +type ( + // swagger:enum Color + Color string + // swagger:enum Shade + Shade string +) + +const ( + ColorRed Color = "red" + ColorBlue Color = "blue" +) + +const ( + ShadeDark Shade = "dark" + ShadeLight Shade = "light" +) +` + if err := os.WriteFile(filepath.Join(dir, "grouped.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + colorKey := EnumKey([]any{"red", "blue"}) + shadeKey := EnumKey([]any{"dark", "light"}) + if got[colorKey] != "Color" { + t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color") + } + if got[shadeKey] != "Shade" { + t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade") + } +} diff --git a/go.mod b/go.mod index d7577bfbf0..2e3df9b68f 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/ethantkoenig/rupture v1.0.1 github.com/felixge/fgprof v0.9.5 github.com/fsnotify/fsnotify v1.9.0 + github.com/getkin/kin-openapi v0.134.0 github.com/gliderlabs/ssh v0.3.8 github.com/go-chi/chi/v5 v5.2.5 github.com/go-chi/cors v1.2.2 @@ -192,6 +193,8 @@ require ( github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-ini/ini v1.67.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/go-webauthn/x v0.2.3 // indirect github.com/goccy/go-json v0.10.6 // indirect @@ -232,15 +235,19 @@ require ( github.com/minio/minlz v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nwaples/rardecode/v2 v2.2.2 // indirect + github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c // indirect + github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/errors v1.2.0 // indirect github.com/olekukonko/ll v0.1.8 // indirect github.com/olekukonko/tablewriter v1.1.4 // indirect github.com/onsi/ginkgo v1.16.5 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect @@ -260,6 +267,7 @@ require ( github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tinylib/msgp v1.6.4 // indirect github.com/unknwon/com v1.0.1 // indirect + github.com/woodsbury/decimal128 v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect diff --git a/go.sum b/go.sum index 0b65e6305f..d7e5690040 100644 --- a/go.sum +++ b/go.sum @@ -275,6 +275,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/getkin/kin-openapi v0.134.0 h1:/L5+1+kfe6dXh8Ot/wqiTgUkjOIEJiC0bbYVziHB8rU= +github.com/getkin/kin-openapi v0.134.0/go.mod h1:wK6ZLG/VgoETO9pcLJ/VmAtIcl/DNlMayNTb716EUxE= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0= github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= @@ -310,6 +312,10 @@ github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZR github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= @@ -547,6 +553,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= @@ -565,6 +573,10 @@ github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsR github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c h1:7ACFcSaQsrWtrH4WHHfUqE1C+f8r2uv8KGaW0jTNjus= +github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c/go.mod h1:JKox4Gszkxt57kj27u7rvi7IFoIULvCZHUsBTUmQM/s= +github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b h1:vivRhVUAa9t1q0Db4ZmezBP8pWQWnXHFokZj0AOea2g= +github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= @@ -591,6 +603,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= @@ -700,6 +714,8 @@ github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77ro github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -712,6 +728,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= +github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= +github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= diff --git a/models/user/user.go b/models/user/user.go index 69b97e9b47..0e87d7b7b9 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -615,6 +615,7 @@ var ( "sitemap.xml", // search engine sitemap "ssh_info", // agit info "swagger.v1.json", + "openapi3.v1.json", "ghost", // reserved name for deleted users (id: -1) "gitea-actions", // gitea builtin user (id: -2) diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index d158a5fd31..ee65e016bc 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -30,7 +30,7 @@ type CreateUserOption struct { // Whether the user has restricted access privileges Restricted *bool `json:"restricted"` // User visibility level: public, limited, or private - Visibility string `json:"visibility" binding:"In(,public,limited,private)"` + Visibility UserVisibility `json:"visibility" binding:"In(,public,limited,private)"` // For explicitly setting the user creation timestamp. Useful when users are // migrated from other systems. When omitted, the user's creation timestamp @@ -79,5 +79,5 @@ type EditUserOption struct { // Whether the user has restricted access privileges Restricted *bool `json:"restricted"` // User visibility level: public, limited, or private - Visibility string `json:"visibility" binding:"In(,public,limited,private)"` + Visibility UserVisibility `json:"visibility" binding:"In(,public,limited,private)"` } diff --git a/modules/structs/org.go b/modules/structs/org.go index 723689cb53..b93f68b600 100644 --- a/modules/structs/org.go +++ b/modules/structs/org.go @@ -22,7 +22,7 @@ type Organization struct { // The location of the organization Location string `json:"location"` // The visibility level of the organization (public, limited, private) - Visibility string `json:"visibility"` + Visibility UserVisibility `json:"visibility"` // Whether repository administrators can change team access RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` // username of the organization @@ -60,8 +60,7 @@ type CreateOrgOption struct { // The location of the organization Location string `json:"location" binding:"MaxSize(50)"` // possible values are `public` (default), `limited` or `private` - // enum: ["public","limited","private"] - Visibility string `json:"visibility" binding:"In(,public,limited,private)"` + Visibility UserVisibility `json:"visibility" binding:"In(,public,limited,private)"` // Whether repository administrators can change team access RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` } @@ -79,8 +78,7 @@ type EditOrgOption struct { // The location of the organization Location *string `json:"location" binding:"MaxSize(50)"` // possible values are `public`, `limited` or `private` - // enum: ["public","limited","private"] - Visibility *string `json:"visibility" binding:"In(,public,limited,private)"` + Visibility *UserVisibility `json:"visibility" binding:"In(,public,limited,private)"` // Whether repository administrators can change team access RepoAdminChangeTeamAccess *bool `json:"repo_admin_change_team_access"` } diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go index f730a5681c..20959931d3 100644 --- a/modules/structs/org_team.go +++ b/modules/structs/org_team.go @@ -15,9 +15,8 @@ type Team struct { // The organization that the team belongs to Organization *Organization `json:"organization"` // Whether the team has access to all repositories in the organization - IncludesAllRepositories bool `json:"includes_all_repositories"` - // enum: ["none","read","write","admin","owner"] - Permission string `json:"permission"` + IncludesAllRepositories bool `json:"includes_all_repositories"` + Permission AccessLevelName `json:"permission"` // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` @@ -34,9 +33,8 @@ type CreateTeamOption struct { // The description of the team Description string `json:"description" binding:"MaxSize(255)"` // Whether the team has access to all repositories in the organization - IncludesAllRepositories bool `json:"includes_all_repositories"` - // enum: ["read","write","admin"] - Permission string `json:"permission"` + IncludesAllRepositories bool `json:"includes_all_repositories"` + Permission RepoWritePermission `json:"permission"` // example: ["repo.actions","repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.ext_wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` @@ -53,9 +51,8 @@ type EditTeamOption struct { // The description of the team Description *string `json:"description" binding:"MaxSize(255)"` // Whether the team has access to all repositories in the organization - IncludesAllRepositories *bool `json:"includes_all_repositories"` - // enum: ["read","write","admin"] - Permission string `json:"permission"` + IncludesAllRepositories *bool `json:"includes_all_repositories"` + Permission RepoWritePermission `json:"permission"` // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 7cd64fd7a4..138be33c1c 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -8,6 +8,15 @@ import ( "time" ) +// ObjectFormatName is the git hash algorithm used by a repository. +// swagger:enum ObjectFormatName +type ObjectFormatName string + +const ( + ObjectFormatSHA1 ObjectFormatName = "sha1" + ObjectFormatSHA256 ObjectFormatName = "sha256" +) + // Permission represents a set of permissions type Permission struct { Admin bool `json:"admin"` // Admin indicates if the user is an administrator of the repository. @@ -114,8 +123,7 @@ type Repository struct { Internal bool `json:"internal"` MirrorInterval string `json:"mirror_interval"` // ObjectFormatName of the underlying git repository - // enum: ["sha1","sha256"] - ObjectFormatName string `json:"object_format_name"` + ObjectFormatName ObjectFormatName `json:"object_format_name"` // swagger:strfmt date-time MirrorUpdated time.Time `json:"mirror_updated"` RepoTransfer *RepoTransfer `json:"repo_transfer,omitempty"` @@ -153,8 +161,7 @@ type CreateRepoOption struct { // enum: ["default","collaborator","committer","collaboratorcommitter"] TrustModel string `json:"trust_model"` // ObjectFormatName of the underlying git repository, empty string for default (sha1) - // enum: ["sha1","sha256"] - ObjectFormatName string `json:"object_format_name" binding:"MaxSize(6)"` + ObjectFormatName ObjectFormatName `json:"object_format_name" binding:"MaxSize(6)"` } // EditRepoOption options when editing a repository's properties diff --git a/modules/structs/repo_collaborator.go b/modules/structs/repo_collaborator.go index 6b315df403..fb36ecf485 100644 --- a/modules/structs/repo_collaborator.go +++ b/modules/structs/repo_collaborator.go @@ -3,17 +3,41 @@ package structs +// RepoWritePermission is a permission level callers may grant to a team or +// collaborator on input. Output fields use AccessLevelName instead. +// swagger:enum RepoWritePermission +type RepoWritePermission string + +const ( + RepoWritePermissionRead RepoWritePermission = "read" + RepoWritePermissionWrite RepoWritePermission = "write" + RepoWritePermissionAdmin RepoWritePermission = "admin" +) + +// AccessLevelName is the string rendering of a perm.AccessMode produced on +// API responses. Callers must not send these values; use RepoWritePermission +// on input. +// swagger:enum AccessLevelName +type AccessLevelName string + +const ( + AccessLevelNameNone AccessLevelName = "none" + AccessLevelNameRead AccessLevelName = "read" + AccessLevelNameWrite AccessLevelName = "write" + AccessLevelNameAdmin AccessLevelName = "admin" + AccessLevelNameOwner AccessLevelName = "owner" +) + // AddCollaboratorOption options when adding a user as a collaborator of a repository type AddCollaboratorOption struct { - // enum: ["read","write","admin"] // Permission level to grant the collaborator - Permission *string `json:"permission"` + Permission *RepoWritePermission `json:"permission"` } // RepoCollaboratorPermission to get repository permission for a collaborator type RepoCollaboratorPermission struct { // Permission level of the collaborator - Permission string `json:"permission"` + Permission AccessLevelName `json:"permission"` // RoleName is the name of the permission role RoleName string `json:"role_name"` // User information of the collaborator diff --git a/modules/structs/user.go b/modules/structs/user.go index 90dbcff25c..a25c0c5a1e 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -51,7 +51,7 @@ type User struct { // the user's description Description string `json:"description"` // User visibility level option: public, limited, private - Visibility string `json:"visibility"` + Visibility UserVisibility `json:"visibility"` // user counts Followers int `json:"followers_count"` diff --git a/modules/structs/visible_type.go b/modules/structs/visible_type.go index b5ff353b87..f537884963 100644 --- a/modules/structs/visible_type.go +++ b/modules/structs/visible_type.go @@ -56,3 +56,14 @@ func ExtractKeysFromMapString(in map[string]VisibleType) (keys []string) { } return keys } + +// UserVisibility defines the visibility level of a user or organization as +// rendered in API payloads. The DB representation is VisibleType (int). +// swagger:enum UserVisibility +type UserVisibility string + +const ( + UserVisibilityPublic UserVisibility = "public" + UserVisibilityLimited UserVisibility = "limited" + UserVisibilityPrivate UserVisibility = "private" +) diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index 6390bb7e82..28c21de15f 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -48,7 +48,7 @@ func CreateOrg(ctx *context.APIContext) { visibility := api.VisibleTypePublic if form.Visibility != "" { - visibility = api.VisibilityModes[form.Visibility] + visibility = api.VisibilityModes[string(form.Visibility)] } org := &organization.Organization{ diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index b9dd12f8ff..8a6924b4a3 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -123,7 +123,7 @@ func CreateUser(ctx *context.APIContext) { } if form.Visibility != "" { - visibility := api.VisibilityModes[form.Visibility] + visibility := api.VisibilityModes[string(form.Visibility)] overwriteDefault.Visibility = &visibility } @@ -239,7 +239,7 @@ func EditUser(ctx *context.APIContext) { Description: optional.FromPtr(form.Description), IsActive: optional.FromPtr(form.Active), IsAdmin: user_service.UpdateOptionFieldFromPtr(form.Admin), - Visibility: optional.FromMapLookup(api.VisibilityModes, form.Visibility), + Visibility: optional.FromMapLookup(api.VisibilityModes, string(form.Visibility)), AllowGitHook: optional.FromPtr(form.AllowGitHook), AllowImportLocal: optional.FromPtr(form.AllowImportLocal), MaxRepoCreation: optional.FromPtr(form.MaxRepoCreation), diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 7c6d11bbc4..2df871a0aa 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -258,7 +258,7 @@ func Create(ctx *context.APIContext) { visibility := api.VisibleTypePublic if form.Visibility != "" { - visibility = api.VisibilityModes[form.Visibility] + visibility = api.VisibilityModes[string(form.Visibility)] } org := &organization.Organization{ @@ -402,7 +402,7 @@ func Edit(ctx *context.APIContext) { Description: optional.FromPtr(form.Description), Website: optional.FromPtr(form.Website), Location: optional.FromPtr(form.Location), - Visibility: optional.FromMapLookup(api.VisibilityModes, optional.FromPtr(form.Visibility).Value()), + Visibility: optional.FromMapLookup(api.VisibilityModes, string(optional.FromPtr(form.Visibility).Value())), RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess), } if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil { diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 659218e837..74ad987540 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -210,7 +210,7 @@ func CreateTeam(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" form := web.GetForm(ctx).(*api.CreateTeamOption) - teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) + teamPermission := perm.ParseAccessMode(string(form.Permission), perm.AccessModeNone, perm.AccessModeAdmin) team := &organization.Team{ OrgID: ctx.Org.Organization.ID, Name: form.Name, @@ -224,7 +224,7 @@ func CreateTeam(ctx *context.APIContext) { if len(form.UnitsMap) > 0 { attachTeamUnitsMap(team, form.UnitsMap) } else if len(form.Units) > 0 { - unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) + unitPerm := perm.ParseAccessMode(string(form.Permission), perm.AccessModeRead, perm.AccessModeWrite) attachTeamUnits(team, unitPerm, form.Units) } else { ctx.APIErrorInternal(errors.New("units permission should not be empty")) @@ -298,7 +298,7 @@ func EditTeam(ctx *context.APIContext) { isAuthChanged := false isIncludeAllChanged := false if !team.IsOwnerTeam() && len(form.Permission) != 0 { - teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) + teamPermission := perm.ParseAccessMode(string(form.Permission), perm.AccessModeNone, perm.AccessModeAdmin) if team.AccessMode != teamPermission { isAuthChanged = true team.AccessMode = teamPermission @@ -314,7 +314,7 @@ func EditTeam(ctx *context.APIContext) { if len(form.UnitsMap) > 0 { attachTeamUnitsMap(team, form.UnitsMap) } else if len(form.Units) > 0 { - unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) + unitPerm := perm.ParseAccessMode(string(form.Permission), perm.AccessModeRead, perm.AccessModeWrite) attachTeamUnits(team, unitPerm, form.Units) } } else { diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index 2c40e26508..7d9b290c2a 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -181,7 +181,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) { p := perm.AccessModeWrite if form.Permission != nil { - p = perm.ParseAccessMode(*form.Permission, perm.AccessModeRead, perm.AccessModeWrite, perm.AccessModeAdmin) + p = perm.ParseAccessMode(string(*form.Permission), perm.AccessModeRead, perm.AccessModeWrite, perm.AccessModeAdmin) } if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil { diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 24f486be9d..f0428f2f6d 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -262,7 +262,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre DefaultBranch: opt.DefaultBranch, TrustModel: repo_model.ToTrustModel(opt.TrustModel), IsTemplate: opt.Template, - ObjectFormatName: opt.ObjectFormatName, + ObjectFormatName: string(opt.ObjectFormatName), }) if err != nil { if repo_model.IsErrRepoAlreadyExist(err) { diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go index 52f6beaf59..da41fd9013 100644 --- a/routers/web/swagger_json.go +++ b/routers/web/swagger_json.go @@ -16,3 +16,10 @@ func SwaggerV1Json(ctx *context.Context) { ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe ctx.JSONTemplate("swagger/v1_json") } + +// OpenAPI3Json render OpenAPI 3.0 json (auto-converted from Swagger 2.0) +func OpenAPI3Json(ctx *context.Context) { + ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer)) + ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe + ctx.JSONTemplate("swagger/v1_openapi3_json") +} diff --git a/routers/web/web.go b/routers/web/web.go index d70eb2d02d..c6dece7b56 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1751,6 +1751,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { if setting.API.EnableSwagger { m.Get("/swagger.v1.json", SwaggerV1Json) + m.Get("/openapi3.v1.json", OpenAPI3Json) } if !setting.IsProd || setting.IsInE2eTesting() { diff --git a/services/convert/convert.go b/services/convert/convert.go index b254d29785..a7622644d8 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -711,7 +711,7 @@ func ToOrganization(ctx context.Context, org *organization.Organization) *api.Or Description: org.Description, Website: org.Website, Location: org.Location, - Visibility: org.Visibility.String(), + Visibility: api.UserVisibility(org.Visibility.String()), RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess, } } @@ -740,7 +740,7 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([] Description: t.Description, IncludesAllRepositories: t.IncludesAllRepositories, CanCreateOrgRepo: t.CanCreateOrgRepo, - Permission: t.AccessMode.ToString(), + Permission: api.AccessLevelName(t.AccessMode.ToString()), Units: t.GetUnitNames(), UnitsMap: t.GetUnitsMap(), } diff --git a/services/convert/repository.go b/services/convert/repository.go index 3c9cc83ccb..503f6bb2a3 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -251,7 +251,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR MirrorUpdated: mirrorUpdated, RepoTransfer: transfer, Topics: util.SliceNilAsEmpty(repo.Topics), - ObjectFormatName: repo.ObjectFormatName, + ObjectFormatName: api.ObjectFormatName(repo.ObjectFormatName), Licenses: util.SliceNilAsEmpty(repoLicenses.StringList()), } } diff --git a/services/convert/user.go b/services/convert/user.go index cee4de5091..ce9b256061 100644 --- a/services/convert/user.go +++ b/services/convert/user.go @@ -65,7 +65,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap StarredRepos: user.NumStars, } - result.Visibility = user.Visibility.String() + result.Visibility = api.UserVisibility(user.Visibility.String()) // hide primary email if API caller is anonymous or user keep email private if signed && (!user.KeepEmailPrivate || authed) { @@ -104,7 +104,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings { func ToUserAndPermission(ctx context.Context, user, doer *user_model.User, accessMode perm.AccessMode) api.RepoCollaboratorPermission { return api.RepoCollaboratorPermission{ User: ToUser(ctx, user, doer), - Permission: accessMode.ToString(), + Permission: api.AccessLevelName(accessMode.ToString()), RoleName: accessMode.ToString(), } } diff --git a/services/convert/user_test.go b/services/convert/user_test.go index d32cffb512..349b0058fe 100644 --- a/services/convert/user_test.go +++ b/services/convert/user_test.go @@ -29,11 +29,11 @@ func TestUser_ToUser(t *testing.T) { apiUser = toUser(t.Context(), user1, false, false) assert.False(t, apiUser.IsAdmin) - assert.Equal(t, api.VisibleTypePublic.String(), apiUser.Visibility) + assert.Equal(t, api.UserVisibilityPublic, apiUser.Visibility) user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}) apiUser = toUser(t.Context(), user31, true, true) assert.False(t, apiUser.IsAdmin) - assert.Equal(t, api.VisibleTypePrivate.String(), apiUser.Visibility) + assert.Equal(t, api.UserVisibilityPrivate, apiUser.Visibility) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 49da9f08e7..e6bee5e30b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -22299,12 +22299,14 @@ "type": "object", "properties": { "permission": { + "description": "Permission level to grant the collaborator\nread RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin", "type": "string", "enum": [ "read", "write", "admin" ], + "x-go-enum-desc": "read RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin", "x-go-name": "Permission" } }, @@ -24031,13 +24033,14 @@ "x-go-name": "UserName" }, "visibility": { - "description": "possible values are `public` (default), `limited` or `private`", + "description": "possible values are `public` (default), `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", "enum": [ "public", "limited", "private" ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" }, "website": { @@ -24322,12 +24325,13 @@ "x-go-name": "Name" }, "object_format_name": { - "description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)", + "description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256", "type": "string", "enum": [ "sha1", "sha256" ], + "x-go-enum-desc": "sha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256", "x-go-name": "ObjectFormatName" }, "private": { @@ -24480,6 +24484,7 @@ "write", "admin" ], + "x-go-enum-desc": "read RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin", "x-go-name": "Permission" }, "units": { @@ -24574,8 +24579,14 @@ "x-go-name": "Username" }, "visibility": { - "description": "User visibility level: public, limited, or private", + "description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", + "enum": [ + "public", + "limited", + "private" + ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" } }, @@ -25200,13 +25211,14 @@ "x-go-name": "RepoAdminChangeTeamAccess" }, "visibility": { - "description": "possible values are `public`, `limited` or `private`", + "description": "possible values are `public`, `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", "enum": [ "public", "limited", "private" ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" }, "website": { @@ -25570,6 +25582,7 @@ "write", "admin" ], + "x-go-enum-desc": "read RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin", "x-go-name": "Permission" }, "units": { @@ -25700,8 +25713,14 @@ "x-go-name": "SourceID" }, "visibility": { - "description": "User visibility level: public, limited, or private", + "description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", + "enum": [ + "public", + "limited", + "private" + ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" }, "website": { @@ -27652,8 +27671,14 @@ "x-go-name": "UserName" }, "visibility": { - "description": "The visibility level of the organization (public, limited, private)", + "description": "The visibility level of the organization (public, limited, private)\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", + "enum": [ + "public", + "limited", + "private" + ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" }, "website": { @@ -28637,8 +28662,16 @@ "type": "object", "properties": { "permission": { - "description": "Permission level of the collaborator", + "description": "Permission level of the collaborator\nnone AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner", "type": "string", + "enum": [ + "none", + "read", + "write", + "admin", + "owner" + ], + "x-go-enum-desc": "none AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner", "x-go-name": "Permission" }, "role_name": { @@ -28915,12 +28948,13 @@ "x-go-name": "Name" }, "object_format_name": { - "description": "ObjectFormatName of the underlying git repository", + "description": "ObjectFormatName of the underlying git repository\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256", "type": "string", "enum": [ "sha1", "sha256" ], + "x-go-enum-desc": "sha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256", "x-go-name": "ObjectFormatName" }, "open_issues_count": { @@ -29294,6 +29328,7 @@ "admin", "owner" ], + "x-go-enum-desc": "none AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner", "x-go-name": "Permission" }, "units": { @@ -29840,8 +29875,14 @@ "x-go-name": "StarredRepos" }, "visibility": { - "description": "User visibility level option: public, limited, private", + "description": "User visibility level option: public, limited, private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "type": "string", + "enum": [ + "public", + "limited", + "private" + ], + "x-go-enum-desc": "public UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate", "x-go-name": "Visibility" }, "website": { diff --git a/templates/swagger/v1_openapi3_json.tmpl b/templates/swagger/v1_openapi3_json.tmpl new file mode 100644 index 0000000000..2cd9f59d33 --- /dev/null +++ b/templates/swagger/v1_openapi3_json.tmpl @@ -0,0 +1,33451 @@ +{ + "components": { + "responses": { + "AccessToken": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccessToken" + } + } + }, + "description": "AccessToken represents an API access token." + }, + "AccessTokenList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AccessToken" + }, + "type": "array" + } + } + }, + "description": "AccessTokenList represents a list of API access token." + }, + "ActionVariable": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionVariable" + } + } + }, + "description": "ActionVariable" + }, + "ActionWorkflow": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflow" + } + } + }, + "description": "ActionWorkflow" + }, + "ActionWorkflowList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflowResponse" + } + } + }, + "description": "ActionWorkflowList" + }, + "ActivityFeedsList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Activity" + }, + "type": "array" + } + } + }, + "description": "ActivityFeedsList" + }, + "AnnotatedTag": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AnnotatedTag" + } + } + }, + "description": "AnnotatedTag" + }, + "Artifact": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionArtifact" + } + } + }, + "description": "Artifact" + }, + "ArtifactsList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionArtifactsResponse" + } + } + }, + "description": "ArtifactsList" + }, + "Attachment": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Attachment" + } + } + }, + "description": "Attachment" + }, + "AttachmentList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Attachment" + }, + "type": "array" + } + } + }, + "description": "AttachmentList" + }, + "BadgeList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Badge" + }, + "type": "array" + } + } + }, + "description": "BadgeList" + }, + "Branch": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Branch" + } + } + }, + "description": "Branch" + }, + "BranchList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Branch" + }, + "type": "array" + } + } + }, + "description": "BranchList" + }, + "BranchProtection": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BranchProtection" + } + } + }, + "description": "BranchProtection" + }, + "BranchProtectionList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/BranchProtection" + }, + "type": "array" + } + } + }, + "description": "BranchProtectionList" + }, + "ChangedFileList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChangedFile" + }, + "type": "array" + } + } + }, + "description": "ChangedFileList", + "headers": { + "X-HasMore": { + "description": "True if there is another page", + "schema": { + "type": "boolean" + } + }, + "X-Page": { + "description": "The current page", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-PageCount": { + "description": "Total number of pages", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-PerPage": { + "description": "Commits per page", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-Total": { + "description": "Total commit count", + "schema": { + "format": "int64", + "type": "integer" + } + } + } + }, + "CombinedStatus": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CombinedStatus" + } + } + }, + "description": "CombinedStatus" + }, + "Comment": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Comment" + } + } + }, + "description": "Comment" + }, + "CommentList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Comment" + }, + "type": "array" + } + } + }, + "description": "CommentList" + }, + "Commit": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Commit" + } + } + }, + "description": "Commit" + }, + "CommitList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Commit" + }, + "type": "array" + } + } + }, + "description": "CommitList", + "headers": { + "X-HasMore": { + "description": "True if there is another page", + "schema": { + "type": "boolean" + } + }, + "X-Page": { + "description": "The current page", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-PageCount": { + "description": "Total number of pages", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-PerPage": { + "description": "Commits per page", + "schema": { + "format": "int64", + "type": "integer" + } + }, + "X-Total": { + "description": "Total commit count", + "schema": { + "format": "int64", + "type": "integer" + } + } + } + }, + "CommitStatus": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommitStatus" + } + } + }, + "description": "CommitStatus" + }, + "CommitStatusList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/CommitStatus" + }, + "type": "array" + } + } + }, + "description": "CommitStatusList" + }, + "Compare": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Compare" + } + } + }, + "description": "" + }, + "ContentsExtResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentsExtResponse" + } + } + }, + "description": "" + }, + "ContentsListResponse": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ContentsResponse" + }, + "type": "array" + } + } + }, + "description": "ContentsListResponse" + }, + "ContentsResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentsResponse" + } + } + }, + "description": "ContentsResponse" + }, + "CronList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Cron" + }, + "type": "array" + } + } + }, + "description": "CronList" + }, + "DeployKey": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeployKey" + } + } + }, + "description": "DeployKey" + }, + "DeployKeyList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/DeployKey" + }, + "type": "array" + } + } + }, + "description": "DeployKeyList" + }, + "EmailList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Email" + }, + "type": "array" + } + } + }, + "description": "EmailList" + }, + "EmptyRepository": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "EmptyRepository" + }, + "FileDeleteResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileDeleteResponse" + } + } + }, + "description": "FileDeleteResponse" + }, + "FileResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileResponse" + } + } + }, + "description": "FileResponse" + }, + "FilesResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilesResponse" + } + } + }, + "description": "FilesResponse" + }, + "GPGKey": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GPGKey" + } + } + }, + "description": "GPGKey" + }, + "GPGKeyList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/GPGKey" + }, + "type": "array" + } + } + }, + "description": "GPGKeyList" + }, + "GeneralAPISettings": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralAPISettings" + } + } + }, + "description": "GeneralAPISettings" + }, + "GeneralAttachmentSettings": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralAttachmentSettings" + } + } + }, + "description": "GeneralAttachmentSettings" + }, + "GeneralRepoSettings": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralRepoSettings" + } + } + }, + "description": "GeneralRepoSettings" + }, + "GeneralUISettings": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralUISettings" + } + } + }, + "description": "GeneralUISettings" + }, + "GitBlobResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GitBlobResponse" + } + } + }, + "description": "GitBlobResponse" + }, + "GitHook": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GitHook" + } + } + }, + "description": "GitHook" + }, + "GitHookList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/GitHook" + }, + "type": "array" + } + } + }, + "description": "GitHookList" + }, + "GitTreeResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GitTreeResponse" + } + } + }, + "description": "GitTreeResponse" + }, + "GitignoreTemplateInfo": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GitignoreTemplateInfo" + } + } + }, + "description": "GitignoreTemplateInfo" + }, + "GitignoreTemplateList": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "description": "GitignoreTemplateList" + }, + "Hook": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Hook" + } + } + }, + "description": "Hook" + }, + "HookList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Hook" + }, + "type": "array" + } + } + }, + "description": "HookList" + }, + "Issue": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Issue" + } + } + }, + "description": "Issue" + }, + "IssueDeadline": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueDeadline" + } + } + }, + "description": "IssueDeadline" + }, + "IssueList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Issue" + }, + "type": "array" + } + } + }, + "description": "IssueList" + }, + "IssueTemplates": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/IssueTemplate" + }, + "type": "array" + } + } + }, + "description": "IssueTemplates" + }, + "Label": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Label" + } + } + }, + "description": "Label" + }, + "LabelList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Label" + }, + "type": "array" + } + } + }, + "description": "LabelList" + }, + "LabelTemplateInfo": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/LabelTemplate" + }, + "type": "array" + } + } + }, + "description": "LabelTemplateInfo" + }, + "LabelTemplateList": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "description": "LabelTemplateList" + }, + "LanguageStatistics": { + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "format": "int64", + "type": "integer" + }, + "type": "object" + } + } + }, + "description": "LanguageStatistics" + }, + "LicenseTemplateInfo": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LicenseTemplateInfo" + } + } + }, + "description": "LicenseTemplateInfo" + }, + "LicenseTemplateList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/LicensesTemplateListEntry" + }, + "type": "array" + } + } + }, + "description": "LicenseTemplateList" + }, + "LicensesList": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "description": "LicensesList" + }, + "MarkdownRender": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "MarkdownRender is a rendered markdown document" + }, + "MarkupRender": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "MarkupRender is a rendered markup document" + }, + "MergeUpstreamRequest": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MergeUpstreamRequest" + } + } + }, + "description": "" + }, + "MergeUpstreamResponse": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MergeUpstreamResponse" + } + } + }, + "description": "" + }, + "Milestone": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Milestone" + } + } + }, + "description": "Milestone" + }, + "MilestoneList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Milestone" + }, + "type": "array" + } + } + }, + "description": "MilestoneList" + }, + "NodeInfo": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NodeInfo" + } + } + }, + "description": "NodeInfo" + }, + "Note": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Note" + } + } + }, + "description": "Note" + }, + "NotificationCount": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationCount" + } + } + }, + "description": "Number of unread notifications" + }, + "NotificationThread": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationThread" + } + } + }, + "description": "NotificationThread" + }, + "NotificationThreadList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/NotificationThread" + }, + "type": "array" + } + } + }, + "description": "NotificationThreadList" + }, + "OAuth2Application": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuth2Application" + } + } + }, + "description": "OAuth2Application" + }, + "OAuth2ApplicationList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/OAuth2Application" + }, + "type": "array" + } + } + }, + "description": "OAuth2ApplicationList represents a list of OAuth2 applications." + }, + "Organization": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Organization" + } + } + }, + "description": "Organization" + }, + "OrganizationList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Organization" + }, + "type": "array" + } + } + }, + "description": "OrganizationList" + }, + "OrganizationPermissions": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationPermissions" + } + } + }, + "description": "OrganizationPermissions" + }, + "Package": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Package" + } + } + }, + "description": "Package" + }, + "PackageFileList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PackageFile" + }, + "type": "array" + } + } + }, + "description": "PackageFileList" + }, + "PackageList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Package" + }, + "type": "array" + } + } + }, + "description": "PackageList" + }, + "PublicKey": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicKey" + } + } + }, + "description": "PublicKey" + }, + "PublicKeyList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PublicKey" + }, + "type": "array" + } + } + }, + "description": "PublicKeyList" + }, + "PullRequest": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PullRequest" + } + } + }, + "description": "PullRequest" + }, + "PullRequestList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PullRequest" + }, + "type": "array" + } + } + }, + "description": "PullRequestList" + }, + "PullReview": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PullReview" + } + } + }, + "description": "PullReview" + }, + "PullReviewComment": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PullReviewComment" + } + } + }, + "description": "PullComment" + }, + "PullReviewCommentList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PullReviewComment" + }, + "type": "array" + } + } + }, + "description": "PullCommentList" + }, + "PullReviewList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PullReview" + }, + "type": "array" + } + } + }, + "description": "PullReviewList" + }, + "PushMirror": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PushMirror" + } + } + }, + "description": "PushMirror" + }, + "PushMirrorList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PushMirror" + }, + "type": "array" + } + } + }, + "description": "PushMirrorList" + }, + "Reaction": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Reaction" + } + } + }, + "description": "Reaction" + }, + "ReactionList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Reaction" + }, + "type": "array" + } + } + }, + "description": "ReactionList" + }, + "Reference": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Reference" + } + } + }, + "description": "Reference" + }, + "ReferenceList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Reference" + }, + "type": "array" + } + } + }, + "description": "ReferenceList" + }, + "RegistrationToken": { + "description": "RegistrationToken is response related to registration token", + "headers": { + "token": { + "schema": { + "type": "string" + } + } + } + }, + "Release": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Release" + } + } + }, + "description": "Release" + }, + "ReleaseList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Release" + }, + "type": "array" + } + } + }, + "description": "ReleaseList" + }, + "RepoCollaboratorPermission": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepoCollaboratorPermission" + } + } + }, + "description": "RepoCollaboratorPermission" + }, + "RepoIssueConfig": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueConfig" + } + } + }, + "description": "RepoIssueConfig" + }, + "RepoIssueConfigValidation": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueConfigValidation" + } + } + }, + "description": "RepoIssueConfigValidation" + }, + "RepoNewIssuePinsAllowed": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewIssuePinsAllowed" + } + } + }, + "description": "RepoNewIssuePinsAllowed" + }, + "Repository": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Repository" + } + } + }, + "description": "Repository" + }, + "RepositoryList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Repository" + }, + "type": "array" + } + } + }, + "description": "RepositoryList" + }, + "RunDetails": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunDetails" + } + } + }, + "description": "RunDetails" + }, + "Runner": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionRunner" + } + } + }, + "description": "Runner" + }, + "RunnerList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionRunnersResponse" + } + } + }, + "description": "RunnerList" + }, + "SearchResults": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchResults" + } + } + }, + "description": "SearchResults" + }, + "Secret": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Secret" + } + } + }, + "description": "Secret" + }, + "SecretList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Secret" + }, + "type": "array" + } + } + }, + "description": "SecretList" + }, + "ServerVersion": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerVersion" + } + } + }, + "description": "ServerVersion" + }, + "StopWatch": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StopWatch" + } + } + }, + "description": "StopWatch" + }, + "StopWatchList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/StopWatch" + }, + "type": "array" + } + } + }, + "description": "StopWatchList" + }, + "StringSlice": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "description": "StringSlice" + }, + "Tag": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Tag" + } + } + }, + "description": "Tag" + }, + "TagList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Tag" + }, + "type": "array" + } + } + }, + "description": "TagList" + }, + "TagProtection": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagProtection" + } + } + }, + "description": "TagProtection" + }, + "TagProtectionList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TagProtection" + }, + "type": "array" + } + } + }, + "description": "TagProtectionList" + }, + "TasksList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionTaskResponse" + } + } + }, + "description": "TasksList" + }, + "Team": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Team" + } + } + }, + "description": "Team" + }, + "TeamList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Team" + }, + "type": "array" + } + } + }, + "description": "TeamList" + }, + "TimelineList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TimelineComment" + }, + "type": "array" + } + } + }, + "description": "TimelineList" + }, + "TopicListResponse": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TopicResponse" + }, + "type": "array" + } + } + }, + "description": "TopicListResponse" + }, + "TopicNames": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TopicName" + } + } + }, + "description": "TopicNames" + }, + "TrackedTime": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TrackedTime" + } + } + }, + "description": "TrackedTime" + }, + "TrackedTimeList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TrackedTime" + }, + "type": "array" + } + } + }, + "description": "TrackedTimeList" + }, + "User": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "User" + }, + "UserHeatmapData": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserHeatmapData" + }, + "type": "array" + } + } + }, + "description": "UserHeatmapData" + }, + "UserList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" + } + } + }, + "description": "UserList" + }, + "UserSettings": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserSettings" + }, + "type": "array" + } + } + }, + "description": "UserSettings" + }, + "VariableList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ActionVariable" + }, + "type": "array" + } + } + }, + "description": "VariableList" + }, + "WatchInfo": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WatchInfo" + } + } + }, + "description": "WatchInfo" + }, + "WikiCommitList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WikiCommitList" + } + } + }, + "description": "WikiCommitList" + }, + "WikiPage": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WikiPage" + } + } + }, + "description": "WikiPage" + }, + "WikiPageList": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/WikiPageMetaData" + }, + "type": "array" + } + } + }, + "description": "WikiPageList" + }, + "WorkflowJob": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflowJob" + } + } + }, + "description": "WorkflowJob" + }, + "WorkflowJobsList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflowJobsResponse" + } + } + }, + "description": "WorkflowJobsList" + }, + "WorkflowRun": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflowRun" + } + } + }, + "description": "WorkflowRun" + }, + "WorkflowRunsList": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionWorkflowRunsResponse" + } + } + }, + "description": "WorkflowRunsList" + }, + "conflict": { + "description": "APIConflict is a conflict empty response" + }, + "empty": { + "description": "APIEmpty is an empty response" + }, + "error": { + "description": "APIError is error format response", + "headers": { + "message": { + "schema": { + "type": "string" + } + }, + "url": { + "schema": { + "type": "string" + } + } + } + }, + "forbidden": { + "description": "APIForbiddenError is a forbidden error response", + "headers": { + "message": { + "schema": { + "type": "string" + } + }, + "url": { + "schema": { + "type": "string" + } + } + } + }, + "invalidTopicsError": { + "description": "APIInvalidTopicsError is error format response to invalid topics", + "headers": { + "invalidTopics": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "message": { + "schema": { + "type": "string" + } + } + } + }, + "notFound": { + "description": "APINotFound is a not found empty response" + }, + "parameterBodies": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LockIssueOption" + } + } + }, + "description": "parameterBodies" + }, + "redirect": { + "description": "APIRedirect is a redirect response" + }, + "repoArchivedError": { + "description": "APIRepoArchivedError is an error that is raised when an archived repo should be modified", + "headers": { + "message": { + "schema": { + "type": "string" + } + }, + "url": { + "schema": { + "type": "string" + } + } + } + }, + "string": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "APIString is a string response" + }, + "validationError": { + "description": "APIValidationError is error format response related to input validation", + "headers": { + "message": { + "schema": { + "type": "string" + } + }, + "url": { + "schema": { + "type": "string" + } + } + } + } + }, + "schemas": { + "APIError": { + "description": "APIError is an api error with a message", + "properties": { + "message": { + "description": "Message contains the error description", + "type": "string", + "x-go-name": "Message" + }, + "url": { + "description": "URL contains the documentation URL for this error", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "AccessLevelName": { + "enum": [ + "none", + "read", + "write", + "admin", + "owner" + ], + "type": "string" + }, + "AccessToken": { + "properties": { + "created_at": { + "description": "The timestamp when the token was created", + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "The unique identifier of the access token", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "last_used_at": { + "description": "The timestamp when the token was last used", + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "name": { + "description": "The name of the access token", + "type": "string", + "x-go-name": "Name" + }, + "scopes": { + "description": "The scopes granted to this access token", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Scopes" + }, + "sha1": { + "description": "The SHA1 hash of the access token", + "type": "string", + "x-go-name": "Token" + }, + "token_last_eight": { + "description": "The last eight characters of the token", + "type": "string", + "x-go-name": "TokenLastEight" + } + }, + "title": "AccessToken represents an API access token.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionArtifact": { + "description": "ActionArtifact represents a ActionArtifact", + "properties": { + "archive_download_url": { + "format": "uri", + "type": "string", + "x-go-name": "ArchiveDownloadURL" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "expired": { + "type": "boolean", + "x-go-name": "Expired" + }, + "expires_at": { + "format": "date-time", + "type": "string", + "x-go-name": "ExpiresAt" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "size_in_bytes": { + "format": "int64", + "type": "integer", + "x-go-name": "SizeInBytes" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "UpdatedAt" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "workflow_run": { + "$ref": "#/components/schemas/ActionWorkflowRun" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionArtifactsResponse": { + "description": "ActionArtifactsResponse returns ActionArtifacts", + "properties": { + "artifacts": { + "items": { + "$ref": "#/components/schemas/ActionArtifact" + }, + "type": "array", + "x-go-name": "Entries" + }, + "total_count": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionRunner": { + "description": "ActionRunner represents a Runner", + "properties": { + "busy": { + "type": "boolean", + "x-go-name": "Busy" + }, + "disabled": { + "type": "boolean", + "x-go-name": "Disabled" + }, + "ephemeral": { + "type": "boolean", + "x-go-name": "Ephemeral" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "labels": { + "items": { + "$ref": "#/components/schemas/ActionRunnerLabel" + }, + "type": "array", + "x-go-name": "Labels" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "status": { + "type": "string", + "x-go-name": "Status" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionRunnerLabel": { + "description": "ActionRunnerLabel represents a Runner Label", + "properties": { + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "type": { + "type": "string", + "x-go-name": "Type" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionRunnersResponse": { + "description": "ActionRunnersResponse returns Runners", + "properties": { + "runners": { + "items": { + "$ref": "#/components/schemas/ActionRunner" + }, + "type": "array", + "x-go-name": "Entries" + }, + "total_count": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionTask": { + "description": "ActionTask represents a ActionTask", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "display_title": { + "description": "DisplayTitle is the display title for the workflow run", + "type": "string", + "x-go-name": "DisplayTitle" + }, + "event": { + "description": "Event is the type of event that triggered the workflow", + "type": "string", + "x-go-name": "Event" + }, + "head_branch": { + "description": "HeadBranch is the branch that triggered the workflow", + "type": "string", + "x-go-name": "HeadBranch" + }, + "head_sha": { + "description": "HeadSHA is the commit SHA that triggered the workflow", + "type": "string", + "x-go-name": "HeadSHA" + }, + "id": { + "description": "ID is the unique identifier for the action task", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "Name is the name of the workflow", + "type": "string", + "x-go-name": "Name" + }, + "run_number": { + "description": "RunNumber is the sequential number of the workflow run", + "format": "int64", + "type": "integer", + "x-go-name": "RunNumber" + }, + "run_started_at": { + "format": "date-time", + "type": "string", + "x-go-name": "RunStartedAt" + }, + "status": { + "description": "Status indicates the current status of the workflow run", + "type": "string", + "x-go-name": "Status" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "UpdatedAt" + }, + "url": { + "description": "URL is the API URL for this workflow run", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "workflow_id": { + "description": "WorkflowID is the identifier of the workflow", + "type": "string", + "x-go-name": "WorkflowID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionTaskResponse": { + "description": "ActionTaskResponse returns a ActionTask", + "properties": { + "total_count": { + "description": "TotalCount is the total number of workflow runs", + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + }, + "workflow_runs": { + "description": "Entries contains the list of workflow runs", + "items": { + "$ref": "#/components/schemas/ActionTask" + }, + "type": "array", + "x-go-name": "Entries" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionVariable": { + "description": "ActionVariable return value of the query API", + "properties": { + "data": { + "description": "the value of the variable", + "type": "string", + "x-go-name": "Data" + }, + "description": { + "description": "the description of the variable", + "type": "string", + "x-go-name": "Description" + }, + "name": { + "description": "the name of the variable", + "type": "string", + "x-go-name": "Name" + }, + "owner_id": { + "description": "the owner to which the variable belongs", + "format": "int64", + "type": "integer", + "x-go-name": "OwnerID" + }, + "repo_id": { + "description": "the repository to which the variable belongs", + "format": "int64", + "type": "integer", + "x-go-name": "RepoID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflow": { + "description": "ActionWorkflow represents a ActionWorkflow", + "properties": { + "badge_url": { + "description": "BadgeURL is the URL for the workflow badge", + "format": "uri", + "type": "string", + "x-go-name": "BadgeURL" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "deleted_at": { + "format": "date-time", + "type": "string", + "x-go-name": "DeletedAt" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing the workflow", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "ID is the unique identifier for the workflow", + "type": "string", + "x-go-name": "ID" + }, + "name": { + "description": "Name is the name of the workflow", + "type": "string", + "x-go-name": "Name" + }, + "path": { + "description": "Path is the file path of the workflow", + "type": "string", + "x-go-name": "Path" + }, + "state": { + "description": "State indicates if the workflow is active or disabled", + "type": "string", + "x-go-name": "State" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "UpdatedAt" + }, + "url": { + "description": "URL is the API URL for this workflow", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowJob": { + "description": "ActionWorkflowJob represents a WorkflowJob", + "properties": { + "completed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CompletedAt" + }, + "conclusion": { + "type": "string", + "x-go-name": "Conclusion" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "head_branch": { + "type": "string", + "x-go-name": "HeadBranch" + }, + "head_sha": { + "type": "string", + "x-go-name": "HeadSha" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "labels": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Labels" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "run_attempt": { + "format": "int64", + "type": "integer", + "x-go-name": "RunAttempt" + }, + "run_id": { + "format": "int64", + "type": "integer", + "x-go-name": "RunID" + }, + "run_url": { + "format": "uri", + "type": "string", + "x-go-name": "RunURL" + }, + "runner_id": { + "format": "int64", + "type": "integer", + "x-go-name": "RunnerID" + }, + "runner_name": { + "type": "string", + "x-go-name": "RunnerName" + }, + "started_at": { + "format": "date-time", + "type": "string", + "x-go-name": "StartedAt" + }, + "status": { + "type": "string", + "x-go-name": "Status" + }, + "steps": { + "items": { + "$ref": "#/components/schemas/ActionWorkflowStep" + }, + "type": "array", + "x-go-name": "Steps" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowJobsResponse": { + "description": "ActionWorkflowJobsResponse returns ActionWorkflowJobs", + "properties": { + "jobs": { + "items": { + "$ref": "#/components/schemas/ActionWorkflowJob" + }, + "type": "array", + "x-go-name": "Entries" + }, + "total_count": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowResponse": { + "description": "ActionWorkflowResponse returns a ActionWorkflow", + "properties": { + "total_count": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + }, + "workflows": { + "items": { + "$ref": "#/components/schemas/ActionWorkflow" + }, + "type": "array", + "x-go-name": "Workflows" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowRun": { + "description": "ActionWorkflowRun represents a WorkflowRun", + "properties": { + "actor": { + "$ref": "#/components/schemas/User" + }, + "completed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CompletedAt" + }, + "conclusion": { + "type": "string", + "x-go-name": "Conclusion" + }, + "display_title": { + "type": "string", + "x-go-name": "DisplayTitle" + }, + "event": { + "type": "string", + "x-go-name": "Event" + }, + "head_branch": { + "type": "string", + "x-go-name": "HeadBranch" + }, + "head_repository": { + "$ref": "#/components/schemas/Repository" + }, + "head_sha": { + "type": "string", + "x-go-name": "HeadSha" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "path": { + "type": "string", + "x-go-name": "Path" + }, + "previous_attempt_url": { + "description": "PreviousAttemptURL is the API URL of the previous attempt of this run, e.g. \".../actions/runs/{run_id}/attempts/{attempt-1}\".\nIt is set only when the current attempt is \u003e 1 (i.e. a rerun). For the first attempt, or for legacy runs that pre-date ActionRunAttempt, it is null.", + "format": "uri", + "type": "string", + "x-go-name": "PreviousAttemptURL" + }, + "repository": { + "$ref": "#/components/schemas/Repository" + }, + "repository_id": { + "format": "int64", + "type": "integer", + "x-go-name": "RepositoryID" + }, + "run_attempt": { + "description": "RunAttempt is 1-based for runs created after ActionRunAttempt was introduced.\nA value of 0 is a legacy-only sentinel for runs created before attempts existed\nand indicates no corresponding /attempts/{n} resource is available.", + "format": "int64", + "type": "integer", + "x-go-name": "RunAttempt" + }, + "run_number": { + "format": "int64", + "type": "integer", + "x-go-name": "RunNumber" + }, + "started_at": { + "format": "date-time", + "type": "string", + "x-go-name": "StartedAt" + }, + "status": { + "type": "string", + "x-go-name": "Status" + }, + "trigger_actor": { + "$ref": "#/components/schemas/User" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowRunsResponse": { + "description": "ActionWorkflowRunsResponse returns ActionWorkflowRuns", + "properties": { + "total_count": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + }, + "workflow_runs": { + "items": { + "$ref": "#/components/schemas/ActionWorkflowRun" + }, + "type": "array", + "x-go-name": "Entries" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionWorkflowStep": { + "description": "ActionWorkflowStep represents a step of a WorkflowJob", + "properties": { + "completed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CompletedAt" + }, + "conclusion": { + "type": "string", + "x-go-name": "Conclusion" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "number": { + "format": "int64", + "type": "integer", + "x-go-name": "Number" + }, + "started_at": { + "format": "date-time", + "type": "string", + "x-go-name": "StartedAt" + }, + "status": { + "type": "string", + "x-go-name": "Status" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Activity": { + "properties": { + "act_user": { + "$ref": "#/components/schemas/User" + }, + "act_user_id": { + "description": "The ID of the user who performed the action", + "format": "int64", + "type": "integer", + "x-go-name": "ActUserID" + }, + "comment": { + "$ref": "#/components/schemas/Comment" + }, + "comment_id": { + "description": "The ID of the comment associated with the activity (if applicable)", + "format": "int64", + "type": "integer", + "x-go-name": "CommentID" + }, + "content": { + "description": "Additional content or details about the activity", + "type": "string", + "x-go-name": "Content" + }, + "created": { + "description": "The date and time when the activity occurred", + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "The unique identifier of the activity", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "is_private": { + "description": "Whether this activity is from a private repository", + "type": "boolean", + "x-go-name": "IsPrivate" + }, + "op_type": { + "description": "the type of action", + "enum": [ + "create_repo", + "rename_repo", + "star_repo", + "watch_repo", + "commit_repo", + "create_issue", + "create_pull_request", + "transfer_repo", + "push_tag", + "comment_issue", + "merge_pull_request", + "close_issue", + "reopen_issue", + "close_pull_request", + "reopen_pull_request", + "delete_tag", + "delete_branch", + "mirror_sync_push", + "mirror_sync_create", + "mirror_sync_delete", + "approve_pull_request", + "reject_pull_request", + "comment_pull", + "publish_release", + "pull_review_dismissed", + "pull_request_ready_for_review", + "auto_merge_pull_request" + ], + "type": "string", + "x-go-name": "OpType" + }, + "ref_name": { + "description": "The name of the git reference (branch/tag) associated with the activity", + "type": "string", + "x-go-name": "RefName" + }, + "repo": { + "$ref": "#/components/schemas/Repository" + }, + "repo_id": { + "description": "The ID of the repository associated with the activity", + "format": "int64", + "type": "integer", + "x-go-name": "RepoID" + }, + "user_id": { + "description": "The ID of the user who receives/sees this activity", + "format": "int64", + "type": "integer", + "x-go-name": "UserID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "AddCollaboratorOption": { + "description": "AddCollaboratorOption options when adding a user as a collaborator of a repository", + "properties": { + "permission": { + "allOf": [ + { + "$ref": "#/components/schemas/RepoWritePermission" + } + ], + "description": "Permission level to grant the collaborator\nread RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "AddTimeOption": { + "description": "AddTimeOption options for adding time to an issue", + "properties": { + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "time": { + "description": "time in seconds", + "format": "int64", + "type": "integer", + "x-go-name": "Time" + }, + "user_name": { + "description": "username of the user who spent the time working on the issue (optional)", + "type": "string", + "x-go-name": "User" + } + }, + "required": [ + "time" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "AnnotatedTag": { + "description": "AnnotatedTag represents an annotated tag", + "properties": { + "message": { + "description": "The message associated with the annotated tag", + "type": "string", + "x-go-name": "Message" + }, + "object": { + "$ref": "#/components/schemas/AnnotatedTagObject" + }, + "sha": { + "description": "The SHA hash of the annotated tag", + "type": "string", + "x-go-name": "SHA" + }, + "tag": { + "description": "The name of the annotated tag", + "type": "string", + "x-go-name": "Tag" + }, + "tagger": { + "$ref": "#/components/schemas/CommitUser" + }, + "url": { + "description": "The URL to access the annotated tag", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "AnnotatedTagObject": { + "description": "AnnotatedTagObject contains meta information of the tag object", + "properties": { + "sha": { + "description": "The SHA hash of the tagged object", + "type": "string", + "x-go-name": "SHA" + }, + "type": { + "description": "The type of the tagged object (e.g., commit, tree)", + "type": "string", + "x-go-name": "Type" + }, + "url": { + "description": "The URL to access the tagged object", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ApplyDiffPatchFileOptions": { + "description": "ApplyDiffPatchFileOptions options for applying a diff patch\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", + "properties": { + "author": { + "$ref": "#/components/schemas/Identity" + }, + "branch": { + "description": "branch (optional) is the base branch for the changes. If not supplied, the default branch is used", + "type": "string", + "x-go-name": "BranchName" + }, + "committer": { + "$ref": "#/components/schemas/Identity" + }, + "content": { + "type": "string", + "x-go-name": "Content" + }, + "dates": { + "$ref": "#/components/schemas/CommitDateOptions" + }, + "force_push": { + "description": "force_push (optional) will do a force-push if the new branch already exists", + "type": "boolean", + "x-go-name": "ForcePush" + }, + "message": { + "description": "message (optional) is the commit message of the changes. If not supplied, a default message will be used", + "type": "string", + "x-go-name": "Message" + }, + "new_branch": { + "description": "new_branch (optional) will make a new branch from base branch for the changes. If not supplied, the changes will be committed to the base branch", + "type": "string", + "x-go-name": "NewBranchName" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" + } + }, + "required": [ + "content" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Attachment": { + "description": "Attachment a generic attachment", + "properties": { + "browser_download_url": { + "description": "DownloadURL is the URL to download the attachment", + "format": "uri", + "type": "string", + "x-go-name": "DownloadURL" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "download_count": { + "description": "DownloadCount is the number of times the attachment has been downloaded", + "format": "int64", + "type": "integer", + "x-go-name": "DownloadCount" + }, + "id": { + "description": "ID is the unique identifier for the attachment", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "Name is the filename of the attachment", + "type": "string", + "x-go-name": "Name" + }, + "size": { + "description": "Size is the file size in bytes", + "format": "int64", + "type": "integer", + "x-go-name": "Size" + }, + "uuid": { + "description": "UUID is the unique identifier for the attachment file", + "type": "string", + "x-go-name": "UUID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Badge": { + "description": "Badge represents a user badge", + "properties": { + "description": { + "type": "string", + "x-go-name": "Description" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "image_url": { + "format": "uri", + "type": "string", + "x-go-name": "ImageURL" + }, + "slug": { + "type": "string", + "x-go-name": "Slug" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Branch": { + "description": "Branch represents a repository branch", + "properties": { + "commit": { + "$ref": "#/components/schemas/PayloadCommit" + }, + "effective_branch_protection_name": { + "description": "EffectiveBranchProtectionName is the name of the effective branch protection rule", + "type": "string", + "x-go-name": "EffectiveBranchProtectionName" + }, + "enable_status_check": { + "description": "EnableStatusCheck indicates if status checks are enabled", + "type": "boolean", + "x-go-name": "EnableStatusCheck" + }, + "name": { + "description": "Name is the branch name", + "type": "string", + "x-go-name": "Name" + }, + "protected": { + "description": "Protected indicates if the branch is protected", + "type": "boolean", + "x-go-name": "Protected" + }, + "required_approvals": { + "description": "RequiredApprovals is the number of required approvals for pull requests", + "format": "int64", + "type": "integer", + "x-go-name": "RequiredApprovals" + }, + "status_check_contexts": { + "description": "StatusCheckContexts contains the list of required status check contexts", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "StatusCheckContexts" + }, + "user_can_merge": { + "description": "UserCanMerge indicates if the current user can merge to this branch", + "type": "boolean", + "x-go-name": "UserCanMerge" + }, + "user_can_push": { + "description": "UserCanPush indicates if the current user can push to this branch", + "type": "boolean", + "x-go-name": "UserCanPush" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "BranchProtection": { + "description": "BranchProtection represents a branch protection for a repository", + "properties": { + "approvals_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistTeams" + }, + "approvals_whitelist_username": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistUsernames" + }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, + "block_on_official_review_requests": { + "type": "boolean", + "x-go-name": "BlockOnOfficialReviewRequests" + }, + "block_on_outdated_branch": { + "type": "boolean", + "x-go-name": "BlockOnOutdatedBranch" + }, + "block_on_rejected_reviews": { + "type": "boolean", + "x-go-name": "BlockOnRejectedReviews" + }, + "branch_name": { + "deprecated": true, + "description": "Deprecated: true", + "type": "string", + "x-go-name": "BranchName" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "dismiss_stale_approvals": { + "type": "boolean", + "x-go-name": "DismissStaleApprovals" + }, + "enable_approvals_whitelist": { + "type": "boolean", + "x-go-name": "EnableApprovalsWhitelist" + }, + "enable_force_push": { + "type": "boolean", + "x-go-name": "EnableForcePush" + }, + "enable_force_push_allowlist": { + "type": "boolean", + "x-go-name": "EnableForcePushAllowlist" + }, + "enable_merge_whitelist": { + "type": "boolean", + "x-go-name": "EnableMergeWhitelist" + }, + "enable_push": { + "type": "boolean", + "x-go-name": "EnablePush" + }, + "enable_push_whitelist": { + "type": "boolean", + "x-go-name": "EnablePushWhitelist" + }, + "enable_status_check": { + "type": "boolean", + "x-go-name": "EnableStatusCheck" + }, + "force_push_allowlist_deploy_keys": { + "type": "boolean", + "x-go-name": "ForcePushAllowlistDeployKeys" + }, + "force_push_allowlist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistTeams" + }, + "force_push_allowlist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistUsernames" + }, + "ignore_stale_approvals": { + "type": "boolean", + "x-go-name": "IgnoreStaleApprovals" + }, + "merge_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistTeams" + }, + "merge_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistUsernames" + }, + "priority": { + "description": "Priority is the priority of this branch protection rule", + "format": "int64", + "type": "integer", + "x-go-name": "Priority" + }, + "protected_file_patterns": { + "type": "string", + "x-go-name": "ProtectedFilePatterns" + }, + "push_whitelist_deploy_keys": { + "type": "boolean", + "x-go-name": "PushWhitelistDeployKeys" + }, + "push_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistTeams" + }, + "push_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistUsernames" + }, + "require_signed_commits": { + "type": "boolean", + "x-go-name": "RequireSignedCommits" + }, + "required_approvals": { + "format": "int64", + "type": "integer", + "x-go-name": "RequiredApprovals" + }, + "rule_name": { + "description": "RuleName is the name of the branch protection rule", + "type": "string", + "x-go-name": "RuleName" + }, + "status_check_contexts": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "StatusCheckContexts" + }, + "unprotected_file_patterns": { + "type": "string", + "x-go-name": "UnprotectedFilePatterns" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ChangeFileOperation": { + "description": "ChangeFileOperation for creating, updating or deleting a file", + "properties": { + "content": { + "description": "new or updated file content, it must be base64 encoded", + "type": "string", + "x-go-name": "ContentBase64" + }, + "from_path": { + "description": "old path of the file to move", + "type": "string", + "x-go-name": "FromPath" + }, + "operation": { + "description": "indicates what to do with the file: \"create\" for creating a new file, \"update\" for updating an existing file,\n\"upload\" for creating or updating a file, \"rename\" for renaming a file, and \"delete\" for deleting an existing file.", + "enum": [ + "create", + "update", + "upload", + "rename", + "delete" + ], + "type": "string", + "x-go-name": "Operation" + }, + "path": { + "description": "path to the existing or new file", + "type": "string", + "x-go-name": "Path" + }, + "sha": { + "description": "the blob ID (SHA) for the file that already exists, required for changing existing files", + "type": "string", + "x-go-name": "SHA" + } + }, + "required": [ + "operation", + "path" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ChangeFilesOptions": { + "description": "ChangeFilesOptions options for creating, updating or deleting multiple files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", + "properties": { + "author": { + "$ref": "#/components/schemas/Identity" + }, + "branch": { + "description": "branch (optional) is the base branch for the changes. If not supplied, the default branch is used", + "type": "string", + "x-go-name": "BranchName" + }, + "committer": { + "$ref": "#/components/schemas/Identity" + }, + "dates": { + "$ref": "#/components/schemas/CommitDateOptions" + }, + "files": { + "description": "list of file operations", + "items": { + "$ref": "#/components/schemas/ChangeFileOperation" + }, + "type": "array", + "x-go-name": "Files" + }, + "force_push": { + "description": "force_push (optional) will do a force-push if the new branch already exists", + "type": "boolean", + "x-go-name": "ForcePush" + }, + "message": { + "description": "message (optional) is the commit message of the changes. If not supplied, a default message will be used", + "type": "string", + "x-go-name": "Message" + }, + "new_branch": { + "description": "new_branch (optional) will make a new branch from base branch for the changes. If not supplied, the changes will be committed to the base branch", + "type": "string", + "x-go-name": "NewBranchName" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" + } + }, + "required": [ + "files" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ChangedFile": { + "description": "ChangedFile store information about files affected by the pull request", + "properties": { + "additions": { + "description": "The number of lines added to the file", + "format": "int64", + "type": "integer", + "x-go-name": "Additions" + }, + "changes": { + "description": "The total number of changes to the file", + "format": "int64", + "type": "integer", + "x-go-name": "Changes" + }, + "contents_url": { + "description": "The API URL to get the file contents", + "format": "uri", + "type": "string", + "x-go-name": "ContentsURL" + }, + "deletions": { + "description": "The number of lines deleted from the file", + "format": "int64", + "type": "integer", + "x-go-name": "Deletions" + }, + "filename": { + "description": "The name of the changed file", + "type": "string", + "x-go-name": "Filename" + }, + "html_url": { + "description": "The HTML URL to view the file changes", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "previous_filename": { + "description": "The previous filename if the file was renamed", + "type": "string", + "x-go-name": "PreviousFilename" + }, + "raw_url": { + "description": "The raw URL to download the file", + "format": "uri", + "type": "string", + "x-go-name": "RawURL" + }, + "status": { + "description": "The status of the file change (added, modified, deleted, etc.)", + "type": "string", + "x-go-name": "Status" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CombinedStatus": { + "description": "CombinedStatus holds the combined state of several statuses for a single commit", + "properties": { + "commit_url": { + "description": "CommitURL is the API URL for the commit", + "format": "uri", + "type": "string", + "x-go-name": "CommitURL" + }, + "repository": { + "$ref": "#/components/schemas/Repository" + }, + "sha": { + "description": "SHA is the commit SHA this status applies to", + "type": "string", + "x-go-name": "SHA" + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/CommitStatusState" + } + ], + "description": "State is the overall combined status state\npending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped" + }, + "statuses": { + "description": "Statuses contains all individual commit statuses", + "items": { + "$ref": "#/components/schemas/CommitStatus" + }, + "type": "array", + "x-go-name": "Statuses" + }, + "total_count": { + "description": "TotalCount is the total number of statuses", + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + }, + "url": { + "description": "URL is the API URL for this combined status", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Comment": { + "description": "Comment represents a comment on a commit or issue", + "properties": { + "assets": { + "description": "Attachments contains files attached to the comment", + "items": { + "$ref": "#/components/schemas/Attachment" + }, + "type": "array", + "x-go-name": "Attachments" + }, + "body": { + "description": "Body contains the comment text content", + "type": "string", + "x-go-name": "Body" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing the comment", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "ID is the unique identifier for the comment", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "issue_url": { + "description": "IssueURL is the API URL for the issue", + "format": "uri", + "type": "string", + "x-go-name": "IssueURL" + }, + "original_author": { + "description": "OriginalAuthor is the original author name (for imported comments)", + "type": "string", + "x-go-name": "OriginalAuthor" + }, + "original_author_id": { + "description": "OriginalAuthorID is the original author ID (for imported comments)", + "format": "int64", + "type": "integer", + "x-go-name": "OriginalAuthorID" + }, + "pull_request_url": { + "description": "PRURL is the API URL for the pull request (if applicable)", + "format": "uri", + "type": "string", + "x-go-name": "PRURL" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Commit": { + "properties": { + "author": { + "$ref": "#/components/schemas/User" + }, + "commit": { + "$ref": "#/components/schemas/RepoCommit" + }, + "committer": { + "$ref": "#/components/schemas/User" + }, + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "files": { + "description": "Files contains information about files affected by the commit", + "items": { + "$ref": "#/components/schemas/CommitAffectedFiles" + }, + "type": "array", + "x-go-name": "Files" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing the commit", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "parents": { + "description": "Parents contains the parent commit information", + "items": { + "$ref": "#/components/schemas/CommitMeta" + }, + "type": "array", + "x-go-name": "Parents" + }, + "sha": { + "description": "SHA is the commit SHA hash", + "type": "string", + "x-go-name": "SHA" + }, + "stats": { + "$ref": "#/components/schemas/CommitStats" + }, + "url": { + "description": "URL is the API URL for the commit", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "title": "Commit contains information generated from a Git commit.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitAffectedFiles": { + "description": "CommitAffectedFiles store information about files affected by the commit", + "properties": { + "filename": { + "description": "Filename is the path of the affected file", + "type": "string", + "x-go-name": "Filename" + }, + "status": { + "description": "Status indicates how the file was affected (added, modified, deleted)", + "type": "string", + "x-go-name": "Status" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitDateOptions": { + "description": "CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE", + "properties": { + "author": { + "format": "date-time", + "type": "string", + "x-go-name": "Author" + }, + "committer": { + "format": "date-time", + "type": "string", + "x-go-name": "Committer" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitMeta": { + "properties": { + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "sha": { + "description": "SHA is the commit SHA hash", + "type": "string", + "x-go-name": "SHA" + }, + "url": { + "description": "URL is the API URL for the commit", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "title": "CommitMeta contains meta information of a commit in terms of API.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitStats": { + "description": "CommitStats is statistics for a RepoCommit", + "properties": { + "additions": { + "description": "Additions is the number of lines added", + "format": "int64", + "type": "integer", + "x-go-name": "Additions" + }, + "deletions": { + "description": "Deletions is the number of lines deleted", + "format": "int64", + "type": "integer", + "x-go-name": "Deletions" + }, + "total": { + "description": "Total is the total number of lines changed", + "format": "int64", + "type": "integer", + "x-go-name": "Total" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitStatus": { + "description": "CommitStatus holds a single status of a single Commit", + "properties": { + "context": { + "description": "Context is the unique context identifier for the status", + "type": "string", + "x-go-name": "Context" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "creator": { + "$ref": "#/components/schemas/User" + }, + "description": { + "description": "Description provides a brief description of the status", + "type": "string", + "x-go-name": "Description" + }, + "id": { + "description": "ID is the unique identifier for the commit status", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/CommitStatusState" + } + ], + "description": "State represents the status state (pending, success, error, failure)\npending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped" + }, + "target_url": { + "description": "TargetURL is the URL to link to for more details", + "format": "uri", + "type": "string", + "x-go-name": "TargetURL" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "url": { + "description": "URL is the API URL for this status", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CommitStatusState": { + "enum": [ + "pending", + "success", + "error", + "failure", + "warning", + "skipped" + ], + "type": "string" + }, + "CommitUser": { + "properties": { + "date": { + "description": "Date is the commit date in string format", + "type": "string", + "x-go-name": "Date" + }, + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "name": { + "description": "Name is the person's name", + "type": "string", + "x-go-name": "Name" + } + }, + "title": "CommitUser contains information of a user in the context of a commit.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Compare": { + "properties": { + "commits": { + "items": { + "$ref": "#/components/schemas/Commit" + }, + "type": "array", + "x-go-name": "Commits" + }, + "total_commits": { + "format": "int64", + "type": "integer", + "x-go-name": "TotalCommits" + } + }, + "title": "Compare represents a comparison between two commits.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ContentsExtResponse": { + "properties": { + "dir_contents": { + "description": "DirContents contains directory listing when the path represents a directory", + "items": { + "$ref": "#/components/schemas/ContentsResponse" + }, + "type": "array", + "x-go-name": "DirContents" + }, + "file_contents": { + "$ref": "#/components/schemas/ContentsResponse" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ContentsResponse": { + "description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content", + "properties": { + "_links": { + "$ref": "#/components/schemas/FileLinksResponse" + }, + "content": { + "description": "`content` is populated when `type` is `file`, otherwise null", + "type": "string", + "x-go-name": "Content" + }, + "download_url": { + "description": "DownloadURL is the direct download URL for this file", + "format": "uri", + "type": "string", + "x-go-name": "DownloadURL" + }, + "encoding": { + "description": "`encoding` is populated when `type` is `file`, otherwise null", + "type": "string", + "x-go-name": "Encoding" + }, + "git_url": { + "description": "GitURL is the Git API URL for this blob or tree", + "format": "uri", + "type": "string", + "x-go-name": "GitURL" + }, + "html_url": { + "description": "HTMLURL is the web URL for this file or directory", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "last_author_date": { + "format": "date-time", + "type": "string", + "x-go-name": "LastAuthorDate" + }, + "last_commit_message": { + "description": "LastCommitMessage is the message of the last commit that affected this file", + "type": "string", + "x-go-name": "LastCommitMessage" + }, + "last_commit_sha": { + "description": "LastCommitSHA is the SHA of the last commit that affected this file", + "type": "string", + "x-go-name": "LastCommitSHA" + }, + "last_committer_date": { + "format": "date-time", + "type": "string", + "x-go-name": "LastCommitterDate" + }, + "lfs_oid": { + "description": "LfsOid is the Git LFS object ID if this file is stored in LFS", + "type": "string", + "x-go-name": "LfsOid" + }, + "lfs_size": { + "description": "LfsSize is the file size if this file is stored in LFS", + "format": "int64", + "type": "integer", + "x-go-name": "LfsSize" + }, + "name": { + "description": "Name is the file or directory name", + "type": "string", + "x-go-name": "Name" + }, + "path": { + "description": "Path is the full path to the file or directory", + "type": "string", + "x-go-name": "Path" + }, + "sha": { + "description": "SHA is the Git blob or tree SHA", + "type": "string", + "x-go-name": "SHA" + }, + "size": { + "description": "Size is the file size in bytes", + "format": "int64", + "type": "integer", + "x-go-name": "Size" + }, + "submodule_git_url": { + "description": "`submodule_git_url` is populated when `type` is `submodule`, otherwise null", + "format": "uri", + "type": "string", + "x-go-name": "SubmoduleGitURL" + }, + "target": { + "description": "`target` is populated when `type` is `symlink`, otherwise null", + "type": "string", + "x-go-name": "Target" + }, + "type": { + "description": "`type` will be `file`, `dir`, `symlink`, or `submodule`", + "type": "string", + "x-go-name": "Type" + }, + "url": { + "description": "URL is the API URL for this file or directory", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateAccessTokenOption": { + "description": "CreateAccessTokenOption options when create access token", + "properties": { + "name": { + "type": "string", + "x-go-name": "Name" + }, + "scopes": { + "example": [ + "all", + "read:activitypub", + "read:issue", + "write:misc", + "read:notification", + "read:organization", + "read:package", + "read:repository", + "read:user" + ], + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Scopes" + } + }, + "required": [ + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateActionWorkflowDispatch": { + "description": "CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event", + "properties": { + "inputs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "x-go-name": "Inputs" + }, + "ref": { + "example": "refs/heads/main", + "type": "string", + "x-go-name": "Ref" + } + }, + "required": [ + "ref" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateBranchProtectionOption": { + "description": "CreateBranchProtectionOption options for creating a branch protection", + "properties": { + "approvals_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistTeams" + }, + "approvals_whitelist_username": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistUsernames" + }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, + "block_on_official_review_requests": { + "type": "boolean", + "x-go-name": "BlockOnOfficialReviewRequests" + }, + "block_on_outdated_branch": { + "type": "boolean", + "x-go-name": "BlockOnOutdatedBranch" + }, + "block_on_rejected_reviews": { + "type": "boolean", + "x-go-name": "BlockOnRejectedReviews" + }, + "branch_name": { + "deprecated": true, + "description": "Deprecated: true", + "type": "string", + "x-go-name": "BranchName" + }, + "dismiss_stale_approvals": { + "type": "boolean", + "x-go-name": "DismissStaleApprovals" + }, + "enable_approvals_whitelist": { + "type": "boolean", + "x-go-name": "EnableApprovalsWhitelist" + }, + "enable_force_push": { + "type": "boolean", + "x-go-name": "EnableForcePush" + }, + "enable_force_push_allowlist": { + "type": "boolean", + "x-go-name": "EnableForcePushAllowlist" + }, + "enable_merge_whitelist": { + "type": "boolean", + "x-go-name": "EnableMergeWhitelist" + }, + "enable_push": { + "type": "boolean", + "x-go-name": "EnablePush" + }, + "enable_push_whitelist": { + "type": "boolean", + "x-go-name": "EnablePushWhitelist" + }, + "enable_status_check": { + "type": "boolean", + "x-go-name": "EnableStatusCheck" + }, + "force_push_allowlist_deploy_keys": { + "type": "boolean", + "x-go-name": "ForcePushAllowlistDeployKeys" + }, + "force_push_allowlist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistTeams" + }, + "force_push_allowlist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistUsernames" + }, + "ignore_stale_approvals": { + "type": "boolean", + "x-go-name": "IgnoreStaleApprovals" + }, + "merge_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistTeams" + }, + "merge_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistUsernames" + }, + "priority": { + "format": "int64", + "type": "integer", + "x-go-name": "Priority" + }, + "protected_file_patterns": { + "type": "string", + "x-go-name": "ProtectedFilePatterns" + }, + "push_whitelist_deploy_keys": { + "type": "boolean", + "x-go-name": "PushWhitelistDeployKeys" + }, + "push_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistTeams" + }, + "push_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistUsernames" + }, + "require_signed_commits": { + "type": "boolean", + "x-go-name": "RequireSignedCommits" + }, + "required_approvals": { + "format": "int64", + "type": "integer", + "x-go-name": "RequiredApprovals" + }, + "rule_name": { + "type": "string", + "x-go-name": "RuleName" + }, + "status_check_contexts": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "StatusCheckContexts" + }, + "unprotected_file_patterns": { + "type": "string", + "x-go-name": "UnprotectedFilePatterns" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateBranchRepoOption": { + "description": "CreateBranchRepoOption options when creating a branch in a repository", + "properties": { + "new_branch_name": { + "description": "Name of the branch to create", + "type": "string", + "uniqueItems": true, + "x-go-name": "BranchName" + }, + "old_branch_name": { + "deprecated": true, + "description": "Deprecated: true\nName of the old branch to create from", + "type": "string", + "uniqueItems": true, + "x-go-name": "OldBranchName" + }, + "old_ref_name": { + "description": "Name of the old branch/tag/commit to create from", + "type": "string", + "uniqueItems": true, + "x-go-name": "OldRefName" + } + }, + "required": [ + "new_branch_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateEmailOption": { + "description": "CreateEmailOption options when creating email addresses", + "properties": { + "emails": { + "description": "email addresses to add", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Emails" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateFileOptions": { + "description": "CreateFileOptions options for creating a file\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", + "properties": { + "author": { + "$ref": "#/components/schemas/Identity" + }, + "branch": { + "description": "branch (optional) is the base branch for the changes. If not supplied, the default branch is used", + "type": "string", + "x-go-name": "BranchName" + }, + "committer": { + "$ref": "#/components/schemas/Identity" + }, + "content": { + "description": "content must be base64 encoded", + "type": "string", + "x-go-name": "ContentBase64" + }, + "dates": { + "$ref": "#/components/schemas/CommitDateOptions" + }, + "force_push": { + "description": "force_push (optional) will do a force-push if the new branch already exists", + "type": "boolean", + "x-go-name": "ForcePush" + }, + "message": { + "description": "message (optional) is the commit message of the changes. If not supplied, a default message will be used", + "type": "string", + "x-go-name": "Message" + }, + "new_branch": { + "description": "new_branch (optional) will make a new branch from base branch for the changes. If not supplied, the changes will be committed to the base branch", + "type": "string", + "x-go-name": "NewBranchName" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" + } + }, + "required": [ + "content" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateForkOption": { + "description": "CreateForkOption options for creating a fork", + "properties": { + "name": { + "description": "name of the forked repository", + "type": "string", + "x-go-name": "Name" + }, + "organization": { + "description": "organization name, if forking into an organization", + "type": "string", + "x-go-name": "Organization" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateGPGKeyOption": { + "description": "CreateGPGKeyOption options create user GPG key", + "properties": { + "armored_public_key": { + "description": "An armored GPG key to add", + "type": "string", + "uniqueItems": true, + "x-go-name": "ArmoredKey" + }, + "armored_signature": { + "description": "An optional armored signature for the GPG key", + "type": "string", + "x-go-name": "Signature" + } + }, + "required": [ + "armored_public_key" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateHookOption": { + "description": "CreateHookOption options when create a hook", + "properties": { + "active": { + "default": false, + "type": "boolean", + "x-go-name": "Active" + }, + "authorization_header": { + "description": "Authorization header to include in webhook requests", + "type": "string", + "x-go-name": "AuthorizationHeader" + }, + "branch_filter": { + "description": "Branch filter pattern to determine which branches trigger the webhook", + "type": "string", + "x-go-name": "BranchFilter" + }, + "config": { + "$ref": "#/components/schemas/CreateHookOptionConfig" + }, + "events": { + "description": "List of events that will trigger this webhook", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Events" + }, + "name": { + "description": "Optional human-readable name for the webhook", + "type": "string", + "x-go-name": "Name" + }, + "type": { + "enum": [ + "dingtalk", + "discord", + "gitea", + "gogs", + "msteams", + "slack", + "telegram", + "feishu", + "wechatwork", + "packagist" + ], + "type": "string", + "x-go-name": "Type" + } + }, + "required": [ + "type", + "config" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateHookOptionConfig": { + "additionalProperties": { + "type": "string" + }, + "description": "CreateHookOptionConfig has all config options in it\nrequired are \"content_type\" and \"url\" Required", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateIssueCommentOption": { + "description": "CreateIssueCommentOption options for creating a comment on an issue", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + } + }, + "required": [ + "body" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateIssueOption": { + "description": "CreateIssueOption options to create one issue", + "properties": { + "assignee": { + "deprecated": true, + "description": "deprecated", + "type": "string", + "x-go-name": "Assignee" + }, + "assignees": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "body": { + "type": "string", + "x-go-name": "Body" + }, + "closed": { + "type": "boolean", + "x-go-name": "Closed" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "labels": { + "description": "list of label ids", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array", + "x-go-name": "Labels" + }, + "milestone": { + "description": "milestone id", + "format": "int64", + "type": "integer", + "x-go-name": "Milestone" + }, + "ref": { + "type": "string", + "x-go-name": "Ref" + }, + "title": { + "type": "string", + "x-go-name": "Title" + } + }, + "required": [ + "title" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateKeyOption": { + "description": "CreateKeyOption options when creating a key", + "properties": { + "key": { + "description": "An armored SSH key to add", + "type": "string", + "uniqueItems": true, + "x-go-name": "Key" + }, + "read_only": { + "description": "Describe if the key has only read access or read/write", + "type": "boolean", + "x-go-name": "ReadOnly" + }, + "title": { + "description": "Title of the key to add", + "type": "string", + "uniqueItems": true, + "x-go-name": "Title" + } + }, + "required": [ + "title", + "key" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateLabelOption": { + "description": "CreateLabelOption options for creating a label", + "properties": { + "color": { + "example": "#00aabb", + "type": "string", + "x-go-name": "Color" + }, + "description": { + "description": "Description provides additional context about the label's purpose", + "type": "string", + "x-go-name": "Description" + }, + "exclusive": { + "example": false, + "type": "boolean", + "x-go-name": "Exclusive" + }, + "is_archived": { + "example": false, + "type": "boolean", + "x-go-name": "IsArchived" + }, + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "required": [ + "name", + "color" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateMilestoneOption": { + "description": "CreateMilestoneOption options for creating a milestone", + "properties": { + "description": { + "description": "Description provides details about the milestone", + "type": "string", + "x-go-name": "Description" + }, + "due_on": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "state": { + "$ref": "#/components/schemas/StateType" + }, + "title": { + "description": "Title is the title of the new milestone", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateOAuth2ApplicationOptions": { + "description": "CreateOAuth2ApplicationOptions holds options to create an oauth2 application", + "properties": { + "confidential_client": { + "description": "Whether the client is confidential", + "type": "boolean", + "x-go-name": "ConfidentialClient" + }, + "name": { + "description": "The name of the OAuth2 application", + "type": "string", + "x-go-name": "Name" + }, + "redirect_uris": { + "description": "The list of allowed redirect URIs", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "RedirectURIs" + }, + "skip_secondary_authorization": { + "description": "Whether to skip secondary authorization", + "type": "boolean", + "x-go-name": "SkipSecondaryAuthorization" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateOrUpdateSecretOption": { + "description": "CreateOrUpdateSecretOption options when creating or updating secret", + "properties": { + "data": { + "description": "Data of the secret to update", + "type": "string", + "x-go-name": "Data" + }, + "description": { + "description": "Description of the secret to update", + "type": "string", + "x-go-name": "Description" + } + }, + "required": [ + "data" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateOrgOption": { + "description": "CreateOrgOption options for creating an organization", + "properties": { + "description": { + "description": "The description of the organization", + "type": "string", + "x-go-name": "Description" + }, + "email": { + "description": "The email address of the organization", + "type": "string", + "x-go-name": "Email" + }, + "full_name": { + "description": "The full display name of the organization", + "type": "string", + "x-go-name": "FullName" + }, + "location": { + "description": "The location of the organization", + "type": "string", + "x-go-name": "Location" + }, + "repo_admin_change_team_access": { + "description": "Whether repository administrators can change team access", + "type": "boolean", + "x-go-name": "RepoAdminChangeTeamAccess" + }, + "username": { + "description": "username of the organization", + "type": "string", + "x-go-name": "UserName" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "possible values are `public` (default), `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + }, + "website": { + "description": "The website URL of the organization", + "type": "string", + "x-go-name": "Website" + } + }, + "required": [ + "username" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreatePullRequestOption": { + "description": "CreatePullRequestOption options when creating a pull request", + "properties": { + "allow_maintainer_edit": { + "description": "Whether maintainers can edit the pull request", + "type": "boolean", + "x-go-name": "AllowMaintainerEdit" + }, + "assignee": { + "description": "The primary assignee username", + "type": "string", + "x-go-name": "Assignee" + }, + "assignees": { + "description": "The list of assignee usernames", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "base": { + "description": "The base branch for the pull request", + "type": "string", + "x-go-name": "Base" + }, + "body": { + "description": "The description body of the pull request", + "type": "string", + "x-go-name": "Body" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "head": { + "description": "The head branch for the pull request, it could be a branch name on the base repository or\na form like `\u003cusername\u003e:\u003cbranch\u003e` which refers to the user's fork repository's branch.", + "type": "string", + "x-go-name": "Head" + }, + "labels": { + "description": "The list of label IDs to assign to the pull request", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array", + "x-go-name": "Labels" + }, + "milestone": { + "description": "The milestone ID to assign to the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "Milestone" + }, + "reviewers": { + "description": "The list of reviewer usernames", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Reviewers" + }, + "team_reviewers": { + "description": "The list of team reviewer names", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "TeamReviewers" + }, + "title": { + "description": "The title of the pull request", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreatePullReviewComment": { + "description": "CreatePullReviewComment represent a review comment for creation api", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + }, + "new_position": { + "description": "if comment to new file line or 0", + "format": "int64", + "type": "integer", + "x-go-name": "NewLineNum" + }, + "old_position": { + "description": "if comment to old file line or 0", + "format": "int64", + "type": "integer", + "x-go-name": "OldLineNum" + }, + "path": { + "description": "the tree path", + "type": "string", + "x-go-name": "Path" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreatePullReviewCommentReplyOptions": { + "description": "CreatePullReviewCommentReplyOptions are options to reply to a pull request review comment", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreatePullReviewOptions": { + "description": "CreatePullReviewOptions are options to create a pull request review", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + }, + "comments": { + "items": { + "$ref": "#/components/schemas/CreatePullReviewComment" + }, + "type": "array", + "x-go-name": "Comments" + }, + "commit_id": { + "type": "string", + "x-go-name": "CommitID" + }, + "event": { + "$ref": "#/components/schemas/ReviewStateType" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreatePushMirrorOption": { + "properties": { + "interval": { + "description": "The sync interval for automatic updates", + "type": "string", + "x-go-name": "Interval" + }, + "remote_address": { + "description": "The remote repository URL to push to", + "type": "string", + "x-go-name": "RemoteAddress" + }, + "remote_password": { + "description": "The password for authentication with the remote repository", + "type": "string", + "x-go-name": "RemotePassword" + }, + "remote_username": { + "description": "The username for authentication with the remote repository", + "type": "string", + "x-go-name": "RemoteUsername" + }, + "sync_on_commit": { + "description": "Whether to sync on every commit", + "type": "boolean", + "x-go-name": "SyncOnCommit" + } + }, + "title": "CreatePushMirrorOption represents need information to create a push mirror of a repository.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateReleaseOption": { + "description": "CreateReleaseOption options when creating a release", + "properties": { + "body": { + "description": "The release notes or description", + "type": "string", + "x-go-name": "Note" + }, + "draft": { + "description": "Whether to create the release as a draft", + "type": "boolean", + "x-go-name": "IsDraft" + }, + "name": { + "description": "The display title of the release", + "type": "string", + "x-go-name": "Title" + }, + "prerelease": { + "description": "Whether to mark the release as a prerelease", + "type": "boolean", + "x-go-name": "IsPrerelease" + }, + "tag_message": { + "description": "The message for the git tag", + "type": "string", + "x-go-name": "TagMessage" + }, + "tag_name": { + "type": "string", + "x-go-name": "TagName" + }, + "target_commitish": { + "description": "The target commitish for the release", + "type": "string", + "x-go-name": "Target" + } + }, + "required": [ + "tag_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateRepoOption": { + "description": "CreateRepoOption options when creating repository", + "properties": { + "auto_init": { + "description": "Whether the repository should be auto-initialized?", + "type": "boolean", + "x-go-name": "AutoInit" + }, + "default_branch": { + "description": "DefaultBranch of the repository (used when initializes and in template)", + "type": "string", + "x-go-name": "DefaultBranch" + }, + "description": { + "description": "Description of the repository to create", + "type": "string", + "x-go-name": "Description" + }, + "gitignores": { + "description": "Gitignores to use", + "type": "string", + "x-go-name": "Gitignores" + }, + "issue_labels": { + "description": "Label-Set to use", + "type": "string", + "x-go-name": "IssueLabels" + }, + "license": { + "description": "License to use", + "type": "string", + "x-go-name": "License" + }, + "name": { + "description": "Name of the repository to create", + "type": "string", + "uniqueItems": true, + "x-go-name": "Name" + }, + "object_format_name": { + "allOf": [ + { + "$ref": "#/components/schemas/ObjectFormatName" + } + ], + "description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256" + }, + "private": { + "description": "Whether the repository is private", + "type": "boolean", + "x-go-name": "Private" + }, + "readme": { + "description": "Readme of the repository to create", + "type": "string", + "x-go-name": "Readme" + }, + "template": { + "description": "Whether the repository is template", + "type": "boolean", + "x-go-name": "Template" + }, + "trust_model": { + "description": "TrustModel of the repository", + "enum": [ + "default", + "collaborator", + "committer", + "collaboratorcommitter" + ], + "type": "string", + "x-go-name": "TrustModel" + } + }, + "required": [ + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateStatusOption": { + "description": "CreateStatusOption holds the information needed to create a new CommitStatus for a Commit", + "properties": { + "context": { + "description": "Context is the unique context identifier for the status", + "type": "string", + "x-go-name": "Context" + }, + "description": { + "description": "Description provides a brief description of the status", + "type": "string", + "x-go-name": "Description" + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/CommitStatusState" + } + ], + "description": "State represents the status state to set (pending, success, error, failure)\npending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped" + }, + "target_url": { + "description": "TargetURL is the URL to link to for more details", + "format": "uri", + "type": "string", + "x-go-name": "TargetURL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateTagOption": { + "description": "CreateTagOption options when creating a tag", + "properties": { + "message": { + "description": "The message to associate with the tag", + "type": "string", + "x-go-name": "Message" + }, + "tag_name": { + "type": "string", + "x-go-name": "TagName" + }, + "target": { + "description": "The target commit SHA or branch name for the tag", + "type": "string", + "x-go-name": "Target" + } + }, + "required": [ + "tag_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateTagProtectionOption": { + "description": "CreateTagProtectionOption options for creating a tag protection", + "properties": { + "name_pattern": { + "description": "The pattern to match tag names for protection", + "type": "string", + "x-go-name": "NamePattern" + }, + "whitelist_teams": { + "description": "List of team names allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistTeams" + }, + "whitelist_usernames": { + "description": "List of usernames allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistUsernames" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateTeamOption": { + "description": "CreateTeamOption options for creating a team", + "properties": { + "can_create_org_repo": { + "description": "Whether the team can create repositories in the organization", + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, + "description": { + "description": "The description of the team", + "type": "string", + "x-go-name": "Description" + }, + "includes_all_repositories": { + "description": "Whether the team has access to all repositories in the organization", + "type": "boolean", + "x-go-name": "IncludesAllRepositories" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "permission": { + "$ref": "#/components/schemas/RepoWritePermission" + }, + "units": { + "example": [ + "repo.actions", + "repo.code", + "repo.issues", + "repo.ext_issues", + "repo.wiki", + "repo.ext_wiki", + "repo.pulls", + "repo.releases", + "repo.projects", + "repo.ext_wiki" + ], + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Units" + }, + "units_map": { + "additionalProperties": { + "type": "string" + }, + "example": "{\"repo.actions\",\"repo.packages\",\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"}", + "type": "object", + "x-go-name": "UnitsMap" + } + }, + "required": [ + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateUserOption": { + "description": "CreateUserOption create user options", + "properties": { + "created_at": { + "description": "For explicitly setting the user creation timestamp. Useful when users are\nmigrated from other systems. When omitted, the user's creation timestamp\nwill be set to \"now\".", + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "full_name": { + "description": "The full display name of the user", + "type": "string", + "x-go-name": "FullName" + }, + "login_name": { + "default": "empty", + "description": "identifier of the user, provided by the external authenticator (if configured)", + "type": "string", + "x-go-name": "LoginName" + }, + "must_change_password": { + "description": "Whether the user must change password on first login", + "type": "boolean", + "x-go-name": "MustChangePassword" + }, + "password": { + "description": "The plain text password for the user", + "type": "string", + "x-go-name": "Password" + }, + "restricted": { + "description": "Whether the user has restricted access privileges", + "type": "boolean", + "x-go-name": "Restricted" + }, + "send_notify": { + "description": "Whether to send welcome notification email to the user", + "type": "boolean", + "x-go-name": "SendNotify" + }, + "source_id": { + "description": "The authentication source ID to associate with the user", + "format": "int64", + "type": "integer", + "x-go-name": "SourceID" + }, + "username": { + "description": "username of the user", + "type": "string", + "x-go-name": "Username" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + } + }, + "required": [ + "username", + "email" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateVariableOption": { + "description": "CreateVariableOption the option when creating variable", + "properties": { + "description": { + "description": "Description of the variable to create", + "type": "string", + "x-go-name": "Description" + }, + "value": { + "description": "Value of the variable to create", + "type": "string", + "x-go-name": "Value" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "CreateWikiPageOptions": { + "description": "CreateWikiPageOptions form for creating wiki", + "properties": { + "content_base64": { + "description": "content must be base64 encoded", + "type": "string", + "x-go-name": "ContentBase64" + }, + "message": { + "description": "optional commit message summarizing the change", + "type": "string", + "x-go-name": "Message" + }, + "title": { + "description": "page title. leave empty to keep unchanged", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Cron": { + "description": "Cron represents a Cron task", + "properties": { + "exec_times": { + "description": "The total number of times this cron task has been executed", + "format": "int64", + "type": "integer", + "x-go-name": "ExecTimes" + }, + "name": { + "description": "The name of the cron task", + "type": "string", + "x-go-name": "Name" + }, + "next": { + "description": "The next scheduled execution time", + "format": "date-time", + "type": "string", + "x-go-name": "Next" + }, + "prev": { + "description": "The previous execution time", + "format": "date-time", + "type": "string", + "x-go-name": "Prev" + }, + "schedule": { + "description": "The cron schedule expression (e.g., \"0 0 * * *\")", + "type": "string", + "x-go-name": "Schedule" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "DeleteEmailOption": { + "description": "DeleteEmailOption options when deleting email addresses", + "properties": { + "emails": { + "description": "email addresses to delete", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Emails" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "DeleteFileOptions": { + "description": "DeleteFileOptions options for deleting a file\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", + "properties": { + "author": { + "$ref": "#/components/schemas/Identity" + }, + "branch": { + "description": "branch (optional) is the base branch for the changes. If not supplied, the default branch is used", + "type": "string", + "x-go-name": "BranchName" + }, + "committer": { + "$ref": "#/components/schemas/Identity" + }, + "dates": { + "$ref": "#/components/schemas/CommitDateOptions" + }, + "force_push": { + "description": "force_push (optional) will do a force-push if the new branch already exists", + "type": "boolean", + "x-go-name": "ForcePush" + }, + "message": { + "description": "message (optional) is the commit message of the changes. If not supplied, a default message will be used", + "type": "string", + "x-go-name": "Message" + }, + "new_branch": { + "description": "new_branch (optional) will make a new branch from base branch for the changes. If not supplied, the changes will be committed to the base branch", + "type": "string", + "x-go-name": "NewBranchName" + }, + "sha": { + "description": "the blob ID (SHA) for the file to delete", + "type": "string", + "x-go-name": "SHA" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" + } + }, + "required": [ + "sha" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "DeployKey": { + "description": "DeployKey a deploy key", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "fingerprint": { + "description": "Fingerprint is the key's fingerprint", + "type": "string", + "x-go-name": "Fingerprint" + }, + "id": { + "description": "ID is the unique identifier for the deploy key", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "key": { + "description": "Key contains the actual SSH key content", + "type": "string", + "x-go-name": "Key" + }, + "key_id": { + "description": "KeyID is the associated public key ID", + "format": "int64", + "type": "integer", + "x-go-name": "KeyID" + }, + "read_only": { + "description": "ReadOnly indicates if the key has read-only access", + "type": "boolean", + "x-go-name": "ReadOnly" + }, + "repository": { + "$ref": "#/components/schemas/Repository" + }, + "title": { + "description": "Title is the human-readable name for the key", + "type": "string", + "x-go-name": "Title" + }, + "url": { + "description": "URL is the API URL for this deploy key", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "DismissPullReviewOptions": { + "description": "DismissPullReviewOptions are options to dismiss a pull request review", + "properties": { + "message": { + "type": "string", + "x-go-name": "Message" + }, + "priors": { + "type": "boolean", + "x-go-name": "Priors" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditActionRunnerOption": { + "properties": { + "disabled": { + "type": "boolean", + "x-go-name": "Disabled" + } + }, + "required": [ + "disabled" + ], + "title": "EditActionRunnerOption represents the editable fields for a runner.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditAttachmentOptions": { + "description": "EditAttachmentOptions options for editing attachments", + "properties": { + "name": { + "description": "Name is the new filename for the attachment", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditBranchProtectionOption": { + "description": "EditBranchProtectionOption options for editing a branch protection", + "properties": { + "approvals_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistTeams" + }, + "approvals_whitelist_username": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ApprovalsWhitelistUsernames" + }, + "block_admin_merge_override": { + "type": "boolean", + "x-go-name": "BlockAdminMergeOverride" + }, + "block_on_official_review_requests": { + "type": "boolean", + "x-go-name": "BlockOnOfficialReviewRequests" + }, + "block_on_outdated_branch": { + "type": "boolean", + "x-go-name": "BlockOnOutdatedBranch" + }, + "block_on_rejected_reviews": { + "type": "boolean", + "x-go-name": "BlockOnRejectedReviews" + }, + "dismiss_stale_approvals": { + "type": "boolean", + "x-go-name": "DismissStaleApprovals" + }, + "enable_approvals_whitelist": { + "type": "boolean", + "x-go-name": "EnableApprovalsWhitelist" + }, + "enable_force_push": { + "type": "boolean", + "x-go-name": "EnableForcePush" + }, + "enable_force_push_allowlist": { + "type": "boolean", + "x-go-name": "EnableForcePushAllowlist" + }, + "enable_merge_whitelist": { + "type": "boolean", + "x-go-name": "EnableMergeWhitelist" + }, + "enable_push": { + "type": "boolean", + "x-go-name": "EnablePush" + }, + "enable_push_whitelist": { + "type": "boolean", + "x-go-name": "EnablePushWhitelist" + }, + "enable_status_check": { + "type": "boolean", + "x-go-name": "EnableStatusCheck" + }, + "force_push_allowlist_deploy_keys": { + "type": "boolean", + "x-go-name": "ForcePushAllowlistDeployKeys" + }, + "force_push_allowlist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistTeams" + }, + "force_push_allowlist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "ForcePushAllowlistUsernames" + }, + "ignore_stale_approvals": { + "type": "boolean", + "x-go-name": "IgnoreStaleApprovals" + }, + "merge_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistTeams" + }, + "merge_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "MergeWhitelistUsernames" + }, + "priority": { + "format": "int64", + "type": "integer", + "x-go-name": "Priority" + }, + "protected_file_patterns": { + "type": "string", + "x-go-name": "ProtectedFilePatterns" + }, + "push_whitelist_deploy_keys": { + "type": "boolean", + "x-go-name": "PushWhitelistDeployKeys" + }, + "push_whitelist_teams": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistTeams" + }, + "push_whitelist_usernames": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "PushWhitelistUsernames" + }, + "require_signed_commits": { + "type": "boolean", + "x-go-name": "RequireSignedCommits" + }, + "required_approvals": { + "format": "int64", + "type": "integer", + "x-go-name": "RequiredApprovals" + }, + "status_check_contexts": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "StatusCheckContexts" + }, + "unprotected_file_patterns": { + "type": "string", + "x-go-name": "UnprotectedFilePatterns" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditDeadlineOption": { + "description": "EditDeadlineOption options for creating a deadline", + "properties": { + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + } + }, + "required": [ + "due_date" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditGitHookOption": { + "description": "EditGitHookOption options when modifying one Git hook", + "properties": { + "content": { + "description": "Content is the new script content for the hook", + "type": "string", + "x-go-name": "Content" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditHookOption": { + "description": "EditHookOption options when modify one hook", + "properties": { + "active": { + "description": "Whether the webhook is active and will be triggered", + "type": "boolean", + "x-go-name": "Active" + }, + "authorization_header": { + "description": "Authorization header to include in webhook requests", + "type": "string", + "x-go-name": "AuthorizationHeader" + }, + "branch_filter": { + "description": "Branch filter pattern to determine which branches trigger the webhook", + "type": "string", + "x-go-name": "BranchFilter" + }, + "config": { + "additionalProperties": { + "type": "string" + }, + "description": "Configuration settings for the webhook", + "type": "object", + "x-go-name": "Config" + }, + "events": { + "description": "List of events that trigger this webhook", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Events" + }, + "name": { + "description": "Optional human-readable name", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditIssueCommentOption": { + "description": "EditIssueCommentOption options for editing a comment", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + } + }, + "required": [ + "body" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditIssueOption": { + "description": "EditIssueOption options for editing an issue", + "properties": { + "assignee": { + "deprecated": true, + "description": "deprecated", + "type": "string", + "x-go-name": "Assignee" + }, + "assignees": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "body": { + "type": "string", + "x-go-name": "Body" + }, + "content_version": { + "description": "The current version of the issue content to detect conflicts during editing", + "format": "int64", + "type": "integer", + "x-go-name": "ContentVersion" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "milestone": { + "format": "int64", + "type": "integer", + "x-go-name": "Milestone" + }, + "ref": { + "type": "string", + "x-go-name": "Ref" + }, + "state": { + "type": "string", + "x-go-name": "State" + }, + "title": { + "type": "string", + "x-go-name": "Title" + }, + "unset_due_date": { + "type": "boolean", + "x-go-name": "RemoveDeadline" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditLabelOption": { + "description": "EditLabelOption options for editing a label", + "properties": { + "color": { + "example": "#00aabb", + "type": "string", + "x-go-name": "Color" + }, + "description": { + "description": "Description provides additional context about the label's purpose", + "type": "string", + "x-go-name": "Description" + }, + "exclusive": { + "example": false, + "type": "boolean", + "x-go-name": "Exclusive" + }, + "is_archived": { + "example": false, + "type": "boolean", + "x-go-name": "IsArchived" + }, + "name": { + "description": "Name is the new display name for the label", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditMilestoneOption": { + "description": "EditMilestoneOption options for editing a milestone", + "properties": { + "description": { + "description": "Description provides updated details about the milestone", + "type": "string", + "x-go-name": "Description" + }, + "due_on": { + "description": "Deadline is the updated due date for the milestone", + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/StateType" + } + ], + "description": "State indicates the updated state of the milestone" + }, + "title": { + "description": "Title is the updated title of the milestone", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditOrgOption": { + "description": "EditOrgOption options for editing an organization", + "properties": { + "description": { + "description": "The description of the organization", + "type": "string", + "x-go-name": "Description" + }, + "email": { + "description": "The email address of the organization; use empty string to clear", + "type": "string", + "x-go-name": "Email" + }, + "full_name": { + "description": "The full display name of the organization", + "type": "string", + "x-go-name": "FullName" + }, + "location": { + "description": "The location of the organization", + "type": "string", + "x-go-name": "Location" + }, + "repo_admin_change_team_access": { + "description": "Whether repository administrators can change team access", + "type": "boolean", + "x-go-name": "RepoAdminChangeTeamAccess" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "possible values are `public`, `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + }, + "website": { + "description": "The website URL of the organization", + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditPullRequestOption": { + "description": "EditPullRequestOption options when modify pull request", + "properties": { + "allow_maintainer_edit": { + "description": "Whether to allow maintainer edits", + "type": "boolean", + "x-go-name": "AllowMaintainerEdit" + }, + "assignee": { + "description": "The new primary assignee username", + "type": "string", + "x-go-name": "Assignee" + }, + "assignees": { + "description": "The new list of assignee usernames", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "base": { + "description": "The new base branch for the pull request", + "type": "string", + "x-go-name": "Base" + }, + "body": { + "description": "The new description body for the pull request", + "type": "string", + "x-go-name": "Body" + }, + "content_version": { + "description": "The current version of the pull request content to detect conflicts during editing", + "format": "int64", + "type": "integer", + "x-go-name": "ContentVersion" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "labels": { + "description": "The new list of label IDs for the pull request", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array", + "x-go-name": "Labels" + }, + "milestone": { + "description": "The new milestone ID for the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "Milestone" + }, + "state": { + "description": "The new state for the pull request", + "type": "string", + "x-go-name": "State" + }, + "title": { + "description": "The new title for the pull request", + "type": "string", + "x-go-name": "Title" + }, + "unset_due_date": { + "description": "Whether to remove the current deadline", + "type": "boolean", + "x-go-name": "RemoveDeadline" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditReactionOption": { + "description": "EditReactionOption contain the reaction type", + "properties": { + "content": { + "description": "The reaction content (e.g., emoji or reaction type)", + "type": "string", + "x-go-name": "Reaction" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditReleaseOption": { + "description": "EditReleaseOption options when editing a release", + "properties": { + "body": { + "description": "The new release notes or description", + "type": "string", + "x-go-name": "Note" + }, + "draft": { + "description": "Whether to change the draft status", + "type": "boolean", + "x-go-name": "IsDraft" + }, + "name": { + "description": "The new display title of the release", + "type": "string", + "x-go-name": "Title" + }, + "prerelease": { + "description": "Whether to change the prerelease status", + "type": "boolean", + "x-go-name": "IsPrerelease" + }, + "tag_name": { + "description": "The new name of the git tag", + "type": "string", + "x-go-name": "TagName" + }, + "target_commitish": { + "description": "The new target commitish for the release", + "type": "string", + "x-go-name": "Target" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditRepoOption": { + "description": "EditRepoOption options when editing a repository's properties", + "properties": { + "allow_fast_forward_only_merge": { + "description": "either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging.", + "type": "boolean", + "x-go-name": "AllowFastForwardOnly" + }, + "allow_manual_merge": { + "description": "either `true` to allow mark pr as merged manually, or `false` to prevent it.", + "type": "boolean", + "x-go-name": "AllowManualMerge" + }, + "allow_merge_commits": { + "description": "either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits.", + "type": "boolean", + "x-go-name": "AllowMerge" + }, + "allow_rebase": { + "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging.", + "type": "boolean", + "x-go-name": "AllowRebase" + }, + "allow_rebase_explicit": { + "description": "either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits.", + "type": "boolean", + "x-go-name": "AllowRebaseMerge" + }, + "allow_rebase_update": { + "description": "either `true` to allow updating pull request branch by rebase, or `false` to prevent it.", + "type": "boolean", + "x-go-name": "AllowRebaseUpdate" + }, + "allow_squash_merge": { + "description": "either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.", + "type": "boolean", + "x-go-name": "AllowSquash" + }, + "archived": { + "description": "set to `true` to archive this repository.", + "type": "boolean", + "x-go-name": "Archived" + }, + "autodetect_manual_merge": { + "description": "either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.", + "type": "boolean", + "x-go-name": "AutodetectManualMerge" + }, + "default_allow_maintainer_edit": { + "description": "set to `true` to allow edits from maintainers by default", + "type": "boolean", + "x-go-name": "DefaultAllowMaintainerEdit" + }, + "default_branch": { + "description": "sets the default branch for this repository.", + "type": "string", + "x-go-name": "DefaultBranch" + }, + "default_delete_branch_after_merge": { + "description": "set to `true` to delete pr branch after merge by default", + "type": "boolean", + "x-go-name": "DefaultDeleteBranchAfterMerge" + }, + "default_merge_style": { + "description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", \"squash\", or \"fast-forward-only\".", + "type": "string", + "x-go-name": "DefaultMergeStyle" + }, + "description": { + "description": "a short description of the repository.", + "type": "string", + "x-go-name": "Description" + }, + "enable_prune": { + "description": "enable prune - remove obsolete remote-tracking references when mirroring", + "type": "boolean", + "x-go-name": "EnablePrune" + }, + "external_tracker": { + "$ref": "#/components/schemas/ExternalTracker" + }, + "external_wiki": { + "$ref": "#/components/schemas/ExternalWiki" + }, + "has_actions": { + "description": "either `true` to enable actions unit, or `false` to disable them.", + "type": "boolean", + "x-go-name": "HasActions" + }, + "has_code": { + "description": "either `true` to enable code for this repository or `false` to disable it.", + "type": "boolean", + "x-go-name": "HasCode" + }, + "has_issues": { + "description": "either `true` to enable issues for this repository or `false` to disable them.", + "type": "boolean", + "x-go-name": "HasIssues" + }, + "has_packages": { + "description": "either `true` to enable packages unit, or `false` to disable them.", + "type": "boolean", + "x-go-name": "HasPackages" + }, + "has_projects": { + "description": "either `true` to enable project unit, or `false` to disable them.", + "type": "boolean", + "x-go-name": "HasProjects" + }, + "has_pull_requests": { + "description": "either `true` to allow pull requests, or `false` to prevent pull request.", + "type": "boolean", + "x-go-name": "HasPullRequests" + }, + "has_releases": { + "description": "either `true` to enable releases unit, or `false` to disable them.", + "type": "boolean", + "x-go-name": "HasReleases" + }, + "has_wiki": { + "description": "either `true` to enable the wiki for this repository or `false` to disable it.", + "type": "boolean", + "x-go-name": "HasWiki" + }, + "ignore_whitespace_conflicts": { + "description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace.", + "type": "boolean", + "x-go-name": "IgnoreWhitespaceConflicts" + }, + "internal_tracker": { + "$ref": "#/components/schemas/InternalTracker" + }, + "mirror_interval": { + "description": "set to a string like `8h30m0s` to set the mirror interval time", + "type": "string", + "x-go-name": "MirrorInterval" + }, + "name": { + "description": "name of the repository", + "type": "string", + "uniqueItems": true, + "x-go-name": "Name" + }, + "private": { + "description": "either `true` to make the repository private or `false` to make it public.\nNote: you will get a 422 error if the organization restricts changing repository visibility to organization\nowners and a non-owner tries to change the value of private.", + "type": "boolean", + "x-go-name": "Private" + }, + "projects_mode": { + "description": "`repo` to only allow repo-level projects, `owner` to only allow owner projects, `all` to allow both.", + "type": "string", + "x-go-name": "ProjectsMode" + }, + "template": { + "description": "either `true` to make this repository a template or `false` to make it a normal repository", + "type": "boolean", + "x-go-name": "Template" + }, + "website": { + "description": "a URL with more information about the repository.", + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditTagProtectionOption": { + "description": "EditTagProtectionOption options for editing a tag protection", + "properties": { + "name_pattern": { + "description": "The pattern to match tag names for protection", + "type": "string", + "x-go-name": "NamePattern" + }, + "whitelist_teams": { + "description": "List of team names allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistTeams" + }, + "whitelist_usernames": { + "description": "List of usernames allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistUsernames" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditTeamOption": { + "description": "EditTeamOption options for editing a team", + "properties": { + "can_create_org_repo": { + "description": "Whether the team can create repositories in the organization", + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, + "description": { + "description": "The description of the team", + "type": "string", + "x-go-name": "Description" + }, + "includes_all_repositories": { + "description": "Whether the team has access to all repositories in the organization", + "type": "boolean", + "x-go-name": "IncludesAllRepositories" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "permission": { + "$ref": "#/components/schemas/RepoWritePermission" + }, + "units": { + "example": [ + "repo.code", + "repo.issues", + "repo.ext_issues", + "repo.wiki", + "repo.pulls", + "repo.releases", + "repo.projects", + "repo.ext_wiki" + ], + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Units" + }, + "units_map": { + "additionalProperties": { + "type": "string" + }, + "example": { + "repo.code": "read", + "repo.ext_issues": "none", + "repo.ext_wiki": "none", + "repo.issues": "write", + "repo.projects": "none", + "repo.pulls": "owner", + "repo.releases": "none", + "repo.wiki": "admin" + }, + "type": "object", + "x-go-name": "UnitsMap" + } + }, + "required": [ + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "EditUserOption": { + "description": "EditUserOption edit user options", + "properties": { + "active": { + "description": "Whether the user account is active", + "type": "boolean", + "x-go-name": "Active" + }, + "admin": { + "description": "Whether the user has administrator privileges", + "type": "boolean", + "x-go-name": "Admin" + }, + "allow_create_organization": { + "description": "Whether the user can create organizations", + "type": "boolean", + "x-go-name": "AllowCreateOrganization" + }, + "allow_git_hook": { + "description": "Whether the user can use Git hooks", + "type": "boolean", + "x-go-name": "AllowGitHook" + }, + "allow_import_local": { + "description": "Whether the user can import local repositories", + "type": "boolean", + "x-go-name": "AllowImportLocal" + }, + "description": { + "description": "The user's personal description or bio", + "type": "string", + "x-go-name": "Description" + }, + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "full_name": { + "description": "The full display name of the user", + "type": "string", + "x-go-name": "FullName" + }, + "location": { + "description": "The user's location or address", + "type": "string", + "x-go-name": "Location" + }, + "login_name": { + "default": "empty", + "description": "identifier of the user, provided by the external authenticator (if configured)", + "type": "string", + "x-go-name": "LoginName" + }, + "max_repo_creation": { + "description": "Maximum number of repositories the user can create", + "format": "int64", + "type": "integer", + "x-go-name": "MaxRepoCreation" + }, + "must_change_password": { + "description": "Whether the user must change password on next login", + "type": "boolean", + "x-go-name": "MustChangePassword" + }, + "password": { + "description": "The plain text password for the user", + "type": "string", + "x-go-name": "Password" + }, + "prohibit_login": { + "description": "Whether the user is prohibited from logging in", + "type": "boolean", + "x-go-name": "ProhibitLogin" + }, + "restricted": { + "description": "Whether the user has restricted access privileges", + "type": "boolean", + "x-go-name": "Restricted" + }, + "source_id": { + "format": "int64", + "type": "integer", + "x-go-name": "SourceID" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + }, + "website": { + "description": "The user's personal website URL", + "type": "string", + "x-go-name": "Website" + } + }, + "required": [ + "source_id", + "login_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Email": { + "description": "Email an email address belonging to a user", + "properties": { + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "primary": { + "description": "Whether this is the primary email address", + "type": "boolean", + "x-go-name": "Primary" + }, + "user_id": { + "description": "The unique identifier of the user who owns this email", + "format": "int64", + "type": "integer", + "x-go-name": "UserID" + }, + "username": { + "description": "username of the user", + "type": "string", + "x-go-name": "UserName" + }, + "verified": { + "description": "Whether the email address has been verified", + "type": "boolean", + "x-go-name": "Verified" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ExternalTracker": { + "description": "ExternalTracker represents settings for external tracker", + "properties": { + "external_tracker_format": { + "description": "External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.", + "type": "string", + "x-go-name": "ExternalTrackerFormat" + }, + "external_tracker_regexp_pattern": { + "description": "External Issue Tracker issue regular expression", + "type": "string", + "x-go-name": "ExternalTrackerRegexpPattern" + }, + "external_tracker_style": { + "description": "External Issue Tracker Number Format, either `numeric`, `alphanumeric`, or `regexp`", + "type": "string", + "x-go-name": "ExternalTrackerStyle" + }, + "external_tracker_url": { + "description": "URL of external issue tracker.", + "format": "uri", + "type": "string", + "x-go-name": "ExternalTrackerURL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ExternalWiki": { + "description": "ExternalWiki represents setting for external wiki", + "properties": { + "external_wiki_url": { + "description": "URL of external wiki.", + "format": "uri", + "type": "string", + "x-go-name": "ExternalWikiURL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "FileCommitResponse": { + "properties": { + "author": { + "$ref": "#/components/schemas/CommitUser" + }, + "committer": { + "$ref": "#/components/schemas/CommitUser" + }, + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing this commit", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "message": { + "description": "Message is the commit message", + "type": "string", + "x-go-name": "Message" + }, + "parents": { + "description": "Parents contains parent commit metadata", + "items": { + "$ref": "#/components/schemas/CommitMeta" + }, + "type": "array", + "x-go-name": "Parents" + }, + "sha": { + "description": "SHA is the commit SHA hash", + "type": "string", + "x-go-name": "SHA" + }, + "tree": { + "$ref": "#/components/schemas/CommitMeta" + }, + "url": { + "description": "URL is the API URL for the commit", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "title": "FileCommitResponse contains information generated from a Git commit for a repo's file.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "FileDeleteResponse": { + "description": "FileDeleteResponse contains information about a repo's file that was deleted", + "properties": { + "commit": { + "$ref": "#/components/schemas/FileCommitResponse" + }, + "content": { + "description": "Content is always null for delete operations", + "x-go-name": "Content" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "FileLinksResponse": { + "description": "FileLinksResponse contains the links for a repo's file", + "properties": { + "git": { + "description": "GitURL is the Git API URL for this file", + "type": "string", + "x-go-name": "GitURL" + }, + "html": { + "description": "HTMLURL is the web URL for this file", + "type": "string", + "x-go-name": "HTMLURL" + }, + "self": { + "description": "Self is the API URL for this file", + "type": "string", + "x-go-name": "Self" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "FileResponse": { + "description": "FileResponse contains information about a repo's file", + "properties": { + "commit": { + "$ref": "#/components/schemas/FileCommitResponse" + }, + "content": { + "$ref": "#/components/schemas/ContentsResponse" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "FilesResponse": { + "description": "FilesResponse contains information about multiple files from a repo", + "properties": { + "commit": { + "$ref": "#/components/schemas/FileCommitResponse" + }, + "files": { + "description": "Files contains the list of file contents and metadata", + "items": { + "$ref": "#/components/schemas/ContentsResponse" + }, + "type": "array", + "x-go-name": "Files" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GPGKey": { + "description": "GPGKey a user GPG key to sign commit and tag in repository", + "properties": { + "can_certify": { + "description": "Whether the key can be used for certification", + "type": "boolean", + "x-go-name": "CanCertify" + }, + "can_encrypt_comms": { + "description": "Whether the key can be used for encrypting communications", + "type": "boolean", + "x-go-name": "CanEncryptComms" + }, + "can_encrypt_storage": { + "description": "Whether the key can be used for encrypting storage", + "type": "boolean", + "x-go-name": "CanEncryptStorage" + }, + "can_sign": { + "description": "Whether the key can be used for signing", + "type": "boolean", + "x-go-name": "CanSign" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "emails": { + "description": "List of email addresses associated with this GPG key", + "items": { + "$ref": "#/components/schemas/GPGKeyEmail" + }, + "type": "array", + "x-go-name": "Emails" + }, + "expires_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Expires" + }, + "id": { + "description": "The unique identifier of the GPG key", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "key_id": { + "description": "The key ID of the GPG key", + "type": "string", + "x-go-name": "KeyID" + }, + "primary_key_id": { + "description": "The primary key ID of the GPG key", + "type": "string", + "x-go-name": "PrimaryKeyID" + }, + "public_key": { + "description": "The public key content in armored format", + "type": "string", + "x-go-name": "PublicKey" + }, + "subkeys": { + "description": "List of subkeys of this GPG key", + "items": { + "$ref": "#/components/schemas/GPGKey" + }, + "type": "array", + "x-go-name": "SubsKey" + }, + "verified": { + "description": "Whether the GPG key has been verified", + "type": "boolean", + "x-go-name": "Verified" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GPGKeyEmail": { + "description": "GPGKeyEmail an email attached to a GPGKey", + "properties": { + "email": { + "description": "The email address associated with the GPG key", + "type": "string", + "x-go-name": "Email" + }, + "verified": { + "description": "Whether the email address has been verified", + "type": "boolean", + "x-go-name": "Verified" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GeneralAPISettings": { + "description": "GeneralAPISettings contains global api settings exposed by it", + "properties": { + "default_git_trees_per_page": { + "description": "DefaultGitTreesPerPage is the default number of Git tree items per page", + "format": "int64", + "type": "integer", + "x-go-name": "DefaultGitTreesPerPage" + }, + "default_max_blob_size": { + "description": "DefaultMaxBlobSize is the default maximum blob size for API responses", + "format": "int64", + "type": "integer", + "x-go-name": "DefaultMaxBlobSize" + }, + "default_max_response_size": { + "description": "DefaultMaxResponseSize is the default maximum response size", + "format": "int64", + "type": "integer", + "x-go-name": "DefaultMaxResponseSize" + }, + "default_paging_num": { + "description": "DefaultPagingNum is the default number of items per page", + "format": "int64", + "type": "integer", + "x-go-name": "DefaultPagingNum" + }, + "max_response_items": { + "description": "MaxResponseItems is the maximum number of items returned in API responses", + "format": "int64", + "type": "integer", + "x-go-name": "MaxResponseItems" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GeneralAttachmentSettings": { + "description": "GeneralAttachmentSettings contains global Attachment settings exposed by API", + "properties": { + "allowed_types": { + "description": "AllowedTypes contains the allowed file types for attachments", + "type": "string", + "x-go-name": "AllowedTypes" + }, + "enabled": { + "description": "Enabled indicates if file attachments are enabled", + "type": "boolean", + "x-go-name": "Enabled" + }, + "max_files": { + "description": "MaxFiles is the maximum number of files per attachment", + "format": "int64", + "type": "integer", + "x-go-name": "MaxFiles" + }, + "max_size": { + "description": "MaxSize is the maximum size for individual attachments", + "format": "int64", + "type": "integer", + "x-go-name": "MaxSize" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GeneralRepoSettings": { + "description": "GeneralRepoSettings contains global repository settings exposed by API", + "properties": { + "http_git_disabled": { + "description": "HTTPGitDisabled indicates if HTTP Git operations are disabled", + "type": "boolean", + "x-go-name": "HTTPGitDisabled" + }, + "lfs_disabled": { + "description": "LFSDisabled indicates if Git LFS support is disabled", + "type": "boolean", + "x-go-name": "LFSDisabled" + }, + "migrations_disabled": { + "description": "MigrationsDisabled indicates if repository migrations are disabled", + "type": "boolean", + "x-go-name": "MigrationsDisabled" + }, + "mirrors_disabled": { + "description": "MirrorsDisabled indicates if repository mirroring is disabled", + "type": "boolean", + "x-go-name": "MirrorsDisabled" + }, + "stars_disabled": { + "description": "StarsDisabled indicates if repository starring is disabled", + "type": "boolean", + "x-go-name": "StarsDisabled" + }, + "time_tracking_disabled": { + "description": "TimeTrackingDisabled indicates if time tracking is disabled", + "type": "boolean", + "x-go-name": "TimeTrackingDisabled" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GeneralUISettings": { + "description": "GeneralUISettings contains global ui settings exposed by API", + "properties": { + "allowed_reactions": { + "description": "AllowedReactions contains the list of allowed emoji reactions", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "AllowedReactions" + }, + "custom_emojis": { + "description": "CustomEmojis contains the list of custom emojis", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "CustomEmojis" + }, + "default_theme": { + "description": "DefaultTheme is the default UI theme", + "type": "string", + "x-go-name": "DefaultTheme" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GenerateRepoOption": { + "description": "GenerateRepoOption options when creating a repository using a template", + "properties": { + "avatar": { + "description": "include avatar of the template repo", + "type": "boolean", + "x-go-name": "Avatar" + }, + "default_branch": { + "description": "Default branch of the new repository", + "type": "string", + "x-go-name": "DefaultBranch" + }, + "description": { + "description": "Description of the repository to create", + "type": "string", + "x-go-name": "Description" + }, + "git_content": { + "description": "include git content of default branch in template repo", + "type": "boolean", + "x-go-name": "GitContent" + }, + "git_hooks": { + "description": "include git hooks in template repo", + "type": "boolean", + "x-go-name": "GitHooks" + }, + "labels": { + "description": "include labels in template repo", + "type": "boolean", + "x-go-name": "Labels" + }, + "name": { + "type": "string", + "uniqueItems": true, + "x-go-name": "Name" + }, + "owner": { + "description": "the organization's name or individual user's name who will own the new repository", + "type": "string", + "x-go-name": "Owner" + }, + "private": { + "description": "Whether the repository is private", + "type": "boolean", + "x-go-name": "Private" + }, + "protected_branch": { + "description": "include protected branches in template repo", + "type": "boolean", + "x-go-name": "ProtectedBranch" + }, + "topics": { + "description": "include topics in template repo", + "type": "boolean", + "x-go-name": "Topics" + }, + "webhooks": { + "description": "include webhooks in template repo", + "type": "boolean", + "x-go-name": "Webhooks" + } + }, + "required": [ + "owner", + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GetFilesOptions": { + "description": "GetFilesOptions options for retrieving metadate and content of multiple files", + "properties": { + "files": { + "description": "Files is the list of file paths to retrieve", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Files" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitBlobResponse": { + "description": "GitBlobResponse represents a git blob", + "properties": { + "content": { + "description": "The content of the git blob (may be base64 encoded)", + "type": "string", + "x-go-name": "Content" + }, + "encoding": { + "description": "The encoding used for the content (e.g., \"base64\")", + "type": "string", + "x-go-name": "Encoding" + }, + "lfs_oid": { + "description": "The LFS object ID if this blob is stored in LFS", + "type": "string", + "x-go-name": "LfsOid" + }, + "lfs_size": { + "description": "The size of the LFS object if this blob is stored in LFS", + "format": "int64", + "type": "integer", + "x-go-name": "LfsSize" + }, + "sha": { + "description": "The SHA hash of the git blob", + "type": "string", + "x-go-name": "SHA" + }, + "size": { + "description": "The size of the git blob in bytes", + "format": "int64", + "type": "integer", + "x-go-name": "Size" + }, + "url": { + "description": "The URL to access this git blob", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitEntry": { + "description": "GitEntry represents a git tree", + "properties": { + "mode": { + "description": "Mode is the file mode (permissions)", + "type": "string", + "x-go-name": "Mode" + }, + "path": { + "description": "Path is the file or directory path", + "type": "string", + "x-go-name": "Path" + }, + "sha": { + "description": "SHA is the Git object SHA", + "type": "string", + "x-go-name": "SHA" + }, + "size": { + "description": "Size is the file size in bytes", + "format": "int64", + "type": "integer", + "x-go-name": "Size" + }, + "type": { + "description": "Type indicates if this is a file, directory, or symlink", + "type": "string", + "x-go-name": "Type" + }, + "url": { + "description": "URL is the API URL for this tree entry", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitHook": { + "description": "GitHook represents a Git repository hook", + "properties": { + "content": { + "description": "Content contains the script content of the hook", + "type": "string", + "x-go-name": "Content" + }, + "is_active": { + "description": "IsActive indicates if the hook is active", + "type": "boolean", + "x-go-name": "IsActive" + }, + "name": { + "description": "Name is the name of the Git hook", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitObject": { + "properties": { + "sha": { + "description": "The SHA hash of the Git object", + "type": "string", + "x-go-name": "SHA" + }, + "type": { + "description": "The type of the Git object (e.g., commit, tag, tree, blob)", + "type": "string", + "x-go-name": "Type" + }, + "url": { + "description": "The URL to access this Git object", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "title": "GitObject represents a Git object.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitTreeResponse": { + "description": "GitTreeResponse returns a git tree", + "properties": { + "page": { + "description": "Page is the current page number for pagination", + "format": "int64", + "type": "integer", + "x-go-name": "Page" + }, + "sha": { + "description": "SHA is the tree object SHA", + "type": "string", + "x-go-name": "SHA" + }, + "total_count": { + "description": "TotalCount is the total number of entries in the tree", + "format": "int64", + "type": "integer", + "x-go-name": "TotalCount" + }, + "tree": { + "description": "Entries contains the tree entries (files and directories)", + "items": { + "$ref": "#/components/schemas/GitEntry" + }, + "type": "array", + "x-go-name": "Entries" + }, + "truncated": { + "description": "Truncated indicates if the response was truncated due to size", + "type": "boolean", + "x-go-name": "Truncated" + }, + "url": { + "description": "URL is the API URL for this tree", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "GitignoreTemplateInfo": { + "description": "GitignoreTemplateInfo name and text of a gitignore template", + "properties": { + "name": { + "description": "Name is the name of the gitignore template", + "type": "string", + "x-go-name": "Name" + }, + "source": { + "description": "Source contains the content of the gitignore template", + "type": "string", + "x-go-name": "Source" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Hook": { + "description": "Hook a hook is a web hook when one repository changed", + "properties": { + "active": { + "description": "Whether the webhook is active and will be triggered", + "type": "boolean", + "x-go-name": "Active" + }, + "authorization_header": { + "description": "Authorization header to include in webhook requests", + "type": "string", + "x-go-name": "AuthorizationHeader" + }, + "branch_filter": { + "description": "Branch filter pattern to determine which branches trigger the webhook", + "type": "string", + "x-go-name": "BranchFilter" + }, + "config": { + "additionalProperties": { + "type": "string" + }, + "description": "Configuration settings for the webhook", + "type": "object", + "x-go-name": "Config" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "events": { + "description": "List of events that trigger this webhook", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Events" + }, + "id": { + "description": "The unique identifier of the webhook", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "Optional human-readable name for the webhook", + "type": "string", + "x-go-name": "Name" + }, + "type": { + "description": "The type of the webhook (e.g., gitea, slack, discord)", + "type": "string", + "x-go-name": "Type" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Identity": { + "description": "Identity for a person's identity like an author or committer", + "properties": { + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "name": { + "description": "Name is the person's name", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "InternalTracker": { + "description": "InternalTracker represents settings for internal tracker", + "properties": { + "allow_only_contributors_to_track_time": { + "description": "Let only contributors track time (Built-in issue tracker)", + "type": "boolean", + "x-go-name": "AllowOnlyContributorsToTrackTime" + }, + "enable_issue_dependencies": { + "description": "Enable dependencies for issues and pull requests (Built-in issue tracker)", + "type": "boolean", + "x-go-name": "EnableIssueDependencies" + }, + "enable_time_tracker": { + "description": "Enable time tracking (Built-in issue tracker)", + "type": "boolean", + "x-go-name": "EnableTimeTracker" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Issue": { + "description": "Issue represents an issue in a repository", + "properties": { + "assets": { + "items": { + "$ref": "#/components/schemas/Attachment" + }, + "type": "array", + "x-go-name": "Attachments" + }, + "assignee": { + "$ref": "#/components/schemas/User" + }, + "assignees": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "body": { + "type": "string", + "x-go-name": "Body" + }, + "closed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Closed" + }, + "comments": { + "format": "int64", + "type": "integer", + "x-go-name": "Comments" + }, + "content_version": { + "description": "The version of the issue content for optimistic locking", + "format": "int64", + "type": "integer", + "x-go-name": "ContentVersion" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "is_locked": { + "type": "boolean", + "x-go-name": "IsLocked" + }, + "labels": { + "items": { + "$ref": "#/components/schemas/Label" + }, + "type": "array", + "x-go-name": "Labels" + }, + "milestone": { + "$ref": "#/components/schemas/Milestone" + }, + "number": { + "format": "int64", + "type": "integer", + "x-go-name": "Index" + }, + "original_author": { + "type": "string", + "x-go-name": "OriginalAuthor" + }, + "original_author_id": { + "format": "int64", + "type": "integer", + "x-go-name": "OriginalAuthorID" + }, + "pin_order": { + "format": "int64", + "type": "integer", + "x-go-name": "PinOrder" + }, + "pull_request": { + "$ref": "#/components/schemas/PullRequestMeta" + }, + "ref": { + "type": "string", + "x-go-name": "Ref" + }, + "repository": { + "$ref": "#/components/schemas/RepositoryMeta" + }, + "state": { + "$ref": "#/components/schemas/StateType" + }, + "time_estimate": { + "format": "int64", + "type": "integer", + "x-go-name": "TimeEstimate" + }, + "title": { + "type": "string", + "x-go-name": "Title" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueConfig": { + "properties": { + "blank_issues_enabled": { + "type": "boolean", + "x-go-name": "BlankIssuesEnabled" + }, + "contact_links": { + "items": { + "$ref": "#/components/schemas/IssueConfigContactLink" + }, + "type": "array", + "x-go-name": "ContactLinks" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueConfigContactLink": { + "properties": { + "about": { + "type": "string", + "x-go-name": "About" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueConfigValidation": { + "properties": { + "message": { + "type": "string", + "x-go-name": "Message" + }, + "valid": { + "type": "boolean", + "x-go-name": "Valid" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueDeadline": { + "description": "IssueDeadline represents an issue deadline", + "properties": { + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueFormField": { + "description": "IssueFormField represents a form field", + "properties": { + "attributes": { + "additionalProperties": {}, + "type": "object", + "x-go-name": "Attributes" + }, + "id": { + "type": "string", + "x-go-name": "ID" + }, + "type": { + "enum": [ + "markdown", + "textarea", + "input", + "dropdown", + "checkboxes" + ], + "type": "string", + "x-go-enum-desc": "markdown IssueFormFieldTypeMarkdown\ntextarea IssueFormFieldTypeTextarea\ninput IssueFormFieldTypeInput\ndropdown IssueFormFieldTypeDropdown\ncheckboxes IssueFormFieldTypeCheckboxes", + "x-go-name": "Type" + }, + "validations": { + "additionalProperties": {}, + "type": "object", + "x-go-name": "Validations" + }, + "visible": { + "items": { + "enum": [ + "form", + "content" + ], + "type": "string", + "x-go-enum-desc": "form IssueFormFieldVisibleForm\ncontent IssueFormFieldVisibleContent" + }, + "type": "array", + "x-go-name": "Visible" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueLabelsOption": { + "description": "IssueLabelsOption a collection of labels", + "properties": { + "labels": { + "description": "Labels can be a list of integers representing label IDs\nor a list of strings representing label names", + "items": {}, + "type": "array", + "x-go-name": "Labels" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueMeta": { + "description": "IssueMeta basic issue information", + "properties": { + "index": { + "format": "int64", + "type": "integer", + "x-go-name": "Index" + }, + "owner": { + "description": "owner of the issue's repo", + "type": "string", + "x-go-name": "Owner" + }, + "repo": { + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueTemplate": { + "description": "IssueTemplate represents an issue template for a repository", + "properties": { + "about": { + "type": "string", + "x-go-name": "About" + }, + "assignees": { + "$ref": "#/components/schemas/IssueTemplateStringSlice" + }, + "body": { + "items": { + "$ref": "#/components/schemas/IssueFormField" + }, + "type": "array", + "x-go-name": "Fields" + }, + "content": { + "type": "string", + "x-go-name": "Content" + }, + "file_name": { + "type": "string", + "x-go-name": "FileName" + }, + "labels": { + "$ref": "#/components/schemas/IssueTemplateStringSlice" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "ref": { + "type": "string", + "x-go-name": "Ref" + }, + "title": { + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "IssueTemplateStringSlice": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Label": { + "description": "Label a label to an issue or a pr", + "properties": { + "color": { + "example": "00aabb", + "type": "string", + "x-go-name": "Color" + }, + "description": { + "description": "Description provides additional context about the label's purpose", + "type": "string", + "x-go-name": "Description" + }, + "exclusive": { + "example": false, + "type": "boolean", + "x-go-name": "Exclusive" + }, + "id": { + "description": "ID is the unique identifier for the label", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "is_archived": { + "example": false, + "type": "boolean", + "x-go-name": "IsArchived" + }, + "name": { + "description": "Name is the display name of the label", + "type": "string", + "x-go-name": "Name" + }, + "url": { + "description": "URL is the API endpoint for accessing this label", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "LabelTemplate": { + "description": "LabelTemplate info of a Label template", + "properties": { + "color": { + "example": "00aabb", + "type": "string", + "x-go-name": "Color" + }, + "description": { + "description": "Description provides additional context about the label template's purpose", + "type": "string", + "x-go-name": "Description" + }, + "exclusive": { + "example": false, + "type": "boolean", + "x-go-name": "Exclusive" + }, + "name": { + "description": "Name is the display name of the label template", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "LicenseTemplateInfo": { + "description": "LicensesInfo contains information about a License", + "properties": { + "body": { + "description": "Body contains the full text of the license", + "type": "string", + "x-go-name": "Body" + }, + "implementation": { + "description": "Implementation contains license implementation details", + "type": "string", + "x-go-name": "Implementation" + }, + "key": { + "description": "Key is the unique identifier for the license template", + "type": "string", + "x-go-name": "Key" + }, + "name": { + "description": "Name is the display name of the license", + "type": "string", + "x-go-name": "Name" + }, + "url": { + "description": "URL is the reference URL for the license", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "LicensesTemplateListEntry": { + "description": "LicensesListEntry is used for the API", + "properties": { + "key": { + "description": "Key is the unique identifier for the license template", + "type": "string", + "x-go-name": "Key" + }, + "name": { + "description": "Name is the display name of the license", + "type": "string", + "x-go-name": "Name" + }, + "url": { + "description": "URL is the reference URL for the license", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "LockIssueOption": { + "description": "LockIssueOption options to lock an issue", + "properties": { + "lock_reason": { + "type": "string", + "x-go-name": "Reason" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "MarkdownOption": { + "description": "MarkdownOption markdown options", + "properties": { + "Context": { + "description": "URL path for rendering issue, media and file links\nExpected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}\n\nin: body", + "type": "string" + }, + "Mode": { + "description": "Mode to render (markdown, comment, wiki, file)\n\nin: body", + "type": "string" + }, + "Text": { + "description": "Text markdown to render\n\nin: body", + "type": "string" + }, + "Wiki": { + "deprecated": true, + "description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true\nin: body", + "type": "boolean" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "MarkupOption": { + "description": "MarkupOption markup options", + "properties": { + "Context": { + "description": "URL path for rendering issue, media and file links\nExpected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}\n\nin: body", + "type": "string" + }, + "FilePath": { + "description": "File path for detecting extension in file mode\n\nin: body", + "type": "string" + }, + "Mode": { + "description": "Mode to render (markdown, comment, wiki, file)\n\nin: body", + "type": "string" + }, + "Text": { + "description": "Text markup to render\n\nin: body", + "type": "string" + }, + "Wiki": { + "deprecated": true, + "description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true\nin: body", + "type": "boolean" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "MergePullRequestOption": { + "description": "MergePullRequestForm form for merging Pull Request", + "properties": { + "delete_branch_after_merge": { + "type": "boolean", + "x-go-name": "DeleteBranchAfterMerge" + }, + "do": { + "enum": [ + "merge", + "rebase", + "rebase-merge", + "squash", + "fast-forward-only", + "manually-merged" + ], + "type": "string", + "x-go-name": "Do" + }, + "force_merge": { + "type": "boolean", + "x-go-name": "ForceMerge" + }, + "head_commit_id": { + "type": "string", + "x-go-name": "HeadCommitID" + }, + "merge_commit_id": { + "type": "string", + "x-go-name": "MergeCommitID" + }, + "merge_message_field": { + "type": "string", + "x-go-name": "MergeMessageField" + }, + "merge_title_field": { + "type": "string", + "x-go-name": "MergeTitleField" + }, + "merge_when_checks_succeed": { + "type": "boolean", + "x-go-name": "MergeWhenChecksSucceed" + } + }, + "required": [ + "do" + ], + "type": "object", + "x-go-name": "MergePullRequestForm", + "x-go-package": "code.gitea.io/gitea/services/forms" + }, + "MergeUpstreamRequest": { + "properties": { + "branch": { + "type": "string", + "x-go-name": "Branch" + }, + "ff_only": { + "type": "boolean", + "x-go-name": "FfOnly" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "MergeUpstreamResponse": { + "properties": { + "merge_type": { + "type": "string", + "x-go-name": "MergeStyle" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "MigrateRepoOptions": { + "description": "MigrateRepoOptions options for migrating repository's\nthis is used to interact with api v1", + "properties": { + "auth_password": { + "type": "string", + "x-go-name": "AuthPassword" + }, + "auth_token": { + "type": "string", + "x-go-name": "AuthToken" + }, + "auth_username": { + "type": "string", + "x-go-name": "AuthUsername" + }, + "aws_access_key_id": { + "type": "string", + "x-go-name": "AWSAccessKeyID" + }, + "aws_secret_access_key": { + "type": "string", + "x-go-name": "AWSSecretAccessKey" + }, + "clone_addr": { + "type": "string", + "x-go-name": "CloneAddr" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "issues": { + "type": "boolean", + "x-go-name": "Issues" + }, + "labels": { + "type": "boolean", + "x-go-name": "Labels" + }, + "lfs": { + "type": "boolean", + "x-go-name": "LFS" + }, + "lfs_endpoint": { + "type": "string", + "x-go-name": "LFSEndpoint" + }, + "milestones": { + "type": "boolean", + "x-go-name": "Milestones" + }, + "mirror": { + "type": "boolean", + "x-go-name": "Mirror" + }, + "mirror_interval": { + "type": "string", + "x-go-name": "MirrorInterval" + }, + "private": { + "type": "boolean", + "x-go-name": "Private" + }, + "pull_requests": { + "type": "boolean", + "x-go-name": "PullRequests" + }, + "releases": { + "type": "boolean", + "x-go-name": "Releases" + }, + "repo_name": { + "type": "string", + "x-go-name": "RepoName" + }, + "repo_owner": { + "description": "the organization's name or individual user's name who will own the migrated repository", + "type": "string", + "x-go-name": "RepoOwner" + }, + "service": { + "enum": [ + "git", + "github", + "gitea", + "gitlab", + "gogs", + "onedev", + "gitbucket", + "codebase", + "codecommit" + ], + "type": "string", + "x-go-name": "Service" + }, + "uid": { + "deprecated": true, + "description": "deprecated (only for backwards compatibility, use repo_owner instead)", + "format": "int64", + "type": "integer", + "x-go-name": "RepoOwnerID" + }, + "wiki": { + "type": "boolean", + "x-go-name": "Wiki" + } + }, + "required": [ + "clone_addr", + "repo_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Milestone": { + "description": "Milestone milestone is a collection of issues on one repository", + "properties": { + "closed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Closed" + }, + "closed_issues": { + "description": "ClosedIssues is the number of closed issues in this milestone", + "format": "int64", + "type": "integer", + "x-go-name": "ClosedIssues" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "description": { + "description": "Description provides details about the milestone", + "type": "string", + "x-go-name": "Description" + }, + "due_on": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "id": { + "description": "ID is the unique identifier for the milestone", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "open_issues": { + "description": "OpenIssues is the number of open issues in this milestone", + "format": "int64", + "type": "integer", + "x-go-name": "OpenIssues" + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/StateType" + } + ], + "description": "State indicates if the milestone is open or closed\nopen StateOpen StateOpen pr is opened\nclosed StateClosed StateClosed pr is closed" + }, + "title": { + "description": "Title is the title of the milestone", + "type": "string", + "x-go-name": "Title" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NewIssuePinsAllowed": { + "description": "NewIssuePinsAllowed represents an API response that says if new Issue Pins are allowed", + "properties": { + "issues": { + "type": "boolean", + "x-go-name": "Issues" + }, + "pull_requests": { + "type": "boolean", + "x-go-name": "PullRequests" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NodeInfo": { + "description": "NodeInfo contains standardized way of exposing metadata about a server running one of the distributed social networks", + "properties": { + "metadata": { + "description": "Metadata contains free form key value pairs for software specific values", + "type": "object", + "x-go-name": "Metadata" + }, + "openRegistrations": { + "description": "OpenRegistrations indicates if new user registrations are accepted", + "type": "boolean", + "x-go-name": "OpenRegistrations" + }, + "protocols": { + "description": "Protocols lists the protocols supported by this server", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Protocols" + }, + "services": { + "$ref": "#/components/schemas/NodeInfoServices" + }, + "software": { + "$ref": "#/components/schemas/NodeInfoSoftware" + }, + "usage": { + "$ref": "#/components/schemas/NodeInfoUsage" + }, + "version": { + "description": "Version specifies the schema version", + "type": "string", + "x-go-name": "Version" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NodeInfoServices": { + "description": "NodeInfoServices contains the third party sites this server can connect to via their application API", + "properties": { + "inbound": { + "description": "Inbound lists services that can deliver content to this server", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Inbound" + }, + "outbound": { + "description": "Outbound lists services this server can deliver content to", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Outbound" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NodeInfoSoftware": { + "description": "NodeInfoSoftware contains Metadata about server software in use", + "properties": { + "homepage": { + "description": "Homepage is the URL to the homepage of this server software", + "type": "string", + "x-go-name": "Homepage" + }, + "name": { + "description": "Name is the canonical name of this server software", + "type": "string", + "x-go-name": "Name" + }, + "repository": { + "description": "Repository is the URL to the source code repository", + "type": "string", + "x-go-name": "Repository" + }, + "version": { + "description": "Version is the version of this server software", + "type": "string", + "x-go-name": "Version" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NodeInfoUsage": { + "description": "NodeInfoUsage contains usage statistics for this server", + "properties": { + "localComments": { + "description": "LocalComments is the total amount of comments made by users local to this server", + "format": "int64", + "type": "integer", + "x-go-name": "LocalComments" + }, + "localPosts": { + "description": "LocalPosts is the total amount of posts made by users local to this server", + "format": "int64", + "type": "integer", + "x-go-name": "LocalPosts" + }, + "users": { + "$ref": "#/components/schemas/NodeInfoUsageUsers" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NodeInfoUsageUsers": { + "description": "NodeInfoUsageUsers contains statistics about the users of this server", + "properties": { + "activeHalfyear": { + "description": "ActiveHalfyear is the amount of users that signed in at least once in the last 180 days", + "format": "int64", + "type": "integer", + "x-go-name": "ActiveHalfyear" + }, + "activeMonth": { + "description": "ActiveMonth is the amount of users that signed in at least once in the last 30 days", + "format": "int64", + "type": "integer", + "x-go-name": "ActiveMonth" + }, + "total": { + "description": "Total is the total amount of users on this server", + "format": "int64", + "type": "integer", + "x-go-name": "Total" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Note": { + "description": "Note contains information related to a git note", + "properties": { + "commit": { + "$ref": "#/components/schemas/Commit" + }, + "message": { + "description": "The content message of the git note", + "type": "string", + "x-go-name": "Message" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NotificationCount": { + "description": "NotificationCount number of unread notifications", + "properties": { + "new": { + "description": "New is the number of unread notifications", + "format": "int64", + "type": "integer", + "x-go-name": "New" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NotificationSubject": { + "description": "NotificationSubject contains the notification subject (Issue/Pull/Commit)", + "properties": { + "html_url": { + "description": "HTMLURL is the web URL for the notification subject", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "latest_comment_html_url": { + "description": "LatestCommentHTMLURL is the web URL for the latest comment", + "format": "uri", + "type": "string", + "x-go-name": "LatestCommentHTMLURL" + }, + "latest_comment_url": { + "description": "LatestCommentURL is the API URL for the latest comment", + "format": "uri", + "type": "string", + "x-go-name": "LatestCommentURL" + }, + "state": { + "description": "State indicates the current state of the notification subject\nopen NotifySubjectStateOpen NotifySubjectStateOpen is an open subject\nclosed NotifySubjectStateClosed NotifySubjectStateClosed is a closed subject\nmerged NotifySubjectStateMerged NotifySubjectStateMerged is a merged pull request", + "enum": [ + "open", + "closed", + "merged" + ], + "type": "string", + "x-go-enum-desc": "open NotifySubjectStateOpen NotifySubjectStateOpen is an open subject\nclosed NotifySubjectStateClosed NotifySubjectStateClosed is a closed subject\nmerged NotifySubjectStateMerged NotifySubjectStateMerged is a merged pull request", + "x-go-name": "State" + }, + "title": { + "description": "Title is the title of the notification subject", + "type": "string", + "x-go-name": "Title" + }, + "type": { + "description": "Type indicates the type of the notification subject\nIssue NotifySubjectIssue NotifySubjectIssue a issue is subject of an notification\nPull NotifySubjectPull NotifySubjectPull a pull is subject of an notification\nCommit NotifySubjectCommit NotifySubjectCommit a commit is subject of an notification\nRepository NotifySubjectRepository NotifySubjectRepository a repository is subject of an notification", + "enum": [ + "Issue", + "Pull", + "Commit", + "Repository" + ], + "type": "string", + "x-go-enum-desc": "Issue NotifySubjectIssue NotifySubjectIssue a issue is subject of an notification\nPull NotifySubjectPull NotifySubjectPull a pull is subject of an notification\nCommit NotifySubjectCommit NotifySubjectCommit a commit is subject of an notification\nRepository NotifySubjectRepository NotifySubjectRepository a repository is subject of an notification", + "x-go-name": "Type" + }, + "url": { + "description": "URL is the API URL for the notification subject", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "NotificationThread": { + "description": "NotificationThread expose Notification on API", + "properties": { + "id": { + "description": "ID is the unique identifier for the notification thread", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "pinned": { + "description": "Pinned indicates if the notification is pinned", + "type": "boolean", + "x-go-name": "Pinned" + }, + "repository": { + "$ref": "#/components/schemas/Repository" + }, + "subject": { + "$ref": "#/components/schemas/NotificationSubject" + }, + "unread": { + "description": "Unread indicates if the notification has been read", + "type": "boolean", + "x-go-name": "Unread" + }, + "updated_at": { + "description": "UpdatedAt is the time when the notification was last updated", + "format": "date-time", + "type": "string", + "x-go-name": "UpdatedAt" + }, + "url": { + "description": "URL is the API URL for this notification thread", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "OAuth2Application": { + "properties": { + "client_id": { + "description": "The client ID of the OAuth2 application", + "type": "string", + "x-go-name": "ClientID" + }, + "client_secret": { + "description": "The client secret of the OAuth2 application", + "type": "string", + "x-go-name": "ClientSecret" + }, + "confidential_client": { + "description": "Whether the client is confidential", + "type": "boolean", + "x-go-name": "ConfidentialClient" + }, + "created": { + "description": "The timestamp when the application was created", + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "The unique identifier of the OAuth2 application", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "The name of the OAuth2 application", + "type": "string", + "x-go-name": "Name" + }, + "redirect_uris": { + "description": "The list of allowed redirect URIs", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "RedirectURIs" + }, + "skip_secondary_authorization": { + "description": "Whether to skip secondary authorization", + "type": "boolean", + "x-go-name": "SkipSecondaryAuthorization" + } + }, + "title": "OAuth2Application represents an OAuth2 application.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ObjectFormatName": { + "enum": [ + "sha1", + "sha256" + ], + "type": "string" + }, + "Organization": { + "description": "Organization represents an organization", + "properties": { + "avatar_url": { + "description": "The URL of the organization's avatar", + "format": "uri", + "type": "string", + "x-go-name": "AvatarURL" + }, + "description": { + "description": "The description of the organization", + "type": "string", + "x-go-name": "Description" + }, + "email": { + "description": "The email address of the organization", + "type": "string", + "x-go-name": "Email" + }, + "full_name": { + "description": "The full display name of the organization", + "type": "string", + "x-go-name": "FullName" + }, + "id": { + "description": "The unique identifier of the organization", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "location": { + "description": "The location of the organization", + "type": "string", + "x-go-name": "Location" + }, + "name": { + "description": "The name of the organization", + "type": "string", + "x-go-name": "Name" + }, + "repo_admin_change_team_access": { + "description": "Whether repository administrators can change team access", + "type": "boolean", + "x-go-name": "RepoAdminChangeTeamAccess" + }, + "username": { + "deprecated": true, + "description": "username of the organization\ndeprecated", + "type": "string", + "x-go-name": "UserName" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "The visibility level of the organization (public, limited, private)\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + }, + "website": { + "description": "The website URL of the organization", + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "OrganizationPermissions": { + "description": "OrganizationPermissions list different users permissions on an organization", + "properties": { + "can_create_repository": { + "description": "Whether the user can create repositories in the organization", + "type": "boolean", + "x-go-name": "CanCreateRepository" + }, + "can_read": { + "description": "Whether the user can read the organization", + "type": "boolean", + "x-go-name": "CanRead" + }, + "can_write": { + "description": "Whether the user can write to the organization", + "type": "boolean", + "x-go-name": "CanWrite" + }, + "is_admin": { + "description": "Whether the user is an admin of the organization", + "type": "boolean", + "x-go-name": "IsAdmin" + }, + "is_owner": { + "description": "Whether the user is an owner of the organization", + "type": "boolean", + "x-go-name": "IsOwner" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PRBranchInfo": { + "description": "PRBranchInfo information about a branch", + "properties": { + "label": { + "description": "The display name of the branch", + "type": "string", + "x-go-name": "Name" + }, + "ref": { + "description": "The git reference of the branch", + "type": "string", + "x-go-name": "Ref" + }, + "repo": { + "$ref": "#/components/schemas/Repository" + }, + "repo_id": { + "description": "The unique identifier of the repository", + "format": "int64", + "type": "integer", + "x-go-name": "RepoID" + }, + "sha": { + "description": "The commit SHA of the branch head", + "type": "string", + "x-go-name": "Sha" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Package": { + "description": "Package represents a package", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "creator": { + "$ref": "#/components/schemas/User" + }, + "html_url": { + "description": "The HTML URL to view the package", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "The unique identifier of the package", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "The name of the package", + "type": "string", + "x-go-name": "Name" + }, + "owner": { + "$ref": "#/components/schemas/User" + }, + "repository": { + "$ref": "#/components/schemas/Repository" + }, + "type": { + "description": "The type of the package (e.g., npm, maven, docker)", + "type": "string", + "x-go-name": "Type" + }, + "version": { + "description": "The version of the package", + "type": "string", + "x-go-name": "Version" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PackageFile": { + "description": "PackageFile represents a package file", + "properties": { + "id": { + "description": "The unique identifier of the package file", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "md5": { + "description": "The MD5 hash of the package file", + "type": "string", + "x-go-name": "HashMD5" + }, + "name": { + "description": "The name of the package file", + "type": "string", + "x-go-name": "Name" + }, + "sha1": { + "description": "The SHA1 hash of the package file", + "type": "string", + "x-go-name": "HashSHA1" + }, + "sha256": { + "description": "The SHA256 hash of the package file", + "type": "string", + "x-go-name": "HashSHA256" + }, + "sha512": { + "description": "The SHA512 hash of the package file", + "type": "string", + "x-go-name": "HashSHA512" + }, + "size": { + "description": "The size of the package file in bytes", + "format": "int64", + "type": "integer", + "x-go-name": "Size" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PayloadCommit": { + "description": "PayloadCommit represents a commit", + "properties": { + "added": { + "description": "List of files added in this commit", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Added" + }, + "author": { + "$ref": "#/components/schemas/PayloadUser" + }, + "committer": { + "$ref": "#/components/schemas/PayloadUser" + }, + "id": { + "description": "sha1 hash of the commit", + "type": "string", + "x-go-name": "ID" + }, + "message": { + "description": "The commit message", + "type": "string", + "x-go-name": "Message" + }, + "modified": { + "description": "List of files modified in this commit", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Modified" + }, + "removed": { + "description": "List of files removed in this commit", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Removed" + }, + "timestamp": { + "format": "date-time", + "type": "string", + "x-go-name": "Timestamp" + }, + "url": { + "description": "The URL to view this commit", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PayloadCommitVerification": { + "description": "PayloadCommitVerification represents the GPG verification of a commit", + "properties": { + "payload": { + "description": "The signed payload content", + "type": "string", + "x-go-name": "Payload" + }, + "reason": { + "description": "The reason for the verification status", + "type": "string", + "x-go-name": "Reason" + }, + "signature": { + "description": "The GPG signature of the commit", + "type": "string", + "x-go-name": "Signature" + }, + "signer": { + "$ref": "#/components/schemas/PayloadUser" + }, + "verified": { + "description": "Whether the commit signature is verified", + "type": "boolean", + "x-go-name": "Verified" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PayloadUser": { + "description": "PayloadUser represents the author or committer of a commit", + "properties": { + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "name": { + "description": "Full name of the commit author", + "type": "string", + "x-go-name": "Name" + }, + "username": { + "description": "username of the user", + "type": "string", + "x-go-name": "UserName" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Permission": { + "description": "Permission represents a set of permissions", + "properties": { + "admin": { + "type": "boolean", + "x-go-name": "Admin" + }, + "pull": { + "type": "boolean", + "x-go-name": "Pull" + }, + "push": { + "type": "boolean", + "x-go-name": "Push" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PublicKey": { + "description": "PublicKey publickey is a user key to push code to repository", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "fingerprint": { + "description": "Fingerprint is the key's fingerprint", + "type": "string", + "x-go-name": "Fingerprint" + }, + "id": { + "description": "ID is the unique identifier for the public key", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "key": { + "description": "Key contains the actual SSH public key content", + "type": "string", + "x-go-name": "Key" + }, + "key_type": { + "description": "KeyType indicates the type of the SSH key", + "type": "string", + "x-go-name": "KeyType" + }, + "last_used_at": { + "description": "Updated is the time when the key was last used", + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "read_only": { + "description": "ReadOnly indicates if the key has read-only access", + "type": "boolean", + "x-go-name": "ReadOnly" + }, + "title": { + "description": "Title is the human-readable name for the key", + "type": "string", + "x-go-name": "Title" + }, + "url": { + "description": "URL is the API URL for this key", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PullRequest": { + "description": "PullRequest represents a pull request", + "properties": { + "additions": { + "description": "The number of lines added in the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "Additions" + }, + "allow_maintainer_edit": { + "description": "Whether maintainers can edit the pull request", + "type": "boolean", + "x-go-name": "AllowMaintainerEdit" + }, + "assignee": { + "$ref": "#/components/schemas/User" + }, + "assignees": { + "description": "The list of users assigned to the pull request", + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array", + "x-go-name": "Assignees" + }, + "base": { + "$ref": "#/components/schemas/PRBranchInfo" + }, + "body": { + "description": "The description body of the pull request", + "type": "string", + "x-go-name": "Body" + }, + "changed_files": { + "description": "The number of files changed in the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "ChangedFiles" + }, + "closed_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Closed" + }, + "comments": { + "description": "The number of comments on the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "Comments" + }, + "content_version": { + "description": "The version of the pull request content for optimistic locking", + "format": "int64", + "type": "integer", + "x-go-name": "ContentVersion" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "deletions": { + "description": "The number of lines deleted in the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "Deletions" + }, + "diff_url": { + "description": "The URL to download the diff patch", + "format": "uri", + "type": "string", + "x-go-name": "DiffURL" + }, + "draft": { + "description": "Whether the pull request is a draft", + "type": "boolean", + "x-go-name": "Draft" + }, + "due_date": { + "format": "date-time", + "type": "string", + "x-go-name": "Deadline" + }, + "head": { + "$ref": "#/components/schemas/PRBranchInfo" + }, + "html_url": { + "description": "The HTML URL to view the pull request", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "The unique identifier of the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "is_locked": { + "description": "Whether the pull request conversation is locked", + "type": "boolean", + "x-go-name": "IsLocked" + }, + "labels": { + "description": "The labels attached to the pull request", + "items": { + "$ref": "#/components/schemas/Label" + }, + "type": "array", + "x-go-name": "Labels" + }, + "merge_base": { + "description": "The merge base commit SHA", + "type": "string", + "x-go-name": "MergeBase" + }, + "merge_commit_sha": { + "description": "The SHA of the merge commit", + "type": "string", + "x-go-name": "MergedCommitID" + }, + "mergeable": { + "description": "Whether the pull request can be merged", + "type": "boolean", + "x-go-name": "Mergeable" + }, + "merged": { + "description": "Whether the pull request has been merged", + "type": "boolean", + "x-go-name": "HasMerged" + }, + "merged_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Merged" + }, + "merged_by": { + "$ref": "#/components/schemas/User" + }, + "milestone": { + "$ref": "#/components/schemas/Milestone" + }, + "number": { + "description": "The pull request number", + "format": "int64", + "type": "integer", + "x-go-name": "Index" + }, + "patch_url": { + "description": "The URL to download the patch file", + "format": "uri", + "type": "string", + "x-go-name": "PatchURL" + }, + "pin_order": { + "description": "The pin order for the pull request", + "format": "int64", + "type": "integer", + "x-go-name": "PinOrder" + }, + "requested_reviewers": { + "description": "The users requested to review the pull request", + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array", + "x-go-name": "RequestedReviewers" + }, + "requested_reviewers_teams": { + "description": "The teams requested to review the pull request", + "items": { + "$ref": "#/components/schemas/Team" + }, + "type": "array", + "x-go-name": "RequestedReviewersTeams" + }, + "review_comments": { + "description": "number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)", + "format": "int64", + "type": "integer", + "x-go-name": "ReviewComments" + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/StateType" + } + ], + "description": "The current state of the pull request\nopen StateOpen StateOpen pr is opened\nclosed StateClosed StateClosed pr is closed" + }, + "title": { + "description": "The title of the pull request", + "type": "string", + "x-go-name": "Title" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "url": { + "description": "The API URL of the pull request", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PullRequestMeta": { + "description": "PullRequestMeta PR info if an issue is a PR", + "properties": { + "draft": { + "type": "boolean", + "x-go-name": "IsWorkInProgress" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "merged": { + "type": "boolean", + "x-go-name": "HasMerged" + }, + "merged_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Merged" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PullReview": { + "description": "PullReview represents a pull request review", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + }, + "comments_count": { + "format": "int64", + "type": "integer", + "x-go-name": "CodeCommentsCount" + }, + "commit_id": { + "type": "string", + "x-go-name": "CommitID" + }, + "dismissed": { + "type": "boolean", + "x-go-name": "Dismissed" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing the review", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "official": { + "type": "boolean", + "x-go-name": "Official" + }, + "pull_request_url": { + "description": "HTMLPullURL is the web URL for the pull request", + "format": "uri", + "type": "string", + "x-go-name": "HTMLPullURL" + }, + "stale": { + "type": "boolean", + "x-go-name": "Stale" + }, + "state": { + "$ref": "#/components/schemas/ReviewStateType" + }, + "submitted_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Submitted" + }, + "team": { + "$ref": "#/components/schemas/Team" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PullReviewComment": { + "description": "PullReviewComment represents a comment on a pull request review", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + }, + "commit_id": { + "type": "string", + "x-go-name": "CommitID" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "diff_hunk": { + "type": "string", + "x-go-name": "DiffHunk" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "original_commit_id": { + "type": "string", + "x-go-name": "OrigCommitID" + }, + "original_position": { + "format": "uint64", + "type": "integer", + "x-go-name": "OldLineNum" + }, + "path": { + "type": "string", + "x-go-name": "Path" + }, + "position": { + "format": "uint64", + "type": "integer", + "x-go-name": "LineNum" + }, + "pull_request_review_id": { + "format": "int64", + "type": "integer", + "x-go-name": "ReviewID" + }, + "pull_request_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLPullURL" + }, + "resolver": { + "$ref": "#/components/schemas/User" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PullReviewRequestOptions": { + "description": "PullReviewRequestOptions are options to add or remove pull request review requests", + "properties": { + "reviewers": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Reviewers" + }, + "team_reviewers": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "TeamReviewers" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "PushMirror": { + "description": "PushMirror represents information of a push mirror", + "properties": { + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedUnix" + }, + "interval": { + "description": "The sync interval for automatic updates", + "type": "string", + "x-go-name": "Interval" + }, + "last_error": { + "description": "The last error message encountered during sync", + "type": "string", + "x-go-name": "LastError" + }, + "last_update": { + "format": "date-time", + "type": "string", + "x-go-name": "LastUpdateUnix" + }, + "remote_address": { + "description": "The remote repository URL being mirrored to", + "type": "string", + "x-go-name": "RemoteAddress" + }, + "remote_name": { + "description": "The name of the remote in the git configuration", + "type": "string", + "x-go-name": "RemoteName" + }, + "repo_name": { + "description": "The name of the source repository", + "type": "string", + "x-go-name": "RepoName" + }, + "sync_on_commit": { + "description": "Whether to sync on every commit", + "type": "boolean", + "x-go-name": "SyncOnCommit" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Reaction": { + "description": "Reaction contain one reaction", + "properties": { + "content": { + "description": "The reaction content (e.g., emoji or reaction type)", + "type": "string", + "x-go-name": "Reaction" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Reference": { + "properties": { + "object": { + "$ref": "#/components/schemas/GitObject" + }, + "ref": { + "description": "The name of the Git reference (e.g., refs/heads/main)", + "type": "string", + "x-go-name": "Ref" + }, + "url": { + "description": "The URL to access this Git reference", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "title": "Reference represents a Git reference.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Release": { + "description": "Release represents a repository release", + "properties": { + "assets": { + "description": "The files attached to the release", + "items": { + "$ref": "#/components/schemas/Attachment" + }, + "type": "array", + "x-go-name": "Attachments" + }, + "author": { + "$ref": "#/components/schemas/User" + }, + "body": { + "description": "The release notes or description", + "type": "string", + "x-go-name": "Note" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "draft": { + "description": "Whether the release is a draft", + "type": "boolean", + "x-go-name": "IsDraft" + }, + "html_url": { + "description": "The HTML URL to view the release", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "The unique identifier of the release", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "description": "The display title of the release", + "type": "string", + "x-go-name": "Title" + }, + "prerelease": { + "description": "Whether the release is a prerelease", + "type": "boolean", + "x-go-name": "IsPrerelease" + }, + "published_at": { + "format": "date-time", + "type": "string", + "x-go-name": "PublishedAt" + }, + "tag_name": { + "description": "The name of the git tag associated with the release", + "type": "string", + "x-go-name": "TagName" + }, + "tarball_url": { + "description": "The URL to download the tarball archive", + "format": "uri", + "type": "string", + "x-go-name": "TarURL" + }, + "target_commitish": { + "description": "The target commitish for the release", + "type": "string", + "x-go-name": "Target" + }, + "upload_url": { + "description": "The URL template for uploading release assets", + "format": "uri", + "type": "string", + "x-go-name": "UploadURL" + }, + "url": { + "description": "The API URL of the release", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "zipball_url": { + "description": "The URL to download the zip archive", + "format": "uri", + "type": "string", + "x-go-name": "ZipURL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RenameBranchRepoOption": { + "description": "RenameBranchRepoOption options when renaming a branch in a repository", + "properties": { + "name": { + "description": "New branch name", + "type": "string", + "uniqueItems": true, + "x-go-name": "Name" + } + }, + "required": [ + "name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RenameOrgOption": { + "description": "RenameOrgOption options when renaming an organization", + "properties": { + "new_name": { + "description": "New username for this org. This name cannot be in use yet by any other user.", + "type": "string", + "uniqueItems": true, + "x-go-name": "NewName" + } + }, + "required": [ + "new_name" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RenameUserOption": { + "description": "RenameUserOption options when renaming a user", + "properties": { + "new_username": { + "description": "New username for this user. This name cannot be in use yet by any other user.", + "type": "string", + "uniqueItems": true, + "x-go-name": "NewName" + } + }, + "required": [ + "new_username" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepoCollaboratorPermission": { + "description": "RepoCollaboratorPermission to get repository permission for a collaborator", + "properties": { + "permission": { + "allOf": [ + { + "$ref": "#/components/schemas/AccessLevelName" + } + ], + "description": "Permission level of the collaborator\nnone AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner" + }, + "role_name": { + "description": "RoleName is the name of the permission role", + "type": "string", + "x-go-name": "RoleName" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepoCommit": { + "properties": { + "author": { + "$ref": "#/components/schemas/CommitUser" + }, + "committer": { + "$ref": "#/components/schemas/CommitUser" + }, + "message": { + "description": "Message is the commit message", + "type": "string", + "x-go-name": "Message" + }, + "tree": { + "$ref": "#/components/schemas/CommitMeta" + }, + "url": { + "description": "URL is the API URL for the commit", + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "verification": { + "$ref": "#/components/schemas/PayloadCommitVerification" + } + }, + "title": "RepoCommit contains information of a commit in the context of a repository.", + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepoTopicOptions": { + "description": "RepoTopicOptions a collection of repo topic names", + "properties": { + "topics": { + "description": "list of topic names", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Topics" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepoTransfer": { + "description": "RepoTransfer represents a pending repo transfer", + "properties": { + "doer": { + "$ref": "#/components/schemas/User" + }, + "recipient": { + "$ref": "#/components/schemas/User" + }, + "teams": { + "items": { + "$ref": "#/components/schemas/Team" + }, + "type": "array", + "x-go-name": "Teams" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepoWritePermission": { + "enum": [ + "read", + "write", + "admin" + ], + "type": "string" + }, + "Repository": { + "description": "Repository represents a repository", + "properties": { + "allow_fast_forward_only_merge": { + "type": "boolean", + "x-go-name": "AllowFastForwardOnly" + }, + "allow_manual_merge": { + "type": "boolean", + "x-go-name": "AllowManualMerge" + }, + "allow_merge_commits": { + "type": "boolean", + "x-go-name": "AllowMerge" + }, + "allow_rebase": { + "type": "boolean", + "x-go-name": "AllowRebase" + }, + "allow_rebase_explicit": { + "type": "boolean", + "x-go-name": "AllowRebaseMerge" + }, + "allow_rebase_update": { + "type": "boolean", + "x-go-name": "AllowRebaseUpdate" + }, + "allow_squash_merge": { + "type": "boolean", + "x-go-name": "AllowSquash" + }, + "archived": { + "type": "boolean", + "x-go-name": "Archived" + }, + "archived_at": { + "format": "date-time", + "type": "string", + "x-go-name": "ArchivedAt" + }, + "autodetect_manual_merge": { + "type": "boolean", + "x-go-name": "AutodetectManualMerge" + }, + "avatar_url": { + "format": "uri", + "type": "string", + "x-go-name": "AvatarURL" + }, + "branch_count": { + "format": "int64", + "type": "integer", + "x-go-name": "BranchCount" + }, + "clone_url": { + "format": "uri", + "type": "string", + "x-go-name": "CloneURL" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "default_allow_maintainer_edit": { + "type": "boolean", + "x-go-name": "DefaultAllowMaintainerEdit" + }, + "default_branch": { + "type": "string", + "x-go-name": "DefaultBranch" + }, + "default_delete_branch_after_merge": { + "type": "boolean", + "x-go-name": "DefaultDeleteBranchAfterMerge" + }, + "default_merge_style": { + "type": "string", + "x-go-name": "DefaultMergeStyle" + }, + "default_target_branch": { + "type": "string", + "x-go-name": "DefaultTargetBranch" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "empty": { + "type": "boolean", + "x-go-name": "Empty" + }, + "external_tracker": { + "$ref": "#/components/schemas/ExternalTracker" + }, + "external_wiki": { + "$ref": "#/components/schemas/ExternalWiki" + }, + "fork": { + "type": "boolean", + "x-go-name": "Fork" + }, + "forks_count": { + "format": "int64", + "type": "integer", + "x-go-name": "Forks" + }, + "full_name": { + "type": "string", + "x-go-name": "FullName" + }, + "has_actions": { + "type": "boolean", + "x-go-name": "HasActions" + }, + "has_code": { + "type": "boolean", + "x-go-name": "HasCode" + }, + "has_issues": { + "type": "boolean", + "x-go-name": "HasIssues" + }, + "has_packages": { + "type": "boolean", + "x-go-name": "HasPackages" + }, + "has_projects": { + "type": "boolean", + "x-go-name": "HasProjects" + }, + "has_pull_requests": { + "type": "boolean", + "x-go-name": "HasPullRequests" + }, + "has_releases": { + "type": "boolean", + "x-go-name": "HasReleases" + }, + "has_wiki": { + "type": "boolean", + "x-go-name": "HasWiki" + }, + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "ignore_whitespace_conflicts": { + "type": "boolean", + "x-go-name": "IgnoreWhitespaceConflicts" + }, + "internal": { + "type": "boolean", + "x-go-name": "Internal" + }, + "internal_tracker": { + "$ref": "#/components/schemas/InternalTracker" + }, + "language": { + "type": "string", + "x-go-name": "Language" + }, + "languages_url": { + "format": "uri", + "type": "string", + "x-go-name": "LanguagesURL" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Licenses" + }, + "link": { + "type": "string", + "x-go-name": "Link" + }, + "mirror": { + "type": "boolean", + "x-go-name": "Mirror" + }, + "mirror_interval": { + "type": "string", + "x-go-name": "MirrorInterval" + }, + "mirror_updated": { + "format": "date-time", + "type": "string", + "x-go-name": "MirrorUpdated" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "object_format_name": { + "allOf": [ + { + "$ref": "#/components/schemas/ObjectFormatName" + } + ], + "description": "ObjectFormatName of the underlying git repository\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256" + }, + "open_issues_count": { + "format": "int64", + "type": "integer", + "x-go-name": "OpenIssues" + }, + "open_pr_counter": { + "format": "int64", + "type": "integer", + "x-go-name": "OpenPulls" + }, + "original_url": { + "format": "uri", + "type": "string", + "x-go-name": "OriginalURL" + }, + "owner": { + "$ref": "#/components/schemas/User" + }, + "parent": { + "$ref": "#/components/schemas/Repository" + }, + "permissions": { + "$ref": "#/components/schemas/Permission" + }, + "private": { + "type": "boolean", + "x-go-name": "Private" + }, + "projects_mode": { + "type": "string", + "x-go-name": "ProjectsMode" + }, + "release_counter": { + "format": "int64", + "type": "integer", + "x-go-name": "Releases" + }, + "repo_transfer": { + "$ref": "#/components/schemas/RepoTransfer" + }, + "size": { + "format": "int64", + "type": "integer", + "x-go-name": "Size" + }, + "ssh_url": { + "format": "uri", + "type": "string", + "x-go-name": "SSHURL" + }, + "stars_count": { + "format": "int64", + "type": "integer", + "x-go-name": "Stars" + }, + "template": { + "type": "boolean", + "x-go-name": "Template" + }, + "topics": { + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Topics" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "url": { + "format": "uri", + "type": "string", + "x-go-name": "URL" + }, + "watchers_count": { + "format": "int64", + "type": "integer", + "x-go-name": "Watchers" + }, + "website": { + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "RepositoryMeta": { + "description": "RepositoryMeta basic repository information", + "properties": { + "full_name": { + "type": "string", + "x-go-name": "FullName" + }, + "id": { + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "owner": { + "type": "string", + "x-go-name": "Owner" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ReviewStateType": { + "enum": [ + "APPROVED", + "PENDING", + "COMMENT", + "REQUEST_CHANGES", + "REQUEST_REVIEW" + ], + "type": "string" + }, + "RunDetails": { + "description": "RunDetails returns workflow_dispatch runid and url", + "properties": { + "html_url": { + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "run_url": { + "format": "uri", + "type": "string", + "x-go-name": "RunURL" + }, + "workflow_run_id": { + "format": "int64", + "type": "integer", + "x-go-name": "WorkflowRunID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "SearchResults": { + "description": "SearchResults results of a successful search", + "properties": { + "data": { + "description": "Data contains the repository search results", + "items": { + "$ref": "#/components/schemas/Repository" + }, + "type": "array", + "x-go-name": "Data" + }, + "ok": { + "description": "OK indicates if the search was successful", + "type": "boolean", + "x-go-name": "OK" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Secret": { + "description": "Secret represents a secret", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "description": { + "description": "the secret's description", + "type": "string", + "x-go-name": "Description" + }, + "name": { + "description": "the secret's name", + "type": "string", + "x-go-name": "Name" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ServerVersion": { + "description": "ServerVersion wraps the version of the server", + "properties": { + "version": { + "description": "Version is the server version string", + "type": "string", + "x-go-name": "Version" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "StateType": { + "enum": [ + "open", + "closed" + ], + "type": "string" + }, + "StopWatch": { + "description": "StopWatch represent a running stopwatch", + "properties": { + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "duration": { + "description": "Duration is a human-readable duration string", + "type": "string", + "x-go-name": "Duration" + }, + "issue_index": { + "description": "IssueIndex is the index number of the associated issue", + "format": "int64", + "type": "integer", + "x-go-name": "IssueIndex" + }, + "issue_title": { + "description": "IssueTitle is the title of the associated issue", + "type": "string", + "x-go-name": "IssueTitle" + }, + "repo_name": { + "description": "RepoName is the name of the repository", + "type": "string", + "x-go-name": "RepoName" + }, + "repo_owner_name": { + "description": "RepoOwnerName is the name of the repository owner", + "type": "string", + "x-go-name": "RepoOwnerName" + }, + "seconds": { + "description": "Seconds is the total elapsed time in seconds", + "format": "int64", + "type": "integer", + "x-go-name": "Seconds" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "SubmitPullReviewOptions": { + "description": "SubmitPullReviewOptions are options to submit a pending pull request review", + "properties": { + "body": { + "type": "string", + "x-go-name": "Body" + }, + "event": { + "$ref": "#/components/schemas/ReviewStateType" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Tag": { + "description": "Tag represents a repository tag", + "properties": { + "commit": { + "$ref": "#/components/schemas/CommitMeta" + }, + "id": { + "description": "The ID (SHA) of the tag", + "type": "string", + "x-go-name": "ID" + }, + "message": { + "description": "The message associated with the tag", + "type": "string", + "x-go-name": "Message" + }, + "name": { + "description": "The name of the tag", + "type": "string", + "x-go-name": "Name" + }, + "tarball_url": { + "description": "The URL to download the tarball archive", + "format": "uri", + "type": "string", + "x-go-name": "TarballURL" + }, + "zipball_url": { + "description": "The URL to download the zipball archive", + "format": "uri", + "type": "string", + "x-go-name": "ZipballURL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TagProtection": { + "description": "TagProtection represents a tag protection", + "properties": { + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "The unique identifier of the tag protection", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "name_pattern": { + "description": "The pattern to match tag names for protection", + "type": "string", + "x-go-name": "NamePattern" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "whitelist_teams": { + "description": "List of team names allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistTeams" + }, + "whitelist_usernames": { + "description": "List of usernames allowed to create/delete protected tags", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "WhitelistUsernames" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "Team": { + "description": "Team represents a team in an organization", + "properties": { + "can_create_org_repo": { + "description": "Whether the team can create repositories in the organization", + "type": "boolean", + "x-go-name": "CanCreateOrgRepo" + }, + "description": { + "description": "The description of the team", + "type": "string", + "x-go-name": "Description" + }, + "id": { + "description": "The unique identifier of the team", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "includes_all_repositories": { + "description": "Whether the team has access to all repositories in the organization", + "type": "boolean", + "x-go-name": "IncludesAllRepositories" + }, + "name": { + "description": "The name of the team", + "type": "string", + "x-go-name": "Name" + }, + "organization": { + "$ref": "#/components/schemas/Organization" + }, + "permission": { + "$ref": "#/components/schemas/AccessLevelName" + }, + "units": { + "example": [ + "repo.code", + "repo.issues", + "repo.ext_issues", + "repo.wiki", + "repo.pulls", + "repo.releases", + "repo.projects", + "repo.ext_wiki" + ], + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "Units" + }, + "units_map": { + "additionalProperties": { + "type": "string" + }, + "example": { + "repo.code": "read", + "repo.ext_issues": "none", + "repo.ext_wiki": "none", + "repo.issues": "write", + "repo.projects": "none", + "repo.pulls": "owner", + "repo.releases": "none", + "repo.wiki": "admin" + }, + "type": "object", + "x-go-name": "UnitsMap" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TimeStamp": { + "description": "TimeStamp defines a timestamp", + "format": "int64", + "type": "integer", + "x-go-package": "code.gitea.io/gitea/modules/timeutil" + }, + "TimelineComment": { + "description": "TimelineComment represents a timeline comment (comment of any type) on a commit or issue", + "properties": { + "assignee": { + "$ref": "#/components/schemas/User" + }, + "assignee_team": { + "$ref": "#/components/schemas/Team" + }, + "body": { + "description": "Body contains the timeline event content", + "type": "string", + "x-go-name": "Body" + }, + "created_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "dependent_issue": { + "$ref": "#/components/schemas/Issue" + }, + "html_url": { + "description": "HTMLURL is the web URL for viewing the comment", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "ID is the unique identifier for the timeline comment", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "issue_url": { + "description": "IssueURL is the API URL for the issue", + "format": "uri", + "type": "string", + "x-go-name": "IssueURL" + }, + "label": { + "$ref": "#/components/schemas/Label" + }, + "milestone": { + "$ref": "#/components/schemas/Milestone" + }, + "new_ref": { + "type": "string", + "x-go-name": "NewRef" + }, + "new_title": { + "type": "string", + "x-go-name": "NewTitle" + }, + "old_milestone": { + "$ref": "#/components/schemas/Milestone" + }, + "old_project_id": { + "format": "int64", + "type": "integer", + "x-go-name": "OldProjectID" + }, + "old_ref": { + "type": "string", + "x-go-name": "OldRef" + }, + "old_title": { + "type": "string", + "x-go-name": "OldTitle" + }, + "project_id": { + "format": "int64", + "type": "integer", + "x-go-name": "ProjectID" + }, + "pull_request_url": { + "description": "PRURL is the API URL for the pull request (if applicable)", + "format": "uri", + "type": "string", + "x-go-name": "PRURL" + }, + "ref_action": { + "type": "string", + "x-go-name": "RefAction" + }, + "ref_comment": { + "$ref": "#/components/schemas/Comment" + }, + "ref_commit_sha": { + "description": "commit SHA where issue/PR was referenced", + "type": "string", + "x-go-name": "RefCommitSHA" + }, + "ref_issue": { + "$ref": "#/components/schemas/Issue" + }, + "removed_assignee": { + "description": "whether the assignees were removed or added", + "type": "boolean", + "x-go-name": "RemovedAssignee" + }, + "resolve_doer": { + "$ref": "#/components/schemas/User" + }, + "review_id": { + "format": "int64", + "type": "integer", + "x-go-name": "ReviewID" + }, + "tracked_time": { + "$ref": "#/components/schemas/TrackedTime" + }, + "type": { + "description": "Type indicates the type of timeline event", + "type": "string", + "x-go-name": "Type" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TopicName": { + "description": "TopicName a list of repo topic names", + "properties": { + "topics": { + "description": "List of topic names", + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "TopicNames" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TopicResponse": { + "description": "TopicResponse for returning topics", + "properties": { + "created": { + "description": "The date and time when the topic was created", + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "The unique identifier of the topic", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "repo_count": { + "description": "The number of repositories using this topic", + "format": "int64", + "type": "integer", + "x-go-name": "RepoCount" + }, + "topic_name": { + "description": "The name of the topic", + "type": "string", + "x-go-name": "Name" + }, + "updated": { + "description": "The date and time when the topic was last updated", + "format": "date-time", + "type": "string", + "x-go-name": "Updated" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TrackedTime": { + "description": "TrackedTime worked time for an issue / pr", + "properties": { + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "id": { + "description": "ID is the unique identifier for the tracked time entry", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "issue": { + "$ref": "#/components/schemas/Issue" + }, + "issue_id": { + "deprecated": true, + "description": "deprecated (only for backwards compatibility)", + "format": "int64", + "type": "integer", + "x-go-name": "IssueID" + }, + "time": { + "description": "Time in seconds", + "format": "int64", + "type": "integer", + "x-go-name": "Time" + }, + "user_id": { + "deprecated": true, + "description": "deprecated (only for backwards compatibility)", + "format": "int64", + "type": "integer", + "x-go-name": "UserID" + }, + "user_name": { + "description": "username of the user", + "type": "string", + "x-go-name": "UserName" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "TransferRepoOption": { + "description": "TransferRepoOption options when transfer a repository's ownership", + "properties": { + "new_owner": { + "type": "string", + "x-go-name": "NewOwner" + }, + "team_ids": { + "description": "ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array", + "x-go-name": "TeamIDs" + } + }, + "required": [ + "new_owner" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateBranchProtectionPriories": { + "description": "UpdateBranchProtectionPriories a list to update the branch protection rule priorities", + "properties": { + "ids": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array", + "x-go-name": "IDs" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateBranchRepoOption": { + "description": "UpdateBranchRepoOption options when updating a branch reference in a repository", + "properties": { + "force": { + "description": "Force update even if the change is not a fast-forward", + "type": "boolean", + "x-go-name": "Force" + }, + "new_commit_id": { + "description": "New commit SHA (or any ref) the branch should point to", + "type": "string", + "x-go-name": "NewCommitID" + }, + "old_commit_id": { + "description": "Expected old commit SHA of the branch; if provided it must match the current tip", + "type": "string", + "x-go-name": "OldCommitID" + } + }, + "required": [ + "new_commit_id" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateFileOptions": { + "description": "UpdateFileOptions options for updating or creating a file\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)", + "properties": { + "author": { + "$ref": "#/components/schemas/Identity" + }, + "branch": { + "description": "branch (optional) is the base branch for the changes. If not supplied, the default branch is used", + "type": "string", + "x-go-name": "BranchName" + }, + "committer": { + "$ref": "#/components/schemas/Identity" + }, + "content": { + "description": "content must be base64 encoded", + "type": "string", + "x-go-name": "ContentBase64" + }, + "dates": { + "$ref": "#/components/schemas/CommitDateOptions" + }, + "force_push": { + "description": "force_push (optional) will do a force-push if the new branch already exists", + "type": "boolean", + "x-go-name": "ForcePush" + }, + "from_path": { + "description": "from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL", + "type": "string", + "x-go-name": "FromPath" + }, + "message": { + "description": "message (optional) is the commit message of the changes. If not supplied, a default message will be used", + "type": "string", + "x-go-name": "Message" + }, + "new_branch": { + "description": "new_branch (optional) will make a new branch from base branch for the changes. If not supplied, the changes will be committed to the base branch", + "type": "string", + "x-go-name": "NewBranchName" + }, + "sha": { + "description": "the blob ID (SHA) for the file that already exists to update, or leave it empty to create a new file", + "type": "string", + "x-go-name": "SHA" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" + } + }, + "required": [ + "content" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateRepoAvatarOption": { + "description": "UpdateRepoAvatarUserOption options when updating the repo avatar", + "properties": { + "image": { + "description": "image must be base64 encoded", + "type": "string", + "x-go-name": "Image" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateUserAvatarOption": { + "description": "UpdateUserAvatarUserOption options when updating the user avatar", + "properties": { + "image": { + "description": "image must be base64 encoded", + "type": "string", + "x-go-name": "Image" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UpdateVariableOption": { + "description": "UpdateVariableOption the option when updating variable", + "properties": { + "description": { + "description": "Description of the variable to update", + "type": "string", + "x-go-name": "Description" + }, + "name": { + "description": "New name for the variable. If the field is empty, the variable name won't be updated.", + "type": "string", + "x-go-name": "Name" + }, + "value": { + "description": "Value of the variable to update", + "type": "string", + "x-go-name": "Value" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "User": { + "description": "User represents a user", + "properties": { + "active": { + "description": "Is user active", + "type": "boolean", + "x-go-name": "IsActive" + }, + "avatar_url": { + "description": "URL to the user's avatar", + "format": "uri", + "type": "string", + "x-go-name": "AvatarURL" + }, + "created": { + "format": "date-time", + "type": "string", + "x-go-name": "Created" + }, + "description": { + "description": "the user's description", + "type": "string", + "x-go-name": "Description" + }, + "email": { + "format": "email", + "type": "string", + "x-go-name": "Email" + }, + "followers_count": { + "description": "user counts", + "format": "int64", + "type": "integer", + "x-go-name": "Followers" + }, + "following_count": { + "format": "int64", + "type": "integer", + "x-go-name": "Following" + }, + "full_name": { + "description": "the user's full name", + "type": "string", + "x-go-name": "FullName" + }, + "html_url": { + "description": "URL to the user's gitea page", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "id": { + "description": "the user's id", + "format": "int64", + "type": "integer", + "x-go-name": "ID" + }, + "is_admin": { + "description": "Is the user an administrator", + "type": "boolean", + "x-go-name": "IsAdmin" + }, + "language": { + "description": "User locale", + "type": "string", + "x-go-name": "Language" + }, + "last_login": { + "format": "date-time", + "type": "string", + "x-go-name": "LastLogin" + }, + "location": { + "description": "the user's location", + "type": "string", + "x-go-name": "Location" + }, + "login": { + "description": "login of the user, same as `username`", + "type": "string", + "x-go-name": "UserName" + }, + "login_name": { + "default": "empty", + "description": "identifier of the user, provided by the external authenticator (if configured)", + "type": "string", + "x-go-name": "LoginName" + }, + "prohibit_login": { + "description": "Is user login prohibited", + "type": "boolean", + "x-go-name": "ProhibitLogin" + }, + "restricted": { + "description": "Is user restricted", + "type": "boolean", + "x-go-name": "Restricted" + }, + "source_id": { + "description": "The ID of the user's Authentication Source", + "format": "int64", + "type": "integer", + "x-go-name": "SourceID" + }, + "starred_repos_count": { + "format": "int64", + "type": "integer", + "x-go-name": "StarredRepos" + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/UserVisibility" + } + ], + "description": "User visibility level option: public, limited, private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate" + }, + "website": { + "description": "the user's website", + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UserBadgeOption": { + "description": "UserBadgeOption options for link between users and badges", + "properties": { + "badge_slugs": { + "example": [ + "badge1", + "badge2" + ], + "items": { + "type": "string" + }, + "type": "array", + "x-go-name": "BadgeSlugs" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UserHeatmapData": { + "description": "UserHeatmapData represents the data needed to create a heatmap", + "properties": { + "contributions": { + "format": "int64", + "type": "integer", + "x-go-name": "Contributions" + }, + "timestamp": { + "$ref": "#/components/schemas/TimeStamp" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/models/activities" + }, + "UserSettings": { + "description": "UserSettings represents user settings", + "properties": { + "description": { + "type": "string", + "x-go-name": "Description" + }, + "diff_view_style": { + "type": "string", + "x-go-name": "DiffViewStyle" + }, + "full_name": { + "type": "string", + "x-go-name": "FullName" + }, + "hide_activity": { + "type": "boolean", + "x-go-name": "HideActivity" + }, + "hide_email": { + "description": "Privacy", + "type": "boolean", + "x-go-name": "HideEmail" + }, + "language": { + "type": "string", + "x-go-name": "Language" + }, + "location": { + "type": "string", + "x-go-name": "Location" + }, + "theme": { + "type": "string", + "x-go-name": "Theme" + }, + "website": { + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UserSettingsOptions": { + "description": "UserSettingsOptions represents options to change user settings", + "properties": { + "description": { + "type": "string", + "x-go-name": "Description" + }, + "diff_view_style": { + "type": "string", + "x-go-name": "DiffViewStyle" + }, + "full_name": { + "type": "string", + "x-go-name": "FullName" + }, + "hide_activity": { + "type": "boolean", + "x-go-name": "HideActivity" + }, + "hide_email": { + "description": "Privacy", + "type": "boolean", + "x-go-name": "HideEmail" + }, + "language": { + "type": "string", + "x-go-name": "Language" + }, + "location": { + "type": "string", + "x-go-name": "Location" + }, + "theme": { + "type": "string", + "x-go-name": "Theme" + }, + "website": { + "type": "string", + "x-go-name": "Website" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "UserVisibility": { + "enum": [ + "public", + "limited", + "private" + ], + "type": "string" + }, + "WatchInfo": { + "description": "WatchInfo represents an API watch status of one repository", + "properties": { + "created_at": { + "description": "The timestamp when the watch status was created", + "format": "date-time", + "type": "string", + "x-go-name": "CreatedAt" + }, + "ignored": { + "description": "Whether notifications for the repository are ignored", + "type": "boolean", + "x-go-name": "Ignored" + }, + "reason": { + "description": "The reason for the current watch status", + "x-go-name": "Reason" + }, + "repository_url": { + "description": "The URL of the repository being watched", + "format": "uri", + "type": "string", + "x-go-name": "RepositoryURL" + }, + "subscribed": { + "description": "Whether the repository is being watched for notifications", + "type": "boolean", + "x-go-name": "Subscribed" + }, + "url": { + "description": "The URL for managing the watch status", + "format": "uri", + "type": "string", + "x-go-name": "URL" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "WikiCommit": { + "description": "WikiCommit page commit/revision", + "properties": { + "author": { + "$ref": "#/components/schemas/CommitUser" + }, + "commiter": { + "$ref": "#/components/schemas/CommitUser" + }, + "message": { + "description": "The commit message", + "type": "string", + "x-go-name": "Message" + }, + "sha": { + "description": "The commit SHA hash", + "type": "string", + "x-go-name": "ID" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "WikiCommitList": { + "description": "WikiCommitList commit/revision list", + "properties": { + "commits": { + "description": "The list of wiki commits", + "items": { + "$ref": "#/components/schemas/WikiCommit" + }, + "type": "array", + "x-go-name": "WikiCommits" + }, + "count": { + "description": "The total count of commits", + "format": "int64", + "type": "integer", + "x-go-name": "Count" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "WikiPage": { + "description": "WikiPage a wiki page", + "properties": { + "commit_count": { + "description": "The number of commits that modified this page", + "format": "int64", + "type": "integer", + "x-go-name": "CommitCount" + }, + "content_base64": { + "description": "Page content, base64 encoded", + "type": "string", + "x-go-name": "ContentBase64" + }, + "footer": { + "description": "The footer content for the wiki page", + "type": "string", + "x-go-name": "Footer" + }, + "html_url": { + "description": "The HTML URL to view the wiki page", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "last_commit": { + "$ref": "#/components/schemas/WikiCommit" + }, + "sidebar": { + "description": "The sidebar content for the wiki page", + "type": "string", + "x-go-name": "Sidebar" + }, + "sub_url": { + "description": "The sub URL path for the wiki page", + "format": "uri", + "type": "string", + "x-go-name": "SubURL" + }, + "title": { + "description": "The title of the wiki page", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "WikiPageMetaData": { + "description": "WikiPageMetaData wiki page meta information", + "properties": { + "html_url": { + "description": "The HTML URL to view the wiki page", + "format": "uri", + "type": "string", + "x-go-name": "HTMLURL" + }, + "last_commit": { + "$ref": "#/components/schemas/WikiCommit" + }, + "sub_url": { + "description": "The sub URL path for the wiki page", + "format": "uri", + "type": "string", + "x-go-name": "SubURL" + }, + "title": { + "description": "The title of the wiki page", + "type": "string", + "x-go-name": "Title" + } + }, + "type": "object", + "x-go-package": "code.gitea.io/gitea/modules/structs" + } + }, + "securitySchemes": { + "AccessToken": { + "description": "This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.", + "in": "query", + "name": "access_token", + "type": "apiKey" + }, + "AuthorizationHeaderToken": { + "description": "API tokens must be prepended with \"token\" followed by a space.", + "in": "header", + "name": "Authorization", + "type": "apiKey" + }, + "BasicAuth": { + "scheme": "basic", + "type": "http" + }, + "SudoHeader": { + "description": "Sudo API request as the user provided as the key. Admin privileges are required.", + "in": "header", + "name": "Sudo", + "type": "apiKey" + }, + "SudoParam": { + "description": "Sudo API request as the user provided as the key. Admin privileges are required.", + "in": "query", + "name": "sudo", + "type": "apiKey" + }, + "TOTPHeader": { + "description": "Must be used in combination with BasicAuth if two-factor authentication is enabled.", + "in": "header", + "name": "X-GITEA-OTP", + "type": "apiKey" + }, + "Token": { + "description": "This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.", + "in": "query", + "name": "token", + "type": "apiKey" + } + } + }, + "info": { + "description": "This documentation describes the Gitea API.", + "license": { + "name": "MIT", + "url": "http://opensource.org/licenses/MIT" + }, + "title": "Gitea API", + "version": "{{.SwaggerAppVer}}" + }, + "openapi": "3.0.3", + "paths": { + "/admin/actions/jobs": { + "get": { + "operationId": "listAdminWorkflowJobs", + "parameters": [ + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all jobs", + "tags": [ + "admin" + ] + } + }, + "/admin/actions/runners": { + "get": { + "operationId": "getAdminRunners", + "parameters": [ + { + "description": "filter by disabled status (true or false)", + "in": "query", + "name": "disabled", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RunnerList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get all runners", + "tags": [ + "admin" + ] + } + }, + "/admin/actions/runners/registration-token": { + "post": { + "operationId": "adminCreateRunnerRegistrationToken", + "responses": { + "200": { + "$ref": "#/components/responses/RegistrationToken" + } + }, + "summary": "Get a global actions runner registration token", + "tags": [ + "admin" + ] + } + }, + "/admin/actions/runners/{runner_id}": { + "delete": { + "operationId": "deleteAdminRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "runner has been deleted" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a global runner", + "tags": [ + "admin" + ] + }, + "get": { + "operationId": "getAdminRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a global runner", + "tags": [ + "admin" + ] + }, + "patch": { + "operationId": "updateAdminRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditActionRunnerOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a global runner", + "tags": [ + "admin" + ] + } + }, + "/admin/actions/runs": { + "get": { + "operationId": "listAdminWorkflowRuns", + "parameters": [ + { + "description": "workflow event name", + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "description": "workflow branch", + "in": "query", + "name": "branch", + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "triggered by user", + "in": "query", + "name": "actor", + "schema": { + "type": "string" + } + }, + { + "description": "triggering sha of the workflow run", + "in": "query", + "name": "head_sha", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRunsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all runs", + "tags": [ + "admin" + ] + } + }, + "/admin/cron": { + "get": { + "operationId": "adminCronList", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CronList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "List cron tasks", + "tags": [ + "admin" + ] + } + }, + "/admin/cron/{task}": { + "post": { + "operationId": "adminCronRun", + "parameters": [ + { + "description": "task to run", + "in": "path", + "name": "task", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Run cron task", + "tags": [ + "admin" + ] + } + }, + "/admin/emails": { + "get": { + "operationId": "adminGetAllEmails", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/EmailList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "List all emails", + "tags": [ + "admin" + ] + } + }, + "/admin/emails/search": { + "get": { + "operationId": "adminSearchEmails", + "parameters": [ + { + "description": "keyword", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/EmailList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "Search all emails", + "tags": [ + "admin" + ] + } + }, + "/admin/hooks": { + "get": { + "operationId": "adminListHooks", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "system, default or both kinds of webhooks", + "in": "query", + "name": "type", + "schema": { + "default": "system", + "enum": [ + "system", + "default", + "all" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/HookList" + } + }, + "summary": "List system's webhooks", + "tags": [ + "admin" + ] + }, + "post": { + "operationId": "adminCreateHook", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateHookOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Create a hook", + "tags": [ + "admin" + ] + } + }, + "/admin/hooks/{id}": { + "delete": { + "operationId": "adminDeleteHook", + "parameters": [ + { + "description": "id of the hook to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + } + }, + "summary": "Delete a hook", + "tags": [ + "admin" + ] + }, + "get": { + "operationId": "adminGetHook", + "parameters": [ + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Get a hook", + "tags": [ + "admin" + ] + }, + "patch": { + "operationId": "adminEditHook", + "parameters": [ + { + "description": "id of the hook to update", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Update a hook", + "tags": [ + "admin" + ] + } + }, + "/admin/orgs": { + "get": { + "operationId": "adminGetAllOrgs", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OrganizationList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "List all organizations", + "tags": [ + "admin" + ] + } + }, + "/admin/unadopted": { + "get": { + "operationId": "adminUnadoptedList", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "pattern of repositories to search for", + "in": "query", + "name": "pattern", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/StringSlice" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "List unadopted repositories", + "tags": [ + "admin" + ] + } + }, + "/admin/unadopted/{owner}/{repo}": { + "delete": { + "operationId": "adminDeleteUnadoptedRepository", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "Delete unadopted files", + "tags": [ + "admin" + ] + }, + "post": { + "operationId": "adminAdoptRepository", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Adopt unadopted files as a repository", + "tags": [ + "admin" + ] + } + }, + "/admin/users": { + "get": { + "operationId": "adminSearchUsers", + "parameters": [ + { + "description": "ID of the user's login source to search for", + "in": "query", + "name": "source_id", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "identifier of the user, provided by the external authenticator", + "in": "query", + "name": "login_name", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "sort users by attribute. Supported values are \"name\", \"created\", \"updated\" and \"id\". Default is \"name\"", + "in": "query", + "name": "sort", + "schema": { + "type": "string" + } + }, + { + "description": "sort order, either \"asc\" (ascending) or \"desc\" (descending). Default is \"asc\", ignored if \"sort\" is not specified.", + "in": "query", + "name": "order", + "schema": { + "type": "string" + } + }, + { + "description": "search term (username, full name, email)", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "visibility filter. Supported values are \"public\", \"limited\" and \"private\".", + "in": "query", + "name": "visibility", + "schema": { + "type": "string" + } + }, + { + "description": "filter active users", + "in": "query", + "name": "is_active", + "schema": { + "type": "boolean" + } + }, + { + "description": "filter admin users", + "in": "query", + "name": "is_admin", + "schema": { + "type": "boolean" + } + }, + { + "description": "filter restricted users", + "in": "query", + "name": "is_restricted", + "schema": { + "type": "boolean" + } + }, + { + "description": "filter 2FA enabled users", + "in": "query", + "name": "is_2fa_enabled", + "schema": { + "type": "boolean" + } + }, + { + "description": "filter login prohibited users", + "in": "query", + "name": "is_prohibit_login", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Search users according filter conditions", + "tags": [ + "admin" + ] + }, + "post": { + "operationId": "adminCreateUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/User" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a user", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}": { + "delete": { + "operationId": "adminDeleteUser", + "parameters": [ + { + "description": "username of the user to delete", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "purge the user from the system completely", + "in": "query", + "name": "purge", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Delete a user", + "tags": [ + "admin" + ] + }, + "patch": { + "operationId": "adminEditUser", + "parameters": [ + { + "description": "username of the user whose data is to be edited", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditUserOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/User" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Edit an existing user", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/badges": { + "delete": { + "operationId": "adminDeleteUserBadges", + "parameters": [ + { + "description": "username of the user whose badge is to be deleted", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserBadgeOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Remove a badge from a user", + "tags": [ + "admin" + ] + }, + "get": { + "operationId": "adminListUserBadges", + "parameters": [ + { + "description": "username of the user whose badges are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/BadgeList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a user's badges", + "tags": [ + "admin" + ] + }, + "post": { + "operationId": "adminAddUserBadges", + "parameters": [ + { + "description": "username of the user to whom a badge is to be added", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserBadgeOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "Add a badge to a user", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/keys": { + "post": { + "operationId": "adminCreatePublicKey", + "parameters": [ + { + "description": "username of the user who is to receive a public key", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateKeyOption" + } + } + }, + "x-originalParamName": "key" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PublicKey" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Add a public key on behalf of a user", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/keys/{id}": { + "delete": { + "operationId": "adminDeleteUserPublicKey", + "parameters": [ + { + "description": "username of the user whose public key is to be deleted", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the key to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a user's public key", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/orgs": { + "post": { + "operationId": "adminCreateOrg", + "parameters": [ + { + "description": "username of the user who will own the created organization", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrgOption" + } + } + }, + "required": true, + "x-originalParamName": "organization" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Organization" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create an organization", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/rename": { + "post": { + "operationId": "adminRenameUser", + "parameters": [ + { + "description": "current username of the user", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameUserOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Rename a user", + "tags": [ + "admin" + ] + } + }, + "/admin/users/{username}/repos": { + "post": { + "operationId": "adminCreateRepo", + "parameters": [ + { + "description": "username of the user who will own the created repository", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRepoOption" + } + } + }, + "required": true, + "x-originalParamName": "repository" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a repository on behalf of a user", + "tags": [ + "admin" + ] + } + }, + "/gitignore/templates": { + "get": { + "operationId": "listGitignoresTemplates", + "responses": { + "200": { + "$ref": "#/components/responses/GitignoreTemplateList" + } + }, + "summary": "Returns a list of all gitignore templates", + "tags": [ + "miscellaneous" + ] + } + }, + "/gitignore/templates/{name}": { + "get": { + "operationId": "getGitignoreTemplateInfo", + "parameters": [ + { + "description": "name of the template", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GitignoreTemplateInfo" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns information about a gitignore template", + "tags": [ + "miscellaneous" + ] + } + }, + "/label/templates": { + "get": { + "operationId": "listLabelTemplates", + "responses": { + "200": { + "$ref": "#/components/responses/LabelTemplateList" + } + }, + "summary": "Returns a list of all label templates", + "tags": [ + "miscellaneous" + ] + } + }, + "/label/templates/{name}": { + "get": { + "operationId": "getLabelTemplateInfo", + "parameters": [ + { + "description": "name of the template", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LabelTemplateInfo" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns all labels in a template", + "tags": [ + "miscellaneous" + ] + } + }, + "/licenses": { + "get": { + "operationId": "listLicenseTemplates", + "responses": { + "200": { + "$ref": "#/components/responses/LicenseTemplateList" + } + }, + "summary": "Returns a list of all license templates", + "tags": [ + "miscellaneous" + ] + } + }, + "/licenses/{name}": { + "get": { + "operationId": "getLicenseTemplateInfo", + "parameters": [ + { + "description": "name of the license", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LicenseTemplateInfo" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns information about a license template", + "tags": [ + "miscellaneous" + ] + } + }, + "/markdown": { + "post": { + "operationId": "renderMarkdown", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkdownOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/MarkdownRender" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Render a markdown document as HTML", + "tags": [ + "miscellaneous" + ] + } + }, + "/markdown/raw": { + "post": { + "operationId": "renderMarkdownRaw", + "requestBody": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "Request body to render", + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/MarkdownRender" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Render raw markdown as HTML", + "tags": [ + "miscellaneous" + ] + } + }, + "/markup": { + "post": { + "operationId": "renderMarkup", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkupOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/MarkupRender" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Render a markup document as HTML", + "tags": [ + "miscellaneous" + ] + } + }, + "/notifications": { + "get": { + "operationId": "notifyGetList", + "parameters": [ + { + "description": "If true, show notifications marked as read. Default value is false", + "in": "query", + "name": "all", + "schema": { + "type": "boolean" + } + }, + { + "description": "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread \u0026 pinned.", + "in": "query", + "name": "status-types", + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "filter notifications by subject type", + "in": "query", + "name": "subject-type", + "schema": { + "items": { + "enum": [ + "issue", + "pull", + "commit", + "repository" + ], + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/NotificationThreadList" + } + }, + "summary": "List users's notification threads", + "tags": [ + "notification" + ] + }, + "put": { + "operationId": "notifyReadList", + "parameters": [ + { + "description": "Describes the last point that notifications were checked. Anything updated since this time will not be updated.", + "in": "query", + "name": "last_read_at", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "If true, mark all notifications on this repo. Default value is false", + "in": "query", + "name": "all", + "schema": { + "type": "string" + } + }, + { + "description": "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread.", + "in": "query", + "name": "status-types", + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Status to mark notifications as, Defaults to read.", + "in": "query", + "name": "to-status", + "schema": { + "type": "string" + } + } + ], + "responses": { + "205": { + "$ref": "#/components/responses/NotificationThreadList" + } + }, + "summary": "Mark notification threads as read, pinned or unread", + "tags": [ + "notification" + ] + } + }, + "/notifications/new": { + "get": { + "operationId": "notifyNewAvailable", + "responses": { + "200": { + "$ref": "#/components/responses/NotificationCount" + } + }, + "summary": "Check if unread notifications exist", + "tags": [ + "notification" + ] + } + }, + "/notifications/threads/{id}": { + "get": { + "operationId": "notifyGetThread", + "parameters": [ + { + "description": "id of notification thread", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/NotificationThread" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get notification thread by ID", + "tags": [ + "notification" + ] + }, + "patch": { + "operationId": "notifyReadThread", + "parameters": [ + { + "description": "id of notification thread", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Status to mark notifications as", + "in": "query", + "name": "to-status", + "schema": { + "default": "read", + "type": "string" + } + } + ], + "responses": { + "205": { + "$ref": "#/components/responses/NotificationThread" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Mark notification thread as read by ID", + "tags": [ + "notification" + ] + } + }, + "/org/{org}/repos": { + "post": { + "deprecated": true, + "operationId": "createOrgRepoDeprecated", + "parameters": [ + { + "description": "name of organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a repository in an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs": { + "get": { + "operationId": "orgGetAll", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OrganizationList" + } + }, + "summary": "Get list of organizations", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "orgCreate", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrgOption" + } + } + }, + "required": true, + "x-originalParamName": "organization" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Organization" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}": { + "delete": { + "operationId": "orgDelete", + "parameters": [ + { + "description": "organization that is to be deleted", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete an organization", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgGet", + "parameters": [ + { + "description": "name of the organization to get", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Organization" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an organization", + "tags": [ + "organization" + ] + }, + "patch": { + "operationId": "orgEdit", + "parameters": [ + { + "description": "name of the organization to edit", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditOrgOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Organization" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Edit an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/jobs": { + "get": { + "operationId": "getOrgWorkflowJobs", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get org-level workflow jobs", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/runners": { + "get": { + "operationId": "getOrgRunners", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "filter by disabled status (true or false)", + "in": "query", + "name": "disabled", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RunnerList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get org-level runners", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/runners/registration-token": { + "post": { + "operationId": "orgCreateRunnerRegistrationToken", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RegistrationToken" + } + }, + "summary": "Get an organization's actions runner registration token", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/runners/{runner_id}": { + "delete": { + "operationId": "deleteOrgRunner", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "runner has been deleted" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete an org-level runner", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "getOrgRunner", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an org-level runner", + "tags": [ + "organization" + ] + }, + "patch": { + "operationId": "updateOrgRunner", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditActionRunnerOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update an org-level runner", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/runs": { + "get": { + "operationId": "getOrgWorkflowRuns", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "workflow event name", + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "description": "workflow branch", + "in": "query", + "name": "branch", + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "triggered by user", + "in": "query", + "name": "actor", + "schema": { + "type": "string" + } + }, + { + "description": "triggering sha of the workflow run", + "in": "query", + "name": "head_sha", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRunsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get org-level workflow runs", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/secrets": { + "get": { + "operationId": "orgListActionsSecrets", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/SecretList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's actions secrets", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/secrets/{secretname}": { + "delete": { + "operationId": "deleteOrgSecret", + "parameters": [ + { + "description": "name of organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "delete one secret of the organization" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a secret in an organization", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "updateOrgSecret", + "parameters": [ + { + "description": "name of organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrUpdateSecretOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when creating a secret" + }, + "204": { + "description": "response when updating a secret" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create or Update a secret value in an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/variables": { + "get": { + "operationId": "getOrgVariablesList", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VariableList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an org-level variables list", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/actions/variables/{variablename}": { + "delete": { + "operationId": "deleteOrgVariable", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionVariable" + }, + "201": { + "description": "response when deleting a variable" + }, + "204": { + "description": "response when deleting a variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete an org-level variable", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "getOrgVariable", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionVariable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an org-level variable", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "createOrgVariable", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "successfully created the org-level variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "409": { + "description": "variable name already exists." + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Create an org-level variable", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "updateOrgVariable", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when updating an org-level variable" + }, + "204": { + "description": "response when updating an org-level variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update an org-level variable", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/activities/feeds": { + "get": { + "operationId": "orgListActivityFeeds", + "parameters": [ + { + "description": "name of the org", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the date of the activities to be found", + "in": "query", + "name": "date", + "schema": { + "format": "date", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActivityFeedsList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's activity feeds", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/avatar": { + "delete": { + "operationId": "orgDeleteAvatar", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete Avatar", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "orgUpdateAvatar", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserAvatarOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update Avatar", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/blocks": { + "get": { + "operationId": "organizationListBlocks", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + } + }, + "summary": "List users blocked by the organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/blocks/{username}": { + "delete": { + "operationId": "organizationUnblockUser", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to unblock", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Unblock a user", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "organizationCheckUserBlock", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to check", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Check if a user is blocked by the organization", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "organizationBlockUser", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to block", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "optional note for the block", + "in": "query", + "name": "note", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Block a user", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/hooks": { + "get": { + "operationId": "orgListHooks", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/HookList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's webhooks", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "orgCreateHook", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateHookOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create a hook", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/hooks/{id}": { + "delete": { + "operationId": "orgDeleteHook", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a hook", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgGetHook", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a hook", + "tags": [ + "organization" + ] + }, + "patch": { + "operationId": "orgEditHook", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to update", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update a hook", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/labels": { + "get": { + "operationId": "orgListLabels", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LabelList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's labels", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "orgCreateLabel", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLabelOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a label for an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/labels/{id}": { + "delete": { + "operationId": "orgDeleteLabel", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a label", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgGetLabel", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a single label", + "tags": [ + "organization" + ] + }, + "patch": { + "operationId": "orgEditLabel", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLabelOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a label", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/members": { + "get": { + "operationId": "orgListMembers", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's members", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/members/{username}": { + "delete": { + "operationId": "orgDeleteMember", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to remove from the organization", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "member removed" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a member from an organization", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgIsMember", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to check for an organization membership", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "user is a member" + }, + "303": { + "description": "redirection to /orgs/{org}/public_members/{username}" + }, + "404": { + "description": "user is not a member" + } + }, + "summary": "Check if a user is a member of an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/public_members": { + "get": { + "operationId": "orgListPublicMembers", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's public members", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/public_members/{username}": { + "delete": { + "operationId": "orgConcealMember", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user whose membership is to be concealed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Conceal a user's membership", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgIsPublicMember", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to check for a public organization membership", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "user is a public member" + }, + "404": { + "description": "user is not a public member" + } + }, + "summary": "Check if a user is a public member of an organization", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "orgPublicizeMember", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user whose membership is to be publicized", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "membership publicized" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Publicize a user's membership", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/rename": { + "post": { + "operationId": "renameOrg", + "parameters": [ + { + "description": "existing org name", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameOrgOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Rename an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/repos": { + "delete": { + "operationId": "orgDeleteRepos", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "202": { + "$ref": "#/components/responses/empty" + }, + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete all repositories in an organization", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgListRepos", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's repos", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "createOrgRepo", + "parameters": [ + { + "description": "name of organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create a repository in an organization", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/teams": { + "get": { + "operationId": "orgListTeams", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TeamList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an organization's teams", + "tags": [ + "organization" + ] + }, + "post": { + "operationId": "orgCreateTeam", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTeamOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Team" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a team", + "tags": [ + "organization" + ] + } + }, + "/orgs/{org}/teams/search": { + "get": { + "operationId": "teamSearch", + "parameters": [ + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "keywords to search", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "include search within team description (defaults to true)", + "in": "query", + "name": "include_desc", + "schema": { + "type": "boolean" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Team" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + } + }, + "type": "object" + } + } + }, + "description": "SearchResults of a successful search" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Search for teams within an organization", + "tags": [ + "organization" + ] + } + }, + "/packages/{owner}": { + "get": { + "operationId": "listPackages", + "parameters": [ + { + "description": "owner of the packages", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "package type filter", + "in": "query", + "name": "type", + "schema": { + "enum": [ + "alpine", + "cargo", + "chef", + "composer", + "conan", + "conda", + "container", + "cran", + "debian", + "generic", + "go", + "helm", + "maven", + "npm", + "nuget", + "pub", + "pypi", + "rpm", + "rubygems", + "swift", + "terraform", + "vagrant" + ], + "type": "string" + } + }, + { + "description": "name filter", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PackageList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets all packages of an owner", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}": { + "delete": { + "operationId": "deletePackage", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a package", + "tags": [ + "package" + ] + }, + "get": { + "operationId": "listPackageVersions", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PackageList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets all versions of a package", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}/-/latest": { + "get": { + "operationId": "getLatestPackageVersion", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Package" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the latest version of a package", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}/-/link/{repo_name}": { + "post": { + "operationId": "linkPackage", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository to link.", + "in": "path", + "name": "repo_name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Link a package to a repository", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}/-/unlink": { + "post": { + "operationId": "unlinkPackage", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unlink a package from a repository", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}/{version}": { + "delete": { + "operationId": "deletePackageVersion", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "version of the package", + "in": "path", + "name": "version", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a package version", + "tags": [ + "package" + ] + }, + "get": { + "operationId": "getPackage", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "version of the package", + "in": "path", + "name": "version", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Package" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets a package", + "tags": [ + "package" + ] + } + }, + "/packages/{owner}/{type}/{name}/{version}/files": { + "get": { + "operationId": "listPackageFiles", + "parameters": [ + { + "description": "owner of the package", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of the package", + "in": "path", + "name": "type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the package", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "version of the package", + "in": "path", + "name": "version", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PackageFileList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets all files of a package", + "tags": [ + "package" + ] + } + }, + "/repos/issues/search": { + "get": { + "operationId": "issueSearchIssues", + "parameters": [ + { + "description": "State of the issue", + "in": "query", + "name": "state", + "schema": { + "default": "open", + "enum": [ + "open", + "closed", + "all" + ], + "type": "string" + } + }, + { + "description": "Comma-separated list of label names. Fetch only issues that have any of these labels. Non existent labels are discarded.", + "in": "query", + "name": "labels", + "schema": { + "type": "string" + } + }, + { + "description": "Comma-separated list of milestone names. Fetch only issues that have any of these milestones. Non existent milestones are discarded.", + "in": "query", + "name": "milestones", + "schema": { + "type": "string" + } + }, + { + "description": "Search string", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "Filter by issue type", + "in": "query", + "name": "type", + "schema": { + "enum": [ + "issues", + "pulls" + ], + "type": "string" + } + }, + { + "description": "Only show issues updated after the given time (RFC 3339 format)", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show issues updated before the given time (RFC 3339 format)", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Filter issues or pulls assigned to the authenticated user", + "in": "query", + "name": "assigned", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "Filter issues or pulls created by the authenticated user", + "in": "query", + "name": "created", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "Filter issues or pulls mentioning the authenticated user", + "in": "query", + "name": "mentioned", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "Filter pull requests where the authenticated user's review was requested", + "in": "query", + "name": "review_requested", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "Filter pull requests reviewed by the authenticated user", + "in": "query", + "name": "reviewed", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "Filter by repository owner", + "in": "query", + "name": "owner", + "schema": { + "type": "string" + } + }, + { + "description": "Only show items which were created by the given user", + "in": "query", + "name": "created_by", + "schema": { + "type": "string" + } + }, + { + "description": "Filter by team (requires organization owner parameter)", + "in": "query", + "name": "team", + "schema": { + "type": "string" + } + }, + { + "description": "Page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "default": 1, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Number of items per page", + "in": "query", + "name": "limit", + "schema": { + "minimum": 0, + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Search for issues across the repositories that the user has access to", + "tags": [ + "issue" + ] + } + }, + "/repos/migrate": { + "post": { + "operationId": "repoMigrate", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MigrateRepoOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "409": { + "description": "The repository with the same name already exists." + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Migrate a remote git repository", + "tags": [ + "repository" + ] + } + }, + "/repos/search": { + "get": { + "operationId": "repoSearch", + "parameters": [ + { + "description": "keyword", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "Limit search to repositories with keyword as topic", + "in": "query", + "name": "topic", + "schema": { + "type": "boolean" + } + }, + { + "description": "include search of keyword within repository description", + "in": "query", + "name": "includeDesc", + "schema": { + "type": "boolean" + } + }, + { + "description": "search only for repos that the user with the given id owns or contributes to", + "in": "query", + "name": "uid", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "repo owner to prioritize in the results", + "in": "query", + "name": "priority_owner_id", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "search only for repos that belong to the given team id", + "in": "query", + "name": "team_id", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "search only for repos that the user with the given id has starred", + "in": "query", + "name": "starredBy", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "include private repositories this user has access to (defaults to true)", + "in": "query", + "name": "private", + "schema": { + "type": "boolean" + } + }, + { + "description": "show only pubic, private or all repositories (defaults to all)", + "in": "query", + "name": "is_private", + "schema": { + "type": "boolean" + } + }, + { + "description": "include template repositories this user has access to (defaults to true)", + "in": "query", + "name": "template", + "schema": { + "type": "boolean" + } + }, + { + "description": "show only archived, non-archived or all repositories (defaults to all)", + "in": "query", + "name": "archived", + "schema": { + "type": "boolean" + } + }, + { + "description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"", + "in": "query", + "name": "mode", + "schema": { + "type": "string" + } + }, + { + "description": "if `uid` is given, search only for repos that the user owns", + "in": "query", + "name": "exclusive", + "schema": { + "type": "boolean" + } + }, + { + "description": "sort repos by attribute. Supported values are \"alpha\", \"created\", \"updated\", \"size\", \"git_size\", \"lfs_size\", \"stars\", \"forks\" and \"id\". Default is \"alpha\"", + "in": "query", + "name": "sort", + "schema": { + "type": "string" + } + }, + { + "description": "sort order, either \"asc\" (ascending) or \"desc\" (descending). Default is \"asc\", ignored if \"sort\" is not specified.", + "in": "query", + "name": "order", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/SearchResults" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Search for repositories", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}": { + "delete": { + "operationId": "repoDelete", + "parameters": [ + { + "description": "owner of the repo to delete", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to delete", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGet", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Repository" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a repository", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEdit", + "parameters": [ + { + "description": "owner of the repo to edit", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to edit", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditRepoOption" + } + } + }, + "description": "Properties of a repo that you can edit", + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Edit a repository's properties. Only fields that are set will be changed.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/artifacts": { + "get": { + "operationId": "getArtifacts", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the artifact", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ArtifactsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all artifacts for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}": { + "delete": { + "operationId": "deleteArtifact", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the artifact", + "in": "path", + "name": "artifact_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Deletes a specific artifact for a workflow run", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "getArtifact", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the artifact", + "in": "path", + "name": "artifact_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Artifact" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets a specific artifact for a workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}/zip": { + "get": { + "operationId": "downloadArtifact", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the artifact", + "in": "path", + "name": "artifact_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "302": { + "description": "redirect to the blob download" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Downloads a specific artifact for a workflow run redirects to blob url", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/jobs": { + "get": { + "operationId": "listWorkflowJobs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all jobs for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/jobs/{job_id}": { + "get": { + "operationId": "getWorkflowJob", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the job", + "in": "path", + "name": "job_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJob" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets a specific workflow job for a workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs": { + "get": { + "operationId": "downloadActionsRunJobLogs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the job", + "in": "path", + "name": "job_id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "output blob content" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Downloads the job logs for a workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runners": { + "get": { + "operationId": "getRepoRunners", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "filter by disabled status (true or false)", + "in": "query", + "name": "disabled", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RunnerList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get repo-level runners", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runners/registration-token": { + "post": { + "operationId": "repoCreateRunnerRegistrationToken", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RegistrationToken" + } + }, + "summary": "Get a repository's actions runner registration token", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runners/{runner_id}": { + "delete": { + "operationId": "deleteRepoRunner", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "runner has been deleted" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a repo-level runner", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "getRepoRunner", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a repo-level runner", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "updateRepoRunner", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditActionRunnerOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a repo-level runner", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs": { + "get": { + "operationId": "getWorkflowRuns", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "workflow event name", + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "description": "workflow branch", + "in": "query", + "name": "branch", + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "triggered by user", + "in": "query", + "name": "actor", + "schema": { + "type": "string" + } + }, + { + "description": "triggering sha of the workflow run", + "in": "query", + "name": "head_sha", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRunsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all runs for a repository run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}": { + "delete": { + "operationId": "deleteActionRun", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "runid of the workflow run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a workflow run", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "GetWorkflowRun", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRun" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets a specific workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/artifacts": { + "get": { + "operationId": "getArtifactsOfRun", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "runid of the workflow run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "name of the artifact", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ArtifactsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all artifacts for a repository run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/attempts/{attempt}": { + "get": { + "operationId": "getWorkflowRunAttempt", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "logical attempt number of the run", + "in": "path", + "name": "attempt", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRun" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets a specific workflow run attempt", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/attempts/{attempt}/jobs": { + "get": { + "operationId": "listWorkflowRunAttemptJobs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the workflow run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "logical attempt number of the run", + "in": "path", + "name": "attempt", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all jobs for a workflow run attempt", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/jobs": { + "get": { + "operationId": "listWorkflowRunJobs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "runid of the workflow run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lists all jobs for a workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/jobs/{job_id}/rerun": { + "post": { + "operationId": "rerunWorkflowJob", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "id of the job", + "in": "path", + "name": "job_id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/WorkflowJob" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Reruns a specific workflow job in a run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/rerun": { + "post": { + "operationId": "rerunWorkflowRun", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/WorkflowRun" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Reruns an entire workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/runs/{run}/rerun-failed-jobs": { + "post": { + "operationId": "rerunFailedWorkflowRun", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the run", + "in": "path", + "name": "run", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Reruns all failed jobs in a workflow run", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/secrets": { + "get": { + "operationId": "repoListActionsSecrets", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/SecretList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an repo's actions secrets", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/secrets/{secretname}": { + "delete": { + "operationId": "deleteRepoSecret", + "parameters": [ + { + "description": "owner of the repository", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "delete one secret of the repository" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a secret in a repository", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "updateRepoSecret", + "parameters": [ + { + "description": "owner of the repository", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrUpdateSecretOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when creating a secret" + }, + "204": { + "description": "response when updating a secret" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create or Update a secret value in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/tasks": { + "get": { + "operationId": "ListActionTasks", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results, default maximum page size is 50", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TasksList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/conflict" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "List a repository's action tasks", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/variables": { + "get": { + "operationId": "getRepoVariablesList", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VariableList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get repo-level variables list", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/variables/{variablename}": { + "delete": { + "operationId": "deleteRepoVariable", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionVariable" + }, + "201": { + "description": "response when deleting a variable" + }, + "204": { + "description": "response when deleting a variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a repo-level variable", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "getRepoVariable", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionVariable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a repo-level variable", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "createRepoVariable", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when creating a repo-level variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "409": { + "description": "variable name already exists." + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Create a repo-level variable", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "updateRepoVariable", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repository", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when updating a repo-level variable" + }, + "204": { + "description": "response when updating a repo-level variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update a repo-level variable", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/workflows": { + "get": { + "operationId": "ActionsListRepositoryWorkflows", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionWorkflowList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "summary": "List repository workflows", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/workflows/{workflow_id}": { + "get": { + "operationId": "ActionsGetWorkflow", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the workflow", + "in": "path", + "name": "workflow_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionWorkflow" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Get a workflow", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable": { + "put": { + "operationId": "ActionsDisableWorkflow", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the workflow", + "in": "path", + "name": "workflow_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Disable a workflow", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches": { + "post": { + "operationId": "ActionsDispatchWorkflow", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the workflow", + "in": "path", + "name": "workflow_id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Whether the response should include the workflow run ID and URLs.", + "in": "query", + "name": "return_run_details", + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateActionWorkflowDispatch" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/RunDetails" + }, + "204": { + "description": "No Content, if return_run_details is missing or false" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a workflow dispatch event", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable": { + "put": { + "operationId": "ActionsEnableWorkflow", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the workflow", + "in": "path", + "name": "workflow_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/conflict" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Enable a workflow", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/activities/feeds": { + "get": { + "operationId": "repoListActivityFeeds", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the date of the activities to be found", + "in": "query", + "name": "date", + "schema": { + "format": "date", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActivityFeedsList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's activity feeds", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/archive/{archive}": { + "get": { + "operationId": "repoGetArchive", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the git reference for download with attached archive format (e.g. master.zip)", + "in": "path", + "name": "archive", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "subpath of the repository to download", + "in": "query", + "name": "path", + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + ], + "responses": { + "200": { + "description": "success" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an archive of a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/assignees": { + "get": { + "operationId": "repoGetAssignees", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Return all users that have write access and can be assigned to issues", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/avatar": { + "delete": { + "operationId": "repoDeleteAvatar", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete avatar", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoUpdateAvatar", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRepoAvatarOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update avatar", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/branch_protections": { + "get": { + "operationId": "repoListBranchProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/BranchProtectionList" + } + }, + "summary": "List branch protections for a repository", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateBranchProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateBranchProtectionOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/BranchProtection" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a branch protections for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/branch_protections/priority": { + "post": { + "operationId": "repoUpdateBranchProtectionPriories", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateBranchProtectionPriories" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Update the priorities of branch protections for a repository.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/branch_protections/{name}": { + "delete": { + "operationId": "repoDeleteBranchProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of protected branch", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a specific branch protection for the repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetBranchProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of protected branch", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/BranchProtection" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a specific branch protection for the repository", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditBranchProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of protected branch", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditBranchProtectionOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/BranchProtection" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit a branch protections for a repository. Only fields that are set will be changed", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/branches": { + "get": { + "operationId": "repoListBranches", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/BranchList" + } + }, + "summary": "List a repository's branches", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateBranch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateBranchRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Branch" + }, + "403": { + "description": "The branch is archived or a mirror." + }, + "404": { + "description": "The old branch does not exist." + }, + "409": { + "description": "The branch with the same name already exists." + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a branch", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/branches/{branch}": { + "delete": { + "operationId": "repoDeleteBranch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "branch to delete", + "in": "path", + "name": "branch", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete a specific branch from a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetBranch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "branch to get", + "in": "path", + "name": "branch", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Branch" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Retrieve a specific branch from a repository, including its effective branch protection", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoRenameBranch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the branch", + "in": "path", + "name": "branch", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameBranchRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Rename a branch", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoUpdateBranch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the branch", + "in": "path", + "name": "branch", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateBranchRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/conflict" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a branch reference to a new commit", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/collaborators": { + "get": { + "operationId": "repoListCollaborators", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's collaborators", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/collaborators/{collaborator}": { + "delete": { + "operationId": "repoDeleteCollaborator", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the collaborator to delete", + "in": "path", + "name": "collaborator", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Delete a collaborator from a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoCheckCollaborator", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to check for being a collaborator", + "in": "path", + "name": "collaborator", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Check if a user is a collaborator of a repository", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoAddCollaborator", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user to add or update as a collaborator", + "in": "path", + "name": "collaborator", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddCollaboratorOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Add or Update a collaborator to a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/collaborators/{collaborator}/permission": { + "get": { + "operationId": "repoGetRepoPermissions", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the collaborator whose permissions are to be obtained", + "in": "path", + "name": "collaborator", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepoCollaboratorPermission" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get repository permissions for a user", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/commits": { + "get": { + "operationId": "repoGetAllCommits", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "SHA or branch to start listing commits from (usually 'master')", + "in": "query", + "name": "sha", + "schema": { + "type": "string" + } + }, + { + "description": "filepath of a file/dir", + "in": "query", + "name": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Only commits after this date will be returned (ISO 8601 format)", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only commits before this date will be returned (ISO 8601 format)", + "in": "query", + "name": "until", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "include diff stats for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "stat", + "schema": { + "type": "boolean" + } + }, + { + "description": "include verification for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "verification", + "schema": { + "type": "boolean" + } + }, + { + "description": "include a list of affected files for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "files", + "schema": { + "type": "boolean" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results (ignored if used with 'path')", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "commits that match the given specifier will not be listed.", + "in": "query", + "name": "not", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommitList" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/EmptyRepository" + } + }, + "summary": "Get a list of all commits from a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/commits/{ref}/status": { + "get": { + "operationId": "repoGetCombinedStatusByRef", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of branch/tag/commit", + "in": "path", + "name": "ref", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CombinedStatus" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a commit's combined status, by branch/tag/commit reference", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/commits/{ref}/statuses": { + "get": { + "operationId": "repoListStatusesByRef", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of branch/tag/commit", + "in": "path", + "name": "ref", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of sort", + "in": "query", + "name": "sort", + "schema": { + "enum": [ + "oldest", + "recentupdate", + "leastupdate", + "leastindex", + "highestindex" + ], + "type": "string" + } + }, + { + "description": "type of state", + "in": "query", + "name": "state", + "schema": { + "enum": [ + "pending", + "success", + "error", + "failure", + "warning" + ], + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommitStatusList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a commit's statuses, by branch/tag/commit reference", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/commits/{sha}/pull": { + "get": { + "operationId": "repoGetCommitPullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "SHA of the commit to get", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullRequest" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the merged pull request of the commit", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/compare/{basehead}": { + "get": { + "operationId": "repoCompareDiff", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "compare two branches or commits", + "in": "path", + "name": "basehead", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Compare" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get commit comparison information", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/contents": { + "get": { + "description": "This API follows GitHub's design, and it is not easy to use. Recommend users to use our \"contents-ext\" API instead.", + "operationId": "repoGetContentsList", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ContentsListResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the metadata of all the entries of the root dir.", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoChangeFiles", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangeFilesOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/FilesResponse" + }, + "403": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Modify multiple files in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/contents-ext/{filepath}": { + "get": { + "description": "It guarantees that only one of the response fields is set if the request succeeds. Users can pass \"includes=file_content\" or \"includes=lfs_metadata\" to retrieve more fields. \"includes=file_content\" only works for single file, if you need to retrieve file contents in batch, use \"file-contents\" API after listing the directory.", + "operationId": "repoGetContentsExt", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be \"required\", you can leave it empty or pass a single dot (\".\") to get the root directory.", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the name of the commit/branch/tag, default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + }, + { + "description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, \"lfs_metadata\" will try to retrieve LFS metadata, \"commit_metadata\" will try to retrieve commit metadata, and \"commit_message\" will try to retrieve commit message.", + "in": "query", + "name": "includes", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ContentsExtResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "The extended \"contents\" API, to get file metadata and/or content, or list a directory.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/contents/{filepath}": { + "delete": { + "operationId": "repoDeleteFile", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the file to delete", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteFileOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileDeleteResponse" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete a file in a repository", + "tags": [ + "repository" + ] + }, + "get": { + "description": "This API follows GitHub's design, and it is not easy to use. Recommend users to use the \"contents-ext\" API instead.", + "operationId": "repoGetContents", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the dir, file, symlink or submodule in the repo", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ContentsResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir.", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateFile", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the file to create", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateFileOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/FileResponse" + }, + "403": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a file in a repository", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoUpdateFile", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the file to update", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateFileOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileResponse" + }, + "201": { + "$ref": "#/components/responses/FileResponse" + }, + "403": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Update a file in a repository if SHA is set, or create the file if SHA is not set", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/diffpatch": { + "post": { + "operationId": "repoApplyDiffPatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplyDiffPatchFileOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Apply diff patch to repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/editorconfig/{filepath}": { + "get": { + "operationId": "repoGetEditorConfig", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "filepath of file to get", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the EditorConfig definitions of a file in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/file-contents": { + "get": { + "description": "See the POST method. This GET method supports using JSON encoded request body in query parameter.", + "operationId": "repoGetFileContents", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + }, + { + "description": "The JSON encoded body (see the POST request): {\"files\": [\"filename1\", \"filename2\"]}", + "in": "query", + "name": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ContentsListResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the metadata and contents of requested files", + "tags": [ + "repository" + ] + }, + "post": { + "description": "Uses automatic pagination based on default page size and max response size and returns the maximum allowed number of files. Files which could not be retrieved are null. Files which are too large are being returned with `encoding == null`, `content == null` and `size \u003e 0`, they can be requested separately by using the `download_url`.", + "operationId": "repoGetFileContentsPost", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetFilesOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/ContentsListResponse" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the metadata and contents of requested files", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/forks": { + "get": { + "operationId": "listForks", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's forks", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "createFork", + "parameters": [ + { + "description": "owner of the repo to fork", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to fork", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateForkOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "202": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "description": "The repository with the same name already exists." + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Fork a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/blobs/{sha}": { + "get": { + "operationId": "GetBlob", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "sha of the commit", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GitBlobResponse" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the blob of a repository.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/commits/{sha}": { + "get": { + "operationId": "repoGetSingleCommit", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "a git ref or commit sha", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "include diff stats for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "stat", + "schema": { + "type": "boolean" + } + }, + { + "description": "include verification for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "verification", + "schema": { + "type": "boolean" + } + }, + { + "description": "include a list of affected files for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "files", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Commit" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Get a single commit from a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/commits/{sha}.{diffType}": { + "get": { + "operationId": "repoDownloadCommitDiffOrPatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "SHA of the commit to get", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "whether the output is diff or patch", + "in": "path", + "name": "diffType", + "required": true, + "schema": { + "enum": [ + "diff", + "patch" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/string" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a commit's diff or patch", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/notes/{sha}": { + "get": { + "operationId": "repoGetNote", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "a git ref or commit sha", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "include verification for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "verification", + "schema": { + "type": "boolean" + } + }, + { + "description": "include a list of affected files for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "files", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Note" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Get a note corresponding to a single commit from a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/refs": { + "get": { + "operationId": "repoListAllGitRefs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ReferenceList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get specified ref or filtered repository's refs", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/refs/{ref}": { + "get": { + "operationId": "repoListGitRefs", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "part or full name of the ref", + "in": "path", + "name": "ref", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ReferenceList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get specified ref or filtered repository's refs", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/tags/{sha}": { + "get": { + "operationId": "GetAnnotatedTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "sha of the tag. The Git tags API only supports annotated tag objects, not lightweight tags.", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AnnotatedTag" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the tag object of an annotated tag (not lightweight tags)", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/git/trees/{sha}": { + "get": { + "operationId": "GetTree", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "sha of the commit", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "show all directories and files", + "in": "query", + "name": "recursive", + "schema": { + "type": "boolean" + } + }, + { + "description": "page number; the 'truncated' field in the response will be true if there are still more items after this page, false if the last page", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "number of items per page", + "in": "query", + "name": "per_page", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GitTreeResponse" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the tree of a repository.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/hooks": { + "get": { + "operationId": "repoListHooks", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/HookList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the hooks in a repository", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create a hook", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/hooks/git": { + "get": { + "operationId": "repoListGitHooks", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GitHookList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the Git hooks in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/hooks/git/{id}": { + "delete": { + "operationId": "repoDeleteGitHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a Git hook in a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetGitHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GitHook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a Git hook", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditGitHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditGitHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/GitHook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Edit a Git hook in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/hooks/{id}": { + "delete": { + "operationId": "repoDeleteHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a hook in a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a hook", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the hook", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Edit a hook in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/hooks/{id}/tests": { + "post": { + "operationId": "repoTestHook", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the hook to test", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The name of the commit/branch/tag, indicates which commit will be loaded to the webhook payload.", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Test a push webhook", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/issue_config": { + "get": { + "operationId": "repoGetIssueConfig", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepoIssueConfig" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns the issue config for a repo", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/issue_config/validate": { + "get": { + "operationId": "repoValidateIssueConfig", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepoIssueConfigValidation" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns the validation information for a issue config", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/issue_templates": { + "get": { + "operationId": "repoGetIssueTemplates", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueTemplates" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get available issue templates for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/issues": { + "get": { + "operationId": "issueListIssues", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "whether issue is open or closed", + "in": "query", + "name": "state", + "schema": { + "enum": [ + "closed", + "open", + "all" + ], + "type": "string" + } + }, + { + "description": "comma separated list of label names. Fetch only issues that have any of this label names. Non existent labels are discarded.", + "in": "query", + "name": "labels", + "schema": { + "type": "string" + } + }, + { + "description": "search string", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "filter by type (issues / pulls) if set", + "in": "query", + "name": "type", + "schema": { + "enum": [ + "issues", + "pulls" + ], + "type": "string" + } + }, + { + "description": "comma separated list of milestone names or ids. It uses names and fall back to ids. Fetch only issues that have any of this milestones. Non existent milestones are discarded", + "in": "query", + "name": "milestones", + "schema": { + "type": "string" + } + }, + { + "description": "Only show items updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show items updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show items which were created by the given user", + "in": "query", + "name": "created_by", + "schema": { + "type": "string" + } + }, + { + "description": "Only show items for which the given user is assigned", + "in": "query", + "name": "assigned_by", + "schema": { + "type": "string" + } + }, + { + "description": "Only show items in which the given user was mentioned", + "in": "query", + "name": "mentioned_by", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's issues", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIssueOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Issue" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "412": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create an issue. If using deadline only the date will be taken into account, and time of day ignored.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/comments": { + "get": { + "operationId": "issueGetRepoComments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "if provided, only comments updated since the provided time are returned.", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "if provided, only comments updated before the provided time are returned.", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommentList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List all comments in a repository", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/comments/{id}": { + "delete": { + "operationId": "issueDeleteComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of comment to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a comment", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Comment" + }, + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a comment", + "tags": [ + "issue" + ] + }, + "patch": { + "operationId": "issueEditComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditIssueCommentOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Comment" + }, + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit a comment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/comments/{id}/assets": { + "get": { + "operationId": "issueListIssueCommentAttachments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AttachmentList" + }, + "404": { + "$ref": "#/components/responses/error" + } + }, + "summary": "List comment's attachments", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateIssueCommentAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "name of the attachment", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "attachment": { + "description": "attachment to upload", + "format": "binary", + "type": "string", + "x-formData-name": "attachment" + } + }, + "required": [ + "attachment" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "413": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a comment attachment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}": { + "delete": { + "operationId": "issueDeleteIssueCommentAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to delete", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete a comment attachment", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetIssueCommentAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to get", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Get a comment attachment", + "tags": [ + "issue" + ] + }, + "patch": { + "operationId": "issueEditIssueCommentAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to edit", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditAttachmentOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit a comment attachment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/comments/{id}/reactions": { + "delete": { + "operationId": "issueDeleteCommentReaction", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditReactionOption" + } + } + }, + "x-originalParamName": "content" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a reaction from a comment of an issue", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetCommentReactions", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ReactionList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a list of reactions from a comment of an issue", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issuePostCommentReaction", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the comment to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditReactionOption" + } + } + }, + "x-originalParamName": "content" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Reaction" + }, + "201": { + "$ref": "#/components/responses/Reaction" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add a reaction to a comment of an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/pinned": { + "get": { + "operationId": "repoListPinnedIssues", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's pinned issues", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}": { + "delete": { + "operationId": "issueDelete", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of issue to delete", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete an issue", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Issue" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an issue", + "tags": [ + "issue" + ] + }, + "patch": { + "description": "Pass `content_version` to enable optimistic locking on body edits.\nIf the version doesn't match the current value, the request fails with 409 Conflict.\n", + "operationId": "issueEditIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to edit", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditIssueOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Issue" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "412": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Edit an issue. If using deadline only the date will be taken into account, and time of day ignored.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/assets": { + "get": { + "operationId": "issueListIssueAttachments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AttachmentList" + }, + "404": { + "$ref": "#/components/responses/error" + } + }, + "summary": "List issue's attachments", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateIssueAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "name of the attachment", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "attachment": { + "description": "attachment to upload", + "format": "binary", + "type": "string", + "x-formData-name": "attachment" + } + }, + "required": [ + "attachment" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "413": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create an issue attachment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}": { + "delete": { + "operationId": "issueDeleteIssueAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to delete", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete an issue attachment", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetIssueAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to get", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Get an issue attachment", + "tags": [ + "issue" + ] + }, + "patch": { + "operationId": "issueEditIssueAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to edit", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditAttachmentOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit an issue attachment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/blocks": { + "delete": { + "operationId": "issueRemoveIssueBlocking", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueMeta" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Issue" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unblock the issue given in the body by the issue in path", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueListBlocks", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List issues that are blocked by this issue", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateIssueBlocking", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueMeta" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Issue" + }, + "404": { + "description": "the issue does not exist" + } + }, + "summary": "Block the issue given in the body by the issue in path", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/comments": { + "get": { + "operationId": "issueGetComments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "if provided, only comments updated since the specified time are returned.", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "if provided, only comments updated before the provided time are returned.", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommentList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List all comments on an issue", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIssueCommentOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Comment" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Add a comment to an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/comments/{id}": { + "delete": { + "deprecated": true, + "operationId": "issueDeleteCommentDeprecated", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "this parameter is ignored", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "id of comment to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a comment", + "tags": [ + "issue" + ] + }, + "patch": { + "deprecated": true, + "operationId": "issueEditCommentDeprecated", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "this parameter is ignored", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "id of the comment to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditIssueCommentOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Comment" + }, + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Edit a comment", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/deadline": { + "post": { + "operationId": "issueEditIssueDeadline", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to create or update a deadline on", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditDeadlineOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/IssueDeadline" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/dependencies": { + "delete": { + "operationId": "issueRemoveIssueDependencies", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueMeta" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Issue" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Remove an issue dependency", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueListIssueDependencies", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/IssueList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an issue's dependencies, i.e all issues that block this issue.", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateIssueDependencies", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueMeta" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Issue" + }, + "404": { + "description": "the issue does not exist" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Make the issue in the url depend on the issue in the form.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/labels": { + "delete": { + "operationId": "issueClearLabels", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove all labels from an issue", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetLabels", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LabelList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get an issue's labels", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueAddLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueLabelsOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/LabelList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add a label to an issue", + "tags": [ + "issue" + ] + }, + "put": { + "operationId": "issueReplaceLabels", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueLabelsOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/LabelList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Replace an issue's labels", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/labels/{id}": { + "delete": { + "operationId": "issueRemoveLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the label to remove", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Remove a label from an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/lock": { + "delete": { + "operationId": "issueUnlockIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unlock an issue", + "tags": [ + "issue" + ] + }, + "put": { + "operationId": "issueLockIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LockIssueOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Lock an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/pin": { + "delete": { + "operationId": "unpinIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of issue to unpin", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unpin an Issue", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "pinIssue", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of issue to pin", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Pin an Issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/pin/{position}": { + "patch": { + "operationId": "moveIssuePin", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "the new position", + "in": "path", + "name": "position", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Moves the Pin to the given Position", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/reactions": { + "delete": { + "operationId": "issueDeleteIssueReaction", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditReactionOption" + } + } + }, + "x-originalParamName": "content" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a reaction from an issue", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetIssueReactions", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ReactionList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a list reactions of an issue", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issuePostIssueReaction", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditReactionOption" + } + } + }, + "x-originalParamName": "content" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Reaction" + }, + "201": { + "$ref": "#/components/responses/Reaction" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add a reaction to an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/stopwatch/delete": { + "delete": { + "operationId": "issueDeleteStopWatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to stop the stopwatch on", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "description": "Not repo writer, user does not have rights to toggle stopwatch" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "description": "Cannot cancel a non-existent stopwatch" + } + }, + "summary": "Delete an issue's existing stopwatch.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/stopwatch/start": { + "post": { + "operationId": "issueStartStopWatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to create the stopwatch on", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/empty" + }, + "403": { + "description": "Not repo writer, user does not have rights to toggle stopwatch" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "description": "Cannot start a stopwatch again if it already exists" + } + }, + "summary": "Start stopwatch on an issue.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/stopwatch/stop": { + "post": { + "operationId": "issueStopStopWatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to stop the stopwatch on", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "$ref": "#/components/responses/empty" + }, + "403": { + "description": "Not repo writer, user does not have rights to toggle stopwatch" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "description": "Cannot stop a non-existent stopwatch" + } + }, + "summary": "Stop an issue's existing stopwatch.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/subscriptions": { + "get": { + "operationId": "issueSubscriptions", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get users who subscribed on an issue.", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/subscriptions/check": { + "get": { + "operationId": "issueCheckSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WatchInfo" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Check if user is subscribed to an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/subscriptions/{user}": { + "delete": { + "operationId": "issueDeleteSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "username of the user to unsubscribe from an issue", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Already unsubscribed" + }, + "201": { + "description": "Successfully Unsubscribed" + }, + "304": { + "description": "User can only subscribe itself if he is no admin" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unsubscribe user from issue", + "tags": [ + "issue" + ] + }, + "put": { + "operationId": "issueAddSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "username of the user to subscribe the issue to", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Already subscribed" + }, + "201": { + "description": "Successfully Subscribed" + }, + "304": { + "description": "User can only subscribe itself if he is no admin" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Subscribe user to issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/timeline": { + "get": { + "operationId": "issueGetCommentsAndTimeline", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "if provided, only comments updated since the specified time are returned.", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "if provided, only comments updated before the provided time are returned.", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TimelineList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List all comments and events on an issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/times": { + "delete": { + "operationId": "issueResetTime", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue to add tracked time to", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Reset a tracked time of an issue", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueTrackedTimes", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "optional filter by user (available for issue managers)", + "in": "query", + "name": "user", + "schema": { + "type": "string" + } + }, + { + "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TrackedTimeList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List an issue's tracked times", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueAddTime", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddTimeOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/TrackedTime" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add tracked time to a issue", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/issues/{index}/times/{id}": { + "delete": { + "operationId": "issueDeleteTime", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the issue", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of time to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete specific tracked time", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/keys": { + "get": { + "operationId": "repoListKeys", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the key_id to search for", + "in": "query", + "name": "key_id", + "schema": { + "type": "integer" + } + }, + { + "description": "fingerprint of the key", + "in": "query", + "name": "fingerprint", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/DeployKeyList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's keys", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateKey", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateKeyOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/DeployKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Add a key to a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/keys/{id}": { + "delete": { + "operationId": "repoDeleteKey", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the key to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a key from a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetKey", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the key to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/DeployKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a repository's key by id", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/labels": { + "get": { + "operationId": "issueListLabels", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LabelList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get all of a repository's labels", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLabelOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a label", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/labels/{id}": { + "delete": { + "operationId": "issueDeleteLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a label", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a single label", + "tags": [ + "issue" + ] + }, + "patch": { + "operationId": "issueEditLabel", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the label to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditLabelOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Label" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a label", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/languages": { + "get": { + "operationId": "repoGetLanguages", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LanguageStatistics" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get languages and number of bytes of code written", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/licenses": { + "get": { + "operationId": "repoGetLicenses", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LicensesList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get repo licenses", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/media/{filepath}": { + "get": { + "operationId": "repoGetRawFileOrLFS", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the file to get, it should be \"{ref}/{filepath}\". If there is no ref could be inferred, it will be treated as the default branch", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Returns raw file content." + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a file or it's LFS object from a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/merge-upstream": { + "post": { + "operationId": "repoMergeUpstream", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MergeUpstreamRequest" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/MergeUpstreamResponse" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Merge a branch from upstream", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/milestones": { + "get": { + "operationId": "issueGetMilestonesList", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Milestone state, Recognized values are open, closed and all. Defaults to \"open\"", + "in": "query", + "name": "state", + "schema": { + "type": "string" + } + }, + { + "description": "filter by milestone name", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MilestoneList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get all of a repository's opened milestones", + "tags": [ + "issue" + ] + }, + "post": { + "operationId": "issueCreateMilestone", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMilestoneOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Milestone" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create a milestone", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/milestones/{id}": { + "delete": { + "operationId": "issueDeleteMilestone", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the milestone to delete, identified by ID and if not available by name", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a milestone", + "tags": [ + "issue" + ] + }, + "get": { + "operationId": "issueGetMilestone", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the milestone to get, identified by ID and if not available by name", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Milestone" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a milestone", + "tags": [ + "issue" + ] + }, + "patch": { + "operationId": "issueEditMilestone", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "the milestone to edit, identified by ID and if not available by name", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditMilestoneOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Milestone" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update a milestone", + "tags": [ + "issue" + ] + } + }, + "/repos/{owner}/{repo}/mirror-sync": { + "post": { + "operationId": "repoMirrorSync", + "parameters": [ + { + "description": "owner of the repo to sync", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to sync", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Sync a mirrored repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/new_pin_allowed": { + "get": { + "operationId": "repoNewPinAllowed", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepoNewIssuePinsAllowed" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Returns if new Issue Pins are allowed", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/notifications": { + "get": { + "operationId": "notifyGetRepoList", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "If true, show notifications marked as read. Default value is false", + "in": "query", + "name": "all", + "schema": { + "type": "boolean" + } + }, + { + "description": "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread \u0026 pinned", + "in": "query", + "name": "status-types", + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "filter notifications by subject type", + "in": "query", + "name": "subject-type", + "schema": { + "items": { + "enum": [ + "issue", + "pull", + "commit", + "repository" + ], + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/NotificationThreadList" + } + }, + "summary": "List users's notification threads on a specific repo", + "tags": [ + "notification" + ] + }, + "put": { + "operationId": "notifyReadRepoList", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "If true, mark all notifications on this repo. Default value is false", + "in": "query", + "name": "all", + "schema": { + "type": "string" + } + }, + { + "description": "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread.", + "in": "query", + "name": "status-types", + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + { + "description": "Status to mark notifications as. Defaults to read.", + "in": "query", + "name": "to-status", + "schema": { + "type": "string" + } + }, + { + "description": "Describes the last point that notifications were checked. Anything updated since this time will not be updated.", + "in": "query", + "name": "last_read_at", + "schema": { + "format": "date-time", + "type": "string" + } + } + ], + "responses": { + "205": { + "$ref": "#/components/responses/NotificationThreadList" + } + }, + "summary": "Mark notification threads as read, pinned or unread on a specific repo", + "tags": [ + "notification" + ] + } + }, + "/repos/{owner}/{repo}/pulls": { + "get": { + "operationId": "repoListPullRequests", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Filter by target base branch of the pull request", + "in": "query", + "name": "base_branch", + "schema": { + "type": "string" + } + }, + { + "description": "State of pull request", + "in": "query", + "name": "state", + "schema": { + "default": "open", + "enum": [ + "open", + "closed", + "all" + ], + "type": "string" + } + }, + { + "description": "Type of sort", + "in": "query", + "name": "sort", + "schema": { + "enum": [ + "oldest", + "recentupdate", + "recentclose", + "leastupdate", + "mostcomment", + "leastcomment", + "priority" + ], + "type": "string" + } + }, + { + "description": "ID of the milestone", + "in": "query", + "name": "milestone", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "Label IDs", + "in": "query", + "name": "labels", + "schema": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + } + }, + { + "description": "Filter by pull request author", + "in": "query", + "name": "poster", + "schema": { + "type": "string" + } + }, + { + "description": "Page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "default": 1, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Page size of results", + "in": "query", + "name": "limit", + "schema": { + "minimum": 0, + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullRequestList" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "summary": "List a repo's pull requests", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreatePullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePullRequestOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PullRequest" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/comments/{id}/resolve": { + "post": { + "operationId": "repoResolvePullReviewComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the review comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/validationError" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Resolve a pull request review comment", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/comments/{id}/unresolve": { + "post": { + "operationId": "repoUnresolvePullReviewComment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the review comment", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/validationError" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unresolve a pull request review comment", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/pinned": { + "get": { + "operationId": "repoListPinnedPullRequests", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullRequestList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's pinned pull requests", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{base}/{head}": { + "get": { + "operationId": "repoGetPullRequestByBaseHead", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "base of the pull request to get", + "in": "path", + "name": "base", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "head of the pull request to get", + "in": "path", + "name": "head", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullRequest" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a pull request by base and head", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}": { + "get": { + "operationId": "repoGetPullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullRequest" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a pull request", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditPullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to edit", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditPullRequestOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PullRequest" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "412": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a pull request. If using deadline only the date will be taken into account, and time of day ignored.", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}.{diffType}": { + "get": { + "operationId": "repoDownloadPullDiffOrPatch", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "whether the output is diff or patch", + "in": "path", + "name": "diffType", + "required": true, + "schema": { + "enum": [ + "diff", + "patch" + ], + "type": "string" + } + }, + { + "description": "whether to include binary file changes. if true, the diff is applicable with `git apply`", + "in": "query", + "name": "binary", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/string" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a pull request diff or patch", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/comments/{id}/replies": { + "post": { + "operationId": "repoCreatePullReviewCommentReply", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review comment to reply to", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePullReviewCommentReplyOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PullReviewComment" + }, + "400": { + "$ref": "#/components/responses/validationError" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Reply to a pull request review comment", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/commits": { + "get": { + "operationId": "repoGetPullRequestCommits", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "include verification for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "verification", + "schema": { + "type": "boolean" + } + }, + { + "description": "include a list of affected files for every commit (disable for speedup, default 'true')", + "in": "query", + "name": "files", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommitList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get commits for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/files": { + "get": { + "operationId": "repoGetPullRequestFiles", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "skip to given file", + "in": "query", + "name": "skip-to", + "schema": { + "type": "string" + } + }, + { + "description": "whitespace behavior", + "in": "query", + "name": "whitespace", + "schema": { + "enum": [ + "ignore-all", + "ignore-change", + "ignore-eol", + "show-all" + ], + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ChangedFileList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get changed files for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/merge": { + "delete": { + "operationId": "repoCancelScheduledAutoMerge", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to merge", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Cancel the scheduled auto merge for the given pull request", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoPullRequestIsMerged", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "pull request has been merged" + }, + "404": { + "description": "pull request has not been merged" + } + }, + "summary": "Check if a pull request has been merged", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoMergePullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to merge", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MergePullRequestOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/empty" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Merge a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/requested_reviewers": { + "delete": { + "operationId": "repoDeletePullReviewRequests", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PullReviewRequestOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "cancel review requests for a pull request", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreatePullReviewRequests", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PullReviewRequestOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PullReviewList" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "create review requests for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/reviews": { + "get": { + "operationId": "repoListPullReviews", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullReviewList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List all reviews for a pull request", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreatePullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePullReviewOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PullReview" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a review to a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/reviews/{id}": { + "delete": { + "operationId": "repoDeletePullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a specific review from a pull request", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetPullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullReview" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a specific review for a pull request", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoSubmitPullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubmitPullReviewOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PullReview" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Submit a pending review to a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments": { + "get": { + "operationId": "repoGetPullReviewComments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullReviewCommentList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a specific review for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismissals": { + "post": { + "operationId": "repoDismissPullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DismissPullReviewOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PullReview" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Dismiss a review for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/undismissals": { + "post": { + "operationId": "repoUnDismissPullReview", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the review", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PullReview" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Cancel to dismiss a review for a pull request", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/pulls/{index}/update": { + "post": { + "operationId": "repoUpdatePullRequest", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "index of the pull request to get", + "in": "path", + "name": "index", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "how to update pull request", + "in": "query", + "name": "style", + "schema": { + "enum": [ + "merge", + "rebase" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Merge PR's baseBranch into headBranch", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/push_mirrors": { + "get": { + "operationId": "repoListPushMirrors", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PushMirrorList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get all push mirrors of the repository", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoAddPushMirror", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePushMirrorOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/PushMirror" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "add a push mirror to the repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/push_mirrors-sync": { + "post": { + "operationId": "repoPushMirrorSync", + "parameters": [ + { + "description": "owner of the repo to sync", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to sync", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Sync all push mirrored repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/push_mirrors/{name}": { + "delete": { + "operationId": "repoDeletePushMirror", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "remote name of the pushMirror", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "deletes a push mirror from a repository by remoteName", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetPushMirrorByRemoteName", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "remote name of push mirror", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PushMirror" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get push mirror of the repository by remoteName", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/raw/{filepath}": { + "get": { + "operationId": "repoGetRawFile", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "path of the file to get, it should be \"{ref}/{filepath}\". If there is no ref could be inferred, it will be treated as the default branch", + "in": "path", + "name": "filepath", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The name of the commit/branch/tag. Default to the repository’s default branch", + "in": "query", + "name": "ref", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "Returns raw file content." + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a file from a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases": { + "get": { + "operationId": "repoListReleases", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "filter (exclude / include) drafts, if you don't have repo write access none will show", + "in": "query", + "name": "draft", + "schema": { + "type": "boolean" + } + }, + { + "description": "filter (exclude / include) pre-releases", + "in": "query", + "name": "pre-release", + "schema": { + "type": "boolean" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ReleaseList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's releases", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateRelease", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateReleaseOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Release" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a release", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases/latest": { + "get": { + "operationId": "repoGetLatestRelease", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Release" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases/tags/{tag}": { + "delete": { + "operationId": "repoDeleteReleaseByTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "tag name of the release to delete", + "in": "path", + "name": "tag", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Delete a release by tag name", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetReleaseByTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "tag name of the release to get", + "in": "path", + "name": "tag", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Release" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a release by tag name", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases/{id}": { + "delete": { + "operationId": "repoDeleteRelease", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Delete a release", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetRelease", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Release" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a release", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditRelease", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditReleaseOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Release" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update a release", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases/{id}/assets": { + "get": { + "operationId": "repoListReleaseAttachments", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AttachmentList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List release's attachments", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateReleaseAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "name of the attachment", + "in": "query", + "name": "name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "properties": { + "attachment": { + "description": "attachment to upload", + "format": "binary", + "type": "string", + "x-formData-name": "attachment" + } + }, + "type": "object" + } + }, + "multipart/form-data": { + "schema": { + "properties": { + "attachment": { + "description": "attachment to upload", + "format": "binary", + "type": "string", + "x-formData-name": "attachment" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "413": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Create a release attachment", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}": { + "delete": { + "operationId": "repoDeleteReleaseAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to delete", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a release attachment", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetReleaseAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to get", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a release attachment", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditReleaseAttachment", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the release", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "id of the attachment to edit", + "in": "path", + "name": "attachment_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditAttachmentOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Attachment" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Edit a release attachment", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/reviewers": { + "get": { + "operationId": "repoGetReviewers", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Return all users that can be requested to review in this repo", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/signing-key.gpg": { + "get": { + "operationId": "repoSigningKey", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "GPG armored public key" + } + }, + "summary": "Get signing-key.gpg for given repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/signing-key.pub": { + "get": { + "operationId": "repoSigningKeySSH", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "ssh public key" + } + }, + "summary": "Get signing-key.pub for given repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/stargazers": { + "get": { + "operationId": "repoListStargazers", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's stargazers", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/statuses/{sha}": { + "get": { + "operationId": "repoListStatuses", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "sha of the commit", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "type of sort", + "in": "query", + "name": "sort", + "schema": { + "enum": [ + "oldest", + "recentupdate", + "leastupdate", + "leastindex", + "highestindex" + ], + "type": "string" + } + }, + { + "description": "type of state", + "in": "query", + "name": "state", + "schema": { + "enum": [ + "pending", + "success", + "error", + "failure", + "warning" + ], + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CommitStatusList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a commit's statuses", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateStatus", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "sha of the commit", + "in": "path", + "name": "sha", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStatusOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/CommitStatus" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create a commit status", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/subscribers": { + "get": { + "operationId": "repoListSubscribers", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's watchers", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/subscription": { + "delete": { + "operationId": "userCurrentDeleteSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unwatch a repo", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "userCurrentCheckSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WatchInfo" + }, + "404": { + "description": "User is not watching this repo or repo do not exist" + } + }, + "summary": "Check if the current user is watching a repo", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "userCurrentPutSubscription", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WatchInfo" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Watch a repo", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/tag_protections": { + "get": { + "operationId": "repoListTagProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TagProtectionList" + } + }, + "summary": "List tag protections for a repository", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateTagProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTagProtectionOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/TagProtection" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a tag protections for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/tag_protections/{id}": { + "delete": { + "operationId": "repoDeleteTagProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of protected tag", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a specific tag protection for the repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetTagProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of the tag protect to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TagProtection" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a specific tag protection for the repository", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditTagProtection", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "id of protected tag", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditTagProtectionOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/TagProtection" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit a tag protections for a repository. Only fields that are set will be changed", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/tags": { + "get": { + "operationId": "repoListTags", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results, default maximum page size is 50", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TagList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's tags", + "tags": [ + "repository" + ] + }, + "post": { + "operationId": "repoCreateTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTagOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Tag" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/empty" + }, + "409": { + "$ref": "#/components/responses/conflict" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a new git tag in a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/tags/{tag}": { + "delete": { + "operationId": "repoDeleteTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of tag to delete", + "in": "path", + "name": "tag", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/empty" + }, + "409": { + "$ref": "#/components/responses/conflict" + }, + "422": { + "$ref": "#/components/responses/validationError" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete a repository's tag by name", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetTag", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of tag", + "in": "path", + "name": "tag", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Tag" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the tag of a repository by tag name", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/teams": { + "get": { + "operationId": "repoListTeams", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TeamList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repository's teams", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/teams/{team}": { + "delete": { + "operationId": "repoDeleteTeam", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "team name", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Delete a team from a repository", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoCheckTeam", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "team name", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Team" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/error" + } + }, + "summary": "Check if a team is assigned to a repository", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoAddTeam", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "team name", + "in": "path", + "name": "team", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "405": { + "$ref": "#/components/responses/error" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Add a team to a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/times": { + "get": { + "operationId": "repoTrackedTimes", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "optional filter by user (available for issue managers)", + "in": "query", + "name": "user", + "schema": { + "type": "string" + } + }, + { + "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TrackedTimeList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a repo's tracked times", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/times/{user}": { + "get": { + "deprecated": true, + "operationId": "userTrackedTimes", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the user whose tracked times are to be listed", + "in": "path", + "name": "user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TrackedTimeList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a user's tracked times in a repo", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/topics": { + "get": { + "operationId": "repoListTopics", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TopicNames" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get list of topics that a repository has", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoUpdateTopics", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepoTopicOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/invalidTopicsError" + } + }, + "summary": "Replace list of topics for a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/topics/{topic}": { + "delete": { + "operationId": "repoDeleteTopic", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the topic to delete", + "in": "path", + "name": "topic", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/invalidTopicsError" + } + }, + "summary": "Delete a topic from a repository", + "tags": [ + "repository" + ] + }, + "put": { + "operationId": "repoAddTopic", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the topic to add", + "in": "path", + "name": "topic", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/invalidTopicsError" + } + }, + "summary": "Add a topic to a repository", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/transfer": { + "post": { + "operationId": "repoTransfer", + "parameters": [ + { + "description": "owner of the repo to transfer", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to transfer", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransferRepoOption" + } + } + }, + "description": "Transfer Options", + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "202": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Transfer a repo ownership", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/transfer/accept": { + "post": { + "operationId": "acceptRepoTransfer", + "parameters": [ + { + "description": "owner of the repo to transfer", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to transfer", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "202": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Accept a repo transfer", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/transfer/reject": { + "post": { + "operationId": "rejectRepoTransfer", + "parameters": [ + { + "description": "owner of the repo to transfer", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to transfer", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Reject a repo transfer", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/wiki/new": { + "post": { + "operationId": "repoCreateWikiPage", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWikiPageOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/WikiPage" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Create a wiki page", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/wiki/page/{pageName}": { + "delete": { + "operationId": "repoDeleteWikiPage", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the page", + "in": "path", + "name": "pageName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Delete a wiki page", + "tags": [ + "repository" + ] + }, + "get": { + "operationId": "repoGetWikiPage", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the page", + "in": "path", + "name": "pageName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WikiPage" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a wiki page", + "tags": [ + "repository" + ] + }, + "patch": { + "operationId": "repoEditWikiPage", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the page", + "in": "path", + "name": "pageName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWikiPageOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/WikiPage" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "423": { + "$ref": "#/components/responses/repoArchivedError" + } + }, + "summary": "Edit a wiki page", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/wiki/pages": { + "get": { + "operationId": "repoGetWikiPages", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WikiPageList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get all wiki pages", + "tags": [ + "repository" + ] + } + }, + "/repos/{owner}/{repo}/wiki/revisions/{pageName}": { + "get": { + "operationId": "repoGetWikiPageRevisions", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the page", + "in": "path", + "name": "pageName", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WikiCommitList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get revisions of a wiki page", + "tags": [ + "repository" + ] + } + }, + "/repos/{template_owner}/{template_repo}/generate": { + "post": { + "operationId": "generateRepo", + "parameters": [ + { + "description": "owner of the template repository", + "in": "path", + "name": "template_owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the template repository", + "in": "path", + "name": "template_repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "409": { + "description": "The repository with the same name already exists." + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a repository using a template", + "tags": [ + "repository" + ] + } + }, + "/repositories/{id}": { + "get": { + "operationId": "repoGetByID", + "parameters": [ + { + "description": "id of the repo to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Repository" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a repository by id", + "tags": [ + "repository" + ] + } + }, + "/settings/api": { + "get": { + "operationId": "getGeneralAPISettings", + "responses": { + "200": { + "$ref": "#/components/responses/GeneralAPISettings" + } + }, + "summary": "Get instance's global settings for api", + "tags": [ + "settings" + ] + } + }, + "/settings/attachment": { + "get": { + "operationId": "getGeneralAttachmentSettings", + "responses": { + "200": { + "$ref": "#/components/responses/GeneralAttachmentSettings" + } + }, + "summary": "Get instance's global settings for Attachment", + "tags": [ + "settings" + ] + } + }, + "/settings/repository": { + "get": { + "operationId": "getGeneralRepositorySettings", + "responses": { + "200": { + "$ref": "#/components/responses/GeneralRepoSettings" + } + }, + "summary": "Get instance's global settings for repositories", + "tags": [ + "settings" + ] + } + }, + "/settings/ui": { + "get": { + "operationId": "getGeneralUISettings", + "responses": { + "200": { + "$ref": "#/components/responses/GeneralUISettings" + } + }, + "summary": "Get instance's global settings for ui", + "tags": [ + "settings" + ] + } + }, + "/signing-key.gpg": { + "get": { + "operationId": "getSigningKey", + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "GPG armored public key" + } + }, + "summary": "Get default signing-key.gpg", + "tags": [ + "miscellaneous" + ] + } + }, + "/signing-key.pub": { + "get": { + "operationId": "getSigningKeySSH", + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "ssh public key" + } + }, + "summary": "Get default signing-key.pub", + "tags": [ + "miscellaneous" + ] + } + }, + "/teams/{id}": { + "delete": { + "operationId": "orgDeleteTeam", + "parameters": [ + { + "description": "id of the team to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "team deleted" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a team", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgGetTeam", + "parameters": [ + { + "description": "id of the team to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Team" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a team", + "tags": [ + "organization" + ] + }, + "patch": { + "operationId": "orgEditTeam", + "parameters": [ + { + "description": "id of the team to edit", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditTeamOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Team" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Edit a team", + "tags": [ + "organization" + ] + } + }, + "/teams/{id}/activities/feeds": { + "get": { + "operationId": "orgListTeamActivityFeeds", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "the date of the activities to be found", + "in": "query", + "name": "date", + "schema": { + "format": "date", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActivityFeedsList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a team's activity feeds", + "tags": [ + "organization" + ] + } + }, + "/teams/{id}/members": { + "get": { + "operationId": "orgListTeamMembers", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a team's members", + "tags": [ + "organization" + ] + } + }, + "/teams/{id}/members/{username}": { + "delete": { + "operationId": "orgRemoveTeamMember", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "username of the user to remove from a team", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a team member", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgListTeamMember", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "username of the user whose data is to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/User" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a particular member of team", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "orgAddTeamMember", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "username of the user to add to a team", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add a team member", + "tags": [ + "organization" + ] + } + }, + "/teams/{id}/repos": { + "get": { + "operationId": "orgListTeamRepos", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a team's repos", + "tags": [ + "organization" + ] + } + }, + "/teams/{id}/repos/{org}/{repo}": { + "delete": { + "description": "This does not delete the repository, it only removes the repository from the team.", + "operationId": "orgRemoveTeamRepository", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "organization that owns the repo to remove", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to remove", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a repository from a team", + "tags": [ + "organization" + ] + }, + "get": { + "operationId": "orgListTeamRepo", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "organization that owns the repo to list", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to list", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Repository" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a particular repo of team", + "tags": [ + "organization" + ] + }, + "put": { + "operationId": "orgAddTeamRepository", + "parameters": [ + { + "description": "id of the team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "organization that owns the repo to add", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to add", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Add a repository to a team", + "tags": [ + "organization" + ] + } + }, + "/topics/search": { + "get": { + "operationId": "topicSearch", + "parameters": [ + { + "description": "keywords to search", + "in": "query", + "name": "q", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TopicListResponse" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "search topics via keyword", + "tags": [ + "repository" + ] + } + }, + "/user": { + "get": { + "operationId": "userGetCurrent", + "responses": { + "200": { + "$ref": "#/components/responses/User" + } + }, + "summary": "Get the authenticated user", + "tags": [ + "user" + ] + } + }, + "/user/actions/jobs": { + "get": { + "operationId": "getUserWorkflowJobs", + "parameters": [ + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowJobsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get workflow jobs", + "tags": [ + "user" + ] + } + }, + "/user/actions/runners": { + "get": { + "operationId": "getUserRunners", + "parameters": [ + { + "description": "filter by disabled status (true or false)", + "in": "query", + "name": "disabled", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RunnerList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get user-level runners", + "tags": [ + "user" + ] + } + }, + "/user/actions/runners/registration-token": { + "post": { + "operationId": "userCreateRunnerRegistrationToken", + "responses": { + "200": { + "$ref": "#/components/responses/RegistrationToken" + } + }, + "summary": "Get a user's actions runner registration token", + "tags": [ + "user" + ] + } + }, + "/user/actions/runners/{runner_id}": { + "delete": { + "operationId": "deleteUserRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "runner has been deleted" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a user-level runner", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "getUserRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a user-level runner", + "tags": [ + "user" + ] + }, + "patch": { + "operationId": "updateUserRunner", + "parameters": [ + { + "description": "id of the runner", + "in": "path", + "name": "runner_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditActionRunnerOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Runner" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Update a user-level runner", + "tags": [ + "user" + ] + } + }, + "/user/actions/runs": { + "get": { + "operationId": "getUserWorkflowRuns", + "parameters": [ + { + "description": "workflow event name", + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "description": "workflow branch", + "in": "query", + "name": "branch", + "schema": { + "type": "string" + } + }, + { + "description": "workflow status (pending, queued, in_progress, failure, success, skipped)", + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + }, + { + "description": "triggered by user", + "in": "query", + "name": "actor", + "schema": { + "type": "string" + } + }, + { + "description": "triggering sha of the workflow run", + "in": "query", + "name": "head_sha", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/WorkflowRunsList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get workflow runs", + "tags": [ + "user" + ] + } + }, + "/user/actions/secrets/{secretname}": { + "delete": { + "operationId": "deleteUserSecret", + "parameters": [ + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "delete one secret of the user" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a secret in a user scope", + "tags": [ + "user" + ] + }, + "put": { + "operationId": "updateUserSecret", + "parameters": [ + { + "description": "name of the secret", + "in": "path", + "name": "secretname", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrUpdateSecretOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when creating a secret" + }, + "204": { + "description": "response when updating a secret" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Create or Update a secret value in a user scope", + "tags": [ + "user" + ] + } + }, + "/user/actions/variables": { + "get": { + "operationId": "getUserVariablesList", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VariableList" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get the user-level list of variables which is created by current doer", + "tags": [ + "user" + ] + } + }, + "/user/actions/variables/{variablename}": { + "delete": { + "operationId": "deleteUserVariable", + "parameters": [ + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "response when deleting a variable" + }, + "204": { + "description": "response when deleting a variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a user-level variable which is created by current doer", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "getUserVariable", + "parameters": [ + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActionVariable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a user-level variable which is created by current doer", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "createUserVariable", + "parameters": [ + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "successfully created the user-level variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "409": { + "description": "variable name already exists." + } + }, + "summary": "Create a user-level variable", + "tags": [ + "user" + ] + }, + "put": { + "operationId": "updateUserVariable", + "parameters": [ + { + "description": "name of the variable", + "in": "path", + "name": "variablename", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateVariableOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "description": "response when updating a variable" + }, + "204": { + "description": "response when updating a variable" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Update a user-level variable which is created by current doer", + "tags": [ + "user" + ] + } + }, + "/user/applications/oauth2": { + "get": { + "operationId": "userGetOauth2Application", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OAuth2ApplicationList" + } + }, + "summary": "List the authenticated user's oauth2 applications", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userCreateOAuth2Application", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOAuth2ApplicationOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/OAuth2Application" + }, + "400": { + "$ref": "#/components/responses/error" + } + }, + "summary": "creates a new OAuth2 application", + "tags": [ + "user" + ] + } + }, + "/user/applications/oauth2/{id}": { + "delete": { + "operationId": "userDeleteOAuth2Application", + "parameters": [ + { + "description": "token to be deleted", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "delete an OAuth2 Application", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userGetOAuth2Application", + "parameters": [ + { + "description": "Application ID to be found", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OAuth2Application" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "get an OAuth2 Application", + "tags": [ + "user" + ] + }, + "patch": { + "operationId": "userUpdateOAuth2Application", + "parameters": [ + { + "description": "application to be updated", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOAuth2ApplicationOptions" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/OAuth2Application" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "update an OAuth2 Application, this includes regenerating the client secret", + "tags": [ + "user" + ] + } + }, + "/user/avatar": { + "delete": { + "operationId": "userDeleteAvatar", + "responses": { + "204": { + "$ref": "#/components/responses/empty" + } + }, + "summary": "Delete Avatar", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userUpdateAvatar", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserAvatarOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + } + }, + "summary": "Update Avatar", + "tags": [ + "user" + ] + } + }, + "/user/blocks": { + "get": { + "operationId": "userListBlocks", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + } + }, + "summary": "List users blocked by the authenticated user", + "tags": [ + "user" + ] + } + }, + "/user/blocks/{username}": { + "delete": { + "operationId": "userUnblockUser", + "parameters": [ + { + "description": "username of the user to unblock", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Unblock a user", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userCheckUserBlock", + "parameters": [ + { + "description": "username of the user to check", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Check if a user is blocked by the authenticated user", + "tags": [ + "user" + ] + }, + "put": { + "operationId": "userBlockUser", + "parameters": [ + { + "description": "username of the user to block", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "optional note for the block", + "in": "query", + "name": "note", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Block a user", + "tags": [ + "user" + ] + } + }, + "/user/emails": { + "delete": { + "operationId": "userDeleteEmail", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteEmailOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete email addresses", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userListEmails", + "responses": { + "200": { + "$ref": "#/components/responses/EmailList" + } + }, + "summary": "List the authenticated user's email addresses", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userAddEmail", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmailOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/EmailList" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Add email addresses", + "tags": [ + "user" + ] + } + }, + "/user/followers": { + "get": { + "operationId": "userCurrentListFollowers", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + } + }, + "summary": "List the authenticated user's followers", + "tags": [ + "user" + ] + } + }, + "/user/following": { + "get": { + "operationId": "userCurrentListFollowing", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + } + }, + "summary": "List the users that the authenticated user is following", + "tags": [ + "user" + ] + } + }, + "/user/following/{username}": { + "delete": { + "operationId": "userCurrentDeleteFollow", + "parameters": [ + { + "description": "username of the user to unfollow", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unfollow a user", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userCurrentCheckFollowing", + "parameters": [ + { + "description": "username of the user to check for authenticated followers", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Check whether a user is followed by the authenticated user", + "tags": [ + "user" + ] + }, + "put": { + "operationId": "userCurrentPutFollow", + "parameters": [ + { + "description": "username of the user to follow", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Follow a user", + "tags": [ + "user" + ] + } + }, + "/user/gpg_key_token": { + "get": { + "operationId": "getVerificationToken", + "responses": { + "200": { + "$ref": "#/components/responses/string" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a Token to verify", + "tags": [ + "user" + ] + } + }, + "/user/gpg_key_verify": { + "post": { + "operationId": "userVerifyGPGKey", + "responses": { + "201": { + "$ref": "#/components/responses/GPGKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Verify a GPG key", + "tags": [ + "user" + ] + } + }, + "/user/gpg_keys": { + "get": { + "operationId": "userCurrentListGPGKeys", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GPGKeyList" + } + }, + "summary": "List the authenticated user's GPG keys", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userCurrentPostGPGKey", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateGPGKeyOption" + } + } + }, + "x-originalParamName": "Form" + }, + "responses": { + "201": { + "$ref": "#/components/responses/GPGKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a GPG key", + "tags": [ + "user" + ] + } + }, + "/user/gpg_keys/{id}": { + "delete": { + "operationId": "userCurrentDeleteGPGKey", + "parameters": [ + { + "description": "id of key to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Remove a GPG key", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userCurrentGetGPGKey", + "parameters": [ + { + "description": "id of key to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GPGKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a GPG key", + "tags": [ + "user" + ] + } + }, + "/user/hooks": { + "get": { + "operationId": "userListHooks", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/HookList" + } + }, + "summary": "List the authenticated user's webhooks", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userCreateHook", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateHookOption" + } + } + }, + "required": true, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Create a hook", + "tags": [ + "user" + ] + } + }, + "/user/hooks/{id}": { + "delete": { + "operationId": "userDeleteHook", + "parameters": [ + { + "description": "id of the hook to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + } + }, + "summary": "Delete a hook", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userGetHook", + "parameters": [ + { + "description": "id of the hook to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Get a hook", + "tags": [ + "user" + ] + }, + "patch": { + "operationId": "userEditHook", + "parameters": [ + { + "description": "id of the hook to update", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditHookOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/Hook" + } + }, + "summary": "Update a hook", + "tags": [ + "user" + ] + } + }, + "/user/keys": { + "get": { + "operationId": "userCurrentListKeys", + "parameters": [ + { + "description": "fingerprint of the key", + "in": "query", + "name": "fingerprint", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PublicKeyList" + } + }, + "summary": "List the authenticated user's public keys", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userCurrentPostKey", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateKeyOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/PublicKey" + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a public key", + "tags": [ + "user" + ] + } + }, + "/user/keys/{id}": { + "delete": { + "operationId": "userCurrentDeleteKey", + "parameters": [ + { + "description": "id of key to delete", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Delete a public key", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userCurrentGetKey", + "parameters": [ + { + "description": "id of key to get", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PublicKey" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a public key", + "tags": [ + "user" + ] + } + }, + "/user/orgs": { + "get": { + "operationId": "orgListCurrentUserOrgs", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OrganizationList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the current user's organizations", + "tags": [ + "organization" + ] + } + }, + "/user/repos": { + "get": { + "operationId": "userCurrentListRepos", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + } + }, + "summary": "List the repos that the authenticated user owns", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "createCurrentUserRepo", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRepoOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/Repository" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "409": { + "description": "The repository with the same name already exists." + }, + "422": { + "$ref": "#/components/responses/validationError" + } + }, + "summary": "Create a repository", + "tags": [ + "repository", + "user" + ] + } + }, + "/user/settings": { + "get": { + "operationId": "getUserSettings", + "responses": { + "200": { + "$ref": "#/components/responses/UserSettings" + } + }, + "summary": "Get user settings", + "tags": [ + "user" + ] + }, + "patch": { + "operationId": "updateUserSettings", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSettingsOptions" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "200": { + "$ref": "#/components/responses/UserSettings" + } + }, + "summary": "Update user settings", + "tags": [ + "user" + ] + } + }, + "/user/starred": { + "get": { + "operationId": "userCurrentListStarred", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "The repos that the authenticated user has starred", + "tags": [ + "user" + ] + } + }, + "/user/starred/{owner}/{repo}": { + "delete": { + "operationId": "userCurrentDeleteStar", + "parameters": [ + { + "description": "owner of the repo to unstar", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to unstar", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Unstar the given repo", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "userCurrentCheckStarring", + "parameters": [ + { + "description": "owner of the repo", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Whether the authenticated is starring the repo", + "tags": [ + "user" + ] + }, + "put": { + "operationId": "userCurrentPutStar", + "parameters": [ + { + "description": "owner of the repo to star", + "in": "path", + "name": "owner", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the repo to star", + "in": "path", + "name": "repo", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Star the given repo", + "tags": [ + "user" + ] + } + }, + "/user/stopwatches": { + "get": { + "operationId": "userGetStopWatches", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/StopWatchList" + } + }, + "summary": "Get list of all existing stopwatches", + "tags": [ + "user" + ] + } + }, + "/user/subscriptions": { + "get": { + "operationId": "userCurrentListSubscriptions", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + } + }, + "summary": "List repositories watched by the authenticated user", + "tags": [ + "user" + ] + } + }, + "/user/teams": { + "get": { + "operationId": "userListTeams", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TeamList" + } + }, + "summary": "List all the teams a user belongs to", + "tags": [ + "user" + ] + } + }, + "/user/times": { + "get": { + "operationId": "userCurrentTrackedTimes", + "parameters": [ + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "since", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format", + "in": "query", + "name": "before", + "schema": { + "format": "date-time", + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TrackedTimeList" + } + }, + "summary": "List the current user's tracked times", + "tags": [ + "user" + ] + } + }, + "/users/search": { + "get": { + "operationId": "userSearch", + "parameters": [ + { + "description": "keyword", + "in": "query", + "name": "q", + "schema": { + "type": "string" + } + }, + { + "description": "ID of the user to search for", + "in": "query", + "name": "uid", + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + } + }, + "type": "object" + } + } + }, + "description": "SearchResults of a successful search" + } + }, + "summary": "Search for users", + "tags": [ + "user" + ] + } + }, + "/users/{username}": { + "get": { + "operationId": "userGet", + "parameters": [ + { + "description": "username of the user whose data is to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/User" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a user", + "tags": [ + "user" + ] + } + }, + "/users/{username}/activities/feeds": { + "get": { + "operationId": "userListActivityFeeds", + "parameters": [ + { + "description": "username of the user whose activity feeds are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "if true, only show actions performed by the requested user", + "in": "query", + "name": "only-performed-by", + "schema": { + "type": "boolean" + } + }, + { + "description": "the date of the activities to be found", + "in": "query", + "name": "date", + "schema": { + "format": "date", + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ActivityFeedsList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a user's activity feeds", + "tags": [ + "user" + ] + } + }, + "/users/{username}/followers": { + "get": { + "operationId": "userListFollowers", + "parameters": [ + { + "description": "username of the user whose followers are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the given user's followers", + "tags": [ + "user" + ] + } + }, + "/users/{username}/following": { + "get": { + "operationId": "userListFollowing", + "parameters": [ + { + "description": "username of the user whose followed users are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the users that the given user is following", + "tags": [ + "user" + ] + } + }, + "/users/{username}/following/{target}": { + "get": { + "operationId": "userCheckFollowing", + "parameters": [ + { + "description": "username of the following user", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "username of the followed user", + "in": "path", + "name": "target", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Check if one user is following another user", + "tags": [ + "user" + ] + } + }, + "/users/{username}/gpg_keys": { + "get": { + "operationId": "userListGPGKeys", + "parameters": [ + { + "description": "username of the user whose GPG key list is to be obtained", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GPGKeyList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the given user's GPG keys", + "tags": [ + "user" + ] + } + }, + "/users/{username}/heatmap": { + "get": { + "operationId": "userGetHeatmapData", + "parameters": [ + { + "description": "username of the user whose heatmap is to be obtained", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/UserHeatmapData" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get a user's heatmap", + "tags": [ + "user" + ] + } + }, + "/users/{username}/keys": { + "get": { + "operationId": "userListKeys", + "parameters": [ + { + "description": "username of the user whose public keys are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "fingerprint of the key", + "in": "query", + "name": "fingerprint", + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PublicKeyList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the given user's public keys", + "tags": [ + "user" + ] + } + }, + "/users/{username}/orgs": { + "get": { + "operationId": "orgListUserOrgs", + "parameters": [ + { + "description": "username of the user whose organizations are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OrganizationList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List a user's organizations", + "tags": [ + "organization" + ] + } + }, + "/users/{username}/orgs/{org}/permissions": { + "get": { + "operationId": "orgGetUserPermissions", + "parameters": [ + { + "description": "username of the user whose permissions are to be obtained", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "name of the organization", + "in": "path", + "name": "org", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/OrganizationPermissions" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "Get user permissions in organization", + "tags": [ + "organization" + ] + } + }, + "/users/{username}/repos": { + "get": { + "operationId": "userListRepos", + "parameters": [ + { + "description": "username of the user whose owned repos are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the repos owned by the given user", + "tags": [ + "user" + ] + } + }, + "/users/{username}/starred": { + "get": { + "operationId": "userListStarred", + "parameters": [ + { + "description": "username of the user whose starred repos are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "The repos that the given user has starred", + "tags": [ + "user" + ] + } + }, + "/users/{username}/subscriptions": { + "get": { + "operationId": "userListSubscriptions", + "parameters": [ + { + "description": "username of the user whose watched repos are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/RepositoryList" + }, + "404": { + "$ref": "#/components/responses/notFound" + } + }, + "summary": "List the repositories watched by a user", + "tags": [ + "user" + ] + } + }, + "/users/{username}/tokens": { + "get": { + "operationId": "userGetTokens", + "parameters": [ + { + "description": "username of to user whose access tokens are to be listed", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "page number of results to return (1-based)", + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "description": "page size of results", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AccessTokenList" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "List the authenticated user's access tokens", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "userCreateToken", + "parameters": [ + { + "description": "username of the user whose token is to be created", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateAccessTokenOption" + } + } + }, + "x-originalParamName": "body" + }, + "responses": { + "201": { + "$ref": "#/components/responses/AccessToken" + }, + "400": { + "$ref": "#/components/responses/error" + }, + "403": { + "$ref": "#/components/responses/forbidden" + } + }, + "summary": "Create an access token", + "tags": [ + "user" + ] + } + }, + "/users/{username}/tokens/{token}": { + "delete": { + "operationId": "userDeleteAccessToken", + "parameters": [ + { + "description": "username of the user whose token is to be deleted", + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "token to be deleted, identified by ID and if not available by name", + "in": "path", + "name": "token", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/empty" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "422": { + "$ref": "#/components/responses/error" + } + }, + "summary": "delete an access token", + "tags": [ + "user" + ] + } + }, + "/version": { + "get": { + "operationId": "getVersion", + "responses": { + "200": { + "$ref": "#/components/responses/ServerVersion" + } + }, + "summary": "Returns the version of the Gitea application", + "tags": [ + "miscellaneous" + ] + } + } + }, + "security": [ + { + "BasicAuth": [] + }, + { + "Token": [] + }, + { + "AccessToken": [] + }, + { + "AuthorizationHeaderToken": [] + }, + { + "SudoParam": [] + }, + { + "SudoHeader": [] + }, + { + "TOTPHeader": [] + } + ], + "servers": [ + { + "url": "{{.SwaggerAppSubUrl}}/api/v1" + } + ] +} \ No newline at end of file diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index 8abfdd6489..77ce392b4b 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -96,12 +96,12 @@ func doAPIEditRepository(ctx APITestContext, editRepoOption *api.EditRepoOption, func doAPIAddCollaborator(ctx APITestContext, username string, mode perm.AccessMode) func(*testing.T) { return func(t *testing.T) { - permission := "read" + permission := api.RepoWritePermissionRead if mode == perm.AccessModeAdmin { - permission = "admin" + permission = api.RepoWritePermissionAdmin } else if mode > perm.AccessModeRead { - permission = "write" + permission = api.RepoWritePermissionWrite } addCollaboratorOption := &api.AddCollaboratorOption{ Permission: &permission, diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 92e7774aa7..fb2e1712c6 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -127,7 +127,7 @@ func testAPIOrgGeneral(t *testing.T) { apiOrgList := DecodeJSON(t, resp, []*api.Organization{}) assert.Len(t, apiOrgList, 13) assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName) - assert.Equal(t, "limited", apiOrgList[1].Visibility) + assert.Equal(t, api.UserVisibilityLimited, apiOrgList[1].Visibility) // accessing without a token will return only public orgs req = NewRequest(t, "GET", "/api/v1/orgs") @@ -136,7 +136,7 @@ func testAPIOrgGeneral(t *testing.T) { apiOrgList = DecodeJSON(t, resp, []*api.Organization{}) assert.Len(t, apiOrgList, 9) assert.Equal(t, "org 17", apiOrgList[0].FullName) - assert.Equal(t, "public", apiOrgList[0].Visibility) + assert.Equal(t, api.UserVisibilityPublic, apiOrgList[0].Visibility) }) t.Run("OrgEdit", func(t *testing.T) { @@ -148,7 +148,7 @@ func testAPIOrgGeneral(t *testing.T) { Description: new("new description"), Website: new("https://org3-new-website.example.com"), Location: new("new location"), - Visibility: new("limited"), + Visibility: new(api.UserVisibilityLimited), Email: new("org3-new-email@example.com"), } req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org3Edit).AddTokenAuth(user1Token) @@ -178,7 +178,7 @@ func testAPIOrgGeneral(t *testing.T) { t.Run("OrgEditInvalidVisibility", func(t *testing.T) { org := api.EditOrgOption{ - Visibility: new("invalid-visibility"), + Visibility: new(api.UserVisibility("invalid-visibility")), } req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token) MakeRequest(t, req, http.StatusUnprocessableEntity) diff --git a/tests/integration/api_repo_collaborator_test.go b/tests/integration/api_repo_collaborator_test.go index df1f715dc3..f5474b1ed8 100644 --- a/tests/integration/api_repo_collaborator_test.go +++ b/tests/integration/api_repo_collaborator_test.go @@ -38,7 +38,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "owner", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameOwner, repoPermission.Permission) }) t.Run("CollaboratorWithReadAccess", func(t *testing.T) { @@ -50,7 +50,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "read", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameRead, repoPermission.Permission) }) t.Run("CollaboratorWithWriteAccess", func(t *testing.T) { @@ -62,7 +62,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "write", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameWrite, repoPermission.Permission) }) t.Run("CollaboratorWithAdminAccess", func(t *testing.T) { @@ -74,7 +74,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "admin", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameAdmin, repoPermission.Permission) }) t.Run("CollaboratorNotFound", func(t *testing.T) { @@ -101,7 +101,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "read", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameRead, repoPermission.Permission) t.Run("CollaboratorCanReadOwnPermission", func(t *testing.T) { session := loginUser(t, user5.Name) @@ -112,7 +112,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoCollPerm := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "read", repoCollPerm.Permission) + assert.Equal(t, api.AccessLevelNameRead, repoCollPerm.Permission) }) }) @@ -128,7 +128,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "read", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameRead, repoPermission.Permission) }) t.Run("RepoAdminCanQueryACollaboratorsPermissions", func(t *testing.T) { @@ -144,6 +144,6 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { repoPermission := DecodeJSON(t, resp, &api.RepoCollaboratorPermission{}) - assert.Equal(t, "read", repoPermission.Permission) + assert.Equal(t, api.AccessLevelNameRead, repoPermission.Permission) }) } diff --git a/tests/integration/api_repo_teams_test.go b/tests/integration/api_repo_teams_test.go index 584416dfa3..042712165b 100644 --- a/tests/integration/api_repo_teams_test.go +++ b/tests/integration/api_repo_teams_test.go @@ -39,12 +39,12 @@ func TestAPIRepoTeams(t *testing.T) { assert.Equal(t, "Owners", teams[0].Name) assert.True(t, teams[0].CanCreateOrgRepo) assert.True(t, util.SliceSortedEqual(unit.AllUnitKeyNames(), teams[0].Units), "%v == %v", unit.AllUnitKeyNames(), teams[0].Units) - assert.Equal(t, "owner", teams[0].Permission) + assert.Equal(t, api.AccessLevelNameOwner, teams[0].Permission) assert.Equal(t, "test_team", teams[1].Name) assert.False(t, teams[1].CanCreateOrgRepo) assert.Equal(t, []string{"repo.issues"}, teams[1].Units) - assert.Equal(t, "write", teams[1].Permission) + assert.Equal(t, api.AccessLevelNameWrite, teams[1].Permission) } // IsTeam diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go index dffa6a50e1..baf41ef460 100644 --- a/tests/integration/api_team_test.go +++ b/tests/integration/api_team_test.go @@ -75,9 +75,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusCreated) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "CreateTeam1", apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, - "none", teamToCreate.Units, nil) + api.AccessLevelNameNone, teamToCreate.Units, nil) checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, - "none", teamToCreate.Units, nil) + api.AccessLevelNameNone, teamToCreate.Units, nil) teamID := apiTeam.ID // Edit team. @@ -96,9 +96,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "EditTeam1", apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, - teamToEdit.Permission, unit.AllUnitKeyNames(), nil) + api.AccessLevelName(teamToEdit.Permission), unit.AllUnitKeyNames(), nil) checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, - teamToEdit.Permission, unit.AllUnitKeyNames(), nil) + api.AccessLevelName(teamToEdit.Permission), unit.AllUnitKeyNames(), nil) // Edit team Description only editDescription = "first team" @@ -108,9 +108,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "EditTeam1_DescOnly", apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, - teamToEdit.Permission, unit.AllUnitKeyNames(), nil) + api.AccessLevelName(teamToEdit.Permission), unit.AllUnitKeyNames(), nil) checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, - teamToEdit.Permission, unit.AllUnitKeyNames(), nil) + api.AccessLevelName(teamToEdit.Permission), unit.AllUnitKeyNames(), nil) // Read team. teamRead := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) @@ -120,7 +120,7 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "ReadTeam1", apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories, - teamRead.AccessMode.ToString(), teamRead.GetUnitNames(), teamRead.GetUnitsMap()) + api.AccessLevelName(teamRead.AccessMode.ToString()), teamRead.GetUnitNames(), teamRead.GetUnitsMap()) // Delete team. req = NewRequestf(t, "DELETE", "/api/v1/teams/%d", teamID). @@ -142,9 +142,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusCreated) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "CreateTeam2", apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, - "none", nil, teamToCreate.UnitsMap) + api.AccessLevelNameNone, nil, teamToCreate.UnitsMap) checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, - "none", nil, teamToCreate.UnitsMap) + api.AccessLevelNameNone, nil, teamToCreate.UnitsMap) teamID = apiTeam.ID // Edit team. @@ -163,9 +163,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "EditTeam2", apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, - "none", nil, teamToEdit.UnitsMap) + api.AccessLevelNameNone, nil, teamToEdit.UnitsMap) checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, - "none", nil, teamToEdit.UnitsMap) + api.AccessLevelNameNone, nil, teamToEdit.UnitsMap) // Edit team Description only editDescription = "second team" @@ -175,9 +175,9 @@ func TestAPITeam(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) apiTeam = DecodeJSON(t, resp, &api.Team{}) checkTeamResponse(t, "EditTeam2_DescOnly", apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, - "none", nil, teamToEdit.UnitsMap) + api.AccessLevelNameNone, nil, teamToEdit.UnitsMap) checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, - "none", nil, teamToEdit.UnitsMap) + api.AccessLevelNameNone, nil, teamToEdit.UnitsMap) // Read team. teamRead = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) @@ -187,7 +187,7 @@ func TestAPITeam(t *testing.T) { apiTeam = DecodeJSON(t, resp, &api.Team{}) assert.NoError(t, teamRead.LoadUnits(t.Context())) checkTeamResponse(t, "ReadTeam2", apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories, - teamRead.AccessMode.ToString(), teamRead.GetUnitNames(), teamRead.GetUnitsMap()) + api.AccessLevelName(teamRead.AccessMode.ToString()), teamRead.GetUnitNames(), teamRead.GetUnitsMap()) // Delete team. req = NewRequestf(t, "DELETE", "/api/v1/teams/%d", teamID). @@ -227,7 +227,7 @@ func TestAPITeam(t *testing.T) { unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID}) } -func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) { +func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission api.AccessLevelName, units []string, unitsMap map[string]string) { t.Run(testName, func(t *testing.T) { assert.Equal(t, name, apiTeam.Name, "name") assert.Equal(t, description, apiTeam.Description, "description") @@ -244,7 +244,7 @@ func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, d }) } -func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) { +func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission api.AccessLevelName, units []string, unitsMap map[string]string) { team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: id}) assert.NoError(t, team.LoadUnits(t.Context()), "LoadUnits") apiTeam, err := convert.ToTeam(t.Context(), team) diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index 3165d34e00..4b0fbcae20 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -47,7 +47,7 @@ func TestAPIUserSearchLoggedIn(t *testing.T) { for _, user := range results.Data { assert.Contains(t, user.UserName, query) assert.NotEmpty(t, user.Email) - assert.Equal(t, "public", user.Visibility) + assert.Equal(t, api.UserVisibilityPublic, user.Visibility) } } @@ -103,7 +103,7 @@ func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) { for _, user := range results.Data { assert.Contains(t, user.UserName, query) assert.NotEmpty(t, user.Email) - assert.Equal(t, "private", user.Visibility) + assert.Equal(t, api.UserVisibilityPrivate, user.Visibility) } } diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index a124dc33c4..f1eaf89889 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -192,17 +192,21 @@ func testRenameInvalidUsername(t *testing.T) { } func testRenameReservedUsername(t *testing.T) { - reservedUsernames := []string{ - // ".", "..", ".well-known", // The names are not only reserved but also invalid + // ".", "..", ".well-known" are also reserved but invalid as form input. + reservedNames := []string{ "api", + "openapi3.v1.json", + "swagger.v1.json", + } + patternNotAllowedNames := []string{ "name.keys", } session := loginUser(t, "user2") locale := translation.NewLocale("en-US") - for _, reservedUsername := range reservedUsernames { + check := func(name, msgKey string) { req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{ - "name": reservedUsername, + "name": name, "email": "user2@example.com", "language": "en-US", }) @@ -212,12 +216,14 @@ func testRenameReservedUsername(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) actualMsg := strings.TrimSpace(htmlDoc.doc.Find(".ui.negative.message").Text()) - expectedMsg := locale.TrString("user.form.name_reserved", reservedUsername) - if strings.Contains(reservedUsername, ".") { - expectedMsg = locale.TrString("user.form.name_pattern_not_allowed", reservedUsername) - } - assert.Equal(t, expectedMsg, actualMsg) - unittest.AssertNotExistsBean(t, &user_model.User{Name: reservedUsername}) + assert.Equal(t, locale.TrString(msgKey, name), actualMsg) + unittest.AssertNotExistsBean(t, &user_model.User{Name: name}) + } + for _, name := range reservedNames { + check(name, "user.form.name_reserved") + } + for _, name := range patternNotAllowedNames { + check(name, "user.form.name_pattern_not_allowed") } } From 92628036217256f860fff2cc2fee59d2a1b4b977 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 29 Apr 2026 23:37:33 +0800 Subject: [PATCH 14/32] Fix allow maintainer edit permission check (#37479) --- models/issues/pull_list.go | 73 +++++++++++++++++-------- models/issues/pull_list_test.go | 95 ++++++++++++++++++++++++++++++--- routers/web/repo/pull.go | 4 +- 3 files changed, 141 insertions(+), 31 deletions(-) diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index bf8f9b7d91..6a6abca970 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -71,38 +71,69 @@ func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch } // CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch -func CanMaintainerWriteToBranch(ctx context.Context, p access_model.Permission, branch string, user *user_model.User) bool { - if p.CanWrite(unit.TypeCode) { - return true +func CanMaintainerWriteToBranch(ctx context.Context, headPerm access_model.Permission, headBranch string, doer *user_model.User) bool { + can, err := canMaintainerWriteToBranch(ctx, headPerm, headBranch, doer) + if err != nil { + log.Error("CanMaintainerWriteToBranch: %v", err) + return false + } + return can +} + +func canMaintainerWriteToBranch(ctx context.Context, headPerm access_model.Permission, headBranch string, doer *user_model.User) (bool, error) { + if headPerm.CanWrite(unit.TypeCode) { + return true, nil } // the code below depends on units to get the repository ID, not ideal but just keep it for now - firstUnitRepoID := p.GetFirstUnitRepoID() + firstUnitRepoID := headPerm.GetFirstUnitRepoID() if firstUnitRepoID == 0 { - return false + return false, nil } - prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, firstUnitRepoID, branch) + prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, firstUnitRepoID, headBranch) if err != nil { - return false + return false, err + } + if _, err := prs.LoadIssues(ctx); err != nil { + return false, err } - for _, pr := range prs { - if pr.AllowMaintainerEdit { - err = pr.LoadBaseRepo(ctx) - if err != nil { - continue - } - prPerm, err := access_model.GetIndividualUserRepoPermission(ctx, pr.BaseRepo, user) - if err != nil { - continue - } - if prPerm.CanWrite(unit.TypeCode) { - return true - } + if !pr.AllowMaintainerEdit { + continue + } + + // check the PR's poster's permissions + // If a "reader" poster created the PR in base repo from head repo, even if it is allowed to be edited by maintainers, + // the maintainers should not be allowed to write, because they don't really have "write" permission in the head repo + if err := pr.Issue.LoadPoster(ctx); err != nil { + return false, err + } + if err := pr.LoadHeadRepo(ctx); err != nil { + return false, err + } + posterHeadPerm, err := access_model.GetIndividualUserRepoPermission(ctx, pr.HeadRepo, pr.Issue.Poster) + if err != nil { + return false, err + } + if !posterHeadPerm.CanWrite(unit.TypeCode) { + continue + } + + // check the doer's permission + // Only allow the doer to edit the PR if they have write access to the base repository + if err := pr.LoadBaseRepo(ctx); err != nil { + return false, err + } + doerBasePerm, err := access_model.GetIndividualUserRepoPermission(ctx, pr.BaseRepo, doer) + if err != nil { + return false, err + } + if doerBasePerm.CanWrite(unit.TypeCode) { + return true, nil } } - return false + return false, nil } // HasUnmergedPullRequestsByHeadInfo checks if there are open and not merged pull request diff --git a/models/issues/pull_list_test.go b/models/issues/pull_list_test.go index 437830701c..302b2ca0ba 100644 --- a/models/issues/pull_list_test.go +++ b/models/issues/pull_list_test.go @@ -6,15 +6,28 @@ package issues_test import ( "testing" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/perm/access" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "xorm.io/builder" ) -func TestPullRequestList_LoadAttributes(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) +func TestPullRequestList(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + t.Run("LoadAttributes", testPullRequestListLoadAttributes) + t.Run("LoadReviewCommentsCounts", testPullRequestListLoadReviewCommentsCounts) + t.Run("LoadReviews", testPullRequestListLoadReviews) + t.Run("CanMaintainerWriteToBranch", testCanMaintainerWriteToBranch) +} +func testPullRequestListLoadAttributes(t *testing.T) { prs := issues_model.PullRequestList{ unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}), @@ -28,9 +41,7 @@ func TestPullRequestList_LoadAttributes(t *testing.T) { assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes(t.Context())) } -func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - +func testPullRequestListLoadReviewCommentsCounts(t *testing.T) { prs := issues_model.PullRequestList{ unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}), @@ -43,9 +54,7 @@ func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) { } } -func TestPullRequestList_LoadReviews(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - +func testPullRequestListLoadReviews(t *testing.T) { prs := issues_model.PullRequestList{ unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}), @@ -61,3 +70,73 @@ func TestPullRequestList_LoadReviews(t *testing.T) { assert.EqualValues(t, 10, reviewList[4].ID) assert.EqualValues(t, 22, reviewList[5].ID) } + +func testCanMaintainerWriteToBranch(t *testing.T) { + ctx := t.Context() + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) + headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11}) + + _ = baseRepo.LoadOwner(ctx) + _ = headRepo.LoadOwner(ctx) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // a PR from header's owner + headOwnerPR := &issues_model.PullRequest{ + Issue: &issues_model.Issue{ + RepoID: baseRepo.ID, + PosterID: headRepo.OwnerID, + }, + HeadRepoID: headRepo.ID, + BaseRepoID: baseRepo.ID, + HeadBranch: "pr-from-head-owner", + BaseBranch: "master", + } + require.NoError(t, issues_model.NewPullRequest(ctx, baseRepo, headOwnerPR.Issue, nil, nil, headOwnerPR)) + + // a PR from a user, they might have or not have "write" permission in the target repo + anyUserPR := &issues_model.PullRequest{ + Issue: &issues_model.Issue{ + RepoID: baseRepo.ID, + PosterID: user.ID, + }, + HeadRepoID: headRepo.ID, + BaseRepoID: baseRepo.ID, + HeadBranch: "pr-from-head-user", + BaseBranch: "master", + } + require.NoError(t, issues_model.NewPullRequest(ctx, baseRepo, anyUserPR.Issue, nil, nil, anyUserPR)) + + doerCanWrite := func(doer *user_model.User, pr *issues_model.PullRequest) bool { + headPerm, _ := access.GetIndividualUserRepoPermission(ctx, headRepo, doer) + return issues_model.CanMaintainerWriteToBranch(ctx, headPerm, pr.HeadBranch, doer) + } + + t.Run("NoAllowMaintainerEdit", func(t *testing.T) { + assert.True(t, doerCanWrite(headRepo.Owner, headOwnerPR)) + assert.False(t, doerCanWrite(baseRepo.Owner, headOwnerPR)) + assert.False(t, doerCanWrite(baseRepo.Owner, anyUserPR)) + assert.False(t, doerCanWrite(user, anyUserPR)) + }) + + t.Run("WithAllowMaintainerEdit-HeadPosterReader", func(t *testing.T) { + _, err := db.GetEngine(ctx).Where(builder.In("id", []int64{headOwnerPR.ID, anyUserPR.ID})). + Cols("allow_maintainer_edit"). + Update(&issues_model.PullRequest{AllowMaintainerEdit: true}) + require.NoError(t, err) + assert.True(t, doerCanWrite(baseRepo.Owner, headOwnerPR)) + assert.False(t, doerCanWrite(baseRepo.Owner, anyUserPR)) // poster doesn't have write permission, so maintainer can't write either + }) + + t.Run("WithAllowMaintainerEdit-HeadPosterWriter", func(t *testing.T) { + _, err := db.GetEngine(ctx).Where(builder.In("id", []int64{headOwnerPR.ID, anyUserPR.ID})). + Cols("allow_maintainer_edit"). + Update(&issues_model.PullRequest{AllowMaintainerEdit: true}) + require.NoError(t, err) + err = db.Insert(ctx, &repo_model.Collaboration{RepoID: headRepo.ID, UserID: user.ID, Mode: perm.AccessModeWrite}) + require.NoError(t, err) + err = db.Insert(ctx, &access.Access{RepoID: headRepo.ID, UserID: user.ID, Mode: perm.AccessModeWrite}) + require.NoError(t, err) + assert.True(t, doerCanWrite(baseRepo.Owner, headOwnerPR)) + assert.True(t, doerCanWrite(baseRepo.Owner, anyUserPR)) // now the poster has the write permission + }) +} diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 805fa6f419..4599a437d4 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -888,13 +888,13 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { if pull.HeadRepo != nil { if !pull.HasMerged && ctx.Doer != nil { - perm, err := access_model.GetDoerRepoPermission(ctx, pull.HeadRepo, ctx.Doer) + headPerm, err := access_model.GetDoerRepoPermission(ctx, pull.HeadRepo, ctx.Doer) if err != nil { ctx.ServerError("GetDoerRepoPermission", err) return } - if perm.CanWrite(unit.TypeCode) || issues_model.CanMaintainerWriteToBranch(ctx, perm, pull.HeadBranch, ctx.Doer) { + if issues_model.CanMaintainerWriteToBranch(ctx, headPerm, pull.HeadBranch, ctx.Doer) { ctx.Data["CanEditFile"] = true ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") ctx.Data["HeadRepoLink"] = pull.HeadRepo.Link() From d57d06335d85ae6525d98129961d156954087f6e Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 29 Apr 2026 18:37:38 +0200 Subject: [PATCH 15/32] Refactor integration tests infrastructure (#37462) Co-authored-by: Claude (Opus 4.7) Co-authored-by: wxiaoguang --- .dockerignore | 8 - .github/workflows/cache-seeder.yml | 2 +- .github/workflows/part-docker-dryrun.yml | 35 ++ .github/workflows/pull-db-tests.yml | 42 +-- .github/workflows/pull-docker-dryrun.yml | 47 ++- .github/workflows/pull-e2e-tests.yml | 5 +- .gitignore | 4 - CONTRIBUTING.md | 10 +- Makefile | 301 ++++-------------- models/migrations/base/db_test.go | 8 +- models/migrations/base/tests.go | 11 +- modules/setting/testenv.go | 68 +++- tests/integration/README.md | 148 ++++----- tests/integration/README_ZH.md | 91 ------ tests/integration/api_org_avatar_test.go | 6 +- tests/integration/api_repo_avatar_test.go | 6 +- .../integration/api_repo_file_create_test.go | 29 -- tests/integration/api_user_avatar_test.go | 6 +- tests/integration/gpg_ssh_git_test.go | 6 +- tests/integration/integration_test.go | 2 +- .../migration-test/migration_test.go | 11 +- tests/mssql.ini.tmpl | 2 +- tests/mysql.ini.tmpl | 2 +- tests/pgsql.ini.tmpl | 2 +- tests/sqlite.ini.tmpl | 6 +- tests/test_utils.go | 17 +- 26 files changed, 331 insertions(+), 544 deletions(-) create mode 100644 .github/workflows/part-docker-dryrun.yml delete mode 100644 tests/integration/README_ZH.md diff --git a/.dockerignore b/.dockerignore index c88fb144fe..f61d8aea3f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -40,9 +40,7 @@ cpu.out *.log /gitea -/gitea-vet /debug -/integrations.test /bin /dist @@ -54,12 +52,6 @@ cpu.out /indexers /log /tests/integration/gitea-integration-* -/tests/integration/indexers-* -/tests/e2e/gitea-e2e-* -/tests/e2e/indexers-* -/tests/e2e/reports -/tests/e2e/test-artifacts -/tests/e2e/test-snapshots /tests/*.ini /node_modules /yarn.lock diff --git a/.github/workflows/cache-seeder.yml b/.github/workflows/cache-seeder.yml index d0801a1078..358179e385 100644 --- a/.github/workflows/cache-seeder.yml +++ b/.github/workflows/cache-seeder.yml @@ -57,7 +57,7 @@ jobs: include: - { job: lint-backend, tags: "bindata sqlite sqlite_unlock_notify", target: "lint-backend" } - { job: lint-go-windows, tags: "bindata sqlite sqlite_unlock_notify", target: "lint-go-windows" } - - { job: lint-go-gogit, tags: "bindata sqlite sqlite_unlock_notify gogit", target: "lint-go" } + - { job: lint-go-gogit, tags: "bindata gogit sqlite sqlite_unlock_notify", target: "lint-go" } steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.github/workflows/part-docker-dryrun.yml b/.github/workflows/part-docker-dryrun.yml new file mode 100644 index 0000000000..e5eca3dfc1 --- /dev/null +++ b/.github/workflows/part-docker-dryrun.yml @@ -0,0 +1,35 @@ +# Reusable workflow that performs the container build steps for a single platform. +# Used by `pull-docker-dryrun.yml` to run builds in parallel per-platform. +on: + workflow_call: + inputs: + platform: + description: 'The target platform(s) to build for (e.g. linux/amd64)' + required: true + type: string + +jobs: + build-dryrun: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Build rootful image + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + platforms: ${{ inputs.platform }} + push: false + file: Dockerfile + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful + - name: Build rootless image + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + platforms: ${{ inputs.platform }} + push: false + file: Dockerfile.rootless + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index d49fc33dad..a9958a8a1f 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -58,13 +58,12 @@ jobs: env: TAGS: bindata - name: run migration tests - run: make test-pgsql-migration + run: GITEA_TEST_DATABASE=pgsql make test-migration - name: run tests - run: make test-pgsql + run: GITEA_TEST_DATABASE=pgsql make test-integration timeout-minutes: 50 env: TAGS: bindata gogit - TEST_TAGS: gogit TEST_LDAP: 1 test-sqlite: @@ -84,18 +83,21 @@ jobs: with: cache-name: sqlite - run: make deps-backend - - run: GOEXPERIMENT='' make backend + - run: make backend env: TAGS: bindata gogit sqlite sqlite_unlock_notify + GOEXPERIMENT: - name: run migration tests - run: make test-sqlite-migration + run: GITEA_TEST_DATABASE=sqlite make test-migration + env: + TAGS: bindata gogit - name: run tests - run: GOEXPERIMENT='' make test-sqlite + run: GITEA_TEST_DATABASE=sqlite make test-integration timeout-minutes: 50 env: - TAGS: bindata gogit sqlite sqlite_unlock_notify - RACE_ENABLED: true - TEST_TAGS: gogit sqlite sqlite_unlock_notify + TAGS: bindata gogit + GOEXPERIMENT: + GOTEST_FLAGS: -race -timeout=40m test-unit: if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' @@ -156,16 +158,15 @@ jobs: - name: unit-tests run: make test-backend test-check env: - TAGS: bindata - RACE_ENABLED: true - GOTESTFLAGS: -timeout=20m + TAGS: bindata sqlite sqlite_unlock_notify + GOTEST_FLAGS: -race -timeout=20m GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} - name: unit-tests-gogit - run: GOEXPERIMENT='' make test-backend test-check + run: make test-backend test-check env: - TAGS: bindata gogit - RACE_ENABLED: true - GOTESTFLAGS: -timeout=20m + TAGS: bindata gogit sqlite sqlite_unlock_notify + GOEXPERIMENT: + GOTEST_FLAGS: -race -timeout=20m GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} test-mysql: @@ -215,10 +216,9 @@ jobs: env: TAGS: bindata - name: run migration tests - run: make test-mysql-migration + run: GITEA_TEST_DATABASE=mysql make test-migration - name: run tests - # run: make integration-test-coverage (at the moment, no coverage is really handled) - run: make test-mysql + run: GITEA_TEST_DATABASE=mysql make test-integration env: TAGS: bindata TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200" @@ -258,9 +258,9 @@ jobs: - run: make backend env: TAGS: bindata - - run: make test-mssql-migration + - run: GITEA_TEST_DATABASE=mssql make test-migration - name: run tests - run: make test-mssql + run: GITEA_TEST_DATABASE=mssql make test-integration timeout-minutes: 50 env: TAGS: bindata diff --git a/.github/workflows/pull-docker-dryrun.yml b/.github/workflows/pull-docker-dryrun.yml index e0c0fff815..702506f660 100644 --- a/.github/workflows/pull-docker-dryrun.yml +++ b/.github/workflows/pull-docker-dryrun.yml @@ -7,34 +7,33 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: files-changed: uses: ./.github/workflows/files-changed.yml permissions: contents: read - container: + # dryrun build is slow, so run them in parallel per-platform + container-amd64: if: needs.files-changed.outputs.docker == 'true' - needs: files-changed - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - name: Build regular container image - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/riscv64 - push: false - cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful - - name: Build rootless container image - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - push: false - platforms: linux/amd64,linux/arm64,linux/riscv64 - file: Dockerfile.rootless - cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless + needs: [files-changed] + uses: ./.github/workflows/part-docker-dryrun.yml + with: + platform: linux/amd64 + + container-arm64: + if: needs.files-changed.outputs.docker == 'true' + needs: [files-changed] + uses: ./.github/workflows/part-docker-dryrun.yml + with: + platform: linux/arm64 + + container-riscv64: + if: needs.files-changed.outputs.docker == 'true' + needs: [files-changed] + uses: ./.github/workflows/part-docker-dryrun.yml + with: + platform: linux/riscv64 diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml index afa9587022..3b298d94e1 100644 --- a/.github/workflows/pull-e2e-tests.yml +++ b/.github/workflows/pull-e2e-tests.yml @@ -39,10 +39,13 @@ jobs: - run: make deps-frontend - run: make frontend - run: make deps-backend - - run: make gitea-e2e + - run: make backend + env: + TAGS: bindata sqlite sqlite_unlock_notify - run: make playwright - run: make test-e2e timeout-minutes: 10 env: + TAGS: bindata sqlite sqlite_unlock_notify FORCE_COLOR: 1 GITEA_TEST_E2E_DEBUG: 1 diff --git a/.gitignore b/.gitignore index 019ee94c7a..d4a677b6fa 100644 --- a/.gitignore +++ b/.gitignore @@ -55,10 +55,7 @@ cpu.out *.log.*.gz /gitea -/gitea-e2e -/gitea-vet /debug -/integrations.test /bin /dist @@ -68,7 +65,6 @@ cpu.out /indexers /log /public/assets/img/avatar -/tests/e2e-output /tests/integration/gitea-integration-* /tests/integration/indexers-* /tests/*.ini diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f0c548dcb..f1871f1470 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,11 +139,11 @@ Here's how to run the test suite: - run tests (we suggest running them on Linux) -| Command | Action | | -| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- | -|``make test[\#SpecificTestName]`` | run unit test(s) | | -|``make test-sqlite[\#SpecificTestName]`` | run [integration](tests/integration) test(s) for SQLite | [More details](tests/integration/README.md) | -|``make test-e2e`` | run [end-to-end](tests/e2e) test(s) using Playwright | | +| Command | Action | | +|:----------------------------------------------|:-----------------------------------------------------| ------------------------------------------- | +| ``make test-backend[\#SpecificTestName]`` | run unit test(s) | | +| ``make test-integration[\#SpecificTestName]`` | run [integration](tests/integration) test(s) | [More details](tests/integration/README.md) | +| ``make test-e2e`` | run [end-to-end](tests/e2e) test(s) using Playwright | | - E2E test environment variables diff --git a/Makefile b/Makefile index f85d042279..7db7313915 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,9 @@ export GOEXPERIMENT ?= jsonv2 GO ?= go SHASUM ?= shasum -a 256 -HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes) COMMA := , -XGO_VERSION := go-1.25.x +XGO_VERSION := go-1.26.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3 # renovate: datasource=go @@ -22,15 +21,28 @@ XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.11 # renovate: datasource=go -DOCKER_IMAGE ?= gitea/gitea -DOCKER_TAG ?= latest -DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG) - +HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes) ifeq ($(HAS_GO), yes) CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766 CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) endif +MAKE_EVIDENCE_DIR := .make_evidence + +# Use sqlite as default database if running tests, only do so for local tests, not in CI. +# CI should explicitly set the database to avoid unexpected results. +ifneq ($(findstring test-,$(MAKECMDGOALS)),) + ifeq ($(CI),) + GITEA_TEST_DATABASE ?= sqlite + endif +endif + +TAGS ?= +ifeq ($(GITEA_TEST_DATABASE),sqlite) + TAGS += sqlite sqlite_unlock_notify +endif +TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags + CGO_ENABLED ?= 0 ifneq (,$(findstring sqlite,$(TAGS))$(findstring pam,$(TAGS))) CGO_ENABLED = 1 @@ -49,15 +61,16 @@ else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) IS_WINDOWS := yes endif endif + +# GOFLAGS and EXTRA_GOFLAGS are for the 'go build' command only ifeq ($(IS_WINDOWS),yes) GOFLAGS := -v -buildmode=exe EXECUTABLE ?= gitea.exe - EXECUTABLE_E2E ?= gitea-e2e.exe else GOFLAGS := -v EXECUTABLE ?= gitea - EXECUTABLE_E2E ?= gitea-e2e endif +EXTRA_GOFLAGS ?= ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu) SED_INPLACE := sed -i @@ -65,15 +78,8 @@ else SED_INPLACE := sed -i '' endif -EXTRA_GOFLAGS ?= - -MAKE_EVIDENCE_DIR := .make_evidence - -GOTESTFLAGS ?= -ifeq ($(RACE_ENABLED),true) - GOFLAGS += -race - GOTESTFLAGS += -race -endif +# GOTEST_FLAGS is for unit test and integration test +GOTEST_FLAGS ?= -timeout 40m STORED_VERSION_FILE := VERSION @@ -126,12 +132,6 @@ AIR_TMP_DIR := .air GO_LICENSE_FILE := assets/go-licenses.json -TAGS ?= -TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS)) -TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags - -TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify - TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) GO_DIRS := build cmd models modules routers services tests tools @@ -170,7 +170,13 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1 # Include local Makefile # Makefile.local is listed in .gitignore -sinclude Makefile.local +ifneq ("$(wildcard Makefile.local)","") + include Makefile.local +endif + +$(foreach v, $(filter TEST_%, $(.VARIABLES)), $(eval MAKEFILE_VARS+=$v=$($v))) +$(foreach v, $(filter GITEA_TEST_%, $(.VARIABLES)), $(eval MAKEFILE_VARS+=$v=$($v))) +export MAKEFILE_VARS .PHONY: all all: build @@ -179,15 +185,8 @@ all: build help: Makefile ## print Makefile help information. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST) @printf " \033[36m%-46s\033[0m %s\n" "test-e2e" "test end to end using playwright" - @printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test" - @printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite" - -.PHONY: git-check -git-check: - @if git lfs >/dev/null 2>&1 ; then : ; else \ - echo "Gitea requires git with lfs support to run tests." ; \ - exit 1; \ - fi + @printf " \033[36m%-46s\033[0m %s\n" "test-backend[#TestSpecificName]" "run unit test (sqlite only)" + @printf " \033[36m%-46s\033[0m %s\n" "test-integration[#TestSpecificName]" "run integration test for GITEA_TEST_DATABASE (sqlite, mysql, pgsql, mssql)" .PHONY: clean-all clean-all: clean ## delete backend, frontend and integration files @@ -195,14 +194,8 @@ clean-all: clean ## delete backend, frontend and integration files .PHONY: clean clean: ## delete backend and integration files - rm -rf $(EXECUTABLE) $(EXECUTABLE_E2E) $(DIST) $(BINDATA_DEST_WILDCARD) \ - integrations*.test \ - tests/integration/gitea-integration-* \ - tests/integration/indexers-* \ - tests/sqlite.ini tests/mysql.ini tests/pgsql.ini tests/mssql.ini man/ \ - tests/e2e/gitea-e2e-*/ \ - tests/e2e/indexers-*/ \ - tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/ + rm -f $(EXECUTABLE) test-*.test tests/*.ini + rm -rf $(DIST) $(BINDATA_DEST_WILDCARD) man tests/integration/gitea-integration-* .PHONY: fmt fmt: ## format the Go and template code @@ -389,13 +382,10 @@ watch-frontend: node_modules ## start vite dev server for frontend watch-backend: ## watch backend files and continuously rebuild GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml -.PHONY: test -test: test-frontend test-backend ## test everything - .PHONY: test-backend test-backend: ## test backend files - @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) + @echo "Running go test with $(GOTEST_FLAGS) -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' $(GO_TEST_PACKAGES) .PHONY: test-frontend test-frontend: node_modules ## test frontend files @@ -413,10 +403,10 @@ test-check: exit 1; \ fi -.PHONY: test\#% -test\#%: - @echo "Running go test with -tags '$(TEST_TAGS)'..." - @$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) +.PHONY: test-backend\#% +test-backend\#%: + @echo "Running go test with -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) .PHONY: coverage coverage: @@ -426,8 +416,8 @@ coverage: .PHONY: unit-test-coverage unit-test-coverage: - @echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 + @echo "Running unit-test-coverage $(GOTEST_FLAGS) -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 .PHONY: tidy tidy: ## run go mod tidy @@ -454,83 +444,33 @@ go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses $(GO_LICENSE_FILE): go.mod go.sum GO=$(GO) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_FILE) -generate-ini-sqlite: - sed -e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-sqlite|g' \ - -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ - tests/sqlite.ini.tmpl > tests/sqlite.ini +.PHONY: test-integration +test-integration: + @# Use a compiled binary: testlogger forwards gitea logs to t.Log, so `go test -v` + @# would flood output per passing test. testcache can't help these tests anyway — + @# they mutate the work directory, so cache inputs change between runs. + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c code.gitea.io/gitea/tests/integration -o ./test-integration-$(GITEA_TEST_DATABASE).test + ./test-integration-$(GITEA_TEST_DATABASE).test -.PHONY: test-sqlite -test-sqlite: integrations.sqlite.test generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini ./integrations.sqlite.test +.PHONY: test-integration\#% +test-integration\#%: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run $(subst .,/,$*) code.gitea.io/gitea/tests/integration -.PHONY: test-sqlite\#% -test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*) +.PHONY: test-migration +test-migration: migrations.integration.test migrations.individual.test -.PHONY: test-sqlite-migration -test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test +.PHONY: migrations.integration.test +migrations.integration.test: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' code.gitea.io/gitea/tests/integration/migration-test -generate-ini-mysql: - sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \ - -e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \ - -e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \ - -e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \ - -e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-mysql|g' \ - -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ - tests/mysql.ini.tmpl > tests/mysql.ini +.PHONY: migrations.individual.test +migrations.individual.test: + @# tests of multiple packages use the same database, don't run in parallel + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) -.PHONY: test-mysql -test-mysql: integrations.mysql.test generate-ini-mysql - GITEA_TEST_CONF=tests/mysql.ini ./integrations.mysql.test - -.PHONY: test-mysql\#% -test-mysql\#%: integrations.mysql.test generate-ini-mysql - GITEA_TEST_CONF=tests/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*) - -.PHONY: test-mysql-migration -test-mysql-migration: migrations.mysql.test migrations.individual.mysql.test - -generate-ini-pgsql: - sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \ - -e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \ - -e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \ - -e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \ - -e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \ - -e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \ - -e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-pgsql|g' \ - -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ - tests/pgsql.ini.tmpl > tests/pgsql.ini - -.PHONY: test-pgsql -test-pgsql: integrations.pgsql.test generate-ini-pgsql - GITEA_TEST_CONF=tests/pgsql.ini ./integrations.pgsql.test - -.PHONY: test-pgsql\#% -test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql - GITEA_TEST_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*) - -.PHONY: test-pgsql-migration -test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test - -generate-ini-mssql: - sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \ - -e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \ - -e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \ - -e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \ - -e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-mssql|g' \ - -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ - tests/mssql.ini.tmpl > tests/mssql.ini - -.PHONY: test-mssql -test-mssql: integrations.mssql.test generate-ini-mssql - GITEA_TEST_CONF=tests/mssql.ini ./integrations.mssql.test - -.PHONY: test-mssql\#% -test-mssql\#%: integrations.mssql.test generate-ini-mssql - GITEA_TEST_CONF=tests/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*) - -.PHONY: test-mssql-migration -test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test +.PHONY: migrations.individual.test\#% +migrations.individual.test\#%: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' code.gitea.io/gitea/models/migrations/$* .PHONY: playwright playwright: deps-frontend @@ -538,109 +478,8 @@ playwright: deps-frontend @pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS) .PHONY: test-e2e -test-e2e: playwright $(EXECUTABLE_E2E) - @EXECUTABLE=$(EXECUTABLE_E2E) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS) - -.PHONY: bench-sqlite -bench-sqlite: integrations.sqlite.test generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . - -.PHONY: bench-mysql -bench-mysql: integrations.mysql.test generate-ini-mysql - GITEA_TEST_CONF=tests/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . - -.PHONY: bench-mssql -bench-mssql: integrations.mssql.test generate-ini-mssql - GITEA_TEST_CONF=tests/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . - -.PHONY: bench-pgsql -bench-pgsql: integrations.pgsql.test generate-ini-pgsql - GITEA_TEST_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . - -.PHONY: integration-test-coverage -integration-test-coverage: integrations.cover.test generate-ini-mysql - GITEA_TEST_CONF=tests/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out - -.PHONY: integration-test-coverage-sqlite -integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out - -integrations.mysql.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test - -integrations.pgsql.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test - -integrations.mssql.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mssql.test - -integrations.sqlite.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)' - -integrations.cover.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.test - -integrations.cover.sqlite.test: git-check $(GO_SOURCES) - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)' - -.PHONY: migrations.mysql.test -migrations.mysql.test: $(GO_SOURCES) generate-ini-mysql - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql.test - GITEA_TEST_CONF=tests/mysql.ini ./migrations.mysql.test - -.PHONY: migrations.pgsql.test -migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test - GITEA_TEST_CONF=tests/pgsql.ini ./migrations.pgsql.test - -.PHONY: migrations.mssql.test -migrations.mssql.test: $(GO_SOURCES) generate-ini-mssql - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mssql.test - GITEA_TEST_CONF=tests/mssql.ini ./migrations.mssql.test - -.PHONY: migrations.sqlite.test -migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite - $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)' - GITEA_TEST_CONF=tests/sqlite.ini ./migrations.sqlite.test - -.PHONY: migrations.individual.mysql.test -migrations.individual.mysql.test: $(GO_SOURCES) generate-ini-mysql - GITEA_TEST_CONF=tests/mysql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) - -.PHONY: migrations.individual.sqlite.test\#% -migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* - -.PHONY: migrations.individual.pgsql.test -migrations.individual.pgsql.test: $(GO_SOURCES) generate-ini-pgsql - GITEA_TEST_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) - -.PHONY: migrations.individual.pgsql.test\#% -migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql - GITEA_TEST_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* - -.PHONY: migrations.individual.mssql.test -migrations.individual.mssql.test: $(GO_SOURCES) generate-ini-mssql - GITEA_TEST_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) - -.PHONY: migrations.individual.mssql.test\#% -migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql - GITEA_TEST_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* - -.PHONY: migrations.individual.sqlite.test -migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) - -.PHONY: migrations.individual.sqlite.test\#% -migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* - -.PHONY: check -check: test - -.PHONY: install $(TAGS_PREREQ) -install: $(wildcard *.go) - CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' +test-e2e: playwright backend + @EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS) .PHONY: build build: frontend backend ## build everything @@ -673,9 +512,6 @@ ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),) endif CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@ -$(EXECUTABLE_E2E): $(GO_SOURCES) $(FRONTEND_DEST) - CGO_ENABLED=1 $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TEST_TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@ - .PHONY: release release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check @@ -833,11 +669,6 @@ generate-manpage: ## generate manpage @gzip -9 man/man1/gitea.1 && echo man/man1/gitea.1.gz created @#TODO A small script that formats config-cheat-sheet.en-us.md nicely for use as a config man page -.PHONY: docker -docker: - docker build --disable-content-trust=false -t $(DOCKER_REF) . -# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" . - # Disable parallel execution because it would break some targets that don't # specify exact dependencies like 'backend' which does currently not depend # on 'frontend' to enable Node.js-less builds from source tarballs. diff --git a/models/migrations/base/db_test.go b/models/migrations/base/db_test.go index 00635ca72e..dc4e502e42 100644 --- a/models/migrations/base/db_test.go +++ b/models/migrations/base/db_test.go @@ -17,11 +17,11 @@ func TestMain(m *testing.M) { func Test_DropTableColumns(t *testing.T) { x, deferable := PrepareTestEnv(t, 0) - if x == nil || t.Failed() { - defer deferable() - return - } defer deferable() + // FIXME: this logic seems wrong. Need to add an assertion here in the future, but it seems causing failure. + if x == nil || t.Failed() { + t.Skip("PrepareTestEnv did not yield a usable engine") + } type DropTest struct { ID int64 `xorm:"pk autoincr"` diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 7482829f1f..0ec979a513 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/tempdir" "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" @@ -120,6 +119,8 @@ func deleteDB() error { if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE [%s]", setting.Database.Name)); err != nil { return err } + default: + return fmt.Errorf("unsupported database type: %s", setting.Database.Type) } return nil @@ -203,14 +204,10 @@ func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table func mainTest(m *testing.M) int { testlogger.Init() - - tempWorkPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") + err := setting.PrepareIntegrationTestConfig() if err != nil { - return testlogger.MainErrorf("Unable to create temporary dir for migration test: %v", err) + return testlogger.MainErrorf("Unable to prepare integration test config: %v", err) } - defer cleanup() - - setting.MockBuiltinPaths(tempWorkPath, "", "") setting.SetupGiteaTestEnv() if err = git.InitFull(); err != nil { diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index d8663d07e2..24d489d77c 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -4,6 +4,7 @@ package setting import ( + "errors" "fmt" "os" "path/filepath" @@ -13,6 +14,8 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" + + "github.com/kballard/go-shellquote" ) var giteaTestSourceRoot *string // intentionally use a pointer to make sure the uninitialized access panics @@ -21,6 +24,16 @@ func GetGiteaTestSourceRoot() string { return *giteaTestSourceRoot } +func detectGiteaTestRoot() string { + _, filename, _, _ := runtime.Caller(0) + giteaRoot := filepath.Dir(filepath.Dir(filepath.Dir(filename))) + fixturesDir := filepath.Join(giteaRoot, "models", "fixtures") + if _, err := os.Stat(fixturesDir); err != nil { + panic("in gitea source code directory, fixtures directory not found: " + fixturesDir) + } + return giteaRoot +} + func SetupGiteaTestEnv() { if giteaTestSourceRoot != nil { return // already initialized @@ -41,12 +54,7 @@ func SetupGiteaTestEnv() { initGiteaRoot := func() string { giteaRoot := os.Getenv("GITEA_TEST_ROOT") if giteaRoot == "" { - _, filename, _, _ := runtime.Caller(0) - giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename))) - fixturesDir := filepath.Join(giteaRoot, "models", "fixtures") - if _, err := os.Stat(fixturesDir); err != nil { - panic("in gitea source code directory, fixtures directory not found: " + fixturesDir) - } + giteaRoot = detectGiteaTestRoot() } giteaTestSourceRoot = &giteaRoot return giteaRoot @@ -117,3 +125,51 @@ func SetupGiteaTestEnv() { _ = os.Setenv("GITEA_ROOT", giteaRoot) _ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts } + +func PrepareIntegrationTestConfig() error { + giteaTestRoot := detectGiteaTestRoot() + isInCI := os.Getenv("CI") != "" + testDatabase := os.Getenv("GITEA_TEST_DATABASE") + if testDatabase == "" { + if isInCI { + return errors.New("GITEA_TEST_DATABASE environment variable not set") + } + // for local development, default to sqlite. CI needs to explicitly set a database to avoid unexpected results + testDatabase = "sqlite" + _, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_DATABASE not set - defaulting to %s\n", testDatabase) + } + + _ = os.Setenv("GITEA_TEST_ROOT", giteaTestRoot) + _ = os.Setenv("GITEA_TEST_CONF", filepath.Join("tests", testDatabase+".ini")) + + workPath := filepath.Join(giteaTestRoot, "tests/integration/gitea-integration-"+testDatabase) + if err := os.MkdirAll(workPath, 0o755); err != nil { + return err + } + + confFile := filepath.Join(giteaTestRoot, "tests", testDatabase+".ini") + tmplBuf, err := os.ReadFile(confFile + ".tmpl") + if err != nil { + return err + } + tmpl := string(tmplBuf) + envVars, err := shellquote.Split(os.Getenv("MAKEFILE_VARS")) + if err != nil { + return err + } + envVarMap := map[string]string{ + "TEST_WORK_PATH": workPath, + "TEST_LOGGER": "test,file", + } + for _, env := range append(os.Environ(), envVars...) { + k, v, _ := strings.Cut(env, "=") + k = strings.TrimSpace(k) + v = strings.TrimSpace(v) + envVarMap[k] = v + } + for k, v := range envVarMap { + tmpl = strings.ReplaceAll(tmpl, fmt.Sprintf("{{%s}}", k), v) + } + err = os.WriteFile(confFile, []byte(tmpl), 0o644) + return err +} diff --git a/tests/integration/README.md b/tests/integration/README.md index 3129be5f5f..a5057e6ef7 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -1,20 +1,77 @@ # Integration tests -Integration tests can be run with make commands for the -appropriate backends, namely: -```shell -make test-sqlite -make test-pgsql -make test-mysql -make test-mssql +Integration tests can be run with command `make test-integration`. +Environment variable `GITEA_TEST_DATABASE` can be used to specify the database type for testing. + +If you encounter some errors like mismatched database version, SSH push errors, etc., +you can try to perform a clean build by: `make clean build`. + +## Run sqlite integration tests + +Start tests directly (empty `GITEA_TEST_DATABASE` defaults to sqlite): + +``` +make test-integration ``` -Make sure to perform a clean build before running tests: +## Run MySQL integration tests + +Set up a MySQL database inside docker: + ``` -make clean build +docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm --name mysql mysql:latest #(just ctrl-c to stop db and clean the container) +docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --rm --name elasticsearch elasticsearch:7.6.0 #(in a second terminal, just ctrl-c to stop db and clean the container) ``` -## Run tests via local act_runner +Start tests based on the database container: + +``` +GITEA_TEST_DATABASE=mysql TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root TEST_MYSQL_PASSWORD='' make test-integration +``` + +## Run pgsql integration tests + +Set up a pgsql database inside docker: + +``` +docker run -e "POSTGRES_DB=test" -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=postgres" -p 5432:5432 --rm --name pgsql postgres:latest #(just ctrl-c to stop db and clean the container) +``` + +Set up minio inside docker: + +``` +docker run --rm -p 9000:9000 -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 --name minio bitnamilegacy/minio:2023.8.31 +``` + +Start tests based on the database container: + +``` +GITEA_TEST_DATABASE=pgsql TEST_MINIO_ENDPOINT=localhost:9000 TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=postgres TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-integration +``` + +## Run mssql integration tests + +Set up a mssql database inside docker: + +``` +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) +``` + +Start tests based on the database container: + +``` +GITEA_TEST_DATABASE=mssql TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-integration +``` + +## Running individual tests + +Example command to run GPG test: + +``` +GITEA_TEST_DATABASE=... make test-integration#GPG +``` + +## Run Gitea Actions tests via local act_runner ### Run all jobs @@ -22,7 +79,7 @@ make clean build act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest ``` -Warning: This file defines many jobs, so it will be resource-intensive and therefor not recommended. +Warning: This file defines many jobs, so it will be resource-intensive and therefore not recommended. ### Run single job @@ -35,72 +92,3 @@ You can list all job names via: ```SHELL act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -l ``` - -## Run sqlite integration tests -Start tests -``` -make test-sqlite -``` - -## Run MySQL integration tests -Setup a MySQL database inside docker -``` -docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm --name mysql mysql:latest #(just ctrl-c to stop db and clean the container) -docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --rm --name elasticsearch elasticsearch:7.6.0 #(in a second terminal, just ctrl-c to stop db and clean the container) -``` -Start tests based on the database container -``` -TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root TEST_MYSQL_PASSWORD='' make test-mysql -``` - -## Run pgsql integration tests -Setup a pgsql database inside docker -``` -docker run -e "POSTGRES_DB=test" -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=postgres" -p 5432:5432 --rm --name pgsql postgres:latest #(just ctrl-c to stop db and clean the container) -``` -Setup minio inside docker -``` -docker run --rm -p 9000:9000 -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 --name minio bitnamilegacy/minio:2023.8.31 -``` -Start tests based on the database container -``` -TEST_MINIO_ENDPOINT=localhost:9000 TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=postgres TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-pgsql -``` - -## Run mssql integration tests -Setup a mssql database inside docker -``` -docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) -``` -Start tests based on the database container -``` -TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql -``` - -## Running individual tests - -Example command to run GPG test: - -For SQLite: - -``` -make test-sqlite#GPG -``` - -For other databases(replace `mssql` to `mysql`, or `pgsql`): - -``` -TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql#GPG -``` - -## Setting timeouts for declaring long-tests and long-flushes - -We appreciate that some testing machines may not be very powerful and -the default timeouts for declaring a slow test or a slow clean-up flush -may not be appropriate. - -You can set the following environment variables: - -```bash -GITEA_TEST_SLOW_RUN="10s" GITEA_TEST_SLOW_FLUSH="1s" make test-sqlite -``` diff --git a/tests/integration/README_ZH.md b/tests/integration/README_ZH.md deleted file mode 100644 index a458ac1e85..0000000000 --- a/tests/integration/README_ZH.md +++ /dev/null @@ -1,91 +0,0 @@ -# 关于集成测试 - -使用如下 make 命令可以运行指定的集成测试: -```shell -make test-mysql -make test-pgsql -make test-sqlite -``` - -在执行集成测试命令前请确保清理了之前的构建环境,清理命令如下: -``` -make clean build -``` - -## 如何在本地 act_runner 上运行测试 - -### 运行所有任务 - -``` -act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -``` - -警告:由于在此文件中定义了许多任务,因此此操作将花费太多的CPU和内存来运行。所以不建议这样做。 - -### 运行单个任务 - -```SHELL -act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -j -``` - -您可以通过以下方式列出所有任务名称: -```SHELL -act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -l -``` - -## 如何使用 sqlite 数据库进行集成测试 -使用该命令执行集成测试 -``` -make test-sqlite -``` - -## 如何使用 mysql 数据库进行集成测试 -首先在docker容器里部署一个 mysql 数据库 -``` -docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm --name mysql mysql:8 #(just ctrl-c to stop db and clean the container) -``` -之后便可以基于这个数据库进行集成测试 -``` -TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root TEST_MYSQL_PASSWORD='' make test-mysql -``` - -## 如何使用 pgsql 数据库进行集成测试 -同上,首先在 docker 容器里部署一个 pgsql 数据库 -``` -docker run -e "POSTGRES_DB=test" -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=postgres" -p 5432:5432 --rm --name pgsql postgres:latest #(just ctrl-c to stop db and clean the container) -``` -在docker内设置minio -``` -docker run --rm -p 9000:9000 -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 --name minio bitnamilegacy/minio:2023.8.31 -``` -之后便可以基于这个数据库进行集成测试 -``` -TEST_MINIO_ENDPOINT=localhost:9000 TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=postgres TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-pgsql -``` - -## Run mssql integration tests -同上,首先在 docker 容器里部署一个 mssql 数据库 -``` -docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) -``` -之后便可以基于这个数据库进行集成测试 -``` -TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql -``` - -## 如何进行自定义的集成测试 - -下面的示例展示了怎样在集成测试中只进行 GPG 测试: - -sqlite 数据库: - -``` -make test-sqlite#GPG -``` - -其它数据库(把 MSSQL 替换为 MYSQL, PGSQL): - -``` -TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql#GPG -``` - diff --git a/tests/integration/api_org_avatar_test.go b/tests/integration/api_org_avatar_test.go index cc1452c153..62c54e6706 100644 --- a/tests/integration/api_org_avatar_test.go +++ b/tests/integration/api_org_avatar_test.go @@ -7,9 +7,11 @@ import ( "encoding/base64" "net/http" "os" + "path/filepath" "testing" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +26,7 @@ func TestAPIUpdateOrgAvatar(t *testing.T) { token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) // Test what happens if you use a valid image - avatar, err := os.ReadFile("tests/integration/avatar.png") + avatar, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/avatar.png")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open avatar.png") @@ -48,7 +50,7 @@ func TestAPIUpdateOrgAvatar(t *testing.T) { MakeRequest(t, req, http.StatusBadRequest) // Test what happens if you use a file that is not an image - text, err := os.ReadFile("tests/integration/README.md") + text, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/README.md")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open README.md") diff --git a/tests/integration/api_repo_avatar_test.go b/tests/integration/api_repo_avatar_test.go index 6677885f7e..e4d0e06d00 100644 --- a/tests/integration/api_repo_avatar_test.go +++ b/tests/integration/api_repo_avatar_test.go @@ -8,12 +8,14 @@ import ( "fmt" "net/http" "os" + "path/filepath" "testing" auth_model "code.gitea.io/gitea/models/auth" 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/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -28,7 +30,7 @@ func TestAPIUpdateRepoAvatar(t *testing.T) { token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeWriteRepository) // Test what happens if you use a valid image - avatar, err := os.ReadFile("tests/integration/avatar.png") + avatar, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/avatar.png")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open avatar.png") @@ -52,7 +54,7 @@ func TestAPIUpdateRepoAvatar(t *testing.T) { MakeRequest(t, req, http.StatusBadRequest) // Test what happens if you use a file that is not an image - text, err := os.ReadFile("tests/integration/README.md") + text, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/README.md")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open README.md") diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index cbd1e4963e..26cc0aad0e 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -124,35 +124,6 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons return ret } -func BenchmarkAPICreateFileSmall(b *testing.B) { - onGiteaRun(b, func(b *testing.B, u *url.URL) { - user2 := unittest.AssertExistsAndLoadBean(b, &user_model.User{ID: 2}) // owner of the repo1 & repo16 - repo1 := unittest.AssertExistsAndLoadBean(b, &repo_model.Repository{ID: 1}) // public repo - - b.ResetTimer() - for n := 0; b.Loop(); n++ { - treePath := fmt.Sprintf("update/file%d.txt", n) - _, _ = createFile(user2, repo1, treePath) - } - }) -} - -func BenchmarkAPICreateFileMedium(b *testing.B) { - data := make([]byte, 10*1024*1024) - - onGiteaRun(b, func(b *testing.B, u *url.URL) { - user2 := unittest.AssertExistsAndLoadBean(b, &user_model.User{ID: 2}) // owner of the repo1 & repo16 - repo1 := unittest.AssertExistsAndLoadBean(b, &repo_model.Repository{ID: 1}) // public repo - - b.ResetTimer() - for n := 0; b.Loop(); n++ { - treePath := fmt.Sprintf("update/file%d.txt", n) - copy(data, treePath) - _, _ = createFile(user2, repo1, treePath) - } - }) -} - func TestAPICreateFile(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16 diff --git a/tests/integration/api_user_avatar_test.go b/tests/integration/api_user_avatar_test.go index 5b4546f150..8c002d7a33 100644 --- a/tests/integration/api_user_avatar_test.go +++ b/tests/integration/api_user_avatar_test.go @@ -7,9 +7,11 @@ import ( "encoding/base64" "net/http" "os" + "path/filepath" "testing" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +26,7 @@ func TestAPIUpdateUserAvatar(t *testing.T) { token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) // Test what happens if you use a valid image - avatar, err := os.ReadFile("tests/integration/avatar.png") + avatar, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/avatar.png")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open avatar.png") @@ -48,7 +50,7 @@ func TestAPIUpdateUserAvatar(t *testing.T) { MakeRequest(t, req, http.StatusBadRequest) // Test what happens if you use a file that is not an image - text, err := os.ReadFile("tests/integration/README.md") + text, err := os.ReadFile(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/README.md")) assert.NoError(t, err) if err != nil { assert.FailNow(t, "Unable to open README.md") diff --git a/tests/integration/gpg_ssh_git_test.go b/tests/integration/gpg_ssh_git_test.go index a95ff77dab..a8ec79cd9d 100644 --- a/tests/integration/gpg_ssh_git_test.go +++ b/tests/integration/gpg_ssh_git_test.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "os" + "path/filepath" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -325,10 +326,11 @@ func crudActionCreateFile(_ *testing.T, ctx APITestContext, user *user_model.Use } func importTestingKey() (*openpgp.Entity, error) { - if _, _, err := process.GetManager().Exec("gpg --import tests/integration/private-testing.key", "gpg", "--import", "tests/integration/private-testing.key"); err != nil { + keyPath := filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/private-testing.key") + if _, _, err := process.GetManager().Exec("gpg --import "+keyPath, "gpg", "--import", keyPath); err != nil { return nil, err } - keyringFile, err := os.Open("tests/integration/private-testing.key") + keyringFile, err := os.Open(keyPath) if err != nil { return nil, err } diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 60dc7e77a4..62aea184f3 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -87,7 +87,7 @@ func testMain(m *testing.M) int { graceful.InitManager(managerCtx) defer cancel() - err := tests.InitTest() + err := tests.InitIntegrationTest() if err != nil { return testlogger.MainErrorf("InitTest error: %v", err) } diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index c1acdb999b..2d6fc947a7 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -37,6 +37,7 @@ var currentEngine *xorm.Engine func initMigrationTest(t *testing.T) func() { testlogger.Init() + require.NoError(t, setting.PrepareIntegrationTestConfig()) setting.SetupGiteaTestEnv() assert.NotEmpty(t, setting.RepoRootPath) @@ -49,12 +50,12 @@ func initMigrationTest(t *testing.T) func() { } func availableVersions() ([]string, error) { - migrationsDir, err := os.Open("tests/integration/migration-test") + migrationsDir, err := os.Open(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/migration-test")) if err != nil { return nil, err } defer migrationsDir.Close() - versionRE, err := regexp.Compile("gitea-v(?P.+)\\." + regexp.QuoteMeta(setting.Database.Type.String()) + "\\.sql.gz") + versionRE, err := regexp.Compile("gitea-v(?P.+)" + regexp.QuoteMeta("."+setting.Database.Type.String()+".sql.gz")) if err != nil { return nil, err } @@ -75,7 +76,7 @@ func availableVersions() ([]string, error) { } func readSQLFromFile(version string) (string, error) { - filename := fmt.Sprintf("tests/integration/migration-test/gitea-v%s.%s.sql.gz", version, setting.Database.Type) + filename := filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/integration/migration-test", fmt.Sprintf("gitea-v%s.%s.sql.gz", version, setting.Database.Type)) if _, err := os.Stat(filename); os.IsNotExist(err) { return "", nil @@ -97,7 +98,7 @@ func readSQLFromFile(version string) (string, error) { if err != nil { return "", err } - return string(bytes.TrimPrefix(buf, []byte{'\xef', '\xbb', '\xbf'})), nil + return string(bytes.TrimPrefix(buf, []byte("\xef\xbb\xbf"))), nil } func restoreOldDB(t *testing.T, version string) { @@ -230,6 +231,8 @@ func restoreOldDB(t *testing.T, version string) { assert.NoError(t, err, "Failure whilst running: %s\nError: %v", statement, err) } db.Close() + default: + assert.Failf(t, "unsupported database type", "setting.Database.Type=%v", setting.Database.Type) } } diff --git a/tests/mssql.ini.tmpl b/tests/mssql.ini.tmpl index 6f031691f0..aba67157df 100644 --- a/tests/mssql.ini.tmpl +++ b/tests/mssql.ini.tmpl @@ -1,4 +1,4 @@ -WORK_PATH = {{WORK_PATH}} +WORK_PATH = {{TEST_WORK_PATH}} APP_NAME = Gitea: Git with a cup of tea RUN_MODE = prod diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index 02069b403e..4572cfd724 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -1,4 +1,4 @@ -WORK_PATH = {{WORK_PATH}} +WORK_PATH = {{TEST_WORK_PATH}} APP_NAME = Gitea: Git with a cup of tea RUN_MODE = prod diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 50a649fc79..5e15ca7ba6 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -1,4 +1,4 @@ -WORK_PATH = {{WORK_PATH}} +WORK_PATH = {{TEST_WORK_PATH}} APP_NAME = Gitea: Git with a cup of tea RUN_MODE = prod diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index b43b658bb0..03393c620b 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -1,4 +1,4 @@ -WORK_PATH = {{WORK_PATH}} +WORK_PATH = {{TEST_WORK_PATH}} APP_NAME = Gitea: Git with a cup of tea RUN_MODE = prod @@ -88,7 +88,7 @@ ENABLED = true [markup.html] ENABLED = true FILE_EXTENSIONS = .html -RENDER_COMMAND = go run tools/test-echo.go +RENDER_COMMAND = go run {{GITEA_TEST_ROOT}}/tools/test-echo.go ;RENDER_COMMAND = cat ;IS_INPUT_FILE = true RENDER_CONTENT_MODE = sanitized @@ -96,7 +96,7 @@ RENDER_CONTENT_MODE = sanitized [markup.no-sanitizer] ENABLED = true FILE_EXTENSIONS = .no-sanitizer -RENDER_COMMAND = go run tools/test-echo.go +RENDER_COMMAND = go run {{GITEA_TEST_ROOT}}/tools/test-echo.go ; This test case is reused, at first it is used to test "no-sanitizer" (sandbox doesn't take effect here) ; Then it will be updated and used to test "iframe + sandbox-disabled" RENDER_CONTENT_MODE = no-sanitizer diff --git a/tests/test_utils.go b/tests/test_utils.go index d5a8008cef..f16754b056 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -6,7 +6,6 @@ package tests import ( "database/sql" "fmt" - "os" "path/filepath" "strings" "testing" @@ -25,17 +24,14 @@ import ( "github.com/stretchr/testify/assert" ) -func InitTest() error { +func InitIntegrationTest() error { testlogger.Init() - if os.Getenv("GITEA_TEST_CONF") == "" { - // By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger. - // It's easier for developers to debug bugs step by step with a debugger. - // Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes. - giteaConf := "tests/sqlite.ini" - _ = os.Setenv("GITEA_TEST_CONF", giteaConf) - _, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_CONF not set - defaulting to %s\n", giteaConf) + err := setting.PrepareIntegrationTestConfig() + if err != nil { + return err } + setting.SetupGiteaTestEnv() setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" @@ -132,6 +128,9 @@ func InitTest() error { return err } } + case setting.Database.Type.IsSQLite3(): + default: + return fmt.Errorf("unsupported database type: %s", setting.Database.Type) } routers.InitWebInstalled(graceful.GetManager().HammerContext()) From 184ce17167a77747576ec4c6577536579892915e Mon Sep 17 00:00:00 2001 From: Rayan Salhab Date: Wed, 29 Apr 2026 20:03:26 +0300 Subject: [PATCH 16/32] Fix review submission from single-commit PR view (#37475) Fixes #37415. Pin the review submission form action to the canonical PR files route --------- Co-authored-by: cyphercodes Co-authored-by: Hermes Agent (OpenAI GPT-5.5) Co-authored-by: wxiaoguang --- templates/repo/diff/new_review.tmpl | 2 +- tests/integration/pull_diff_test.go | 43 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl index 98bc255d8e..10acc886ce 100644 --- a/templates/repo/diff/new_review.tmpl +++ b/templates/repo/diff/new_review.tmpl @@ -11,7 +11,7 @@ {{if $.IsShowingAllCommits}}
-
+
{{ctx.Locale.Tr "repo.diff.review.header"}}
diff --git a/tests/integration/pull_diff_test.go b/tests/integration/pull_diff_test.go index 0b286fd2b2..134ae2c0ed 100644 --- a/tests/integration/pull_diff_test.go +++ b/tests/integration/pull_diff_test.go @@ -13,21 +13,34 @@ import ( "github.com/stretchr/testify/assert" ) -func TestPullDiff_CompletePRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files", false, []string{"test1.txt", "test10.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt", "test6.txt", "test7.txt", "test8.txt", "test9.txt"}) -} - -func TestPullDiff_SingleCommitPRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/commits/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", true, []string{"test3.txt"}) -} - -func TestPullDiff_CommitRangePRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"}) -} - -func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) { +func TestPullDiff(t *testing.T) { defer tests.PrepareTestEnv(t)() + t.Run("CompletePRDiff", func(t *testing.T) { + testPullDiffAssertPage(t, "/user2/commitsonpr/pulls/1/files", false, []string{"test1.txt", "test10.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt", "test6.txt", "test7.txt", "test8.txt", "test9.txt"}) + }) + t.Run("SingleCommitPRDiff", func(t *testing.T) { + testPullDiffAssertPage(t, "/user2/commitsonpr/pulls/1/commits/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", true, []string{"test3.txt"}) + }) + t.Run("CommitRangePRDiff", func(t *testing.T) { + testPullDiffAssertPage(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"}) + }) + t.Run("SingleHeadCommitReviewFormAction", testPullDiffSingleHeadCommitReviewFormAction) +} +func testPullDiffSingleHeadCommitReviewFormAction(t *testing.T) { + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/commitsonpr/pulls/1/commits/1978192d98bb1b65e11c2cf37da854fbf94bffd6") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + btn := doc.Find(".js-btn-review") + assert.True(t, btn.Length() == 1 && !btn.HasClass("disabled")) + form := doc.Find(".review-box-panel form") + assert.Equal(t, 1, form.Length()) + assert.Equal(t, "/user2/commitsonpr/pulls/1/files/reviews/submit", form.AttrOr("action", "")) +} + +func testPullDiffAssertPage(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) { session := loginUser(t, "user2") req := NewRequest(t, "GET", "/user2/commitsonpr/pulls") @@ -39,7 +52,7 @@ func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expect doc := NewHTMLParser(t, resp.Body) // Assert all files are visible. - fileContents := doc.doc.Find(".file-content") + fileContents := doc.Find(".file-content") numberOfFiles := fileContents.Length() assert.Equal(t, len(expectedFilenames), numberOfFiles) @@ -50,5 +63,5 @@ func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expect }) // Ensure the review button is enabled for full PR reviews - assert.Equal(t, reviewBtnDisabled, doc.doc.Find(".js-btn-review").HasClass("disabled")) + assert.Equal(t, reviewBtnDisabled, doc.Find(".js-btn-review").HasClass("disabled")) } From 2b2ec6af85be931d99b8783e1b2db50c0f511cb4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 30 Apr 2026 02:32:46 +0800 Subject: [PATCH 17/32] Refactor compare diff/pull page (1) (#37481) 1. Rename CompareInfo.MergeBase to CompareBase, it is not merge base 2. Remove unused template variables `ctx.Data["Username"]` and `ctx.Data["Reponame"]` 3. Decouple some template variable accesses, use typed struct --------- Co-authored-by: Nicolas --- routers/api/v1/repo/pull.go | 4 +- routers/web/repo/commit.go | 13 +--- routers/web/repo/compare.go | 127 +++++++++++++++----------------- routers/web/repo/middlewares.go | 17 +++++ routers/web/repo/pull.go | 29 +++----- routers/web/repo/pull_review.go | 2 +- routers/web/repo/wiki.go | 3 - services/git/compare.go | 34 +++++---- templates/repo/diff/box.tmpl | 2 +- 9 files changed, 115 insertions(+), 116 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index aeecc13f4e..2426a6b3c2 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -521,7 +521,7 @@ func CreatePullRequest(ctx *context.APIContext) { BaseBranch: compareResult.BaseRef.ShortName(), HeadRepo: compareResult.HeadRepo, BaseRepo: repo, - MergeBase: compareResult.MergeBase, + MergeBase: compareResult.CompareBase, Type: issues_model.PullRequestGitea, } @@ -1569,7 +1569,7 @@ func GetPullRequestFiles(ctx *context.APIContext) { return } - startCommitID := compareInfo.MergeBase + startCommitID := compareInfo.CompareBase endCommitID := headCommitID maxLines := setting.Git.MaxGitDiffLines diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index c118f41381..84b51bba5f 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -96,8 +96,6 @@ func Commits(ctx *context.Context) { } else { ctx.Data["CommitsTagsMap"] = commitsTagsMap } - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount pager := context.NewPagination(commitsCount, pageSize, page, 5) @@ -163,9 +161,6 @@ func Graph(ctx *context.Context) { ctx.Data["AllRefs"] = gitRefs - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name - divOnly := ctx.FormBool("div-only") queryParams := ctx.Req.URL.Query() queryParams.Del("div-only") @@ -209,8 +204,6 @@ func SearchCommits(ctx *context.Context) { if all { ctx.Data["All"] = true } - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.HTML(http.StatusOK, tplCommits) } @@ -248,8 +241,6 @@ func FileHistory(ctx *context.Context) { return } - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["FileTreePath"] = ctx.Repo.TreePath ctx.Data["CommitCount"] = commitsCount @@ -321,7 +312,7 @@ func Diff(ctx *context.Context) { MaxLines: maxLines, MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, MaxFiles: maxFiles, - WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), + WhitespaceBehavior: gitdiff.GetWhitespaceFlag(GetWhitespaceBehavior(ctx)), }, files...) if err != nil { ctx.NotFound(err) @@ -346,8 +337,6 @@ func Diff(ctx *context.Context) { ctx.Data["CommitID"] = commitID ctx.Data["AfterCommitID"] = commitID - ctx.Data["Username"] = userName - ctx.Data["Reponame"] = repoName var parentCommit *git.Commit var parentCommitID string diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 174cb1be22..83451843ce 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -190,8 +190,18 @@ func setCsvCompareContext(ctx *context.Context) { } } +type comparePageInfoType struct { + compareInfo *git_service.CompareInfo + nothingToCompare bool + allowCreatePull bool +} + +func newComparePageInfo() *comparePageInfoType { + return &comparePageInfoType{} +} + // parseCompareInfo parse compare info between two commit for preparing comparing references -func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { +func (cpi *comparePageInfoType) parseCompareInfo(ctx *context.Context) error { baseRepo := ctx.Repo.Repository fileOnly := ctx.FormBool("file-only") @@ -200,13 +210,13 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { // remove the check when we support compare with carets if compareReq.BaseOriRefSuffix != "" { - return nil, util.NewInvalidArgumentErrorf("unsupported comparison syntax: ref with suffix") + return util.NewInvalidArgumentErrorf("unsupported comparison syntax: ref with suffix") } // 2 get repository and owner for head headOwner, headRepo, err := common.GetHeadOwnerAndRepo(ctx, baseRepo, compareReq) if err != nil { - return nil, err + return err } // 3 permission check @@ -219,10 +229,10 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { // Assert ctx.Doer has permission to read headRepo's codes permHead, err := access_model.GetDoerRepoPermission(ctx, headRepo, ctx.Doer) if err != nil { - return nil, err + return err } if !permHead.CanRead(unit.TypeCode) { - return nil, util.NewNotExistErrorf("") // permission: no error message for end users + return util.NewNotExistErrorf("") // permission: no error message for end users } ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode) } @@ -233,17 +243,16 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName) if baseRef == "" { - return nil, util.NewNotExistErrorf("no base ref: %s", baseRefName) + return util.NewNotExistErrorf("no base ref: %s", baseRefName) } headGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, headRepo) if err != nil { - ctx.ServerError("OpenRepository", err) - return nil, err + return err } headRef := headGitRepo.UnstableGuessRefByShortName(headRefName) if headRef == "" { - return nil, util.NewNotExistErrorf("no head ref: %s", headRefName) + return util.NewNotExistErrorf("no head ref: %s", headRefName) } ctx.Data["BaseName"] = baseRepo.OwnerName @@ -268,7 +277,7 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { if baseRepo.IsFork { err = baseRepo.GetBaseRepo(ctx) if err != nil && !repo_model.IsErrRepoNotExist(err) { - return nil, err + return err } else if err == nil { rootRepo = baseRepo.BaseRepo } @@ -302,7 +311,7 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) if err != nil { - return nil, err + return err } ctx.Data["RootRepoBranches"] = branches ctx.Data["RootRepoTags"] = tags @@ -325,7 +334,7 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, ownForkRepo) if err != nil { - return nil, err + return err } ctx.Data["OwnForkRepoBranches"] = branches ctx.Data["OwnForkRepoTags"] = tags @@ -335,19 +344,14 @@ func parseCompareInfo(ctx *context.Context) (*git_service.CompareInfo, error) { compareInfo, err := git_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef, headRef, compareReq.DirectComparison(), fileOnly) if err != nil { - return nil, err + return err } // Treat as pull request if both references are branches - allowCreatePullRequest := baseRef.IsBranch() && headRef.IsBranch() && permBase.CanReadIssuesOrPulls(true) - allowCreatePullRequest = allowCreatePullRequest && compareInfo.MergeBase != "" - ctx.Data["PageIsComparePull"] = allowCreatePullRequest - if compareReq.DirectComparison() { - ctx.Data["BeforeCommitID"] = compareInfo.BaseCommitID - } else { - ctx.Data["BeforeCommitID"] = compareInfo.MergeBase - } - return &compareInfo, nil + cpi.allowCreatePull = baseRef.IsBranch() && headRef.IsBranch() && permBase.CanReadIssuesOrPulls(true) + cpi.allowCreatePull = cpi.allowCreatePull && compareInfo.CompareBase != "" + cpi.compareInfo = &compareInfo + return nil } // autoTitleFromBranchName humanizes a branch name into a PR title. @@ -414,14 +418,17 @@ func prepareNewPullRequestTitleContent(ci *git_service.CompareInfo, commits []*g } // prepareCompareDiff renders compare diff page. TODO: need to refactor it and other "compare diff" related functions together -func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, whitespaceBehavior gitcmd.TrustedCmdArgs) (nothingToCompare bool) { - if ci.MergeBase == "" { - return true +func (cpi *comparePageInfoType) prepareCompareDiff(ctx *context.Context, whitespaceBehavior gitcmd.TrustedCmdArgs) { + ci := cpi.compareInfo + if ci.CompareBase == "" { + cpi.nothingToCompare = true + return } repo := ctx.Repo.Repository headCommitID := ci.HeadCommitID ctx.Data["CommitRepoLink"] = ci.HeadRepo.Link() + ctx.Data["BeforeCommitID"] = ci.CompareBase ctx.Data["AfterCommitID"] = headCommitID // follow GitHub's behavior: autofill the form and expand @@ -431,26 +438,18 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white ctx.Data["TitleQuery"] = newPrFormTitle ctx.Data["BodyQuery"] = newPrFormBody - if (headCommitID == ci.MergeBase && !ci.DirectComparison()) || - headCommitID == ci.BaseCommitID { - ctx.Data["IsNothingToCompare"] = true - if unit, err := repo.GetUnit(ctx, unit.TypePullRequests); err == nil { - config := unit.PullRequestsConfig() + if headCommitID == ci.CompareBase { + config := repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + // if auto-detect manual merge, an empty PR will be closed immediately because it is already on base branch + supportEmptyPr := !config.AutodetectManualMerge + acrossRepoPr := !ci.IsSameRef() + ctx.Data["AllowEmptyPr"] = supportEmptyPr && acrossRepoPr - if !config.AutodetectManualMerge { - ctx.Data["AllowEmptyPr"] = !ci.IsSameRef() - return ci.IsSameRef() - } - - ctx.Data["AllowEmptyPr"] = false - } - return true + cpi.nothingToCompare = true + return } - beforeCommitID := ci.MergeBase - if ci.DirectComparison() { - beforeCommitID = ci.BaseCommitID - } + beforeCommitID := ci.CompareBase maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles files := ctx.FormStrings("files") @@ -473,12 +472,12 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white }, ctx.FormStrings("files")...) if err != nil { ctx.ServerError("GetDiff", err) - return false + return } diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ci.HeadRepo, ci.HeadGitRepo, beforeCommitID, headCommitID) if err != nil { ctx.ServerError("GetDiffShortStat", err) - return false + return } ctx.Data["DiffShortStat"] = diffShortStat ctx.Data["Diff"] = diff @@ -493,7 +492,7 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white diffTree, err := gitdiff.GetDiffTree(ctx, ci.HeadGitRepo, false, beforeCommitID, headCommitID) if err != nil { ctx.ServerError("GetDiffTree", err) - return false + return } renderedIconPool := fileicon.NewRenderedIconPool() @@ -506,7 +505,7 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID) if err != nil { ctx.ServerError("GetCommit", err) - return false + return } baseGitRepo := ctx.Repo.GitRepo @@ -514,24 +513,20 @@ func prepareCompareDiff(ctx *context.Context, ci *git_service.CompareInfo, white beforeCommit, err := baseGitRepo.GetCommit(beforeCommitID) if err != nil { ctx.ServerError("GetCommit", err) - return false + return } commits, err := processGitCommits(ctx, ci.Commits) if err != nil { ctx.ServerError("processGitCommits", err) - return false + return } ctx.Data["Commits"] = commits ctx.Data["CommitCount"] = len(commits) ctx.Data["title"], ctx.Data["content"] = prepareNewPullRequestTitleContent(ci, commits, setting.Repository.PullRequest.DefaultTitleSource) - ctx.Data["Username"] = ci.HeadRepo.OwnerName - ctx.Data["Reponame"] = ci.HeadRepo.Name setCompareContext(ctx, beforeCommit, headCommit, ci.HeadRepo.OwnerName, repo.Name) - - return false } func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repository) (branches, tags []string, err error) { @@ -552,10 +547,8 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor // CompareDiff show different from one commit to another commit func CompareDiff(ctx *context.Context) { - ci, err := parseCompareInfo(ctx) - if ctx.Written() { - return - } + comparePageInfo := newComparePageInfo() + err := comparePageInfo.parseCompareInfo(ctx) if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) { ctx.NotFound(nil) return @@ -563,13 +556,13 @@ func CompareDiff(ctx *context.Context) { ctx.ServerError("ParseCompareInfo", err) return } - + ci := comparePageInfo.compareInfo ctx.Data["PageIsViewCode"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["CompareInfo"] = ci // TODO: need to refactor "prepare compare" related functions together - nothingToCompare := prepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string))) + comparePageInfo.prepareCompareDiff(ctx, gitdiff.GetWhitespaceFlag(GetWhitespaceBehavior(ctx))) if ctx.Written() { return } @@ -601,21 +594,23 @@ func CompareDiff(ctx *context.Context) { return } - if ci.MergeBase != "" { - prepareCreatePullRequestPage(ctx, ci, nothingToCompare) + if ci.CompareBase != "" { + comparePageInfo.prepareCreatePullRequestPage(ctx) if ctx.Written() { return } } else { ctx.Flash.Error(ctx.Tr("repo.pulls.no_common_history"), true) - ctx.Data["PageIsComparePull"] = false ctx.Data["CommitCount"] = 0 } + ctx.Data["PageIsComparePull"] = comparePageInfo.allowCreatePull + ctx.Data["IsNothingToCompare"] = comparePageInfo.nothingToCompare ctx.HTML(http.StatusOK, tplCompare) } -func prepareCreatePullRequestPage(ctx *context.Context, ci *git_service.CompareInfo, nothingToCompare bool) { - if ctx.Data["PageIsComparePull"] == true { +func (cpi *comparePageInfoType) prepareCreatePullRequestPage(ctx *context.Context) { + ci := cpi.compareInfo + if cpi.allowCreatePull { pr, err := issues_model.GetUnmergedPullRequest(ctx, ci.HeadRepo.ID, ctx.Repo.Repository.ID, ci.HeadRef.ShortName(), ci.BaseRef.ShortName(), issues_model.PullRequestFlowGithub) if err != nil { if !issues_model.IsErrPullRequestNotExist(err) { @@ -633,7 +628,7 @@ func prepareCreatePullRequestPage(ctx *context.Context, ci *git_service.CompareI return } - if !nothingToCompare { + if !cpi.nothingToCompare { // Setup information for new form. pageMetaData := retrieveRepoIssueMetaData(ctx, ctx.Repo.Repository, nil, true) if ctx.Written() { @@ -645,8 +640,8 @@ func prepareCreatePullRequestPage(ctx *context.Context, ci *git_service.CompareI } } } - beforeCommitID := ctx.Data["BeforeCommitID"].(string) - afterCommitID := ctx.Data["AfterCommitID"].(string) + beforeCommitID := cpi.compareInfo.CompareBase + afterCommitID := cpi.compareInfo.HeadCommitID ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + ci.CompareSeparator + base.ShortSha(afterCommitID) diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go index c7c9da498b..ee525a0db7 100644 --- a/routers/web/repo/middlewares.go +++ b/routers/web/repo/middlewares.go @@ -9,6 +9,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/gitdiff" @@ -80,6 +81,14 @@ func SetWhitespaceBehavior(ctx *context.Context) { } } +func GetWhitespaceBehavior(ctx *context.Context) string { + behavior, ok := ctx.Data["WhitespaceBehavior"].(string) + if !ok { + setting.PanicInDevOrTesting("WhitespaceBehavior is not set in context data or is not a string") + } + return behavior +} + // SetShowOutdatedComments set the show outdated comments option as context variable func SetShowOutdatedComments(ctx *context.Context) { showOutdatedCommentsValue := ctx.FormString("show-outdated") @@ -95,3 +104,11 @@ func SetShowOutdatedComments(ctx *context.Context) { } ctx.Data["ShowOutdatedComments"], _ = strconv.ParseBool(showOutdatedCommentsValue) } + +func GetShowOutdatedComments(ctx *context.Context) bool { + show, ok := ctx.Data["ShowOutdatedComments"].(bool) + if !ok { + setting.PanicInDevOrTesting("ShowOutdatedComments is not set in context data or is not a bool") + } + return show +} diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4599a437d4..4e30c385b9 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -544,7 +544,7 @@ func (prInfo *pullRequestViewInfo) prepareViewOpenPullInfo(ctx *context.Context) ctx.Data["PullHeadCommitID"] = prInfo.CompareInfo.HeadCommitID - if prInfo.CompareInfo.HeadCommitID == prInfo.CompareInfo.MergeBase { + if prInfo.CompareInfo.HeadCommitID == prInfo.CompareInfo.CompareBase { ctx.Data["IsNothingToCompare"] = true } @@ -622,9 +622,6 @@ func ViewPullCommits(ctx *context.Context) { return } - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name - commits, err := processGitCommits(ctx, prCompareInfo.Commits) if err != nil { ctx.ServerError("processGitCommits", err) @@ -680,7 +677,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { headCommitID := prCompareInfo.HeadCommitID isSingleCommit := beforeCommitID == "" && afterCommitID != "" ctx.Data["IsShowingOnlySingleCommit"] = isSingleCommit - isShowAllCommits := (beforeCommitID == "" || beforeCommitID == prCompareInfo.MergeBase) && (afterCommitID == "" || afterCommitID == headCommitID) + isShowAllCommits := (beforeCommitID == "" || beforeCommitID == prCompareInfo.CompareBase) && (afterCommitID == "" || afterCommitID == headCommitID) ctx.Data["IsShowingAllCommits"] = isShowAllCommits if afterCommitID == "" || afterCommitID == headCommitID { @@ -695,8 +692,8 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { var beforeCommit *git.Commit var err error if !isSingleCommit { - if beforeCommitID == "" || beforeCommitID == prCompareInfo.MergeBase { - beforeCommitID = prCompareInfo.MergeBase + if beforeCommitID == "" || beforeCommitID == prCompareInfo.CompareBase { + beforeCommitID = prCompareInfo.CompareBase // merge base commit is not in the list of the pull request commits beforeCommit, err = gitRepo.GetCommit(beforeCommitID) if err != nil { @@ -719,7 +716,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { beforeCommitID = beforeCommit.ID.String() } - ctx.Data["MergeBase"] = prCompareInfo.MergeBase + ctx.Data["CompareInfo"] = prCompareInfo ctx.Data["AfterCommitID"] = afterCommitID ctx.Data["BeforeCommitID"] = beforeCommitID @@ -737,7 +734,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { MaxLines: maxLines, MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, MaxFiles: maxFiles, - WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), + WhitespaceBehavior: gitdiff.GetWhitespaceFlag(GetWhitespaceBehavior(ctx)), } diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...) @@ -776,7 +773,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { "numberOfViewedFiles": numViewedFiles, } - if err = diff.LoadComments(ctx, issue, ctx.Doer, ctx.Data["ShowOutdatedComments"].(bool)); err != nil { + if err = diff.LoadComments(ctx, issue, ctx.Doer, GetShowOutdatedComments(ctx)); err != nil { ctx.ServerError("LoadComments", err) return } @@ -1282,11 +1279,8 @@ func PullsNewRedirect(ctx *context.Context) { func CompareAndPullRequestPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CreateIssueForm) repo := ctx.Repo.Repository - - ci, err := parseCompareInfo(ctx) - if ctx.Written() { - return - } + comparePageInfo := newComparePageInfo() + err := comparePageInfo.parseCompareInfo(ctx) if errors.Is(err, util.ErrNotExist) { ctx.JSONErrorNotFound() return @@ -1297,7 +1291,8 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.ServerError("ParseCompareInfo", err) return } - if ci.MergeBase == "" { + ci := comparePageInfo.compareInfo + if ci.CompareBase == "" { ctx.JSONError(ctx.Tr("repo.pulls.no_common_history")) return } @@ -1358,7 +1353,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { BaseBranch: ci.BaseRef.ShortName(), HeadRepo: ci.HeadRepo, BaseRepo: repo, - MergeBase: ci.MergeBase, + MergeBase: ci.CompareBase, Type: issues_model.PullRequestGitea, AllowMaintainerEdit: form.AllowMaintainerEdit, } diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index eb8e8fa677..bbcf6630b6 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -169,7 +169,7 @@ func UpdateResolveConversation(ctx *context.Context) { func renderConversation(ctx *context.Context, comment *issues_model.Comment, origin string) { ctx.Data["PageIsPullFiles"] = origin == "diff" - showOutdatedComments := origin == "timeline" || ctx.Data["ShowOutdatedComments"].(bool) + showOutdatedComments := origin == "timeline" || GetShowOutdatedComments(ctx) comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, showOutdatedComments) if err != nil { ctx.ServerError("FetchCodeCommentsByLine", err) diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 39075dbdf6..78fb0ca0af 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -334,9 +334,6 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.Data["Title"] = displayName ctx.Data["title"] = displayName - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name - // lookup filename in wiki - get page content, gitTree entry , real filename _, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) if noEntry { diff --git a/services/git/compare.go b/services/git/compare.go index 40a0eaac08..6102d2418d 100644 --- a/services/git/compare.go +++ b/services/git/compare.go @@ -16,17 +16,23 @@ import ( // CompareInfo represents needed information for comparing references. type CompareInfo struct { - BaseRepo *repo_model.Repository - BaseRef git.RefName - BaseCommitID string - HeadRepo *repo_model.Repository - HeadGitRepo *git.Repository - HeadRef git.RefName - HeadCommitID string + BaseRepo *repo_model.Repository + BaseRef git.RefName + BaseCommitID string + HeadRepo *repo_model.Repository + HeadGitRepo *git.Repository + HeadRef git.RefName + HeadCommitID string + CompareSeparator string - MergeBase string - Commits []*git.Commit - NumFiles int + + // CompareBase is the left-side commit ID used for comparing + // for "...": it is merge base (empty for no merge base) + // for direct comparison "..": it is base commit ID + CompareBase string + + Commits []*git.Commit + NumFiles int } func (ci *CompareInfo) IsSameRepository() bool { @@ -75,15 +81,15 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito } if !directComparison { - compareInfo.MergeBase, err = gitrepo.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID) + compareInfo.CompareBase, err = gitrepo.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID) if err != nil && !errors.Is(err, util.ErrNotExist) { return compareInfo, fmt.Errorf("MergeBase: %w", err) } } else { - compareInfo.MergeBase = compareInfo.BaseCommitID + compareInfo.CompareBase = compareInfo.BaseCommitID } - if compareInfo.MergeBase == "" { + if compareInfo.CompareBase == "" { return compareInfo, nil } @@ -93,7 +99,7 @@ func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Reposito // which is different from the meaning of "..." in git diff (where it implies diffing from the merge base). // For listing PR commits, we must use merge-base..head to include only the commits introduced by the head branch. // Otherwise, commits newly pushed to the base branch would also be included, which is incorrect. - compareInfo.Commits, err = headGitRepo.ShowPrettyFormatLogToList(ctx, compareInfo.MergeBase+".."+compareInfo.HeadCommitID) + compareInfo.Commits, err = headGitRepo.ShowPrettyFormatLogToList(ctx, compareInfo.CompareBase+".."+compareInfo.HeadCommitID) if err != nil { return compareInfo, fmt.Errorf("ShowPrettyFormatLogToList: %w", err) } diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index ccb9e80f28..463575b84d 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -35,7 +35,7 @@ {{template "repo/diff/whitespace_dropdown" .}} {{template "repo/diff/options_dropdown" .}} {{if .PageIsPullFiles}} -
+
{{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}}