MAKENTNU/web

View on GitHub
.github/workflows/_reusable_add-content-to-project.yml

Summary

Maintainability
Test Coverage
# This is a reusable workflow; see https://docs.github.com/en/actions/using-workflows/reusing-workflows.
# It was created because GitHub projects' workflows (see e.g. https://github.com/orgs/MAKENTNU/projects/1/workflows)
# don't (currently) support doing the specific things done in the steps below.
#
# The workflow requires that the following repository variables have been created:
# - `ORGANIZATION_PROJECT_NUMBER` - The number/ID in the project URL (https://github.com/orgs/MAKENTNU/projects/1)
# - `ORGANIZATION_PROJECT__DATE_FIELD_NAME` - The name of the project's date field
# - `ORGANIZATION_PROJECT__STATUS_VALUE_FOR_CLOSED_ITEMS` - The value of the 'Status' field set for an item when closed
#                                                           (see https://github.com/orgs/MAKENTNU/projects/1/workflows/3569450)
# and the following organization variables:
# - `ORGANIZATION_NAME` - The name of the organization in the project URL (https://github.com/orgs/MAKENTNU/projects/1)
#
# The workflow also expects that the following project workflows are turned on for the project:
# - "Item closed" (https://github.com/orgs/MAKENTNU/projects/1/workflows/3569450):
#   - "When an item is closed": "issue, pull request"
#   - "Set value": "Status: <the value of the `ORGANIZATION_PROJECT__STATUS_VALUE_FOR_CLOSED_ITEMS` repository variable>"
# and that the following project workflows are turned off:
# - "Item added to project"
# - "Item reopened"
# (The "Pull request merged" project workflow (https://github.com/orgs/MAKENTNU/projects/1/workflows/3569451)
# does not affect and is not affected by any of this.
# However, it could be natural that it sets the same value as the "Item closed" project workflow.)

name: Add content to project

on:
  workflow_call:
    inputs:
      content_id:
        description: "The ID of an issue or a PR, to be added to this repo's GitHub project"
        required: true
        type: string
      status_field_value_name:
        description: "The name of one of the values of the project's (single select) 'Status' field, to set for the added project item"
        required: true
        type: string

jobs:
  add_content:
    runs-on: ubuntu-latest
    # Code based on https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions#example-workflow-authenticating-with-a-github-app
    steps:
      - name: Generate token
        id: generate_token
        uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
        with:
          app_id: ${{ secrets.MAKE_BOT_APP_ID }}
          private_key: ${{ secrets.MAKE_BOT_APP_PEM }}

      - name: Get project data
        env:
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
          ORGANIZATION: ${{ vars.ORGANIZATION_NAME }}
          PROJECT_NUMBER: ${{ vars.ORGANIZATION_PROJECT_NUMBER }}
          DATE_FIELD_NAME: ${{ vars.ORGANIZATION_PROJECT__DATE_FIELD_NAME }}
          # This name is hardcoded by GitHub and cannot be changed
          STATUS_FIELD_NAME: Status
          STATUS_FIELD_VALUE_NAME: ${{ inputs.status_field_value_name }}
        run: |
          gh api graphql -f query='
            query ($org: String!, $number: Int!) {
              organization(login: $org) {
                projectV2(number: $number) {
                  id
                  fields(first: 20) {
                    nodes {
                      ... on ProjectV2Field {
                        id
                        name
                      }
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }' -f org="$ORGANIZATION" -F number="$PROJECT_NUMBER" > project_data.json

          {
            # The ID of this repo's GitHub project
            echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)"
            # The ID of the project's date field
            echo "DATE_FIELD_ID=$(jq --arg FIELD_NAME "$DATE_FIELD_NAME" -r '.data.organization.projectV2.fields.nodes[] | select(.name==$FIELD_NAME) | .id' project_data.json)"
            # The ID of the project's status field
            echo "STATUS_FIELD_ID=$(jq --arg FIELD_NAME "$STATUS_FIELD_NAME" -r '.data.organization.projectV2.fields.nodes[] | select(.name==$FIELD_NAME) | .id' project_data.json)"
            # The ID of one of the values of the project's (single select) status field, with name `inputs.status_field_value_name`
            echo "STATUS_FIELD_VALUE_ID=$(jq --arg FIELD_NAME "$STATUS_FIELD_NAME" --arg STATUS_NAME "$STATUS_FIELD_VALUE_NAME" -r '.data.organization.projectV2.fields.nodes[] | select(.name==$FIELD_NAME) | .options[] | select(.name==$STATUS_NAME) | .id' project_data.json)"
          } >> "$GITHUB_ENV"

      - name: Add content to project
        env:
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
          CONTENT_ID: ${{ inputs.content_id }}
        run: |
          item_id="$( gh api graphql -f query='
            mutation ($project: ID!, $content: ID!) {
              addProjectV2ItemById(input: {projectId: $project, contentId: $content}) {
                item {
                  id
                }
              }
            }' -f project="$PROJECT_ID" -f content="$CONTENT_ID" --jq '.data.addProjectV2ItemById.item.id' )"

          # The ID of the project item (representing an issue or a PR with ID `inputs.content_id`), which was either added or already existed
          echo "ITEM_ID=$item_id" >> "$GITHUB_ENV"

      - name: Get project item data
        env:
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
        run: |
          gh api graphql -f query='
            query ($itemId: ID!) {
              node(id: $itemId) {
                ... on ProjectV2Item {
                  fieldValues(first: 20) {
                    nodes {
                      ... on ProjectV2ItemFieldSingleSelectValue {
                        name
                        field {
                          ... on ProjectV2SingleSelectField {
                            id
                          }
                        }
                      }
                      ... on ProjectV2ItemFieldDateValue {
                        date
                        field {
                          ... on ProjectV2Field {
                            id
                          }
                        }
                      }
                    }
                  }
                }
              }
            }' -f itemId="$ITEM_ID" > project_item_data.json

          {
            # The value of the project item's date field (with ID `env.DATE_FIELD_ID`)
            echo "DATE_FIELD_VALUE=$(jq --arg FIELD_ID "$DATE_FIELD_ID" -r '.data.node.fieldValues.nodes[] | select(.field.id==$FIELD_ID) | .date' project_item_data.json)"
            # The value of the project item's status field (with ID `env.STATUS_FIELD_ID`)
            echo "STATUS_FIELD_VALUE=$(jq --arg FIELD_ID "$STATUS_FIELD_ID" -r '.data.node.fieldValues.nodes[] | select(.field.id==$FIELD_ID) | .name' project_item_data.json)"
          } >> "$GITHUB_ENV"

      - name: Set status
        # Only set the status if it's not already set,
        # or if it's a reopened issue/PR (as they should be marked with the value of `ORGANIZATION_PROJECT__STATUS_VALUE_FOR_CLOSED_ITEMS` when closed
        # - see https://github.com/orgs/MAKENTNU/projects/1/workflows/3569450)
        if: ${{ !env.STATUS_FIELD_VALUE
          || github.event.action == 'reopened' && env.STATUS_FIELD_VALUE == vars.ORGANIZATION_PROJECT__STATUS_VALUE_FOR_CLOSED_ITEMS }}
        env:
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
        run: |
          gh api graphql -f query='
            mutation ($project: ID!, $item: ID!, $status_field: ID!, $status_value: String!) {
              set_status: updateProjectV2ItemFieldValue(input: {
                projectId: $project
                itemId: $item
                fieldId: $status_field
                value: {
                  singleSelectOptionId: $status_value
                }
              }) {
                projectV2Item {
                  id
                }
              }
            }' -f project="$PROJECT_ID" -f item="$ITEM_ID" -f status_field="$STATUS_FIELD_ID" -f status_value="$STATUS_FIELD_VALUE_ID" --silent

      - name: Get date
        run: echo "DATE=$(TZ='Europe/Oslo' date --iso-8601)" >> "$GITHUB_ENV"

      - name: Set date
        # Only set the date if it's not already set
        if: ${{ !env.DATE_FIELD_VALUE }}
        env:
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
        run: |
          gh api graphql -f query='
            mutation ($project: ID!, $item: ID!, $date_field: ID!, $date_value: Date!) {
              set_date_posted: updateProjectV2ItemFieldValue(input: {
                projectId: $project
                itemId: $item
                fieldId: $date_field
                value: {
                  date: $date_value
                }
              }) {
                projectV2Item {
                  id
                }
              }
            }' -f project="$PROJECT_ID" -f item="$ITEM_ID" -f date_field="$DATE_FIELD_ID" -f date_value="$DATE" --silent