@@ -25,26 +25,34 @@ import {
2525} from './utils.js' ;
2626
2727/**
28- * Extracts the latest release from the items.
29- * @param {RSSItem[] } items the RSS items.
28+ * Yields RSS items across pages until there are no more items or the page limit is reached.
29+ * @param {string } baseURL the base URL of the RSS feed.
30+ * @param {number } maxPages the maximum number of pages to fetch.
31+ * @yields {RSSItem} the RSS items.
32+ */
33+ async function * feedItems ( baseURL , maxPages = 1 ) {
34+ for ( let page = 1 ; page <= maxPages ; page ++ ) {
35+ const url = page === 1 ? baseURL : `${ baseURL } ?paged=${ page } ` ;
36+ const items = await getRSSItems ( url ) ;
37+ if ( ! items . length ) {
38+ break ;
39+ }
40+ yield * items ;
41+ }
42+ }
43+
44+ /**
45+ * Builds a Release object from an RSS item.
46+ * @param {RSSItem } item the RSS item.
3047 * @param {RegExp } titleVersionPattern the pattern to match the title and extract the version.
3148 * @param {RegExp } descriptionEngineVersionPattern the pattern to match the description and extract the engine version.
32- * @returns {Promise<Release | null > } the latest release, if found, otherwise null .
49+ * @returns {Promise<Release> } the release.
3350 */
34- const findRelease = async (
35- items ,
51+ const buildRelease = async (
52+ item ,
3653 titleVersionPattern ,
3754 descriptionEngineVersionPattern ,
3855) => {
39- const item = items . find (
40- ( item ) => titleVersionPattern . test ( item . title ) /* &&
41- descriptionEngineVersionPattern.test(item.description)*/ ,
42- ) ;
43-
44- if ( ! item ) {
45- return null ;
46- }
47-
4856 const version = /** @type {RegExpMatchArray } */ (
4957 item . title . match ( titleVersionPattern )
5058 ) [ 1 ] ;
@@ -109,99 +117,125 @@ export const updateOperaReleases = async (options) => {
109117
110118 let result = '' ;
111119
112- const items = await getRSSItems ( options . releaseFeedURL ) ;
113-
114- const release = await findRelease (
115- items . filter (
116- ( item ) =>
117- options . releaseFilterCreator ?. includes ( item [ 'dc:creator' ] ) ?? true ,
118- ) ,
119- options . titleVersionPattern ,
120- options . descriptionEngineVersionPattern ,
121- ) ;
122-
123- if ( ! release ) {
124- return gfmNoteblock (
125- 'NOTE' ,
126- `**${ options . browserName } **: No release announcement found among ${ items . length } items in [this RSS feed](<${ options . releaseFeedURL } >).` ,
127- ) ;
128- }
129-
130120 const file = await fs . readFile ( `${ options . bcdFile } ` , 'utf-8' ) ;
131121 const data = JSON . parse ( file . toString ( ) ) ;
132122
133- const current = structuredClone (
134- data . browsers [ browser ] . releases [ release . version ] ,
135- ) ;
136-
137- if ( ! release . engineVersion ) {
138- const currentEngineVersion = current . engine_version ;
139- if ( ! currentEngineVersion ) {
140- return gfmNoteblock (
141- 'CAUTION' ,
142- `**${ options . browserName } **: No engine version found in [this blog post](<${ release . releaseNote } >).` ,
143- ) ;
123+ // Find the version currently tracked as "current" to use as stopping condition.
124+ const [ currentBCDVersion ] =
125+ Object . entries ( data . browsers [ browser ] . releases ) . find (
126+ ( [ , r ] ) => r . status === 'current' ,
127+ ) ?? [ ] ;
128+
129+ const newItems = /** @type {RSSItem[] } */ ( [ ] ) ;
130+
131+ for await ( const item of feedItems (
132+ options . releaseFeedURL ,
133+ options . maxFeedPages ?? 1 ,
134+ ) ) {
135+ if (
136+ options . releaseFilterCreator &&
137+ ! options . releaseFilterCreator . includes ( item [ 'dc:creator' ] )
138+ ) {
139+ continue ;
144140 }
145-
146- result += gfmNoteblock (
147- 'WARNING' ,
148- `**${ options . browserName } **: No engine version found in [this blog post](<${ release . releaseNote } >). Using (previous engine version + 1) instead.` ,
149- ) ;
150- release . engineVersion = currentEngineVersion ;
141+ if ( ! options . titleVersionPattern . test ( item . title ) ) {
142+ continue ;
143+ }
144+ const version = /** @type {RegExpMatchArray } */ (
145+ item . title . match ( options . titleVersionPattern )
146+ ) [ 1 ] ;
147+ if ( version === currentBCDVersion ) {
148+ break ;
149+ }
150+ newItems . push ( item ) ;
151151 }
152152
153- if ( isDesktop && ! current ) {
153+ if ( ! newItems . length ) {
154154 return gfmNoteblock (
155- 'WARNING ' ,
156- `Latest stable **${ options . browserName } ** release ** ${ release . version } ** not yet tracked .` ,
155+ 'NOTE ' ,
156+ `**${ options . browserName } **: No new release announcements found in [this RSS feed](< ${ options . releaseFeedURL } >) .` ,
157157 ) ;
158158 }
159159
160- result += createOrUpdateBrowserEntry (
161- data ,
162- browser ,
163- release . version ,
164- release . channel ,
165- release . engine ,
166- release . engineVersion ,
167- release . date ,
168- release . releaseNote ,
169- ) ;
160+ // Process releases from oldest to newest.
161+ for ( const item of newItems . reverse ( ) ) {
162+ const release = await buildRelease (
163+ item ,
164+ options . titleVersionPattern ,
165+ options . descriptionEngineVersionPattern ,
166+ ) ;
170167
171- // Set previous release to "retired".
172- const previousVersion = String ( Number ( release . version ) - 1 ) ;
173- result += updateBrowserEntry (
174- data ,
175- browser ,
176- previousVersion ,
177- undefined ,
178- 'retired' ,
179- undefined ,
180- undefined ,
181- ) ;
168+ if ( ! release . engineVersion ) {
169+ const existingEngineVersion =
170+ data . browsers [ browser ] . releases [ release . version ] ?. engine_version ;
171+ if ( ! existingEngineVersion ) {
172+ result += gfmNoteblock (
173+ 'CAUTION' ,
174+ `**${ options . browserName } **: No engine version found in [this blog post](<${ release . releaseNote } >).` ,
175+ ) ;
176+ continue ;
177+ }
178+ result += gfmNoteblock (
179+ 'WARNING' ,
180+ `**${ options . browserName } **: No engine version found in [this blog post](<${ release . releaseNote } >). Using existing engine version instead.` ,
181+ ) ;
182+ release . engineVersion = existingEngineVersion ;
183+ }
182184
183- if ( isDesktop ) {
184- // 1. Set next release to "beta".
185185 result += createOrUpdateBrowserEntry (
186186 data ,
187187 browser ,
188- String ( Number ( release . version ) + 1 ) ,
189- 'beta' ,
188+ release . version ,
189+ release . channel ,
190190 release . engine ,
191- String ( Number ( release . engineVersion ) + 1 ) ,
191+ release . engineVersion ,
192+ release . date ,
193+ release . releaseNote ,
192194 ) ;
193195
194- // 2. Add another release as "nightly ".
195- result += createOrUpdateBrowserEntry (
196+ // Set previous release to "retired ".
197+ result += updateBrowserEntry (
196198 data ,
197199 browser ,
198- String ( Number ( release . version ) + 2 ) ,
199- 'nightly' ,
200- release . engine ,
201- String ( Number ( release . engineVersion ) + 2 ) ,
200+ String ( Number ( release . version ) - 1 ) ,
201+ undefined ,
202+ 'retired' ,
203+ undefined ,
204+ undefined ,
202205 ) ;
203206 }
204207
208+ if ( isDesktop ) {
209+ // Determine the latest processed release (last item after oldest-to-newest reversal).
210+ const latestVersion = /** @type {RegExpMatchArray } */ (
211+ newItems [ newItems . length - 1 ] . title . match ( options . titleVersionPattern )
212+ ) [ 1 ] ;
213+ const latestEngineVersion =
214+ data . browsers [ browser ] . releases [ latestVersion ] ?. engine_version ;
215+
216+ if ( latestEngineVersion ) {
217+ // 1. Set next release to "beta".
218+ result += createOrUpdateBrowserEntry (
219+ data ,
220+ browser ,
221+ String ( Number ( latestVersion ) + 1 ) ,
222+ 'beta' ,
223+ 'Blink' ,
224+ String ( Number ( latestEngineVersion ) + 1 ) ,
225+ ) ;
226+
227+ // 2. Add another release as "nightly".
228+ result += createOrUpdateBrowserEntry (
229+ data ,
230+ browser ,
231+ String ( Number ( latestVersion ) + 2 ) ,
232+ 'nightly' ,
233+ 'Blink' ,
234+ String ( Number ( latestEngineVersion ) + 2 ) ,
235+ ) ;
236+ }
237+ }
238+
205239 await fs . writeFile ( `./${ options . bcdFile } ` , stringify ( data ) + '\n' ) ;
206240
207241 // Returns the log
0 commit comments