From 798353597f292903a6bf00576be7e9cdfaca89f1 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:30:39 +0200 Subject: [PATCH 1/7] ci: configure Dependabot smart grouping, 7-day cooldown, and grouped auto-merge --- .github/dependabot.yml | 33 +++++++++++++++++++++++++++--- .github/workflows/test-website.yml | 5 ++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 46c64120..2f10ad77 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,12 +3,39 @@ updates: - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + cooldown: + default-days: 7 + groups: + npm-dependencies: + patterns: + - "*" + - package-ecosystem: "pip" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + cooldown: + default-days: 7 + groups: + pip-dependencies: + patterns: + - "*" + - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + cooldown: + default-days: 7 + groups: + github-actions: + patterns: + - "*" + diff --git a/.github/workflows/test-website.yml b/.github/workflows/test-website.yml index c8d5fc94..26e5c45b 100644 --- a/.github/workflows/test-website.yml +++ b/.github/workflows/test-website.yml @@ -100,8 +100,11 @@ jobs: - name: Enable auto-merge for Dependabot PRs if: | steps.metadata.outputs.update-type == 'version-update:semver-patch' || - steps.metadata.outputs.update-type == 'version-update:semver-minor' + steps.metadata.outputs.update-type == 'version-update:semver-minor' || + steps.metadata.outputs.update-type == '' || + steps.metadata.outputs.update-type == 'null' run: gh pr merge --squash --auto "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + From 0248a51a73ad57cd9cdb25cc8d7ac656c915b47e Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:44:06 +0200 Subject: [PATCH 2/7] ci: invert update-type check to exclude major and drop redundant PR limit --- .github/dependabot.yml | 3 --- .github/workflows/test-website.yml | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2f10ad77..6fbb0f02 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,6 @@ updates: schedule: interval: "weekly" day: "monday" - open-pull-requests-limit: 10 cooldown: default-days: 7 groups: @@ -18,7 +17,6 @@ updates: schedule: interval: "weekly" day: "monday" - open-pull-requests-limit: 10 cooldown: default-days: 7 groups: @@ -31,7 +29,6 @@ updates: schedule: interval: "weekly" day: "monday" - open-pull-requests-limit: 10 cooldown: default-days: 7 groups: diff --git a/.github/workflows/test-website.yml b/.github/workflows/test-website.yml index 26e5c45b..f528faf5 100644 --- a/.github/workflows/test-website.yml +++ b/.github/workflows/test-website.yml @@ -98,11 +98,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs - if: | - steps.metadata.outputs.update-type == 'version-update:semver-patch' || - steps.metadata.outputs.update-type == 'version-update:semver-minor' || - steps.metadata.outputs.update-type == '' || - steps.metadata.outputs.update-type == 'null' + if: steps.metadata.outputs.update-type != 'version-update:semver-major' run: gh pr merge --squash --auto "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} From 082cbd7ca9e8190f277438815ad4ecb334f9b56a Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:58:59 +0200 Subject: [PATCH 3/7] ci: configure multi-ecosystem-groups for single consolidated PR per repo --- .github/dependabot.yml | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6fbb0f02..cf55450c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,38 +1,34 @@ version: 2 -updates: - - package-ecosystem: "npm" - directory: "/" + +multi-ecosystem-groups: + dependencies: schedule: interval: "weekly" day: "monday" + +updates: + - package-ecosystem: "npm" + directory: "/" + patterns: + - "*" + multi-ecosystem-group: "dependencies" cooldown: default-days: 7 - groups: - npm-dependencies: - patterns: - - "*" - package-ecosystem: "pip" directory: "/" - schedule: - interval: "weekly" - day: "monday" + patterns: + - "*" + multi-ecosystem-group: "dependencies" cooldown: default-days: 7 - groups: - pip-dependencies: - patterns: - - "*" - package-ecosystem: "github-actions" directory: "/" - schedule: - interval: "weekly" - day: "monday" + patterns: + - "*" + multi-ecosystem-group: "dependencies" cooldown: default-days: 7 - groups: - github-actions: - patterns: - - "*" + From ba2bc793faa0c1632a34cd63ab818db4e893654c Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:06:01 +0200 Subject: [PATCH 4/7] chore: update dependabot schedule interval from weekly to monthly Signed-off-by: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> --- .github/dependabot.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cf55450c..904a2690 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,7 @@ version: 2 multi-ecosystem-groups: dependencies: schedule: - interval: "weekly" - day: "monday" + interval: "monthly" updates: - package-ecosystem: "npm" From 6a130618008c2c24475922c22b22013799262ff7 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:06:50 +0200 Subject: [PATCH 5/7] refactor: remove trailing blank lines in dependabot configuration Signed-off-by: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> --- .github/dependabot.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 904a2690..6d68be34 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -29,5 +29,3 @@ updates: multi-ecosystem-group: "dependencies" cooldown: default-days: 7 - - From d30e4d922a27c4414062f48be93683b263cc90ad Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:13:07 +0200 Subject: [PATCH 6/7] ci: wrap long condition in test-website.yml to fix yamllint line length --- .github/workflows/test-website.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-website.yml b/.github/workflows/test-website.yml index f528faf5..579c5c97 100644 --- a/.github/workflows/test-website.yml +++ b/.github/workflows/test-website.yml @@ -66,7 +66,10 @@ jobs: temporaryPublicStorage: true # upload lighthouse report to the temporary storage - name: Deploy to Firebase Hosting Preview Channel - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' + if: | + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + github.actor != 'dependabot[bot]' uses: FirebaseExtended/action-hosting-deploy@v0 with: repoToken: '${{ secrets.GITHUB_TOKEN }}' From 2eab1f0a0437e3b32b961770187aecfdb225bcb7 Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:33:41 +0200 Subject: [PATCH 7/7] fix(techreport): defensively validate filter data arrays to prevent TypeError on API errors --- src/js/components/filters.js | 64 +++++++++++++++++++++--------------- src/js/techreport/index.js | 17 ++++++++-- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/js/components/filters.js b/src/js/components/filters.js index b2a0305e..a9f3711b 100644 --- a/src/js/components/filters.js +++ b/src/js/components/filters.js @@ -3,10 +3,14 @@ import { DataUtils } from "../techreport/utils/data"; class Filters { constructor(filterData, filters) { - this.categories = filterData?.categories; - this.technologies = filterData?.technologies; - this.geos = filterData?.geos; - this.ranks = filterData?.ranks; + this.categories = Array.isArray(filterData?.categories) ? filterData.categories : []; + this.technologies = Array.isArray(filterData?.technologies) ? filterData.technologies : []; + this.geos = Array.isArray(filterData?.geos) && filterData.geos.length > 0 + ? filterData.geos + : [{ geo: 'ALL' }]; + this.ranks = Array.isArray(filterData?.ranks) && filterData.ranks.length > 0 + ? filterData.ranks + : [{ rank: 'ALL' }]; this.filters = filters; @@ -152,37 +156,45 @@ class Filters { /* Update the list with geographies */ updateGeo() { const select = document.querySelector('select#geo'); + if (!select) return; select.innerHTML = ''; const normalizedFilter = decodeURIComponent(this.filters.geo || '').trim(); - this.geos.forEach((geo) => { - const optionTmpl = document.getElementById('filter-option').content.cloneNode(true); - const option = optionTmpl.querySelector('option'); - const formattedTech = geo.geo; - option.textContent = geo.geo; - option.value = formattedTech; - if(formattedTech === this.filters.geo || formattedTech === normalizedFilter) { - option.selected = true; - } - select.append(optionTmpl); - }); + if (Array.isArray(this.geos)) { + this.geos.forEach((geo) => { + const optionTmpl = document.getElementById('filter-option')?.content?.cloneNode(true); + if (!optionTmpl) return; + const option = optionTmpl.querySelector('option'); + const formattedTech = geo.geo; + option.textContent = geo.geo; + option.value = formattedTech; + if(formattedTech === this.filters.geo || formattedTech === normalizedFilter) { + option.selected = true; + } + select.append(optionTmpl); + }); + } } /* Update the list with ranks */ updateRank() { const select = document.querySelector('select#rank'); + if (!select) return; select.innerHTML = ''; const normalizedFilter = decodeURIComponent(this.filters.rank || '').trim(); - this.ranks.forEach((rank) => { - const optionTmpl = document.getElementById('filter-option').content.cloneNode(true); - const option = optionTmpl.querySelector('option'); - const formattedTech = rank.rank; - option.textContent = rank.rank; - option.value = formattedTech; - if(formattedTech === this.filters.rank || formattedTech === normalizedFilter) { - option.selected = true; - } - select.append(optionTmpl); - }); + if (Array.isArray(this.ranks)) { + this.ranks.forEach((rank) => { + const optionTmpl = document.getElementById('filter-option')?.content?.cloneNode(true); + if (!optionTmpl) return; + const option = optionTmpl.querySelector('option'); + const formattedTech = rank.rank; + option.textContent = rank.rank; + option.value = formattedTech; + if(formattedTech === this.filters.rank || formattedTech === normalizedFilter) { + option.selected = true; + } + select.append(optionTmpl); + }); + } } /* Update the list with categories */ diff --git a/src/js/techreport/index.js b/src/js/techreport/index.js index 96ff414a..aa2238ab 100644 --- a/src/js/techreport/index.js +++ b/src/js/techreport/index.js @@ -389,9 +389,20 @@ class TechReport { const url = `${Constants.apiBase}/${api.endpoint}`; return fetch(url) - .then(result => result.json()) - .then(result => filterData[api.name] = result) - .catch(error => console.log('Something went wrong', error)); + .then(result => { + if (!result.ok) { + console.warn(`Failed to fetch ${api.name}: ${result.status}`); + return []; + } + return result.json(); + }) + .then(result => { + filterData[api.name] = Array.isArray(result) ? result : []; + }) + .catch(error => { + console.warn('Something went wrong fetching filter data', error); + filterData[api.name] = []; + }); })).then(() => { const FilterComponent = new Filters(filterData, this.filters);