• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / lwdita / 5f107eca-a10a-43bd-b89c-ebb52af88d2c

06 Nov 2024 09:31PM UTC coverage: 87.964% (-3.5%) from 91.429%
5f107eca-a10a-43bd-b89c-ebb52af88d2c

Pull #262

circleci

adamretter
Address code review from @plutonik-a
Pull Request #262: [feature] assure that each external command exits with success code

603 of 723 branches covered (83.4%)

Branch coverage included in aggregate %.

6 of 92 new or added lines in 1 file covered. (6.52%)

4 existing lines in 2 files now uncovered.

1553 of 1728 relevant lines covered (89.87%)

61.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

8.44
/plugin-release.js
1
module.exports = {
1✔
2
    name: `plugin-addition`,
3
    factory: require => {
4
      const {BaseCommand, WorkspaceRequiredError} = require("@yarnpkg/cli");
2✔
5
      const {Configuration, Project} = require("@yarnpkg/core");
2✔
6
      const {execute} = require("@yarnpkg/shell");
2✔
7
      const {Command, Option} = require("clipanion");
2✔
8
      const {access, copyFile, rm} = require("fs/promises");
2✔
9
      const t = require("typanion");
2✔
10

11
      const semver2Regex = "(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:\\-([1-9A-Za-z-][0-9A-Za-z-]*(?:\\.[1-9A-Za-z-][0-9A-Za-z-]*)*))?(?:\\+([1-9A-Za-z-][0-9A-Za-z-]*(?:\\.[1-9A-Za-z-][0-9A-Za-z-]*)*))?";
2✔
12
      const yarnAutoVersionsRegex = "(major|minor|patch)";
2✔
13
  
14
      class ReleaseCommand extends BaseCommand {
15
        static paths = [[`release`]];
2✔
16
  
17
        // Show descriptive usage for a --help argument passed to this command
18
        static usage = Command.Usage({
2✔
19
          description: `Publish a new release`,
20
          details: `
21
            This command will create a new release for the project, which involves the following steps:
22
            1. Check project status - Requires no unstaged changes, and no un-pushed commits.
23
            2. Pre-release testing - Executes the \`lint\` and \`test\` scripts.
24
            3. Update the version numbers of all packages.
25
            4. git commit the version update.
26
            5. git tag and sign the release tag.
27
            6. git push the updates - Version commit and tag.
28
            7. Publish the packages to npm.js.
29
          `,
30
          examples: [
31
            [
32
                `Release the next major version`,
33
                `yarn release major`,
34
            ],
35
            [
36
                `Release the next version as 2.1.0`,
37
                `yarn release 2.1.0`,
38
            ]
39
        ],
40
        });
41
  
42
        version = Option.String({
1✔
43
            validator: t.matchesRegExp(new RegExp("^" + yarnAutoVersionsRegex + "|(?:" + semver2Regex + ")$"))
44
        });
45
        // b = Option.String({validator: t.isNumber()});
46
  
47
        async execute() {
48
          const configuration = await Configuration.find(this.context.cwd, this.context.plugins);
×
49
          const {project, workspace} = await Project.find(configuration, this.context.cwd);
×
50

51
          if (!workspace) {
×
52
            throw new WorkspaceRequiredError(project.cwd, this.context.cwd);
×
53
          }
54

55
          // process exit code for the last command
NEW
56
          let exitCode = 0;
×
57

UNCOV
58
          this.context.stdout.write(`Preparing to release version: ${this.version}...\n`);
×
59

NEW
60
          const executeOptions = {"cwd": project.cwd};
×
61

62
          // Step 1 - Check project status - requires no unstaged changes, and no un-pushed commits
63
          // TODO(AR) figure out how to capture stdout from the git commands below and check the content
64
          this.context.stdout.write("1. Checking project status...\n");
×
65
          this.context.stdout.write("1.1. Checking for git 'main' branch...\n");
×
66
          //   await execute('git', ['branch', '--show-current'], executeOptions);
67
          this.context.stdout.write("1.2. Checking for unstaged changes...\n");
×
68
          //   await execute('git', ['status', '--porcelain', '--untracked-files=no'], executeOptions);
69
          this.context.stdout.write("1.3. Checking for un-pushed commits...\n");
×
70
          //   await execute('git', ['log', 'origin/main..HEAD'], [], executeOptions);
71

72
          // Step 2 - Pre-release testing - Executes the `lint` and `test` scripts
73
          this.context.stdout.write("2. Performing pre-release testing...\n");
×
NEW
74
          exitCode = await this.cli.run(["clean"]);
×
NEW
75
          if (exitCode !== 0) {
×
NEW
76
            this.context.stderr.write(`Error: 'yarn clean' failed with code: ${exitCode}\n`);
×
NEW
77
            return;
×
78
          }
NEW
79
          this.context.stdout.write("2.1. Performing build...\n");
×
NEW
80
          exitCode = await this.cli.run(["build"]);
×
NEW
81
          if (exitCode !== 0) {
×
NEW
82
            this.context.stderr.write(`Error: 'yarn build' failed with code: ${exitCode}\n`);
×
NEW
83
            return;
×
84
          }
NEW
85
          this.context.stdout.write("2.2. Performing lint...\n");
×
NEW
86
          exitCode = await this.cli.run(["lint"]);
×
NEW
87
          if (exitCode !== 0) {
×
NEW
88
            this.context.stderr.write(`Error: 'yarn lint' failed with code: ${exitCode}\n`);
×
NEW
89
            return;
×
90
          }
91
          // this.context.stdout.write("2.3. Running tests...\n");
NEW
92
          exitCode = await this.cli.run(["test"]);
×
NEW
93
          if (exitCode !== 0) {
×
NEW
94
            this.context.stderr.write(`Error: 'yarn test' failed with code: ${exitCode}\n`);
×
NEW
95
            return;
×
96
          }
97

98
          // Cleanup after Step 2 - Pre-release testing
NEW
99
          exitCode = await this.cli.run(["clean"]);
×
NEW
100
          if (exitCode !== 0) {
×
NEW
101
            this.context.stderr.write(`Error: 'yarn clean' failed with code: ${exitCode}\n`);
×
NEW
102
            return;
×
103
          }
104

105
          // Step 3 - Increment the versions of all packages (including the project root)
106
          let packageFiles = [];
×
107
          this.context.stdout.write(`3. Running \`yarn version\` to bump versions of all workspaces to ${this.version}...\n`);
×
108
          for (let i = 0; i < project.workspaces.length; i++) {
×
NEW
109
            const projectWorkspace = project.workspaces[i];
×
110

111
            if (projectWorkspace.cwd == project.cwd) {
×
NEW
112
              exitCode = await this.cli.run(["version", this.version]);
×
NEW
113
              if (exitCode !== 0) {
×
NEW
114
                this.context.stderr.write(`Error: Incrementing project version failed with code: ${exitCode}\n`);
×
NEW
115
                return;
×
116
              }
117
            } else {
NEW
118
              const projectWorkspaceName = `@${projectWorkspace.manifest.name.scope}/${projectWorkspace.manifest.name.name}`;
×
NEW
119
              exitCode = await this.cli.run(["workspace", projectWorkspaceName, "version", this.version]);
×
NEW
120
              if (exitCode !== 0) {
×
NEW
121
                this.context.stderr.write(`Error: Incrementing workspace '` + projectWorkspaceName + `' version failed with code: ${exitCode}\n`);
×
NEW
122
                return;
×
123
              }
124
            }
125
            packageFiles.push(`${projectWorkspace.cwd}/package.json`);
×
126
          }
127

128
          // Step 4 - git commit the version update.
NEW
129
          this.context.stdout.write("4. git Committing the version update...\n");
×
NEW
130
          exitCode = await execute("git", ["commit", `--message=[release] Release version: ${this.version}`].concat(packageFiles), executeOptions);
×
NEW
131
          if (exitCode !== 0) {
×
NEW
132
            this.context.stderr.write(`Error: git commit failed with code: ${exitCode}\n`);
×
NEW
133
            return;
×
134
          } else {
NEW
135
            this.context.stdout.write("git commit OK!\n");
×
136
          } 
137

138
          // Step 5 - git tag and sign the release tag.
NEW
139
          this.context.stdout.write("5. git Committing the version update...\n");
×
NEW
140
          exitCode = await execute("git", ["tag", `--message=[release] Release version: ${this.version}`, "--sign", `v${this.version}`], executeOptions);
×
NEW
141
          if (exitCode !== 0) {
×
NEW
142
            this.context.stderr.write(`Error: git commit failed with code: ${exitCode}\n`);
×
NEW
143
            return;
×
144
          } else {
NEW
145
            this.context.stdout.write("git tag OK!\n");
×
146
          } 
147
          // Step 6 - git push the updates.
NEW
148
          this.context.stdout.write("6. git push the version update...\n");
×
NEW
149
          this.context.stdout.write("6.1. git pushing the branch...\n");
×
NEW
150
          exitCode = await execute("git", ["push"], executeOptions);
×
NEW
151
          if (exitCode !== 0) {
×
NEW
152
            this.context.stderr.write(`Error: git push failed with code: ${exitCode}\n`)
×
NEW
153
            return;
×
154
          } else {
NEW
155
            this.context.stdout.write("git push OK!\n");
×
156
          } 
NEW
157
          this.context.stdout.write("6.2. git pushing the tag...\n");
×
NEW
158
          exitCode = await execute("git", ["push", "--tags"], executeOptions);
×
NEW
159
          if (exitCode !== 0) {
×
NEW
160
            this.context.stderr.write("Error: git push tags failed with code: ${exitCode}\n");
×
NEW
161
            return;
×
162
          } else {
NEW
163
            this.context.stdout.write("git push tags OK!\n");
×
164
          } 
165
          // Step 7 - Publish the packages to npm.js.
NEW
166
          this.context.stdout.write("7. Running `yarn npn publish` to publish packages to npm.js...\n");
×
NEW
167
          exitCode = await this.cli.run(["npm", "login"]);
×
NEW
168
          if (exitCode !== 0) {
×
NEW
169
            this.context.stderr.write("Error: npm login failed with code: ${exitCode}\n");
×
NEW
170
            return;
×
171
          } else {
NEW
172
            this.context.stdout.write("npm login OK!\n");
×
173
          }
174
          for (let i = 0; i < project.workspaces.length; i++) {
×
NEW
175
            const projectWorkspace = project.workspaces[i];
×
176

177
            if (projectWorkspace.cwd == project.cwd) {
×
178
              this.context.stdout.write("NOTE: Skipping publish of project root workspace!\n")
×
179
            } else {
NEW
180
              const projectWorkspaceName = `@${projectWorkspace.manifest.name.scope}/${projectWorkspace.manifest.name.name}`;
×
181

NEW
182
              const licenseFileExists = await access(`${projectWorkspace.cwd}/LICENSE`).then(_x => true).catch(_e => false);
×
NEW
183
              let copiedLicenseFile = false;
×
NEW
184
              if (!licenseFileExists) {
×
185
                // Make a copy of the LICENSE file to the workspace so that it is published as part of the package
NEW
186
                try {
×
NEW
187
                  await copyFile(`${project.cwd}/LICENSE`, `${projectWorkspace.cwd}/LICENSE`);
×
NEW
188
                  copiedLicenseFile = true;
×
189
                } catch(error) {
NEW
190
                  this.context.stderr.write(`Error: Copying LICENSE to workspace '` + projectWorkspaceName+ `' failed: ${error}\n`);
×
NEW
191
                  return;
×
192
                }
193
              }
194
              // Publish the workspace package
NEW
195
              exitCode = await this.cli.run(["workspace", projectWorkspaceName, "npm", "publish", "--access", "public"]);
×
NEW
196
              if (exitCode !== 0) {
×
NEW
197
                this.context.stderr.write(`Error: npm publish '` + projectWorkspaceName + `' failed with code: ${exitCode}\n`);
×
NEW
198
                return;
×
199
              } else {
NEW
200
                this.context.stdout.write(`npm publish '` + projectWorkspaceName + `' OK!\n`);
×
201
              }
202

NEW
203
              if (copiedLicenseFile) {
×
204
                // Remove the copy of the LICENSE file from the workspace
NEW
205
                try {
×
NEW
206
                  await rm(`${projectWorkspace.cwd}/LICENSE`);
×
207
                } catch(error) {
NEW
208
                  this.context.stderr.write(`Error: Removing LICENSE from workspace '` + projectWorkspaceName+ `' failed: ${error}\n`);
×
NEW
209
                  return;
×
210
                }
211
              }
212
            }
213
          }
214

215
          this.context.stdout.write(`Released version: ${this.version} OK!\n`);
×
216
        }
217

218
        async catch(error) {
NEW
219
          throw error;
×
220
        }
221
      }
222
  
223
      return {
2✔
224
        commands: [
225
          ReleaseCommand,
226
        ],
227
      };
228
    },
229
  };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc