Skip to content

Commit c120f91

Browse files
authored
Merge pull request #514 from aryan9600/push-refspec
git: add push.refspec to push using a refspec
2 parents e127374 + f7c5f69 commit c120f91

File tree

6 files changed

+364
-81
lines changed

6 files changed

+364
-81
lines changed

api/v1beta1/git.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ type PushSpec struct {
8585
// Branch specifies that commits should be pushed to the branch
8686
// named. The branch is created using `.spec.checkout.branch` as the
8787
// starting point, if it doesn't already exist.
88-
// +required
89-
Branch string `json:"branch"`
88+
// +optional
89+
Branch string `json:"branch,omitempty"`
90+
91+
// Refspec specifies the Git Refspec to use for a push operation.
92+
// If both Branch and Refspec are provided, then the commit is pushed
93+
// to the branch and also using the specified refspec.
94+
// For more details about Git Refspecs, see:
95+
// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
96+
// +optional
97+
Refspec string `json:"refspec,omitempty"`
9098
}

config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,13 @@ spec:
135135
to the branch named. The branch is created using `.spec.checkout.branch`
136136
as the starting point, if it doesn't already exist.
137137
type: string
138-
required:
139-
- branch
138+
refspec:
139+
description: 'Refspec specifies the Git Refspec to use for
140+
a push operation. If both Branch and Refspec are provided,
141+
then the commit is pushed to the branch and also using the
142+
specified refspec. For more details about Git Refspecs,
143+
see: https://git-scm.com/book/en/v2/Git-Internals-The-Refspec'
144+
type: string
140145
type: object
141146
required:
142147
- commit

docs/api/v1beta1/image-automation.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,28 @@ string
638638
</em>
639639
</td>
640640
<td>
641+
<em>(Optional)</em>
641642
<p>Branch specifies that commits should be pushed to the branch
642643
named. The branch is created using <code>.spec.checkout.branch</code> as the
643644
starting point, if it doesn&rsquo;t already exist.</p>
644645
</td>
645646
</tr>
647+
<tr>
648+
<td>
649+
<code>refspec</code><br>
650+
<em>
651+
string
652+
</em>
653+
</td>
654+
<td>
655+
<em>(Optional)</em>
656+
<p>Refspec specifies the Git Refspec to use for a push operation.
657+
If both Branch and Refspec are provided, then the commit is pushed
658+
to the branch and also using the specified refspec.
659+
For more details about Git Refspecs, see:
660+
<a href="https://git-scm.com/book/en/v2/Git-Internals-The-Refspec">https://git-scm.com/book/en/v2/Git-Internals-The-Refspec</a></p>
661+
</td>
662+
</tr>
646663
</tbody>
647664
</table>
648665
</div>

docs/spec/v1beta1/imageupdateautomations.md

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ spec:
398398
name: fluxcdbot
399399
```
400400
There are over 70 available functions. Some of them are defined by the [Go template language](https://pkg.go.dev/text/template) itself. Most of the others are part of the [Sprig template library](http://masterminds.github.io/sprig/).
401+
401402
### Push
402403

403404
The optional `push` field defines how commits are pushed to the origin.
@@ -408,17 +409,29 @@ type PushSpec struct {
408409
// Branch specifies that commits should be pushed to the branch
409410
// named. The branch is created using `.spec.checkout.branch` as the
410411
// starting point, if it doesn't already exist.
411-
// +required
412-
Branch string `json:"branch"`
412+
// +optional
413+
Branch string `json:"branch,omitempty"`
414+
415+
// Refspec specifies the Git Refspec to use for a push operation.
416+
// If both Branch and Refspec are provided, then the commit is pushed
417+
// to the branch and also using the specified refspec.
418+
// For more details about Git Refspecs, see:
419+
// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
420+
// +optional
421+
Refspec string `json:"refspec,omitempty"`
413422
}
414423
```
415424

416-
If `push` is not present, commits are made on the branch given in `.spec.git.checkout.branch` and
425+
If `.push` is not present, commits are made on the branch given in `.spec.git.checkout.branch` and
417426
pushed to the same branch at the origin. If `.spec.git.checkout` is not present, it will fall back
418427
to the branch given in the `GitRepository` referenced by `.spec.sourceRef`. If none of these yield a
419428
branch name, the automation will fail.
420429

421-
When `push` is present, the `branch` field specifies a branch to push to at the origin. The branch
430+
If `.push.refspec` is present, the refspec specified is used to perform the push operation.
431+
An example of a valid refspec is `refs/heads/branch:refs/heads/branch`. This allows users to
432+
push to an arbitary destination reference.
433+
434+
If `.push.branch` is present, the specified branch is pushed to at the origin. The branch
422435
will be created locally if it does not already exist, starting from the checkout branch. If it does
423436
already exist, it will be overwritten with the cloned version plus the changes made by the
424437
controller. Alternatively, force push can be disabled by starting the controller with `--feature-gates=GitForcePushBranch=false`,
@@ -427,6 +440,16 @@ Note that without force push in push branches, if the target branch is stale, th
427440
be able to conclude the operation and will consistently fail until the branch is either deleted or
428441
refreshed.
429442

443+
If both `.push.refspec` and `.push.branch` are specified, then the reconciler will perform
444+
two push operations, one to the specified branch and another using the specified refspec.
445+
This is particularly useful for working with Gerrit servers. For more information about this,
446+
please refer to the [Gerrit](#Gerrit) section.
447+
448+
**Note:** If both `.push.refspec` and `.push.branch` are essentially equal to
449+
each other (for e.g.: `.push.refspec: refs/heads/main:refs/heads/main` and
450+
`.push.branch: main`), then the reconciler might fail to perform the second push
451+
operation and error out with an `already up-to-date` error.
452+
430453
In the following snippet, updates will be pushed as commits to the branch `auto`, and when that
431454
branch does not exist at the origin, it will be created locally starting from the branch `main`, and
432455
pushed:
@@ -441,6 +464,102 @@ spec:
441464
branch: auto
442465
```
443466
467+
In the following snippet, updates and commits will be made on the `main` branch locally.
468+
The commits will be then pushed using the `refs/heads/main:refs/heads/auto` refspec:
469+
470+
```yaml
471+
spec:
472+
git:
473+
checkout:
474+
ref:
475+
branch: main
476+
push:
477+
refspec: refs/heads/main:refs/heads/auto
478+
```
479+
480+
#### Gerrit
481+
482+
483+
[Gerrit](https://www.gerritcodereview.com/) operates differently from a
484+
standard Git server. Rather than sending individual commits to a branch,
485+
all changes are bundled into a single commit. This commit requires a distinct
486+
identifier separate from the commit SHA. Additionally, instead of initiating
487+
a Pull Request between branches, the commit is pushed using a refspec:
488+
`HEAD:refs/for/main`.
489+
490+
As the image-automation-controller is primarily designed to work with
491+
standard Git servers, these special characteristics necessitate a few
492+
workarounds. The following is an example configuration that works
493+
well with Gerrit:
494+
495+
```yaml
496+
spec:
497+
git:
498+
checkout:
499+
ref:
500+
branch: main
501+
commit:
502+
author:
503+
email: flux@localdomain
504+
name: flux
505+
messageTemplate: |
506+
Perform automatic image update
507+
508+
Automation name: {{ .AutomationObject }}
509+
510+
Files:
511+
{{ range $filename, $_ := .Updated.Files -}}
512+
- {{ $filename }}
513+
{{ end }}
514+
Objects:
515+
{{ range $resource, $_ := .Updated.Objects -}}
516+
- {{ $resource.Kind }} {{ $resource.Name }}
517+
{{ end }}
518+
Images:
519+
{{ range .Updated.Images -}}
520+
- {{ . }}
521+
{{ end }}
522+
{{- $ChangeId := .AutomationObject -}}
523+
{{- $ChangeId = printf "%s%s" $ChangeId ( .Updated.Files | toString ) -}}
524+
{{- $ChangeId = printf "%s%s" $ChangeId ( .Updated.Objects | toString ) -}}
525+
{{- $ChangeId = printf "%s%s" $ChangeId ( .Updated.Images | toString ) }}
526+
Change-Id: {{ printf "I%s" ( sha256sum $ChangeId | trunc 40 ) }}
527+
push:
528+
branch: auto
529+
refspec: refs/heads/auto:refs/heads/main
530+
```
531+
532+
This instructs the image-automation-controller to clone the repository using the
533+
`main` branch but execute its update logic and commit with the provided message
534+
template on the `auto` branch. Commits are then pushed to the `auto` branch,
535+
followed by pushing the `HEAD` of the `auto` branch to the `HEAD` of the remote
536+
`main` branch. The message template ensures the inclusion of a [Change-Id](https://gerrit-review.googlesource.com/Documentation/concept-changes.html#change-id)
537+
at the bottom of the commit message.
538+
539+
The initial branch push aims to prevent multiple
540+
[Patch Sets](https://gerrit-review.googlesource.com/Documentation/concept-patch-sets.html).
541+
If we exclude `.push.branch` and only specify
542+
`.push.refspec: refs/heads/main:refs/heads/main`, the desired [Change](https://gerrit-review.googlesource.com/Documentation/concept-changes.html)
543+
can be created as intended. However, when the controller freshly clones the
544+
`main` branch while a Change is open, it executes its update logic on `main`,
545+
leading to new commits being pushed with the same changes to the existing open
546+
Change. Specifying `.push.branch` circumvents this by instructing the controller
547+
to apply the update logic to the `auto` branch, already containing the desired
548+
commit. This approach is also recommended in the
549+
[Gerrit documentation](https://gerrit-review.googlesource.com/Documentation/intro-gerrit-walkthrough-github.html#create-change).
550+
551+
Another thing to note is the syntax of `.push.refspec`. Instead of it being
552+
`HEAD:refs/for/main`, commonly used by Gerrit users, we specify the full
553+
refname `refs/heads/auto` in the source part of the refpsec.
554+
555+
**Note:** A known limitation of using the image-automation-controller with
556+
Gerrit involves handling multiple concurrent Changes. This is due to the
557+
calculation of the Change-Id, relying on factors like file names and image
558+
tags. If the controller introduces a new file or modifies a previously updated
559+
image tag to a different one, it leads to a distinct Change-Id for the commit.
560+
Consequently, this action will trigger the creation of an additional Change,
561+
even when an existing Change containing outdated modifications remains open.
562+
444563
## Update strategy
445564

446565
The `.spec.update` field specifies how to carry out updates on the git repository. There is one

0 commit comments

Comments
 (0)