release-it 18.0.0-next.3 → 18.0.0

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/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.is(releaseUrl, 'https://gitlab.com/group/sub-group/repo/-/releases');
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 handle certificate authority options', t => {
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
- const options = { gitlab: {} };
256
- const gitlab = factory(GitLab, { options });
257
- t.deepEqual(gitlab.certificateAuthorityOption, {});
258
- }
259
-
260
- {
261
- const options = { gitlab: { certificateAuthorityFile: 'cert.crt' } };
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
+ });
@@ -44,7 +44,16 @@ const interceptCreate = ({
44
44
  host = 'github.com',
45
45
  owner = 'user',
46
46
  project = 'repo',
47
- body: { tag_name, name = '', generate_release_notes = false, body = null, prerelease = false, draft = false }
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`, {
@@ -54,7 +63,8 @@ const interceptCreate = ({
54
63
  prerelease,
55
64
  draft,
56
65
  generate_release_notes,
57
- make_latest: 'true'
66
+ make_latest,
67
+ discussion_category_name
58
68
  })
59
69
  .reply(() => {
60
70
  const id = 1;
@@ -67,7 +77,8 @@ const interceptCreate = ({
67
77
  draft,
68
78
  generate_release_notes,
69
79
  upload_url: `https://uploads.${host}/repos/${owner}/${project}/releases/${id}/assets{?name,label}`,
70
- 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
71
82
  };
72
83
  return [200, responseBody, { location: `${api}/repos/${owner}/${project}/releases/${id}` }];
73
84
  });
@@ -78,7 +89,16 @@ const interceptUpdate = ({
78
89
  api = 'https://api.github.com',
79
90
  owner = 'user',
80
91
  project = 'repo',
81
- body: { tag_name, name = '', body = null, prerelease = false, draft = false, generate_release_notes = false }
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
+ }
82
102
  } = {}) => {
83
103
  nock(api)
84
104
  .patch(`/repos/${owner}/${project}/releases/1`, {
@@ -88,7 +108,8 @@ const interceptUpdate = ({
88
108
  draft,
89
109
  prerelease,
90
110
  generate_release_notes,
91
- make_latest: 'true'
111
+ make_latest,
112
+ discussion_category_name
92
113
  })
93
114
  .reply(200, {
94
115
  id: 1,
@@ -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).post(`/api/v4/projects/${owner}%2F${project}/releases`, body).reply(200, {});
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, function (_, requestBody) {
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
- markdown: `[${name}](/uploads/7e8bec1fe27cc46a4bc6a91b9e82a07c/${name})`
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-----
@@ -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-----