1+ import * as fs from 'fs' ;
2+ import * as path from 'path' ;
3+
4+ interface ApiVersion {
5+ name : string ;
6+ version : string ;
7+ }
8+
9+ /**
10+ * Extracts API name and version from directory name
11+ * Example: "assignments-oas-1.0.35" -> { name: "assignments", version: "1.0.35" }
12+ */
13+ function extractApiInfo ( dirName : string ) : ApiVersion | null {
14+ // Match pattern like "api-name-oas-version"
15+ const match = dirName . match ( / ^ ( .+ ) - o a s - ( .+ ) $ / ) ;
16+ if ( ! match ) {
17+ return null ;
18+ }
19+
20+ const apiName = match [ 1 ] ;
21+ const version = match [ 2 ] ;
22+
23+ // Validate version format (should be like 1.0.35, 2.0.16, etc.)
24+ if ( ! / ^ \d + \. \d + \. \d + $ / . test ( version ) ) {
25+ return null ;
26+ }
27+
28+ return { name : apiName , version } ;
29+ }
30+
31+ /**
32+ * Scans the apis directory and extracts all API versions
33+ */
34+ function scanApisDirectory ( ) : ApiVersion [ ] {
35+ const apisDir = path . join ( process . cwd ( ) , 'apis' ) ;
36+ const apis : ApiVersion [ ] = [ ] ;
37+
38+ if ( ! fs . existsSync ( apisDir ) ) {
39+ console . error ( 'apis directory not found' ) ;
40+ return apis ;
41+ }
42+
43+ const apiDirs = fs . readdirSync ( apisDir , { withFileTypes : true } )
44+ . filter ( dirent => dirent . isDirectory ( ) )
45+ . map ( dirent => dirent . name ) ;
46+
47+ for ( const apiDir of apiDirs ) {
48+ const apiPath = path . join ( apisDir , apiDir ) ;
49+ const versionDirs = fs . readdirSync ( apiPath , { withFileTypes : true } )
50+ . filter ( dirent => dirent . isDirectory ( ) )
51+ . map ( dirent => dirent . name ) ;
52+
53+ for ( const versionDir of versionDirs ) {
54+ const apiInfo = extractApiInfo ( versionDir ) ;
55+ if ( apiInfo ) {
56+ apis . push ( apiInfo ) ;
57+ }
58+ }
59+ }
60+
61+ return apis ;
62+ }
63+
64+ /**
65+ * Generates the API version table markdown
66+ */
67+ function generateApiVersionTable ( apis : ApiVersion [ ] ) : string {
68+ if ( apis . length === 0 ) {
69+ return '' ;
70+ }
71+ // Sort by API name, then version (descending)
72+ apis . sort ( ( a , b ) => {
73+ if ( a . name !== b . name ) return a . name . localeCompare ( b . name ) ;
74+ return compareVersions ( b . version , a . version ) ; // descending version
75+ } ) ;
76+ const table = [
77+ '| API Name | API Version |' ,
78+ '|----------|-------------|' ,
79+ ...apis . map ( api => `| ${ api . name } | ${ api . version } |` )
80+ ] . join ( '\n' ) ;
81+
82+ return `\n### API Versions\n\n${ table } \n` ;
83+ }
84+
85+ /**
86+ * Compares two version strings
87+ * Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
88+ */
89+ function compareVersions ( v1 : string , v2 : string ) : number {
90+ const parts1 = v1 . split ( '.' ) . map ( Number ) ;
91+ const parts2 = v2 . split ( '.' ) . map ( Number ) ;
92+
93+ for ( let i = 0 ; i < Math . max ( parts1 . length , parts2 . length ) ; i ++ ) {
94+ const part1 = parts1 [ i ] || 0 ;
95+ const part2 = parts2 [ i ] || 0 ;
96+
97+ if ( part1 > part2 ) return 1 ;
98+ if ( part1 < part2 ) return - 1 ;
99+ }
100+
101+ return 0 ;
102+ }
103+
104+ /**
105+ * Updates the CHANGELOG.md file with the API version table under the latest version section
106+ */
107+ function updateChangelog ( apiTable : string ) : void {
108+ const changelogPath = path . join ( process . cwd ( ) , 'CHANGELOG.md' ) ;
109+
110+ if ( ! fs . existsSync ( changelogPath ) ) {
111+ console . error ( 'CHANGELOG.md not found' ) ;
112+ return ;
113+ }
114+
115+ let content = fs . readFileSync ( changelogPath , 'utf8' ) ;
116+
117+ // Find the first version section (## vX.Y.Z)
118+ const versionSectionRegex = / ^ ( # # v \d + \. \d + \. \d + \s * ) $ / m;
119+ const match = content . match ( versionSectionRegex ) ;
120+
121+ if ( ! match ) {
122+ console . error ( 'No version section found in CHANGELOG.md' ) ;
123+ return ;
124+ }
125+
126+ // Remove any existing API Versions table under the first version section
127+ const apiTableRegex = / ( # # v \d + \. \d + \. \d + \s * ) ( \n # # # A P I V e r s i o n s \n [ \s \S ] * ?) (? = \n # # | \n # | $ ) / m;
128+ if ( apiTableRegex . test ( content ) ) {
129+ content = content . replace ( apiTableRegex , `$1` ) ; // Remove old table
130+ }
131+
132+ // Insert the table after the first version header
133+ const insertIndex = match . index ! + match [ 0 ] . length ;
134+ const beforeSection = content . substring ( 0 , insertIndex ) ;
135+ const afterSection = content . substring ( insertIndex ) ;
136+
137+ const updatedContent = beforeSection + apiTable + afterSection ;
138+
139+ fs . writeFileSync ( changelogPath , updatedContent , 'utf8' ) ;
140+ console . log ( 'Successfully updated CHANGELOG.md with API version table' ) ;
141+ }
142+
143+ /**
144+ * Main function
145+ */
146+ function main ( ) : void {
147+ console . log ( 'Scanning apis directory for API versions...' ) ;
148+
149+ const allApis = scanApisDirectory ( ) ;
150+ console . log ( `Found ${ allApis . length } API versions` ) ;
151+
152+ const apiTable = generateApiVersionTable ( allApis ) ;
153+
154+ if ( apiTable ) {
155+ updateChangelog ( apiTable ) ;
156+ console . log ( 'API version table generated successfully' ) ;
157+ } else {
158+ console . log ( 'No valid API versions found' ) ;
159+ }
160+ }
161+
162+ // Run the script
163+ if ( require . main === module ) {
164+ main ( ) ;
165+ }
166+
167+ export { main , scanApisDirectory , generateApiVersionTable } ;
0 commit comments