Skip to content

Commit d6f1627

Browse files
committed
Improve separation of concerns
Extract the technical upgrade functionality into a standalone method
1 parent 8491d53 commit d6f1627

File tree

7 files changed

+94
-17
lines changed

7 files changed

+94
-17
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ All changes that impact users of this module are documented in this file, in the
66

77
> Development of this release was supported by the [French Ministry for Foreign Affairs](https://www.diplomatie.gouv.fr/fr/politique-etrangere-de-la-france/diplomatie-numerique/) through its ministerial [State Startups incubator](https://beta.gouv.fr/startups/open-terms-archive.html) under the aegis of the Ambassador for Digital Affairs.
88
9+
### Added
10+
11+
- Add `applyTechnicalUpgrades()` function as a standalone method to apply technical upgrades without regular tracking
12+
- Add `ota apply-technical-upgrades` CLI command to apply technical upgrades independently
13+
914
### Changed
1015

11-
- **Breaking:** Rename `extractOnly` parameter to `technicalUpgradeOnly` in `track()` function to clarify that only the technical upgrade pass will run; ifyou are using the `track()` function directly with the `extractOnly` parameter, rename it to `technicalUpgradeOnly`
12-
- **Breaking:** Rename CLI flag from `--extract-only` to `--technical-upgrade-only` for consistency and clarity; if you are using the CLI with the `--extract-only` flag, rename it to `--technical-upgrade-only`
16+
- **Breaking:** Remove `--extract-only` option from `ota track` command; use the new `ota apply-technical-upgrades` command instead
1317
- Enhance technical upgrade pass to fetch missing snapshots for newly added source documents in combined terms declarations
1418

1519
## 9.2.0 - 2025-11-05
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#! /usr/bin/env node
2+
import './env.js';
3+
4+
import path from 'path';
5+
import { fileURLToPath, pathToFileURL } from 'url';
6+
7+
import { program } from 'commander';
8+
9+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
10+
11+
const { applyTechnicalUpgrades } = await import(pathToFileURL(path.resolve(__dirname, '../src/index.js'))); // load asynchronously to ensure env.js is loaded before
12+
13+
program
14+
.name('ota apply-technical-upgrades')
15+
.description('Apply technical upgrades: regenerate versions from existing snapshots with updated declarations/engine, and fetch missing snapshots for newly added source documents')
16+
.option('-s, --services [serviceId...]', 'service IDs to apply technical upgrades to')
17+
.option('-t, --types [termsType...]', 'terms types to apply technical upgrades to');
18+
19+
applyTechnicalUpgrades(program.parse(process.argv).opts());

bin/ota-track.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ program
1515
.description('Retrieve declared documents, record snapshots, extract versions and publish the resulting records')
1616
.option('-s, --services [serviceId...]', 'service IDs of services to track')
1717
.option('-t, --types [termsType...]', 'terms types to track')
18-
.option('-u, --technical-upgrade-only', 'only apply technical upgrades: regenerate versions from existing snapshots with updated declarations/engine, and fetch missing snapshots for newly added source documents; skip regular tracking')
1918
.option('--schedule', 'track automatically at a regular interval');
2019

2120
track(program.parse(process.argv).opts());

bin/ota.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ program
1111
.description(description)
1212
.version(version)
1313
.command('track', 'Track the current terms of services according to provided declarations')
14+
.command('apply-technical-upgrades', 'Apply technical upgrades: regenerate versions from existing snapshots with updated declarations/engine')
1415
.command('validate', 'Run a series of tests to check the validity of terms declarations')
1516
.command('lint', 'Check format and stylistic errors in declarations and auto fix them')
1617
.command('dataset', 'Export the versions dataset into a ZIP file and optionally publish it to GitHub releases')

src/archivist/index.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const { version: PACKAGE_VERSION } = require('../../package.json');
2020
// - too many requests on the same endpoint yield 403
2121
// - sometimes when creating a commit no SHA are returned for unknown reasons
2222
const MAX_PARALLEL_TRACKING = 1;
23-
const MAX_PARALLEL_EXTRACTING = 10;
23+
const MAX_PARALLEL_TECHNICAL_UPGRADES = 10;
2424

2525
export const EVENTS = [
2626
'snapshotRecorded',
@@ -128,22 +128,22 @@ export default class Archivist extends events.EventEmitter {
128128
});
129129
}
130130

131-
async track({ services: servicesIds = this.servicesIds, types: termsTypes = [], technicalUpgradeOnly = false } = {}) {
131+
async track({ services: servicesIds = this.servicesIds, types: termsTypes = [] } = {}) {
132132
const numberOfTerms = Service.getNumberOfTerms(this.services, servicesIds, termsTypes);
133133

134-
this.emit('trackingStarted', servicesIds.length, numberOfTerms, technicalUpgradeOnly);
134+
this.emit('trackingStarted', servicesIds.length, numberOfTerms, false);
135135

136136
await Promise.all([ launchHeadlessBrowser(), this.recorder.initialize() ]);
137137

138-
this.trackingQueue.concurrency = technicalUpgradeOnly ? MAX_PARALLEL_EXTRACTING : MAX_PARALLEL_TRACKING;
138+
this.trackingQueue.concurrency = MAX_PARALLEL_TRACKING;
139139

140140
servicesIds.forEach(serviceId => {
141141
this.services[serviceId].getTermsTypes().forEach(termsType => {
142142
if (termsTypes.length && !termsTypes.includes(termsType)) {
143143
return;
144144
}
145145

146-
this.trackingQueue.push({ terms: this.services[serviceId].getTerms({ type: termsType }), technicalUpgradeOnly });
146+
this.trackingQueue.push({ terms: this.services[serviceId].getTerms({ type: termsType }), technicalUpgradeOnly: false });
147147
});
148148
});
149149

@@ -153,7 +153,35 @@ export default class Archivist extends events.EventEmitter {
153153

154154
await Promise.all([ stopHeadlessBrowser(), this.recorder.finalize() ]);
155155

156-
this.emit('trackingCompleted', servicesIds.length, numberOfTerms, technicalUpgradeOnly);
156+
this.emit('trackingCompleted', servicesIds.length, numberOfTerms, false);
157+
}
158+
159+
async applyTechnicalUpgrades({ services: servicesIds = this.servicesIds, types: termsTypes = [] } = {}) {
160+
const numberOfTerms = Service.getNumberOfTerms(this.services, servicesIds, termsTypes);
161+
162+
this.emit('trackingStarted', servicesIds.length, numberOfTerms, true);
163+
164+
await Promise.all([ launchHeadlessBrowser(), this.recorder.initialize() ]);
165+
166+
this.trackingQueue.concurrency = MAX_PARALLEL_TECHNICAL_UPGRADES;
167+
168+
servicesIds.forEach(serviceId => {
169+
this.services[serviceId].getTermsTypes().forEach(termsType => {
170+
if (termsTypes.length && !termsTypes.includes(termsType)) {
171+
return;
172+
}
173+
174+
this.trackingQueue.push({ terms: this.services[serviceId].getTerms({ type: termsType }), technicalUpgradeOnly: true });
175+
});
176+
});
177+
178+
if (this.trackingQueue.length()) {
179+
await this.trackingQueue.drain();
180+
}
181+
182+
await Promise.all([ stopHeadlessBrowser(), this.recorder.finalize() ]);
183+
184+
this.emit('trackingCompleted', servicesIds.length, numberOfTerms, true);
157185
}
158186

159187
async trackTermsChanges({ terms, technicalUpgradeOnly = false }) {

src/archivist/index.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe('Archivist', function () {
167167

168168
app.services[SERVICE_A_ID].getTerms({ type: SERVICE_A_TYPE }).sourceDocuments[0].contentSelectors = 'h1';
169169

170-
await app.track({ services: [ 'service·A', 'Service B!' ], technicalUpgradeOnly: true });
170+
await app.applyTechnicalUpgrades({ services: [ 'service·A', 'Service B!' ] });
171171

172172
const [reExtractedVersionCommit] = await gitVersion.log({ file: SERVICE_A_EXPECTED_VERSION_FILE_PATH });
173173

@@ -229,7 +229,7 @@ describe('Archivist', function () {
229229
}
230230
versionNotChangedSpy(record);
231231
});
232-
await app.track({ services, technicalUpgradeOnly: true });
232+
await app.applyTechnicalUpgrades({ services });
233233
});
234234

235235
after(resetGitRepositories);

src/index.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Reporter from './reporter/index.js';
1313
const require = createRequire(import.meta.url);
1414
const { version: PACKAGE_VERSION } = require('../package.json');
1515

16-
export default async function track({ services, types, technicalUpgradeOnly, schedule }) {
16+
export default async function track({ services, types, schedule }) {
1717
const archivist = new Archivist({
1818
recorderConfig: config.get('@opentermsarchive/engine.recorder'),
1919
fetcherConfig: config.get('@opentermsarchive/engine.fetcher'),
@@ -44,11 +44,7 @@ export default async function track({ services, types, technicalUpgradeOnly, sch
4444
// This regenerates versions from existing snapshots with updated extraction logic.
4545
// For terms with combined source documents, if a new document was added to the declaration, it will be fetched and combined with existing snapshots to regenerate the complete version.
4646
// All versions from this pass are labeled as technical upgrades to avoid false notifications about content changes.
47-
await archivist.track({ services, types, technicalUpgradeOnly: true });
48-
49-
if (technicalUpgradeOnly) {
50-
return;
51-
}
47+
await archivist.applyTechnicalUpgrades({ services, types });
5248

5349
if (process.env.OTA_ENGINE_SENDINBLUE_API_KEY) {
5450
try {
@@ -91,3 +87,33 @@ export default async function track({ services, types, technicalUpgradeOnly, sch
9187
() => archivist.track({ services, types }),
9288
);
9389
}
90+
91+
export async function applyTechnicalUpgrades({ services, types }) {
92+
const archivist = new Archivist({
93+
recorderConfig: config.get('@opentermsarchive/engine.recorder'),
94+
fetcherConfig: config.get('@opentermsarchive/engine.fetcher'),
95+
});
96+
97+
archivist.attach(logger);
98+
99+
await archivist.initialize();
100+
101+
const collection = await getCollection();
102+
const collectionName = collection?.name ? ` with ${collection.name} collection` : '';
103+
104+
logger.info(`Start engine v${PACKAGE_VERSION}${collectionName}\n`);
105+
106+
if (services?.length) {
107+
services = services.filter(serviceId => {
108+
const isServiceDeclared = archivist.services[serviceId];
109+
110+
if (!isServiceDeclared) {
111+
logger.warn(`Parameter "${serviceId}" was interpreted as a service ID to update, but no matching declaration was found; it will be ignored`);
112+
}
113+
114+
return isServiceDeclared;
115+
});
116+
}
117+
118+
await archivist.applyTechnicalUpgrades({ services, types });
119+
}

0 commit comments

Comments
 (0)