Skip to content

Instantly share code, notes, and snippets.

@galargh
Created March 8, 2022 13:09
Show Gist options
  • Save galargh/5124957127e0bebc80eb4fc335914789 to your computer and use it in GitHub Desktop.
Save galargh/5124957127e0bebc80eb4fc335914789 to your computer and use it in GitHub Desktop.
How to migrate from GitHub Projects to GitHub Projects (Beta)?

How to migrate from GitHub Projects to GitHub Projects (Beta)?

This is a short tutorial on how to automate the migration process between the legacy project boards and the new projects.

Prerequisites

GITHUB_TOKEN

An exported GITHUB_TOKEN with the following permissions will be required:

  • ...

An existing legacy project board

An object describing the existing legacy project board will be required.

You can retrieve the legacy user project board as follows:

user=''
legacy_project_board_name=''

legacy_project_board="$(
  gh api --paginate "users/${user}/projects" --jq "map(select(.name == \"${legacy_project_board_name}\"))" | 
    jq -n '[inputs] | add | .[0]'
)"

You can retrieve the legacy organisation project board as follows:

org=''
legacy_project_board_name=''

legacy_project_board="$(
  gh api --paginate "orgs/${org}/projects" --jq "map(select(.name == \"${legacy_project_board_name}\"))" | 
    jq -n '[inputs] | add | .[0]'
)"

Migration process

Creating a new project

Unfortunately, new project creation cannot be automated. There are no API endpoints that can create new projects.

Follow either the creating an organization project instructions or the creating a user project instructions to create a new project.

Subsequent steps will require a node ID of the new project.

You can retrieve the new user project as follows:

user=''
new_project_name=''

new_project="$(
  gh api graphql -f query='query($user: String!, $new_project_name: String) {
    user(login: $user) {
      projectsNext(first: 1, query: $new_project_name) {
        nodes {
          id
          fields(first: 100) {
            nodes {
              id
              name
            }
          }
        }
      }
    }
  }' -f user="${user}" -f new_project_name="${new_project_name}" --jq '.data.user.projectsNext.nodes[0]'
)"

You can retrieve the new organisation project as follows:

org=''
new_project_name=''

new_project="$(
  gh api graphql -f query='query($org: String!, $new_project_name: String) {
    organization(login: $org) {
      projectsNext(first: 1, query: $new_project_name) {
        nodes {
          id
          fields(first: 100) {
            nodes {
              id
              name
            }
          }
        }
      }
    }
  }' -f org="${org}" -f new_project_name="${new_project_name}" --jq '.data.organization.projectsNext.nodes[0]'
)"

Synchronising metadata

Since the API allows manipulating new project settings, legacy project name and body can be copied over.

You can synchronise the metadata as follows:

legacy_project_board=''
new_project=''

legacy_project_board_name"$(jq '.name' <<< "${legacy_project_board}")"
legacy_project_board_body"$(jq '.body' <<< "${legacy_project_board}")"

new_project_id="$(jq '.id' <<< "${new_project}")"

gh api graphql -f query='mutation(new_project_id: ID!, legacy_project_board_name: String, legacy_project_board_body: String) {
  updateProjectNext(input: {
    projectId: $new_project_id, 
    title: $legacy_project_board_name,
    description: $legacy_project_board_body
  }) {}
}' -f new_project_id="${new_project_id}" -f legacy_project_board_name="${legacy_project_board_name}" -f legacy_project_board_body="${legacy_project_board_body}"

Synchronising cards

owner=''
repo=''
legacy_project_board=''
new_project=''

legacy_project_board_id="$(jq '.id' <<< "${legacy_project_board}")"
legacy_project_board_columns="$(gh api --paginate "projects/${legacy_project_board_id}/columns" | jq -n '[inputs] | add')"
new_project_id="$(jq '.id' <<< "${new_project}")"
new_project_status_field_id="$(jq '.fields.nodes | map(select(.name == "Status")) | .[0].id' <<< "${new_project}")"

while read legacy_project_board_column_id; do 
  legacy_project_board_column_name="$(jq -r 'map(select(.id == $legacy_project_board_column_id)) | .[0].name' --argjson legacy_project_board_column_id "${legacy_project_board_column_id}" <<< "$legacy_project_board_columns")"
  legacy_project_board_cards="$(gh api --paginate "projects/columns/${legacy_project_board_column_id}/cards" --jq 'map(select(.node_id != null))' | jq -n '[inputs] | add')"
  
  while read legacy_project_board_card_id; do
    legacy_project_board_card="$(jq -r 'map(select(.id == $legacy_project_board_card_id)) | .[0]' --argjson legacy_project_board_card_id "${legacy_project_board_card_id}" <<< "$legacy_project_board_cards")"
    legacy_project_board_card_note="$(jq -r '.note // ""' <<< "${legacy_project_board_card}")"

    if [[ -z "${legacy_project_board_card_note}" ]]; then
      legacy_project_board_card_node_id="$(gh api "repos/${owner}/${repo}/issues -f title="${legacy_project_board_card_note:60}" -f body="${legacy_project_board_card_note}" --jq '.node_id')"
    else
      legacy_project_board_card_node_id="$(jq '.node_id' <<< "${legacy_project_board_card}")"
    fi

    new_project_item_id="$(
      gh api graphql -f query='mutation(new_project_id: ID!, legacy_project_board_card_node_id: ID!) {
        addProjectNextItem(input: {
          projectId: $new_project_id, 
          contentId: $legacy_project_board_card_node_id
        }) {
          projectNextItem {
            id
          }
        }
      }' -f new_project_id="${new_project_id}" -f legacy_project_board_card_node_id="${legacy_project_board_card_node_id}" --jq 'data.addProjectNextItem.projectNextItem.id'
    )"
    
    gh api graphql -f query='mutation(new_project_id: ID!, new_project_item_id: ID!, new_project_status_field_id: ID!, legacy_project_board_column_name: String!) {
      updateProjectNextItemField(input: {
        projectId: $new_project_id,
        itemId: $new_project_item_id,
        fieldId: $new_project_status_field_id,
        value: $legacy_project_board_column_name,
      }) {}
    }' -f new_project_id="${new_project_id}" -f new_project_item_id="${new_project_item_id}" -f new_project_status_field_id="${new_project_status_field_id}" -f legacy_project_board_column_name="${legacy_project_board_column_name}"
  done <<< "$(jq '.[].id' <<< "$legacy_project_board_cards")"
done <<< "$(jq '.[].id' <<< "$legacy_project_board_columns")"

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment