wikiploy 1.3.4 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,20 +7,20 @@
7
7
  {
8
8
  "type": "node",
9
9
  "request": "launch",
10
- "name": "Deploy test.js",
10
+ "name": "Run current JS",
11
11
  "skipFiles": [
12
12
  "<node_internals>/**"
13
13
  ],
14
- "program": "${workspaceFolder}\\src\\Wiki_bot_test.js"
14
+ "program": "${workspaceFolder}\\${file}"
15
15
  },
16
16
  {
17
17
  "type": "node",
18
18
  "request": "launch",
19
- "name": "Run current JS",
19
+ "name": "ploy-lite test.js",
20
20
  "skipFiles": [
21
21
  "<node_internals>/**"
22
22
  ],
23
- "program": "${workspaceFolder}\\${file}"
24
- }
23
+ "program": "${workspaceFolder}\\src\\ploy_test_lite.js"
24
+ },
25
25
  ]
26
26
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "search.exclude": {
3
+ "package-lock.json": true,
4
+ },
5
+ "editor.detectIndentation": false,
6
+ "editor.useTabStops": true,
7
+ "editor.insertSpaces": false,
8
+ }
package/DEV.md CHANGED
@@ -16,7 +16,7 @@ Run first `npm i`.
16
16
  You might want to run `npm up` to update some scripts too.
17
17
 
18
18
  Recomended global modules/tools:
19
- ```
19
+ ```bash
20
20
  npm install -g eslint
21
21
  npm install -g mocha
22
22
  ```
@@ -36,13 +36,14 @@ You can also run (and debug) each test case directly from a test file. You might
36
36
  ## Publishing
37
37
 
38
38
  Step. 1. Check.
39
- ```
39
+ ```bash
40
+ npm up
40
41
  npm test
41
42
  ```
42
43
  Step. 2. Change version in the package.
43
44
 
44
45
  Step. 3. Final commands.
45
- ```
46
+ ```bash
46
47
  npm up
47
48
  npm test
48
49
  npm publish
package/README.md CHANGED
@@ -10,23 +10,54 @@ Wikiploy
10
10
  User scripts and gadgets deployment for wikis (Wikipedia or more generally MediaWiki based wiki).
11
11
  Rollout your JS, CSS etc from your git repository to as many MW wikis as you need.
12
12
 
13
- This is using [Puppeteer](https://pptr.dev/) to control [Chrome Canary](https://www.google.com/chrome/canary/) or similar. You just open Chrome with remote debug enabled and run a script. The idea is that you are logged in in Chrome and so all edits are still your edits. You can keep the Canary running in the background when you are changing and deploying more stuff.
13
+ ### Wikiploy full
14
+
15
+ This is using [Puppeteer](https://pptr.dev/) to control [Chrome Canary](https://www.google.com/chrome/canary/) or similar. You just open Chrome with remote debug enabled and run a script. The idea is that you are logged in in Chrome and so all edits are still your edits. You can keep the Canary running in the background when you are changing and deploying more stuff.
14
16
 
15
17
  Wikiploy will also work with other Chromium based browser. [Instructions for enabling remote debug in MS Edge](https://learn.microsoft.com/en-us/microsoft-edge/devtools-protocol-chromium/).
16
18
  Note that to completely close Edge you might need use settings: Continue running in background when Microsoft Edge is closed (pl. *Kontynuuj działanie aplikacji i rozszerzeń w tle po zamknięciu przeglądarki Microsoft Edge*).
17
19
 
20
+ ### WikiployLite
21
+
22
+ The `WikiployLite` class is using a bot API to deploy scripts. You can use standard `Wikiploy` and `WikiployLite` interchangeably. The `DeployConfig` is the same. WikiployLite is recommended as long as you can use it.
23
+
24
+ **WikiployLite** is less memory heavy as it doesn't use a browser (and doesn't use `Puppeteer`). You do need to setup a bot password though (on [[Special:BotPasswords]]). It's not as hard as it might seem as you can do this on any Wikimedia wiki and it will work for all WMF wikis. You don't need a bot account for this to work.
25
+
26
+ Botpass configuration:
27
+ * Setup on e.g.: https://test.wikipedia.org/wiki/Special:BotPasswords
28
+ * Rights you should setup (if you can): `assets\Bot passwords - Test Wikipedia.png`.
29
+ * Example config file in: `assets\bot.config.public.js`.
30
+
31
+ **Warning!** Never, ever publish your bot password. If you do spill your password, reset/remove the password ASAP (on Special:BotPasswords).
32
+
33
+ Example `.gitignore` for your project:
34
+ ```
35
+ /node_modules
36
+ *.lnk
37
+ *.priv.*
38
+ bot.config.js
39
+ ```
40
+
18
41
  ## Basic script and dst
19
42
  ```js
20
- import {DeployConfig, Wikiploy} from 'wikiploy';
43
+ import {DeployConfig, Wikiploy, WikiployLite, verlib} from 'wikiploy';
44
+
45
+ // full
46
+ //const ployBot = new Wikiploy();
47
+ // lite
48
+ import * as botpass from './bot.config.js';
49
+ const ployBot = new WikiployLite(botpass);
21
50
 
22
- const ployBot = new Wikiploy();
51
+ // extra if you want to read your version from the package.json
52
+ const version = await verlib.readVersion('./package.json');
23
53
 
24
54
  // custom summary
25
55
  ployBot.summary = () => {
26
- return 'v2.7.0: adding new test mode';
56
+ //return 'v2.7.0: adding new test mode';
57
+ return `v${version}: adding new test mode`;
27
58
  }
28
59
 
29
- // run asynchronusly to be able to wait for results
60
+ // run asynchronously to be able to wait for results
30
61
  (async () => {
31
62
  const configs = [];
32
63
  configs.push(new DeployConfig({
@@ -40,7 +71,7 @@ ployBot.summary = () => {
40
71
  });
41
72
  ```
42
73
 
43
- Note that `~` will be `User:SomeName` so the user space of currenlty logged in user.
74
+ Note that `~` will be `User:SomeName` so the user space of a currently logged in user (you user space).
44
75
  You can omit `dst` and it will default to `dst: '~/${src}'`.
45
76
 
46
77
  More about this basic code and `dst` in the [Wikiploy rollout example](https://github.com/Eccenux/wikiploy-rollout-example/).
@@ -65,4 +96,5 @@ configs.push(new DeployConfig({
65
96
  ## External links
66
97
  * [Wikiploy rollout example (repo)](https://github.com/Eccenux/wikiploy-rollout-example/)
67
98
  * [Wikiploy on npm](https://www.npmjs.com/package/wikiploy)
99
+ * [Wikiploy on en.wiki](https://en.wikipedia.org/wiki/Wikipedia:Wikiploy)
68
100
  * [Wikiploy on pl.wiki](https://pl.wikipedia.org/wiki/Wikipedia:Wikiploy)
@@ -0,0 +1,13 @@
1
+ /**
2
+ Bot with edit&create rights.
3
+
4
+ You can create the bot account on any wiki. E.g. on the test wiki:
5
+ https://test.wikipedia.org/wiki/Special:BotPasswords
6
+
7
+ The username will be something like `MyName@Wikiploy` where `Wikiploy` would be a bot name
8
+ (you can choose any bot name but "Wikiploy" would be a good choice to separate it from other things).
9
+
10
+ The password will be something like `12345abcdefpqrst123456abcdef`.
11
+ */
12
+ export const username = '...@...';
13
+ export const password = '...';
package/assets/test.css CHANGED
@@ -5,4 +5,4 @@
5
5
  .something {
6
6
  display: block;
7
7
  }
8
- /* puppeteer 20.8.x */
8
+ /* puppeteer 20.8.x xor mwn 1.11.x */
package/assets/test.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // test.js
2
2
  console.log('test');
3
- // puppeteer 20.8.x
3
+ // puppeteer 20.8.x xor mwn 1.11.x
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiploy",
3
- "version": "1.3.4",
3
+ "version": "1.4.1",
4
4
  "description": "User scripts and gadgets deployment for MediaWiki (Wikipedia).",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -25,11 +25,12 @@
25
25
  },
26
26
  "homepage": "https://github.com/Eccenux/Wikiploy#readme",
27
27
  "dependencies": {
28
+ "mwn": "1.11.x",
28
29
  "puppeteer": "20.8.x"
29
30
  },
30
31
  "devDependencies": {
31
32
  "chai": "4.x",
32
- "mocha": "10.x",
33
- "eslint": "8.x"
33
+ "eslint": "8.x",
34
+ "mocha": "10.x"
34
35
  }
35
36
  }
@@ -53,7 +53,7 @@ export default class DeployConfig {
53
53
 
54
54
  /** info. */
55
55
  info() {
56
- return `deploy "${this.src}" to "${this.dst}"`;
56
+ return `deploy "${this.src}" to "${this.dst}"` + (!this.site.length ? '' : ` (to ${this.site})`);
57
57
  }
58
58
 
59
59
  }
package/src/Wikiploy.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import puppeteer, { Browser } from 'puppeteer'; // v13+
2
2
 
3
+ import WikiployBase from './WikiployBase .js';
3
4
  import WikiOps from './WikiOps.js';
4
5
  import PageCache from './PageCache.js';
5
6
  // eslint-disable-next-line no-unused-vars
@@ -18,18 +19,18 @@ function sleep(sleepMs) {
18
19
 
19
20
  /**
20
21
  * MediaWiki deployment automation.
22
+ * Deploy scripts with a browser (puppetter).
21
23
  *
22
- * @property _browser {Browser} The user's email
24
+ * @property _browser {Browser} Browser connection.
23
25
  */
24
- export default class Wikiploy {
26
+ export default class Wikiploy extends WikiployBase {
25
27
  constructor() {
28
+ super();
29
+
26
30
  this.cache = new PageCache();
27
- /** Disable save. */
28
- this.mock = false;
31
+
29
32
  /** Wait before close [ms] (or you can set a breakpoint to check stuff). */
30
33
  this.mockSleep = 0;
31
- /** Default wiki site (domain). */
32
- this.site = 'pl.wikipedia.org';
33
34
 
34
35
  /** @private Browser connection. */
35
36
  this._browser = false;
@@ -42,10 +43,12 @@ export default class Wikiploy {
42
43
  }
43
44
 
44
45
  /**
45
- * Deploy scripts.
46
+ * Deploy scripts with a browser (puppetter).
46
47
  * @param {DeployConfig[]} configs
47
48
  */
48
49
  async deploy(configs) {
50
+ console.log('[Wikiploy] deploy %d configs (default site: %s)...\n', configs.length, this.site);
51
+
49
52
  const bot = this._bot;
50
53
  const browser = await this.init();
51
54
  const page = await bot.openTab(browser);
@@ -102,7 +105,7 @@ export default class Wikiploy {
102
105
  async prepareUser(config, page) {
103
106
  const bot = this._bot;
104
107
 
105
- let site = config.site;
108
+ const site = this.getSite(config);
106
109
  // from cache
107
110
  if (site in this._users) {
108
111
  let changed = config.setUser(this._users[site]);
@@ -127,31 +130,6 @@ export default class Wikiploy {
127
130
  return changed;
128
131
  }
129
132
 
130
- /**
131
- * Prepare edit summary.
132
- *
133
- * @param {DeployConfig} config Config.
134
- * @returns {String} Edit summary.
135
- * @private
136
- */
137
- prepareSummary(config) {
138
- let summary = typeof config.summary === 'function' ? config.summary() : this.summary(config);
139
- return `#Wikiploy ${summary}`;
140
- }
141
-
142
- /**
143
- * Custom summary.
144
- *
145
- * This can be e.g. version number and short, text summary.
146
- * You can use config to add e.g. file name too (which is default).
147
- *
148
- * @param {DeployConfig} config Deployment configuration object.
149
- * @returns {String} Summary added to saved edits.
150
- */
151
- summary(config) {
152
- return config.src;
153
- }
154
-
155
133
  /**
156
134
  * Prepare base URL.
157
135
  *
@@ -160,7 +138,7 @@ export default class Wikiploy {
160
138
  * @private
161
139
  */
162
140
  baseUrl(config) {
163
- const site = config.site.length ? config.site : this.site;
141
+ const site = this.getSite(config);
164
142
  const origin = `https://${site}`;
165
143
  const baseUrl = `${origin}/w/index.php`;
166
144
  return baseUrl;
@@ -0,0 +1,54 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import DeployConfig from './DeployConfig.js';
3
+
4
+ /**
5
+ * Generic Wikiploy.
6
+ */
7
+ export default class WikiployBase {
8
+ constructor() {
9
+ /** Disable save. */
10
+ this.mock = false;
11
+ /** Default wiki site (domain). */
12
+ this.site = 'pl.wikipedia.org';
13
+ }
14
+
15
+ /**
16
+ * Deploy many...
17
+ * @param {DeployConfig[]} configs
18
+ */
19
+ async deploy(configs) {
20
+ console.log(`done %d`, configs.length);
21
+ throw 'Not implemented';
22
+ }
23
+
24
+ /**
25
+ * Prepare edit summary.
26
+ *
27
+ * @param {DeployConfig} config Config.
28
+ * @returns {String} Edit summary.
29
+ * @private
30
+ */
31
+ prepareSummary(config) {
32
+ let summary = typeof config.summary === 'function' ? config.summary() : this.summary(config);
33
+ return `#Wikiploy ${summary}`;
34
+ }
35
+
36
+ /** @private Get site for a config. */
37
+ getSite(config) {
38
+ let site = config.site.length ? config.site : this.site;
39
+ return site;
40
+ }
41
+
42
+ /**
43
+ * Custom summary.
44
+ *
45
+ * This can be e.g. version number and short, text summary.
46
+ * You can use config to add e.g. file name too (which is default).
47
+ *
48
+ * @param {DeployConfig} config Deployment configuration object.
49
+ * @returns {String} Summary added to saved edits.
50
+ */
51
+ summary(config) {
52
+ return config.src;
53
+ }
54
+ }
@@ -0,0 +1,112 @@
1
+ import { mwn as Mwn } from 'mwn';
2
+
3
+ import WikiployBase from './WikiployBase .js';
4
+ // eslint-disable-next-line no-unused-vars
5
+ import DeployConfig from './DeployConfig.js';
6
+
7
+ import { promises as fs } from "fs"; // node v11+
8
+
9
+ import * as verlib from './version.js';
10
+ const version = await verlib.readVersion('./package.json');
11
+
12
+ /**
13
+ * MediaWiki deployment automation.
14
+ *
15
+ * Deploy scripts with the Bot API.
16
+ * https://test.wikipedia.org/wiki/Special:BotPasswords
17
+ */
18
+ export default class WikiployLite extends WikiployBase {
19
+ constructor(botpass) {
20
+ super();
21
+
22
+ /** [[Special:BotPasswords]] data. */
23
+ this.botpass = {
24
+ username: botpass.username,
25
+ password: botpass.password,
26
+ };
27
+
28
+ /** @private Bots cache. */
29
+ this._bots = {};
30
+ }
31
+
32
+ /**
33
+ * Deploy with bot password.
34
+ * @param {DeployConfig[]} configs
35
+ */
36
+ async deploy(configs) {
37
+ console.log('[Wikiploy] deploy %d configs (default site: %s)...\n', configs.length, this.site);
38
+ // main loop
39
+ for (const config of configs) {
40
+ await this.save(config);
41
+ }
42
+ console.log(`done`);
43
+ }
44
+
45
+ /**
46
+ * Init/get bot for given config.
47
+ * @private
48
+ * @param {DeployConfig} config Config.
49
+ */
50
+ async getBot(config) {
51
+ const site = this.getSite(config);
52
+ // from cache
53
+ if (site in this._bots) {
54
+ return this._bots[site];
55
+ }
56
+ const apiUrl = `https://${site}/w/api.php`;
57
+ const bot = await Mwn.init({
58
+ apiUrl: apiUrl,
59
+ username: this.botpass.username,
60
+ password: this.botpass.password,
61
+ // UA required for WMF wikis: https://meta.wikimedia.org/wiki/User-Agent_policy
62
+ userAgent: `Wikiploy ${version} ([[:en:Wikipedia:Wikiploy|Wikiploy]])`,
63
+ });
64
+ this._bots[site] = bot;
65
+ return bot;
66
+ }
67
+
68
+ /**
69
+ * Deploy script.
70
+ * @param {DeployConfig} config Config.
71
+ */
72
+ async save(config) {
73
+ console.log('[Wikiploy]', config.info());
74
+ const bot = await this.getBot(config);
75
+ // prepare user
76
+ if (config.needsUser()) {
77
+ await this.prepareUser(config);
78
+ }
79
+ // page
80
+ const pageTitle = config.dst;
81
+ // content of the file
82
+ const contents = await fs.readFile(config.src, 'utf8');
83
+ // edit description
84
+ const summary = this.prepareSummary(config);
85
+
86
+ // save
87
+ if (!this.mock) {
88
+ await bot.save(pageTitle, contents, summary);
89
+ console.log('saved');
90
+ } else {
91
+ console.warn('mock on "%s", inserting %d bytes, summary: %s', pageTitle, contents.length, summary);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Prepare user in config.
97
+ *
98
+ * Note! Modifies the config.
99
+ *
100
+ * @param {DeployConfig} config Config.
101
+ * @private
102
+ */
103
+ async prepareUser(config) {
104
+ // read
105
+ let userName = this.botpass.username.replace(/@.+/, '');
106
+
107
+ // finalize
108
+ let changed = config.setUser(userName);
109
+ return changed;
110
+ }
111
+
112
+ }
package/src/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import DeployConfig from './DeployConfig.js';
2
2
  import Wikiploy from './Wikiploy.js';
3
+ import WikiployLite from './WikiployLite.js';
4
+ import * as verlib from './version.js';
3
5
 
4
6
  export {
5
7
  DeployConfig,
8
+ verlib,
9
+ WikiployLite,
6
10
  Wikiploy
7
11
  };
@@ -0,0 +1,40 @@
1
+ // import DeployConfig from './DeployConfig.js';
2
+ // import Wikiploy from './Wikiploy.js';
3
+
4
+ import { mwn as Mwn } from 'mwn';
5
+
6
+ import * as ver from './version.js';
7
+
8
+ // https://test.wikipedia.org/wiki/Special:BotPasswords
9
+ // Note that username should contain `@`.
10
+ import * as botpass from './bot.config.js';
11
+
12
+ const version = await ver.readVersion('./package.json');
13
+
14
+
15
+ console.log('Test:', version);
16
+
17
+ // auth
18
+ const bot = await Mwn.init({
19
+ apiUrl: 'https://pl.wikipedia.org/w/api.php',
20
+ username: botpass.username,
21
+ password: botpass.password,
22
+ // UA required for WMF wikis: https://meta.wikimedia.org/wiki/User-Agent_policy
23
+ userAgent: `Wikiploy ${version} ([[:en:Wikipedia:Wikiploy|Wikiploy]])`,
24
+ });
25
+
26
+ /**
27
+ * Mwn info:
28
+ * https://mwn.toolforge.org/docs/editing-pages
29
+ * (note that docs seem a bit outdated)
30
+ *
31
+ * bot.save() tests:
32
+ * <li>Works for editing and creating pages.
33
+ * <li>Ignores empty edit.
34
+ */
35
+ const pageTitle = 'Wikipedysta:Nux/test-mwnapi--test.js';
36
+ const content = 'yadda(xy);';
37
+ const summary = `Test ([[:en:Wikipedia:Wikiploy|Wikiploy]] ${version})`;
38
+ await bot.save(pageTitle, content, summary);
39
+
40
+ console.log('Done:', version);
@@ -0,0 +1,41 @@
1
+ import DeployConfig from './DeployConfig.js';
2
+ import WikiployLite from './WikiployLite.js';
3
+
4
+ // https://test.wikipedia.org/wiki/Special:BotPasswords
5
+ // Note that username should contain `@`.
6
+ import * as botpass from './bot.config.js';
7
+
8
+ const ployBot = new WikiployLite(botpass);
9
+ // mock
10
+ // ployBot.mock = true;
11
+ // ployBot.mockSleep = 5_000;
12
+
13
+ (async () => {
14
+ const configs = [];
15
+ configs.push(new DeployConfig({
16
+ src: 'assets/test.js',
17
+ dst: '~/test-wikiploylite--test.js',
18
+ }));
19
+ configs.push(new DeployConfig({
20
+ src: 'assets/test.js',
21
+ dst: '~/test-wikiploylite--test.js',
22
+ site: 'en.wikipedia.org',
23
+ }));
24
+ configs.push(new DeployConfig({
25
+ src: 'assets/test.css',
26
+ dst: '~/test-wikiploylite--test.css',
27
+ }));
28
+ configs.push(new DeployConfig({
29
+ src: 'assets/test.css',
30
+ dst: '~/test-wikiploylite--test.css',
31
+ site: 'en.wikipedia.org',
32
+ }));
33
+ await ployBot.deploy(configs);
34
+
35
+ // check bots
36
+ let bots = Object.keys(ployBot._bots);
37
+ console.log('Bots: %d', bots.length, bots);
38
+ })().catch(err => {
39
+ console.error(err);
40
+ process.exit(1);
41
+ });
package/src/version.js ADDED
@@ -0,0 +1,12 @@
1
+ import fsa from 'fs/promises'
2
+
3
+ /** Read version from JSON file (like package.json). */
4
+ export async function readVersion (config) {
5
+ const data = JSON.parse(await fsa.readFile(config, 'utf8'));
6
+ return data.version;
7
+ }
8
+
9
+ /** Replace version placeholders. */
10
+ export function applyVersion (content, version) {
11
+ return content.replace(/\{version\}/g, version);
12
+ }
File without changes