From 409e83b0d376955b0a5404fd02f15475d085ee3e Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Fri, 7 Nov 2025 16:46:31 +0100 Subject: [PATCH] fix: do not re-set releasedf_at Signed-off-by: Miguel Martinez --- .../biz/projectversion_integration_test.go | 45 ++++++++++++++++++- app/controlplane/pkg/data/projectversion.go | 20 +++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/app/controlplane/pkg/biz/projectversion_integration_test.go b/app/controlplane/pkg/biz/projectversion_integration_test.go index cf2a9a331..00dee076f 100644 --- a/app/controlplane/pkg/biz/projectversion_integration_test.go +++ b/app/controlplane/pkg/biz/projectversion_integration_test.go @@ -1,5 +1,5 @@ // -// Copyright 2024 The Chainloop Authors. +// Copyright 2024-2025 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package biz_test import ( "context" "testing" + "time" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" @@ -57,6 +58,48 @@ func (s *ProjectVersionIntegrationTestSuite) TestUpdateReleaseStatus() { require.Error(t, err) } +func (s *ProjectVersionIntegrationTestSuite) TestReleasedAtTimestampPreserved() { + t := s.T() + ctx := context.Background() + + // Create a prerelease version + version, err := s.ProjectVersion.Create(ctx, s.project.ID.String(), "2.0.0", true) + require.NoError(t, err) + require.True(t, version.Prerelease) + require.Nil(t, version.ReleasedAt, "Prerelease version should not have released_at set") + + // Update to release status for the first time + releasedVersion, err := s.ProjectVersion.UpdateReleaseStatus(ctx, version.ID.String(), true) + require.NoError(t, err) + require.False(t, releasedVersion.Prerelease) + require.NotNil(t, releasedVersion.ReleasedAt, "Released version should have released_at set") + firstReleasedAt := releasedVersion.ReleasedAt + + // Wait a bit to ensure timestamps would differ if reset + time.Sleep(100 * time.Millisecond) + + // Update to release status again (should preserve original timestamp) + reReleasedVersion, err := s.ProjectVersion.UpdateReleaseStatus(ctx, version.ID.String(), true) + require.NoError(t, err) + require.False(t, reReleasedVersion.Prerelease) + require.NotNil(t, reReleasedVersion.ReleasedAt, "Released version should still have released_at set") + require.Equal(t, firstReleasedAt, reReleasedVersion.ReleasedAt, "released_at timestamp should be preserved on subsequent release updates") + + // Update back to prerelease (should clear released_at) + preReleaseVersion, err := s.ProjectVersion.UpdateReleaseStatus(ctx, version.ID.String(), false) + require.NoError(t, err) + require.True(t, preReleaseVersion.Prerelease) + require.Nil(t, preReleaseVersion.ReleasedAt, "Prerelease version should have released_at cleared") + + // Update to release status again (should set a new timestamp) + time.Sleep(100 * time.Millisecond) + newReleasedVersion, err := s.ProjectVersion.UpdateReleaseStatus(ctx, version.ID.String(), true) + require.NoError(t, err) + require.False(t, newReleasedVersion.Prerelease) + require.NotNil(t, newReleasedVersion.ReleasedAt, "Re-released version should have released_at set") + require.NotEqual(t, firstReleasedAt, newReleasedVersion.ReleasedAt, "released_at should be a new timestamp after toggling through prerelease") +} + // 3 orgs, user belongs to org1 and org2 but not org3 func (s *ProjectVersionIntegrationTestSuite) SetupTest() { t := s.T() diff --git a/app/controlplane/pkg/data/projectversion.go b/app/controlplane/pkg/data/projectversion.go index da63d2f3e..88ceae4e5 100644 --- a/app/controlplane/pkg/data/projectversion.go +++ b/app/controlplane/pkg/data/projectversion.go @@ -54,16 +54,30 @@ func (r *ProjectVersionRepo) Update(ctx context.Context, id uuid.UUID, updates * if updates == nil { updates = &biz.ProjectVersionUpdateOpts{} } + // Only set released_at if it's not already set + existing, err := r.data.DB.ProjectVersion.Query().Where(projectversion.IDEQ(id), projectversion.DeletedAtIsNil()).Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, biz.NewErrNotFound("Version") + } + + return nil, err + } - q := r.data.DB.ProjectVersion.UpdateOneID(id).SetNillablePrerelease(updates.Prerelease).SetUpdatedAt(time.Now()) + now := time.Now() + q := existing.Update().SetNillablePrerelease(updates.Prerelease).SetUpdatedAt(now) // we are setting the value either false or true if updates.Prerelease != nil { // We are marking it as a release if !*updates.Prerelease { - q = q.SetReleasedAt(time.Now()) + // if not set + if existing.ReleasedAt.IsZero() { + // Only set released_at if it's not already set + q.SetReleasedAt(now) + } } else { // We are resetting it to pre-release - q = q.ClearReleasedAt() + q.ClearReleasedAt() } }