@@ -78,7 +78,14 @@ const expectParentHasOid = async ({
7878 expect ( commit . parents . nodes ) . toEqual ( [ { oid } ] ) ;
7979} ;
8080
81- const makeFileChanges = async ( repoDirectory : string ) => {
81+ const makeFileChanges = async (
82+ repoDirectory : string ,
83+ changegroup :
84+ | "standard"
85+ | "with-ignored-symlink"
86+ | "with-included-valid-symlink"
87+ | "with-included-invalid-symlink" ,
88+ ) => {
8289 // Update an existing file
8390 await fs . promises . writeFile (
8491 path . join ( repoDirectory , "LICENSE" ) ,
@@ -113,6 +120,34 @@ const makeFileChanges = async (repoDirectory: string) => {
113120 path . join ( repoDirectory , "coverage" , "foo" , "bar" ) ,
114121 "This file should be ignored" ,
115122 ) ;
123+ if ( changegroup === "with-ignored-symlink" ) {
124+ // node_modules is ignored in this repo
125+ await fs . promises . mkdir ( path . join ( repoDirectory , "node_modules" ) , {
126+ recursive : true ,
127+ } ) ;
128+ await fs . promises . symlink (
129+ path . join ( repoDirectory , "non-existent" ) ,
130+ path . join ( repoDirectory , "node_modules" , "nested" ) ,
131+ ) ;
132+ }
133+ if ( changegroup === "with-included-valid-symlink" ) {
134+ await fs . promises . mkdir ( path . join ( repoDirectory , "some-dir" ) , {
135+ recursive : true ,
136+ } ) ;
137+ await fs . promises . symlink (
138+ path . join ( repoDirectory , "README.md" ) ,
139+ path . join ( repoDirectory , "some-dir" , "nested" ) ,
140+ ) ;
141+ }
142+ if ( changegroup === "with-included-invalid-symlink" ) {
143+ await fs . promises . mkdir ( path . join ( repoDirectory , "some-dir" ) , {
144+ recursive : true ,
145+ } ) ;
146+ await fs . promises . symlink (
147+ path . join ( repoDirectory , "non-existent" ) ,
148+ path . join ( repoDirectory , "some-dir" , "nested" ) ,
149+ ) ;
150+ }
116151} ;
117152
118153const makeFileChangeAssertions = async ( branch : string ) => {
@@ -154,62 +189,156 @@ describe("git", () => {
154189 describe ( "commitChangesFromRepo" , ( ) => {
155190 const testDir = path . join ( ROOT_TEMP_DIRECTORY , "commitChangesFromRepo" ) ;
156191
157- it ( "should correctly commit all changes" , async ( ) => {
158- const branch = `${ TEST_BRANCH_PREFIX } -multiple-changes` ;
159- branches . push ( branch ) ;
160-
161- await fs . promises . mkdir ( testDir , { recursive : true } ) ;
162- const repoDirectory = path . join ( testDir , "repo-1" ) ;
163-
164- // Clone the git repo locally using the git cli and child-process
165- await new Promise < void > ( ( resolve , reject ) => {
166- const p = execFile (
167- "git" ,
168- [ "clone" , process . cwd ( ) , "repo-1" ] ,
169- { cwd : testDir } ,
170- ( error ) => {
171- if ( error ) {
172- reject ( error ) ;
173- } else {
174- resolve ( ) ;
175- }
192+ for ( const group of [ "standard" , "with-ignored-symlink" ] as const ) {
193+ it ( `should correctly commit all changes for group: ${ group } ` , async ( ) => {
194+ const branch = `${ TEST_BRANCH_PREFIX } -multiple-changes-${ group } ` ;
195+ branches . push ( branch ) ;
196+
197+ await fs . promises . mkdir ( testDir , { recursive : true } ) ;
198+ const repoDirectory = path . join ( testDir , `repo-1-${ group } ` ) ;
199+
200+ // Clone the git repo locally using the git cli and child-process
201+ await new Promise < void > ( ( resolve , reject ) => {
202+ const p = execFile (
203+ "git" ,
204+ [ "clone" , process . cwd ( ) , `repo-1-${ group } ` ] ,
205+ { cwd : testDir } ,
206+ ( error ) => {
207+ if ( error ) {
208+ reject ( error ) ;
209+ } else {
210+ resolve ( ) ;
211+ }
212+ } ,
213+ ) ;
214+ p . stdout ?. pipe ( process . stdout ) ;
215+ p . stderr ?. pipe ( process . stderr ) ;
216+ } ) ;
217+
218+ await makeFileChanges ( repoDirectory , group ) ;
219+
220+ // Push the changes
221+ await commitChangesFromRepo ( {
222+ octokit,
223+ ...REPO ,
224+ branch,
225+ message : {
226+ headline : "Test commit" ,
227+ body : "This is a test commit" ,
176228 } ,
229+ repoDirectory,
230+ log,
231+ } ) ;
232+
233+ await waitForGitHubToBeReady ( ) ;
234+
235+ await makeFileChangeAssertions ( branch ) ;
236+
237+ // Expect the OID to be the HEAD commit
238+ const oid =
239+ (
240+ await git . log ( {
241+ fs,
242+ dir : repoDirectory ,
243+ ref : "HEAD" ,
244+ depth : 1 ,
245+ } )
246+ ) [ 0 ] ?. oid ?? "NO_OID" ;
247+
248+ await expectParentHasOid ( { branch, oid } ) ;
249+ } ) ;
250+ }
251+
252+ describe ( `should throw appropriate error when symlink is present` , ( ) => {
253+ it ( `and file does not exist` , async ( ) => {
254+ const branch = `${ TEST_BRANCH_PREFIX } -invalid-symlink-error` ;
255+ branches . push ( branch ) ;
256+
257+ await fs . promises . mkdir ( testDir , { recursive : true } ) ;
258+ const repoDirectory = path . join ( testDir , `repo-invalid-symlink` ) ;
259+
260+ // Clone the git repo locally using the git cli and child-process
261+ await new Promise < void > ( ( resolve , reject ) => {
262+ const p = execFile (
263+ "git" ,
264+ [ "clone" , process . cwd ( ) , `repo-invalid-symlink` ] ,
265+ { cwd : testDir } ,
266+ ( error ) => {
267+ if ( error ) {
268+ reject ( error ) ;
269+ } else {
270+ resolve ( ) ;
271+ }
272+ } ,
273+ ) ;
274+ p . stdout ?. pipe ( process . stdout ) ;
275+ p . stderr ?. pipe ( process . stderr ) ;
276+ } ) ;
277+
278+ await makeFileChanges ( repoDirectory , "with-included-invalid-symlink" ) ;
279+
280+ // Push the changes
281+ await expect ( ( ) =>
282+ commitChangesFromRepo ( {
283+ octokit,
284+ ...REPO ,
285+ branch,
286+ message : {
287+ headline : "Test commit" ,
288+ body : "This is a test commit" ,
289+ } ,
290+ repoDirectory,
291+ log,
292+ } ) ,
293+ ) . rejects . toThrow (
294+ "Unexpected symlink at some-dir/nested, GitHub API only supports files and directories. You may need to add this file to .gitignore" ,
177295 ) ;
178- p . stdout ?. pipe ( process . stdout ) ;
179- p . stderr ?. pipe ( process . stderr ) ;
180296 } ) ;
181297
182- await makeFileChanges ( repoDirectory ) ;
183-
184- // Push the changes
185- await commitChangesFromRepo ( {
186- octokit,
187- ...REPO ,
188- branch,
189- message : {
190- headline : "Test commit" ,
191- body : "This is a test commit" ,
192- } ,
193- repoDirectory,
194- log,
298+ it ( `and file exists` , async ( ) => {
299+ const branch = `${ TEST_BRANCH_PREFIX } -valid-symlink-error` ;
300+ branches . push ( branch ) ;
301+
302+ await fs . promises . mkdir ( testDir , { recursive : true } ) ;
303+ const repoDirectory = path . join ( testDir , `repo-valid-symlink` ) ;
304+
305+ // Clone the git repo locally using the git cli and child-process
306+ await new Promise < void > ( ( resolve , reject ) => {
307+ const p = execFile (
308+ "git" ,
309+ [ "clone" , process . cwd ( ) , `repo-valid-symlink` ] ,
310+ { cwd : testDir } ,
311+ ( error ) => {
312+ if ( error ) {
313+ reject ( error ) ;
314+ } else {
315+ resolve ( ) ;
316+ }
317+ } ,
318+ ) ;
319+ p . stdout ?. pipe ( process . stdout ) ;
320+ p . stderr ?. pipe ( process . stderr ) ;
321+ } ) ;
322+
323+ await makeFileChanges ( repoDirectory , "with-included-valid-symlink" ) ;
324+
325+ // Push the changes
326+ await expect ( ( ) =>
327+ commitChangesFromRepo ( {
328+ octokit,
329+ ...REPO ,
330+ branch,
331+ message : {
332+ headline : "Test commit" ,
333+ body : "This is a test commit" ,
334+ } ,
335+ repoDirectory,
336+ log,
337+ } ) ,
338+ ) . rejects . toThrow (
339+ "Unexpected symlink at some-dir/nested, GitHub API only supports files and directories. You may need to add this file to .gitignore" ,
340+ ) ;
195341 } ) ;
196-
197- await waitForGitHubToBeReady ( ) ;
198-
199- await makeFileChangeAssertions ( branch ) ;
200-
201- // Expect the OID to be the HEAD commit
202- const oid =
203- (
204- await git . log ( {
205- fs,
206- dir : repoDirectory ,
207- ref : "HEAD" ,
208- depth : 1 ,
209- } )
210- ) [ 0 ] ?. oid ?? "NO_OID" ;
211-
212- await expectParentHasOid ( { branch, oid } ) ;
213342 } ) ;
214343
215344 it ( "should correctly be able to base changes off specific commit" , async ( ) => {
@@ -237,7 +366,7 @@ describe("git", () => {
237366 p . stderr ?. pipe ( process . stderr ) ;
238367 } ) ;
239368
240- makeFileChanges ( repoDirectory ) ;
369+ makeFileChanges ( repoDirectory , "standard" ) ;
241370
242371 // Determine the previous commit hash
243372 const gitLog = await git . log ( {
0 commit comments