@@ -98,32 +98,51 @@ public static function getLatestGithubTarball(string $name, array $source, strin
9898 {
9999 logger ()->debug ("finding {$ name } source from github {$ type } tarball " );
100100 $ source ['query ' ] ??= '' ;
101- $ data = json_decode (self ::curlExec (
102- url: "https://api.github.com/repos/ {$ source ['repo ' ]}/ {$ type }{$ source ['query ' ]}" ,
103- hooks: [[CurlHook::class, 'setupGithubToken ' ]],
104- retries: self ::getRetryAttempts ()
105- ), true , 512 , JSON_THROW_ON_ERROR );
106101
107- $ url = null ;
108- foreach ($ data as $ rel ) {
109- if (($ rel ['prerelease ' ] ?? false ) === true && ($ source ['prefer-stable ' ] ?? false )) {
110- continue ;
111- }
112- if (($ rel ['draft ' ] ?? false ) === true && (($ source ['prefer-stable ' ] ?? false ) || !$ rel ['tarball_url ' ])) {
113- continue ;
102+ // Use /releases/latest when possible: it returns the semantically latest stable
103+ // release regardless of publish order, avoiding issues with concurrent release branches.
104+ if ($ type === 'releases ' && empty ($ source ['query ' ]) && !($ source ['match ' ] ?? null )) {
105+ $ data = json_decode (self ::curlExec (
106+ url: "https://api.github.com/repos/ {$ source ['repo ' ]}/releases/latest " ,
107+ hooks: [[CurlHook::class, 'setupGithubToken ' ]],
108+ retries: self ::getRetryAttempts ()
109+ ), true , 512 , JSON_THROW_ON_ERROR );
110+ if (!is_array ($ data ) || empty ($ data ['tarball_url ' ])) {
111+ throw new DownloaderException ("failed to find {$ name } source " );
114112 }
115- if (!($ source ['match ' ] ?? null )) {
116- $ url = $ rel ['tarball_url ' ] ?? null ;
117- break ;
113+ $ url = $ data ['tarball_url ' ];
114+ $ version = $ data ['tag_name ' ] ?? $ data ['name ' ] ?? null ;
115+ } else {
116+ $ data = json_decode (self ::curlExec (
117+ url: "https://api.github.com/repos/ {$ source ['repo ' ]}/ {$ type }{$ source ['query ' ]}" ,
118+ hooks: [[CurlHook::class, 'setupGithubToken ' ]],
119+ retries: self ::getRetryAttempts ()
120+ ), true , 512 , JSON_THROW_ON_ERROR );
121+
122+ $ url = null ;
123+ $ version = null ;
124+ foreach ($ data as $ rel ) {
125+ if (($ rel ['prerelease ' ] ?? false ) === true && ($ source ['prefer-stable ' ] ?? false )) {
126+ continue ;
127+ }
128+ if (($ rel ['draft ' ] ?? false ) === true && (($ source ['prefer-stable ' ] ?? false ) || !$ rel ['tarball_url ' ])) {
129+ continue ;
130+ }
131+ if (!($ source ['match ' ] ?? null )) {
132+ $ url = $ rel ['tarball_url ' ] ?? null ;
133+ $ version = $ rel ['tag_name ' ] ?? $ rel ['name ' ] ?? null ;
134+ break ;
135+ }
136+ if (preg_match ('| ' . $ source ['match ' ] . '| ' , $ rel ['tarball_url ' ])) {
137+ $ url = $ rel ['tarball_url ' ];
138+ $ version = $ rel ['tag_name ' ] ?? $ rel ['name ' ] ?? null ;
139+ break ;
140+ }
118141 }
119- if (preg_match ('| ' . $ source ['match ' ] . '| ' , $ rel ['tarball_url ' ])) {
120- $ url = $ rel ['tarball_url ' ];
121- break ;
142+ if (!$ url ) {
143+ throw new DownloaderException ("failed to find {$ name } source " );
122144 }
123145 }
124- if (!$ url ) {
125- throw new DownloaderException ("failed to find {$ name } source " );
126- }
127146 $ headers = self ::curlExec (
128147 url: $ url ,
129148 method: 'HEAD ' ,
@@ -134,7 +153,7 @@ public static function getLatestGithubTarball(string $name, array $source, strin
134153 if ($ matches ) {
135154 $ filename = $ matches ['filename ' ];
136155 } else {
137- $ filename = "{$ name }- " . ($ type === ' releases ' ? $ data [ ' tag_name ' ] : $ data [ ' name ' ] ) . '.tar.gz ' ;
156+ $ filename = "{$ name }- " . ($ version ?? ' latest ' ) . '.tar.gz ' ;
138157 }
139158
140159 return [$ url , $ filename ];
0 commit comments