fix(release): separate publication time from the release date (#36761)

`published_at` was an alias for `created_at`, so a release created from
an existing tag reported that tag's commit date as its publication time,
and drafts reported one despite never having been published. It is now
stored separately, set when a release is published and null for drafts.

`created_at` in turn means the date of the commit the release points at,
matching what GitHub documents it to be, and the latest release is
selected by it again. Publishing a release for an old commit no longer
takes over the latest badge, and a tag created in the web UI is dated
the same way as one pushed from the CLI.

Fixes https://github.com/go-gitea/gitea/issues/11206
Fixes https://github.com/go-gitea/gitea/issues/38714
Fixes https://github.com/go-gitea/gitea/issues/31789

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
silverwind
2026-08-22 08:59:37 +00:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 6bb6ce678b
commit fa0b39a42b
22 changed files with 238 additions and 67 deletions
+2 -2
View File
@@ -367,7 +367,7 @@ func (stats *ActivityStats) FillReleases(ctx context.Context, repoID int64, from
// Published releases list
sess := releasesForActivityStatement(ctx, repoID, fromTime)
sess.OrderBy("`release`.created_unix DESC")
sess.OrderBy("`release`.published_unix DESC")
stats.PublishedReleases = make([]*repo_model.Release, 0)
if err = sess.Find(&stats.PublishedReleases); err != nil {
return err
@@ -386,5 +386,5 @@ func (stats *ActivityStats) FillReleases(ctx context.Context, repoID int64, from
func releasesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time) db.Session {
return db.GetEngine(ctx).Where("`release`.repo_id = ?", repoID).
And("`release`.is_draft = ?", false).
And("`release`.created_unix >= ?", fromTime.Unix())
And("`release`.published_unix >= ?", fromTime.Unix())
}
+10
View File
@@ -11,6 +11,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684800
published_unix: 946684800
- id: 2
repo_id: 40
@@ -25,6 +26,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684800
published_unix: 946684800
- id: 3
repo_id: 1
@@ -39,6 +41,7 @@
is_prerelease: false
is_tag: true
created_unix: 946684800
published_unix: 946684800
- id: 4
repo_id: 1
@@ -66,6 +69,7 @@
is_prerelease: true
is_tag: false
created_unix: 946684800
published_unix: 946684800
- id: 6
repo_id: 57
@@ -80,6 +84,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684801
published_unix: 946684801
- id: 7
repo_id: 57
@@ -94,6 +99,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684802
published_unix: 946684802
- id: 8
repo_id: 57
@@ -108,6 +114,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684803
published_unix: 946684803
- id: 9
repo_id: 57
@@ -122,6 +129,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684803
published_unix: 946684803
- id: 10
repo_id: 57
@@ -136,6 +144,7 @@
is_prerelease: false
is_tag: false
created_unix: 946684803
published_unix: 946684803
- id: 11
repo_id: 2
@@ -150,5 +159,6 @@
is_prerelease: false
is_tag: false
created_unix: 946684803
published_unix: 946684803
# DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly
+2 -1
View File
@@ -87,6 +87,7 @@ type Release struct {
IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
Attachments []*Attachment `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
PublishedUnix timeutil.TimeStamp `xorm:"NOT NULL DEFAULT 0"`
}
func init() {
@@ -473,7 +474,7 @@ func PushUpdateDeleteTags(ctx context.Context, repo *Repository, tags []string)
if _, err := db.GetEngine(ctx).
Where("repo_id = ? AND is_tag = ?", repo.ID, false).
In("lower_tag_name", lowerTags).
Cols("is_draft", "num_commits", "sha1").
Cols("is_draft", "num_commits", "sha1", "published_unix").
Update(&Release{
IsDraft: true,
}); err != nil {