Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Stages that write to the same branch do not write to the same files.
| `targetBranch` | `string` | N | The branch to push to in the remote repository. Mutually exclusive with `generateTargetBranch=true`. If neither of these is provided, the target branch will be the same as the branch currently checked out in the working tree. |
| `maxAttempts` | `int32` | N | The maximum number of attempts to make when pushing to the remote repository. Default is 50. |
| `generateTargetBranch` | `boolean` | N | Whether to push to a remote branch named like `kargo/promotion/<promotionName>`. If such a branch does not already exist, it will be created. A value of 'true' is mutually exclusive with `targetBranch`. If neither of these is provided, the target branch will be the currently checked out branch. This option is useful when a subsequent promotion step will open a pull request against a Stage-specific branch. In such a case, the generated target branch pushed to by the `git-push` step can later be utilized as the source branch of the pull request. |
| `force` | `boolean` | N | Whether to force push to the target branch, overwriting any existing history. This is useful for scenarios where you need to rewrite commit history (e.g., after cleaning up commits, removing sensitive data, or synchronizing divergent histories). **Use with caution** as this will overwrite any commits that exist on the remote branch but not in your local branch. When `force: true`, the step will skip the pull/rebase operation to allow overwriting remote history. Default is `false`. |
| `provider` | `string` | N | The name of the Git provider to use. Currently 'azure', 'bitbucket', 'gitea', 'github', and 'gitlab' are supported. Kargo will try to infer the provider if it is not explicitly specified. This setting does not affect the push operation but helps generate the correct [`commitURL` output](#output) when working with repositories where the provider cannot be automatically determined, such as self-hosted instances. |

## Output
Expand Down Expand Up @@ -95,3 +96,4 @@ steps:
generateTargetBranch: true
# Open a PR and wait for it to be merged or closed...
```

8 changes: 8 additions & 0 deletions pkg/promotion/runner/builtin/git_pusher.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,22 @@ func (g *gitPushPusher) run(
// Attempt to rebase on top of the state of the remote branch to help
// avoid conflicts.
PullRebase: true,
// Allow force push if explicitly requested in config
Force: cfg.Force,
}
// If we're supposed to generate a target branch name, do so.
if cfg.GenerateTargetBranch {
// TargetBranch and GenerateTargetBranch are mutually exclusive, so we're
// never overwriting a user-specified target branch here.
pushOpts.TargetBranch = fmt.Sprintf("kargo/promotion/%s", stepCtx.Promotion)
// Always force push for generated branches to ensure they can be overwritten
pushOpts.Force = true
}

// Disable pull/rebase when force pushing to allow overwriting remote history
if pushOpts.Force {
pushOpts.PullRebase = false
}
if pushOpts.TargetBranch == "" {
// If targetBranch is still empty, we want to set it to the current branch
// because we will want to return the branch that was pushed to, but we
Expand Down
14 changes: 14 additions & 0 deletions pkg/promotion/runner/builtin/git_pusher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ func Test_gitPusher_convert(t *testing.T) {
"targetBranch": "fake-branch",
},
},
{
name: "force is true",
config: promotion.Config{ // Should be completely valid
"path": "/fake/path",
"force": true,
},
},
{
name: "force is false",
config: promotion.Config{ // Should be completely valid
"path": "/fake/path",
"force": false,
},
},
}

r := newGitPusher(promotion.StepRunnerCapabilities{})
Expand Down
5 changes: 5 additions & 0 deletions pkg/promotion/runner/builtin/schemas/git-push-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
"type": "string",
"description": "The name of the Git provider to use. Currently 'azure', 'bitbucket', 'gitea', 'github', and 'gitlab' are supported. Kargo will try to infer the provider if it is not explicitly specified.",
"enum": ["azure", "bitbucket", "gitea", "github", "gitlab"]
},
"force": {
"type": "boolean",
"description": "Whether to force push to the target branch, overwriting any existing history. This is useful for scenarios where you want to completely replace the branch content (e.g., pushing rendered manifests that don't depend on previous state). Use with caution as this will overwrite any commits that exist on the remote branch but not in your local branch. When true, the step will skip the pull/rebase operation to allow overwriting remote history.",
"default": false
}
},
"oneOf": [
Expand Down
5 changes: 5 additions & 0 deletions pkg/x/promotion/runner/builtin/zz_config_types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions ui/src/gen/directives/git-push-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
"github",
"gitlab"
]
},
"force": {
"type": "boolean",
"description": "Whether to force push to the target branch, overwriting any existing history. This is useful for scenarios where you want to completely replace the branch content (e.g., pushing rendered manifests that don't depend on previous state). Use with caution as this will overwrite any commits that exist on the remote branch but not in your local branch. When true, the step will skip the pull/rebase operation to allow overwriting remote history.",
"default": false
}
}
}