Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • f.uhlig-master-patch-46771
2 results

inform_codeowners.sh

Blame
  • Forked from CbmSoft / cbmroot_parameter
    1 commit ahead of the upstream repository.
    Florian Uhlig's avatar
    Administrator authored
    Inform responsible persons about merge requests in their directories.
    3aa2f571
    History
    inform_codeowners.sh 5.91 KiB
    #!/bin/bash
    # Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
    # SPDX-License-Identifier: GPL-3.0-only
    # First commited by Florian Uhlig
    
    
    main () {
      # get passead parameters
      # 1. git name of the official repo
      if [[ ! $# -eq 1 ]]; then
        echo "Pass the name of the upstream repository as argument"
        exit 1
      fi
    
      # Since bash only supports dictionaries from version 4
      # the dictionary is implemented as list. The values in
      # the list are the directory (key) and the list of users owning
      # this directory separated by a colon. If there is more than one
      # user owning a directory the list of users is separated by a
      # semicolon.
      # An example of such an entry is "/macro/:f.uhlig;v.friese"
      declare -a dictionary
      declare -a unique_users
    
      parse_codeowners_file
      #declare -p dictionary
    
      CHANGED_FILES=""
      generate_file_list "$1"
    
      ALL_USERS=""
      generate_codeowners_list "$CHANGED_FILES"
    
      generate_comment
    }
    
    
    # Add a key value pair to the dictionary
    function add_hash () {
      dictionary+=("$1":"$2")
    }
    
    # extract the directory (key) from the passed string
    function get_key () {
      dir="${1%%:*}"
    }
    
    # extract the user list (value) from the passed string
    function get_value () {
      users="${1##*:}"
    }
    
    # Parse the CODEOWNERS file and store the information of owned directories
    # in a dictionray with the directory as key and the code owners as value
    # read a file line by line
    # Parse the remaining lines, split them at the first blank
    # First entry of the array is the directory, all other entries
    # are the list of people to inform
    # We have to check that the CODEOWNERS file is properly formated since the
    # used parser is very simply
    function parse_codeowners_file () {
      while read line; do
        # exclude commented and empty lines
        if [[ $line =~ ^\# || $line =~ ^$ ]]; then
         dummy=$line
        else
          # split the line at blank characters and store the results in an array
          IFS=' ' read -ra my_array <<< "$line"
          # first entry is the directory
          directory="${my_array[0]}"
          # in case of two entries the second is the user
          if [[ ${#my_array[@]} -eq 2 ]]; then
            users="${my_array[1]}"
          # in case of more entries the entries from the second to the last are a list of users
          # Add the users to a semicolon separated list and store the list in the user array
          else
            arr2=("${my_array[@]:1:${#my_array[@]}}")
            newUserList=""
            for i in "${arr2[@]}"; do
              newUserList="$newUserList;$i"
            done
            newUserList="${newUserList:1}"
            users="$newUserList"
          fi
          add_hash "$directory" "$users"
        fi
      done < CODEOWNERS
    }
    
    function generate_codeowners_list () {
      CHANGED_FILES=$1
      all_users=""
      for file in $CHANGED_FILES; do
    #    echo $file
        file="/$file"
        inform_users=""
        longest_match=0
        for entry in "${dictionary[@]}"; do
          get_key $entry
          if [[ $file =~ ^$dir ]]; then
            get_value $entry
            # echo "The length of the match is ${#dir}"
            if [[ ${#dir} -gt $longest_match ]]; then
              inform_users=$users
            fi
          fi
        done
    #    echo "We have to inform user $inform_users for file $file"
        all_users="$all_users;$inform_users"
      done
      ALL_USERS="${all_users:1}"
    
      # remove duplications from string
      IFS=';'
      read -ra users <<< "$ALL_USERS"
    
      for user in "${users[@]}"; do   # access each element of array
        if [[ ! " ${unique_users[@]} " =~ " ${user} " ]]; then
          unique_users+=("$user")
        fi
      done
      OWNERS_LIST=${unique_users[@]}
    
      echo "We have to inform the following users about the code ownership: $OWNERS_LIST"
    }
    
    function generate_file_list() {
    
      if [[ $# -eq 1 ]]; then
        UPSTREAM=$1
      else
        if [ -z $UPSTREAM ]; then
          UPSTREAM=$(git remote -v | grep git.cbm.gsi.de[:/]computing/cbmroot | cut -f1 | uniq)
          if [ -z $UPSTREAM ]; then
            echo "Error: Name of upstream repository not provided and not found by automatic means"
            echo 'Please provide if by checking your remotes with "git remote -v" and exporting UPSTREAM'
            echo "or passing as an argument"
            exit -1
          fi
        fi
      fi
      echo "Upstream name is :" $UPSTREAM
    
      # Rebase the MR to extract the changed files properly
      # If upstream/master was changed while the pipline was running one would otherwise get
      # a wrong list of files
      git rebase $UPSTREAM/master
      BASE_COMMIT=$UPSTREAM/master
      CHANGED_FILES=$(git diff --name-only $BASE_COMMIT)
    }
    
    function generate_comment() {
    
      # Get Information about the MR in JSON format
      # Check if the returned string contains the signature that the CodeOwners label is set
      # Only create GitLab comment if there is no comment yet (CodeOwners label not set)
      MRInfo=$(curl -s --request GET --header "PRIVATE-TOKEN: $COMMENT_TOKEN" "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID" | grep labels.*\\[.*CodeOwners.*\\])
      if [[ -z $MRInfo ]]; then
    
        comment="Dear "
        for user in "${unique_users[@]}"; do
          comment="$comment $user,"
        done
        comment="$comment
    
    you have been identified as code owner of at least one file which was changed with this merge request.
    
    Please check the changes and approve them or request changes."
    
    
        # Add a comment to the merge request which informs code owners about their duty to review
        curl -s --request POST \
           -d "body=$comment" \
           --header "PRIVATE-TOKEN: $COMMENT_TOKEN" \
           "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
    
        # On first call set a label which can be checked later on to avoid sending the comment over and over again
        curl -s --request PUT \
           --header "PRIVATE-TOKEN: $COMMENT_TOKEN" \
           "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID?add_labels=CodeOwners"
      else
        echo "Information message already exits, so there is no need to create another one."
      fi
    }
    
    main "$@"