@@ -367,38 +367,38 @@ Params:
367367GithubIssue[] getGithubIssuesRest (const string project, const string repo
368368 , const DateTime startDate, const DateTime endDate, const string bearer)
369369{
370+ import std.regex : ctRegex, matchFirst;
371+
370372 GithubIssue[] ret;
371- foreach (page; 1 .. 100 )
373+ // Initial URL for first request
374+ string nextUrl = (" https://api.github.com/repos/%s/%s/issues?per_page=100"
375+ ~ " &state=closed&since=%s" )
376+ .format(project, repo, startDate.toISOExtString() ~ " Z" );
377+
378+ foreach (_; 0 .. 100 )
372379 { // 1000 issues per release should be enough
373- string req = (" https://api.github.com/repos/%s/%s/issues?per_page=100&since=%s"
374- ~ " &state=closed&since=%s&page=%s" )
375- .format(project, repo,
376- () {
377- switch (repo) {
378- case " dmd" : return " 2024-12-01T12:00:00Z" ;
379- case " phobos" : return " 2024-12-01T12:00:00Z" ;
380- case " tools" : return " 2001-01-01T00:00:00Z" ;
381- case " dub" : return " 2001-01-01T00:00:00Z" ;
382- case " visuald" : return " 2023-10-18T00:00:00Z" ;
383- case " installer" : return " 2001-01-01T00:00:00Z" ;
384- default : return " 2001-01-01T00:00:00Z" ;
385- }
386- }()
387- , startDate.toISOExtString() ~ " Z" , page);
380+ if (nextUrl.empty)
381+ break ;
388382
389- HTTP http = HTTP (req );
383+ HTTP http = HTTP (nextUrl );
390384 http.addRequestHeader(" Accept" , " application/vnd.github+json" );
391385 http.addRequestHeader(" X-GitHub-Api-Version" , " 2022-11-28" );
392386 http.addRequestHeader(" Authorization" , bearer);
393387
394388 char [] response;
389+ string linkHeader;
395390 try
396391 {
397392 http.onReceive = (ubyte [] d)
398393 {
399394 response ~= cast (char [])d;
400395 return d.length;
401396 };
397+ http.onReceiveHeader = (const (char )[] key, const (char )[] value)
398+ {
399+ if (key == " link" )
400+ linkHeader = value.idup;
401+ };
402402 http.perform();
403403 }
404404 catch (Exception e)
@@ -502,9 +502,18 @@ GithubIssue[] getGithubIssuesRest(const string project, const string repo
502502 }
503503 ret ~= tmp;
504504 }
505- if (arr.length < 100 )
505+
506+ // Parse Link header for cursor-based pagination
507+ // Format: <url>; rel="next", <url>; rel="last"
508+ nextUrl = null ;
509+ if (! linkHeader.empty)
506510 {
507- break ;
511+ enum linkRe = ctRegex! ` <([^>]+)>;\s*rel="next"` ;
512+ auto m = matchFirst(linkHeader, linkRe);
513+ if (! m.empty)
514+ {
515+ nextUrl = m[1 ];
516+ }
508517 }
509518 }
510519 return ret;
@@ -664,42 +673,26 @@ bool less(T)(ref T a, ref T b)
664673 return lessImpl (a, b);
665674}
666675
667- enum BugzillaOrGithub {
668- bugzilla,
669- github
670- }
671-
672676/**
673677Writes the fixed issued from Bugzilla in the ddoc format as a single list.
674678
675679Params:
676680 changes = parsed InputRange of changelog information
677681 w = Output range to use
678682*/
679- void writeBugzillaChanges (Entries, Writer )(BugzillaOrGithub bog, Entries entries, Writer w)
683+ void writeBugzillaChanges (Entries, Writer )(Entries entries, Writer w)
680684 if (isOutputRange! (Writer , string ))
681685{
682686 immutable components = [" DMD Compiler" , " Phobos" , " Druntime" , " dlang.org" , " Optlink" , " Tools" , " Installer" ];
683687 immutable bugtypes = [" regression fixes" , " bug fixes" , " enhancements" ];
684- const string macroTitle = () {
685- final switch (bog) {
686- case BugzillaOrGithub.bugzilla: return " BUGSTITLE_BUGZILLA" ;
687- case BugzillaOrGithub.github: return " BUGSTITLE_GITHUB" ;
688- }
689- }();
690688 const macroLi = (string component) {
691- final switch (bog) {
692- case BugzillaOrGithub.bugzilla:
693- return " BUGZILLA" ;
694- case BugzillaOrGithub.github:
695- final switch (component)
696- {
697- case " dlang.org" : return " DLANGORGGITHUB" ;
698- case " DMD Compiler" : return " DMDGITHUB" ;
699- case " Phobos" : return " PHOBOSGITHUB" ;
700- case " Tools" : return " TOOLSGITHUB" ;
701- case " Installer" : return " INSTALLERGITHUB" ;
702- }
689+ final switch (component)
690+ {
691+ case " dlang.org" : return " DLANGORGGITHUB" ;
692+ case " DMD Compiler" : return " DMDGITHUB" ;
693+ case " Phobos" : return " PHOBOSGITHUB" ;
694+ case " Tools" : return " TOOLSGITHUB" ;
695+ case " Installer" : return " INSTALLERGITHUB" ;
703696 }
704697 };
705698
@@ -711,7 +704,7 @@ void writeBugzillaChanges(Entries, Writer)(BugzillaOrGithub bog, Entries entries
711704 {
712705 if (auto bugs = bugtype in * comp)
713706 {
714- w.formattedWrite(" $(%s %s %s,\n\n " , macroTitle , component, bugtype);
707+ w.formattedWrite(" $(BUGSTITLE_GITHUB %s %s,\n\n " , component, bugtype);
715708 alias lessFunc = less! (ElementEncodingType! (typeof (* bugs)));
716709 foreach (bug; sort! lessFunc(* bugs))
717710 {
@@ -813,13 +806,6 @@ Please supply a bugzilla version
813806 w.put(" Ddoc\n\n " );
814807 w.put(" $(CHANGELOG_NAV_INJECT)\n\n " );
815808
816- // Accumulate Bugzilla issues
817- BugzillaEntry[][string ][string ] bugzillaChanges;
818- if (! revRange.empty)
819- {
820- bugzillaChanges = getBugzillaChanges(revRange);
821- }
822-
823809 GithubIssue[][string ][string ] githubChanges;
824810 if (! revRange.empty && ! githubClassicTokenFileName.empty)
825811 {
@@ -903,14 +889,9 @@ Please supply a bugzilla version
903889 w.put(" $(CHANGELOG_SEP_NO_TEXT_BUGZILLA)\n\n " );
904890 }
905891
906- // print the entire changelog history
907- if (revRange.length)
908- {
909- writeBugzillaChanges(BugzillaOrGithub.bugzilla, bugzillaChanges, w);
910- }
911892 if (revRange.length)
912893 {
913- writeBugzillaChanges(BugzillaOrGithub.github, githubChanges, w);
894+ writeBugzillaChanges(githubChanges, w);
914895 }
915896 }
916897
0 commit comments