From 24666f08d1edc6e69aea3ff44edb7db7b73a5fb0 Mon Sep 17 00:00:00 2001 From: Yang Chiu Date: Thu, 22 Aug 2024 11:52:09 +0800 Subject: [PATCH] ci: add qase sync action Signed-off-by: Yang Chiu --- add-to-project-action/action.yml | 2 +- add-zenhub-release-action/README.md | 5 +- add-zenhub-release-action/action.yml | 2 +- filter-org-members-action/README.md | 2 + filter-org-members-action/action.yml | 2 +- qase-sync-action/README.md | 24 ++++ qase-sync-action/action.yml | 175 +++++++++++++++++++++++++++ 7 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 qase-sync-action/README.md create mode 100644 qase-sync-action/action.yml diff --git a/add-to-project-action/action.yml b/add-to-project-action/action.yml index ead0c99..420c728 100644 --- a/add-to-project-action/action.yml +++ b/add-to-project-action/action.yml @@ -12,4 +12,4 @@ inputs: required: true runs: using: 'node20' - main: '../lib/add-to-project-action.js' \ No newline at end of file + main: '../lib/add-to-project-action.js' diff --git a/add-zenhub-release-action/README.md b/add-zenhub-release-action/README.md index 89ced7b..0b473e6 100644 --- a/add-zenhub-release-action/README.md +++ b/add-zenhub-release-action/README.md @@ -21,7 +21,7 @@ Add an issue to Zenhub release. If the release doesn't exist, it would be create **Required** The Zenhub release name to be added/created. ## Example usage - +``` steps: - name: Get Repo Object uses: octokit/request-action@v2.x @@ -36,4 +36,5 @@ steps: zenhub_token: ${{ secrets.ZENHUB_TOKEN }} repo_id: ${{ fromJSON(steps.repo.outputs.data).id }} issue_number: ${{ github.event.issue.number }} - release_name: 1.4.0 \ No newline at end of file + release_name: 1.4.0 +``` diff --git a/add-zenhub-release-action/action.yml b/add-zenhub-release-action/action.yml index baf8afd..d3e1144 100644 --- a/add-zenhub-release-action/action.yml +++ b/add-zenhub-release-action/action.yml @@ -11,4 +11,4 @@ inputs: required: true runs: using: 'node16' - main: '../lib/add-zenhub-release-action.js' \ No newline at end of file + main: '../lib/add-zenhub-release-action.js' diff --git a/filter-org-members-action/README.md b/filter-org-members-action/README.md index 24d7827..d6c2a7f 100644 --- a/filter-org-members-action/README.md +++ b/filter-org-members-action/README.md @@ -18,6 +18,7 @@ Get list of users belong to a specific organization from an input user list. ## Example usage +``` steps: - name: Get Longhorn Members uses: longhorn/bot/filter-org-members-action@master @@ -26,3 +27,4 @@ steps: token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} organization: longhorn usernames: ${{ github.event.issue.assignees.*.login }} +``` diff --git a/filter-org-members-action/action.yml b/filter-org-members-action/action.yml index df6ff76..5d374be 100644 --- a/filter-org-members-action/action.yml +++ b/filter-org-members-action/action.yml @@ -9,4 +9,4 @@ inputs: required: true runs: using: 'node20' - main: '../lib/filter-org-members-action.js' \ No newline at end of file + main: '../lib/filter-org-members-action.js' diff --git a/qase-sync-action/README.md b/qase-sync-action/README.md new file mode 100644 index 0000000..2cb5fe1 --- /dev/null +++ b/qase-sync-action/README.md @@ -0,0 +1,24 @@ +# Qase Sync composite action + +Sync manual test cases between github and qase. + +## Inputs + +## `project-code` + +**Required** Code of project, where to sync test cases. + +## `token` + +**Required** Qase API token. + +## Example usage + +``` +steps: + - name: Qase Sync + uses: longhorn/bot/qase-sync-action@master + with: + project-code: ${{ secrets.PROJECT_CODE }} + token: ${{ secrets.QASE_TOKEN }} +``` diff --git a/qase-sync-action/action.yml b/qase-sync-action/action.yml new file mode 100644 index 0000000..58d361d --- /dev/null +++ b/qase-sync-action/action.yml @@ -0,0 +1,175 @@ +name: 'Qase Sync' +description: 'Sync manual test cases between github and qase' +inputs: + project-code: + description: 'Code of project, where to sync test cases.' + required: true + token: + description: 'Qase API token' + required: true +runs: + using: 'composite' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 3 + + - name: Fetch changed files + shell: bash + run: | + files=($(git diff --name-only HEAD HEAD^)) + echo ${files[@]} + echo "files=${files[@]}" >> $GITHUB_ENV + + - name: Filter changed test cases + shell: bash + env: + files: ${{ env.files }} + run: | + test_cases=() + files=(${files}) + for file in "${files[@]}"; do + if [[ "${file}" == *"/manual/"* ]] && [[ "${file}" != *"_index"* ]]; then + test_case=$(echo "${file#*manual/}") + test_cases+=("${test_case}") + fi + done + echo "collected test cases: ${test_cases[@]}" + echo "test_cases=${test_cases[@]}" >> $GITHUB_ENV + echo "test_cases_length=${#test_cases[@]}" >> $GITHUB_ENV + + - name: Create missing test suites + shell: bash + if: ${{ env.test_cases_length > 0 }} + env: + project_code: ${{ inputs.project-code }} + token: ${{ inputs.token }} + test_cases: ${{ env.test_cases }} + run: | + test_cases=(${test_cases}) + for test_case in "${test_cases[@]}"; do + + echo "processing test case: ${test_case}" + IFS='/' read -ra arr <<< "${test_case}" + + parent_suite_id="" + + for str in "${arr[@]}"; do + if [[ $str == *".md"* ]]; then + # only deal with test suite in this step + # test case will be processed in the next step + break + fi + str=${str//-/ } # replace - with whitespace + if [[ $str =~ ^v.+\..+\.+.+$ ]]; then + : # skip v*.*.* + elif [[ ${#str} -lt 4 ]]; then + # capitalize acronym like ha, eks, etc + str=${str^^} + else + str=($str) + str="${str[@]^}" # capitalize every word + fi + + # check if the test suite already exists + res=$(curl -s --request GET --url "https://api.qase.io/v1/suite/${project_code}" --get --data-urlencode "search=${str}" --header "Token: ${token}" --header "accept: application/json") + echo "checked if test suite ${str} exists: ${res}" + + # if not, create new test suite + if [[ $(echo "$res" | jq .result.count) == "0" ]]; then + echo "creating new test suite ${str} with parent id ${parent_suite_id}" + res=$(curl --request POST -s \ + --url https://api.qase.io/v1/suite/${project_code} \ + --header "Token: ${token}" \ + --header "accept: application/json" \ + --header "content-type: application/json" \ + --data "{ \"title\": \"${str}\", \"parent_id\": \"${parent_suite_id}\" }") + echo "created new test suite ${str}: ${res}" + fi + + # update parent suite id for the next iteration + res=$(curl -s --request GET --url "https://api.qase.io/v1/suite/${project_code}" --get --data-urlencode "search=${str}" --header "Token: ${token}" --header "accept: application/json") + parent_suite_id=$(echo "$res" | jq .result.entities[0].id) + echo "updated parent suite id to ${parent_suite_id}: ${res}" + done + done + + - name: Create or update test cases + shell: bash + if: ${{ env.test_cases_length > 0 }} + env: + project_code: ${{ inputs.project-code }} + token: ${{ inputs.token }} + test_cases: ${{ env.test_cases }} + working-directory: ./docs/content/manual + run: | + test_cases=(${test_cases}) + + for file_path in "${test_cases[@]}"; do + + delete_test_case=false + if [[ ! -e "${file_path}" ]]; then + echo "${file_path} has been deleted" + git checkout HEAD^ -- "${file_path}" + delete_test_case=true + fi + + title=$(grep '^title:' ${file_path} | sed 's/title: //g' | sed 's/"/\\"/g') + echo "got test case title: ${title}" + description=$(sed -z 's/\n/\\n/g' ${file_path} | sed 's/ \\/ \\\\/g' | sed 's/"/\\"/g') + echo "got test case description: ${description}" + + res=$(curl -s --request GET --url "https://api.qase.io/v1/case/${project_code}" --get --data-urlencode "search=${title}" --header "Token: ${token}" --header "accept: application/json") + if [[ $(echo $res | jq .result.count) -ne "0" ]] && [[ "${delete_test_case}" = true ]]; then + # delete existing test case + test_case_id=$(echo $res | jq .result.entities[0].id) + + res=$(curl --request DELETE -s \ + --url "https://api.qase.io/v1/case/${project_code}/${test_case_id}" \ + --header "Token: ${token}" \ + --header "accept: application/json") + + echo "deleted existing test case: ${res}" + elif [[ $(echo $res | jq .result.count) -ne "0" ]]; then + # update existing test case + test_case_id=$(echo $res | jq .result.entities[0].id) + + res=$(curl --request PATCH -s \ + --url "https://api.qase.io/v1/case/${project_code}/${test_case_id}" \ + --header "Token: ${token}" \ + --header "accept: application/json" \ + --header "content-type: application/json" \ + --data "{ \"description\": \"${description}\", \"title\": \"${title}\" }") + + echo "updated existing test case: ${res}" + else + # create new test case + parent_suite_name=$(basename $(dirname ${file_path})) + + if [[ "${parent_suite_name}" == "manual" ]]; then + parent_suite_id="" + else + parent_suite_name=${parent_suite_name//-/ } # replace - with whitespace + if [[ $parent_suite_name =~ ^v.+\..+\.+.+$ ]]; then + : # skip v*.*.* + elif [[ ${#parent_suite_name} -lt 4 ]]; then + # capitalize acronym like ha, eks, etc + parent_suite_name=${parent_suite_name^^} + else + parent_suite_name=($parent_suite_name) + parent_suite_name="${parent_suite_name[@]^}" # capitalize every word + fi + res=$(curl -s --request GET --url "https://api.qase.io/v1/suite/${project_code}" --get --data-urlencode "search=${parent_suite_name}" --header "Token: ${token}" --header "accept: application/json") + parent_suite_id=$(echo "$res" | jq .result.entities[0].id) + fi + echo "creating new test case ${title} under test suite ${parent_suite_name}(${parent_suite_id})" + res=$(curl --request POST -s \ + --url https://api.qase.io/v1/case/${project_code}/ \ + --header "Token: ${token}" \ + --header "accept: application/json" \ + --header "content-type: application/json" \ + --data "{ \"description\": \"${description}\", \"title\": \"${title}\", \"suite_id\": \"${parent_suite_id}\" }") + echo "created new test case ${title}: ${res}" + fi + done