Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ By default, the Step performs a shallow clone in most cases (fetching only the l

## 🧩 Get started

Add this step directly to your workflow in the [Bitrise Workflow Editor](https://devcenter.bitrise.io/steps-and-workflows/steps-and-workflows-index/).
Add this step directly to your workflow in the [Bitrise Workflow Editor](https://docs.bitrise.io/en/bitrise-ci/workflows-and-pipelines/steps/adding-steps-to-a-workflow.html).

You can also run this step directly with [Bitrise CLI](https://github.com/bitrise-io/bitrise).

Expand All @@ -50,6 +50,8 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris
| Key | Description | Flags | Default |
| --- | --- | --- | --- |
| `merge_pr` | This only applies to builds triggered by pull requests. Options: - `yes`: Depending on the information in the build trigger, either fetches the PR merge ref or creates the merged state locally. - `no`: Checks out the head of the PR branch without merging it into the destination branch. | | `yes` |
| `git_http_username` | Username for establishing an HTTP(S) connection to the repository. This is typically required when the repository is private. | sensitive | `$GIT_HTTP_USERNAME` |
| `git_http_password` | Personal access token (or password) for establishing an HTTP(S) connection to the repository. This is typically required when the repository is private. | sensitive | `$GIT_HTTP_PASSWORD` |
| `clone_into_dir` | Local directory where the repository is cloned | required | `$BITRISE_SOURCE_DIR` |
| `clone_depth` | Limit fetching to the specified number of commits. By default, the Step tries to do a shallow clone (depth of 1) if it's possible based on the build trigger parameters. If it's not possible, it applies a low depth value, unless another value is specified here. It's not recommended to define this input because a shallow clone ensures fast clone times. Examples of when you want to override the clone depth: - A Step in the workflow reads the commit history in order to generate a changelog - A Step in the workflow runs a git diff against a previous commit Use the value `-1` to disable the depth limit completely and fetch the entire repo history. | | |
| `update_submodules` | Update registered submodules to match what the superproject expects. If set to `no`, `git fetch` calls will use the `--no-recurse-submodules` flag. | | `yes` |
Expand All @@ -66,10 +68,9 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris
| `pull_request_unverified_merge_branch` | This input is the same as **Pull request merge ref**, but the provided merge ref can be potentially outdated. The Step will make an attempt to check it's validity and only use it for the checkout if it's up-to-date with the PR head. | | `$BITRISEIO_PULL_REQUEST_UNVERIFIED_MERGE_BRANCH` |
| `pull_request_head_branch` | Git ref pointing to the head of the PR branch. Even if the source of the PR is a fork, this is a reference to the destination repository. Example: `refs/pull/14/head` Note: not all Git services provide this value. | | `$BITRISEIO_PULL_REQUEST_HEAD_BRANCH` |
| `reset_repository` | Reset repository contents with `git reset --hard HEAD` and `git clean -f` before fetching. | | `No` |
| `performance_monitoring` | Prints extra performance related information for every git operation. | | `no` |
| `build_url` | Unique build URL of this build on Bitrise.io | | `$BITRISE_BUILD_URL` |
| `build_api_token` | The build's API Token for the build on Bitrise.io | sensitive | `$BITRISE_BUILD_API_TOKEN` |
| `git_http_username` | Username for establishing an HTTP(S) connection to the repository | sensitive | `$GIT_HTTP_USERNAME` |
| `git_http_password` | Personal access token (or password) for establishing an HTTP(S) connection to the repository | sensitive | `$GIT_HTTP_PASSWORD` |
</details>

<details>
Expand All @@ -91,9 +92,10 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris

We welcome [pull requests](https://github.com/bitrise-steplib/steps-git-clone/pulls) and [issues](https://github.com/bitrise-steplib/steps-git-clone/issues) against this repository.

For pull requests, work on your changes in a forked repository and use the Bitrise CLI to [run step tests locally](https://devcenter.bitrise.io/bitrise-cli/run-your-first-build/).
For pull requests, work on your changes in a forked repository and use the Bitrise CLI to [run step tests locally](https://docs.bitrise.io/en/bitrise-ci/bitrise-cli/running-your-first-local-build-with-the-cli.html).

Note: this step's end-to-end tests (defined in e2e/bitrise.yml) are working with secrets which are intentionally not stored in this repo. External contributors won't be able to run those tests. Don't worry, if you open a PR with your contribution, we will help with running tests and make sure that they pass.

Learn more about developing steps:

- [Create your own step](https://devcenter.bitrise.io/contributors/create-your-own-step/)
- [Testing your Step](https://devcenter.bitrise.io/contributors/testing-and-versioning-your-steps/)
- [Create your own step](https://docs.bitrise.io/en/bitrise-ci/workflows-and-pipelines/developing-your-own-bitrise-step/developing-a-new-step.html)
5 changes: 4 additions & 1 deletion bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ workflows:

generate_readme:
steps:
- git::https://github.com/bitrise-steplib/steps-readme-generator.git@main: { }
- git::https://github.com/bitrise-steplib/steps-readme-generator.git@main:
inputs:
- contrib_section: docs/contribution.md
- example_section: docs/examples.md
1 change: 1 addition & 0 deletions docs/contribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Note: this step's end-to-end tests (defined in e2e/bitrise.yml) are working with secrets which are intentionally not stored in this repo. External contributors won't be able to run those tests. Don't worry, if you open a PR with your contribution, we will help with running tests and make sure that they pass.
Empty file added docs/examples.md
Empty file.
12 changes: 6 additions & 6 deletions gitclone/checkout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package gitclone
import (
"fmt"

"github.com/bitrise-io/go-utils/command/git"
"github.com/bitrise-io/go-utils/log"
"github.com/bitrise-io/go-utils/v2/git"
"github.com/bitrise-steplib/steps-git-clone/gitclone/bitriseapi"
)

Expand Down Expand Up @@ -65,7 +65,7 @@ func NewParameterValidationError(msg string) error {

// checkoutStrategy is the interface an actual checkout strategy implements
type checkoutStrategy interface {
do(gitCmd git.Git, fetchOptions fetchOptions, fallback fallbackRetry) error
do(gitFactory git.Factory, fetchOptions fetchOptions, fallback fallbackRetry) error

// getBuildTriggerRef returns ref to the commit/branch/tag that triggered the build.
// For simple checkout strategies the returned ref will be HEAD (after running 'do').
Expand Down Expand Up @@ -250,7 +250,7 @@ func createCheckoutStrategy(checkoutMethod CheckoutMethod, cfg Config, patchFile

return checkoutPRMergeRef{
params: *params,
fallbackCheckout: func(gitCmd git.Git) error {
fallbackCheckout: func(gitFactory git.Factory) error {
log.Warnf("Using manual merge strategy with PR source branch")

manualMergeFallbackFetchOpts := selectFetchOptions(CheckoutPRManualMergeMethod, cfg.CloneDepth, cfg.FetchTags, cfg.UpdateSubmodules, len(cfg.SparseDirectories) != 0)
Expand All @@ -267,7 +267,7 @@ func createCheckoutStrategy(checkoutMethod CheckoutMethod, cfg Config, patchFile
}

// PR merge branch checkout falls back to PR manual merge strategy using the PR source branch
return fallbackManualMergeWithSourceBranch.do(gitCmd, manualMergeFallbackFetchOpts, manualMergeFallbackFallback)
return fallbackManualMergeWithSourceBranch.do(gitFactory, manualMergeFallbackFetchOpts, manualMergeFallbackFallback)
},
}, nil
}
Expand Down Expand Up @@ -314,7 +314,7 @@ func createCheckoutStrategy(checkoutMethod CheckoutMethod, cfg Config, patchFile

return checkoutCommit{
params: *params,
fallbackCheckout: func(gitCmd git.Git) error {
fallbackCheckout: func(gitFactory git.Factory) error {
log.Warnf("Using commit checkout strategy with PR source branch")

if cfg.Branch == "" || cfg.Commit == "" {
Expand All @@ -339,7 +339,7 @@ func createCheckoutStrategy(checkoutMethod CheckoutMethod, cfg Config, patchFile
params: *params,
}

return commitCheckoutFallbackCheckoutMethod.do(gitCmd, commitCheckoutFallbackFetchOpts, commitCheckoutFallbackFallback)
return commitCheckoutFallbackCheckoutMethod.do(gitFactory, commitCheckoutFallbackFetchOpts, commitCheckoutFallbackFallback)
},
}, nil
}
Expand Down
45 changes: 22 additions & 23 deletions gitclone/checkout_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import (
"fmt"
"strings"

"github.com/bitrise-io/go-utils/command"
"github.com/bitrise-io/go-utils/command/git"
"github.com/bitrise-io/go-utils/log"
"github.com/bitrise-io/go-utils/v2/git"
)

type fetchOptions struct {
Expand Down Expand Up @@ -38,7 +37,7 @@ const (
refsHeadsPrefix = "refs/heads/"
)

func fetch(gitCmd git.Git, remote string, ref string, options fetchOptions) error {
func fetch(gitFactory git.Factory, remote string, ref string, options fetchOptions) error {
var opts []string
opts = append(opts, jobsFlag)

Expand Down Expand Up @@ -67,11 +66,11 @@ func fetch(gitCmd git.Git, remote string, ref string, options fetchOptions) erro
branch = strings.TrimPrefix(ref, refsHeadsPrefix)
}

if err := runner.RunWithRetry(func() *command.Model {
return gitCmd.Fetch(opts...)
if err := runner.RunWithRetry(func() git.Template {
return gitFactory.Fetch(opts...)
}); err != nil {
return handleCheckoutError(
listBranches(gitCmd),
listBranches(gitFactory),
fetchFailedTag,
err,
"Fetching repository has failed",
Expand All @@ -82,15 +81,15 @@ func fetch(gitCmd git.Git, remote string, ref string, options fetchOptions) erro
return nil
}

func checkoutWithCustomRetry(gitCmd git.Git, arg string, retry fallbackRetry) error {
if cErr := runner.Run(gitCmd.Checkout(arg)); cErr != nil {
func checkoutWithCustomRetry(gitFactory git.Factory, arg string, retry fallbackRetry) error {
if cErr := runner.Run(gitFactory.Checkout(arg)); cErr != nil {
if retry != nil {
log.Warnf("Checkout failed (%s): %v", arg, cErr)
if err := retry.do(gitCmd); err != nil {
if err := retry.do(gitFactory); err != nil {
return err
}

return runner.Run(gitCmd.Checkout(arg))
return runner.Run(gitFactory.Checkout(arg))
}

return fmt.Errorf("checkout failed (%s): %w", arg, cErr)
Expand All @@ -99,21 +98,21 @@ func checkoutWithCustomRetry(gitCmd git.Git, arg string, retry fallbackRetry) er
return nil
}

func forceCheckoutRemoteBranch(gitCmd git.Git, remote string, branchRef string, fetchTraits fetchOptions) error {
func forceCheckoutRemoteBranch(gitFactory git.Factory, remote string, branchRef string, fetchTraits fetchOptions) error {
branch := strings.TrimPrefix(branchRef, refsHeadsPrefix)
if err := fetch(gitCmd, remote, branchRef, fetchTraits); err != nil {
if err := fetch(gitFactory, remote, branchRef, fetchTraits); err != nil {
wErr := fmt.Errorf("fetch branch %s: %w", branchRef, err)
return fmt.Errorf("%v: %w", wErr, errors.New("please make sure the branch still exists"))
}

remoteBranch := fmt.Sprintf("%s/%s", remote, branch)
// -B: create the branch if it doesn't exist, reset if it does
// The latter is important in persistent environments because shallow-fetching only fetches 1 commit,
// so the next run would see unrelated histories after shallow-fetching another single commit.
err := runner.Run(gitCmd.Checkout("-B", branch, remoteBranch))
err := runner.Run(gitFactory.Checkout("-B", branch, remoteBranch))
if err != nil {
return handleCheckoutError(
listBranches(gitCmd),
listBranches(gitFactory),
checkoutFailedTag,
err,
"Checkout has failed",
Expand All @@ -124,15 +123,15 @@ func forceCheckoutRemoteBranch(gitCmd git.Git, remote string, branchRef string,
return nil
}

func mergeWithCustomRetry(gitCmd git.Git, arg string, retry fallbackRetry) error {
if mErr := runner.Run(gitCmd.Merge(arg)); mErr != nil {
func mergeWithCustomRetry(gitFactory git.Factory, arg string, retry fallbackRetry) error {
if mErr := runner.Run(gitFactory.Merge(arg)); mErr != nil {
if retry != nil {
log.Warnf("Merge failed (%s): %v", arg, mErr)
if err := retry.do(gitCmd); err != nil {
if err := retry.do(gitFactory); err != nil {
return err
}

return runner.Run(gitCmd.Merge(arg))
return runner.Run(gitFactory.Merge(arg))
}

wErr := fmt.Errorf("merge failed (%s): %w", arg, mErr)
Expand All @@ -142,8 +141,8 @@ func mergeWithCustomRetry(gitCmd git.Git, arg string, retry fallbackRetry) error
return nil
}

func detachHead(gitCmd git.Git) error {
if err := runner.Run(gitCmd.Checkout("--detach")); err != nil {
func detachHead(gitFactory git.Factory) error {
if err := runner.Run(gitFactory.Checkout("--detach")); err != nil {
return newStepError(
"detach_head_failed",
fmt.Errorf("detaching head failed: %w", err),
Expand All @@ -154,6 +153,6 @@ func detachHead(gitCmd git.Git) error {
return nil
}

func deleteRef(gitCmd git.Git, ref string) error {
return runner.Run(gitCmd.UpdateRef("-d", ref))
func deleteRef(gitFactory git.Factory, ref string) error {
return runner.Run(gitFactory.UpdateRef("-d", ref))
}
14 changes: 7 additions & 7 deletions gitclone/checkout_method_pr_auto_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"fmt"
"strings"

"github.com/bitrise-io/go-utils/command/git"
"github.com/bitrise-io/go-utils/log"
"github.com/bitrise-io/go-utils/v2/git"
)

// PRDiffFileParams are parameters to check out a Merge/Pull Request (when a diff file is available)
Expand Down Expand Up @@ -35,28 +35,28 @@ type checkoutPRDiffFile struct {
patchFile string
}

func (c checkoutPRDiffFile) do(gitCmd git.Git, fetchOptions fetchOptions, fallback fallbackRetry) error {
func (c checkoutPRDiffFile) do(gitFactory git.Factory, fetchOptions fetchOptions, fallback fallbackRetry) error {
destBranchRef := refsHeadsPrefix + c.params.DestinationBranch
if err := fetch(gitCmd, originRemoteName, destBranchRef, fetchOptions); err != nil {
if err := fetch(gitFactory, originRemoteName, destBranchRef, fetchOptions); err != nil {
return fmt.Errorf("failed to fetch base branch: %w", err)
}

if err := checkoutWithCustomRetry(gitCmd, c.params.DestinationBranch, fallback); err != nil {
if err := checkoutWithCustomRetry(gitFactory, c.params.DestinationBranch, fallback); err != nil {
return err
}

if err := runner.Run(gitCmd.Apply(c.patchFile)); err != nil {
if err := runner.Run(gitFactory.Apply(c.patchFile)); err != nil {
log.Warnf("Could not apply patch (%s): %v", c.patchFile, err)
log.Warnf("Falling back to manual merge...")

if err := c.params.PRManualMergeStrategy.do(gitCmd, fetchOptions, fallback); err != nil {
if err := c.params.PRManualMergeStrategy.do(gitFactory, fetchOptions, fallback); err != nil {
return fmt.Errorf("fallback failed for applying patch (%s): %v", c.patchFile, err)
}

return nil
}

return detachHead(gitCmd)
return detachHead(gitFactory)
}

func (c checkoutPRDiffFile) getBuildTriggerRef() string {
Expand Down
28 changes: 14 additions & 14 deletions gitclone/checkout_method_pr_auto_merge_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package gitclone
import (
"fmt"
"strings"

"github.com/bitrise-io/go-utils/command/git"

"github.com/bitrise-io/go-utils/log"
"github.com/bitrise-io/go-utils/v2/git"
)

// PRMergeRefParams are parameters to check out a Merge/Pull Request's merge ref (the result of merging the 2 branches)
Expand Down Expand Up @@ -38,20 +38,20 @@ type checkoutPRMergeRef struct {
fallbackCheckout fallbackCheckoutFunc
}

type fallbackCheckoutFunc func(gitCmd git.Git) error
type fallbackCheckoutFunc func(gitFactory git.Factory) error

func (c checkoutPRMergeRef) do(gitCmd git.Git, fetchOpts fetchOptions, fallback fallbackRetry) error {
if err := c.performCheckout(gitCmd, fetchOpts, fallback); err != nil {
func (c checkoutPRMergeRef) do(gitFactory git.Factory, fetchOpts fetchOptions, fallback fallbackRetry) error {
if err := c.performCheckout(gitFactory, fetchOpts, fallback); err != nil {
if c.fallbackCheckout != nil {
log.Warnf("Failed to checkout PR merge branch: %s", err)
return c.fallbackCheckout(gitCmd)
return c.fallbackCheckout(gitFactory)
}
return err
}
return nil
}

func (c checkoutPRMergeRef) performCheckout(gitCmd git.Git, fetchOpts fetchOptions, fallback fallbackRetry) error {
func (c checkoutPRMergeRef) performCheckout(gitFactory git.Factory, fetchOpts fetchOptions, fallback fallbackRetry) error {
// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
refSpec := fmt.Sprintf("%s:%s", c.remoteMergeRef(), c.localMergeRef())

Expand All @@ -62,33 +62,33 @@ func (c checkoutPRMergeRef) performCheckout(gitCmd git.Git, fetchOpts fetchOptio
// This is caused by the remote merge and head branches being "force-pushed" by GitHub.
// To solve it we remove merge and head branch refs.
// $ git update-ref -d refs/remotes/pull/7/merge
err := deleteRef(gitCmd, c.localMergeRef())
err := deleteRef(gitFactory, c.localMergeRef())
if err != nil {
return fmt.Errorf("failed to delete ref: %w", err)
}

// $ git update-ref -d refs/remotes/pull/7/head
// If the ref does not exist, the command still exits with 0 exit code.
err = deleteRef(gitCmd, c.localHeadRef())
err = deleteRef(gitFactory, c.localHeadRef())
if err != nil {
return fmt.Errorf("failed to delete ref: %w", err)
}

//$ git fetch origin refs/remotes/pull/7/merge:refs/pull/7/merge
err = fetch(gitCmd, originRemoteName, refSpec, fetchOpts)
err = fetch(gitFactory, originRemoteName, refSpec, fetchOpts)
if err != nil {
return fmt.Errorf("failed to fetch merge ref: %w", err)
}

// Also fetch the PR head ref because the step exports outputs based on the PR head commit (see output.go)
// $ git fetch origin refs/remotes/pull/7/head:refs/pull/7/head
err = c.fetchPRHeadRef(gitCmd, fetchOpts)
err = c.fetchPRHeadRef(gitFactory, fetchOpts)
if err != nil {
return err
}

// $ git checkout refs/remotes/pull/7/merge
err = checkoutWithCustomRetry(gitCmd, c.localMergeRef(), nil)
err = checkoutWithCustomRetry(gitFactory, c.localMergeRef(), nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -116,7 +116,7 @@ func (c checkoutPRMergeRef) remoteHeadRef() string {
return fmt.Sprintf("refs/%s", c.params.HeadRef)
}

func (c checkoutPRMergeRef) fetchPRHeadRef(gitCmd git.Git, fetchOpts fetchOptions) error {
func (c checkoutPRMergeRef) fetchPRHeadRef(gitFactory git.Factory, fetchOpts fetchOptions) error {
refSpec := fmt.Sprintf("%s:%s", c.remoteHeadRef(), c.localHeadRef())
return fetch(gitCmd, originRemoteName, refSpec, fetchOpts)
return fetch(gitFactory, originRemoteName, refSpec, fetchOpts)
}
Loading