diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bab5c6c..5109b634 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,6 @@ jobs: TEAMWORK_URI: "localhost" TEAMWORK_API_TOKEN: "test_api_token" AUTOMATIC_TAGGING: true - BOARD_COLUMN_OPENED: "PR Open" - BOARD_COLUMN_MERGED: "Ready to Test" - BOARD_COLUMN_CLOSED: "Rejected" + WORKFLOW_STAGE_OPENED: "PR Open" + WORKFLOW_STAGE_MERGED: "Ready to Test" + WORKFLOW_STAGE_CLOSED: "Rejected" diff --git a/README.md b/README.md index 972f4803..5d8344ce 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ jobs: TEAMWORK_URI: ${{ secrets.TEAMWORK_URI }} TEAMWORK_API_TOKEN: ${{ secrets.TEAMWORK_API_TOKEN }} AUTOMATIC_TAGGING: false - BOARD_COLUMN_OPENED: 'PR Open' - BOARD_COLUMN_MERGED: 'Ready to Test' - BOARD_COLUMN_CLOSED: 'Rejected' + WORKFLOW_STAGE_OPENED: 'PR Open' + WORKFLOW_STAGE_MERGED: 'Ready to Test' + WORKFLOW_STAGE_CLOSED: 'Rejected' env: IGNORE_PROJECT_IDS: '1 2 3' @@ -71,12 +71,12 @@ Tags are added automatically on the task if you are have the option `AUTOMATIC_T - A PR is merged: tags `PR Open` and `PR Approved` removed, tag `PR merged` added - A PR is closed: tags `PR Open` and `PR Approved` removed -You may also specify columns you'd like the task to be moved to on every stage of the PR: -- `BOARD_COLUMN_OPENED`: The case-sensitive column name of the column you'd like the task to be moved to once the PR has been opened -- `BOARD_COLUMN_MERGED`: The case-sensitive column name of the column you'd like the task to be moved to once the PR has been merged -- `BOARD_COLUMN_CLOSED`: The case-sensitive column name of the column you'd like the task to be moved to if the PR was closed without being merged +You may also specify [workflow](https://support.teamwork.com/projects/workflows/workflows-introduction) stages you'd like the task to be moved to on every stage of the PR: +- `WORKFLOW_STAGE_OPENED`: The case-sensitive name of the workflow stage you'd like the task to be moved to once the PR has been opened +- `WORKFLOW_STAGE_MERGED`: The case-sensitive name of the workflow stage you'd like the task to be moved to once the PR has been merged +- `WORKFLOW_STAGE_CLOSED`: The case-sensitive name of the workflow stage you'd like the task to be moved to if the PR was closed without being merged -The column names will be checked against all board columns in the task's project, this will be using a `contains()` method so you may specify part of the name instead of the full name, however this `contains()` check is case-sensitive. The first matching column will be used. +The stage names will be checked against the stages of every workflow in the task's project, this will be using a `contains()` method so you may specify part of the name instead of the full name, however this `contains()` check is case-sensitive. The first matching stage will be used. ## Contributing * Open a PR: https://github.com/Teamwork/github-sync/pulls diff --git a/action.yml b/action.yml index 82cb7eb4..014a25f7 100644 --- a/action.yml +++ b/action.yml @@ -16,22 +16,34 @@ inputs: AUTOMATIC_TAGGING: description: 'Do you want to enable automatic tagging: true/false' required: false - BOARD_COLUMN_OPENED: - description: 'The case-sensitive column name of the column you would like the task to be moved to once the PR has been opened' + WORKFLOW_STAGE_OPENED: + description: 'The case-sensitive name of the workflow stage you would like the task to be moved to once the PR has been opened' required: false default: '' - BOARD_COLUMN_MERGED: - description: 'The case-sensitive column name of the column you would like the task to be moved to once the PR has been merged' + WORKFLOW_STAGE_MERGED: + description: 'The case-sensitive name of the workflow stage you would like the task to be moved to once the PR has been merged' required: false default: '' - BOARD_COLUMN_CLOSED: - description: 'The case-sensitive column name of the column you would like the task to be moved to if the PR was closed without being merged' + WORKFLOW_STAGE_CLOSED: + description: 'The case-sensitive name of the workflow stage you would like the task to be moved to if the PR was closed without being merged' required: false default: '' LIGHTWEIGHT_COMMENT: description: 'Generate tiny comments with minimal information' required: false default: 'false' + BOARD_COLUMN_OPENED: + description: 'Deprecated.' + required: false + default: '' + BOARD_COLUMN_MERGED: + description: 'Deprecated.' + required: false + default: '' + BOARD_COLUMN_CLOSED: + description: 'Deprecated.' + required: false + default: '' runs: using: 'docker' image: 'Dockerfile' @@ -40,7 +52,10 @@ runs: - ${{ inputs.TEAMWORK_URI }} - ${{ inputs.TEAMWORK_API_TOKEN }} - ${{ inputs.AUTOMATIC_TAGGING }} + - ${{ inputs.WORKFLOW_STAGE_OPENED }} + - ${{ inputs.WORKFLOW_STAGE_MERGED }} + - ${{ inputs.WORKFLOW_STAGE_CLOSED }} + - ${{ inputs.LIGHTWEIGHT_COMMENT }} - ${{ inputs.BOARD_COLUMN_OPENED }} - ${{ inputs.BOARD_COLUMN_MERGED }} - - ${{ inputs.BOARD_COLUMN_CLOSED }} - - ${{ inputs.LIGHTWEIGHT_COMMENT }} \ No newline at end of file + - ${{ inputs.BOARD_COLUMN_CLOSED }} \ No newline at end of file diff --git a/src/main.sh b/src/main.sh index 9646920d..62a3b37f 100644 --- a/src/main.sh +++ b/src/main.sh @@ -17,10 +17,20 @@ main() { export TEAMWORK_URI="$2" export TEAMWORK_API_TOKEN="$3" export AUTOMATIC_TAGGING="$4" - export BOARD_COLUMN_OPENED="$5" - export BOARD_COLUMN_MERGED="$6" - export BOARD_COLUMN_CLOSED="$7" + export WORKFLOW_STAGE_OPENED="$5" + export WORKFLOW_STAGE_MERGED="$6" + export WORKFLOW_STAGE_CLOSED="$7" export LIGHTWEIGHT_COMMENT="$8" + # BOARD_COLUMN_* are deprecated aliases kept for backwards compatibility. + export BOARD_COLUMN_OPENED="${9:-}" + export BOARD_COLUMN_MERGED="${10:-}" + export BOARD_COLUMN_CLOSED="${11:-}" + + # Fall back to the deprecated BOARD_COLUMN_* inputs when their WORKFLOW_STAGE_* + # replacements are not set. + : "${WORKFLOW_STAGE_OPENED:=$BOARD_COLUMN_OPENED}" + : "${WORKFLOW_STAGE_MERGED:=$BOARD_COLUMN_MERGED}" + : "${WORKFLOW_STAGE_CLOSED:=$BOARD_COLUMN_CLOSED}" env::set_environment diff --git a/src/teamwork.sh b/src/teamwork.sh index a80abb0a..c7da9e07 100644 --- a/src/teamwork.sh +++ b/src/teamwork.sh @@ -31,54 +31,57 @@ teamwork::get_project_id_from_task() { echo "$response" } -teamwork::get_matching_board_column_id() { - local -r column_name=$1 - - if [ -z "$column_name" ]; then +# Resolves a workflow stage name to its " " pair within the +# task's project. Matching is a case-sensitive substring match against the stage +# name (mirroring the legacy board-column behaviour), and the first match wins. +# Echoes nothing when no stage name is provided or no stage matches. +teamwork::get_workflow_stage() { + local -r stage_name=$1 + + if [ -z "$stage_name" ]; then return fi if [ "$ENV" == "test" ]; then - echo "$TEAMWORK_PROJECT_ID" + echo "$TEAMWORK_PROJECT_ID $TEAMWORK_PROJECT_ID" return fi - response=$( - curl "$TEAMWORK_URI/projects/$TEAMWORK_PROJECT_ID/boards/columns.json" -u "$TEAMWORK_API_TOKEN"':' |\ - jq -r --arg column_name "$column_name" '[.columns[] | select(.name | contains($column_name))] | map(.id)[0]' - ) - - if [ "$response" = "null" ]; then - return - fi - - echo "$response" + # A single call scoped to the project sideloads every stage across the + # project's workflow(s); each stage carries its parent workflow id, which the + # move endpoint also needs. + curl -s "$TEAMWORK_URI/projects/api/v3/workflows.json?projectIds=$TEAMWORK_PROJECT_ID&include=stages" \ + -u "$TEAMWORK_API_TOKEN"':' |\ + jq -r --arg stage_name "$stage_name" \ + '[.included.stages[]? | select(.name | contains($stage_name)) | "\(.workflow.id) \(.id)"][0] // empty' } -teamwork::move_task_to_column() { - local -r task_id=$TEAMWORK_TASK_ID - local -r column_name=$1 +teamwork::move_task_to_stage() { + local -r stage_name=$1 - if [ -z "$column_name" ]; then - log::message "No column name provided" + if [ -z "$stage_name" ]; then + log::message "No workflow stage name provided" return fi - local -r column_id=$(teamwork::get_matching_board_column_id "$column_name") - if [ -z "$column_id" ]; then - log::message "Failed to find a matching board column for '$column_name'" + local -r stage=$(teamwork::get_workflow_stage "$stage_name") + if [ -z "$stage" ]; then + log::message "Failed to find a matching workflow stage for '$stage_name'" return fi + local workflow_id stage_id + read -r workflow_id stage_id <<< "$stage" + if [ "$ENV" == "test" ]; then - log::message "Test - Simulate request. Task ID: $TEAMWORK_TASK_ID - Project ID: $TEAMWORK_PROJECT_ID - Column ID: $column_id" + log::message "Test - Simulate request. Task ID: $TEAMWORK_TASK_ID - Project ID: $TEAMWORK_PROJECT_ID - Workflow ID: $workflow_id - Stage ID: $stage_id" return fi - response=$(curl -X "PUT" "$TEAMWORK_URI/tasks/$TEAMWORK_TASK_ID.json" \ + response=$(curl -X "PATCH" "$TEAMWORK_URI/projects/api/v3/tasks/$TEAMWORK_TASK_ID/workflows/$workflow_id.json" \ -u "$TEAMWORK_API_TOKEN"':' \ -H 'Content-Type: application/json; charset=utf-8' \ - -d "{ \"todo-item\": { \"columnId\": $column_id } }" ) + -d "{ \"stageId\": $stage_id, \"positionAfterTask\": -1 }" ) log::message "$response" } @@ -165,7 +168,7 @@ ${pr_body} fi teamwork::add_tag "PR Open" - teamwork::move_task_to_column "$BOARD_COLUMN_OPENED" + teamwork::move_task_to_stage "$WORKFLOW_STAGE_OPENED" } teamwork::pull_request_closed() { @@ -186,7 +189,7 @@ teamwork::pull_request_closed() { teamwork::add_tag "PR Merged" teamwork::remove_tag "PR Open" teamwork::remove_tag "PR Approved" - teamwork::move_task_to_column "$BOARD_COLUMN_MERGED" + teamwork::move_task_to_stage "$WORKFLOW_STAGE_MERGED" else if [ "$LIGHTWEIGHT_COMMENT" == "true" ]; then teamwork::add_comment "PR [**\"$pr_title\"**]($pr_url) closed without merging by **$user**" @@ -198,7 +201,7 @@ teamwork::pull_request_closed() { fi teamwork::remove_tag "PR Open" teamwork::remove_tag "PR Approved" - teamwork::move_task_to_column "$BOARD_COLUMN_CLOSED" + teamwork::move_task_to_stage "$WORKFLOW_STAGE_CLOSED" fi }