@@ -27,7 +27,10 @@ async function writeExecutable(filePath: string, contents: string): Promise<void
2727
2828async function createReleaseHelperFixture (
2929 t : TestContext ,
30- options : { ghReleaseViewScript : string } ,
30+ options : {
31+ ghReleaseViewScript : string ;
32+ localTagMode ?: "absent" | "annotated-at-head" | "lightweight-at-head" | "wrong-target" ;
33+ } ,
3134) : Promise < { binDir : string ; logPath : string ; scriptPath : string ; tempDir : string } > {
3235 const tempDir = await mkdtemp ( path . join ( tmpdir ( ) , "ray-gh-release-helper-" ) ) ;
3336 t . after ( async ( ) => {
@@ -40,6 +43,7 @@ async function createReleaseHelperFixture(
4043
4144 const binDir = path . join ( tempDir , "bin" ) ;
4245 const logPath = path . join ( tempDir , "commands.log" ) ;
46+ const localTagMode = options . localTagMode ?? "absent" ;
4347
4448 await writeExecutable (
4549 path . join ( binDir , "timeout" ) ,
@@ -80,6 +84,7 @@ async function createReleaseHelperFixture(
8084 [
8185 "#!/usr/bin/env bash" ,
8286 "set -euo pipefail" ,
87+ `LOCAL_TAG_MODE=${ JSON . stringify ( localTagMode ) } ` ,
8388 'printf "git %s\\n" "$*" >> "${RAY_TEST_LOG:?}"' ,
8489 'if [ "${1:-}" = "status" ]; then' ,
8590 " exit 0" ,
@@ -100,7 +105,26 @@ async function createReleaseHelperFixture(
100105 " exit 0" ,
101106 "fi" ,
102107 'if [ "${1:-}" = "rev-parse" ] && [ "${2:-}" = "-q" ]; then' ,
103- " exit 1" ,
108+ ' if [ "$LOCAL_TAG_MODE" = "absent" ]; then' ,
109+ " exit 1" ,
110+ " fi" ,
111+ " exit 0" ,
112+ "fi" ,
113+ 'if [ "${1:-}" = "cat-file" ] && [ "${2:-}" = "-t" ]; then' ,
114+ ' if [ "$LOCAL_TAG_MODE" = "lightweight-at-head" ]; then' ,
115+ ' echo "commit"' ,
116+ " else" ,
117+ ' echo "tag"' ,
118+ " fi" ,
119+ " exit 0" ,
120+ "fi" ,
121+ 'if [ "${1:-}" = "rev-parse" ] && [[ "${2:-}" == *"^{}" ]]; then' ,
122+ ' if [ "$LOCAL_TAG_MODE" = "wrong-target" ]; then' ,
123+ ' echo "deadbeef00000000"' ,
124+ " else" ,
125+ ' echo "abcdef1234567890"' ,
126+ " fi" ,
127+ " exit 0" ,
104128 "fi" ,
105129 'if [ "${1:-}" = "ls-remote" ]; then' ,
106130 " exit 2" ,
@@ -218,6 +242,9 @@ test("gh release helper bounds network release operations", async () => {
218242 assert . match ( contents , / g i t f e t c h - - t a g s o r i g i n r e f s \/ h e a d s \/ m a i n : r e f s \/ r e m o t e s \/ o r i g i n \/ m a i n / ) ;
219243 assert . match ( contents , / r u n _ b o u n d e d 6 0 g i t l s - r e m o t e - - e x i t - c o d e - - t a g s o r i g i n / ) ;
220244 assert . match ( contents , / f a i l " c o u l d n o t c h e c k r e m o t e t a g \$ t a g \( e x i t \$ s t a t u s \) " / ) ;
245+ assert . match ( contents , / l o c a l _ r e l e a s e _ t a g _ r e a d y \( \) \{ / ) ;
246+ assert . match ( contents , / g i t c a t - f i l e - t " r e f s \/ t a g s \/ \$ t a g " / ) ;
247+ assert . match ( contents , / g i t r e v - p a r s e " \$ t a g \^ \{ \} " / ) ;
221248 assert . match ( contents , / r u n _ b o u n d e d 6 0 g h r e l e a s e v i e w " \$ t a g " / ) ;
222249 assert . match ( contents , / f a i l " t i m e d o u t c h e c k i n g G i t H u b r e l e a s e : \$ t a g " / ) ;
223250 assert . match (
@@ -284,3 +311,50 @@ test("gh release helper continues when GitHub reports releases are missing", asy
284311 / ^ g h r e l e a s e c r e a t e s d k - v 1 \. 2 \. 3 - - g e n e r a t e - n o t e s - - t i t l e s d k - v 1 \. 2 \. 3 $ / m,
285312 ) ;
286313} ) ;
314+
315+ test ( "gh release helper reuses matching local annotated tags" , async ( t ) => {
316+ const fixture = await createReleaseHelperFixture ( t , {
317+ ghReleaseViewScript : [ ' echo "release not found" >&2' , " exit 1" ] . join ( "\n" ) ,
318+ localTagMode : "annotated-at-head" ,
319+ } ) ;
320+
321+ const result = await runFixtureReleaseHelper ( fixture ) ;
322+
323+ assert . equal ( result . code , 0 ) ;
324+ assert . match ( result . stdout , / R e u s i n g l o c a l a n n o t a t e d t a g c o r e - v 1 \. 2 \. 3 / ) ;
325+ assert . match ( result . stdout , / R e u s i n g l o c a l a n n o t a t e d t a g s d k - v 1 \. 2 \. 3 / ) ;
326+
327+ const commandLog = await readFile ( fixture . logPath , "utf8" ) ;
328+ assert . match ( commandLog , / ^ g i t c a t - f i l e - t r e f s \/ t a g s \/ c o r e - v 1 \. 2 \. 3 $ / m) ;
329+ assert . match ( commandLog , / ^ g i t r e v - p a r s e c o r e - v 1 \. 2 \. 3 \^ \{ \} $ / m) ;
330+ assert . doesNotMatch ( commandLog , / ^ g i t t a g - a / m) ;
331+ assert . match ( commandLog , / ^ g i t p u s h - - a t o m i c o r i g i n c o r e - v 1 \. 2 \. 3 s d k - v 1 \. 2 \. 3 $ / m) ;
332+ } ) ;
333+
334+ test ( "gh release helper rejects unsafe existing local tags" , async ( t ) => {
335+ const lightweightFixture = await createReleaseHelperFixture ( t , {
336+ ghReleaseViewScript : [ ' echo "release not found" >&2' , " exit 1" ] . join ( "\n" ) ,
337+ localTagMode : "lightweight-at-head" ,
338+ } ) ;
339+
340+ const lightweightResult = await runFixtureReleaseHelper ( lightweightFixture ) ;
341+
342+ assert . notEqual ( lightweightResult . code , 0 ) ;
343+ assert . match (
344+ lightweightResult . stderr ,
345+ / l o c a l t a g c o r e - v 1 \. 2 \. 3 a l r e a d y e x i s t s b u t i s n o t a n n o t a t e d / ,
346+ ) ;
347+
348+ const wrongTargetFixture = await createReleaseHelperFixture ( t , {
349+ ghReleaseViewScript : [ ' echo "release not found" >&2' , " exit 1" ] . join ( "\n" ) ,
350+ localTagMode : "wrong-target" ,
351+ } ) ;
352+
353+ const wrongTargetResult = await runFixtureReleaseHelper ( wrongTargetFixture ) ;
354+
355+ assert . notEqual ( wrongTargetResult . code , 0 ) ;
356+ assert . match (
357+ wrongTargetResult . stderr ,
358+ / l o c a l t a g c o r e - v 1 \. 2 \. 3 p o i n t s a t d e a d b e e f 0 0 0 0 0 0 0 0 , e x p e c t e d a b c d e f 1 2 3 4 5 6 7 8 9 0 / ,
359+ ) ;
360+ } ) ;
0 commit comments