release-it 18.0.0-next.2 → 18.0.0-next.4
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/README.md +41 -38
- package/config/release-it.json +3 -0
- package/lib/plugin/GitBase.js +1 -1
- package/lib/plugin/GitRelease.js +4 -1
- package/lib/plugin/github/GitHub.js +40 -7
- package/lib/plugin/gitlab/GitLab.js +89 -43
- package/package.json +25 -24
- package/schema/github.json +8 -0
- package/schema/gitlab.json +13 -1
- package/test/github.js +37 -0
- package/test/gitlab.js +165 -43
- package/test/stub/github.js +35 -5
- package/test/stub/gitlab.js +22 -3
- package/test/tasks.interactive.js +7 -0
- package/test/tasks.js +7 -1
- package/test/util/https-server/client/my-private-root-ca.cert.pem +22 -0
- package/test/util/https-server/client/my-private-root-ca.crt +0 -0
- package/test/util/https-server/gen-cert.sh +65 -0
- package/test/util/https-server/server/cert.pem +20 -0
- package/test/util/https-server/server/fullchain.pem +20 -0
- package/test/util/https-server/server/my-private-root-ca.cert.pem +22 -0
- package/test/util/https-server/server/privkey.pem +28 -0
- package/test/util/https-server/server.js +150 -0
- package/types/config.d.ts +20 -1
package/test/gitlab.js
CHANGED
|
@@ -2,13 +2,17 @@ import fs from 'node:fs';
|
|
|
2
2
|
import test from 'ava';
|
|
3
3
|
import sinon from 'sinon';
|
|
4
4
|
import nock from 'nock';
|
|
5
|
+
import { Agent } from 'undici';
|
|
6
|
+
import Git from '../lib/plugin/git/Git.js';
|
|
5
7
|
import GitLab from '../lib/plugin/gitlab/GitLab.js';
|
|
8
|
+
import { GitlabTestServer } from './util/https-server/server.js';
|
|
6
9
|
import { factory, runTasks } from './util/index.js';
|
|
7
10
|
import {
|
|
8
11
|
interceptUser,
|
|
9
12
|
interceptCollaborator,
|
|
10
13
|
interceptPublish,
|
|
11
14
|
interceptAsset,
|
|
15
|
+
interceptAssetGeneric,
|
|
12
16
|
interceptMilestones
|
|
13
17
|
} from './stub/gitlab.js';
|
|
14
18
|
|
|
@@ -63,6 +67,9 @@ test.serial('should upload assets and release', async t => {
|
|
|
63
67
|
const gitlab = factory(GitLab, { options });
|
|
64
68
|
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
65
69
|
|
|
70
|
+
const git = factory(Git);
|
|
71
|
+
const ref = (await git.getBranchName()) ?? 'HEAD';
|
|
72
|
+
|
|
66
73
|
interceptUser();
|
|
67
74
|
interceptCollaborator();
|
|
68
75
|
interceptMilestones({
|
|
@@ -89,7 +96,9 @@ test.serial('should upload assets and release', async t => {
|
|
|
89
96
|
interceptPublish({
|
|
90
97
|
body: {
|
|
91
98
|
name: 'Release 2.0.1',
|
|
99
|
+
ref,
|
|
92
100
|
tag_name: '2.0.1',
|
|
101
|
+
tag_message: 'Release 2.0.1',
|
|
93
102
|
description: 'Custom notes',
|
|
94
103
|
assets: {
|
|
95
104
|
links: [
|
|
@@ -108,7 +117,58 @@ test.serial('should upload assets and release', async t => {
|
|
|
108
117
|
t.is(gitlab.assets[0].url, `${pushRepo}/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`);
|
|
109
118
|
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
110
119
|
t.true(isReleased);
|
|
111
|
-
t.is(releaseUrl, `${pushRepo}/-/releases`);
|
|
120
|
+
t.is(releaseUrl, `${pushRepo}/-/releases/2.0.1`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test.serial('should upload assets with ID-based URLs too', async t => {
|
|
124
|
+
const host = 'https://gitlab.com';
|
|
125
|
+
const pushRepo = `${host}/user/repo`;
|
|
126
|
+
const options = {
|
|
127
|
+
git: { pushRepo },
|
|
128
|
+
gitlab: {
|
|
129
|
+
tokenRef,
|
|
130
|
+
release: true,
|
|
131
|
+
assets: 'test/resources/file-v${version}.txt',
|
|
132
|
+
useIdsForUrls: true
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const gitlab = factory(GitLab, { options });
|
|
136
|
+
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
137
|
+
|
|
138
|
+
interceptUser();
|
|
139
|
+
interceptCollaborator();
|
|
140
|
+
interceptAsset();
|
|
141
|
+
interceptPublish();
|
|
142
|
+
|
|
143
|
+
await runTasks(gitlab);
|
|
144
|
+
|
|
145
|
+
t.is(gitlab.assets[0].url, `${host}/-/project/1234/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/file-v2.0.1.txt`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test.serial('should upload assets to generic repo', async t => {
|
|
149
|
+
const host = 'https://gitlab.com';
|
|
150
|
+
const pushRepo = `${host}/user/repo`;
|
|
151
|
+
const options = {
|
|
152
|
+
git: { pushRepo },
|
|
153
|
+
gitlab: {
|
|
154
|
+
tokenRef,
|
|
155
|
+
release: true,
|
|
156
|
+
assets: 'test/resources/file-v${version}.txt',
|
|
157
|
+
useGenericPackageRepositoryForAssets: true,
|
|
158
|
+
genericPackageRepositoryName: 'release-it'
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const gitlab = factory(GitLab, { options });
|
|
162
|
+
sinon.stub(gitlab, 'getLatestVersion').resolves('2.0.0');
|
|
163
|
+
|
|
164
|
+
interceptUser();
|
|
165
|
+
interceptCollaborator();
|
|
166
|
+
interceptAssetGeneric();
|
|
167
|
+
interceptPublish();
|
|
168
|
+
|
|
169
|
+
await runTasks(gitlab);
|
|
170
|
+
|
|
171
|
+
t.is(gitlab.assets[0].url, `${host}/api/v4/projects/user%2Frepo/packages/generic/release-it/2.0.1/file-v2.0.1.txt`);
|
|
112
172
|
});
|
|
113
173
|
|
|
114
174
|
test.serial('should throw when release milestone is missing', async t => {
|
|
@@ -180,7 +240,7 @@ test.serial('should release to sub-grouped repo', async t => {
|
|
|
180
240
|
|
|
181
241
|
const { isReleased, releaseUrl } = gitlab.getContext();
|
|
182
242
|
t.true(isReleased);
|
|
183
|
-
t.
|
|
243
|
+
t.regex(releaseUrl, /https:\/\/gitlab.com\/group\/sub-group\/repo\/-\/releases\//);
|
|
184
244
|
});
|
|
185
245
|
|
|
186
246
|
test.serial('should throw for unauthenticated user', async t => {
|
|
@@ -234,7 +294,7 @@ test('should not make requests in dry run', async t => {
|
|
|
234
294
|
t.is(gitlab.log.exec.args[2][0], 'gitlab releases#uploadAssets');
|
|
235
295
|
t.is(gitlab.log.exec.args[3][0], 'gitlab releases#createRelease "R" (1.0.1)');
|
|
236
296
|
t.true(isReleased);
|
|
237
|
-
t.is(releaseUrl, `${pushRepo}/-/releases`);
|
|
297
|
+
t.is(releaseUrl, `${pushRepo}/-/releases/1.0.1`);
|
|
238
298
|
});
|
|
239
299
|
|
|
240
300
|
test('should skip checks', async t => {
|
|
@@ -247,49 +307,111 @@ test('should skip checks', async t => {
|
|
|
247
307
|
t.is(gitlab.log.exec.args.filter(entry => /checkReleaseMilestones/.test(entry[0])).length, 0);
|
|
248
308
|
});
|
|
249
309
|
|
|
250
|
-
test('should
|
|
310
|
+
test.serial('should not create fetch agent', t => {
|
|
311
|
+
const options = { gitlab: {} };
|
|
312
|
+
const gitlab = factory(GitLab, { options });
|
|
313
|
+
|
|
314
|
+
t.deepEqual(gitlab.certificateAuthorityOption, {});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test.serial('should create fetch agent if secure == false', t => {
|
|
318
|
+
const options = { gitlab: { secure: false } };
|
|
319
|
+
const gitlab = factory(GitLab, { options });
|
|
320
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
321
|
+
|
|
322
|
+
t.true(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
323
|
+
|
|
324
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
325
|
+
t.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: false, ca: undefined });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test.serial('should create fetch agent if certificateAuthorityFile', t => {
|
|
251
329
|
const sandbox = sinon.createSandbox();
|
|
252
330
|
sandbox.stub(fs, 'readFileSync').returns('test certificate');
|
|
253
331
|
|
|
254
|
-
{
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const gitlab = factory(GitLab, { options });
|
|
263
|
-
t.deepEqual(gitlab.certificateAuthorityOption, { https: { certificateAuthority: 'test certificate' } });
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
{
|
|
267
|
-
const options = { gitlab: { secure: false } };
|
|
268
|
-
const gitlab = factory(GitLab, { options });
|
|
269
|
-
t.deepEqual(gitlab.certificateAuthorityOption, { https: { rejectUnauthorized: false } });
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
{
|
|
273
|
-
const options = { gitlab: { secure: true } };
|
|
274
|
-
const gitlab = factory(GitLab, { options });
|
|
275
|
-
t.deepEqual(gitlab.certificateAuthorityOption, { https: { rejectUnauthorized: true } });
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
{
|
|
279
|
-
const options = { gitlab: { certificateAuthorityFile: 'cert.crt', secure: true } };
|
|
280
|
-
const gitlab = factory(GitLab, { options });
|
|
281
|
-
t.deepEqual(gitlab.certificateAuthorityOption, {
|
|
282
|
-
https: { certificateAuthority: 'test certificate', rejectUnauthorized: true }
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
{
|
|
287
|
-
const options = { gitlab: { certificateAuthorityFile: 'cert.crt', secure: false } };
|
|
288
|
-
const gitlab = factory(GitLab, { options });
|
|
289
|
-
t.deepEqual(gitlab.certificateAuthorityOption, {
|
|
290
|
-
https: { certificateAuthority: 'test certificate', rejectUnauthorized: false }
|
|
291
|
-
});
|
|
292
|
-
}
|
|
332
|
+
const options = { gitlab: { certificateAuthorityFile: 'cert.crt' } };
|
|
333
|
+
const gitlab = factory(GitLab, { options });
|
|
334
|
+
const { dispatcher } = gitlab.certificateAuthorityOption;
|
|
335
|
+
|
|
336
|
+
t.true(dispatcher instanceof Agent, "Fetch dispatcher should be an instance of undici's Agent class");
|
|
337
|
+
|
|
338
|
+
const kOptions = Object.getOwnPropertySymbols(dispatcher).find(symbol => symbol.description === 'options');
|
|
339
|
+
t.deepEqual(dispatcher[kOptions].connect, { rejectUnauthorized: undefined, ca: 'test certificate' });
|
|
293
340
|
|
|
294
341
|
sandbox.restore();
|
|
295
342
|
});
|
|
343
|
+
|
|
344
|
+
test.serial('should throw for insecure connections to self-hosted instances', async t => {
|
|
345
|
+
const host = 'https://localhost:3000';
|
|
346
|
+
|
|
347
|
+
const options = {
|
|
348
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
349
|
+
gitlab: { host, tokenRef, origin: host }
|
|
350
|
+
};
|
|
351
|
+
const gitlab = factory(GitLab, { options });
|
|
352
|
+
const server = new GitlabTestServer();
|
|
353
|
+
|
|
354
|
+
t.teardown(async () => {
|
|
355
|
+
nock.disableNetConnect();
|
|
356
|
+
await server.stop();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
await server.run();
|
|
360
|
+
nock.enableNetConnect();
|
|
361
|
+
|
|
362
|
+
await t.throwsAsync(gitlab.init(), {
|
|
363
|
+
message: /^Could not authenticate with GitLab using environment variable "GITLAB_TOKEN"/
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test.serial('should succesfully connect to self-hosted instance if insecure connection allowed', async t => {
|
|
368
|
+
const host = 'https://localhost:3000';
|
|
369
|
+
|
|
370
|
+
const options = {
|
|
371
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
372
|
+
gitlab: {
|
|
373
|
+
host,
|
|
374
|
+
tokenRef,
|
|
375
|
+
origin: host,
|
|
376
|
+
secure: false
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const gitlab = factory(GitLab, { options });
|
|
380
|
+
const server = new GitlabTestServer();
|
|
381
|
+
|
|
382
|
+
t.teardown(async () => {
|
|
383
|
+
nock.disableNetConnect();
|
|
384
|
+
await server.stop();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await server.run();
|
|
388
|
+
nock.enableNetConnect();
|
|
389
|
+
|
|
390
|
+
await t.notThrowsAsync(gitlab.init());
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test.serial('should succesfully connect to self-hosted instance with valid CA file', async t => {
|
|
394
|
+
const host = 'https://localhost:3000';
|
|
395
|
+
|
|
396
|
+
const options = {
|
|
397
|
+
git: { pushRepo: `${host}/user/repo` },
|
|
398
|
+
gitlab: {
|
|
399
|
+
host,
|
|
400
|
+
tokenRef,
|
|
401
|
+
origin: host,
|
|
402
|
+
certificateAuthorityFile: 'test/util/https-server/client/my-private-root-ca.cert.pem'
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
const gitlab = factory(GitLab, { options });
|
|
406
|
+
const server = new GitlabTestServer();
|
|
407
|
+
|
|
408
|
+
t.teardown(async () => {
|
|
409
|
+
nock.disableNetConnect();
|
|
410
|
+
await server.stop();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
await server.run();
|
|
414
|
+
nock.enableNetConnect();
|
|
415
|
+
|
|
416
|
+
await t.notThrowsAsync(gitlab.init());
|
|
417
|
+
});
|
package/test/stub/github.js
CHANGED
|
@@ -44,7 +44,16 @@ const interceptCreate = ({
|
|
|
44
44
|
host = 'github.com',
|
|
45
45
|
owner = 'user',
|
|
46
46
|
project = 'repo',
|
|
47
|
-
body: {
|
|
47
|
+
body: {
|
|
48
|
+
tag_name,
|
|
49
|
+
name = '',
|
|
50
|
+
body = null,
|
|
51
|
+
prerelease = false,
|
|
52
|
+
draft = false,
|
|
53
|
+
generate_release_notes = false,
|
|
54
|
+
make_latest = 'true',
|
|
55
|
+
discussion_category_name = undefined
|
|
56
|
+
}
|
|
48
57
|
} = {}) => {
|
|
49
58
|
nock(api)
|
|
50
59
|
.post(`/repos/${owner}/${project}/releases`, {
|
|
@@ -53,7 +62,9 @@ const interceptCreate = ({
|
|
|
53
62
|
body,
|
|
54
63
|
prerelease,
|
|
55
64
|
draft,
|
|
56
|
-
generate_release_notes
|
|
65
|
+
generate_release_notes,
|
|
66
|
+
make_latest,
|
|
67
|
+
discussion_category_name
|
|
57
68
|
})
|
|
58
69
|
.reply(() => {
|
|
59
70
|
const id = 1;
|
|
@@ -66,7 +77,8 @@ const interceptCreate = ({
|
|
|
66
77
|
draft,
|
|
67
78
|
generate_release_notes,
|
|
68
79
|
upload_url: `https://uploads.${host}/repos/${owner}/${project}/releases/${id}/assets{?name,label}`,
|
|
69
|
-
html_url: `https://${host}/${owner}/${project}/releases/tag/${tag_name}
|
|
80
|
+
html_url: `https://${host}/${owner}/${project}/releases/tag/${tag_name}`,
|
|
81
|
+
discussion_url: discussion_category_name ? `https://${host}/${owner}/${project}/discussions/${id}` : undefined
|
|
70
82
|
};
|
|
71
83
|
return [200, responseBody, { location: `${api}/repos/${owner}/${project}/releases/${id}` }];
|
|
72
84
|
});
|
|
@@ -77,10 +89,28 @@ const interceptUpdate = ({
|
|
|
77
89
|
api = 'https://api.github.com',
|
|
78
90
|
owner = 'user',
|
|
79
91
|
project = 'repo',
|
|
80
|
-
body: {
|
|
92
|
+
body: {
|
|
93
|
+
tag_name,
|
|
94
|
+
name = '',
|
|
95
|
+
body = null,
|
|
96
|
+
prerelease = false,
|
|
97
|
+
draft = false,
|
|
98
|
+
generate_release_notes = false,
|
|
99
|
+
make_latest = 'true',
|
|
100
|
+
discussion_category_name = undefined
|
|
101
|
+
}
|
|
81
102
|
} = {}) => {
|
|
82
103
|
nock(api)
|
|
83
|
-
.patch(`/repos/${owner}/${project}/releases/1`, {
|
|
104
|
+
.patch(`/repos/${owner}/${project}/releases/1`, {
|
|
105
|
+
tag_name,
|
|
106
|
+
name,
|
|
107
|
+
body,
|
|
108
|
+
draft,
|
|
109
|
+
prerelease,
|
|
110
|
+
generate_release_notes,
|
|
111
|
+
make_latest,
|
|
112
|
+
discussion_category_name
|
|
113
|
+
})
|
|
84
114
|
.reply(200, {
|
|
85
115
|
id: 1,
|
|
86
116
|
tag_name,
|
package/test/stub/gitlab.js
CHANGED
|
@@ -12,7 +12,13 @@ export let interceptCollaborator = (
|
|
|
12
12
|
.reply(200, { id: userId, username: owner, access_level: 30 });
|
|
13
13
|
|
|
14
14
|
export let interceptPublish = ({ host = 'https://gitlab.com', owner = 'user', project = 'repo', body } = {}, options) =>
|
|
15
|
-
nock(host, options)
|
|
15
|
+
nock(host, options)
|
|
16
|
+
.post(`/api/v4/projects/${owner}%2F${project}/releases`, body)
|
|
17
|
+
.reply(200, {
|
|
18
|
+
_links: {
|
|
19
|
+
self: `https://gitlab.com/${owner}/${project}/-/releases/${body?.tag_name ?? '1.0.0'}`
|
|
20
|
+
}
|
|
21
|
+
});
|
|
16
22
|
|
|
17
23
|
export let interceptMilestones = (
|
|
18
24
|
{ host = 'https://gitlab.com', owner = 'user', project = 'repo', query = {}, milestones = [] } = {},
|
|
@@ -34,11 +40,24 @@ export let interceptAsset = ({ host = 'https://gitlab.com', owner = 'user', proj
|
|
|
34
40
|
nock(host)
|
|
35
41
|
.post(`/api/v4/projects/${owner}%2F${project}/uploads`)
|
|
36
42
|
.query(true)
|
|
37
|
-
.reply(200,
|
|
43
|
+
.reply(200, (_, requestBody) => {
|
|
38
44
|
const [, name] = requestBody.match(/filename="([^"]+)/);
|
|
39
45
|
return {
|
|
40
46
|
alt: name,
|
|
41
47
|
url: `/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/${name}`,
|
|
42
|
-
|
|
48
|
+
full_path: `/-/project/1234/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/${name}`,
|
|
49
|
+
markdown: `[${name}](/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/${name})`,
|
|
50
|
+
_links: {
|
|
51
|
+
self: `https://gitlab.com/${owner}/${project}/-/releases/${name}`
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export let interceptAssetGeneric = ({ host = 'https://gitlab.com', owner = 'user', project = 'repo' } = {}) =>
|
|
57
|
+
nock(host)
|
|
58
|
+
.put(`/api/v4/projects/${owner}%2F${project}/packages/generic/release-it/2.0.1/file-v2.0.1.txt`)
|
|
59
|
+
.reply(200, () => {
|
|
60
|
+
return {
|
|
61
|
+
message: '201 Created'
|
|
43
62
|
};
|
|
44
63
|
});
|
|
@@ -8,10 +8,12 @@ import Spinner from '../lib/spinner.js';
|
|
|
8
8
|
import Prompt from '../lib/prompt.js';
|
|
9
9
|
import Config from '../lib/config.js';
|
|
10
10
|
import runTasks from '../lib/index.js';
|
|
11
|
+
import Git from '../lib/plugin/git/Git.js';
|
|
11
12
|
import { mkTmpDir, gitAdd } from './util/helpers.js';
|
|
12
13
|
import ShellStub from './stub/shell.js';
|
|
13
14
|
import { interceptPublish as interceptGitLabPublish } from './stub/gitlab.js';
|
|
14
15
|
import { interceptCreate as interceptGitHubCreate } from './stub/github.js';
|
|
16
|
+
import { factory } from './util/index.js';
|
|
15
17
|
|
|
16
18
|
const noop = Promise.resolve();
|
|
17
19
|
|
|
@@ -160,6 +162,9 @@ test.serial('should run "after:*:release" plugin hooks', async t => {
|
|
|
160
162
|
sh.exec('git tag 1.0.0');
|
|
161
163
|
const sha = gitAdd('line', 'file', 'More file');
|
|
162
164
|
|
|
165
|
+
const git = factory(Git);
|
|
166
|
+
const ref = (await git.getBranchName()) ?? 'HEAD';
|
|
167
|
+
|
|
163
168
|
interceptGitHubCreate({
|
|
164
169
|
owner,
|
|
165
170
|
project,
|
|
@@ -171,7 +176,9 @@ test.serial('should run "after:*:release" plugin hooks', async t => {
|
|
|
171
176
|
project,
|
|
172
177
|
body: {
|
|
173
178
|
name: 'Release 1.1.0',
|
|
179
|
+
ref,
|
|
174
180
|
tag_name: '1.1.0',
|
|
181
|
+
tag_message: 'Release 1.1.0',
|
|
175
182
|
description: `* More file (${sha})`
|
|
176
183
|
}
|
|
177
184
|
});
|
package/test/tasks.js
CHANGED
|
@@ -8,6 +8,7 @@ import Log from '../lib/log.js';
|
|
|
8
8
|
import Spinner from '../lib/spinner.js';
|
|
9
9
|
import Config from '../lib/config.js';
|
|
10
10
|
import runTasks from '../lib/index.js';
|
|
11
|
+
import Git from '../lib/plugin/git/Git.js';
|
|
11
12
|
import { mkTmpDir, gitAdd, getArgs } from './util/helpers.js';
|
|
12
13
|
import ShellStub from './stub/shell.js';
|
|
13
14
|
import {
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
interceptCreate as interceptGitHubCreate,
|
|
23
24
|
interceptAsset as interceptGitHubAsset
|
|
24
25
|
} from './stub/github.js';
|
|
26
|
+
import { factory } from './util/index.js';
|
|
25
27
|
|
|
26
28
|
const rootDir = new URL('..', import.meta.url);
|
|
27
29
|
|
|
@@ -247,6 +249,8 @@ test.serial('should release all the things (pre-release, github, gitlab)', async
|
|
|
247
249
|
sh.exec('git tag v1.0.0');
|
|
248
250
|
const sha = gitAdd('line', 'file', 'More file');
|
|
249
251
|
sh.exec('git push --follow-tags');
|
|
252
|
+
const git = factory(Git);
|
|
253
|
+
const ref = (await git.getBranchName()) ?? 'HEAD';
|
|
250
254
|
|
|
251
255
|
interceptGitHubAuthentication();
|
|
252
256
|
interceptGitHubCollaborator({ owner, project });
|
|
@@ -270,7 +274,9 @@ test.serial('should release all the things (pre-release, github, gitlab)', async
|
|
|
270
274
|
project,
|
|
271
275
|
body: {
|
|
272
276
|
name: 'Release 1.1.0-alpha.0',
|
|
277
|
+
ref,
|
|
273
278
|
tag_name: 'v1.1.0-alpha.0',
|
|
279
|
+
tag_message: `${owner} ${owner}/${project} ${project}`,
|
|
274
280
|
description: `Notes for ${pkgName}: ${sha}`,
|
|
275
281
|
assets: {
|
|
276
282
|
links: [
|
|
@@ -333,7 +339,7 @@ test.serial('should release all the things (pre-release, github, gitlab)', async
|
|
|
333
339
|
t.true(log.obtrusive.firstCall.args[0].endsWith(`release ${pkgName} (1.0.0...1.1.0-alpha.0)`));
|
|
334
340
|
t.true(log.log.firstCall.args[0].endsWith(`https://www.npmjs.com/package/${pkgName}`));
|
|
335
341
|
t.true(log.log.secondCall.args[0].endsWith(`https://github.com/${owner}/${project}/releases/tag/v1.1.0-alpha.0`));
|
|
336
|
-
t.true(log.log.thirdCall.args[0].endsWith(`${project}/-/releases`));
|
|
342
|
+
t.true(log.log.thirdCall.args[0].endsWith(`${project}/-/releases/v1.1.0-alpha.0`));
|
|
337
343
|
t.regex(log.log.lastCall.args[0], /Done \(in [0-9]+s\.\)/);
|
|
338
344
|
|
|
339
345
|
exec.restore();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDrzCCApegAwIBAgIUUAyKcu86WuJQd1fdlHsXkCl1mYMwDQYJKoZIhvcNAQEL
|
|
3
|
+
BQAwZzELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBVByb3Zv
|
|
4
|
+
MSMwIQYDVQQKDBpBQ01FIFNpZ25pbmcgQXV0aG9yaXR5IEluYzEUMBIGA1UEAwwL
|
|
5
|
+
ZXhhbXBsZS5jb20wHhcNMjQxMjExMTMxMzIyWhcNMjcxMDAxMTMxMzIyWjBnMQsw
|
|
6
|
+
CQYDVQQGEwJVUzENMAsGA1UECAwEVXRhaDEOMAwGA1UEBwwFUHJvdm8xIzAhBgNV
|
|
7
|
+
BAoMGkFDTUUgU2lnbmluZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDDAtleGFtcGxl
|
|
8
|
+
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlp3Kz278oyTDY4
|
|
9
|
+
+1iNv1xMzWCJBWf7YxyYkXACVvZzUqgwxotNwAE/kE131HgpBJnu7HsDdS8nLy80
|
|
10
|
+
mDkFCGmqEuVZMDaMGtcz3HJSklESjxYM1VNgifz2IlNmoaidNehFkCi1rz6cLMR0
|
|
11
|
+
rEZpi98EbVZGl3n0vDD0eEZF5QQn/eDwclt/aKVBVLcQxo6MHhQdaK85a1uNyUU3
|
|
12
|
+
HPUYiyPGVn9oX9PGcOpYFomJoBdg3sUVk32xKCUf15+uqVchdtyGFW/bp5KSBCnB
|
|
13
|
+
QD0TzN+SgBHnVTzIfoi8WY56uuwnYauvT4fRh8SfkV91BzHBtWSbulNTfTUzgWhs
|
|
14
|
+
VvCGw7ECAwEAAaNTMFEwHQYDVR0OBBYEFC4qTshaPoBie1ukhhM0KBi95NzJMB8G
|
|
15
|
+
A1UdIwQYMBaAFC4qTshaPoBie1ukhhM0KBi95NzJMA8GA1UdEwEB/wQFMAMBAf8w
|
|
16
|
+
DQYJKoZIhvcNAQELBQADggEBAB+fRdUHgqwpTWRfF+jQB4Af75HTfp6hgemUjapI
|
|
17
|
+
eZn/OugS75/jfJt9npVsHl/aa63GL/W6kQShoMVOhrYqW52J1TSsLKZR2L7sv0ji
|
|
18
|
+
KYfakv+aLkRKewPoVadsCL8GUmaCByE9mwlhmmZprkjDmA3hWsjEM5lyg7qleJ0k
|
|
19
|
+
V32FVysdhLLnftt2SJB7lyoTujhkNAjJhLT/0Qr8t59v0sViPtL8532jSXqE1GK+
|
|
20
|
+
zncsJDK7v2VEuurz1lPTRY6tPQOJ1Qt8vUzDH/ugcc5JPBEuHhjjrd5K65lxnGNw
|
|
21
|
+
lnPHIS7FJm1OMkuatQXomNuuoWDPyM7fuVyGUUpmlkbpJsg=
|
|
22
|
+
-----END CERTIFICATE-----
|
|
Binary file
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Generate client and server self-signed HTTPS certificates
|
|
4
|
+
# Adapted from:
|
|
5
|
+
# https://stackoverflow.com/questions/19665863/how-do-i-use-a-self-signed-certificate-for-a-https-node-js-server/24749608#24749608
|
|
6
|
+
|
|
7
|
+
FQDN=$1
|
|
8
|
+
|
|
9
|
+
if [ -z "$FQDN" ]; then
|
|
10
|
+
echo -e "\nError: Missing required parameter for domain name.\n"
|
|
11
|
+
echo -e "Usage:\n\t./gen-cert.sh <DOMAIN_NAME>\n"
|
|
12
|
+
exit 1
|
|
13
|
+
fi;
|
|
14
|
+
|
|
15
|
+
# make directories to work from
|
|
16
|
+
mkdir -p server/ client/ all/
|
|
17
|
+
|
|
18
|
+
# Create your very own Root Certificate Authority
|
|
19
|
+
openssl genrsa \
|
|
20
|
+
-out all/my-private-root-ca.privkey.pem \
|
|
21
|
+
2048
|
|
22
|
+
|
|
23
|
+
# Self-sign your Root Certificate Authority
|
|
24
|
+
# Since this is private, the details can be as bogus as you like
|
|
25
|
+
openssl req \
|
|
26
|
+
-x509 \
|
|
27
|
+
-new \
|
|
28
|
+
-nodes \
|
|
29
|
+
-key all/my-private-root-ca.privkey.pem \
|
|
30
|
+
-days 1024 \
|
|
31
|
+
-out all/my-private-root-ca.cert.pem \
|
|
32
|
+
-subj "/C=US/ST=Utah/L=Provo/O=ACME Signing Authority Inc/CN=example.com"
|
|
33
|
+
|
|
34
|
+
# Create a Device Certificate for each domain,
|
|
35
|
+
# such as example.com, *.example.com, awesome.example.com
|
|
36
|
+
# NOTE: You MUST match CN to the domain name or ip address you want to use
|
|
37
|
+
openssl genrsa \
|
|
38
|
+
-out all/privkey.pem \
|
|
39
|
+
2048
|
|
40
|
+
|
|
41
|
+
# Create a request from your Device, which your Root CA will sign
|
|
42
|
+
openssl req -new \
|
|
43
|
+
-key all/privkey.pem \
|
|
44
|
+
-out all/csr.pem \
|
|
45
|
+
-subj "/C=US/ST=Utah/L=Provo/O=ACME Tech Inc/CN=${FQDN}"
|
|
46
|
+
|
|
47
|
+
# Sign the request from Device with your Root CA
|
|
48
|
+
openssl x509 \
|
|
49
|
+
-req -in all/csr.pem \
|
|
50
|
+
-CA all/my-private-root-ca.cert.pem \
|
|
51
|
+
-CAkey all/my-private-root-ca.privkey.pem \
|
|
52
|
+
-CAcreateserial \
|
|
53
|
+
-out all/cert.pem \
|
|
54
|
+
-days 500
|
|
55
|
+
|
|
56
|
+
# Put things in their proper place
|
|
57
|
+
rsync -a all/{privkey,cert}.pem server/
|
|
58
|
+
cat all/cert.pem > server/fullchain.pem # we have no intermediates in this case
|
|
59
|
+
rsync -a all/my-private-root-ca.cert.pem server/
|
|
60
|
+
rsync -a all/my-private-root-ca.cert.pem client/
|
|
61
|
+
|
|
62
|
+
# create DER format crt for iOS Mobile Safari, etc
|
|
63
|
+
openssl x509 -outform der -in all/my-private-root-ca.cert.pem -out client/my-private-root-ca.crt
|
|
64
|
+
|
|
65
|
+
rm -r all
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDRjCCAi4CFGCNNJXfcHchSO1xHnWt5wcENGznMA0GCSqGSIb3DQEBCwUAMGcx
|
|
3
|
+
CzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMQ4wDAYDVQQHDAVQcm92bzEjMCEG
|
|
4
|
+
A1UECgwaQUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxFDASBgNVBAMMC2V4YW1w
|
|
5
|
+
bGUuY29tMB4XDTI0MTIxMTEzMTMyMloXDTI2MDQyNTEzMTMyMlowWDELMAkGA1UE
|
|
6
|
+
BhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBVByb3ZvMRYwFAYDVQQKDA1B
|
|
7
|
+
Q01FIFRlY2ggSW5jMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
|
|
8
|
+
AQUAA4IBDwAwggEKAoIBAQC1+BYy7XcJoIAHqPcU7f74Dp/6N6f/hjLTADMIT1OO
|
|
9
|
+
49W3Rrc6xbJjeLsiTD0Rj9Z/9CSI7Vh/AOLoW7OGMOZkJZNRw8L9lc14ZDB1FAcJ
|
|
10
|
+
ITn8c9d0d0YltbxUOq509wEP6GSxYCqYZlvAjeyYADRW5PnP82n6MRMW9Ve5y50s
|
|
11
|
+
ijeaVk120je5zKvQTd0IR9rXf8K3M8CmnlEVuuf4+uXvg0gUsGvSSkBxPVGeUQHM
|
|
12
|
+
/zRxj/I8WYCewb9Qs2TSJjgnFsfUCF6C3xs/T8p4vJ8PxeLs6GNcQhhtNH0q7Wxa
|
|
13
|
+
IV4cdrXkxPi6YS8ph1WErqNR2pdsCA5bUNTCT32vPjr/AgMBAAEwDQYJKoZIhvcN
|
|
14
|
+
AQELBQADggEBACu1hC1yvFKF6nEjz4mdTmVIyqktfkQX4+5edpSCLM0UhOmQ9Tm3
|
|
15
|
+
3NnQ06YhPnAkiXLiyKxoCGqulgh1B1+3Ii0/ttDq4D4HIEVMwT5Hmko3vppUkJD7
|
|
16
|
+
aIacmTKxGFrtF+p1hIDXmAAYFUB2bWDgVvba2z70DkbffJBOwe/+2+hOgFXI5x3+
|
|
17
|
+
IJuF0bYKRlFG0yvVX+ooWAaKmCSR4reCsjWNuP7KBBJv15GZLKfHPjDUuxW3u43/
|
|
18
|
+
ZmyBM+1Fs91jt0v5w1dIDF0z1pxwbaRJ3w1M1kEU8i6/q+1YaahYvdeaa0jsdSsy
|
|
19
|
+
kpz3YhZtb324TKmjfUFMzYvfcoR8IYDkekQ=
|
|
20
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDRjCCAi4CFGCNNJXfcHchSO1xHnWt5wcENGznMA0GCSqGSIb3DQEBCwUAMGcx
|
|
3
|
+
CzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMQ4wDAYDVQQHDAVQcm92bzEjMCEG
|
|
4
|
+
A1UECgwaQUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxFDASBgNVBAMMC2V4YW1w
|
|
5
|
+
bGUuY29tMB4XDTI0MTIxMTEzMTMyMloXDTI2MDQyNTEzMTMyMlowWDELMAkGA1UE
|
|
6
|
+
BhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBVByb3ZvMRYwFAYDVQQKDA1B
|
|
7
|
+
Q01FIFRlY2ggSW5jMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
|
|
8
|
+
AQUAA4IBDwAwggEKAoIBAQC1+BYy7XcJoIAHqPcU7f74Dp/6N6f/hjLTADMIT1OO
|
|
9
|
+
49W3Rrc6xbJjeLsiTD0Rj9Z/9CSI7Vh/AOLoW7OGMOZkJZNRw8L9lc14ZDB1FAcJ
|
|
10
|
+
ITn8c9d0d0YltbxUOq509wEP6GSxYCqYZlvAjeyYADRW5PnP82n6MRMW9Ve5y50s
|
|
11
|
+
ijeaVk120je5zKvQTd0IR9rXf8K3M8CmnlEVuuf4+uXvg0gUsGvSSkBxPVGeUQHM
|
|
12
|
+
/zRxj/I8WYCewb9Qs2TSJjgnFsfUCF6C3xs/T8p4vJ8PxeLs6GNcQhhtNH0q7Wxa
|
|
13
|
+
IV4cdrXkxPi6YS8ph1WErqNR2pdsCA5bUNTCT32vPjr/AgMBAAEwDQYJKoZIhvcN
|
|
14
|
+
AQELBQADggEBACu1hC1yvFKF6nEjz4mdTmVIyqktfkQX4+5edpSCLM0UhOmQ9Tm3
|
|
15
|
+
3NnQ06YhPnAkiXLiyKxoCGqulgh1B1+3Ii0/ttDq4D4HIEVMwT5Hmko3vppUkJD7
|
|
16
|
+
aIacmTKxGFrtF+p1hIDXmAAYFUB2bWDgVvba2z70DkbffJBOwe/+2+hOgFXI5x3+
|
|
17
|
+
IJuF0bYKRlFG0yvVX+ooWAaKmCSR4reCsjWNuP7KBBJv15GZLKfHPjDUuxW3u43/
|
|
18
|
+
ZmyBM+1Fs91jt0v5w1dIDF0z1pxwbaRJ3w1M1kEU8i6/q+1YaahYvdeaa0jsdSsy
|
|
19
|
+
kpz3YhZtb324TKmjfUFMzYvfcoR8IYDkekQ=
|
|
20
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDrzCCApegAwIBAgIUUAyKcu86WuJQd1fdlHsXkCl1mYMwDQYJKoZIhvcNAQEL
|
|
3
|
+
BQAwZzELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBVByb3Zv
|
|
4
|
+
MSMwIQYDVQQKDBpBQ01FIFNpZ25pbmcgQXV0aG9yaXR5IEluYzEUMBIGA1UEAwwL
|
|
5
|
+
ZXhhbXBsZS5jb20wHhcNMjQxMjExMTMxMzIyWhcNMjcxMDAxMTMxMzIyWjBnMQsw
|
|
6
|
+
CQYDVQQGEwJVUzENMAsGA1UECAwEVXRhaDEOMAwGA1UEBwwFUHJvdm8xIzAhBgNV
|
|
7
|
+
BAoMGkFDTUUgU2lnbmluZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDDAtleGFtcGxl
|
|
8
|
+
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKlp3Kz278oyTDY4
|
|
9
|
+
+1iNv1xMzWCJBWf7YxyYkXACVvZzUqgwxotNwAE/kE131HgpBJnu7HsDdS8nLy80
|
|
10
|
+
mDkFCGmqEuVZMDaMGtcz3HJSklESjxYM1VNgifz2IlNmoaidNehFkCi1rz6cLMR0
|
|
11
|
+
rEZpi98EbVZGl3n0vDD0eEZF5QQn/eDwclt/aKVBVLcQxo6MHhQdaK85a1uNyUU3
|
|
12
|
+
HPUYiyPGVn9oX9PGcOpYFomJoBdg3sUVk32xKCUf15+uqVchdtyGFW/bp5KSBCnB
|
|
13
|
+
QD0TzN+SgBHnVTzIfoi8WY56uuwnYauvT4fRh8SfkV91BzHBtWSbulNTfTUzgWhs
|
|
14
|
+
VvCGw7ECAwEAAaNTMFEwHQYDVR0OBBYEFC4qTshaPoBie1ukhhM0KBi95NzJMB8G
|
|
15
|
+
A1UdIwQYMBaAFC4qTshaPoBie1ukhhM0KBi95NzJMA8GA1UdEwEB/wQFMAMBAf8w
|
|
16
|
+
DQYJKoZIhvcNAQELBQADggEBAB+fRdUHgqwpTWRfF+jQB4Af75HTfp6hgemUjapI
|
|
17
|
+
eZn/OugS75/jfJt9npVsHl/aa63GL/W6kQShoMVOhrYqW52J1TSsLKZR2L7sv0ji
|
|
18
|
+
KYfakv+aLkRKewPoVadsCL8GUmaCByE9mwlhmmZprkjDmA3hWsjEM5lyg7qleJ0k
|
|
19
|
+
V32FVysdhLLnftt2SJB7lyoTujhkNAjJhLT/0Qr8t59v0sViPtL8532jSXqE1GK+
|
|
20
|
+
zncsJDK7v2VEuurz1lPTRY6tPQOJ1Qt8vUzDH/ugcc5JPBEuHhjjrd5K65lxnGNw
|
|
21
|
+
lnPHIS7FJm1OMkuatQXomNuuoWDPyM7fuVyGUUpmlkbpJsg=
|
|
22
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
|
2
|
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1+BYy7XcJoIAH
|
|
3
|
+
qPcU7f74Dp/6N6f/hjLTADMIT1OO49W3Rrc6xbJjeLsiTD0Rj9Z/9CSI7Vh/AOLo
|
|
4
|
+
W7OGMOZkJZNRw8L9lc14ZDB1FAcJITn8c9d0d0YltbxUOq509wEP6GSxYCqYZlvA
|
|
5
|
+
jeyYADRW5PnP82n6MRMW9Ve5y50sijeaVk120je5zKvQTd0IR9rXf8K3M8CmnlEV
|
|
6
|
+
uuf4+uXvg0gUsGvSSkBxPVGeUQHM/zRxj/I8WYCewb9Qs2TSJjgnFsfUCF6C3xs/
|
|
7
|
+
T8p4vJ8PxeLs6GNcQhhtNH0q7WxaIV4cdrXkxPi6YS8ph1WErqNR2pdsCA5bUNTC
|
|
8
|
+
T32vPjr/AgMBAAECggEAHeLCZpfYmpSpIljuR5o063GfdZ1pco6MT1ozh3Rb0VZ6
|
|
9
|
+
9bBgDH+Gpk6gUWg7CWTZwkcLLw/oHme7XJUe/XWPiTggo2em4TYWumSeDsR8yVOT
|
|
10
|
+
LfKqmp6yPyRDa4P9vgkJPB8bVoRoSoJZJF1K08YI0pKlsrEUITqpG3as8z9NL5C1
|
|
11
|
+
65QdpCwiRjLnM6b7k93Vxa8xOnj51iDXV9uqGASCYmU+uoHG8HU8RzbVI4Xi/RCr
|
|
12
|
+
PszmOi6AMtWPpzH2jDPiYnv96K50O/cOm/n3obuySdH0qeGR37sx94F82iCyiowo
|
|
13
|
+
Ij+iU8VYK6gu27z811ehSlk3/4ey8ArlHvVew3TkqQKBgQD+K70baVqolcGfkDQY
|
|
14
|
+
zgZ+4YlmncDrdhd0anHUEC15l7uPPHFBxrQlzRVTjhHCKn1sQTj/41isUWQAvcRR
|
|
15
|
+
ZzjI+YaHQvvzJCSX6c113IWeemCcuTxenW6G/5BHJxOPddFnrcAAoeHhcNvni/CZ
|
|
16
|
+
WrInTIcWn7eY5MMaxa4ELUoKRwKBgQC3R1R9be3BY36wPYcPkTZFxRAn2o7/Z2Ft
|
|
17
|
+
MDbrddDZt12F7cuD8YnipI7TkJLQTXmg6P7u2XexcEyAUy2J+jdgfpM0N/HXu3Su
|
|
18
|
+
YeYXHJfyGUK6bTlQT+trNuEyX5K7h2IbBstvH9A4PXEZPQ2I3aPZk3WsL630cMo6
|
|
19
|
+
tbTy0o3tiQKBgQCv11qxSCXsVA7scTtZnc9ooGgKkkERpVV8uNefOsH7STn9UneY
|
|
20
|
+
Zfvj2wpSEAvBJNw4tLbWcVa7gGOLD75uAteKUvb7RSBBilO2tY3raHEYvtlwE8bs
|
|
21
|
+
PkZlJxGN6D7kFUKWU+JtjZFUAlxgyLPfpJt0DMG4qS6/nCROtUw6n4qFqQKBgB2S
|
|
22
|
+
JrGuIORI91HcO4Rpe4Y6S2cCvnu65F9HnjTTZ4UZLr/DJEj/ma21u02rT+TH+03Z
|
|
23
|
+
CfjjoYpBgjZaNUjD1Fd/VKTiOeUC28qfBQ7JkEKBjOCjatHocyVzT1ZfUT9skoml
|
|
24
|
+
yQD+8wt/7lWSIjLo/9zFDAFiGAEOibJ7Sty62CdxAoGBAJLwVkDCT3yylDRqm2YC
|
|
25
|
+
1uYh/xGLW4F/Jj7PjGNrSYClkbxPTpd4Nfa8EyZmUUya1rsfK1njXZKbwwpa2fbE
|
|
26
|
+
+QVpXB18vaMW3hU7u63JDpLv2pCm91myPMQsF3xMc/S1vFAOWmXetYWax4/xiG2M
|
|
27
|
+
SW4m9mBkzzF/2SpxQd9xy1Jx
|
|
28
|
+
-----END PRIVATE KEY-----
|