@@ -6,9 +6,104 @@ const { GITHUB_REF = 'main' } = process.env;
6
6
const baseUrl = new URL ( `https://github.com/clerkinc/javascript/blob/${ GITHUB_REF } /` ) ;
7
7
8
8
/**
9
- * @returns {Promise<Map<string,{ version: string, path: string }>> }
9
+ * @typedef {Object } PackageData
10
+ * @property {string } name
11
+ * @property {string } version
12
+ * @property {string } changelogUrl
10
13
*/
11
- async function createPackageMap ( ) {
14
+
15
+ /**
16
+ * @typedef {Object } Pusher
17
+ * @property {string } username
18
+ * @property {string } avatarUrl
19
+ * @property {string } profileUrl
20
+ */
21
+
22
+ /**
23
+ * @typedef {Object } ChangelogData
24
+ * @property {PackageData[] } packageData
25
+ * @property {string } releasePrUrl
26
+ * @property {Pusher } pusher
27
+ */
28
+
29
+ /**
30
+ * @typedef {Object } Formatter
31
+ * @property {(data: ChangelogData) => string } generateChangelog
32
+ */
33
+
34
+ /**
35
+ * Slack is using their own Markdown format, see:
36
+ * https://api.slack.com/reference/surfaces/formatting#basics
37
+ * https://app.slack.com/block-kit-builder
38
+ * @type {Formatter }
39
+ */
40
+ const slackFormatter = {
41
+ generateChangelog : ( { packageData, releasePrUrl, pusher } ) => {
42
+ const markdown = text => ( { type : 'section' , text : { type : 'mrkdwn' , text } } ) ;
43
+ const divider = ( ) => ( { type : 'divider' } ) ;
44
+ const header = text => ( { type : 'header' , text : { type : 'plain_text' , text } } ) ;
45
+ const context = ( imgUrl , text ) => ( {
46
+ type : 'context' ,
47
+ elements : [
48
+ ...( imgUrl ? [ { type : 'image' , image_url : imgUrl , alt_text : 'avatar' } ] : [ ] ) ,
49
+ { type : 'mrkdwn' , text } ,
50
+ ] ,
51
+ } ) ;
52
+ const blocks = [ ] ;
53
+
54
+ blocks . push ( header ( `Javascript SDKs - Stable Release - ${ new Date ( ) . toLocaleDateString ( 'en-US' ) } ` ) ) ;
55
+
56
+ let body = '' ;
57
+ for ( const { name, version, changelogUrl } of packageData ) {
58
+ body += `• <${ changelogUrl } |Changelog> - \`${ name } @${ version } \`\n` ;
59
+ }
60
+
61
+ blocks . push ( markdown ( `All release PRs for this day can be found <${ releasePrUrl } |here>.\nReleased packages:\n` ) ) ;
62
+ blocks . push ( markdown ( body ) ) ;
63
+ // blocks.push(divider());
64
+ blocks . push ( markdown ( '\n' ) ) ;
65
+ blocks . push ( context ( pusher . avatarUrl , `<${ pusher . profileUrl } |*${ pusher . username } *> triggered this release.` ) ) ;
66
+
67
+ return JSON . stringify ( { blocks } ) ;
68
+ } ,
69
+ } ;
70
+
71
+ /**
72
+ * @type {Record<string, Formatter> }
73
+ */
74
+ const formatters = {
75
+ slack : slackFormatter ,
76
+ } ;
77
+
78
+ const run = async ( ) => {
79
+ const releasedPackages = JSON . parse ( process . argv [ 2 ] ) ;
80
+ const packageToPathMap = await createPackageToPathMap ( ) ;
81
+ const packageData = createPackageData ( releasedPackages , packageToPathMap ) ;
82
+ const releasePrUrl = createReleasePrUrl ( ) ;
83
+ const pusher = createPusher ( process . argv [ 3 ] ) ;
84
+ const data = { packageData, releasePrUrl, pusher } ;
85
+
86
+ // TODO: Add support for more formatters
87
+ const formatter = formatters [ 'slack' ] ;
88
+ if ( ! formatter ) {
89
+ throw new Error ( 'Invalid formatter, supported formatters are: ' + Object . keys ( formatters ) . join ( ', ' ) ) ;
90
+ }
91
+ console . log ( formatter . generateChangelog ( data ) ) ;
92
+ } ;
93
+
94
+ run ( ) ;
95
+
96
+ /*
97
+ * @returns {Pusher }
98
+ */
99
+ const createPusher = username => {
100
+ return { username, avatarUrl : `https://github.com/${ username } .png` , profileUrl : `https://github.com/${ username } ` } ;
101
+ } ;
102
+
103
+ /**
104
+ * @returns {Promise<Map<string,string>> }
105
+ */
106
+ async function createPackageToPathMap ( ) {
12
107
const map = new Map ( ) ;
13
108
const packagesRoot = new URL ( '../packages/' , import . meta. url ) ;
14
109
const packages = await glob ( [ '*/package.json' , '*/*/package.json' ] , { cwd : fileURLToPath ( packagesRoot ) } ) ;
@@ -17,46 +112,36 @@ async function createPackageMap() {
17
112
const packageJsonPath = fileURLToPath ( new URL ( pkg , packagesRoot ) ) ;
18
113
const packageJson = fs . readJSONSync ( packageJsonPath ) ;
19
114
if ( ! packageJson . private && packageJson . version ) {
20
- const version = packageJson . version ;
21
- const path = `./packages/${ pkg . replace ( '/package.json' , '' ) } ` ;
22
- map . set ( packageJson . name , { version, path } ) ;
115
+ map . set ( packageJson . name , `./packages/${ pkg . replace ( '/package.json' , '' ) } ` ) ;
23
116
}
24
117
} ) ,
25
118
) ;
26
119
return map ;
27
120
}
28
121
29
- const releasedPackages = JSON . parse ( process . argv [ 2 ] ) ;
30
- const prNumber = process . argv [ 3 ] ;
31
-
32
- // console.debug({ releasedPackages, prNumber });
33
-
34
- const packageMap = await createPackageMap ( ) ;
35
- const packages = releasedPackages . map ( ( { name, version } ) => {
36
- const pkg = packageMap . get ( name ) ;
37
- if ( ! pkg ) {
38
- throw new Error ( `Unable to find entrypoint for "${ name } "!` ) ;
39
- }
40
- const url = new URL ( `${ pkg . path } /CHANGELOG.md#${ version . replace ( / \. / g, '' ) } ` , baseUrl ) . toString ( ) ;
41
- return { name, version, url } ;
42
- } ) ;
43
-
44
- // Slack is using their own Markdown format, see:
45
- // https://api.slack.com/reference/surfaces/formatting#basics
46
- // https://app.slack.com/block-kit-builder
47
- let message = '' ;
48
- message += `*Javascript SDKs - Stable Release - ${ new Date ( ) . toLocaleDateString ( 'en-US' ) } *\n\n` ;
49
- for ( const { name, version, url } of packages ) {
50
- message += `• \`${ name } @${ version } \` - <${ url } |release notes>\n` ;
51
- }
52
-
53
- // TODO: Get PR number using the GitHub API
54
- // if (prNumber) {
55
- // message += `\nView <https://github.com/clerkinc/javascript/pull/${prNumber}|release PR>`;
56
- // }
57
-
58
- message += `\nView <https://github.com/clerkinc/javascript/pulls?q=is%3Apr+is%3Aclosed+Version+Packages+in%3Atitle+merged%3A${ new Date ( )
59
- . toISOString ( )
60
- . slice ( 0 , 10 ) } |all release PRs for this day>`;
122
+ /**
123
+ * @returns {PackageData[] }
124
+ */
125
+ const createPackageData = ( releasedPackages , packageToPathMap ) => {
126
+ return releasedPackages . map ( ( { name, version } ) => {
127
+ const relativePath = packageToPathMap . get ( name ) ;
128
+ if ( ! relativePath ) {
129
+ throw new Error ( `Not found: "${ relativePath } "!` ) ;
130
+ }
131
+ const changelogUrl = new URL ( `${ relativePath } /CHANGELOG.md#${ version . replace ( / \. / g, '' ) } ` , baseUrl ) . toString ( ) ;
132
+ return { name, version, changelogUrl } ;
133
+ } ) ;
134
+ } ;
61
135
62
- console . log ( JSON . stringify ( message ) ) ;
136
+ /**
137
+ * @returns {string }
138
+ */
139
+ const createReleasePrUrl = ( ) => {
140
+ // TODO: Get PR number using the GitHub API
141
+ // if (prNumber) {
142
+ // message += `\nView <https://github.com/clerkinc/javascript/pull/${prNumber}|release PR>`;
143
+ // }
144
+ return `https://github.com/clerkinc/javascript/pulls?q=is%3Apr+is%3Aclosed+Version+Packages+in%3Atitle+merged%3A${ new Date ( )
145
+ . toISOString ( )
146
+ . slice ( 0 , 10 ) } `;
147
+ } ;
0 commit comments