1+ import { PrismaClient } from '@sourcebot/db' ;
2+ import { readFile } from 'fs/promises' ;
3+ import stripJsonComments from 'strip-json-comments' ;
4+ import { getGitHubReposFromConfig } from "./github.js" ;
5+ import { getGitLabReposFromConfig , GITLAB_CLOUD_HOSTNAME } from "./gitlab.js" ;
6+ import { SourcebotConfigurationSchema } from "./schemas/v2.js" ;
7+ import { AppContext } from "./types.js" ;
8+ import { getTokenFromConfig , isRemotePath , marshalBool } from "./utils.js" ;
9+
10+ export const syncConfig = async ( configPath : string , db : PrismaClient , signal : AbortSignal , ctx : AppContext ) => {
11+ const configContent = await ( async ( ) => {
12+ if ( isRemotePath ( configPath ) ) {
13+ const response = await fetch ( configPath , {
14+ signal,
15+ } ) ;
16+ if ( ! response . ok ) {
17+ throw new Error ( `Failed to fetch config file ${ configPath } : ${ response . statusText } ` ) ;
18+ }
19+ return response . text ( ) ;
20+ } else {
21+ return readFile ( configPath , {
22+ encoding : 'utf-8' ,
23+ signal,
24+ } ) ;
25+ }
26+ } ) ( ) ;
27+
28+ // @todo : we should validate the configuration file's structure here.
29+ const config = JSON . parse ( stripJsonComments ( configContent ) ) as SourcebotConfigurationSchema ;
30+
31+ for ( const repoConfig of config . repos ?? [ ] ) {
32+ switch ( repoConfig . type ) {
33+ case 'github' : {
34+ const token = repoConfig . token ? getTokenFromConfig ( repoConfig . token , ctx ) : undefined ;
35+ const gitHubRepos = await getGitHubReposFromConfig ( repoConfig , signal , ctx ) ;
36+ const hostUrl = repoConfig . url ?? 'https://github.com' ;
37+ const hostname = repoConfig . url ? new URL ( repoConfig . url ) . hostname : 'github.com' ;
38+
39+ await Promise . all ( gitHubRepos . map ( ( repo ) => {
40+ const repoName = `${ hostname } /${ repo . full_name } ` ;
41+ const cloneUrl = new URL ( repo . clone_url ! ) ;
42+ if ( token ) {
43+ cloneUrl . username = token ;
44+ }
45+
46+ const data = {
47+ external_id : repo . id . toString ( ) ,
48+ external_codeHostType : 'github' ,
49+ external_codeHostUrl : hostUrl ,
50+ cloneUrl : cloneUrl . toString ( ) ,
51+ name : repoName ,
52+ isFork : repo . fork ,
53+ isArchived : ! ! repo . archived ,
54+ metadata : {
55+ 'zoekt.web-url-type' : 'github' ,
56+ 'zoekt.web-url' : repo . html_url ,
57+ 'zoekt.name' : repoName ,
58+ 'zoekt.github-stars' : ( repo . stargazers_count ?? 0 ) . toString ( ) ,
59+ 'zoekt.github-watchers' : ( repo . watchers_count ?? 0 ) . toString ( ) ,
60+ 'zoekt.github-subscribers' : ( repo . subscribers_count ?? 0 ) . toString ( ) ,
61+ 'zoekt.github-forks' : ( repo . forks_count ?? 0 ) . toString ( ) ,
62+ 'zoekt.archived' : marshalBool ( repo . archived ) ,
63+ 'zoekt.fork' : marshalBool ( repo . fork ) ,
64+ 'zoekt.public' : marshalBool ( repo . private === false )
65+ } ,
66+ } ;
67+
68+ return db . repo . upsert ( {
69+ where : {
70+ external_id_external_codeHostUrl : {
71+ external_id : repo . id . toString ( ) ,
72+ external_codeHostUrl : hostUrl ,
73+ } ,
74+ } ,
75+ create : data ,
76+ update : data ,
77+ } )
78+ } ) ) ;
79+
80+ break ;
81+ }
82+ case 'gitlab' : {
83+ const hostUrl = repoConfig . url ?? 'https://gitlab.com' ;
84+ const hostname = repoConfig . url ? new URL ( repoConfig . url ) . hostname : GITLAB_CLOUD_HOSTNAME ;
85+ const token = repoConfig . token ? getTokenFromConfig ( repoConfig . token , ctx ) : undefined ;
86+ const gitLabRepos = await getGitLabReposFromConfig ( repoConfig , ctx ) ;
87+
88+ await Promise . all ( gitLabRepos . map ( ( project ) => {
89+ const repoName = `${ hostname } /${ project . path_with_namespace } ` ;
90+ const isFork = project . forked_from_project !== undefined ;
91+
92+ const cloneUrl = new URL ( project . http_url_to_repo ) ;
93+ if ( token ) {
94+ cloneUrl . username = 'oauth2' ;
95+ cloneUrl . password = token ;
96+ }
97+
98+ const data = {
99+ external_id : project . id . toString ( ) ,
100+ external_codeHostType : 'gitlab' ,
101+ external_codeHostUrl : hostUrl ,
102+ cloneUrl : cloneUrl . toString ( ) ,
103+ name : repoName ,
104+ isFork,
105+ isArchived : project . archived ,
106+ metadata : {
107+ 'zoekt.web-url-type' : 'gitlab' ,
108+ 'zoekt.web-url' : project . web_url ,
109+ 'zoekt.name' : repoName ,
110+ 'zoekt.gitlab-stars' : project . star_count ?. toString ( ) ?? '0' ,
111+ 'zoekt.gitlab-forks' : project . forks_count ?. toString ( ) ?? '0' ,
112+ 'zoekt.archived' : marshalBool ( project . archived ) ,
113+ 'zoekt.fork' : marshalBool ( isFork ) ,
114+ 'zoekt.public' : marshalBool ( project . visibility === 'public' ) ,
115+ }
116+ }
117+
118+ return db . repo . upsert ( {
119+ where : {
120+ external_id_external_codeHostUrl : {
121+ external_id : project . id . toString ( ) ,
122+ external_codeHostUrl : hostUrl ,
123+ } ,
124+ } ,
125+ create : data ,
126+ update : data ,
127+ } )
128+ } ) ) ;
129+
130+ break ;
131+ }
132+ }
133+ }
134+ }
0 commit comments