openvibers/openvibe.tours Commits
The cities job reads a committed list (production had no cities) (#1)
Changes
nnew file mode 100644@@@ -0,0 +1,17 @@++name: ci++on:++ push:++ branches: [main]++ pull_request:++jobs:++ # install, node --check and npm test: the shared test job (Track Q).++ test:++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176++ with:++ syntax-dirs: server test scripts++ # ADR-035: PostgreSQL 18 + PgBouncer (transaction mode) + Valkey 9 containers (openvibe-sdk++ # scripts/test-services.sh). npm test runs every file on PGlite; test:pg runs them all through PgBouncer.++ test: 'eval "$(node_modules/openvibe-sdk/scripts/test-services.sh up)" && npm test && npm run test:pg'++ # Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo (Track Q).++ security:++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176nnew file mode 100644@@@ -0,0 +1,102 @@++[++ { "name": "Tokyo", "country": "Japan" },++ { "name": "Delhi", "country": "India" },++ { "name": "Shanghai", "country": "China" },++ { "name": "Dhaka", "country": "Bangladesh" },++ { "name": "Cairo", "country": "Egypt" },++ { "name": "Mumbai", "country": "India" },++ { "name": "Beijing", "country": "China" },++ { "name": "Osaka", "country": "Japan" },++ { "name": "New York City", "country": "United States", "wikipedia": "New York City" },++ { "name": "Karachi", "country": "Pakistan" },++ { "name": "Buenos Aires", "country": "Argentina" },++ { "name": "Chongqing", "country": "China" },++ { "name": "Istanbul", "country": "Turkey" },++ { "name": "Kolkata", "country": "India" },++ { "name": "Manila", "country": "Philippines" },++ { "name": "Lagos", "country": "Nigeria" },++ { "name": "Rio de Janeiro", "country": "Brazil" },++ { "name": "Tianjin", "country": "China" },++ { "name": "Kinshasa", "country": "Democratic Republic of the Congo" },++ { "name": "Guangzhou", "country": "China" },++ { "name": "Los Angeles", "country": "United States" },++ { "name": "Moscow", "country": "Russia" },++ { "name": "Shenzhen", "country": "China" },++ { "name": "Lahore", "country": "Pakistan" },++ { "name": "Bangalore", "country": "India", "wikipedia": "Bangalore" },++ { "name": "Paris", "country": "France" },++ { "name": "Bogotá", "country": "Colombia" },++ { "name": "Jakarta", "country": "Indonesia" },++ { "name": "Chennai", "country": "India" },++ { "name": "Lima", "country": "Peru" },++ { "name": "Bangkok", "country": "Thailand" },++ { "name": "Seoul", "country": "South Korea" },++ { "name": "Nagoya", "country": "Japan" },++ { "name": "Hyderabad", "country": "India" },++ { "name": "London", "country": "United Kingdom" },++ { "name": "Tehran", "country": "Iran" },++ { "name": "Chicago", "country": "United States" },++ { "name": "Chengdu", "country": "China" },++ { "name": "Nanjing", "country": "China" },++ { "name": "Wuhan", "country": "China" },++ { "name": "Ho Chi Minh City", "country": "Vietnam" },++ { "name": "Luanda", "country": "Angola" },++ { "name": "Ahmedabad", "country": "India" },++ { "name": "Kuala Lumpur", "country": "Malaysia" },++ { "name": "Hong Kong", "country": "China" },++ { "name": "Riyadh", "country": "Saudi Arabia" },++ { "name": "Baghdad", "country": "Iraq" },++ { "name": "Santiago", "country": "Chile" },++ { "name": "Surat", "country": "India" },++ { "name": "Madrid", "country": "Spain" },++ { "name": "Suzhou", "country": "China" },++ { "name": "Pune", "country": "India" },++ { "name": "Harbin", "country": "China" },++ { "name": "Houston", "country": "United States" },++ { "name": "Dallas", "country": "United States" },++ { "name": "Toronto", "country": "Canada" },++ { "name": "Dar es Salaam", "country": "Tanzania" },++ { "name": "Miami", "country": "United States" },++ { "name": "Belo Horizonte", "country": "Brazil" },++ { "name": "Singapore", "country": "Singapore" },++ { "name": "Philadelphia", "country": "United States" },++ { "name": "Atlanta", "country": "United States" },++ { "name": "Fukuoka", "country": "Japan" },++ { "name": "Khartoum", "country": "Sudan" },++ { "name": "Barcelona", "country": "Spain" },++ { "name": "Johannesburg", "country": "South Africa" },++ { "name": "Saint Petersburg", "country": "Russia" },++ { "name": "Qingdao", "country": "China" },++ { "name": "Dalian", "country": "China" },++ { "name": "Washington, D.C.", "country": "United States" },++ { "name": "Yangon", "country": "Myanmar" },++ { "name": "Alexandria", "country": "Egypt" },++ { "name": "Jeddah", "country": "Saudi Arabia" },++ { "name": "Hanoi", "country": "Vietnam" },++ { "name": "Sydney", "country": "Australia" },++ { "name": "Guadalajara", "country": "Mexico" },++ { "name": "Melbourne", "country": "Australia" },++ { "name": "Cape Town", "country": "South Africa" },++ { "name": "Rome", "country": "Italy" },++ { "name": "Vienna", "country": "Austria" },++ { "name": "Amsterdam", "country": "Netherlands" },++ { "name": "Prague", "country": "Czech Republic" },++ { "name": "Lisbon", "country": "Portugal" },++ { "name": "Budapest", "country": "Hungary" },++ { "name": "Copenhagen", "country": "Denmark" },++ { "name": "Stockholm", "country": "Sweden" },++ { "name": "Oslo", "country": "Norway" },++ { "name": "Helsinki", "country": "Finland" },++ { "name": "Dublin", "country": "Ireland" },++ { "name": "Athens", "country": "Greece" },++ { "name": "Zurich", "country": "Switzerland" },++ { "name": "Munich", "country": "Germany" },++ { "name": "Berlin", "country": "Germany" },++ { "name": "Brussels", "country": "Belgium" },++ { "name": "Warsaw", "country": "Poland" },++ { "name": "Edinburgh", "country": "United Kingdom" },++ { "name": "Vancouver", "country": "Canada" },++ { "name": "Montreal", "country": "Canada" },++ { "name": "San Francisco", "country": "United States" },++ { "name": "Mexico City", "country": "Mexico" }++]@@@ -16,7 +16,8 @@ const { createWikidata } = require('../sources/wikidata');const { parseCityPage, pageUrl } = require('../sources/wikivoyage');const { slugify } = require('../store');--const CATALOG = path.join(__dirname, '..', '..', 'data', 'top-cities.json');++// The list of cities to read (committed: data/ is for the development database and is not in git).++const CATALOG = path.join(__dirname, '..', '..', 'seed', 'top-cities.json');function loadCatalog(limit = 100) {const rows = JSON.parse(fs.readFileSync(CATALOG, 'utf8'));nnew file mode 100644@@@ -0,0 +1,15 @@++'use strict';++// The cities job reads its list from seed/top-cities.json, which is committed. It once read data/, which is the++// development database's folder and not in git, so production started with no cities (10-31).++const assert = require('assert');++const fs = require('fs');++const path = require('path');++++const list = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'seed/top-cities.json'), 'utf8'));++assert.ok(Array.isArray(list) && list.length >= 50, `a real list of cities (${list.length})`);++for (const c of list) assert.ok(c && typeof c.name === 'string' && c.name && typeof c.country === 'string' && c.country, JSON.stringify(c));++const job = fs.readFileSync(path.join(__dirname, '..', 'server/jobs/cities.js'), 'utf8');++assert.match(job, /path\.join\(__dirname, '\.\.', '\.\.', 'seed', 'top-cities\.json'\)/, 'the job reads the committed list');++const ignore = fs.readFileSync(path.join(__dirname, '..', '.gitignore'), 'utf8');++assert.ok(!/^seed\/?$/m.test(ignore), 'seed/ is not ignored');++console.log(`tours seed list: ${list.length} cities, committed and read from seed/`);
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+name: ci
+on:
+ push:
+ branches: [main]
+ pull_request:
+jobs:
+ # install, node --check and npm test: the shared test job (Track Q).
+ test:
+ uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
+ with:
+ syntax-dirs: server test scripts
+ # ADR-035: PostgreSQL 18 + PgBouncer (transaction mode) + Valkey 9 containers (openvibe-sdk
+ # scripts/test-services.sh). npm test runs every file on PGlite; test:pg runs them all through PgBouncer.
+ test: 'eval "$(node_modules/openvibe-sdk/scripts/test-services.sh up)" && npm test && npm run test:pg'
+ # Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo (Track Q).
+ security:
+ uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
new file mode 100644
new file mode 100644
@@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
+[
+ { "name": "Tokyo", "country": "Japan" },
+ { "name": "Delhi", "country": "India" },
+ { "name": "Shanghai", "country": "China" },
+ { "name": "Dhaka", "country": "Bangladesh" },
+ { "name": "Cairo", "country": "Egypt" },
+ { "name": "Mumbai", "country": "India" },
+ { "name": "Beijing", "country": "China" },
+ { "name": "Osaka", "country": "Japan" },
+ { "name": "New York City", "country": "United States", "wikipedia": "New York City" },
+ { "name": "Karachi", "country": "Pakistan" },
+ { "name": "Buenos Aires", "country": "Argentina" },
+ { "name": "Chongqing", "country": "China" },
+ { "name": "Istanbul", "country": "Turkey" },
+ { "name": "Kolkata", "country": "India" },
+ { "name": "Manila", "country": "Philippines" },
+ { "name": "Lagos", "country": "Nigeria" },
+ { "name": "Rio de Janeiro", "country": "Brazil" },
+ { "name": "Tianjin", "country": "China" },
+ { "name": "Kinshasa", "country": "Democratic Republic of the Congo" },
+ { "name": "Guangzhou", "country": "China" },
+ { "name": "Los Angeles", "country": "United States" },
+ { "name": "Moscow", "country": "Russia" },
+ { "name": "Shenzhen", "country": "China" },
+ { "name": "Lahore", "country": "Pakistan" },
+ { "name": "Bangalore", "country": "India", "wikipedia": "Bangalore" },
+ { "name": "Paris", "country": "France" },
+ { "name": "Bogotá", "country": "Colombia" },
+ { "name": "Jakarta", "country": "Indonesia" },
+ { "name": "Chennai", "country": "India" },
+ { "name": "Lima", "country": "Peru" },
+ { "name": "Bangkok", "country": "Thailand" },
+ { "name": "Seoul", "country": "South Korea" },
+ { "name": "Nagoya", "country": "Japan" },
+ { "name": "Hyderabad", "country": "India" },
+ { "name": "London", "country": "United Kingdom" },
+ { "name": "Tehran", "country": "Iran" },
+ { "name": "Chicago", "country": "United States" },
+ { "name": "Chengdu", "country": "China" },
+ { "name": "Nanjing", "country": "China" },
+ { "name": "Wuhan", "country": "China" },
+ { "name": "Ho Chi Minh City", "country": "Vietnam" },
+ { "name": "Luanda", "country": "Angola" },
+ { "name": "Ahmedabad", "country": "India" },
+ { "name": "Kuala Lumpur", "country": "Malaysia" },
+ { "name": "Hong Kong", "country": "China" },
+ { "name": "Riyadh", "country": "Saudi Arabia" },
+ { "name": "Baghdad", "country": "Iraq" },
+ { "name": "Santiago", "country": "Chile" },
+ { "name": "Surat", "country": "India" },
+ { "name": "Madrid", "country": "Spain" },
+ { "name": "Suzhou", "country": "China" },
+ { "name": "Pune", "country": "India" },
+ { "name": "Harbin", "country": "China" },
+ { "name": "Houston", "country": "United States" },
+ { "name": "Dallas", "country": "United States" },
+ { "name": "Toronto", "country": "Canada" },
+ { "name": "Dar es Salaam", "country": "Tanzania" },
+ { "name": "Miami", "country": "United States" },
+ { "name": "Belo Horizonte", "country": "Brazil" },
+ { "name": "Singapore", "country": "Singapore" },
+ { "name": "Philadelphia", "country": "United States" },
+ { "name": "Atlanta", "country": "United States" },
+ { "name": "Fukuoka", "country": "Japan" },
+ { "name": "Khartoum", "country": "Sudan" },
+ { "name": "Barcelona", "country": "Spain" },
+ { "name": "Johannesburg", "country": "South Africa" },
+ { "name": "Saint Petersburg", "country": "Russia" },
+ { "name": "Qingdao", "country": "China" },
+ { "name": "Dalian", "country": "China" },
+ { "name": "Washington, D.C.", "country": "United States" },
+ { "name": "Yangon", "country": "Myanmar" },
+ { "name": "Alexandria", "country": "Egypt" },
+ { "name": "Jeddah", "country": "Saudi Arabia" },
+ { "name": "Hanoi", "country": "Vietnam" },
+ { "name": "Sydney", "country": "Australia" },
+ { "name": "Guadalajara", "country": "Mexico" },
+ { "name": "Melbourne", "country": "Australia" },
+ { "name": "Cape Town", "country": "South Africa" },
+ { "name": "Rome", "country": "Italy" },
+ { "name": "Vienna", "country": "Austria" },
+ { "name": "Amsterdam", "country": "Netherlands" },
+ { "name": "Prague", "country": "Czech Republic" },
+ { "name": "Lisbon", "country": "Portugal" },
+ { "name": "Budapest", "country": "Hungary" },
+ { "name": "Copenhagen", "country": "Denmark" },
+ { "name": "Stockholm", "country": "Sweden" },
+ { "name": "Oslo", "country": "Norway" },
+ { "name": "Helsinki", "country": "Finland" },
+ { "name": "Dublin", "country": "Ireland" },
+ { "name": "Athens", "country": "Greece" },
+ { "name": "Zurich", "country": "Switzerland" },
+ { "name": "Munich", "country": "Germany" },
+ { "name": "Berlin", "country": "Germany" },
+ { "name": "Brussels", "country": "Belgium" },
+ { "name": "Warsaw", "country": "Poland" },
+ { "name": "Edinburgh", "country": "United Kingdom" },
+ { "name": "Vancouver", "country": "Canada" },
+ { "name": "Montreal", "country": "Canada" },
+ { "name": "San Francisco", "country": "United States" },
+ { "name": "Mexico City", "country": "Mexico" }
+]
@@ -16,7 +16,8 @@ const { createWikidata } = require('../sources/wikidata');
@@ -16,7 +16,8 @@ const { createWikidata } = require('../sources/wikidata');
const { parseCityPage, pageUrl } = require('../sources/wikivoyage');
const { parseCityPage, pageUrl } = require('../sources/wikivoyage');
const { slugify } = require('../store');
const { slugify } = require('../store');
-const CATALOG = path.join(__dirname, '..', '..', 'data', 'top-cities.json');
+// The list of cities to read (committed: data/ is for the development database and is not in git).
+const CATALOG = path.join(__dirname, '..', '..', 'seed', 'top-cities.json');
function loadCatalog(limit = 100) {
function loadCatalog(limit = 100) {
const rows = JSON.parse(fs.readFileSync(CATALOG, 'utf8'));
const rows = JSON.parse(fs.readFileSync(CATALOG, 'utf8'));
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+'use strict';
+// The cities job reads its list from seed/top-cities.json, which is committed. It once read data/, which is the
+// development database's folder and not in git, so production started with no cities (10-31).
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+
+const list = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'seed/top-cities.json'), 'utf8'));
+assert.ok(Array.isArray(list) && list.length >= 50, `a real list of cities (${list.length})`);
+for (const c of list) assert.ok(c && typeof c.name === 'string' && c.name && typeof c.country === 'string' && c.country, JSON.stringify(c));
+const job = fs.readFileSync(path.join(__dirname, '..', 'server/jobs/cities.js'), 'utf8');
+assert.match(job, /path\.join\(__dirname, '\.\.', '\.\.', 'seed', 'top-cities\.json'\)/, 'the job reads the committed list');
+const ignore = fs.readFileSync(path.join(__dirname, '..', '.gitignore'), 'utf8');
+assert.ok(!/^seed\/?$/m.test(ignore), 'seed/ is not ignored');
+console.log(`tours seed list: ${list.length} cities, committed and read from seed/`);