diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 51d5e81..adcaf63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,24 @@ name: Tests -on: [push, pull_request] +on: + pull_request: + branches: + - master + paths-ignore: + - 'completion/**' + - 'doc/**' + - 'git-mr-completion.bash' + - 'LICENSE' + - 'README.md' + push: + branches: + - master + paths-ignore: + - 'completion/**' + - 'doc/**' + - 'git-mr-completion.bash' + - 'LICENSE' + - 'README.md' jobs: diff --git a/README.md b/README.md index 21240ea..ab19053 100644 --- a/README.md +++ b/README.md @@ -160,41 +160,96 @@ Completion functions for Bash and Zsh are available: ### Configuration -You need to configure the following environment variables: +Git-mr can either be configured through [git-config](https://git-scm.com/docs/git-config) or environment variables. + +When using Git configuration, project-specific overrides can be set by omitting the `--global` option: ```bash -export JIRA_USER="user.name@mycompany.com" +# Global configuration stored in your ~/.gitconfig +git config --global mr.global-option "global-value" + +# Local configuration stored in current project's .git/config +git config mr.local-option "local-value" +``` + +Environment variables take precedence over Git configuration. + +#### Git configuration reference + +```bash +# Required configuration ------------------------------------------------------- + +git config --global mr.jira-instance "mycompany.atlassian.net" +git config --global mr.jira-user "user.name@mycompany.com" +git config --global mr.jira-token "abcdefghijklmnopqrstuvwx" + +git config --global mr.jira-code-pattern "[A-Z]{2,3}-[0-9]+" + +git config --global mr.gitlab-domain "myapp.gitlab.com" +git config --global mr.gitlab-token "Zyxwvutsrqponmlkjihg" + +# Optional configuration ------------------------------------------------------- + +# Default labels for new merge requests +git config --global mr.gitlab-default-labels "Review,My Team" + +# Check "Delete source branch" by default (defaults to 1) +# git config --global mr.gitlab-remove-source-branch-on-merge 1 + +# Gitlab status labels (comma-separated, without spaces in between) +git config --global mr.gitlab-ip-labels "WIP" +git config --global mr.gitlab-cr-labels "Review" +git config --global mr.gitlab-qa-labels "Testing" +git config --global mr.gitlab-ok-labels "Accepted" + +# Jira status - transition IDs +git config --global mr.jira-ip-id "xx" +git config --global mr.jira-cr-id "xx" +git config --global mr.jira-qa-id "xx" +git config --global mr.jira-ok-id "xx" + +# Always use extended commit messages +# git config --global mr.git-mr-extended 1 + +# Required upvote count to turn indicator green in `mr status` (defaults to 2) +# git config --global mr.git-mr-required-upvotes 2 + +# Network timeout (in seconds, defaults to 5) +# git config --global mr.git-mr-timeout 5 +``` + +#### Environment variables reference + +```bash +# Required configuration ------------------------------------------------------- + export JIRA_INSTANCE="mycompany.atlassian.net" +export JIRA_USER="user.name@mycompany.com" export JIRA_TOKEN="abcdefghijklmnopqrstuvwx" + export JIRA_CODE_PATTERN="[A-Z]{2,3}-[0-9]+" export GITLAB_DOMAIN="myapp.gitlab.com" export GITLAB_TOKEN="Zyxwvutsrqponmlkjihg" -``` - -To create a Jira API Token, go to: -* https://id.atlassian.com/manage-profile/security/api-tokens - (Account Settings -> Security -> API Token -> Create and manage API tokens) -To create a Gitlab API Token, go to: -* https://myapp.gitlab.com/-/profile/personal_access_tokens?name=Git-MR+Access+token&scopes=api - (Settings -> Access Tokens) +# Optional configuration ------------------------------------------------------- -Other optional configuration variables: -```bash # Default labels for new merge requests export GITLAB_DEFAULT_LABELS="Review,My Team" +# Check "Delete source branch" by default (defaults to 1) +#export GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE=1 + # Gitlab status labels (comma-separated, without spaces in between) -export GITLAB_IP_LABELS="WIP" # Label(s) set on "In Progress" step -export GITLAB_CR_LABELS="Review" # Label(s) set on "Code Review" step -export GITLAB_QA_LABELS="Testing" # Label(s) set on "Quality Assurance" step -export GITLAB_OK_LABELS="Accepted" # Label(s) set on "Accepted" step +export GITLAB_IP_LABELS="WIP" +export GITLAB_CR_LABELS="Review" +export GITLAB_QA_LABELS="Testing" +export GITLAB_OK_LABELS="Accepted" # Jira status - transition IDs -export JIRA_IP_ID="xx" # "In progress" transition ID -export JIRA_CR_ID="xx" # "Code review" transition ID -export JIRA_QA_ID="xx" # "Quality Assurance" transition ID -export JIRA_OK_ID="xx" # "Accepted" transition ID +export JIRA_IP_ID="xx" +export JIRA_CR_ID="xx" +export JIRA_QA_ID="xx" +export JIRA_OK_ID="xx" # Always use extended commit messages # export GIT_MR_EXTENDED=1 @@ -202,16 +257,19 @@ export JIRA_OK_ID="xx" # "Accepted" transition ID # Required upvote count to turn indicator green in `mr status` (defaults to 2) #export GIT_MR_REQUIRED_UPVOTES=2 -# Number of description lines shown in `mr menu update` (defaults to 15) -#export GIT_MR_MENU_UPDATE_CONTEXT_LINES=15 - -# Check "Delete source branch" by default (defaults to 1) -#export GITLAB_DEFAULT_FORCE_REMOVE_SOURCE_BRANCH=1 - # Network timeout (in seconds, defaults to 5) #export GIT_MR_TIMEOUT=5 ``` +#### Jira & Gitlab tokens + +To create a Jira API Token, go to: +* https://id.atlassian.com/manage-profile/security/api-tokens + (Account Settings -> Security -> API Token -> Create and manage API tokens) + +To create a Gitlab API Token, go to: +* https://myapp.gitlab.com/-/profile/personal_access_tokens?name=Git-MR+Access+token&scopes=api + (Settings -> Access Tokens) ## Commands @@ -231,7 +289,7 @@ This will print a merge request description, with a link to Jira ticket and curr If a merge request based on the current branch is found on Gitlab, its URL will be provided, along with current votes, open and resolved threads and mergeable status. Otherwise, a link to create a new merge request will be provided. -Default labels and "Delete source branch" status can be configured with the `GITLAB_DEFAULT_LABELS` and `GITLAB_DEFAULT_FORCE_REMOVE_SOURCE_BRANCH` environment variables. +Default labels and "Delete source branch" status can be configured with the `GITLAB_DEFAULT_LABELS` and `GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE` environment variables. ![git mr](doc/git-mr.png) diff --git a/git-mr b/git-mr index a5bfdc4..94138ab 100755 --- a/git-mr +++ b/git-mr @@ -490,7 +490,7 @@ jira_request() { -H "Authorization: Basic ${auth_token}" \ -H "Content-Type: application/json" \ --data "${request_data}" \ - --max-time "${GIT_MR_TIMEOUT:-5}" \ + --max-time "${GIT_MR_TIMEOUT}" \ "${jira_base_url}/${request_url}" || exit_error "$ERR_JIRA_API" "Jira request error" } @@ -626,7 +626,7 @@ gitlab_request() { -H "Private-Token: ${GITLAB_TOKEN}" \ -H "Content-Type: application/json" \ --data "${request_data}" \ - --max-time "${GIT_MR_TIMEOUT:-5}" \ + --max-time "${GIT_MR_TIMEOUT}" \ "${gitlab_base_url}/${request_url}") || exit_error "$ERR_GITLAB_API" "Gitlab request error" if [[ $request_verb != "GET" ]]; then @@ -677,7 +677,7 @@ gitlab_new_merge_request_url() { done # other options - if [[ "${GITLAB_DEFAULT_FORCE_REMOVE_SOURCE_BRANCH:-1}" -eq 1 ]]; then + if [[ "${GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE}" -eq 1 ]]; then gitlab_mr_url="${gitlab_mr_url}&$(urlencode "merge_request[force_remove_source_branch]")=1" fi @@ -883,7 +883,7 @@ gitlab_merge_request_merge() { local mr_iid=$1; [[ -n $mr_iid ]] || exit_error "$ERR_MR" "No mr_iid provided" - gitlab_current_project_request "merge_requests/${mr_iid}/merge?should_remove_source_branch=${GITLAB_DEFAULT_FORCE_REMOVE_SOURCE_BRANCH:-1}" "PUT" + gitlab_current_project_request "merge_requests/${mr_iid}/merge?should_remove_source_branch=${GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE}" "PUT" } gitlab_title_is_draft() { @@ -1157,9 +1157,9 @@ mr_print_status() { local labels_str_len=$((${#labels} + $((labels_count * 2)) + 4)) # 2 spaces + 2 for larger icon # Votes - if [[ $upvotes -ge ${GIT_MR_REQUIRED_UPVOTES:-2} ]]; then upvotes=$(colorize "$upvotes" "bold" "lightgreen") - elif [[ $upvotes -gt 0 ]]; then upvotes=$(colorize "$upvotes" "bold" "lightyellow") - else upvotes=$(colorize "$upvotes" "bold"); fi + if [[ $upvotes -ge ${GIT_MR_REQUIRED_UPVOTES} ]]; then upvotes=$(colorize "$upvotes" "bold" "lightgreen") + elif [[ $upvotes -gt 0 ]]; then upvotes=$(colorize "$upvotes" "bold" "lightyellow") + else upvotes=$(colorize "$upvotes" "bold"); fi if [[ $downvotes -gt 0 ]]; then downvotes=$(colorize "$downvotes" "bold" "lightred") @@ -2578,15 +2578,40 @@ ${b}CONFIGURATION${r} EOF if [[ $GIT_MR_VERBOSE -eq 1 ]]; then cat < Security -> API Token -> Create and manage API tokens) @@ -2594,38 +2619,6 @@ EOF To create a Gitlab API Token, go to: https://myapp.gitlab.com/-/profile/personal_access_tokens?name=Git-MR+Access+token&scopes=api (Settings -> Access Tokens) - Optional configuration variables: - - # Default labels for new merge requests - export GITLAB_DEFAULT_LABELS="Review,My Team" - - # Gitlab status labels (comma-separated, without spaces in between) - export GITLAB_IP_LABELS="WIP" # Label(s) set on "In Progress" step - export GITLAB_CR_LABELS="Review" # Label(s) set on "Code Review" step - export GITLAB_QA_LABELS="Testing" # Label(s) set on "Quality Assurance" step - export GITLAB_OK_LABELS="Validated" # Label(s) set on "Accepted" step - - # Jira status - transition IDs - export JIRA_IP_ID="xx" # "In Progress" transition ID - export JIRA_CR_ID="xx" # "Code Review" transition ID - export JIRA_QA_ID="xx" # "Quality Assurance" transition ID - export JIRA_OK_ID="xx" # "Accepted" transition ID - - # Always use extended commit messages - # export GIT_MR_EXTENDED=1 - - # Required upvote count to turn indicator green in \`mr status\` (defaults to 2) - #export GIT_MR_REQUIRED_UPVOTES=2 - - # Number of description lines shown in \`mr menu update\` (defaults to 15) - #export GIT_MR_MENU_UPDATE_CONTEXT_LINES=15 - - # Check "Delete source branch" by default (defaults to 1) - #export GITLAB_DEFAULT_FORCE_REMOVE_SOURCE_BRANCH=1 - - # Network timeout (in seconds, defaults to 5) - #export GIT_MR_TIMEOUT=5 - EOF else cat <&2 + exit 0 +fi current_branch=$(git rev-parse --abbrev-ref HEAD) -issue_code=$(echo "${current_branch}" | grep -Eo "$jira_code_pattern" | tail -n1) +issue_code=$(echo "${current_branch}" | grep -Eo "$JIRA_CODE_PATTERN" | tail -n1) [ -n "$issue_code" ] || exit 0 # No issue code detected current_msg=$(cat "$1") -msg_code=$(echo "${current_msg}" | grep -iEo "$jira_code_pattern" | tail -n1) +msg_code=$(echo "${current_msg}" | grep -iEo "$JIRA_CODE_PATTERN" | tail -n1) [ -z "$msg_code" ] || exit 0 # existing issue code in message if case "$current_msg" in "fixup! "*) ;; *) false;; esac; then diff --git a/test/.gitconfig b/test/.gitconfig new file mode 100644 index 0000000..6d5a75d --- /dev/null +++ b/test/.gitconfig @@ -0,0 +1,36 @@ + +[user] + email = test@example.com + name = Test + +[init] + defaultBranch = main + templatedir = + +[mr] + jira-instance = "mycompany.example.net" + jira-user = "me@jira" + jira-token = "jira-test-token" + + gitlab-domain = "gitlab.example.net" + gitlab-token = "gitlab-test-token" + + jira-code-pattern = "[A-Z]{2,3}-[0-9]+" + + jira-ip-id = "110" + jira-cr-id = "120" + jira-qa-id = "130" + jira-ok-id = "140" + + gitlab-ip-labels = "WIP" + gitlab-cr-labels = "Review" + gitlab-qa-labels = "Testing" + gitlab-ok-labels = "Accepted" + + gitlab-default-labels = "WIP" + + git-mr-extended = + + git-mr-required-upvotes = 2 + gitlab-remove-source-branch-on-merge = 1 + git-mr-timeout = 1 diff --git a/test/git-mr.bats b/test/git-mr.bats index 9b44096..20194d2 100644 --- a/test/git-mr.bats +++ b/test/git-mr.bats @@ -9,16 +9,19 @@ load "test_helper/bats-assert/load" setup_file() { export LANG=C.UTF-8 # ensure tests handle UTF-8 properly + # Unset all git-mr environment variables (would take precedence over local configuration) + unset JIRA_INSTANCE JIRA_USER JIRA_TOKEN JIRA_CODE_PATTERN \ + GITLAB_DOMAIN GITLAB_TOKEN GITLAB_DEFAULT_LABELS \ + GITLAB_IP_LABELS GITLAB_CR_LABELS GITLAB_QA_LABELS GITLAB_OK_LABELS \ + JIRA_IP_ID JIRA_CR_ID JIRA_QA_ID JIRA_OK_ID \ + GIT_MR_EXTENDED GIT_MR_REQUIRED_UPVOTES GIT_MR_TIMEOUT + export GIT_MR_NO_COLORS=1 export GIT_MR_NO_TERMINAL_LINK=1 export GIT_MR_NO_COMMITS= export GIT_MR_EXTENDED= - export JIRA_CODE_PATTERN="[A-Z]{2,3}-[0-9]+" - export JIRA_INSTANCE= - export JIRA_USER= export GITLAB_DOMAIN="gitlab.example.net" - export GITLAB_TOKEN="test" export MD_BR='..' # for easier visualization @@ -35,6 +38,8 @@ setup_file() { git init --bare remote git init --bare gitlab git init repo && cd repo || exit + configure-git-repo + git remote add fs-local ../remote git remote add gitlab ../gitlab -m real-main @@ -86,26 +91,31 @@ teardown_file() { } setup() { + # cd to repo first to take local git-mr configuration into account + cd "${BATS_TEST_DIRNAME}/data" || exit + cd repo + # Source git-mr to load functions . "${BATS_TEST_DIRNAME}"/../git-mr >&3 - cd "${BATS_TEST_DIRNAME}/data" || exit - cd repo git switch feature/AB-123-test-feature } ################################################################################ # Wrappers & utilities +# Use test config (useful when git is run by Bats) git() { command git \ - -c init.defaultBranch=main \ - -c user.email=test@example.com \ - -c user.name=Test \ - -c init.templatedir= \ + -c include.path="${BATS_TEST_DIRNAME}/.gitconfig" \ "$@" } +# Set test config in repo (useful when git is run by git-mr) +configure-git-repo() { + command git config --local include.path "${BATS_TEST_DIRNAME}/.gitconfig" +} + git-push() { [[ $1 == "gitlab" ]] && git remote set-url --push gitlab "../gitlab" git push "$@" @@ -1943,6 +1953,7 @@ This is an example without menu. cd ../repo git -c protocol.file.allow=always submodule add ../subrepo sub cd sub + configure-git-repo git switch -c feature/XY-345-test run git commit --allow-empty -m "Sub message 1" diff --git a/test/test_helper/gitlab-mock-transition.bash b/test/test_helper/gitlab-mock-transition.bash index 6d23031..06d979a 100644 --- a/test/test_helper/gitlab-mock-transition.bash +++ b/test/test_helper/gitlab-mock-transition.bash @@ -1,15 +1,3 @@ -GITLAB_IP_LABELS="WIP" -GITLAB_CR_LABELS="Review" -GITLAB_QA_LABELS="Testing" -GITLAB_OK_LABELS="Accepted" - -JIRA_INSTANCE="mycompany.example.net" -JIRA_USER="me" -JIRA_TOKEN="hcnoiuyrsqgl" -JIRA_IP_ID="110" -JIRA_CR_ID="120" -JIRA_QA_ID="130" -JIRA_OK_ID="140" git_mr_mock_labels='"My Team"' git_mr_mock_title="My MR" diff --git a/test/test_helper/jira-mock.bash b/test/test_helper/jira-mock.bash index 83406dd..9c27d6c 100644 --- a/test/test_helper/jira-mock.bash +++ b/test/test_helper/jira-mock.bash @@ -1,4 +1,3 @@ -JIRA_INSTANCE="mycompany.example.net" jira_ticket_data() { case $1 in