-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathref_strategy.R
150 lines (140 loc) · 5.13 KB
/
ref_strategy.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#' Determine the branch/tag to install based on feature (staging rules)
#'
#' Return the git ref (tag or branch) of the repo to install given the available branches and tags.
#'
#'
#' A ref is either a tag or branches separated by slashes of the form `name1@name2@...@nameN`.
#' Where separator is specified by `branch_sep` argument
#'
#' This function checks for an exact match for the tag if this is not found then
#' among the available branches, it searches in the order
#' `name1@name2@...@nameN`, `name2@name3@...@nameN`, `name3@name4@...@nameN`, ..., `nameN`
#'
#' @md
#' @param ref ref we want to build
#' @param available_refs data.frame with columns `ref` the names of the available refs
#' and `type` (`branch` or `tag`)
#' @param fallback_branch the default branch to try to use if no other matches found
#' @param branch_sep separator between branches in `feature`, `/` does not
#' work well with `git` because it clashes with the filesystem paths
#'
#' @return branch/tag to choose to match feature, error if no suitable branch was provided
#' with the type attribute "tag" or "branch"
#'
#' @export
#'
#' @examples
#' determine_ref(
#' "feature1",
#' data.frame(ref = c("main", "feature1"), type = "branch")
#' ) == structure("feature1", type = "branch")
#'
#' determine_ref(
#' "feature1@devel",
#' data.frame(ref = c("main", "devel", "feature1"), type = "branch")
#' ) == structure("devel", type = "branch")
#'
#' determine_ref(
#' ref = "fix1@feature1@devel",
#' available_refs = data.frame(
#' ref = c(
#' "main", "devel", "feature1", "feature1@devel",
#' "fix1@feature1@devel", "fix1"
#' ),
#' type = "branch"
#' )
#' ) == structure("fix1@feature1@devel", type = "branch")
#'
#' determine_ref(
#' "fix1@feature1@devel",
#' data.frame(
#' ref = c("main", "devel", "feature1", "feature1@devel", "fix1"),
#' type = "branch"
#' )
#' ) == structure("feature1@devel", type = "branch")
#'
#' determine_ref(
#' "fix1@feature1@devel",
#' data.frame(ref = c("main", "devel", "feature1", "fix1"), type = "branch")
#' ) == structure("devel", type = "branch")
#'
#' determine_ref("feature1@release", data.frame(ref = c("main", "devel"), type = "branch"))
#'
#' # error because neither `feature1@release` nor `release` branch exists
#' # determine_ref("feature1@release", data.frame(ref = c("master", "devel"), type = "branch"))
#'
#' # tag examples
#' determine_ref(
#' "v0.1",
#' data.frame(ref = c("main", "devel", "feature1", "v0.1"), type = c(rep("branch", 3), "tag"))
#' ) == structure("v0.1", type = "tag")
#'
#' determine_ref(
#' "v0.2",
#' data.frame(ref = c("main", "devel", "feature1", "v0.1"), type = c(rep("branch", 3), "tag"))
#' ) == structure("main", type = "branch")
#'
determine_ref <- function(ref, available_refs, fallback_branch = "main", branch_sep = "@") {
stopifnot(
is_non_empty_char(ref),
is.data.frame(available_refs),
colnames(available_refs) == c("ref", "type"),
is_non_empty_char(branch_sep)
)
# check for tag
if (ref %in% available_refs$ref[available_refs$type == "tag"]) {
attr(ref, "type") <- "tag"
return(ref)
}
# if tag not found now look at branch strategy
available_branches <- available_refs$ref[available_refs$type == "branch"]
els <- unlist(strsplit(ref, branch_sep, fixed = TRUE))
branches_to_check <- union(
rev(Reduce(function(x, y) paste0(y, branch_sep, x), rev(els), accumulate = TRUE)),
fallback_branch
)
for (branch in branches_to_check) {
if (branch %in% available_branches) {
attr(branch, "type") <- "branch"
return(branch)
}
}
stop(
"Available refs '", toString(available_refs$ref), "' must include at least one of '",
toString(branches_to_check), "'"
)
}
# infer the ref if it is null
infer_ref_from_branch <- function(project = ".") {
checkmate::assert_directory_exists(project)
return(get_current_branch(project))
}
check_ref_consistency <- function(ref, project = ".", remote_name = "origin", fallback_branch = "main") {
checkmate::assert_directory_exists(project)
current_branch <- get_current_branch(project)
# if in detached HEAD then we ignore the ref_consistency check (i.e. so gitlab
# automation does not throw a warning)
if (!is.null(current_branch)) {
expected_current_branch <- determine_ref(ref,
available_refs = available_references(project, remote_name = remote_name),
fallback_branch = fallback_branch
)
if (current_branch != expected_current_branch) {
warning(
"Branch ", ref, " would match ", expected_current_branch, " in project ", project,
", but currently checked out branch is ", current_branch
)
}
}
}
# return a dataframe with columns ref (git tag or branch name), type ("branch" or "tag")
available_references <- function(repo = ".", remote_name, branch_flag = "remote") {
branches <- names(git2r::branches(repo = repo, flags = branch_flag))
branches <- setdiff(gsub(paste0(remote_name, "/"), "", branches, fixed = TRUE), "HEAD")
refs <- data.frame(ref = branches, type = "branch")
tags <- names(git2r::tags(repo))
if (length(tags) > 0) {
refs <- rbind(refs, data.frame(ref = tags, type = "tag"))
}
refs
}