Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"js-yaml": "^4.1.0",
"mdx-mermaid": "^1.2.2",
"mermaid": "^8.14.0",
"nodemw": "^0.16.0",
"nodemw": "^0.17.0",
"plugin-image-zoom": "flexanalytics/plugin-image-zoom",
"prism-react-renderer": "^1.3.1",
"raw-loader": "^4.0.2",
Expand Down Expand Up @@ -87,4 +87,4 @@
"typescript": "^4.6.3",
"unist-util-inspect": "6.0.0"
}
}
}
158 changes: 149 additions & 9 deletions scripts/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,42 @@ const getGetMigratedPageIds = (logger) => (client) => async (pageTitle) => {
return articleData;
};

const getGetPageProtectionState = (logger) => (client) => async (pageTitle) => {
logger.debug(`===> Fetching protection state for ${pageTitle}`);
const options = {
inprop: [
'protection',
],
};
return new Promise((resolve, reject) => {
client.getArticleInfo([pageTitle], options, (err, [data]) => {
if (err) {
reject(err);
}
resolve({
pageTitle,
data,
});
});
});
};

const getProtectPage = (logger) => (client) => async (pageTitle, protections, options) => {
logger.debug(`===> Setting protection state for ${pageTitle} to ${protections}`);

return new Promise((resolve, reject) => {
client.protect(pageTitle, protections, options, (err, data) => {
if (err) {
reject(err);
}
resolve({
pageTitle,
data,
});
});
});
};

const getClient = (remoteHost) => new Bot({
protocol: 'https',
server: remoteHost,
Expand Down Expand Up @@ -129,10 +165,116 @@ const getObsoletePagePath = () => getNormalizedPath('data', 'obsoletePages.json'
*/
const getMigrationPagePath = () => getNormalizedPath('data', 'migratedPages.yml');

const getUpdateMigratedPagesProtection = (logger) => (client) => async () => {
const migratedPageData = yaml.load(await readFile(getMigrationPagePath(), 'utf8'));

const getPageProtectionState = (pageTitle) => getGetPageProtectionState(logger)(client)(pageTitle);
const protectPage = (pageTitle, protections, reason) => getProtectPage(logger)(client)(
pageTitle,
protections,
{
// This is a bot. It does not need to subscribe.
watchlist: 'nochange',
reason,
},
);

/**
* Get the intended protection for the current page, modifying the 'edit' level to sysop.
* Other protections will be preserved.
* If an existing protection exists for edit=sysop, then it will only be modified if the expiry must be updated.
* If no change are required, none will be made.
*
* @param {object[]} current
* @returns {object}
*/
const getTargetProtection = (current) => {
// There are synonyms for 'infinite' which can be returned by the API.
const infiniteNames = [
'never',
'infinite',
'indefinite',
'infinity',
];

const protectionValue = {
type: 'edit',
level: 'sysop',
expiry: 'infinite',
};

let isProtected = false;
let changed = false;
if (!Array.isArray(current)) {
return {
changed: true,
protection: [protectionValue],
};
}

const protection = current.map((protectionItem) => {
if (protectionItem.type !== 'edit') {
return protectionItem;
}

if (protectionItem.level !== 'sysop') {
protectionItem.level = 'sysop';
changed = true;
}

if (infiniteNames.indexOf(protectionItem.expiry) === -1) {
protectionItem.expiry = 'infinite';
changed = true;
}

isProtected = true;

return protectionItem;
});

if (!isProtected) {
changed = true;
protection.push(protectionValue);
}

return {
protection,
changed,
};
};

for (const [legacyPage] of Object.entries(migratedPageData)) {
logger.debug(`=> Checking ${legacyPage}`);

const [
protectionState,
] = await Promise.all([
getPageProtectionState(legacyPage),
]);

const protectionReason = 'Developer Docs Migration';
const { protection, changed } = getTargetProtection(protectionState.data.protection);
if (changed) {
logger.info(`==> Updating page protection for ${legacyPage}`);
protectPage(legacyPage, protection, protectionReason)
.then(() => {
logger.debug(`===> Updated ${legacyPage}`);
})
.catch((...err) => {
logger.error(err);
});
} else {
logger.debug(`===> No need to update protection for ${legacyPage}`);
}
}
};

/**
* Update the migrated page docs in Wikimedia.
*/
const getUpdateMigratedPages = (logger) => (client) => async () => {
const migratedPageData = yaml.load(await readFile(getMigrationPagePath(), 'utf8'));

const getDocIdList = (newDocIds) => {
if (Array.isArray(newDocIds)) {
return newDocIds.map((newDoc) => newDoc.slug);
Expand All @@ -143,21 +285,18 @@ const getUpdateMigratedPages = (logger) => (client) => async () => {

const getCurrentMigratedIdsForPage = (pageTitle) => getGetMigratedPageIds(logger)(client)(pageTitle);

const migratedPageData = yaml.load(await readFile(getMigrationPagePath(), 'utf8'));

for (const [legacyPage, newPages] of Object.entries(migratedPageData)) {
logger.info(`=> Checking ${legacyPage}`);
logger.debug(`=> Checking ${legacyPage}`);
const newDocIds = getDocIdList(newPages);

const migratedDocData = (await getCurrentMigratedIdsForPage(legacyPage));
const migratedDocData = await getCurrentMigratedIdsForPage(legacyPage);
const docIds = migratedDocData.newDocIds.sort();

if (JSON.stringify(docIds) === JSON.stringify(newDocIds)) {
logger.info(`==> No changes (${docIds.join(', ')})`);
logger.debug(`==> No changes for ${legacyPage} (${docIds.join(', ')})`);
} else {
logger.info(`==> Updating ${legacyPage}`);
logger.info(`===> Current docIds are: ${docIds.join(', ')}`);
logger.info(`===> Setting docIds of ${newDocIds.join(', ')}`);
logger.debug(`===> Current docIds for ${legacyPage} are: ${docIds.join(', ')}`);
logger.info(`===> Setting docIds for ${legacyPage} to: ${newDocIds.join(', ')}`);
const newTemplates = newDocIds.map((newDocId) => `{{Template:Migrated|newDocId=${newDocId}}}\n`).join('');

const newContent = newTemplates + migratedDocData.data.replaceAll(
Expand All @@ -173,7 +312,7 @@ const getUpdateMigratedPages = (logger) => (client) => async () => {
if (err) {
throw err;
}
logger.info(`===> Updated ${legacyPage} to ${data.newrevid}`);
logger.debug(`===> Updated ${legacyPage} to ${data.newrevid}`);
},
);
}
Expand Down Expand Up @@ -218,5 +357,6 @@ module.exports = {
getObsoletePagePath,
getNormalizedPath,
getUpdateMigratedPages,
getUpdateMigratedPagesProtection,
guessSlug,
};
4 changes: 3 additions & 1 deletion scripts/wikimedia-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const {
getClient,
getObsoletePagePath,
getUpdateMigratedPages,
getUpdateMigratedPagesProtection,
} = require('./utils');

program
Expand All @@ -50,6 +51,7 @@ program
}

const updateMigratedPages = getUpdateMigratedPages(logger)(client);
const updateMigratedPagesProtection = getUpdateMigratedPagesProtection(logger)(client);

logger.info('Logging in');
try {
Expand All @@ -61,7 +63,7 @@ program
}

logger.info('Starting update of migrated pages in remote site');
await updateMigratedPages();
await Promise.all([updateMigratedPages(), updateMigratedPagesProtection()]);
logger.info('Run completed');
});

Expand Down
20 changes: 10 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4278,9 +4278,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==

color-string@^1.6.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa"
integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
Expand Down Expand Up @@ -9259,10 +9259,10 @@ node-version-compare@^1.0.1:
resolved "https://registry.yarnpkg.com/node-version-compare/-/node-version-compare-1.0.3.tgz#ca6d2005e67822fb4dfa259e08f1f6cfaabe2e81"
integrity sha512-unO5GpBAh5YqeGULMLpmDT94oanSDMwtZB8KHTKCH/qrGv8bHN0mlDj9xQDAicCYXv2OLnzdi67lidCrcVotVw==

nodemw@^0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/nodemw/-/nodemw-0.16.0.tgz#d9b430929258ec27be5959c46e1022dad57a91a1"
integrity sha512-C21O4Bxp1MAbmRvwKzGv4UHWX1qE48dC1OAGrXcnOjPpoJa7efq8DEwEuebgYD1pTWeupn+ozuOaGVT+L/h/4w==
nodemw@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/nodemw/-/nodemw-0.17.0.tgz#e7c2dbb11c692e36eca1103913e98e2d1afccf98"
integrity sha512-Cz5WnBU6dJlhgTOxX3lVJWsVux6ZA1uKBUvcxDbP9vkfN0ppu3wCo2K+7OvfNT7v/moJLyJOm63jyKrWtBTYzA==
dependencies:
ansicolors "0.3.x"
async "^3.2.0"
Expand Down Expand Up @@ -12143,9 +12143,9 @@ unbox-primitive@^1.0.1:
which-boxed-primitive "^1.0.2"

underscore@^1.9.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881"
integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==
version "1.13.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.3.tgz#54bc95f7648c5557897e5e968d0f76bc062c34ee"
integrity sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==

unherit@^1.0.4:
version "1.1.3"
Expand Down