Skip to content

Commit aa78c2f

Browse files
committed
fix(github): search multiple pages to find the deployed commit
This makes tuttle viable for very active repos where there may be 100 commits in no time!
1 parent fb2b59b commit aa78c2f

File tree

1 file changed

+69
-11
lines changed

1 file changed

+69
-11
lines changed

src/modules/github.xqm

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import module namespace config="http://e-editiones.org/tuttle/config" at "config
99

1010
declare namespace http="http://expath.org/ns/http-client";
1111

12+
declare variable $github:max-page-size := 100;
13+
declare variable $github:max-total-result-size := 100;
14+
1215
declare function github:repo-url($config as map(*)) as xs:string {
1316
``[`{$config?baseurl}`repos/`{$config?owner}`/`{$config?repo}`]``
1417
};
@@ -74,7 +77,7 @@ declare function github:get-last-commit($config as map(*)) as map(*) {
7477
: Get all commits
7578
:)
7679
declare function github:get-commits($config as map(*)) as array(*)* {
77-
github:get-commits($config, 100)
80+
github:get-commits($config, $github:max-page-size)
7881
};
7982

8083
(:~
@@ -107,23 +110,50 @@ declare %private function github:short-commit-info ($commit-info as map(*)) as a
107110
: Get commits in full
108111
:)
109112
declare function github:get-raw-commits($config as map(*), $count as xs:integer) as array(*)? {
110-
github:request-json-ignore-pages(
111-
github:commit-ref-url($config, $count), $config?token)
113+
github:get-raw-commits($config, $count, ())
114+
};
115+
116+
(:~
117+
: Get commits in full, going over pages until we find the commit with the correct hash
118+
:)
119+
declare function github:get-raw-commits (
120+
$config as map(*),
121+
$count as xs:integer,
122+
$stop-at-commit-id as xs:string?
123+
) as array(*)? {
124+
let $stop-condition := if (empty($stop-at-commit-id)) then
125+
function ($_) {
126+
(: We are not looking for any SHA. Prevent going over all of the commits in a possibly big repo :)
127+
true()
128+
}
129+
else
130+
function ($results-on-page) {
131+
(: We can stop searching once we have all the commits between 'now' and the commit we looked for :)
132+
let $found-commits := $results-on-page?*?sha
133+
return $found-commits = $stop-at-commit-id
134+
}
135+
136+
let $results := github:request-json-all-pages(
137+
github:commit-ref-url($config, $count),
138+
$config?token,
139+
$stop-condition
140+
)
141+
return array { $results?* }
112142
};
113143

114144
(:~
115145
: Get diff between production collection and github-newest
116146
:)
117147
declare function github:get-newest-commits($config as map(*)) as xs:string* {
118148
let $deployed := $config?deployed
119-
let $commits := github:get-raw-commits($config, 100)
149+
let $commits := github:get-raw-commits($config, $github:max-page-size, $deployed)
120150
let $sha := $commits?*?sha
121151
let $how-many := index-of($sha, $deployed) - 1
122152
return
123153
if (empty($how-many)) then (
124154
error(
125155
xs:QName("github:commit-not-found"),
126-
'The deployed commit hash ' || $deployed || ' was not found in the list of commits on the remote.')
156+
'The deployed commit hash ' || $deployed || ' was not found in the list of commits on the remote. Tuttle can only process incremental upgrades of ' || $github:max-total-result-size || '.')
127157
) else (
128158
reverse(subsequence($sha, 1, $how-many))
129159
)
@@ -218,7 +248,7 @@ declare function github:incremental($config as map(*)) {
218248
:)
219249
declare function github:get-commit-files($config as map(*), $sha as xs:string) as array(*) {
220250
let $url := github:repo-url($config) || "/commits/" || $sha
221-
let $commit := github:request-json-all-pages($url, $config?token, ())
251+
let $commit := github:request-json-all-pages($url, $config?token)
222252

223253
return array { $commit?files?* }
224254
};
@@ -361,7 +391,32 @@ declare %private function github:request-json($url as xs:string, $token as xs:st
361391
)
362392
};
363393

364-
declare %private function github:request-json-all-pages($url as xs:string, $token as xs:string?, $acc) {
394+
(:~
395+
: Get all pages of a specified URL. Github has some paginated endpoints, This function traverses all of
396+
: those and joins the results.
397+
:)
398+
declare %private function github:request-json-all-pages($url as xs:string, $token as xs:string?) {
399+
github:request-json-all-pages($url, $token, function ($_) { (: Traverse all pages :) false() }, ())
400+
};
401+
402+
(:~
403+
: Overload, adds the $stop-condition callback which is given the contents of the current page
404+
: return `true()` to indicate there are sufficient results and we can stop
405+
:)
406+
declare %private function github:request-json-all-pages(
407+
$url as xs:string,
408+
$token as xs:string?,
409+
$stop-condition as function(map(*)) as xs:boolean
410+
) {
411+
github:request-json-all-pages($url, $token, $stop-condition, ())
412+
};
413+
414+
declare %private function github:request-json-all-pages(
415+
$url as xs:string,
416+
$token as xs:string?,
417+
$stop-condition as function(map(*)) as xs:boolean,
418+
$acc
419+
) {
365420
let $response :=
366421
app:request-json(
367422
github:build-request($url, (
@@ -374,13 +429,16 @@ declare %private function github:request-json-all-pages($url as xs:string, $toke
374429
github:parse-link-header($response[1]/http:header[@name="link"]/@value)?next
375430
) else ()
376431

377-
let $all := ($acc, $response[2])
432+
let $results-in-this-page := $response[2]
433+
let $all := ($acc, $results-in-this-page)
434+
435+
let $should-stop := $stop-condition($results-in-this-page)
378436

379437
return (
380-
if (exists($next-url)) then (
381-
github:request-json-all-pages($next-url, $token, $all)
438+
if (not($should-stop) and count($all?*) < $github:max-total-result-size and exists($next-url)) then (
439+
github:request-json-all-pages($next-url, $token, $stop-condition, $all)
382440
) else (
383-
$all
441+
$all
384442
)
385443
)
386444
};

0 commit comments

Comments
 (0)