File size: 5,038 Bytes
d61821a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
diff --git a/common/build.go b/common/build.go
index ef5dda01..73e7972e 100644
--- a/common/build.go
+++ b/common/build.go
@@ -656,11 +656,8 @@ func (b *Build) executeStage(ctx context.Context, buildStage BuildStage, executo
 			// rather than the returned error because gRPC wraps deadline
 			// exceeded as a status error that does not unwrap to
 			// context.DeadlineExceeded.
-			if err != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {
-				b.logger.Warningln(
-					string(buildStage) + " could not run to completion because the timeout was exceeded. " +
-						"For more control over job and script timeouts see: " +
-						"https://docs.gitlab.com/ci/runners/configure_runners/#set-script-and-after_script-timeouts")
+			if err != nil {
+				b.warnTimeoutExceeded(ctx, string(buildStage))
 			}
 			return err
 		}
@@ -676,14 +673,7 @@ func (b *Build) executeStage(ctx context.Context, buildStage BuildStage, executo
 	b.setCurrentStage(buildStage)
 	b.Log().WithField("build_stage", buildStage).Debug("Executing build stage")
 
-	defer func() {
-		if errors.Is(ctx.Err(), context.DeadlineExceeded) {
-			b.logger.Warningln(
-				string(buildStage) + " could not run to completion because the timeout was exceeded. " +
-					"For more control over job and script timeouts see: " +
-					"https://docs.gitlab.com/ci/runners/configure_runners/#set-script-and-after_script-timeouts")
-		}
-	}()
+	defer b.warnTimeoutExceeded(ctx, string(buildStage))
 
 	shell := executor.Shell()
 	if shell == nil {
@@ -828,6 +818,12 @@ func (b *Build) executeScript(ctx context.Context, trace JobTrace, executor Exec
 		//nolint:errcheck
 		err = b.executeStepStage(ctx, executor.(steps.Connector), "concrete", concreteSteps, trace.SetCancelFunc)
 
+		// Whole-job dispatch: stage breakdown isn't visible to the user, so
+		// attribute the timeout to "Job" rather than a build stage.
+		if err != nil {
+			b.warnTimeoutExceeded(ctx, "Job")
+		}
+
 		b.executeUploadReferees(ctx, startTime, time.Now())
 
 		return err
@@ -1092,6 +1088,16 @@ func (b *Build) attemptExecuteStage(
 	return err
 }
 
+func (b *Build) warnTimeoutExceeded(ctx context.Context, subject string) {
+	if !errors.Is(ctx.Err(), context.DeadlineExceeded) {
+		return
+	}
+	b.logger.Warningln(
+		subject + " could not run to completion because the timeout was exceeded. " +
+			"For more control over job and script timeouts see: " +
+			"https://docs.gitlab.com/ci/runners/configure_runners/#set-script-and-after_script-timeouts")
+}
+
 func (b *Build) GetBuildTimeout() time.Duration {
 	buildTimeout := b.RunnerInfo.Timeout
 	if buildTimeout <= 0 {
diff --git a/functions/concrete/builder/builder.go b/functions/concrete/builder/builder.go
index 310ae0fd..30fba029 100644
--- a/functions/concrete/builder/builder.go
+++ b/functions/concrete/builder/builder.go
@@ -353,7 +353,6 @@ func (b *builder) buildSteps() []stages.Step {
 		})
 
 		if step.Name == spec.StepNameAfterScript {
-			s.AllowFailure = true
 			s.OnSuccess = true
 			s.OnFailure = true
 			afterScript = append(afterScript, s)
diff --git a/functions/concrete/run/runner.go b/functions/concrete/run/runner.go
index d17b3d62..cbfc5c64 100644
--- a/functions/concrete/run/runner.go
+++ b/functions/concrete/run/runner.go
@@ -224,17 +224,21 @@ func (r *Runner) runScriptSteps(jobCtx context.Context, steps []stages.Step) err
 	r.setCancel(cancel)
 
 	var firstErr error
+	timeoutStage := "step_script"
 	for _, step := range steps {
 		r.loadGitlabEnv()
 
-		if err := r.section(scriptCtx, "step_"+step.Step, step.Run); err != nil {
+		sectionName := "step_" + step.Step
+		if err := r.section(scriptCtx, sectionName, step.Run); err != nil {
 			if firstErr == nil {
 				firstErr = err
 			}
+			timeoutStage = sectionName
 			break
 		}
 	}
 
+	r.warnStageTimeout(jobCtx, scriptCtx, timeoutStage)
 	return r.classifyScriptContextError(jobCtx, scriptCtx, firstErr)
 }
 
@@ -277,6 +281,7 @@ func (r *Runner) runAfterScriptSteps(jobCtx context.Context, steps []stages.Step
 		return nil
 	})
 
+	r.warnStageTimeout(jobCtx, afterCtx, "after_script")
 	return err
 }
 
@@ -386,6 +391,21 @@ func (r *Runner) logWarningf(format string, args ...interface{}) {
 	fmt.Fprintf(r.env.Stderr, " %s\033[0m\n", msg)
 }
 
+// warnStageTimeout fires only on a stage-only deadline; the job-level
+// case is owned by the outer runner.
+func (r *Runner) warnStageTimeout(jobCtx, stageCtx context.Context, stageName string) {
+	if !errors.Is(stageCtx.Err(), context.DeadlineExceeded) {
+		return
+	}
+	if errors.Is(jobCtx.Err(), context.DeadlineExceeded) {
+		return
+	}
+	r.logWarningf("%s could not run to completion because the timeout was exceeded. "+
+		"For more control over job and script timeouts see: "+
+		"https://docs.gitlab.com/ci/runners/configure_runners/#set-script-and-after_script-timeouts",
+		stageName)
+}
+
 // setupGitlabEnv creates the GITLAB_ENV file so user scripts can append
 // KEY=VALUE lines to define dynamic variables for subsequent steps.
 func (r *Runner) setupGitlabEnv() error {