@webhippie/semantic-release-rubygem 1.0.0 → 1.0.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/lib/common.js ADDED
@@ -0,0 +1 @@
1
+ export default versionRegex = /(VERSION\s*=\s*['"])(?:(?!"|').)*(['"])/;
package/lib/prepare.js ADDED
@@ -0,0 +1,121 @@
1
+ import path from "node:path";
2
+ import { rename, readFile, writeFile } from "node:fs/promises";
3
+
4
+ import { execa } from "execa";
5
+ import { versionRegex } from "./common.js";
6
+
7
+ const writeVersion = async ({ versionFile, nextVersion, logger, cwd }) => {
8
+ const gemVersion = nextVersion.replace("-", ".");
9
+ const fullVersionPath = path.resolve(cwd, versionFile);
10
+ const versionContents = await readFile(fullVersionPath, "utf8");
11
+ const newContents = versionContents.replace(
12
+ versionRegex,
13
+ `$1${gemVersion}$2`,
14
+ );
15
+
16
+ logger.log("Writing version %s to `%s`", nextVersion, versionFile);
17
+
18
+ await writeFile(fullVersionPath, newContents, "utf8");
19
+
20
+ return { gemVersion };
21
+ };
22
+
23
+ const bundleInstall = async ({
24
+ updateGemfileLock,
25
+ cwd,
26
+ env,
27
+ logger,
28
+ stdout,
29
+ stderr,
30
+ }) => {
31
+ const command =
32
+ typeof updateGemfileLock === "string"
33
+ ? updateGemfileLock
34
+ : "bundle install";
35
+
36
+ logger.log("Updating lock file with command `%s`", command);
37
+
38
+ const installResult = execa.command(command, { cwd, env });
39
+
40
+ installResult.stdout.pipe(stdout, { end: false });
41
+
42
+ installResult.stderr.pipe(stderr, { end: false });
43
+
44
+ await installResult;
45
+ };
46
+
47
+ const buildGem = async ({
48
+ gemspec,
49
+ gemName,
50
+ version,
51
+ cwd,
52
+ env,
53
+ logger,
54
+ stdout,
55
+ stderr,
56
+ }) => {
57
+ const gemFile = `${gemName}-${version}.gem`;
58
+
59
+ logger.log("Building gem `%s`", gemFile);
60
+
61
+ const buildResult = execa("gem", ["build", gemspec], { cwd, env });
62
+
63
+ buildResult.stdout.pipe(stdout, { end: false });
64
+
65
+ buildResult.stderr.pipe(stderr, { end: false });
66
+
67
+ await buildResult;
68
+
69
+ return gemFile;
70
+ };
71
+
72
+ export default async function prepare(
73
+ { updateGemfileLock = false, gemFileDir = false },
74
+ { nextRelease: { version }, cwd, env, logger, stdout, stderr },
75
+ { versionFile, gemSpec, gemName },
76
+ ) {
77
+ const { gemVersion } = await writeVersion({
78
+ versionFile,
79
+ nextVersion: version,
80
+ logger,
81
+ cwd,
82
+ });
83
+
84
+ if (updateGemfileLock) {
85
+ await bundleInstall({
86
+ updateGemfileLock,
87
+ cwd,
88
+ env,
89
+ logger,
90
+ stdout,
91
+ stderr,
92
+ });
93
+ }
94
+
95
+ let gemFile = await buildGem({
96
+ gemSpec,
97
+ gemName,
98
+ version: gemVersion,
99
+ cwd,
100
+ env,
101
+ logger,
102
+ stdout,
103
+ stderr,
104
+ });
105
+
106
+ if (gemFileDir) {
107
+ const gemFileSource = path.resolve(cwd, gemFile);
108
+
109
+ const gemFileDestination = path.resolve(cwd, gemFileDir.trim(), gemFile);
110
+
111
+ if (gemFileSource !== gemFileDestination) {
112
+ await rename(gemFileSource, gemFileDestination);
113
+ }
114
+
115
+ gemFile = path.join(gemFileDir.trim(), gemFile);
116
+ }
117
+
118
+ return {
119
+ gemFile,
120
+ };
121
+ }
package/lib/publish.js ADDED
@@ -0,0 +1,36 @@
1
+ import { unlink } from "node:fs/promises";
2
+ import { execa } from "execa";
3
+
4
+ export default async function publish(
5
+ { gemHost, gemPublish = true, gemFileDir = false },
6
+ { cwd, env, logger, nextRelease: { version }, stdout, stderr },
7
+ { gemFile, gemName, credentialsFile },
8
+ ) {
9
+ if (gemPublish !== false) {
10
+ logger.log(`Publishing version ${version} to rubygems`);
11
+
12
+ const args = ["push", gemFile, "--config-file", credentialsFile];
13
+
14
+ if (gemHost) {
15
+ args.push("--host", gemHost);
16
+ }
17
+
18
+ const pushResult = execa("gem", args, { cwd, env });
19
+
20
+ pushResult.stdout.pipe(stdout, { end: false });
21
+
22
+ pushResult.stderr.pipe(stderr, { end: false });
23
+
24
+ await pushResult;
25
+
26
+ logger.log(`Published version ${version} of ${gemName} to rubygems`);
27
+ } else {
28
+ logger.log(
29
+ `Skip publishing to rubygems because gemPublish is ${gemPublish !== false}`,
30
+ );
31
+ }
32
+
33
+ if (gemFileDir === false) {
34
+ await unlink(gemFile);
35
+ }
36
+ }
package/lib/verify.js ADDED
@@ -0,0 +1,142 @@
1
+ import path from "node:path";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+
4
+ import { execa } from "execa";
5
+ import { glob } from "glob";
6
+ import { versionRegex } from "./common.js";
7
+
8
+ import SemanticReleaseError from "@semantic-release/error";
9
+
10
+ const loadGemspec = async ({ cwd }) => {
11
+ const gemSpecs = await glob("*.gemspec", { cwd });
12
+
13
+ if (gemSpecs.length !== 1) {
14
+ throw new SemanticReleaseError(
15
+ "Couldn't find a `.gemspec` file.",
16
+ "ENOGEMSPEC",
17
+ `A single [.gemspec](https://guides.rubygems.org/specification-reference/) file in the root of your project is required to release a Rubygem.
18
+
19
+ Please follow the "[Make your own gem guide](https://guides.rubygems.org/make-your-own-gem/)" to create a valid \`.gemspec\` file
20
+ `,
21
+ );
22
+ }
23
+
24
+ const [gemSpec] = gemSpecs;
25
+
26
+ let gemName = null;
27
+
28
+ try {
29
+ const { stdout } = await execa(
30
+ "ruby",
31
+ ["-e", `puts Gem::Specification.load('${gemSpec}').name`],
32
+ { cwd },
33
+ );
34
+
35
+ gemName = stdout;
36
+ } catch (error) {
37
+ throw new SemanticReleaseError(
38
+ `Error loading \`${gemSpec}\``,
39
+ "EINVALIDGEMSPEC",
40
+ `A valid [.gemspec](https://guides.rubygems.org/specification-reference/) is required to release a Rubygem.
41
+
42
+ Please follow the "[Make your own gem guide](https://guides.rubygems.org/make-your-own-gem/)" to create a valid \`.gemspec\` file
43
+ `,
44
+ );
45
+ }
46
+
47
+ if (gemName === "") {
48
+ throw new SemanticReleaseError(
49
+ `Missing \`name\` attribute in \`${gemSpec}\``,
50
+ "ENOGEMNAME",
51
+ `The [name](https://guides.rubygems.org/specification-reference/#name) attribute is required in your \`.gemspec\` file in order to publish a Rubygem.
52
+
53
+ Please make sure to add a valid \`name\` for your gem in your \`.gemspec\`.
54
+ `,
55
+ );
56
+ }
57
+
58
+ return {
59
+ name: gemName,
60
+ gemSpec,
61
+ };
62
+ };
63
+
64
+ const verifyVersionFile = async ({ cwd, versionGlob }) => {
65
+ const versionFiles = await glob(versionGlob, { cwd });
66
+
67
+ if (versionFiles.length !== 1) {
68
+ throw new SemanticReleaseError(
69
+ "Couldn't find a `version.lib` file.",
70
+ "ENOVERSIONFILE",
71
+ `A \`version.rb\` file in the \`lib/*\` dir of your project is required to release a Ruby gem.
72
+
73
+ Please create a \`version.rb\` file with a defined \`VERSION\` constant in your \`lib\` dir (or subdir).
74
+ `,
75
+ );
76
+ }
77
+
78
+ const [versionFile] = versionFiles;
79
+
80
+ const fullVersionPath = path.resolve(cwd, versionFile);
81
+
82
+ const versionContents = await readFile(fullVersionPath, "utf8");
83
+
84
+ if (!versionRegex.test(versionContents)) {
85
+ throw new SemanticReleaseError(
86
+ `Couldn't find a valid version constant defined in \`${versionFile}\`.`,
87
+ "EINVALIDVERSIONFILE",
88
+ `Your \`version.rb\` file must define a \`VERSION\` constant.
89
+
90
+ Please define your gem's version a string constant named \`VERSION\` inside your \`version.rb\` file.
91
+ `,
92
+ );
93
+ }
94
+
95
+ return versionFile;
96
+ };
97
+
98
+ const verifyApiKey = async ({ env, credentialsFile }) => {
99
+ if (!env.GEM_HOST_API_KEY) {
100
+ throw new SemanticReleaseError(
101
+ "No rubygems API key specified.",
102
+ "ENOGEMAPIKEY",
103
+ `A rubygems API key must be created and set in the \`GEM_HOST_API_KEY\` environment variable on you CI environment.
104
+
105
+ You can retrieve an API key either from your \`~/.gem/credentials\` file or in your profile in [RubyGems.org](http://rubygems.org/).
106
+ `,
107
+ );
108
+ }
109
+
110
+ await writeFile(
111
+ credentialsFile,
112
+ `---\n:rubygems_api_key: ${env.GEM_HOST_API_KEY}`,
113
+ "utf8",
114
+ );
115
+ };
116
+
117
+ export default async function verify(
118
+ { versionGlob = "lib/**/version.rb" },
119
+ { env, cwd },
120
+ { credentialsFile },
121
+ ) {
122
+ const { name, gemSpec } = await loadGemspec({
123
+ cwd,
124
+ });
125
+
126
+ const versionFile = await verifyVersionFile({
127
+ cwd,
128
+ versionGlob,
129
+ });
130
+
131
+ await verifyApiKey({
132
+ env,
133
+ cwd,
134
+ credentialsFile,
135
+ });
136
+
137
+ return {
138
+ gemName: name,
139
+ gemSpec,
140
+ versionFile,
141
+ };
142
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@webhippie/semantic-release-rubygem",
3
3
  "description": "semantic-release plugin to publish a gem to Rubygems",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "type": "module",
6
6
  "author": "Thomas Boerger (https://twitter.com/@tboerger)",
7
7
  "ava": {