Skip to content

Improve accuracy of detecting branch names for repos checked out in detached HEAD state#500

Merged
nikita-tkachenko-datadog merged 4 commits into
masterfrom
nikita-tkachenko/git-detached-state
Jan 29, 2025
Merged

Improve accuracy of detecting branch names for repos checked out in detached HEAD state#500
nikita-tkachenko-datadog merged 4 commits into
masterfrom
nikita-tkachenko/git-detached-state

Conversation

@nikita-tkachenko-datadog

@nikita-tkachenko-datadog nikita-tkachenko-datadog commented Jan 23, 2025

Copy link
Copy Markdown
Collaborator

Requirements for Contributing to this repository

  • Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
  • The pull request must only fix one issue at the time.
  • The pull request must update the test suite to demonstrate the changed functionality.
  • After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see CONTRIBUTING.

What does this PR do?

Improves the accuracy of detecting Git branch names:

  • use BRANCH_NAME environment variable (automatically set by Jenkins for some pipelines) when GIT_BRANCH is not available
  • when a repo is checked out in "detached HEAD" state, and current commit is the head of multiple branches, use branch name available in environment variables as a hint to determine which branch is "correct"

Description of the Change

Alternate Designs

Possible Drawbacks

Verification Process

Additional Notes

Release Notes

Review checklist (to be filled by reviewers)

  • Feature or bug fix MUST have appropriate tests (unit, integration, etc...)
  • PR title must be written as a CHANGELOG entry (see why)
  • Files changes must correspond to the primary purpose of the PR as described in the title (small unrelated changes should have their own PR)
  • PR must have one changelog/ label attached. If applicable it should have the backward-incompatible label attached.
  • PR should not have do-not-merge/ label attached.
  • If Applicable, issue must have kind/ and severity/ labels attached at least.

@nikita-tkachenko-datadog nikita-tkachenko-datadog added the changelog/Fixed Fixed features results into a bug fix version bump label Jan 23, 2025
@nikita-tkachenko-datadog
nikita-tkachenko-datadog marked this pull request as ready for review January 24, 2025 12:00

@drodriguezhdez drodriguezhdez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped some comments.

Comment on lines +101 to +111
for (Ref ref : repository.getRefDatabase().getRefs()) {
String refName = ref.getName();
if (Constants.HEAD.equals(refName) || GitUtils.isValidCommitSha(refName)) {
continue;
}
ObjectId refObjectId = ref.getObjectId();
if (branch.equals(refObjectId.getName())) {
return refName;
matchingBranches.add(normalizeBranch(refName));
}
}
return null;
return matchingBranches;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method now is basically iterating over all available refs in the repository. Is this expected?
I foresee issues with this. The number of iterations can be huge depending on the repository.

Also, it seems the method chooseBranch(...) is always getting the first branch in the collection.

Am I missing something?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does iterate over refs in the repo, but that was possible before the changes too, depending on where in the list the branch that we're looking for was located.

The method returns the first branch if the collection does not contain the name of the branch that is set in the environment variables (which may or may not be the case, especially with pipelines that do multiple checkouts - multi-branch pipelines, pipelines that use shared libraries, etc).

Anyway, I rewrote the logic a bit to address the comments. Now branch resolution is done inside the callback, and examining the references can be disabled with an env var.

Comment on lines +134 to +144
} else if (branches.size() > 1) {
// multiple branches point to the checked out commit,
// and none of them is the same as the branch that is set in the env vars,
// we are taking a guess at this point
LOGGER.warning("Build " + buildName + " has multiple Git branches matched: " + branches);
return branches.iterator().next();

} else if (branches.size() == 1) {
// only one branch points to the checked out commit
// (also possible that the repo was checked out properly, and not in a detached HEAD state)
return branches.iterator().next();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood correctly, here we're always returning the first branch (iterator().next()) for both cases branches.size() >= 1. If that's the case why we need to store all branches?

Also, could we simplify this block using >=1? And, should we use List<> instead of Collection<> to use get(0)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All branches were needed in order to check if the list contains the branch that is set in the env vars. I removed this method in favour of resolving the branch inside the repo callback, but the logic remained essentially the same.

final String repoUrl;
final String defaultBranch;
final String branch;
final @Nonnull Collection<String> branches;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use List<String> to have more convenient methods for random access (e.g get(0))?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote this part

* It is possible that multiple branches point to the same commit.
* In which case the logic in this method tries to use a "hint" extracted from environment variables.
*/
private static String chooseBranch(@Nonnull Collection<String> branches, @Nullable String branchHint, String buildName) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any possibility of adding unit tests for this method?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this logic to the repo callback

@drodriguezhdez drodriguezhdez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I like the idea of adding the env var to disable the ref search.
Let's document it as well on the docs.

@nikita-tkachenko-datadog
nikita-tkachenko-datadog merged commit a2646ad into master Jan 29, 2025
@nikita-tkachenko-datadog
nikita-tkachenko-datadog deleted the nikita-tkachenko/git-detached-state branch January 29, 2025 12:01
public static final String GIT_REPOSITORY_URL = "GIT_URL";
public static final String GIT_REPOSITORY_URL_ALT = "GIT_URL_1";
public static final String GIT_BRANCH = "GIT_BRANCH";
public static final String GIT_BRANCH_ALT = "BRANCH_NAME";

@usmonster usmonster Mar 16, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

In pull request builds, this can end up being "PR-123" (e.g. for a build on a branch that's part of pull request number 123). In those cases, the fallback env var CHANGE_BRANCH should be preferred over others. The precedence we generally use is def branch = env.CHANGE_BRANCH ?: env.GIT_BRANCH ?: env.BRANCH_NAME (Groovy example; though env.CHANGE_BRANCH ?: env.BRANCH_NAME is generally safe enough). Would you consider updating the logic as such? Thanks!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for highlighting this issue! The fix for it is ready and will be released the following week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/Fixed Fixed features results into a bug fix version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants