wikiploy 1.1.0 → 1.3.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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "hbenl.vscode-test-explorer",
4
+ "hbenl.vscode-mocha-test-adapter"
5
+ ]
6
+ }
@@ -7,20 +7,20 @@
7
7
  {
8
8
  "type": "node",
9
9
  "request": "launch",
10
- "name": "Run current JS",
10
+ "name": "Deploy test.js",
11
11
  "skipFiles": [
12
12
  "<node_internals>/**"
13
13
  ],
14
- "program": "${workspaceFolder}\\${file}"
14
+ "program": "${workspaceFolder}\\src\\Wiki_bot_test.js"
15
15
  },
16
16
  {
17
17
  "type": "node",
18
18
  "request": "launch",
19
- "name": "Deploy test.js",
19
+ "name": "Run current JS",
20
20
  "skipFiles": [
21
21
  "<node_internals>/**"
22
22
  ],
23
- "program": "${workspaceFolder}\\src\\Wiki_bot_test.js"
23
+ "program": "${workspaceFolder}\\${file}"
24
24
  }
25
25
  ]
26
26
  }
package/README.md CHANGED
@@ -10,13 +10,56 @@ 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/). You just open Chrome 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
+ 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
14
 
15
- ## Early release notice
15
+ 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
16
 
17
- **Note!** While the program should be working already, I still don't think it's ready for general use.
17
+ ## Basic script and dst
18
+ ```js
19
+ import {DeployConfig, Wikiploy} from 'wikiploy';
20
+
21
+ const ployBot = new Wikiploy();
22
+
23
+ // custom summary
24
+ ployBot.summary = () => {
25
+ return 'v2.7.0: adding new test mode';
26
+ }
27
+
28
+ // run asynchronusly to be able to wait for results
29
+ (async () => {
30
+ const configs = [];
31
+ configs.push(new DeployConfig({
32
+ src: 'test.js',
33
+ dst: '~/test-wikiploy--test.js',
34
+ }));
35
+ await ployBot.deploy(configs);
36
+ })().catch(err => {
37
+ console.error(err);
38
+ process.exit(1);
39
+ });
40
+ ```
41
+
42
+ Note that `~` will be `User:SomeName` so the user space of currenlty logged in user.
43
+ You can omit `dst` and it will default to `dst: '~/${src}'`.
44
+
45
+ More about this basic code and `dst` in the [Wikiploy rollout example](https://github.com/Eccenux/wikiploy-rollout-example/).
46
+
47
+ ## Different wiki sites
48
+ Wikiploy defaults to deployments on `pl.wikipedia.org`.
49
+
50
+ You can change the default like so:
51
+ ```js
52
+ ployBot.site = "en.wikipedia.org";
53
+ ```
54
+
55
+ You can also set a site for an indvidual `DeployConfig` like so:
56
+ ```js
57
+ configs.push(new DeployConfig({
58
+ src: 'test.js',
59
+ site: "de.wikipedia.org",
60
+ }));
61
+ ```
18
62
 
19
- I will definitely add more features and handle more use cases.
20
63
 
21
64
  ## External links
22
65
  * [Wikiploy rollout example (repo)](https://github.com/Eccenux/wikiploy-rollout-example/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiploy",
3
- "version": "1.1.0",
3
+ "version": "1.3.1",
4
4
  "description": "User scripts and gadgets deployment for MediaWiki (Wikipedia).",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "chai": "^4.3.7",
32
- "mocha": "^10.2.0",
32
+ "mocha": "^10.2.0",
33
33
  "eslint": "^8.43.0"
34
34
  }
35
35
  }
@@ -6,14 +6,52 @@ export default class DeployConfig {
6
6
  * @param {DeployConfig} page Edit page (tab).
7
7
  */
8
8
  constructor(options) {
9
+ /** Source page. */
9
10
  this.src = options?.src;
11
+ /** Destination page. */
10
12
  this.dst = options?.dst;
11
13
  if (!this.dst) {
12
14
  this.dst = `~/${this.src}`;
13
15
  }
16
+ if (this.dst.indexOf('~')===0) {
17
+ this._dst = this.dst;
18
+ }
19
+ /** Wiki site (domain). */
20
+ this.site = options?.site;
21
+ if (!this.site || typeof this.site != 'string') {
22
+ this.site = '';
23
+ }
24
+ /**
25
+ * Custom summary (per file).
26
+ *
27
+ * This can either be a string or a function.
28
+ *
29
+ * Note that you can also set a global summary (for the whole wikiploy).
30
+ * Empty summary will fallback to global summary rules.
31
+ */
32
+ this.summary = options?.summary;
33
+ if (typeof this.summary === 'string' && this.summary.length) {
34
+ this.summary = () => options.summary;
35
+ }
36
+ if (typeof this.summary != 'function') {
37
+ this.summary = false;
38
+ }
39
+ }
40
+
41
+ /** Does this config require a user. */
42
+ needsUser() {
43
+ return this._dst ? true : false;
44
+ }
45
+ /** Setup user. */
46
+ setUser(userName) {
47
+ if (this._dst) {
48
+ this.dst = this._dst.replace(/^~/, 'User:' + userName);
49
+ return true;
50
+ }
51
+ return false;
14
52
  }
15
53
 
16
- /** info */
54
+ /** info. */
17
55
  info() {
18
56
  return `deploy "${this.src}" to "${this.dst}"`;
19
57
  }
package/src/WikiOps.js CHANGED
@@ -99,11 +99,27 @@ export default class WikiOps {
99
99
  }
100
100
 
101
101
  /** Go to url (and wait for it). */
102
- async goto(page, url) {
102
+ async goto(page, url, skipDisarm) {
103
103
  let nav = page.waitForNavigation(); // init wait
104
104
  await page.goto(url);
105
105
  await nav; // wait for url
106
- await this.disarmUnloadWarning(page);
106
+ if (!skipDisarm) {
107
+ await this.disarmUnloadWarning(page);
108
+ }
109
+ }
110
+
111
+ /** Read current MW user. */
112
+ async readUser(page) {
113
+ let user = await page.evaluate(() => {
114
+ return mw?.config?.get('wgUserName', '--');
115
+ });
116
+ if (!user) {
117
+ console.warn('problem reading user, too early?');
118
+ }
119
+ if (user === '--') {
120
+ user = false;
121
+ }
122
+ return user;
107
123
  }
108
124
 
109
125
  /** Change intput's value. */
@@ -10,12 +10,22 @@ const ployBot = new Wikiploy();
10
10
  const configs = [];
11
11
  configs.push(new DeployConfig({
12
12
  src: 'assets/test.js',
13
- dst: 'User:Nux/test-jsbot--test.js',
13
+ dst: '~/test-jsbot--test.js',
14
14
  }));
15
15
  configs.push(new DeployConfig({
16
16
  src: 'assets/test.css',
17
- dst: 'User:Nux/test-jsbot--test.css',
17
+ dst: '~/test-jsbot--test.css',
18
18
  }));
19
+ // configs.push(new DeployConfig({
20
+ // src: 'assets/test.js',
21
+ // dst: '~/test-jsbot--test.js',
22
+ // site: 'en.wikipedia.org',
23
+ // }));
24
+ // configs.push(new DeployConfig({
25
+ // src: 'assets/test.css',
26
+ // dst: '~/test-jsbot--test.css',
27
+ // site: 'en.wikipedia.org',
28
+ // }));
19
29
  await ployBot.deploy(configs);
20
30
  })().catch(err => {
21
31
  console.error(err);
package/src/Wikiploy.js CHANGED
@@ -2,6 +2,8 @@ import puppeteer, { Browser } from 'puppeteer'; // v13+
2
2
 
3
3
  import WikiOps from './WikiOps.js';
4
4
  import PageCache from './PageCache.js';
5
+ // eslint-disable-next-line no-unused-vars
6
+ import DeployConfig from './DeployConfig.js';
5
7
 
6
8
  import { promises as fs } from "fs"; // node v11+
7
9
 
@@ -26,12 +28,17 @@ export default class Wikiploy {
26
28
  this.mock = false;
27
29
  /** Wait before close [ms] (or you can set a breakpoint to check stuff). */
28
30
  this.mockSleep = 0;
31
+ /** Default wiki site (domain). */
32
+ this.site = 'pl.wikipedia.org';
29
33
 
30
- /** Browser connection. */
34
+ /** @private Browser connection. */
31
35
  this._browser = false;
32
36
 
33
- /** Bot helper. */
37
+ /** @private Bot helper. */
34
38
  this._bot = new WikiOps(this.cache);
39
+
40
+ /** @private Users cache. */
41
+ this._users = {};
35
42
  }
36
43
 
37
44
  /**
@@ -42,7 +49,7 @@ export default class Wikiploy {
42
49
  const bot = this._bot;
43
50
  const browser = await this.init();
44
51
  const page = await bot.openTab(browser);
45
- console.log(JSON.stringify(configs));
52
+ // console.log(JSON.stringify(configs));
46
53
  // main loop
47
54
  for (const config of configs) {
48
55
  await this.save(config, page);
@@ -60,14 +67,18 @@ export default class Wikiploy {
60
67
  async save(config, page) {
61
68
  console.log('[Wikiploy]', config.info());
62
69
  const bot = this._bot;
70
+ // prepare user
71
+ if (config.needsUser()) {
72
+ await this.prepareUser(config, page);
73
+ }
63
74
  // navigate
64
- let url = this.editUrl(config.dst);
75
+ let url = this.editUrl(config.dst, config);
65
76
  await bot.goto(page, url);
66
77
  // insert the content of the file into the edit field
67
78
  const contents = await fs.readFile(config.src, 'utf8');
68
79
  await bot.fillEdit(page, contents);
69
80
  // edit description
70
- const summary = this.preapreSummary(config);
81
+ const summary = this.prepareSummary(config);
71
82
  await bot.fillSummary(page, summary);
72
83
 
73
84
  // save
@@ -79,27 +90,92 @@ export default class Wikiploy {
79
90
  }
80
91
  }
81
92
 
93
+ /**
94
+ * Prepare user in config.
95
+ *
96
+ * Note! Modifies the config.
97
+ *
98
+ * @param {DeployConfig} config Config.
99
+ * @param {Page} page
100
+ * @private
101
+ */
102
+ async prepareUser(config, page) {
103
+ const bot = this._bot;
104
+
105
+ let site = config.site;
106
+ // from cache
107
+ if (site in this._users) {
108
+ let changed = config.setUser(this._users[site]);
109
+ return changed;
110
+ }
111
+
112
+ // any page
113
+ let url = this.liteUrl(config);
114
+ await bot.goto(page, url, true);
115
+
116
+ // read
117
+ let userName = await bot.readUser(page);
118
+ if (!userName) {
119
+ throw 'Unable to read user name. Not authenticated?';
120
+ }
121
+
122
+ // save to cache
123
+ this._users[site] = userName;
124
+
125
+ // finalize
126
+ let changed = config.setUser(userName);
127
+ return changed;
128
+ }
82
129
 
83
130
  /**
84
131
  * Prepare edit summary.
85
132
  *
86
- * @param {String} pageTitle Title with namespace.
87
- * @returns {String} Full edit URL.
133
+ * @param {DeployConfig} config Config.
134
+ * @returns {String} Edit summary.
88
135
  * @private
89
136
  */
90
- preapreSummary(config) {
91
- return '[Wikiploy]' + ` ${config.src}`;
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
+ /**
156
+ * Prepare base URL.
157
+ *
158
+ * @param {DeployConfig} config Configuration.
159
+ * @returns {String} Base URL.
160
+ * @private
161
+ */
162
+ baseUrl(config) {
163
+ const site = config.site.length ? config.site : this.site;
164
+ const origin = `https://${site}`;
165
+ const baseUrl = `${origin}/w/index.php`;
166
+ return baseUrl;
92
167
  }
93
168
 
94
169
  /**
95
170
  * Prepare edit URL.
96
171
  *
97
172
  * @param {String} pageTitle Title with namespace.
173
+ * @param {DeployConfig} config Configuration.
98
174
  * @returns {String} Full edit URL.
99
175
  * @private
100
176
  */
101
- editUrl(pageTitle) {
102
- const baseUrl = `https://pl.wikipedia.org/w/index.php`; // TODO: config/options
177
+ editUrl(pageTitle, config) {
178
+ const baseUrl = this.baseUrl(config);
103
179
 
104
180
  // common params
105
181
  // note that submit action is not affected by new wikicode editor
@@ -111,6 +187,25 @@ export default class Wikiploy {
111
187
  return baseUrl + '?title=' + encodeURIComponent(pageTitle) + params;
112
188
  }
113
189
 
190
+ /**
191
+ * Prepare URL of some lite page.
192
+ *
193
+ * @param {DeployConfig} config Configuration.
194
+ * @returns {String} URL within Wiki.
195
+ * @private
196
+ */
197
+ liteUrl(config) {
198
+ const baseUrl = this.baseUrl(config);
199
+
200
+ // common params
201
+ // note that submit action is not affected by new wikicode editor
202
+ let params = `
203
+ &useskin=monobook
204
+ `.replace(/\s+/g, '');
205
+
206
+ return baseUrl + '?title=User:Nux/blank.js' + params;
207
+ }
208
+
114
209
  /**
115
210
  * Init browser connection.
116
211
  *
@@ -0,0 +1,118 @@
1
+ /* global describe, it */
2
+ import { assert } from 'chai';
3
+ import DeployConfig from '../src/DeployConfig.js';
4
+
5
+ describe('DeployConfig', function () {
6
+
7
+ describe('init', function () {
8
+ it('should set src, dst', function () {
9
+ let config = {
10
+ src: 'test.src.js',
11
+ dst: 'User:Nux/test.dst.js',
12
+ };
13
+ let result = new DeployConfig(config);
14
+ console.log(result);
15
+ assert.equal(result.src, config.src);
16
+ assert.equal(result.dst, config.dst);
17
+ // console.log(new DeployConfig({
18
+ // src: 'test.js',
19
+ // }));
20
+ });
21
+ it('should default to home', function () {
22
+ let config = {
23
+ src: 'test.js',
24
+ };
25
+ let expected = '~/test.js';
26
+ let result = new DeployConfig(config);
27
+ console.log(result);
28
+ assert.equal(result.dst, expected);
29
+ });
30
+ });
31
+
32
+ describe('setUser', function () {
33
+ it('should set userName', function () {
34
+ let config = {
35
+ src: 'test.js',
36
+ };
37
+ let userName = 'Tester';
38
+ let expected = 'User:Tester/test.js';
39
+ let result = new DeployConfig(config);
40
+ result.setUser(userName);
41
+ console.log(result);
42
+ assert.equal(result.dst, expected);
43
+
44
+
45
+ result = new DeployConfig({
46
+ src: 'assets/test.js',
47
+ dst: '~/test-jsbot--test.js',
48
+ });
49
+ result.setUser(userName);
50
+ console.log(result);
51
+ assert.isTrue(result.dst.indexOf('~') < 0);
52
+ });
53
+ it('should change user', function () {
54
+ let config = {
55
+ src: 'test.js',
56
+ };
57
+ let userName1 = 'Tester1';
58
+ let userName2 = 'Tester2';
59
+ let expected = 'User:Tester2/test.js';
60
+ let result = new DeployConfig(config);
61
+ result.setUser(userName1);
62
+ console.log(result);
63
+ result.setUser(userName2);
64
+ console.log(result);
65
+ assert.equal(result.dst, expected);
66
+ });
67
+ it('should keep inner tilde', function () {
68
+ let config = {
69
+ src: 'test.js',
70
+ dst: 'Mediawiki/~test.js',
71
+ };
72
+ let userName = 'Tester';
73
+ let result = new DeployConfig(config);
74
+ result.setUser(userName);
75
+ assert.equal(result.dst, config.dst);
76
+ });
77
+ });
78
+
79
+ describe('summary', function () {
80
+ it('should use string', function () {
81
+ let expected = 'v1.1';
82
+ let config = {
83
+ src: 'test.js',
84
+ summary: expected,
85
+ };
86
+ let result = new DeployConfig(config);
87
+ console.log(result);
88
+ assert.equal(result.summary(), expected);
89
+ });
90
+ it('should use function', function () {
91
+ let expected = 'v1.1: test.js';
92
+ let config = {
93
+ src: 'test.js',
94
+ summary: function() { return `v1.1: ${this.src}`; },
95
+ };
96
+ let result = new DeployConfig(config);
97
+ console.log(result);
98
+ assert.equal(result.summary(), expected);
99
+ });
100
+ it('should default to false', function () {
101
+ let config = {
102
+ src: 'test.js',
103
+ };
104
+ let result = new DeployConfig(config);
105
+ console.log(result);
106
+ assert.equal(result.summary, false);
107
+ });
108
+ it('empty should be ignored', function () {
109
+ let config = {
110
+ src: 'test.js',
111
+ summary: '',
112
+ };
113
+ let result = new DeployConfig(config);
114
+ console.log(result);
115
+ assert.equal(result.summary, false);
116
+ });
117
+ });
118
+ });
@@ -0,0 +1,48 @@
1
+ /* global describe, it */
2
+ import { assert } from 'chai';
3
+ import Wikiploy from '../src/Wikiploy.js';
4
+ import DeployConfig from '../src/DeployConfig.js';
5
+
6
+ describe('Wikiploy', function () {
7
+
8
+ describe('summary', function () {
9
+ let tag = '#Wikiploy';
10
+ function prepareSummary(ployBot, configDef) {
11
+ const config = new DeployConfig(configDef);
12
+ console.log({config, summary:config.summary?config.summary():'NN'});
13
+ let summary = ployBot.prepareSummary(config);
14
+ return summary;
15
+ }
16
+
17
+ it('should use config string', function () {
18
+ let version = 'v1.1';
19
+ let expected = tag + ' ' + version;
20
+ const ployBot = new Wikiploy();
21
+ const summary = prepareSummary(ployBot, {
22
+ src: 'test.js',
23
+ summary: version,
24
+ });
25
+ assert.equal(summary, expected);
26
+ });
27
+ it('should use config function', function () {
28
+ let version = 'v1.2';
29
+ let expected = tag + ' ' + version;
30
+ const ployBot = new Wikiploy();
31
+ const summary = prepareSummary(ployBot, {
32
+ src: 'test.js',
33
+ summary: () => version,
34
+ });
35
+ assert.equal(summary, expected);
36
+ });
37
+ it('should use global function', function () {
38
+ let version = 'v1.3';
39
+ let expected = tag + ' ' + version;
40
+ const ployBot = new Wikiploy();
41
+ ployBot.summary = () => version;
42
+ const summary = prepareSummary(ployBot, {
43
+ src: 'test.js',
44
+ });
45
+ assert.equal(summary, expected);
46
+ });
47
+ });
48
+ });
@@ -1,31 +0,0 @@
1
- /* global describe, it */
2
- import { assert } from 'chai';
3
- import DeployConfig from '../DeployConfig.js';
4
-
5
- describe('DeployConfig', function () {
6
-
7
- describe('init', function () {
8
- it('should set src, dst', function () {
9
- let config = {
10
- src: 'test.src.js',
11
- dst: 'User:Nux/test.dst.js',
12
- };
13
- let result = new DeployConfig(config);
14
- console.log(result);
15
- assert.equal(result.src, config.src);
16
- assert.equal(result.dst, config.dst);
17
- // console.log(new DeployConfig({
18
- // src: 'test.js',
19
- // }));
20
- });
21
- it('should default to home', function () {
22
- let config = {
23
- src: 'test.js',
24
- };
25
- let expected = '~/test.js';
26
- let result = new DeployConfig(config);
27
- console.log(result);
28
- assert.equal(result.dst, expected);
29
- });
30
- });
31
- });