Skip to content

Commit d90c471

Browse files
authored
Merge pull request #20 from cyberark/conjurinc-ops-492
Add github issue functions with 'hub' cli
2 parents 2e80a61 + 4daad1e commit d90c471

File tree

12 files changed

+489
-22
lines changed

12 files changed

+489
-22
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ files within it's directory.
122122
<td>Git helpers</td>
123123
<td>
124124
<ol>
125+
<li><b>bl_git_available</b>: True if git binary or function is available</li>
126+
<li><b>bl_in_git_repo</b>: True if current directory is a git working directory</li>
127+
<li><b>bl_github_owner_repo</b>: returns $owner/$repo extracted from the url of the origin remote</li>
125128
<li><b>bl_repo_root</b>: Find the root of the current git repo.</li>
126129
<li><b>bl_all_files_in_repo</b>: List files tracked by git.</li>
127130
<li><b>bl_remote_latest_tag</b>: Returns the symbolic name of the latest tag from a remote.</li>
@@ -133,12 +136,28 @@ files within it's directory.
133136
<li><b>bl_cat_gittrees</b>: Returns the contents of .gittrees from the top level of the repo, excluding any comments. Fails if .gittrees is not present.</li>
134137
</ol>
135138
</td>
139+
</tr>
140+
<tr>
141+
<td><a href="github/lib">git</a></td>
142+
<td>Github Related Functions</td>
143+
<td>
144+
<ol>
145+
<li><b>bl_hub_available</b>: True if hub binary or function is available</li>
146+
<li><b>bl_hub_creds_available</b>: True if hub creds are available (file or env vars)</li>
147+
<li><b>bl_hub_check</b>: Preflight check for hub, true if git installed, in git repo, hub installed and hub creds are available</li>
148+
<li><b>bl_hub_download_latest</b>: Download latest hub binary from github and install to ~/bin or specified path</li>
149+
<li><b>bl_hub_issue_number_for_title</b>: Find the issue number for an issue from its title, searches open issues in the current repo. (current repo = workding directory, repo is found by origin remote)</li>
150+
<li><b>bl_hub_add_issue_comment</b>: Add a comment to an issue</li>
151+
<li><b>bl_hub_comment_or_create_issue</b>: Create issue if an issue matching the title doesn't exist. If a match is found, add a comment to it</li>
152+
</ol>
153+
</td>
136154
</tr>
137155
<td><a href="helpers/lib">helpers</a></td>
138156
<td>Bash scripting helpers</td>
139157
<td>
140158
<ol>
141159
<li><b>bl_die</b>: print message and exit 1</li>
160+
<li><b>bl_fail</b>: print message and return 1</li>
142161
<li><b>bl_spushd/bl_spopd</b>: Safe verisons of pushd & popd that call die if the push/pop fails, they also drop stdout. </li>
143162
<li><b>bl_is_num</b>: Check if a value is a number via regex</li>
144163
<li><b>bl_retry</b>: Retry a command until it succeeds up to a user specified maximum number of attempts. Escalating delay between attempts.</li>

filehandling/lib

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ function bl_abs_path() {
77
# generate absolute path from relative path
88
# path : relative filename
99
# return : absolute path
10+
11+
local path
12+
1013
if [[ -z "${1:-}" ]]; then
1114
path="."
1215
else

git/lib

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,43 @@
22

33
: "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}"
44

5+
6+
function bl_git_available(){
7+
type git &>/dev/null || bl_fail "Git binary not found in ${PATH}"
8+
}
9+
10+
function bl_in_git_repo(){
11+
bl_git_available
12+
git status >/dev/null || bl_die "$(pwd) is not within a git repo."
13+
}
14+
15+
function bl_github_owner_repo(){
16+
bl_in_git_repo
17+
remote="${1:-origin}"
18+
19+
git remote -v | grep -q "${remote}" || bl_die "Remote ${remote} doesn't exist for repo ${PWD}"
20+
git remote -v | grep -q "${remote}.*github" || bl_die "Remote ${remote} is not a github remote in repo ${PWD}"
21+
[[ "$(git remote -v |grep "${remote}")" =~ github.com[:/]([^ .]*) ]]
22+
echo "${BASH_REMATCH[1]}"
23+
}
24+
525
# Get the top level of a git repo
626
function bl_repo_root(){
27+
bl_in_git_repo
728
git rev-parse --show-toplevel
829
}
930

1031
# List files tracked by git
1132
function bl_all_files_in_repo(){
33+
bl_in_git_repo
1234
git ls-tree -r HEAD --name-only
1335
}
1436

1537
# Find the latest tag available at a repo url
1638
# Returns tag name, not sha
1739
function bl_remote_latest_tag(){
40+
bl_in_git_repo
41+
1842
local -r remote_url="${1}"
1943
# In ls-remote the ^{} suffix refers to a peeled/dereferenced object.
2044
# eg refs/tags/v0.0.1^{} shows the SHA of the commit that was tagged,
@@ -29,14 +53,19 @@ function bl_remote_latest_tag(){
2953

3054
# Find the SHA of the latests commit to be tagged in a remote repo
3155
function bl_remote_latest_tagged_commit(){
56+
bl_in_git_repo
57+
3258
local -r remote="${1}"
3359
local -r tag="$(bl_remote_latest_tag "${remote}")"
3460
git ls-remote "${remote}" | awk "/refs\/tags\/${tag}\^/{print \$1}"
3561
}
3662

3763
function bl_remote_sha_for_ref(){
64+
bl_in_git_repo
65+
3866
local -r remote="${1}"
3967
local -r ref="${2}"
68+
local peeled_ref
4069

4170
# First try adding ^{} to the ref, incase it's a tag
4271
# and needs peeling. If nothing is found for that,
@@ -55,6 +84,8 @@ function bl_remote_sha_for_ref(){
5584
}
5685

5786
function bl_remote_tag_for_sha(){
87+
bl_in_git_repo
88+
5889
local -r remote="${1}"
5990
local -r sha="${2}"
6091
git ls-remote "${remote}" \
@@ -65,11 +96,13 @@ function bl_remote_tag_for_sha(){
6596
## Minimal git subtree functionality required for tests to pass
6697
# full subtree functionality is not ready for merge.
6798
function bl_gittrees_present(){
99+
bl_in_git_repo
68100
local -r git_trees="$(bl_repo_root)/.gittrees"
69101
[[ -e "${git_trees}" ]]
70102
}
71103

72104
function bl_cat_gittrees(){
105+
bl_in_git_repo
73106
local -r git_trees="$(bl_repo_root)/.gittrees"
74107
local -r subtrees_file_format=".gittrees should contain one subtree per line,\
75108
space seperated with three fields: subtree_path renmote_url remote_name"
@@ -78,6 +111,8 @@ space seperated with three fields: subtree_path renmote_url remote_name"
78111
}
79112

80113
function bl_tracked_files_excluding_subtrees(){
114+
bl_in_git_repo
115+
local subtrees
81116
if bl_gittrees_present; then
82117
subtrees="$(bl_cat_gittrees | awk '{print $1}' | paste -sd '|' -)"
83118
bl_all_files_in_repo | grep -E -v "${subtrees}"

github/lib

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/bin/bash
2+
3+
: "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}"
4+
5+
function bl_hub_available(){
6+
# type instead of which, so it can be stubbed in tests
7+
type hub &>/dev/null || bl_fail "hub (github cli) binary not found, please install it via your package manager or use bl_hub_download_latest."
8+
}
9+
10+
function bl_hub_creds_available(){
11+
config_file="${HUB_CONFIG:-${HOME}/.config/hub}"
12+
[[ -n "${GITHUB_USER:-}" ]] && [[ -n "${GITHUB_TOKEN:-}" ]] && return
13+
[[ -e "${config_file}" ]] && return
14+
bl_fail "No credentials found for (git)hub please set GITHUB_USER and GITHUB_TOKEN or create ~/.config/hub"
15+
}
16+
17+
function bl_hub_check(){
18+
bl_in_git_repo \
19+
&& bl_hub_available \
20+
&& bl_hub_creds_available
21+
}
22+
23+
function bl_hub_download_latest(){
24+
local install_dir="${1:-${HOME}/bin}"
25+
local os_arch="${2:-}"
26+
local tmpdir=".hubdl"
27+
local path
28+
local download_url
29+
local bin_path
30+
31+
if [[ -z "${os_arch}" ]]; then
32+
if [[ "${OSTYPE}" =~ "darwin" ]]; then
33+
os_arch="darwin-amd64"
34+
else
35+
os_arch="linux-amd64"
36+
fi
37+
bl_debug "Hub Download detected arch: ${os_arch}"
38+
fi
39+
40+
path="$(curl -s -L https://github.com/github/hub/releases/latest |grep -o '[^"]*hub-'${os_arch}'[^"]*')"
41+
download_url="https://github.com/${path}"
42+
43+
bin_path="${install_dir}/hub"
44+
mkdir -p "${install_dir}"
45+
46+
mkdir -p "${tmpdir}"
47+
bl_spushd "${tmpdir}"
48+
curl -s -L "${download_url}" > hub.tgz
49+
tar xf hub.tgz
50+
bl_spopd
51+
mv "${tmpdir}"/*/bin/hub "${bin_path}"
52+
rm -rf "${tmpdir}"
53+
54+
bl_info "${download_url}/bin/hub --> ${bin_path}"
55+
}
56+
57+
function bl_hub_issue_number_for_title(){
58+
local title="${1}"
59+
bl_hub_check
60+
hub issue \
61+
|grep "${title}" \
62+
|awk -F'[ #]+' '{print $2}'
63+
}
64+
65+
function bl_hub_add_issue_comment(){
66+
local issue_number="${1}"
67+
local comment="${2}"
68+
69+
bl_hub_check
70+
71+
[[ -n "${comment}" ]] || bl_die "bl_hub_add_issue_comment: Comment must not be empty"
72+
hub issue show "${issue_number}" >/dev/null || bl_die "Github Issue number ${issue_number} isn't valid for repo $(pwd)"
73+
74+
owner_repo="$(bl_github_owner_repo)"
75+
if hub api "repos/${owner_repo}/issues/${issue_number}/comments" --field body="${comment}" >/dev/null; then
76+
bl_debug "Added comment: \"${comment}\" to https://github.com/${owner_repo}/issues/${issue_number}"
77+
else
78+
bl_fail "Failed to add comment: ${comment} to issue: ${owner_repo}#${issue_number}"
79+
fi
80+
}
81+
82+
83+
function bl_hub_comment_or_create_issue(){
84+
local title="${1}"
85+
local message="${2}"
86+
local issue_number
87+
local issue_url
88+
local action
89+
local owner_repo
90+
bl_hub_check
91+
92+
owner_repo="$(bl_github_owner_repo)"
93+
issue_number="$(bl_hub_issue_number_for_title "${title}" ||:)"
94+
95+
if [[ -z "${issue_number}" ]]; then
96+
action="created"
97+
# issue doesn't exist create it
98+
issue_url="$(hub issue create -m "${title}
99+
100+
${message}")"
101+
102+
# Example issue url: https://github.com/{owner}/{repo}/issues/{issue number}"
103+
# To find the issue number, split on / and take the last field
104+
issue_number="$(awk -F'/' '{print $NF}' <<<"${issue_url}" )"
105+
106+
bl_debug "Created issue: ${issue_url} with title \"${title}\""
107+
else
108+
issue_url="https://github.com/${owner_repo}/issues/${issue_number}"
109+
action="commented"
110+
bl_debug "Found existing issue for title \"${title}\": ${issue_url}"
111+
bl_hub_add_issue_comment "${issue_number}" "${message}"
112+
fi
113+
cat <<EOJ
114+
{
115+
"action": "${action}",
116+
"issue_number": "${issue_number}",
117+
"issue_url": "${issue_url}",
118+
"issue_ref": "${owner_repo}#${issue_number}"
119+
}
120+
EOJ
121+
}

helpers/lib

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ function bl_die(){
77
exit 1
88
}
99

10+
function bl_fail(){
11+
bl_error "${@}"
12+
return 1
13+
}
14+
1015
#safe pushd
1116
function bl_spushd(){
1217
if ! pushd "${1}" >/dev/null; then
@@ -36,24 +41,28 @@ function bl_retry {
3641
# Maxiumum amount of fixed delay between attempts
3742
# a random value will still be added.
3843
local -r MAX_BACKOFF=30
44+
local rc
45+
local count
46+
local retries
47+
local backoff
3948

4049
if [[ ${#} -lt 2 ]]; then
4150
bl_die "retry usage: retry <retries> <command>"
4251
fi
4352

44-
local retries=$1
53+
retries=$1
4554
shift
4655

4756
if ! bl_is_num "${retries}"; then
4857
bl_die "Invalid number of retries: ${retries} for command '${*}'".
4958
fi
5059

51-
local count=0
60+
count=0
5261
until eval "$@"; do
5362
# Command failed, otherwise until would have skipped the loop
5463

5564
# Store return code so it can be reported to the user
56-
exit=$?
65+
rc=$?
5766
count=$((count + 1))
5867
if [ "${count}" -lt "${retries}" ]; then
5968
# There are still retries left, calculate delay and notify user.
@@ -65,12 +74,12 @@ function bl_retry {
6574
# Add a random amount to the delay to prevent competing processes
6675
# from re-colliding.
6776
wait=$(( backoff + (RANDOM % count) ))
68-
bl_info "'${*}' Retry $count/$retries exited $exit, retrying in $wait seconds..."
77+
bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $wait seconds..."
6978
sleep $wait
7079
else
7180
# Out of retries :(
72-
bl_error "Retry $count/$retries exited $exit, no more retries left."
73-
return $exit
81+
bl_error "Retry $count/$retries exited $rc, no more retries left."
82+
return $rc
7483
fi
7584
done
7685
return 0
@@ -84,6 +93,9 @@ function bl_retry_constant {
8493

8594
local retries=$1; shift
8695
local interval=$1; shift
96+
local count
97+
local rc
98+
local interval
8799

88100
if ! bl_is_num "${retries}"; then
89101
bl_die "Invalid number of retries: ${retries} for command '${*}'"
@@ -93,20 +105,20 @@ function bl_retry_constant {
93105
bl_die "Invalid interval in seconds: ${retries} for command '${*}'".
94106
fi
95107

96-
local count=0
108+
count=0
97109
until eval "$@"; do
98110
# Command failed, otherwise until would have skipped the loop
99111

100112
# Store return code so it can be reported to the user
101-
exit=$?
113+
rc=$?
102114
count=$((count + 1))
103115
if [ "${count}" -lt "${retries}" ]; then
104-
bl_info "'${*}' Retry $count/$retries exited $exit, retrying in $interval seconds..."
116+
bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $interval seconds..."
105117
sleep "${interval}"
106118
else
107119
# Out of retries :(
108-
bl_error "Retry $count/$retries exited $exit, no more retries left."
109-
return $exit
120+
bl_error "Retry $count/$retries exited $rc, no more retries left."
121+
return $rc
110122
fi
111123
done
112124
return 0

init

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ BASH_LIB_DIR="${BASH_LIB_DIR_RELATIVE}"
2626

2727
# Load the filehandling module for the abspath
2828
# function
29-
for lib in helpers logging filehandling git k8s test-utils; do
29+
for lib in helpers logging filehandling git github k8s test-utils; do
3030
. "${BASH_LIB_DIR_RELATIVE}/${lib}/lib"
3131
done
3232

0 commit comments

Comments
 (0)