Skip to content

Commit

Permalink
Add github issue functions with 'hub' cli
Browse files Browse the repository at this point in the history
This commit adds functions for creating and commenting on github
issues via the hub cli.

Related: conjurinc/ops#492
  • Loading branch information
hughsaunders committed Feb 19, 2020
1 parent 2e80a61 commit 4daad1e
Show file tree
Hide file tree
Showing 12 changed files with 489 additions and 22 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ files within it's directory.
<td>Git helpers</td>
<td>
<ol>
<li><b>bl_git_available</b>: True if git binary or function is available</li>
<li><b>bl_in_git_repo</b>: True if current directory is a git working directory</li>
<li><b>bl_github_owner_repo</b>: returns $owner/$repo extracted from the url of the origin remote</li>
<li><b>bl_repo_root</b>: Find the root of the current git repo.</li>
<li><b>bl_all_files_in_repo</b>: List files tracked by git.</li>
<li><b>bl_remote_latest_tag</b>: Returns the symbolic name of the latest tag from a remote.</li>
Expand All @@ -133,12 +136,28 @@ files within it's directory.
<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>
</ol>
</td>
</tr>
<tr>
<td><a href="github/lib">git</a></td>
<td>Github Related Functions</td>
<td>
<ol>
<li><b>bl_hub_available</b>: True if hub binary or function is available</li>
<li><b>bl_hub_creds_available</b>: True if hub creds are available (file or env vars)</li>
<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>
<li><b>bl_hub_download_latest</b>: Download latest hub binary from github and install to ~/bin or specified path</li>
<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>
<li><b>bl_hub_add_issue_comment</b>: Add a comment to an issue</li>
<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>
</ol>
</td>
</tr>
<td><a href="helpers/lib">helpers</a></td>
<td>Bash scripting helpers</td>
<td>
<ol>
<li><b>bl_die</b>: print message and exit 1</li>
<li><b>bl_fail</b>: print message and return 1</li>
<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>
<li><b>bl_is_num</b>: Check if a value is a number via regex</li>
<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>
Expand Down
3 changes: 3 additions & 0 deletions filehandling/lib
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ function bl_abs_path() {
# generate absolute path from relative path
# path : relative filename
# return : absolute path

local path

if [[ -z "${1:-}" ]]; then
path="."
else
Expand Down
35 changes: 35 additions & 0 deletions git/lib
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,43 @@

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


function bl_git_available(){
type git &>/dev/null || bl_fail "Git binary not found in ${PATH}"
}

function bl_in_git_repo(){
bl_git_available
git status >/dev/null || bl_die "$(pwd) is not within a git repo."
}

function bl_github_owner_repo(){
bl_in_git_repo
remote="${1:-origin}"

git remote -v | grep -q "${remote}" || bl_die "Remote ${remote} doesn't exist for repo ${PWD}"
git remote -v | grep -q "${remote}.*github" || bl_die "Remote ${remote} is not a github remote in repo ${PWD}"
[[ "$(git remote -v |grep "${remote}")" =~ github.com[:/]([^ .]*) ]]
echo "${BASH_REMATCH[1]}"
}

# Get the top level of a git repo
function bl_repo_root(){
bl_in_git_repo
git rev-parse --show-toplevel
}

# List files tracked by git
function bl_all_files_in_repo(){
bl_in_git_repo
git ls-tree -r HEAD --name-only
}

# Find the latest tag available at a repo url
# Returns tag name, not sha
function bl_remote_latest_tag(){
bl_in_git_repo

local -r remote_url="${1}"
# In ls-remote the ^{} suffix refers to a peeled/dereferenced object.
# eg refs/tags/v0.0.1^{} shows the SHA of the commit that was tagged,
Expand All @@ -29,14 +53,19 @@ function bl_remote_latest_tag(){

# Find the SHA of the latests commit to be tagged in a remote repo
function bl_remote_latest_tagged_commit(){
bl_in_git_repo

local -r remote="${1}"
local -r tag="$(bl_remote_latest_tag "${remote}")"
git ls-remote "${remote}" | awk "/refs\/tags\/${tag}\^/{print \$1}"
}

function bl_remote_sha_for_ref(){
bl_in_git_repo

local -r remote="${1}"
local -r ref="${2}"
local peeled_ref

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

function bl_remote_tag_for_sha(){
bl_in_git_repo

local -r remote="${1}"
local -r sha="${2}"
git ls-remote "${remote}" \
Expand All @@ -65,11 +96,13 @@ function bl_remote_tag_for_sha(){
## Minimal git subtree functionality required for tests to pass
# full subtree functionality is not ready for merge.
function bl_gittrees_present(){
bl_in_git_repo
local -r git_trees="$(bl_repo_root)/.gittrees"
[[ -e "${git_trees}" ]]
}

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

function bl_tracked_files_excluding_subtrees(){
bl_in_git_repo
local subtrees
if bl_gittrees_present; then
subtrees="$(bl_cat_gittrees | awk '{print $1}' | paste -sd '|' -)"
bl_all_files_in_repo | grep -E -v "${subtrees}"
Expand Down
121 changes: 121 additions & 0 deletions github/lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/bin/bash

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

function bl_hub_available(){
# type instead of which, so it can be stubbed in tests
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."
}

function bl_hub_creds_available(){
config_file="${HUB_CONFIG:-${HOME}/.config/hub}"
[[ -n "${GITHUB_USER:-}" ]] && [[ -n "${GITHUB_TOKEN:-}" ]] && return
[[ -e "${config_file}" ]] && return
bl_fail "No credentials found for (git)hub please set GITHUB_USER and GITHUB_TOKEN or create ~/.config/hub"
}

function bl_hub_check(){
bl_in_git_repo \
&& bl_hub_available \
&& bl_hub_creds_available
}

function bl_hub_download_latest(){
local install_dir="${1:-${HOME}/bin}"
local os_arch="${2:-}"
local tmpdir=".hubdl"
local path
local download_url
local bin_path

if [[ -z "${os_arch}" ]]; then
if [[ "${OSTYPE}" =~ "darwin" ]]; then
os_arch="darwin-amd64"
else
os_arch="linux-amd64"
fi
bl_debug "Hub Download detected arch: ${os_arch}"
fi

path="$(curl -s -L https://github.com/github/hub/releases/latest |grep -o '[^"]*hub-'${os_arch}'[^"]*')"
download_url="https://github.com/${path}"

bin_path="${install_dir}/hub"
mkdir -p "${install_dir}"

mkdir -p "${tmpdir}"
bl_spushd "${tmpdir}"
curl -s -L "${download_url}" > hub.tgz
tar xf hub.tgz
bl_spopd
mv "${tmpdir}"/*/bin/hub "${bin_path}"
rm -rf "${tmpdir}"

bl_info "${download_url}/bin/hub --> ${bin_path}"
}

function bl_hub_issue_number_for_title(){
local title="${1}"
bl_hub_check
hub issue \
|grep "${title}" \
|awk -F'[ #]+' '{print $2}'
}

function bl_hub_add_issue_comment(){
local issue_number="${1}"
local comment="${2}"

bl_hub_check

[[ -n "${comment}" ]] || bl_die "bl_hub_add_issue_comment: Comment must not be empty"
hub issue show "${issue_number}" >/dev/null || bl_die "Github Issue number ${issue_number} isn't valid for repo $(pwd)"

owner_repo="$(bl_github_owner_repo)"
if hub api "repos/${owner_repo}/issues/${issue_number}/comments" --field body="${comment}" >/dev/null; then
bl_debug "Added comment: \"${comment}\" to https://github.com/${owner_repo}/issues/${issue_number}"
else
bl_fail "Failed to add comment: ${comment} to issue: ${owner_repo}#${issue_number}"
fi
}


function bl_hub_comment_or_create_issue(){
local title="${1}"
local message="${2}"
local issue_number
local issue_url
local action
local owner_repo
bl_hub_check

owner_repo="$(bl_github_owner_repo)"
issue_number="$(bl_hub_issue_number_for_title "${title}" ||:)"

if [[ -z "${issue_number}" ]]; then
action="created"
# issue doesn't exist create it
issue_url="$(hub issue create -m "${title}
${message}")"

# Example issue url: https://github.com/{owner}/{repo}/issues/{issue number}"
# To find the issue number, split on / and take the last field
issue_number="$(awk -F'/' '{print $NF}' <<<"${issue_url}" )"

bl_debug "Created issue: ${issue_url} with title \"${title}\""
else
issue_url="https://github.com/${owner_repo}/issues/${issue_number}"
action="commented"
bl_debug "Found existing issue for title \"${title}\": ${issue_url}"
bl_hub_add_issue_comment "${issue_number}" "${message}"
fi
cat <<EOJ
{
"action": "${action}",
"issue_number": "${issue_number}",
"issue_url": "${issue_url}",
"issue_ref": "${owner_repo}#${issue_number}"
}
EOJ
}
34 changes: 23 additions & 11 deletions helpers/lib
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ function bl_die(){
exit 1
}

function bl_fail(){
bl_error "${@}"
return 1
}

#safe pushd
function bl_spushd(){
if ! pushd "${1}" >/dev/null; then
Expand Down Expand Up @@ -36,24 +41,28 @@ function bl_retry {
# Maxiumum amount of fixed delay between attempts
# a random value will still be added.
local -r MAX_BACKOFF=30
local rc
local count
local retries
local backoff

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

local retries=$1
retries=$1
shift

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

local count=0
count=0
until eval "$@"; do
# Command failed, otherwise until would have skipped the loop

# Store return code so it can be reported to the user
exit=$?
rc=$?
count=$((count + 1))
if [ "${count}" -lt "${retries}" ]; then
# There are still retries left, calculate delay and notify user.
Expand All @@ -65,12 +74,12 @@ function bl_retry {
# Add a random amount to the delay to prevent competing processes
# from re-colliding.
wait=$(( backoff + (RANDOM % count) ))
bl_info "'${*}' Retry $count/$retries exited $exit, retrying in $wait seconds..."
bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $wait seconds..."
sleep $wait
else
# Out of retries :(
bl_error "Retry $count/$retries exited $exit, no more retries left."
return $exit
bl_error "Retry $count/$retries exited $rc, no more retries left."
return $rc
fi
done
return 0
Expand All @@ -84,6 +93,9 @@ function bl_retry_constant {

local retries=$1; shift
local interval=$1; shift
local count
local rc
local interval

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

local count=0
count=0
until eval "$@"; do
# Command failed, otherwise until would have skipped the loop

# Store return code so it can be reported to the user
exit=$?
rc=$?
count=$((count + 1))
if [ "${count}" -lt "${retries}" ]; then
bl_info "'${*}' Retry $count/$retries exited $exit, retrying in $interval seconds..."
bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $interval seconds..."
sleep "${interval}"
else
# Out of retries :(
bl_error "Retry $count/$retries exited $exit, no more retries left."
return $exit
bl_error "Retry $count/$retries exited $rc, no more retries left."
return $rc
fi
done
return 0
Expand Down
2 changes: 1 addition & 1 deletion init
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ BASH_LIB_DIR="${BASH_LIB_DIR_RELATIVE}"

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

Expand Down
Loading

0 comments on commit 4daad1e

Please sign in to comment.