1
1
import { Octokit } from "octokit" ;
2
- import { SFNClient , StartExecutionCommand } from ' @aws-sdk/client-sfn' ;
2
+ import { SFNClient , StartExecutionCommand } from " @aws-sdk/client-sfn" ;
3
3
import { getSecret } from "./utils/secrets" ;
4
+ import { join } from "path" ;
5
+ import { S3Client , PutObjectCommand } from "@aws-sdk/client-s3" ;
4
6
5
7
const sfn = new SFNClient ( { } ) ;
8
+ const s3 = new S3Client ( { } ) ;
9
+ const blogPathDefined = ! ! (
10
+ process . env . BLOG_PATH && process . env . BLOG_PATH !== "/"
11
+ ) ;
6
12
let octokit : Octokit ;
7
13
8
14
export const handler = async ( event : any ) => {
9
15
try {
10
16
await initializeOctokit ( ) ;
11
17
12
- const recentCommits = await getRecentCommits ( ) ;
13
- if ( recentCommits . length ) {
14
- const newContent = await getNewContent ( recentCommits ) ;
15
- if ( newContent . length ) {
16
- const data = await getContentData ( newContent ) ;
17
- await processNewContent ( data ) ;
18
+ let newContent : { fileName : string ; commit : string } [ ] = [ ] ;
19
+ if ( event . body ) {
20
+ const body = JSON . parse ( event . body ) ;
21
+ console . log ( JSON . stringify ( { body } , null , 2 ) ) ;
22
+ if ( body . commits ) {
23
+ newContent = body . commits . reduce (
24
+ (
25
+ p : { fileName : string ; commit : string } [ ] ,
26
+ commit : {
27
+ id : string ;
28
+ added : string [ ] ;
29
+ modified : string [ ] ;
30
+ // ... there's more stuff here, but this is all we need
31
+ }
32
+ ) => {
33
+ const addedFiles = commit . added . filter (
34
+ ( addedFile : string ) =>
35
+ ( ! blogPathDefined ||
36
+ addedFile . startsWith ( `${ process . env . BLOG_PATH } /` ) ) &&
37
+ addedFile . endsWith ( ".md" )
38
+ ) ;
39
+ return [
40
+ ...p ,
41
+ ...addedFiles . map ( ( addedFile ) => ( {
42
+ fileName : addedFile ,
43
+ commit : commit . id ,
44
+ } ) ) ,
45
+ ] ;
46
+ } ,
47
+ [ ] as { fileName : string ; commit : string } [ ]
48
+ ) ;
49
+ } else {
50
+ const recentCommits = await getRecentCommits ( ) ;
51
+ if ( recentCommits . length ) {
52
+ newContent = await getNewContent ( recentCommits ) ;
53
+ }
18
54
}
19
55
}
56
+ if ( newContent . length ) {
57
+ const data = await getContentData ( newContent ) ;
58
+ const imagesProcessed = await saveImagesToS3 ( data ) ;
59
+ await processNewContent ( imagesProcessed ) ;
60
+ }
20
61
} catch ( err ) {
21
62
console . error ( err ) ;
22
63
}
@@ -60,10 +101,12 @@ const getNewContent = async (commits: string[]) => {
60
101
ref : commits [ j ] ,
61
102
} ) ;
62
103
63
- const blogPath = process . env . BLOG_PATH && process . env . BLOG_PATH !== "/" ;
64
104
const newFiles = commitDetail . data . files ?. filter (
65
105
( f ) =>
66
- f . status == "added" && ( ! blogPath || f . filename . startsWith ( `${ process . env . BLOG_PATH } /` ) )
106
+ f . status == "added" &&
107
+ ( ! blogPathDefined ||
108
+ f . filename . startsWith ( `${ process . env . BLOG_PATH } /` ) ) &&
109
+ f . filename . endsWith ( ".md" )
67
110
) ;
68
111
newContent . push (
69
112
...( newFiles ?. map ( ( p ) => {
@@ -120,8 +163,72 @@ const saveImagesToS3 = async (
120
163
sendStatusEmail : boolean ;
121
164
} [ ]
122
165
) => {
123
- // TODO: regex for images stored in github and fetch them / store them in a public s3 bucket
124
- }
166
+ const contentData : {
167
+ fileName : string ;
168
+ commit : string ;
169
+ content : string ;
170
+ sendStatusEmail : boolean ;
171
+ } [ ] = [ ] ;
172
+ const imgRegex = / ! \[ ( .* ?) \] \( ( .* ?) \) / g;
173
+ for ( let j = 0 ; j < newContent . length ; j ++ ) {
174
+ const workingContent = { ...newContent [ j ] } ;
175
+ const imageSet = new Set < string > ( [ ] ) ;
176
+ let match ;
177
+ while ( ( match = imgRegex . exec ( newContent [ j ] . content ) ) !== null ) {
178
+ imageSet . add ( match [ 2 ] ) ;
179
+ }
180
+ const images = [ ...imageSet ] ;
181
+ if ( images . length === 0 ) {
182
+ // no images in the post... passthrough
183
+ contentData . push ( newContent [ j ] ) ;
184
+ continue ;
185
+ }
186
+ const blogFile = newContent [ j ] . fileName ;
187
+ const blogSplit = `${ blogFile } ` . split ( "/" ) ;
188
+ blogSplit . pop ( ) ;
189
+ const blogBase = blogSplit . join ( "/" ) ;
190
+ const s3Mapping : Record < string , string > = { } ;
191
+ for ( let k = 0 ; k < images . length ; k ++ ) {
192
+ const image = images [ k ] ;
193
+ const githubPath = join ( blogBase , image ) ;
194
+ const imageSplit = image . split ( "." ) ;
195
+ const imageExtension = imageSplit [ imageSplit . length - 1 ] ;
196
+ const s3Path = `${ blogFile } /${ k } .${ imageExtension } ` . replace ( / \ / g, "-" ) ;
197
+ const s3Url = `https://s3.amazonaws.com/${ process . env . MEDIA_BUCKET } /${ s3Path } ` ;
198
+ console . log (
199
+ JSON . stringify ( { image, githubPath, s3Path, s3Url } , null , 2 )
200
+ ) ;
201
+ const postContent = await octokit . request (
202
+ "GET /repos/{owner}/{repo}/contents/{path}" ,
203
+ {
204
+ owner : `${ process . env . OWNER } ` ,
205
+ repo : `${ process . env . REPO } ` ,
206
+ path : githubPath ,
207
+ }
208
+ ) ;
209
+
210
+ const buffer = Buffer . from ( ( postContent . data as any ) . content , "base64" ) ;
211
+
212
+ // upload images to s3
213
+ const putImage = new PutObjectCommand ( {
214
+ Bucket : `${ process . env . MEDIA_BUCKET } ` ,
215
+ Key : s3Path ,
216
+ Body : buffer ,
217
+ } ) ;
218
+ await s3 . send ( putImage ) ;
219
+
220
+ s3Mapping [ image ] = s3Url ;
221
+ }
222
+ const rewriteLink = ( match : string , text : string , url : string ) => {
223
+ console . log ( JSON . stringify ( { match, text, url } ) ) ;
224
+ return `` ;
225
+ }
226
+ workingContent . content = workingContent . content . replace ( imgRegex , rewriteLink ) ;
227
+ contentData . push ( workingContent ) ;
228
+ }
229
+ console . log ( JSON . stringify ( { contentData } ) ) ;
230
+ return contentData ;
231
+ } ;
125
232
126
233
const processNewContent = async (
127
234
newContent : {
@@ -131,16 +238,18 @@ const processNewContent = async (
131
238
sendStatusEmail : boolean ;
132
239
} [ ]
133
240
) => {
134
- const executions = await Promise . allSettled ( newContent . map ( async ( content ) => {
135
- const command = new StartExecutionCommand ( {
136
- stateMachineArn : process . env . STATE_MACHINE_ARN ,
137
- input : JSON . stringify ( content )
138
- } ) ;
139
- await sfn . send ( command ) ;
140
- } ) ) ;
241
+ const executions = await Promise . allSettled (
242
+ newContent . map ( async ( content ) => {
243
+ const command = new StartExecutionCommand ( {
244
+ stateMachineArn : process . env . STATE_MACHINE_ARN ,
245
+ input : JSON . stringify ( content ) ,
246
+ } ) ;
247
+ await sfn . send ( command ) ;
248
+ } )
249
+ ) ;
141
250
142
251
for ( const execution of executions ) {
143
- if ( execution . status == ' rejected' ) {
252
+ if ( execution . status == " rejected" ) {
144
253
console . error ( execution . reason ) ;
145
254
}
146
255
}
0 commit comments