···3737 TriggerUser *user_model.User `xorm:"-"`
3838 ScheduleID int64
3939 Ref string `xorm:"index"` // the commit/tag/… that caused the run
4040+ IsRefDeleted bool `xorm:"-"`
4041 CommitSHA string
4142 IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
4243 NeedApproval bool // may need approval if it's a fork pull request
···72727373 url := fmt.Sprintf("%s/objects/batch", c.endpoint)
74747575- request := &BatchRequest{operation, c.transferNames(), nil, objects}
7575+ // `ref` is an "optional object describing the server ref that the objects belong to"
7676+ // but some (incorrect) lfs servers require it, so maybe adding an empty ref here doesn't break the correct ones.
7777+ // https://github.com/git-lfs/git-lfs/blob/a32a02b44bf8a511aa14f047627c49e1a7fd5021/docs/api/batch.md?plain=1#L37
7878+ request := &BatchRequest{operation, c.transferNames(), &Reference{}, objects}
7679 payload := new(bytes.Buffer)
7780 err := json.NewEncoder(payload).Encode(request)
7881 if err != nil {
···236239 req.Header.Set(key, value)
237240 }
238241 req.Header.Set("Accept", AcceptHeader)
242242+ req.Header.Set("User-Agent", UserAgentHeader)
239243240244 return req, nil
241245}
+5-1
modules/lfs/shared.go
···1414const (
1515 // MediaType contains the media type for LFS server requests
1616 MediaType = "application/vnd.git-lfs+json"
1717- // Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
1717+ // AcceptHeader Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
1818 AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
1919+ // UserAgentHeader Add User-Agent for gitea's self-implemented lfs client,
2020+ // and the version is consistent with the latest version of git lfs can be avoided incompatibilities.
2121+ // Some lfs servers will check this
2222+ UserAgentHeader = "git-lfs/3.6.0 (Forgejo)"
1923)
20242125// BatchRequest contains multiple requests processed in one batch operation.
+10
modules/structs/repo.go
···290290 OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
291291}
292292293293+// UpdateBranchRepoOption options when updating a branch in a repository
294294+// swagger:model
295295+type UpdateBranchRepoOption struct {
296296+ // New branch name
297297+ //
298298+ // required: true
299299+ // unique: true
300300+ Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"`
301301+}
302302+293303// TransferRepoOption options when transfer a repository's ownership
294304// swagger:model
295305type TransferRepoOption struct {
+4
release-notes/6271.md
···11+fix: [commit](https://codeberg.org/forgejo/forgejo/commit/96a7f0a3f065c5db8fdf352c93c8367e24d259de) Fix missing outputs for jobs with matrix
22+fix: [commit](https://codeberg.org/forgejo/forgejo/commit/2b5c69c451a684b20119e2521dc23734c7869241) Detect whether action view branch was deleted
33+feat: [commit](https://codeberg.org/forgejo/forgejo/commit/b0d6a7f07bff836190a8e87fe5645d5557893e32) Implement update branch API
44+fix: [commit](https://codeberg.org/forgejo/forgejo/commit/bf934c96c92d643678ac7a18697b6563bc9d20a5) Add standard-compliant route to serve outdated R packages
+16
routers/api/actions/runner/main_test.go
···11+// Copyright 2024 The Gitea Authors. All rights reserved.
22+// SPDX-License-Identifier: MIT
33+44+package runner
55+66+import (
77+ "testing"
88+99+ "code.gitea.io/gitea/models/unittest"
1010+1111+ _ "code.gitea.io/gitea/models/forgefed"
1212+)
1313+1414+func TestMain(m *testing.M) {
1515+ unittest.MainTest(m)
1616+}
+44-16
routers/api/actions/runner/utils.go
···162162 return nil, fmt.Errorf("FindRunJobs: %w", err)
163163 }
164164165165- ret := make(map[string]*runnerv1.TaskNeed, len(needs))
165165+ jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
166166 for _, job := range jobs {
167167- if !needs.Contains(job.JobID) {
167167+ jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
168168+ }
169169+170170+ ret := make(map[string]*runnerv1.TaskNeed, len(needs))
171171+ for jobID, jobsWithSameID := range jobIDJobs {
172172+ if !needs.Contains(jobID) {
168173 continue
169174 }
170170- if job.TaskID == 0 || !job.Status.IsDone() {
171171- // it shouldn't happen, or the job has been rerun
172172- continue
173173- }
174174- outputs := make(map[string]string)
175175- got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
176176- if err != nil {
177177- return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
178178- }
179179- for _, v := range got {
180180- outputs[v.OutputKey] = v.OutputValue
175175+ var jobOutputs map[string]string
176176+ for _, job := range jobsWithSameID {
177177+ if job.TaskID == 0 || !job.Status.IsDone() {
178178+ // it shouldn't happen, or the job has been rerun
179179+ continue
180180+ }
181181+ got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
182182+ if err != nil {
183183+ return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
184184+ }
185185+ outputs := make(map[string]string, len(got))
186186+ for _, v := range got {
187187+ outputs[v.OutputKey] = v.OutputValue
188188+ }
189189+ if len(jobOutputs) == 0 {
190190+ jobOutputs = outputs
191191+ } else {
192192+ jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
193193+ }
181194 }
182182- ret[job.JobID] = &runnerv1.TaskNeed{
183183- Outputs: outputs,
184184- Result: runnerv1.Result(job.Status),
195195+ ret[jobID] = &runnerv1.TaskNeed{
196196+ Outputs: jobOutputs,
197197+ Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)),
185198 }
186199 }
187200188201 return ret, nil
189202}
203203+204204+// mergeTwoOutputs merges two outputs from two different ActionRunJobs
205205+// Values with the same output name may be overridden. The user should ensure the output names are unique.
206206+// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
207207+func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
208208+ ret := make(map[string]string, len(o1))
209209+ for k1, v1 := range o1 {
210210+ if len(v1) > 0 {
211211+ ret[k1] = v1
212212+ } else {
213213+ ret[k1] = o2[k1]
214214+ }
215215+ }
216216+ return ret
217217+}
···166166 os.Unsetenv("GIT_COMMITTER_EMAIL")
167167 os.Unsetenv("GIT_COMMITTER_DATE")
168168169169+ // Avoid loading the default system config. On MacOS, this config
170170+ // sets the osxkeychain credential helper, which will cause tests
171171+ // to freeze with a dialog.
172172+ os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
173173+169174 err := unittest.InitFixtures(
170175 unittest.FixturesOptions{
171176 Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),