From 07ba4d9f87cf21b7ce87158ae5651cae3bb35604 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Fri, 19 Jan 2024 23:05:49 +0900
Subject: [PATCH] Fix incorrect action duration time when rerun the job before
 executed once (#28364)

Fix #28323
Reason was mentioned here:
https://github.com/go-gitea/gitea/issues/28323#issuecomment-1841867298

### Changes: (maybe breaking)
We can rerun jobs in Gitea, so there will be some problems in
calculating duration time.
In this PR, I use the exist `Started` and `Stopped` column to record the
last run time instead of the total time,
and add a new `PreviousDuration` column to record the previous duration
time.
You can also check the cost time of last run:

![image](https://github.com/go-gitea/gitea/assets/18380374/2ca39145-2c92-401a-b78b-43164f7ae061)
---
 models/actions/run.go            | 13 ++++++++-----
 models/migrations/migrations.go  |  2 ++
 models/migrations/v1_22/v285.go  | 18 ++++++++++++++++++
 routers/web/repo/actions/view.go | 11 +++++++++++
 4 files changed, 39 insertions(+), 5 deletions(-)
 create mode 100644 models/migrations/v1_22/v285.go

diff --git a/models/actions/run.go b/models/actions/run.go
index 1a3701b0b0..fcac58d515 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -46,10 +46,13 @@ type ActionRun struct {
 	TriggerEvent      string                       // the trigger event defined in the `on` configuration of the triggered workflow
 	Status            Status                       `xorm:"index"`
 	Version           int                          `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed
-	Started           timeutil.TimeStamp
-	Stopped           timeutil.TimeStamp
-	Created           timeutil.TimeStamp `xorm:"created"`
-	Updated           timeutil.TimeStamp `xorm:"updated"`
+	// Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0
+	Started timeutil.TimeStamp
+	Stopped timeutil.TimeStamp
+	// PreviousDuration is used for recording previous duration
+	PreviousDuration time.Duration
+	Created          timeutil.TimeStamp `xorm:"created"`
+	Updated          timeutil.TimeStamp `xorm:"updated"`
 }
 
 func init() {
@@ -118,7 +121,7 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
 }
 
 func (run *ActionRun) Duration() time.Duration {
-	return calculateDuration(run.Started, run.Stopped, run.Status)
+	return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
 }
 
 func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 3b4ac24a2c..21675cab8a 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -554,6 +554,8 @@ var migrations = []Migration{
 	NewMigration("Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
 	// v284 -> v285
 	NewMigration("Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable),
+	// v285 -> v286
+	NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go
new file mode 100644
index 0000000000..c0dacd40bc
--- /dev/null
+++ b/models/migrations/v1_22/v285.go
@@ -0,0 +1,18 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_22 //nolint
+
+import (
+	"time"
+
+	"xorm.io/xorm"
+)
+
+func AddPreviousDurationToActionRun(x *xorm.Engine) error {
+	type ActionRun struct {
+		PreviousDuration time.Duration
+	}
+
+	return x.Sync(&ActionRun{})
+}
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 1cdae32a32..9cda30d23d 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -279,6 +279,17 @@ func Rerun(ctx *context_module.Context) {
 		return
 	}
 
+	// reset run's start and stop time when it is done
+	if run.Status.IsDone() {
+		run.PreviousDuration = run.Duration()
+		run.Started = 0
+		run.Stopped = 0
+		if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration"); err != nil {
+			ctx.Error(http.StatusInternalServerError, err.Error())
+			return
+		}
+	}
+
 	job, jobs := getRunJobs(ctx, runIndex, jobIndex)
 	if ctx.Written() {
 		return