Skip to content

Commit c671de0

Browse files
authored
Merge branch 'main' into impprev
2 parents 5b40a23 + 3ab8ae5 commit c671de0

File tree

4 files changed

+175
-38
lines changed

4 files changed

+175
-38
lines changed

options/locale/locale_fr-FR.ini

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,9 @@ pulls.status_checks_requested=Requis
19691969
pulls.status_checks_details=Détails
19701970
pulls.status_checks_hide_all=Masquer toutes les vérifications
19711971
pulls.status_checks_show_all=Afficher toutes les vérifications
1972+
pulls.status_checks_approve_all=Accepter tous les flux de travail
1973+
pulls.status_checks_need_approvals=%d flux de travail en attente d’approbation
1974+
pulls.status_checks_need_approvals_helper=Ce flux de travail ne s’exécutera qu’après l’approbation par le mainteneur du dépôt.
19721975
pulls.update_branch=Actualiser la branche par fusion
19731976
pulls.update_branch_rebase=Actualiser la branche par rebasage
19741977
pulls.update_branch_success=La mise à jour de la branche a réussi
@@ -2434,6 +2437,9 @@ settings.event_workflow_job_desc=Travaux du flux de travail Gitea Actions en fil
24342437
settings.event_package=Paquet
24352438
settings.event_package_desc=Paquet créé ou supprimé.
24362439
settings.branch_filter=Filtre de branche
2440+
settings.branch_filter_desc_1=Liste de branches et références autorisées pour la soumission, la création et la suppression de branches, sous forme de glob. En utilisant <code>*</code> ou en laissant vide, cela inclue toutes les branches et étiquettes git.
2441+
settings.branch_filter_desc_2=Utilisez le préfixe <code>refs/heads/</code> ou <code>refs/tags/</code> pour faire correspondre les noms complets des références.
2442+
settings.branch_filter_desc_doc=Consultez <a href="%[1]s">la documentation %[2]s</a> pour utiliser sa syntaxe.
24372443
settings.authorization_header=En-tête « Authorization »
24382444
settings.authorization_header_desc=Si présent, sera ajouté aux requêtes comme en-tête d’authentification. Exemples : %s.
24392445
settings.active=Actif
@@ -3729,6 +3735,7 @@ swift.install=Ajoutez le paquet dans votre fichier <code>Package.swift</code>:
37293735
swift.install2=et exécutez la commande suivante :
37303736
vagrant.install=Pour ajouter une machine Vagrant, exécutez la commande suivante :
37313737
settings.link=Lier ce paquet à un dépôt
3738+
settings.link.description=Si vous associez un paquet à un dépôt, le paquet sera inclus dans sa liste des paquets. Seul les dépôts d’un même propriétaire peuvent être associés. Laisser ce champ vide supprimera le lien.
37323739
settings.link.select=Sélectionner un dépôt
37333740
settings.link.button=Actualiser le lien du dépôt
37343741
settings.link.success=Le lien du dépôt a été mis à jour avec succès.
@@ -3886,6 +3893,7 @@ workflow.has_workflow_dispatch=Ce flux de travail a un déclencheur d’événem
38863893
workflow.has_no_workflow_dispatch=Le flux de travail %s n’a pas de déclencheur d’événement workflow_dispatch.
38873894

38883895
need_approval_desc=Besoin d’approbation pour exécuter des flux de travail pour une demande d’ajout de bifurcation.
3896+
approve_all_success=Tous les flux de travail ont été acceptés.
38893897

38903898
variables=Variables
38913899
variables.management=Gestion des variables
@@ -3906,6 +3914,14 @@ variables.update.success=La variable a bien été modifiée.
39063914
logs.always_auto_scroll=Toujours faire défiler les journaux automatiquement
39073915
logs.always_expand_running=Toujours développer les journaux en cours
39083916

3917+
general=Général
3918+
general.enable_actions=Activer les actions
3919+
general.collaborative_owners_management=Gestion des collaborateurs
3920+
general.collaborative_owners_management_help=Un collaborateur est un utilisateur ou une organisation dont le dépôt privé peut accéder aux actions et flux de travail de ce dépôt.
3921+
general.add_collaborative_owner=Ajouter un collaborateur
3922+
general.collaborative_owner_not_exist=Le collaborateur n’existe pas.
3923+
general.remove_collaborative_owner=Supprimer le collaborateur
3924+
general.remove_collaborative_owner_desc=Supprimer un collaborateur empêchera les dépôts de cet utilisateur d’accéder aux actions dans ce dépôt. Continuer ?
39093925

39103926
[projects]
39113927
deleted.display_name=Projet supprimé

routers/web/repo/actions/view.go

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ func Rerun(ctx *context_module.Context) {
412412
return
413413
}
414414

415+
// rerun is not allowed if the run is not done
416+
if !run.Status.IsDone() {
417+
ctx.JSONError(ctx.Locale.Tr("actions.runs.not_done"))
418+
return
419+
}
420+
415421
// can not rerun job when workflow is disabled
416422
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
417423
cfg := cfgUnit.ActionsConfig()
@@ -420,55 +426,51 @@ func Rerun(ctx *context_module.Context) {
420426
return
421427
}
422428

423-
// check run (workflow-level) concurrency
429+
// reset run's start and stop time
430+
run.PreviousDuration = run.Duration()
431+
run.Started = 0
432+
run.Stopped = 0
433+
run.Status = actions_model.StatusWaiting
424434

425-
job, jobs := getRunJobs(ctx, runIndex, jobIndex)
426-
if ctx.Written() {
435+
vars, err := actions_model.GetVariablesOfRun(ctx, run)
436+
if err != nil {
437+
ctx.ServerError("GetVariablesOfRun", fmt.Errorf("get run %d variables: %w", run.ID, err))
427438
return
428439
}
429440

430-
// reset run's start and stop time when it is done
431-
if run.Status.IsDone() {
432-
run.PreviousDuration = run.Duration()
433-
run.Started = 0
434-
run.Stopped = 0
435-
run.Status = actions_model.StatusWaiting
436-
437-
vars, err := actions_model.GetVariablesOfRun(ctx, run)
438-
if err != nil {
439-
ctx.ServerError("GetVariablesOfRun", fmt.Errorf("get run %d variables: %w", run.ID, err))
441+
if run.RawConcurrency != "" {
442+
var rawConcurrency model.RawConcurrency
443+
if err := yaml.Unmarshal([]byte(run.RawConcurrency), &rawConcurrency); err != nil {
444+
ctx.ServerError("UnmarshalRawConcurrency", fmt.Errorf("unmarshal raw concurrency: %w", err))
440445
return
441446
}
442447

443-
if run.RawConcurrency != "" {
444-
var rawConcurrency model.RawConcurrency
445-
if err := yaml.Unmarshal([]byte(run.RawConcurrency), &rawConcurrency); err != nil {
446-
ctx.ServerError("UnmarshalRawConcurrency", fmt.Errorf("unmarshal raw concurrency: %w", err))
447-
return
448-
}
449-
450-
err = actions_service.EvaluateRunConcurrencyFillModel(ctx, run, &rawConcurrency, vars)
451-
if err != nil {
452-
ctx.ServerError("EvaluateRunConcurrencyFillModel", err)
453-
return
454-
}
455-
456-
run.Status, err = actions_service.PrepareToStartRunWithConcurrency(ctx, run)
457-
if err != nil {
458-
ctx.ServerError("PrepareToStartRunWithConcurrency", err)
459-
return
460-
}
461-
}
462-
if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration", "status", "concurrency_group", "concurrency_cancel"); err != nil {
463-
ctx.ServerError("UpdateRun", err)
448+
err = actions_service.EvaluateRunConcurrencyFillModel(ctx, run, &rawConcurrency, vars)
449+
if err != nil {
450+
ctx.ServerError("EvaluateRunConcurrencyFillModel", err)
464451
return
465452
}
466453

467-
if err := run.LoadAttributes(ctx); err != nil {
468-
ctx.ServerError("run.LoadAttributes", err)
454+
run.Status, err = actions_service.PrepareToStartRunWithConcurrency(ctx, run)
455+
if err != nil {
456+
ctx.ServerError("PrepareToStartRunWithConcurrency", err)
469457
return
470458
}
471-
notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
459+
}
460+
if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration", "status", "concurrency_group", "concurrency_cancel"); err != nil {
461+
ctx.ServerError("UpdateRun", err)
462+
return
463+
}
464+
465+
if err := run.LoadAttributes(ctx); err != nil {
466+
ctx.ServerError("run.LoadAttributes", err)
467+
return
468+
}
469+
notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
470+
471+
job, jobs := getRunJobs(ctx, runIndex, jobIndex)
472+
if ctx.Written() {
473+
return
472474
}
473475

474476
isRunBlocked := run.Status == actions_model.StatusBlocked
@@ -501,7 +503,7 @@ func Rerun(ctx *context_module.Context) {
501503

502504
func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shouldBlock bool) error {
503505
status := job.Status
504-
if !status.IsDone() || !job.Run.Status.IsDone() {
506+
if !status.IsDone() {
505507
return nil
506508
}
507509

snap/snapcraft.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ parts:
6868
override-build: |
6969
set -x
7070
sed -i 's/os.Getuid()/1/g' modules/setting/setting.go
71+
npm install -g pnpm
7172
TAGS="bindata sqlite sqlite_unlock_notify pam cert" make build
7273
install -D gitea "${SNAPCRAFT_PART_INSTALL}/gitea"
7374
cp -r options "${SNAPCRAFT_PART_INSTALL}/"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package integration
5+
6+
import (
7+
"fmt"
8+
"net/http"
9+
"net/url"
10+
"testing"
11+
12+
auth_model "code.gitea.io/gitea/models/auth"
13+
repo_model "code.gitea.io/gitea/models/repo"
14+
"code.gitea.io/gitea/models/unittest"
15+
user_model "code.gitea.io/gitea/models/user"
16+
17+
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
18+
)
19+
20+
func TestActionsRerun(t *testing.T) {
21+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
22+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
23+
session := loginUser(t, user2.Name)
24+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
25+
26+
apiRepo := createActionsTestRepo(t, token, "actions-rerun", false)
27+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
28+
httpContext := NewAPITestContext(t, user2.Name, repo.Name, auth_model.AccessTokenScopeWriteRepository)
29+
defer doAPIDeleteRepository(httpContext)(t)
30+
31+
runner := newMockRunner()
32+
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
33+
34+
wfTreePath := ".gitea/workflows/actions-rerun-workflow-1.yml"
35+
wfFileContent := `name: actions-rerun-workflow-1
36+
on:
37+
push:
38+
paths:
39+
- '.gitea/workflows/actions-rerun-workflow-1.yml'
40+
jobs:
41+
job1:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- run: echo 'job1'
45+
job2:
46+
runs-on: ubuntu-latest
47+
needs: [job1]
48+
steps:
49+
- run: echo 'job2'
50+
`
51+
52+
opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create"+wfTreePath, wfFileContent)
53+
createWorkflowFile(t, token, user2.Name, repo.Name, wfTreePath, opts)
54+
55+
// fetch and exec job1
56+
job1Task := runner.fetchTask(t)
57+
_, _, run := getTaskAndJobAndRunByTaskID(t, job1Task.Id)
58+
runner.execTask(t, job1Task, &mockTaskOutcome{
59+
result: runnerv1.Result_RESULT_SUCCESS,
60+
})
61+
// RERUN-FAILURE: the run is not done
62+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/rerun", user2.Name, repo.Name, run.Index), map[string]string{
63+
"_csrf": GetUserCSRFToken(t, session),
64+
})
65+
session.MakeRequest(t, req, http.StatusBadRequest)
66+
// fetch and exec job2
67+
job2Task := runner.fetchTask(t)
68+
runner.execTask(t, job2Task, &mockTaskOutcome{
69+
result: runnerv1.Result_RESULT_SUCCESS,
70+
})
71+
72+
// RERUN-1: rerun the run
73+
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/rerun", user2.Name, repo.Name, run.Index), map[string]string{
74+
"_csrf": GetUserCSRFToken(t, session),
75+
})
76+
session.MakeRequest(t, req, http.StatusOK)
77+
// fetch and exec job1
78+
job1TaskR1 := runner.fetchTask(t)
79+
runner.execTask(t, job1TaskR1, &mockTaskOutcome{
80+
result: runnerv1.Result_RESULT_SUCCESS,
81+
})
82+
// fetch and exec job2
83+
job2TaskR1 := runner.fetchTask(t)
84+
runner.execTask(t, job2TaskR1, &mockTaskOutcome{
85+
result: runnerv1.Result_RESULT_SUCCESS,
86+
})
87+
88+
// RERUN-2: rerun job1
89+
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/rerun", user2.Name, repo.Name, run.Index, 0), map[string]string{
90+
"_csrf": GetUserCSRFToken(t, session),
91+
})
92+
session.MakeRequest(t, req, http.StatusOK)
93+
// job2 needs job1, so rerunning job1 will also rerun job2
94+
// fetch and exec job1
95+
job1TaskR2 := runner.fetchTask(t)
96+
runner.execTask(t, job1TaskR2, &mockTaskOutcome{
97+
result: runnerv1.Result_RESULT_SUCCESS,
98+
})
99+
// fetch and exec job2
100+
job2TaskR2 := runner.fetchTask(t)
101+
runner.execTask(t, job2TaskR2, &mockTaskOutcome{
102+
result: runnerv1.Result_RESULT_SUCCESS,
103+
})
104+
105+
// RERUN-3: rerun job2
106+
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/rerun", user2.Name, repo.Name, run.Index, 1), map[string]string{
107+
"_csrf": GetUserCSRFToken(t, session),
108+
})
109+
session.MakeRequest(t, req, http.StatusOK)
110+
// only job2 will rerun
111+
// fetch and exec job2
112+
job2TaskR3 := runner.fetchTask(t)
113+
runner.execTask(t, job2TaskR3, &mockTaskOutcome{
114+
result: runnerv1.Result_RESULT_SUCCESS,
115+
})
116+
runner.fetchNoTask(t)
117+
})
118+
}

0 commit comments

Comments
 (0)