Skip to content

Instantly share code, notes, and snippets.

@dennisameling
Created June 28, 2024 11:24
Show Gist options
  • Save dennisameling/67b05435e394a029a4aeb2fa13d84502 to your computer and use it in GitHub Desktop.
Save dennisameling/67b05435e394a029a4aeb2fa13d84502 to your computer and use it in GitHub Desktop.
ClickUp to GitLab integration - automating linking your GitLab repositories to ClickUp projects
/**
* ========= CONFIGURATION =========
* 1. Set the `teamId` to the ID of the ClickUp team you want to add the repositories to.
* 2. Set the `clickUpProjects` to the IDs of the ClickUp projects you want to associate the repositories with.
* 3. Set the `headers` to the headers you need to authenticate with ClickUp.
*/
const teamId = 1234567
const clickUpProjects = []
const headers = {
"Authorization": "Bearer YOUR_AUTH_TOKEN_HERE"
}
const baseUrl = `https://prod-us-west-2-2.clickup.com/integrations/v2/gl/team/${teamId}`
// The GitLab-to-ClickUp project mapping still seems to be using an old/legacy URL
const legacyBaseUrl = `https://prod-us-west-2-2.clickup.com/integrations/v1/team/${teamId}/gl_repo`
/**
* ========= FUNCTIONS =========
*
* You typically shouldn't need to modify these functions. Skip to the "RUNNING THE SCRIPT" section below.
*/
const deleteAllRepos = async () => {
const repos = await fetch(
`${baseUrl}/repo`,
{ headers }
)
const parsedRepos = await repos.json()
console.log(`Found ${parsedRepos.length} repos to delete`)
for (const repo of parsedRepos) {
console.log(`Deleting ${repo.gl_repo_name} (${repo.gl_repo_id})...`)
await fetch(
`${baseUrl}/repo/${repo.gl_repo_id}`,
{
method: 'DELETE',
headers
}
)
console.log(`Deleted ${repo.gl_repo_name} (${repo.gl_repo_id}).`)
}
console.log('All repos deleted')
}
const getAllRespositories = async () => {
const allRepos = []
let page = 1
while (page > 0) {
const reposResponse = await fetchAvailableRepositories(page)
allRepos.push(...reposResponse.repos)
console.log(`Fetched ${reposResponse.repos.length} repos. Total so far: ${allRepos.length}`)
if (!reposResponse.hasMore) {
console.log('Finished fetching repos.')
break
}
page++
}
return allRepos
}
/**
* Repos have the following structure:
* {
* "gl_repo_id": "12345678",
* "gl_repo_name": "repo-name-here",
* "gl_repo_owner": "12345678",
* "gl_repo_owner_name": "subgroup-name-here",
* "gl_repo_full_slug": "your-group-here/subgroup-name-here/repo-name-here",
* "gl_repo_slug": "repo-slug-here"
* }
*/
const fetchAvailableRepositories = async (page) => {
const response = await fetch(
`${baseUrl}/repos/search?query=&page=${page}`,
{ headers }
)
// Returns "repos" (array of repos) and "hasMore" (integer if there are more pages to fetch, and "false" (boolean) otherwise)
return await response.json()
}
const addRepository = async (repo) => {
console.log(`Adding repo ${repo.gl_repo_name}...`)
const response = await fetch(
`${baseUrl}/repo`,
{
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify(repo)
}
)
const responseBody = await response.json()
if (response.status === 400 && responseBody?.ECODE === "GLR_011") {
console.log(`Repo ${repo.gl_repo_name} already exists. Skipping.`)
} else if (!response.ok) {
throw new Error(`Failed to add ${repo.gl_repo_name}. Status: ${response.status}. Data: ${await response.json()}`)
} else {
console.log(`Added ${repo.gl_repo_name}.`)
}
}
const configureClickUpProjectsForRepos = async (repos) => {
console.log(`Configuring ClickUp projects for all repos... This might take a while!`)
const response = await fetch(
`${legacyBaseUrl}/projects`,
{
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({
repos: repos.map(repo => ({
gl_repo_id: repo.gl_repo_id,
projects: clickUpProjects
}))
})
}
)
if (!response.ok) {
throw new Error(`Failed to configure projects for all repos. Status: ${response.status}. Data: ${await response.json()}`)
} else {
console.log(`Configured projects for all repos.`)
}
}
/**
* ========= RUNNING THE SCRIPT =========
*
* By default, the script will:
* - Fetch all available GitLab repositories
* - Add them to ClickUp
* - Configure the projects for which each repo should be available
*
* If you want to remove all repos from ClickUp first (e.g. when you want to reauthorize
* with a different user), you can uncomment the `deleteAllRepos` function below.
*/
// deleteAllRepos()
getAllRespositories().then(async repos => {
// Add repos to ClickUp
for (const repo of repos) {
await addRepository(repo)
}
// Configure the projects for which each repo should be available
await configureClickUpProjectsForRepos(repos)
console.log('All done!')
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment