Skip to content

Commit f638c0e

Browse files
committed
chore: add docs, add org logo, add missing package
1 parent cd3bc22 commit f638c0e

3 files changed

Lines changed: 125 additions & 3 deletions

File tree

pnpm-lock.yaml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/apps/script_executor_worker/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@temporalio/client": "~1.11.8",
2424
"@temporalio/workflow": "~1.11.8",
2525
"axios": "^1.6.8",
26+
"csv-parse": "^5.5.6",
2627
"moment": "~2.29.4",
2728
"tsx": "^4.7.1",
2829
"typescript": "^5.6.3"

services/apps/script_executor_worker/src/bin/onboard-projects.ts

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
1-
#!/usr/bin/env tsx
1+
/**
2+
* Project Onboarding Script
3+
*
4+
* This script automates the onboarding of multiple open-source projects from a CSV file
5+
* into the CDP platform. It performs the following operations for each project:
6+
*
7+
* 1. Creates a new project in the specified project group (LF OSS Index)
8+
* 2. Sets up GitHub integration for the project's repository
9+
*
10+
* Features:
11+
* - Batch processing of projects from CSV input
12+
* - Dry run mode for testing (processes only the first project)
13+
* - Error handling and detailed logging
14+
* - Console output of failed projects for easy troubleshooting
15+
*
16+
* Usage:
17+
* tsx src/bin/onboard-projects.ts <bearer-token> <csv-file-path> [--dry-run]
18+
*
19+
* CSV Format:
20+
* project name,project slug,repo url
21+
* My Project,my-project,https://github.com/owner/repo
22+
*
23+
* Environment Variables Required:
24+
* CROWD_API_SERVICE_URL - Base URL for the CDP API service
25+
*/
226
import axios from 'axios'
327
import { parse } from 'csv-parse'
428
import fs from 'fs'
@@ -20,6 +44,18 @@ interface FailedProjectRow extends ProjectRow {
2044

2145
const LF_OSS_INDEX_PROJECT_GROUP_SLUG = 'lf-oss-index'
2246

47+
/**
48+
* Main function to onboard projects from a CSV file
49+
*
50+
* Reads a CSV file containing project information, validates the data,
51+
* and processes each project by creating the project and setting up
52+
* GitHub integration.
53+
*
54+
* @param csvFilePath - Path to the CSV file containing project data
55+
* @param bearerToken - Authentication token for API calls
56+
* @param isDryRun - If true, only processes the first project for testing
57+
* @returns Promise resolving to results summary including success/failure counts and failed projects
58+
*/
2359
async function onboardProjectsFromCsv(
2460
csvFilePath: string,
2561
bearerToken: string,
@@ -136,6 +172,17 @@ async function onboardProjectsFromCsv(
136172
return result
137173
}
138174

175+
/**
176+
* Creates a new project in the CDP platform
177+
*
178+
* Makes an API call to create a project within the LF OSS Index project group.
179+
* The project is created as a non-LF project with the specified name and slug.
180+
*
181+
* @param project - Project data containing name, slug, and repository URL
182+
* @param bearerToken - Authentication token for API authorization
183+
* @returns Promise resolving to the segment ID of the created project
184+
* @throws Error if project creation fails or returns invalid response
185+
*/
139186
async function createProject(project: ProjectRow, bearerToken: string): Promise<string> {
140187
const url = `${process.env['CROWD_API_SERVICE_URL']}/segment/project`
141188

@@ -170,6 +217,46 @@ async function createProject(project: ProjectRow, bearerToken: string): Promise<
170217
}
171218
}
172219

220+
/**
221+
* Fetches the GitHub organization or user avatar URL
222+
*
223+
* Makes an API call to GitHub's users endpoint to retrieve the organization's
224+
* or user's avatar URL for use as a logo in the integration settings.
225+
*
226+
* @param owner - GitHub organization or user name
227+
* @param bearerToken - Authentication token for GitHub API calls
228+
* @returns Promise resolving to the avatar URL, or empty string if fetch fails
229+
*/
230+
async function fetchGithubOrgLogo(owner: string, bearerToken: string): Promise<string> {
231+
try {
232+
const response = await axios.get(`https://api.github.com/users/${owner}`, {
233+
headers: {
234+
Authorization: `Bearer ${bearerToken}`,
235+
'Content-Type': 'application/json',
236+
},
237+
timeout: 10000,
238+
})
239+
240+
return response.data.avatar_url || ''
241+
} catch (error) {
242+
log.warn(`Failed to fetch GitHub logo for ${owner}: ${error.message}`)
243+
return ''
244+
}
245+
}
246+
247+
/**
248+
* Creates a GitHub integration for the specified project
249+
*
250+
* Sets up GitHub repository integration by parsing the repository URL,
251+
* creating integration settings with repository mapping, and linking
252+
* it to the project segment.
253+
*
254+
* @param project - Project data containing repository URL and metadata
255+
* @param segmentId - The segment ID of the created project to link integration to
256+
* @param bearerToken - Authentication token for API authorization
257+
* @returns Promise that resolves when integration is successfully created
258+
* @throws Error if integration creation fails or returns invalid response
259+
*/
173260
async function createGithubIntegration(
174261
project: ProjectRow,
175262
segmentId: string,
@@ -178,6 +265,9 @@ async function createGithubIntegration(
178265
// Parse GitHub repo URL to extract owner and repo name
179266
const { owner, repo } = parseGithubUrl(project.repoUrl)
180267

268+
// Fetch organization logo
269+
const orgLogo = await fetchGithubOrgLogo(owner, bearerToken)
270+
181271
// Create integration
182272
const integrationUrl = `${process.env['CROWD_API_SERVICE_URL']}/github-nango-connect`
183273

@@ -187,7 +277,7 @@ async function createGithubIntegration(
187277
{
188278
name: owner,
189279
url: project.repoUrl,
190-
logo: '',
280+
logo: orgLogo,
191281
fullSync: false,
192282
updatedAt: new Date().toISOString(),
193283
repos: [
@@ -231,6 +321,20 @@ async function createGithubIntegration(
231321
}
232322
}
233323

324+
/**
325+
* Parses a GitHub repository URL to extract owner and repository name
326+
*
327+
* Handles various GitHub URL formats including HTTPS and SSH URLs.
328+
* Normalizes SSH URLs to HTTPS format for consistent parsing.
329+
*
330+
* @param repoUrl - GitHub repository URL (HTTPS or SSH format)
331+
* @returns Object containing the repository owner and name
332+
* @throws Error if URL format is invalid or cannot be parsed
333+
*
334+
* @example
335+
* parseGithubUrl('https://github.com/owner/repo') // { owner: 'owner', repo: 'repo' }
336+
* parseGithubUrl('git@github.com:owner/repo.git') // { owner: 'owner', repo: 'repo' }
337+
*/
234338
function parseGithubUrl(repoUrl: string): { owner: string; repo: string } {
235339
try {
236340
// Handle different GitHub URL formats
@@ -253,6 +357,20 @@ function parseGithubUrl(repoUrl: string): { owner: string; repo: string } {
253357
}
254358
}
255359

360+
/**
361+
* Main entry point for the project onboarding script
362+
*
363+
* Handles command-line argument parsing, file validation, and orchestrates
364+
* the project onboarding process. Displays results and error information
365+
* to the console upon completion.
366+
*
367+
* Command-line arguments:
368+
* - bearerToken: Authentication token for API calls
369+
* - csvFilePath: Path to CSV file containing project data
370+
* - --dry-run: Optional flag to process only the first project
371+
*
372+
* Exits with code 0 on success, 1 if any projects failed to onboard
373+
*/
256374
async function main() {
257375
const args = process.argv.slice(2)
258376

0 commit comments

Comments
 (0)