diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index 6790ee1da9..781a75730a 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -75,26 +75,28 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us return nil, err } - additionalUserIDs := make([]int64, 0, 10) - if err = e.Table("team_user"). - Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). - Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). - Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))", - repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests). - Distinct("`team_user`.uid"). - Select("`team_user`.uid"). - Find(&additionalUserIDs); err != nil { - return nil, err - } - uniqueUserIDs := make(container.Set[int64]) uniqueUserIDs.AddMultiple(userIDs...) - uniqueUserIDs.AddMultiple(additionalUserIDs...) + + if repo.Owner.IsOrganization() { + additionalUserIDs := make([]int64, 0, 10) + if err = e.Table("team_user"). + Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). + Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). + Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))", + repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests). + Distinct("`team_user`.uid"). + Select("`team_user`.uid"). + Find(&additionalUserIDs); err != nil { + return nil, err + } + uniqueUserIDs.AddMultiple(additionalUserIDs...) + } // Leave a seat for owner itself to append later, but if owner is an organization // and just waste 1 unit is cheaper than re-allocate memory once. users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) - if len(userIDs) > 0 { + if len(uniqueUserIDs) > 0 { if err = e.In("id", uniqueUserIDs.Values()). Where(builder.Eq{"`user`.is_active": true}). OrderBy(user_model.GetOrderByName()). diff --git a/release-notes/5988.md b/release-notes/5988.md new file mode 100644 index 0000000000..52add6347e --- /dev/null +++ b/release-notes/5988.md @@ -0,0 +1 @@ +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/fc26becba4b08877a726f2e7e453992310245fe5) when a tag was removed and a release existed for that tag, it would be broken. The release is no longer broken the tag can be added again. diff --git a/services/repository/push.go b/services/repository/push.go index 0f24295e89..a8e7d0f3b6 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -307,9 +307,10 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo } releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ - RepoID: repo.ID, - TagNames: tags, - IncludeTags: true, + RepoID: repo.ID, + TagNames: tags, + IncludeDrafts: true, + IncludeTags: true, }) if err != nil { return fmt.Errorf("db.Find[repo_model.Release]: %w", err) @@ -394,13 +395,17 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo } newReleases = append(newReleases, rel) } else { - rel.Title = parts[0] - rel.Note = note rel.Sha1 = commit.ID.String() rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) rel.NumCommits = commitsCount - if rel.IsTag && author != nil { - rel.PublisherID = author.ID + if rel.IsTag { + rel.Title = parts[0] + rel.Note = note + if author != nil { + rel.PublisherID = author.ID + } + } else { + rel.IsDraft = false } if err = repo_model.UpdateRelease(ctx, rel); err != nil { return fmt.Errorf("Update: %w", err) diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 06b6c30383..cb2d0964f0 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -5,17 +5,20 @@ package integration import ( + "fmt" "net/http" "net/url" "strings" "testing" "code.gitea.io/gitea/models" + 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/git" repo_module "code.gitea.io/gitea/modules/repository" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/release" "code.gitea.io/gitea/tests" @@ -159,3 +162,47 @@ func TestSyncRepoTags(t *testing.T) { }) }) } + +func TestRepushTag(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + session := loginUser(t, owner.LowerName) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + httpContext := NewAPITestContext(t, owner.Name, repo.Name) + + dstPath := t.TempDir() + + u.Path = httpContext.GitPath() + u.User = url.UserPassword(owner.Name, userPassword) + + doGitClone(dstPath, u)(t) + + // create and push a tag + _, _, err := git.NewCommand(git.DefaultContext, "tag", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // create a release for the tag + createdRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v2.0", "", "Release of v2.0", "desc") + assert.False(t, createdRelease.IsDraft) + // delete the tag + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--delete", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // query the release by API and it should be a draft + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0")) + resp := MakeRequest(t, req, http.StatusOK) + var respRelease *api.Release + DecodeJSON(t, resp, &respRelease) + assert.True(t, respRelease.IsDraft) + // re-push the tag + _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + // query the release by API and it should not be a draft + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0")) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &respRelease) + assert.False(t, respRelease.IsDraft) + }) +} diff --git a/web_src/js/markup/mermaid.js b/web_src/js/markup/mermaid.js index 0549fb3e31..e420ff12a2 100644 --- a/web_src/js/markup/mermaid.js +++ b/web_src/js/markup/mermaid.js @@ -56,10 +56,21 @@ export async function renderMermaid() { btn.setAttribute('data-clipboard-text', source); mermaidBlock.append(btn); + const updateIframeHeight = () => { + iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`; + }; + + // update height when element's visibility state changes, for example when the diagram is inside + // a
+ block and the
block becomes visible upon user interaction, it + // would initially set a incorrect height and the correct height is set during this callback. + (new IntersectionObserver(() => { + updateIframeHeight(); + }, {root: document.documentElement})).observe(iframe); + iframe.addEventListener('load', () => { pre.replaceWith(mermaidBlock); mermaidBlock.classList.remove('tw-hidden'); - iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`; + updateIframeHeight(); setTimeout(() => { // avoid flash of iframe background mermaidBlock.classList.remove('is-loading'); iframe.classList.remove('tw-invisible');