wikiploy 1.0.1 → 1.2.2
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.
- package/.vscode/launch.json +4 -4
- package/README.md +14 -1
- package/package.json +2 -2
- package/src/DeployConfig.js +44 -0
- package/{WikiOps.js → src/WikiOps.js} +18 -2
- package/src/Wiki_bot_test.js +33 -0
- package/{Wikiploy.js → src/Wikiploy.js} +92 -11
- package/src/index.js +7 -0
- package/test/DeployConfig.test.js +48 -1
- package/DeployConfig.js +0 -21
- package/Wiki_bot_test.js +0 -23
- /package/{test.css → assets/test.css} +0 -0
- /package/{test.js → assets/test.js} +0 -0
- /package/{PageCache.js → src/PageCache.js} +0 -0
- /package/{chrome.config.js → src/chrome.config.js} +0 -0
- /package/{chromeBase.js → src/chromeBase.js} +0 -0
package/.vscode/launch.json
CHANGED
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
{
|
|
8
8
|
"type": "node",
|
|
9
9
|
"request": "launch",
|
|
10
|
-
"name": "
|
|
10
|
+
"name": "Deploy test.js",
|
|
11
11
|
"skipFiles": [
|
|
12
12
|
"<node_internals>/**"
|
|
13
13
|
],
|
|
14
|
-
"program": "${workspaceFolder}
|
|
14
|
+
"program": "${workspaceFolder}\\src\\Wiki_bot_test.js"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"type": "node",
|
|
18
18
|
"request": "launch",
|
|
19
|
-
"name": "
|
|
19
|
+
"name": "Run current JS",
|
|
20
20
|
"skipFiles": [
|
|
21
21
|
"<node_internals>/**"
|
|
22
22
|
],
|
|
23
|
-
"program": "${workspaceFolder}
|
|
23
|
+
"program": "${workspaceFolder}\\${file}"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|
package/README.md
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
Wikiploy
|
|
2
2
|
==========================
|
|
3
3
|
|
|
4
|
+
## Polski opis
|
|
5
|
+
|
|
6
|
+
[Wikipedia:Wikiploy](https://pl.wikipedia.org/wiki/Wikipedia:Wikiploy)
|
|
7
|
+
|
|
8
|
+
## English description
|
|
9
|
+
|
|
4
10
|
User scripts and gadgets deployment for wikis (Wikipedia or more generally MediaWiki based wiki).
|
|
5
11
|
Rollout your JS, CSS etc from your git repository to as many MW wikis as you need.
|
|
6
12
|
|
|
7
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.
|
|
8
14
|
|
|
15
|
+
## Early release notice
|
|
16
|
+
|
|
9
17
|
**Note!** While the program should be working already, I still don't think it's ready for general use.
|
|
10
18
|
|
|
11
|
-
I will definitely add more features and handle more use cases.
|
|
19
|
+
I will definitely add more features and handle more use cases.
|
|
20
|
+
|
|
21
|
+
## External links
|
|
22
|
+
* [Wikiploy rollout example (repo)](https://github.com/Eccenux/wikiploy-rollout-example/)
|
|
23
|
+
* [Wikiploy on npm](https://www.npmjs.com/package/wikiploy)
|
|
24
|
+
* [Wikiploy on pl.wiki](https://pl.wikipedia.org/wiki/Wikipedia:Wikiploy)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wikiploy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "User scripts and gadgets deployment for MediaWiki (Wikipedia).",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment metadata.
|
|
3
|
+
*/
|
|
4
|
+
export default class DeployConfig {
|
|
5
|
+
/**
|
|
6
|
+
* @param {DeployConfig} page Edit page (tab).
|
|
7
|
+
*/
|
|
8
|
+
constructor(options) {
|
|
9
|
+
/** Source page. */
|
|
10
|
+
this.src = options?.src;
|
|
11
|
+
/** Destination page. */
|
|
12
|
+
this.dst = options?.dst;
|
|
13
|
+
if (!this.dst) {
|
|
14
|
+
this.dst = `~/${this.src}`;
|
|
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
|
+
|
|
26
|
+
/** Does this config require a user. */
|
|
27
|
+
needsUser() {
|
|
28
|
+
return this._dst ? true : false;
|
|
29
|
+
}
|
|
30
|
+
/** Setup user. */
|
|
31
|
+
setUser(userName) {
|
|
32
|
+
if (this._dst) {
|
|
33
|
+
this.dst = this._dst.replace(/^~/, 'User:' + userName);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** info. */
|
|
40
|
+
info() {
|
|
41
|
+
return `deploy "${this.src}" to "${this.dst}"`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -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
|
-
|
|
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. */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import DeployConfig from './DeployConfig.js';
|
|
2
|
+
import Wikiploy from './Wikiploy.js';
|
|
3
|
+
|
|
4
|
+
const ployBot = new Wikiploy();
|
|
5
|
+
// mock
|
|
6
|
+
// ployBot.mock = true;
|
|
7
|
+
// ployBot.mockSleep = 5_000;
|
|
8
|
+
|
|
9
|
+
(async () => {
|
|
10
|
+
const configs = [];
|
|
11
|
+
configs.push(new DeployConfig({
|
|
12
|
+
src: 'assets/test.js',
|
|
13
|
+
dst: '~/test-jsbot--test.js',
|
|
14
|
+
}));
|
|
15
|
+
configs.push(new DeployConfig({
|
|
16
|
+
src: 'assets/test.css',
|
|
17
|
+
dst: '~/test-jsbot--test.css',
|
|
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
|
+
// }));
|
|
29
|
+
await ployBot.deploy(configs);
|
|
30
|
+
})().catch(err => {
|
|
31
|
+
console.error(err);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
@@ -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.
|
|
81
|
+
const summary = this.prepareSummary(config);
|
|
71
82
|
await bot.fillSummary(page, summary);
|
|
72
83
|
|
|
73
84
|
// save
|
|
@@ -79,27 +90,78 @@ 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 {
|
|
87
|
-
* @returns {String}
|
|
133
|
+
* @param {DeployConfig} config Config.
|
|
134
|
+
* @returns {String} Edit summary.
|
|
135
|
+
* @private
|
|
136
|
+
*/
|
|
137
|
+
prepareSummary(config) {
|
|
138
|
+
return '#Wikiploy' + ` ${config.src}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Prepare base URL.
|
|
143
|
+
*
|
|
144
|
+
* @param {DeployConfig} config Configuration.
|
|
145
|
+
* @returns {String} Base URL.
|
|
88
146
|
* @private
|
|
89
147
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
148
|
+
baseUrl(config) {
|
|
149
|
+
const site = config.site.length ? config.site : this.site;
|
|
150
|
+
const origin = `https://${site}`;
|
|
151
|
+
const baseUrl = `${origin}/w/index.php`;
|
|
152
|
+
return baseUrl;
|
|
92
153
|
}
|
|
93
154
|
|
|
94
155
|
/**
|
|
95
156
|
* Prepare edit URL.
|
|
96
157
|
*
|
|
97
158
|
* @param {String} pageTitle Title with namespace.
|
|
159
|
+
* @param {DeployConfig} config Configuration.
|
|
98
160
|
* @returns {String} Full edit URL.
|
|
99
161
|
* @private
|
|
100
162
|
*/
|
|
101
|
-
editUrl(pageTitle) {
|
|
102
|
-
const baseUrl =
|
|
163
|
+
editUrl(pageTitle, config) {
|
|
164
|
+
const baseUrl = this.baseUrl(config);
|
|
103
165
|
|
|
104
166
|
// common params
|
|
105
167
|
// note that submit action is not affected by new wikicode editor
|
|
@@ -111,6 +173,25 @@ export default class Wikiploy {
|
|
|
111
173
|
return baseUrl + '?title=' + encodeURIComponent(pageTitle) + params;
|
|
112
174
|
}
|
|
113
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Prepare URL of some lite page.
|
|
178
|
+
*
|
|
179
|
+
* @param {DeployConfig} config Configuration.
|
|
180
|
+
* @returns {String} URL within Wiki.
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
liteUrl(config) {
|
|
184
|
+
const baseUrl = this.baseUrl(config);
|
|
185
|
+
|
|
186
|
+
// common params
|
|
187
|
+
// note that submit action is not affected by new wikicode editor
|
|
188
|
+
let params = `
|
|
189
|
+
&useskin=monobook
|
|
190
|
+
`.replace(/\s+/g, '');
|
|
191
|
+
|
|
192
|
+
return baseUrl + '?title=User:Nux/blank.js' + params;
|
|
193
|
+
}
|
|
194
|
+
|
|
114
195
|
/**
|
|
115
196
|
* Init browser connection.
|
|
116
197
|
*
|
package/src/index.js
ADDED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* global describe, it */
|
|
2
2
|
import { assert } from 'chai';
|
|
3
|
-
import DeployConfig from '../DeployConfig.js';
|
|
3
|
+
import DeployConfig from '../src/DeployConfig.js';
|
|
4
4
|
|
|
5
5
|
describe('DeployConfig', function () {
|
|
6
6
|
|
|
@@ -28,4 +28,51 @@ describe('DeployConfig', function () {
|
|
|
28
28
|
assert.equal(result.dst, expected);
|
|
29
29
|
});
|
|
30
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
|
+
});
|
|
31
78
|
});
|
package/DeployConfig.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deployment metadata.
|
|
3
|
-
*/
|
|
4
|
-
export default class DeployConfig {
|
|
5
|
-
/**
|
|
6
|
-
* @param {DeployConfig} page Edit page (tab).
|
|
7
|
-
*/
|
|
8
|
-
constructor(options) {
|
|
9
|
-
this.src = options?.src;
|
|
10
|
-
this.dst = options?.dst;
|
|
11
|
-
if (!this.dst) {
|
|
12
|
-
this.dst = `~/${this.src}`;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** info */
|
|
17
|
-
info() {
|
|
18
|
-
return `deploy "${this.src}" to "${this.dst}"`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
}
|
package/Wiki_bot_test.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import DeployConfig from './DeployConfig.js';
|
|
2
|
-
import Wikiploy from './Wikiploy.js';
|
|
3
|
-
|
|
4
|
-
const ployBot = new Wikiploy();
|
|
5
|
-
// mock
|
|
6
|
-
// ployBot.mock = true;
|
|
7
|
-
// ployBot.mockSleep = 5_000;
|
|
8
|
-
|
|
9
|
-
(async () => {
|
|
10
|
-
const configs = [];
|
|
11
|
-
configs.push(new DeployConfig({
|
|
12
|
-
src: 'test.js',
|
|
13
|
-
dst: 'User:Nux/test-jsbot--test.js',
|
|
14
|
-
}));
|
|
15
|
-
configs.push(new DeployConfig({
|
|
16
|
-
src: 'test.css',
|
|
17
|
-
dst: 'User:Nux/test-jsbot--test.css',
|
|
18
|
-
}));
|
|
19
|
-
await ployBot.deploy(configs);
|
|
20
|
-
})().catch(err => {
|
|
21
|
-
console.error(err);
|
|
22
|
-
process.exit(1);
|
|
23
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|