@ethima/semantic-release-configuration 3.3.5 → 4.0.1

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 CHANGED
@@ -13,7 +13,7 @@ configuration supporting a range of languages and platforms supported by the
13
13
  Commits specification][conventionalcommits-url].
14
14
  - Generates release notes using
15
15
  [`@semantic-release/release-notes-generator`][semantic-release-notes-generator-plugin-url].
16
- - Updates templated content in a project's root-level `README.md` file
16
+ - Updates templated content in files, e.g. a project's root-level `README.md`,
17
17
  with updated version information using
18
18
  [`@google/semantic-release-replace-plugin`][semantic-release-replace-plugin-url]
19
19
  and [`@semantic-release/git`][semantic-release-git-plugin-url].
@@ -35,9 +35,12 @@ configuration supporting a range of languages and platforms supported by the
35
35
 
36
36
  ## Usage
37
37
 
38
- - Create the relevant secret token for the platform on which the project
39
- using this configuration is hosted, e.g. `GH_TOKEN`, `GITLAB_TOKEN`. The
40
- supported platforms are [GitHub][semantic-release-github-plugin-auth-url] and
38
+ - Create the [relevant authentication
39
+ token][semantic-release-authentication-url] for the platform on which the
40
+ project using this configuration is hosted. This secret will be used by the
41
+ main `semantic-release` tool for pushing tags as well as the
42
+ platform-specific plugin i.e.
43
+ [GitHub][semantic-release-github-plugin-auth-url] or
41
44
  [GitLab][semantic-release-gitlab-plugin-auth-url].
42
45
 
43
46
  For improved security, _use a unique token for every project this
@@ -106,39 +109,50 @@ The configuration is not able to automatically register releases with the
106
109
  General registry. Newly created releases should be registered using [JuliaHub's
107
110
  _Register Packages_ interface][juliahub-register-package-url].
108
111
 
109
- ### Templated Content in `README.md` Files
112
+ ### Templated Content in Files
110
113
 
111
114
  The configuration will look for `__NEXT_SEMANTIC_RELEASE_VERSION__` tokens in
112
- templates in a project's root-level `README.md` and replace them with the
113
- version that is being released. This is, for instance, useful for automatically
114
- keeping installation instructions up-to-date.
115
+ templates in files specified in the `files_with_versioned_templates`
116
+ configuration and replace them with the version that is being released. This
117
+ is, for instance, useful for automatically keeping installation instructions
118
+ up-to-date. The configuration defaults to a project's root-level `README.md`.
115
119
 
116
- Templated content is created using HTML comments and has the following
117
- structure:
120
+ Templated content has the following token-based structure:
118
121
 
119
- - A `<!-- START_VERSIONED_TEMPLATE` token,
122
+ - A `BEGIN_VERSIONED_TEMPLATE` token,
120
123
  - the template itself with one or more
121
124
  `__NEXT_SEMANTIC_RELEASE_VERSION__` tokens,
122
- - a comment closing tag `-->`,
123
- - (optionally) content that was previously templated,
124
- - a comment closing the templated content `<!-- END_VERSIONED_TEMPLATE -->`.
125
-
126
- More concretely a section in a `README` that looks like
125
+ - an `END_VERSIONED_TEMPLATE` token,
126
+ - (optionally) content that was previously templated and which will be
127
+ discarded,
128
+ - an `END_VERSIONED_TEMPLATE_REPLACEMENT` token.
129
+
130
+ The `BEGIN_VERSIONED_TEMPLATE`, `END_VERSIONED_TEMPLATE` and
131
+ `END_VERSIONED_TEMPLATE_REPLACEMENT` tokens must each be on their own line.
132
+ These lines may be indented and contain other content that is exempt from
133
+ replacements, e.g. comment markers to ensure the templates do not affect
134
+ surrounding code or documentation. The exact tokens may differ per file-type,
135
+ [see the source code for available
136
+ configurations](./src/versioned-templates.js). For instance, Markdown files can
137
+ use HTML block comments and may replace the literal `END_VERSIONED_TEMPLATE`
138
+ token with an "end comment" token.
139
+
140
+ More concretely a section in a `README.md` that looks like
127
141
 
128
142
  ```markdown
129
- <!-- START_VERSIONED_TEMPLATE
143
+ <!-- BEGIN_VERSIONED_TEMPLATE
130
144
 
131
145
  The next semantically released version will be v__NEXT_SEMANTIC_RELEASE_VERSION__!
132
146
 
133
147
  -->
134
- <!-- END_VERSIONED_TEMPLATE -->
148
+ <!-- END_VERSIONED_TEMPLATE_REPLACEMENT -->
135
149
  ```
136
150
 
137
151
  would, after a v1.2.3 release using the configuration has been triggered,
138
152
  become
139
153
 
140
154
  ```markdown
141
- <!-- START_VERSIONED_TEMPLATE
155
+ <!-- BEGIN_VERSIONED_TEMPLATE
142
156
 
143
157
  The next semantically released version will be v__NEXT_SEMANTIC_RELEASE_VERSION__!
144
158
 
@@ -146,7 +160,7 @@ The next semantically released version will be v__NEXT_SEMANTIC_RELEASE_VERSION_
146
160
 
147
161
  The next semantically released version will be v1.2.3!
148
162
 
149
- <!-- END_VERSIONED_TEMPLATE -->
163
+ <!-- END_VERSIONED_TEMPLATE_REPLACEMENT -->
150
164
  ```
151
165
 
152
166
  Note that the `v` is in the template! The version as derived by the semantic
@@ -182,7 +196,7 @@ including a non-visible whitespace character, e.g. [a _Zero Width Non-Joiner
182
196
  (ZWNJ)_ character](https://unicode-table.com/en/200C/). A sufficient approach
183
197
  is to modify the starting token, so that the template is no longer recognized
184
198
  as one. Using the suggested ZWNJ character the starting token becomes
185
- `<!--‌ START_VERSIONED_TEMPLATE`.
199
+ `<!--‌ BEGIN_VERSIONED_TEMPLATE`.
186
200
 
187
201
  Note that when using this approach, although the templates/tokens _visually_
188
202
  look like versions that should be replaced, they cannot be copied and pasted as
@@ -217,6 +231,7 @@ release by hand from the tag and changelog that was created by the
217
231
  [juliahub-register-package-url]: https://juliahub.com/ui/Registrator
218
232
  [npm-token-url]: https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website
219
233
  [replace-in-file-url]: https://www.npmjs.com/package/replace-in-file
234
+ [semantic-release-authentication-url]: https://semantic-release.gitbook.io/semantic-release/usage/ci-configuration#authentication
220
235
  [semantic-release-changelog-plugin-url]: https://github.com/semantic-release/changelog
221
236
  [semantic-release-commit-analyzer-plugin-url]: https://github.com/semantic-release/commit-analyzer
222
237
  [semantic-release-extends-configuration-url]: https://semantic-release.gitbook.io/semantic-release/usage/configuration#extends
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethima/semantic-release-configuration",
3
- "version": "3.3.5",
3
+ "version": "4.0.1",
4
4
  "description": "A shareable semantic release configuration supporting a range of languages and platforms supported by the Ethima organization.",
5
5
  "main": "./src/index.js",
6
6
  "repository": {
@@ -21,9 +21,9 @@
21
21
  "@google/semantic-release-replace-plugin": "1.2.0",
22
22
  "@semantic-release/changelog": "6.0.3",
23
23
  "@semantic-release/git": "10.0.1",
24
- "@semantic-release/github": "8.0.7",
25
- "@semantic-release/gitlab": "12.0.1",
26
- "conventional-changelog-conventionalcommits": "5.0.0",
27
- "cosmiconfig": "8.1.3"
24
+ "@semantic-release/github": "9.0.3",
25
+ "@semantic-release/gitlab": "12.0.3",
26
+ "conventional-changelog-conventionalcommits": "6.0.0",
27
+ "cosmiconfig": "8.2.0"
28
28
  }
29
29
  }
package/src/index.js CHANGED
@@ -9,25 +9,19 @@ const GIT_ASSETS = [
9
9
  ...CONFIGURATION.files_with_versioned_templates,
10
10
  ];
11
11
 
12
- // Used to detect whether configuration for either the
13
- // `semantic-release/github`, `semantic-release/gitlab` or both platform
14
- // release plugins has been provided through the environment
15
- const GITHUB_TOKEN_NAMES = ["GH_TOKEN", "GITHUB_TOKEN"];
16
- const GITLAB_TOKEN_NAMES = ["GL_TOKEN", "GITLAB_TOKEN"];
17
-
18
12
  /*
19
13
  * Determines whether the provided plugin should be added to the semantic
20
14
  * release configuration based on the presence of select environment variables.
21
15
  *
22
- * @param {Array} token_names An array of strings specifying the names of the
23
- * environment variables to detect.
16
+ * @param {string} environment_variable The environment variable to verify the
17
+ * existence of (and equality to `"true"`) to include the specified `plugin`.
24
18
  * @param {string} plugin The name of the NPM package providing the plugin.
25
19
  *
26
- * @return {Array} References the plugin if any of the `token_names` are
27
- * detected and is empty otherwise.
20
+ * @return {Array} References the plugin if the `environment_variable` was set
21
+ * to `"true"` and is empty otherwise.
28
22
  */
29
- function platformSpecificReleasePlugin(token_names, plugin) {
30
- return token_names.some((token_name) => env[token_name]) ? [plugin] : [];
23
+ function platformSpecificReleasePlugin(environment_variable, plugin) {
24
+ return env[environment_variable] === "true" ? [plugin] : [];
31
25
  }
32
26
 
33
27
  // Ensure the NPM plugin gets added to the semantic release pipeline if a
@@ -101,13 +95,10 @@ const SEMANTIC_RELEASE_CONFIGURATION = {
101
95
  },
102
96
  ],
103
97
  ...platformSpecificReleasePlugin(
104
- GITHUB_TOKEN_NAMES,
98
+ "GITHUB_ACTIONS",
105
99
  "@semantic-release/github"
106
100
  ),
107
- ...platformSpecificReleasePlugin(
108
- GITLAB_TOKEN_NAMES,
109
- "@semantic-release/gitlab"
110
- ),
101
+ ...platformSpecificReleasePlugin("GITLAB_CI", "@semantic-release/gitlab"),
111
102
  ],
112
103
  };
113
104
 
@@ -1,42 +1,229 @@
1
- const BLOCK_COMMENT_END_PATTERN = "-->";
2
- const BLOCK_COMMENT_START_PATTERN = "<!--";
3
- const TEMPLATE_VERSION_PLACEHOLDER = "__NEXT_SEMANTIC_RELEASE_VERSION__";
4
-
5
- const templateMatcher = new RegExp(
6
- [
7
- `(${BLOCK_COMMENT_START_PATTERN}\\s*START_VERSIONED_TEMPLATE\\s*)`,
8
- "(.*?)", // The template to insert the version into
9
- `(\\s*${BLOCK_COMMENT_END_PATTERN})`,
10
- ".*?", // The templated content, will be discarded
11
- `(${BLOCK_COMMENT_START_PATTERN}\\s*END_VERSIONED_TEMPLATE\\s*${BLOCK_COMMENT_END_PATTERN})`,
12
- ].join(""),
13
- "gms"
14
- );
15
-
16
- function templateReplacer(
17
- _,
18
- templateStartMarker,
1
+ const { extname } = require("node:path");
2
+
3
+ /**
4
+ * The default configuration for tokens used by the {@link buildReplacement}
5
+ * and {@link buildTemplateMatcher} functions to parse templates and perform
6
+ * replacements.
7
+ *
8
+ * The tokens serve the following purposes:
9
+ * - `next_version_placeholder_token` represents locations in a template that
10
+ * need to be replaced by the version of the next semantic release.
11
+ * - `replacement_end_token` represents the end of content that was previously
12
+ * filled in. Should be on a separate line. This line may contain additional
13
+ * content which will be maintained along with the token in the replaced
14
+ * content, e.g. indentation, comment markers, etc..
15
+ * - `template_begin_token` indicates the start of the template in which
16
+ * `next_version_placeholder_token`s should be replaced. Should be on a
17
+ * separate line. This line may contain additional content which will be
18
+ * maintained along with the token in the replaced content, e.g. indentation,
19
+ * comment markers, etc..
20
+ * - `template_continuation_token` represents indented markers that are
21
+ * necessary to indicate continuation of the template, but which should not
22
+ * be present in the replaced content, e.g. comment indicators for line-based
23
+ * comments, etc. Indented whitespace does not have to be configured in the
24
+ * token.
25
+ * - `template_end_token` indicates the end of the template. Should be on a
26
+ * separate line. This line may contain additional content which will be
27
+ * maintained along with the token in the replaced content, e.g. indentation,
28
+ * comment markers, etc..
29
+ */
30
+ const DEFAULT_TEMPLATE_TOKEN_CONFIGURATION = {
31
+ next_version_placeholder_token: "__NEXT_SEMANTIC_RELEASE_VERSION__",
32
+ replacement_end_token: "END_VERSIONED_TEMPLATE_REPLACEMENT",
33
+ template_begin_token: "BEGIN_VERSIONED_TEMPLATE",
34
+ template_continuation_token: "",
35
+ template_end_token: "END_VERSIONED_TEMPLATE",
36
+ };
37
+
38
+ /**
39
+ * Template token configuration for files that can be commented using "C-style
40
+ * comments", e.g. C, CSS, JavaScript, TypeScript, etc.
41
+ */
42
+ const C_BLOCK_TEMPLATE_TOKEN_CONFIGURATION = {
43
+ template_continuation_token: "\\s\\*+",
44
+ template_end_token: "\\*+/",
45
+ };
46
+
47
+ /**
48
+ * Template token configuration for files that can be commented using "hash
49
+ * signs", e.g. Python, TOML, YAML, etc.
50
+ */
51
+ const HASH_TEMPLATE_TOKEN_CONFIGURATION = {
52
+ template_continuation_token: "#",
53
+ };
54
+
55
+ /**
56
+ * Template token configuration for files that can be commented using XML
57
+ * comments, e.g. HTML, Markdown, etc.
58
+ */
59
+ const XML_TEMPLATE_TOKEN_CONFIGURATION = {
60
+ template_end_token: "-->",
61
+ };
62
+
63
+ /**
64
+ * Maps file extensions to token configurations for templates that can be
65
+ * included in the specified file formats.
66
+ */
67
+ const FILE_EXTENSION_TO_TEMPLATE_CONFIGURATION = {
68
+ c: C_BLOCK_TEMPLATE_TOKEN_CONFIGURATION,
69
+ css: C_BLOCK_TEMPLATE_TOKEN_CONFIGURATION,
70
+ html: XML_TEMPLATE_TOKEN_CONFIGURATION,
71
+ jl: HASH_TEMPLATE_TOKEN_CONFIGURATION,
72
+ js: C_BLOCK_TEMPLATE_TOKEN_CONFIGURATION,
73
+ md: XML_TEMPLATE_TOKEN_CONFIGURATION,
74
+ py: HASH_TEMPLATE_TOKEN_CONFIGURATION,
75
+ toml: HASH_TEMPLATE_TOKEN_CONFIGURATION,
76
+ ts: C_BLOCK_TEMPLATE_TOKEN_CONFIGURATION,
77
+ xml: XML_TEMPLATE_TOKEN_CONFIGURATION,
78
+ yaml: HASH_TEMPLATE_TOKEN_CONFIGURATION,
79
+ yml: HASH_TEMPLATE_TOKEN_CONFIGURATION,
80
+ };
81
+
82
+ /**
83
+ * Builds a replacement string from the components matched by a matcher created
84
+ * by {@link buildTemplateMatcher}. Any `next_version_placeholder_token`s, as
85
+ * identified by the {@link DEFAULT_TEMPLATE_TOKEN_CONFIGURATION}, are replaced
86
+ * by the next semantic release version. Any "template continuation tokens" are
87
+ * removed from the template using {@link stripTemplateContinuationTokens}.
88
+ */
89
+ function buildReplacement(
90
+ // Only grab the tokens of interest that should be included in the
91
+ // replacement string. These are obtained from the match instead of the
92
+ // configuration to ensure additional information in the templated area is
93
+ // preserved, e.g. whitespace, etc.
94
+ _, // Full match
95
+ template_begin,
19
96
  template,
20
- templateEndMarker,
21
- templateCloseMarker,
22
- ...args
97
+ template_end,
98
+ replacement_end,
99
+ _, // Index of match start
100
+ _, // Searched text
101
+ filename,
102
+ context
23
103
  ) {
24
- const context = args.pop();
104
+ const { next_version_placeholder_token, template_continuation_token } =
105
+ getTemplateTokenConfiguration(filename);
106
+
107
+ const normalized_template = stripTemplateContinuationTokens(
108
+ template_continuation_token,
109
+ template
110
+ );
25
111
 
26
112
  return [
27
- templateStartMarker,
113
+ template_begin,
28
114
  template,
29
- templateEndMarker,
30
- `\n\n`,
31
- template.replaceAll(
32
- TEMPLATE_VERSION_PLACEHOLDER,
115
+ template_end,
116
+ normalized_template.replaceAll(
117
+ next_version_placeholder_token,
33
118
  context.nextRelease.version
34
119
  ),
35
- `\n\n`,
36
- templateCloseMarker,
120
+ replacement_end,
37
121
  ].join("");
38
122
  }
39
123
 
124
+ /**
125
+ * Builds a regular expression to find templated regions in a file. Templated
126
+ * regions are defined by a token configuration based on the {@param filename}'s
127
+ * extension.
128
+ *
129
+ * The resulting regular expression is set up to capture:
130
+ * - The line starting the template,
131
+ * - One or more lines defining the template in which
132
+ * `next_version_placeholder_token` should be replaced by the next semantic
133
+ * release version,
134
+ * - The line indicating the end of the template.
135
+ * - The line indicating the end of the replacement.
136
+ *
137
+ * Any content between the end of the template and the end of the replacement
138
+ * is dropped.
139
+ */
140
+ function buildTemplateMatcher(filename) {
141
+ const { replacement_end_token, template_begin_token, template_end_token } =
142
+ getTemplateTokenConfiguration(filename);
143
+
144
+ return new RegExp(
145
+ [
146
+ `(\n[^\n]*${template_begin_token}[^\n]*)`,
147
+ // The template to insert the version into. Needs to lazily match to
148
+ // ensure only content up until the first `template_end_token` is matched
149
+ // to prevent issues when multiple templates are present in a single file
150
+ "((?:\n[^\n]*?)+?)",
151
+ `(\n[^\n]*${template_end_token}[^\n]*)`,
152
+ // The templated content, will be discarded. Needs to lazily match to
153
+ // ensure only content up until the first `replacement_end_token` is
154
+ // grabbed
155
+ ".*?",
156
+ `(\n[^\n]*${replacement_end_token}[^\n]*?)`,
157
+ ].join(""),
158
+ "gs"
159
+ );
160
+ }
161
+
162
+ /**
163
+ * Returns the template token configuration corresponding to the specified
164
+ * {@param filename} based on its extension. Extension specific configuration
165
+ * is merged with the {@link DEFAULT_TEMPLATE_TOKEN_CONFIGURATION}
166
+ */
167
+ function getTemplateTokenConfiguration(filename) {
168
+ // `extname` returns the extension with a dot-prefix
169
+ return {
170
+ ...DEFAULT_TEMPLATE_TOKEN_CONFIGURATION,
171
+ ...FILE_EXTENSION_TO_TEMPLATE_CONFIGURATION[extname(filename).slice(1)],
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Strips the {@param template_continuation_token} and the largest common
177
+ * length of whitespace after those tokens from the {@param template}.
178
+ *
179
+ * For instance, assuming the {@param template_continuation_token} is `#`, this
180
+ * will turn
181
+ * ```
182
+ * # Foo bar
183
+ * # Quuz Quux
184
+ * ```
185
+ * into
186
+ * ```
187
+ * Foo bar
188
+ * Quuz Quux
189
+ * ```
190
+ */
191
+ function stripTemplateContinuationTokens(
192
+ template_continuation_token,
193
+ template
194
+ ) {
195
+ // The capture group is only necessary when using this matcher in the context
196
+ // of replacement, i.e. where all the matched content should be replaced by
197
+ // the indentation. It is used in that context and for determining the
198
+ // largest common whitespace prefix for each line in the template
199
+ const indented_continuation_token_matcher = `^(\\s*)${template_continuation_token}`;
200
+ const continuation_prefix_matcher = new RegExp(
201
+ `${indented_continuation_token_matcher}(?<prefix>\\s*)`
202
+ );
203
+
204
+ const prefix_lengths = template
205
+ .split("\n")
206
+ // The first line in the template will be empty when splitting on newlines,
207
+ // due to the way the matchers are set up to match on starting newlines
208
+ .slice(1)
209
+ .map((template_line) => {
210
+ const match = template_line.match(continuation_prefix_matcher);
211
+ return match?.groups?.prefix?.length ?? 0;
212
+ });
213
+
214
+ const largest_common_prefix_matcher = new RegExp(
215
+ `${indented_continuation_token_matcher}\\s{${Math.min(...prefix_lengths)}}`,
216
+ "gm"
217
+ );
218
+
219
+ return template.replaceAll(largest_common_prefix_matcher, "$1");
220
+ }
221
+
222
+ /**
223
+ * Returns the semantic release configuration for the
224
+ * `@google/semantic-release-replace-plugin` set up to replace specific tokens
225
+ * with the next semantic release version in the provided {@param files}.
226
+ */
40
227
  function VersionedTemplatesConfiguration(files) {
41
228
  return [
42
229
  "@google/semantic-release-replace-plugin",
@@ -45,8 +232,8 @@ function VersionedTemplatesConfiguration(files) {
45
232
  {
46
233
  countMatches: true,
47
234
  files,
48
- from: templateMatcher,
49
- to: templateReplacer,
235
+ from: buildTemplateMatcher,
236
+ to: buildReplacement,
50
237
  },
51
238
  ],
52
239
  },