diff --git a/CLAUDE.md b/CLAUDE.md index 6b8a6be64..07ebe67e7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -254,4 +254,5 @@ All commits must meet these criteria: Code reviews are required for all submissions via GitHub pull requests. - make sure golang code is always formatted and golang-ci-lint is run - I do not want you to be in the co-author signoff -- when the schema is changed, run make generate, do not create a migration explicitly \ No newline at end of file +- when the schema is changed, run make generate, do not create a migration explicitly +- do not add generated by claude code in PR \ No newline at end of file diff --git a/app/controlplane/pkg/biz/projectversion.go b/app/controlplane/pkg/biz/projectversion.go index db17cd433..da6d7dd47 100644 --- a/app/controlplane/pkg/biz/projectversion.go +++ b/app/controlplane/pkg/biz/projectversion.go @@ -38,7 +38,9 @@ type ProjectVersion struct { CreatedAt *time.Time // ReleasedAt is the time when the version was released. ReleasedAt *time.Time - ProjectID uuid.UUID + // LastRunAt is the time when the last workflow run occurred for this version. + LastRunAt *time.Time + ProjectID uuid.UUID } type ProjectVersionRepo interface { diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20251001215533.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20251001215533.sql new file mode 100644 index 000000000..4cdc4e577 --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20251001215533.sql @@ -0,0 +1,2 @@ +-- Modify "project_versions" table +ALTER TABLE "project_versions" ADD COLUMN "last_run_at" timestamptz NULL; diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20251001215625.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20251001215625.sql new file mode 100644 index 000000000..62b555d75 --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20251001215625.sql @@ -0,0 +1,70 @@ +-- Backfill last_run_at field from existing workflow runs +-- This migration populates the last_run_at timestamp for project versions +-- based on the most recent workflow run for each version + +DO $$ +DECLARE + batch_size INTEGER := 50; + total_remaining INTEGER := 0; +BEGIN + -- Get initial count of project versions that need updating + -- (those with workflow runs but no last_run_at timestamp) + SELECT COUNT(DISTINCT pv.id) + INTO total_remaining + FROM project_versions pv + WHERE pv.last_run_at IS NULL + AND pv.deleted_at IS NULL + AND EXISTS ( + SELECT 1 FROM workflow_runs wr + WHERE wr.version_id = pv.id + ); + + -- Log the initial count + RAISE NOTICE 'Starting backfill of last_run_at for % project versions', total_remaining; + + -- Loop until no more rows remain + WHILE total_remaining > 0 LOOP + -- Update a batch of project versions with their latest workflow run timestamp + WITH versions_to_update AS ( + SELECT pv.id + FROM project_versions pv + WHERE pv.last_run_at IS NULL + AND pv.deleted_at IS NULL + AND EXISTS ( + SELECT 1 FROM workflow_runs wr + WHERE wr.version_id = pv.id + ) + LIMIT batch_size + ), + latest_runs AS ( + SELECT + wr.version_id, + MAX(COALESCE(wr.finished_at, wr.created_at)) as latest_timestamp + FROM workflow_runs wr + INNER JOIN versions_to_update vtu ON wr.version_id = vtu.id + GROUP BY wr.version_id + ) + UPDATE project_versions pv + SET last_run_at = lr.latest_timestamp + FROM latest_runs lr + WHERE pv.id = lr.version_id; + + -- Update the remaining count + SELECT COUNT(DISTINCT pv.id) + INTO total_remaining + FROM project_versions pv + WHERE pv.last_run_at IS NULL + AND pv.deleted_at IS NULL + AND EXISTS ( + SELECT 1 FROM workflow_runs wr + WHERE wr.version_id = pv.id + ); + + -- Log progress + IF total_remaining > 0 THEN + RAISE NOTICE 'Remaining project versions to backfill: %', total_remaining; + END IF; + END LOOP; + + RAISE NOTICE 'Backfill of last_run_at completed successfully'; +END $$; diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index 8d8c5a62c..e0f184c23 100644 --- a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:zMUQdCsdxlu5WHgd4G0cKNp4VzU0ViSDSSMojr38oXQ= +h1:SbJMyd1Na23ALCFwTq0rcVW1zSPsD4bVBvBuO1xOfuA= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -113,3 +113,5 @@ h1:zMUQdCsdxlu5WHgd4G0cKNp4VzU0ViSDSSMojr38oXQ= 20250827093032.sql h1:K+XDWewSLoGBM+zjkBMag3mMQFFQyoQ9SePzfRxC694= 20250902095134.sql h1:e1DP8uYf/CX7RCiCF+E2/TKXiFUR6EUQBPy0wh5Xxl0= 20250908160222.sql h1:bNjptbt2xPpSXqa4eVuWkMnovHt9LMkiakoGrFGZJ0g= +20251001215533.sql h1:+IdTm9OSW1r5nDH1fX2hpia/q0UzklPrDy3SDN3S0dg= +20251001215625.sql h1:adcp5r8CoL/JCfNgNZxBvY5pczlEHFH/IX64g/ji/4s= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index 08512d7ad..9a10e0191 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -489,6 +489,7 @@ var ( {Name: "prerelease", Type: field.TypeBool, Default: true}, {Name: "workflow_run_count", Type: field.TypeInt, Default: 0}, {Name: "released_at", Type: field.TypeTime, Nullable: true}, + {Name: "last_run_at", Type: field.TypeTime, Nullable: true}, {Name: "latest", Type: field.TypeBool, Default: false}, {Name: "project_id", Type: field.TypeUUID}, } @@ -500,7 +501,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "project_versions_projects_versions", - Columns: []*schema.Column{ProjectVersionsColumns[9]}, + Columns: []*schema.Column{ProjectVersionsColumns[10]}, RefColumns: []*schema.Column{ProjectsColumns[0]}, OnDelete: schema.Cascade, }, @@ -509,7 +510,7 @@ var ( { Name: "projectversion_version_project_id", Unique: true, - Columns: []*schema.Column{ProjectVersionsColumns[1], ProjectVersionsColumns[9]}, + Columns: []*schema.Column{ProjectVersionsColumns[1], ProjectVersionsColumns[10]}, Annotation: &entsql.IndexAnnotation{ Where: "deleted_at IS NULL", }, diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index 49187ebed..bbbec66da 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -10762,6 +10762,7 @@ type ProjectVersionMutation struct { workflow_run_count *int addworkflow_run_count *int released_at *time.Time + last_run_at *time.Time latest *bool clearedFields map[string]struct{} project *uuid.UUID @@ -11212,6 +11213,55 @@ func (m *ProjectVersionMutation) ResetReleasedAt() { delete(m.clearedFields, projectversion.FieldReleasedAt) } +// SetLastRunAt sets the "last_run_at" field. +func (m *ProjectVersionMutation) SetLastRunAt(t time.Time) { + m.last_run_at = &t +} + +// LastRunAt returns the value of the "last_run_at" field in the mutation. +func (m *ProjectVersionMutation) LastRunAt() (r time.Time, exists bool) { + v := m.last_run_at + if v == nil { + return + } + return *v, true +} + +// OldLastRunAt returns the old "last_run_at" field's value of the ProjectVersion entity. +// If the ProjectVersion object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectVersionMutation) OldLastRunAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastRunAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastRunAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastRunAt: %w", err) + } + return oldValue.LastRunAt, nil +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (m *ProjectVersionMutation) ClearLastRunAt() { + m.last_run_at = nil + m.clearedFields[projectversion.FieldLastRunAt] = struct{}{} +} + +// LastRunAtCleared returns if the "last_run_at" field was cleared in this mutation. +func (m *ProjectVersionMutation) LastRunAtCleared() bool { + _, ok := m.clearedFields[projectversion.FieldLastRunAt] + return ok +} + +// ResetLastRunAt resets all changes to the "last_run_at" field. +func (m *ProjectVersionMutation) ResetLastRunAt() { + m.last_run_at = nil + delete(m.clearedFields, projectversion.FieldLastRunAt) +} + // SetLatest sets the "latest" field. func (m *ProjectVersionMutation) SetLatest(b bool) { m.latest = &b @@ -11363,7 +11413,7 @@ func (m *ProjectVersionMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ProjectVersionMutation) Fields() []string { - fields := make([]string, 0, 9) + fields := make([]string, 0, 10) if m.version != nil { fields = append(fields, projectversion.FieldVersion) } @@ -11388,6 +11438,9 @@ func (m *ProjectVersionMutation) Fields() []string { if m.released_at != nil { fields = append(fields, projectversion.FieldReleasedAt) } + if m.last_run_at != nil { + fields = append(fields, projectversion.FieldLastRunAt) + } if m.latest != nil { fields = append(fields, projectversion.FieldLatest) } @@ -11415,6 +11468,8 @@ func (m *ProjectVersionMutation) Field(name string) (ent.Value, bool) { return m.WorkflowRunCount() case projectversion.FieldReleasedAt: return m.ReleasedAt() + case projectversion.FieldLastRunAt: + return m.LastRunAt() case projectversion.FieldLatest: return m.Latest() } @@ -11442,6 +11497,8 @@ func (m *ProjectVersionMutation) OldField(ctx context.Context, name string) (ent return m.OldWorkflowRunCount(ctx) case projectversion.FieldReleasedAt: return m.OldReleasedAt(ctx) + case projectversion.FieldLastRunAt: + return m.OldLastRunAt(ctx) case projectversion.FieldLatest: return m.OldLatest(ctx) } @@ -11509,6 +11566,13 @@ func (m *ProjectVersionMutation) SetField(name string, value ent.Value) error { } m.SetReleasedAt(v) return nil + case projectversion.FieldLastRunAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastRunAt(v) + return nil case projectversion.FieldLatest: v, ok := value.(bool) if !ok { @@ -11567,6 +11631,9 @@ func (m *ProjectVersionMutation) ClearedFields() []string { if m.FieldCleared(projectversion.FieldReleasedAt) { fields = append(fields, projectversion.FieldReleasedAt) } + if m.FieldCleared(projectversion.FieldLastRunAt) { + fields = append(fields, projectversion.FieldLastRunAt) + } return fields } @@ -11587,6 +11654,9 @@ func (m *ProjectVersionMutation) ClearField(name string) error { case projectversion.FieldReleasedAt: m.ClearReleasedAt() return nil + case projectversion.FieldLastRunAt: + m.ClearLastRunAt() + return nil } return fmt.Errorf("unknown ProjectVersion nullable field %s", name) } @@ -11619,6 +11689,9 @@ func (m *ProjectVersionMutation) ResetField(name string) error { case projectversion.FieldReleasedAt: m.ResetReleasedAt() return nil + case projectversion.FieldLastRunAt: + m.ResetLastRunAt() + return nil case projectversion.FieldLatest: m.ResetLatest() return nil diff --git a/app/controlplane/pkg/data/ent/projectversion.go b/app/controlplane/pkg/data/ent/projectversion.go index fc79148e1..fb1d007a6 100644 --- a/app/controlplane/pkg/data/ent/projectversion.go +++ b/app/controlplane/pkg/data/ent/projectversion.go @@ -35,6 +35,8 @@ type ProjectVersion struct { WorkflowRunCount int `json:"workflow_run_count,omitempty"` // ReleasedAt holds the value of the "released_at" field. ReleasedAt time.Time `json:"released_at,omitempty"` + // LastRunAt holds the value of the "last_run_at" field. + LastRunAt time.Time `json:"last_run_at,omitempty"` // Whether this is the latest version of the project Latest bool `json:"latest,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -85,7 +87,7 @@ func (*ProjectVersion) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullInt64) case projectversion.FieldVersion: values[i] = new(sql.NullString) - case projectversion.FieldCreatedAt, projectversion.FieldUpdatedAt, projectversion.FieldDeletedAt, projectversion.FieldReleasedAt: + case projectversion.FieldCreatedAt, projectversion.FieldUpdatedAt, projectversion.FieldDeletedAt, projectversion.FieldReleasedAt, projectversion.FieldLastRunAt: values[i] = new(sql.NullTime) case projectversion.FieldID, projectversion.FieldProjectID: values[i] = new(uuid.UUID) @@ -158,6 +160,12 @@ func (pv *ProjectVersion) assignValues(columns []string, values []any) error { } else if value.Valid { pv.ReleasedAt = value.Time } + case projectversion.FieldLastRunAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_run_at", values[i]) + } else if value.Valid { + pv.LastRunAt = value.Time + } case projectversion.FieldLatest: if value, ok := values[i].(*sql.NullBool); !ok { return fmt.Errorf("unexpected type %T for field latest", values[i]) @@ -234,6 +242,9 @@ func (pv *ProjectVersion) String() string { builder.WriteString("released_at=") builder.WriteString(pv.ReleasedAt.Format(time.ANSIC)) builder.WriteString(", ") + builder.WriteString("last_run_at=") + builder.WriteString(pv.LastRunAt.Format(time.ANSIC)) + builder.WriteString(", ") builder.WriteString("latest=") builder.WriteString(fmt.Sprintf("%v", pv.Latest)) builder.WriteByte(')') diff --git a/app/controlplane/pkg/data/ent/projectversion/projectversion.go b/app/controlplane/pkg/data/ent/projectversion/projectversion.go index db81c6b2c..3ba813674 100644 --- a/app/controlplane/pkg/data/ent/projectversion/projectversion.go +++ b/app/controlplane/pkg/data/ent/projectversion/projectversion.go @@ -31,6 +31,8 @@ const ( FieldWorkflowRunCount = "workflow_run_count" // FieldReleasedAt holds the string denoting the released_at field in the database. FieldReleasedAt = "released_at" + // FieldLastRunAt holds the string denoting the last_run_at field in the database. + FieldLastRunAt = "last_run_at" // FieldLatest holds the string denoting the latest field in the database. FieldLatest = "latest" // EdgeProject holds the string denoting the project edge name in mutations. @@ -66,6 +68,7 @@ var Columns = []string{ FieldPrerelease, FieldWorkflowRunCount, FieldReleasedAt, + FieldLastRunAt, FieldLatest, } @@ -146,6 +149,11 @@ func ByReleasedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldReleasedAt, opts...).ToFunc() } +// ByLastRunAt orders the results by the last_run_at field. +func ByLastRunAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastRunAt, opts...).ToFunc() +} + // ByLatest orders the results by the latest field. func ByLatest(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldLatest, opts...).ToFunc() diff --git a/app/controlplane/pkg/data/ent/projectversion/where.go b/app/controlplane/pkg/data/ent/projectversion/where.go index 8dd834eed..a1634ca47 100644 --- a/app/controlplane/pkg/data/ent/projectversion/where.go +++ b/app/controlplane/pkg/data/ent/projectversion/where.go @@ -96,6 +96,11 @@ func ReleasedAt(v time.Time) predicate.ProjectVersion { return predicate.ProjectVersion(sql.FieldEQ(FieldReleasedAt, v)) } +// LastRunAt applies equality check predicate on the "last_run_at" field. It's identical to LastRunAtEQ. +func LastRunAt(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldEQ(FieldLastRunAt, v)) +} + // Latest applies equality check predicate on the "latest" field. It's identical to LatestEQ. func Latest(v bool) predicate.ProjectVersion { return predicate.ProjectVersion(sql.FieldEQ(FieldLatest, v)) @@ -416,6 +421,56 @@ func ReleasedAtNotNil() predicate.ProjectVersion { return predicate.ProjectVersion(sql.FieldNotNull(FieldReleasedAt)) } +// LastRunAtEQ applies the EQ predicate on the "last_run_at" field. +func LastRunAtEQ(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldEQ(FieldLastRunAt, v)) +} + +// LastRunAtNEQ applies the NEQ predicate on the "last_run_at" field. +func LastRunAtNEQ(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldNEQ(FieldLastRunAt, v)) +} + +// LastRunAtIn applies the In predicate on the "last_run_at" field. +func LastRunAtIn(vs ...time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldIn(FieldLastRunAt, vs...)) +} + +// LastRunAtNotIn applies the NotIn predicate on the "last_run_at" field. +func LastRunAtNotIn(vs ...time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldNotIn(FieldLastRunAt, vs...)) +} + +// LastRunAtGT applies the GT predicate on the "last_run_at" field. +func LastRunAtGT(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldGT(FieldLastRunAt, v)) +} + +// LastRunAtGTE applies the GTE predicate on the "last_run_at" field. +func LastRunAtGTE(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldGTE(FieldLastRunAt, v)) +} + +// LastRunAtLT applies the LT predicate on the "last_run_at" field. +func LastRunAtLT(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldLT(FieldLastRunAt, v)) +} + +// LastRunAtLTE applies the LTE predicate on the "last_run_at" field. +func LastRunAtLTE(v time.Time) predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldLTE(FieldLastRunAt, v)) +} + +// LastRunAtIsNil applies the IsNil predicate on the "last_run_at" field. +func LastRunAtIsNil() predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldIsNull(FieldLastRunAt)) +} + +// LastRunAtNotNil applies the NotNil predicate on the "last_run_at" field. +func LastRunAtNotNil() predicate.ProjectVersion { + return predicate.ProjectVersion(sql.FieldNotNull(FieldLastRunAt)) +} + // LatestEQ applies the EQ predicate on the "latest" field. func LatestEQ(v bool) predicate.ProjectVersion { return predicate.ProjectVersion(sql.FieldEQ(FieldLatest, v)) diff --git a/app/controlplane/pkg/data/ent/projectversion_create.go b/app/controlplane/pkg/data/ent/projectversion_create.go index a7a061a33..6ac416ffc 100644 --- a/app/controlplane/pkg/data/ent/projectversion_create.go +++ b/app/controlplane/pkg/data/ent/projectversion_create.go @@ -130,6 +130,20 @@ func (pvc *ProjectVersionCreate) SetNillableReleasedAt(t *time.Time) *ProjectVer return pvc } +// SetLastRunAt sets the "last_run_at" field. +func (pvc *ProjectVersionCreate) SetLastRunAt(t time.Time) *ProjectVersionCreate { + pvc.mutation.SetLastRunAt(t) + return pvc +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (pvc *ProjectVersionCreate) SetNillableLastRunAt(t *time.Time) *ProjectVersionCreate { + if t != nil { + pvc.SetLastRunAt(*t) + } + return pvc +} + // SetLatest sets the "latest" field. func (pvc *ProjectVersionCreate) SetLatest(b bool) *ProjectVersionCreate { pvc.mutation.SetLatest(b) @@ -338,6 +352,10 @@ func (pvc *ProjectVersionCreate) createSpec() (*ProjectVersion, *sqlgraph.Create _spec.SetField(projectversion.FieldReleasedAt, field.TypeTime, value) _node.ReleasedAt = value } + if value, ok := pvc.mutation.LastRunAt(); ok { + _spec.SetField(projectversion.FieldLastRunAt, field.TypeTime, value) + _node.LastRunAt = value + } if value, ok := pvc.mutation.Latest(); ok { _spec.SetField(projectversion.FieldLatest, field.TypeBool, value) _node.Latest = value @@ -529,6 +547,24 @@ func (u *ProjectVersionUpsert) ClearReleasedAt() *ProjectVersionUpsert { return u } +// SetLastRunAt sets the "last_run_at" field. +func (u *ProjectVersionUpsert) SetLastRunAt(v time.Time) *ProjectVersionUpsert { + u.Set(projectversion.FieldLastRunAt, v) + return u +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ProjectVersionUpsert) UpdateLastRunAt() *ProjectVersionUpsert { + u.SetExcluded(projectversion.FieldLastRunAt) + return u +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ProjectVersionUpsert) ClearLastRunAt() *ProjectVersionUpsert { + u.SetNull(projectversion.FieldLastRunAt) + return u +} + // SetLatest sets the "latest" field. func (u *ProjectVersionUpsert) SetLatest(v bool) *ProjectVersionUpsert { u.Set(projectversion.FieldLatest, v) @@ -711,6 +747,27 @@ func (u *ProjectVersionUpsertOne) ClearReleasedAt() *ProjectVersionUpsertOne { }) } +// SetLastRunAt sets the "last_run_at" field. +func (u *ProjectVersionUpsertOne) SetLastRunAt(v time.Time) *ProjectVersionUpsertOne { + return u.Update(func(s *ProjectVersionUpsert) { + s.SetLastRunAt(v) + }) +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ProjectVersionUpsertOne) UpdateLastRunAt() *ProjectVersionUpsertOne { + return u.Update(func(s *ProjectVersionUpsert) { + s.UpdateLastRunAt() + }) +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ProjectVersionUpsertOne) ClearLastRunAt() *ProjectVersionUpsertOne { + return u.Update(func(s *ProjectVersionUpsert) { + s.ClearLastRunAt() + }) +} + // SetLatest sets the "latest" field. func (u *ProjectVersionUpsertOne) SetLatest(v bool) *ProjectVersionUpsertOne { return u.Update(func(s *ProjectVersionUpsert) { @@ -1062,6 +1119,27 @@ func (u *ProjectVersionUpsertBulk) ClearReleasedAt() *ProjectVersionUpsertBulk { }) } +// SetLastRunAt sets the "last_run_at" field. +func (u *ProjectVersionUpsertBulk) SetLastRunAt(v time.Time) *ProjectVersionUpsertBulk { + return u.Update(func(s *ProjectVersionUpsert) { + s.SetLastRunAt(v) + }) +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ProjectVersionUpsertBulk) UpdateLastRunAt() *ProjectVersionUpsertBulk { + return u.Update(func(s *ProjectVersionUpsert) { + s.UpdateLastRunAt() + }) +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ProjectVersionUpsertBulk) ClearLastRunAt() *ProjectVersionUpsertBulk { + return u.Update(func(s *ProjectVersionUpsert) { + s.ClearLastRunAt() + }) +} + // SetLatest sets the "latest" field. func (u *ProjectVersionUpsertBulk) SetLatest(v bool) *ProjectVersionUpsertBulk { return u.Update(func(s *ProjectVersionUpsert) { diff --git a/app/controlplane/pkg/data/ent/projectversion_update.go b/app/controlplane/pkg/data/ent/projectversion_update.go index ebe981cb2..51b93a979 100644 --- a/app/controlplane/pkg/data/ent/projectversion_update.go +++ b/app/controlplane/pkg/data/ent/projectversion_update.go @@ -149,6 +149,26 @@ func (pvu *ProjectVersionUpdate) ClearReleasedAt() *ProjectVersionUpdate { return pvu } +// SetLastRunAt sets the "last_run_at" field. +func (pvu *ProjectVersionUpdate) SetLastRunAt(t time.Time) *ProjectVersionUpdate { + pvu.mutation.SetLastRunAt(t) + return pvu +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (pvu *ProjectVersionUpdate) SetNillableLastRunAt(t *time.Time) *ProjectVersionUpdate { + if t != nil { + pvu.SetLastRunAt(*t) + } + return pvu +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (pvu *ProjectVersionUpdate) ClearLastRunAt() *ProjectVersionUpdate { + pvu.mutation.ClearLastRunAt() + return pvu +} + // SetLatest sets the "latest" field. func (pvu *ProjectVersionUpdate) SetLatest(b bool) *ProjectVersionUpdate { pvu.mutation.SetLatest(b) @@ -300,6 +320,12 @@ func (pvu *ProjectVersionUpdate) sqlSave(ctx context.Context) (n int, err error) if pvu.mutation.ReleasedAtCleared() { _spec.ClearField(projectversion.FieldReleasedAt, field.TypeTime) } + if value, ok := pvu.mutation.LastRunAt(); ok { + _spec.SetField(projectversion.FieldLastRunAt, field.TypeTime, value) + } + if pvu.mutation.LastRunAtCleared() { + _spec.ClearField(projectversion.FieldLastRunAt, field.TypeTime) + } if value, ok := pvu.mutation.Latest(); ok { _spec.SetField(projectversion.FieldLatest, field.TypeBool, value) } @@ -516,6 +542,26 @@ func (pvuo *ProjectVersionUpdateOne) ClearReleasedAt() *ProjectVersionUpdateOne return pvuo } +// SetLastRunAt sets the "last_run_at" field. +func (pvuo *ProjectVersionUpdateOne) SetLastRunAt(t time.Time) *ProjectVersionUpdateOne { + pvuo.mutation.SetLastRunAt(t) + return pvuo +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (pvuo *ProjectVersionUpdateOne) SetNillableLastRunAt(t *time.Time) *ProjectVersionUpdateOne { + if t != nil { + pvuo.SetLastRunAt(*t) + } + return pvuo +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (pvuo *ProjectVersionUpdateOne) ClearLastRunAt() *ProjectVersionUpdateOne { + pvuo.mutation.ClearLastRunAt() + return pvuo +} + // SetLatest sets the "latest" field. func (pvuo *ProjectVersionUpdateOne) SetLatest(b bool) *ProjectVersionUpdateOne { pvuo.mutation.SetLatest(b) @@ -697,6 +743,12 @@ func (pvuo *ProjectVersionUpdateOne) sqlSave(ctx context.Context) (_node *Projec if pvuo.mutation.ReleasedAtCleared() { _spec.ClearField(projectversion.FieldReleasedAt, field.TypeTime) } + if value, ok := pvuo.mutation.LastRunAt(); ok { + _spec.SetField(projectversion.FieldLastRunAt, field.TypeTime, value) + } + if pvuo.mutation.LastRunAtCleared() { + _spec.ClearField(projectversion.FieldLastRunAt, field.TypeTime) + } if value, ok := pvuo.mutation.Latest(); ok { _spec.SetField(projectversion.FieldLatest, field.TypeBool, value) } diff --git a/app/controlplane/pkg/data/ent/runtime.go b/app/controlplane/pkg/data/ent/runtime.go index 1653b9e08..3e92f970c 100644 --- a/app/controlplane/pkg/data/ent/runtime.go +++ b/app/controlplane/pkg/data/ent/runtime.go @@ -246,7 +246,7 @@ func init() { // projectversion.DefaultWorkflowRunCount holds the default value on creation for the workflow_run_count field. projectversion.DefaultWorkflowRunCount = projectversionDescWorkflowRunCount.Default.(int) // projectversionDescLatest is the schema descriptor for latest field. - projectversionDescLatest := projectversionFields[9].Descriptor() + projectversionDescLatest := projectversionFields[10].Descriptor() // projectversion.DefaultLatest holds the default value on creation for the latest field. projectversion.DefaultLatest = projectversionDescLatest.Default.(bool) // projectversionDescID is the schema descriptor for id field. diff --git a/app/controlplane/pkg/data/ent/schema/projectversion.go b/app/controlplane/pkg/data/ent/schema/projectversion.go index c43730269..44bfaac95 100644 --- a/app/controlplane/pkg/data/ent/schema/projectversion.go +++ b/app/controlplane/pkg/data/ent/schema/projectversion.go @@ -60,6 +60,8 @@ func (ProjectVersion) Fields() []ent.Field { field.Int("workflow_run_count").Default(0), // When the version was released field.Time("released_at").Optional(), + // When the last workflow run occurred for this version + field.Time("last_run_at").Optional(), field.Bool("latest").Default(false).Comment("Whether this is the latest version of the project"), } } diff --git a/app/controlplane/pkg/data/projectversion.go b/app/controlplane/pkg/data/projectversion.go index b95b4d250..da63d2f3e 100644 --- a/app/controlplane/pkg/data/projectversion.go +++ b/app/controlplane/pkg/data/projectversion.go @@ -126,6 +126,7 @@ func entProjectVersionToBiz(v *ent.ProjectVersion) *biz.ProjectVersion { TotalWorkflowRuns: v.WorkflowRunCount, CreatedAt: toTimePtr(v.CreatedAt), ReleasedAt: toTimePtr(v.ReleasedAt), + LastRunAt: toTimePtr(v.LastRunAt), ProjectID: v.ProjectID, } diff --git a/app/controlplane/pkg/data/workflowrun.go b/app/controlplane/pkg/data/workflowrun.go index e69cd8dba..f00365068 100644 --- a/app/controlplane/pkg/data/workflowrun.go +++ b/app/controlplane/pkg/data/workflowrun.go @@ -100,9 +100,10 @@ func (r *WorkflowRunRepo) Create(ctx context.Context, opts *biz.WorkflowRunRepoC return fmt.Errorf("updating workflow: %w", err) } - // Update the project version if any incrementing the runs count + // Update the project version if any incrementing the runs count and setting last run timestamp _, err = tx.ProjectVersion.UpdateOneID(version.ID). AddWorkflowRunCount(1). + SetLastRunAt(time.Now()). Save(ctx) if err != nil { return fmt.Errorf("updating project version: %w", err)