1+ const fs = require ( 'fs' ) ;
2+ const fsPromises = require ( 'fs' ) . promises ;
3+ const path = require ( 'path' ) ;
4+
5+ async function getIntegrations ( ) {
6+ const bearerToken = process . env . BOTPRESS_TOKEN ;
7+ const workspaceId = process . env . BOTPRESS_WORKSPACE_ID ;
8+
9+ if ( ! bearerToken ) {
10+ throw new Error ( 'BOTPRESS_TOKEN environment variable is required' ) ;
11+ }
12+
13+ if ( ! workspaceId ) {
14+ throw new Error ( 'BOTPRESS_WORKSPACE_ID environment variable is required' ) ;
15+ }
16+
17+ const options = {
18+ method : 'GET' ,
19+ headers : {
20+ Authorization : `Bearer ${ bearerToken } ` ,
21+ 'x-workspace-id' : workspaceId
22+ } ,
23+ body : undefined
24+ } ;
25+
26+ let allIntegrations = [ ] ;
27+ let nextToken = null ;
28+ let pageCount = 0 ;
29+
30+ try {
31+ do {
32+ pageCount ++ ;
33+ console . log ( `Fetching integrations page ${ pageCount } ${ nextToken ? ` (token: ${ nextToken . substring ( 0 , 20 ) } ...)` : '' } ` ) ;
34+
35+ let url = 'https://api.botpress.cloud/v1/admin/hub/integrations?sortBy=name&limit=1000&version=latest' ;
36+ if ( nextToken ) {
37+ url += `&nextToken=${ encodeURIComponent ( nextToken ) } ` ;
38+ }
39+
40+ const response = await fetch ( url , options ) ;
41+ if ( ! response . ok ) {
42+ throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
43+ }
44+
45+ const data = await response . json ( ) ;
46+
47+ if ( data . integrations && Array . isArray ( data . integrations ) ) {
48+ allIntegrations = allIntegrations . concat ( data . integrations ) ;
49+ console . log ( `Retrieved ${ data . integrations . length } integrations from page ${ pageCount } (total: ${ allIntegrations . length } )` ) ;
50+ }
51+
52+ nextToken = data . meta ?. nextToken || null ;
53+
54+ } while ( nextToken ) ;
55+
56+ console . log ( `Completed pagination. Total integrations retrieved: ${ allIntegrations . length } ` ) ;
57+
58+ return {
59+ integrations : allIntegrations
60+ } ;
61+ } catch ( error ) {
62+ console . error ( 'Error fetching integrations:' , error ) ;
63+ throw error ;
64+ }
65+ }
66+
67+ function filterIntegrations ( data ) {
68+ if ( ! data || ! data . integrations ) {
69+ return { } ;
70+ }
71+
72+ const filtered = data . integrations
73+ . filter ( integration => integration . ownerWorkspace ?. handle === 'botpress' )
74+ . reduce ( ( acc , integration ) => {
75+ acc [ integration . name ] = {
76+ 'version' : integration . version ,
77+ 'id' : integration . id
78+ }
79+ return acc ;
80+ } , { } ) ;
81+
82+ return filtered ;
83+ }
84+
85+ async function getCurrentVersions ( ) {
86+ try {
87+ const filePath = './snippets/integrations/versions.mdx' ;
88+ const content = await fsPromises . readFile ( filePath , 'utf8' ) ;
89+
90+ const match = content . match ( / e x p o r t c o n s t i n t e g r a t i o n V e r s i o n s = ( { [ \s \S ] * } ) / ) ;
91+ if ( ! match ) {
92+ throw new Error ( 'Could not parse current versions file' ) ;
93+ }
94+
95+ return JSON . parse ( match [ 1 ] ) ;
96+ } catch ( error ) {
97+ console . error ( 'Error reading current versions:' , error ) ;
98+ throw error ;
99+ }
100+ }
101+
102+ function compareVersions ( current , latest ) {
103+ const updates = [ ] ;
104+ const newIntegrations = [ ] ;
105+
106+ for ( const [ name , latestInfo ] of Object . entries ( latest ) ) {
107+ if ( current [ name ] ) {
108+ const versionChanged = current [ name ] . version !== latestInfo . version ;
109+ const idChanged = current [ name ] . id !== latestInfo . id ;
110+
111+ if ( versionChanged || idChanged ) {
112+ updates . push ( {
113+ name,
114+ currentVersion : current [ name ] . version ,
115+ latestVersion : latestInfo . version ,
116+ currentId : current [ name ] . id ,
117+ latestId : latestInfo . id ,
118+ versionChanged,
119+ idChanged
120+ } ) ;
121+ }
122+ } else {
123+ newIntegrations . push ( {
124+ name,
125+ version : latestInfo . version ,
126+ id : latestInfo . id
127+ } ) ;
128+ }
129+ }
130+
131+ return { updates, newIntegrations } ;
132+ }
133+
134+ async function updateVersionsFile ( latestVersions ) {
135+ const content = `export const integrationVersions = ${ JSON . stringify ( latestVersions , null , 2 ) } ` ;
136+ const filePath = './snippets/integrations/versions.mdx' ;
137+
138+ await fsPromises . writeFile ( filePath , content , 'utf8' ) ;
139+ console . log ( `Updated versions file: ${ filePath } ` ) ;
140+ }
141+
142+ async function setGitHubOutput ( name , value ) {
143+ if ( process . env . GITHUB_OUTPUT ) {
144+ if ( typeof value === 'string' && value . includes ( '\n' ) ) {
145+ const delimiter = `EOF_${ Math . random ( ) . toString ( 36 ) . substring ( 7 ) } ` ;
146+ fs . appendFileSync ( process . env . GITHUB_OUTPUT , `${ name } <<${ delimiter } \n${ value } \n${ delimiter } \n` ) ;
147+ } else {
148+ fs . appendFileSync ( process . env . GITHUB_OUTPUT , `${ name } =${ value } \n` ) ;
149+ }
150+ } else {
151+ console . log ( `Output: ${ name } =${ value } ` ) ;
152+ }
153+ }
154+
155+ function formatChangesSummary ( updates , newIntegrations ) {
156+ let summary = '' ;
157+
158+ if ( updates . length > 0 ) {
159+ summary += `**Integration Updates (${ updates . length } ):**\n` ;
160+ updates . forEach ( update => {
161+ const changes = [ ] ;
162+ if ( update . versionChanged ) {
163+ changes . push ( `${ update . currentVersion } -> ${ update . latestVersion } ` ) ;
164+ }
165+ if ( update . idChanged ) {
166+ changes . push ( `${ update . currentId } -> ${ update . latestId } ` ) ;
167+ }
168+ summary += `- ${ update . name } : ${ changes . join ( ', ' ) } \n` ;
169+ } ) ;
170+ }
171+
172+ if ( newIntegrations . length > 0 ) {
173+ if ( summary ) summary += '\n' ;
174+ summary += `**New Integrations (${ newIntegrations . length } ):**\n` ;
175+ newIntegrations . forEach ( integration => {
176+ summary += `- ${ integration . name } : ${ integration . version } (${ integration . id } )\n` ;
177+ } ) ;
178+ }
179+
180+ return summary . trim ( ) ;
181+ }
182+
183+ async function main ( ) {
184+ try {
185+ console . log ( 'Fetching latest integration versions from Botpress API...' ) ;
186+ const integrations = await getIntegrations ( ) ;
187+ const latestVersions = filterIntegrations ( integrations ) ;
188+
189+ console . log ( 'Reading current versions file...' ) ;
190+ const currentVersions = await getCurrentVersions ( ) ;
191+
192+ console . log ( 'Comparing versions...' ) ;
193+ const { updates, newIntegrations } = compareVersions ( currentVersions , latestVersions ) ;
194+
195+ const hasUpdates = updates . length > 0 || newIntegrations . length > 0 ;
196+
197+ if ( hasUpdates ) {
198+ console . log ( `Found ${ updates . length } integration updates and ${ newIntegrations . length } new integrations` ) ;
199+
200+ await updateVersionsFile ( latestVersions ) ;
201+
202+ const changesSummary = formatChangesSummary ( updates , newIntegrations ) ;
203+ const updatedIntegrationsList = [
204+ ...updates . map ( u => u . name ) ,
205+ ...newIntegrations . map ( i => i . name )
206+ ] . join ( ', ' ) ;
207+
208+ await setGitHubOutput ( 'has_updates' , 'true' ) ;
209+ await setGitHubOutput ( 'changes_summary' , changesSummary ) ;
210+ await setGitHubOutput ( 'updated_integrations' , updatedIntegrationsList ) ;
211+
212+ console . log ( 'Changes Summary:' ) ;
213+ console . log ( changesSummary ) ;
214+ } else {
215+ console . log ( 'No integration updates found' ) ;
216+ await setGitHubOutput ( 'has_updates' , 'false' ) ;
217+ }
218+
219+ process . exit ( 0 ) ;
220+ } catch ( error ) {
221+ console . error ( 'Error in version check:' , error ) ;
222+ await setGitHubOutput ( 'has_updates' , 'false' ) ;
223+ process . exit ( 1 ) ;
224+ }
225+ }
226+
227+ if ( require . main === module ) {
228+ main ( ) ;
229+ }
0 commit comments