@itcase/config 1.7.8 → 1.7.9

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
@@ -186,17 +186,7 @@ import { semanticReleaseMonorepo } from '@itcase/config/semantic-release/index.j
186
186
  export default semanticReleaseMonorepo
187
187
  ```
188
188
 
189
- В корне монорепозитория зафиксируйте writer 9. Иначе `CHANGELOG.md` получает только заголовок версии: `@commitlint/config-conventional` ставит `conventional-changelog-conventionalcommits` 10, а semantic-release тянет `conventional-changelog-writer` 8, и тот не печатает коммиты.
190
-
191
- ```json
192
- {
193
- "overrides": {
194
- "conventional-changelog-writer": "9.2.1"
195
- }
196
- }
197
- ```
198
-
199
- После этого `npm install`. Уже опубликованный релиз сам не перепишется.
189
+ Заметки монорепозитория печатает `conventional-changelog-writer` 9 из этого пакета. Writer 8, который ставит semantic-release, для `conventional-changelog-conventionalcommits` 10 оставляет в `CHANGELOG.md` только заголовок версии. Отдельный `overrides` в репозитории не нужен. Уже опубликованный релиз сам не перепишется.
200
190
 
201
191
  ## Подключение Husky
202
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itcase/config",
3
- "version": "1.7.8",
3
+ "version": "1.7.9",
4
4
  "author": "ITCase",
5
5
  "description": "ITCase Config",
6
6
  "engines": {
@@ -67,6 +67,7 @@
67
67
  "chokidar-cli": "^3.0.0",
68
68
  "commitizen": "^4.3.2",
69
69
  "conventional-changelog-conventionalcommits": "9.3.1",
70
+ "conventional-changelog-writer": "9.2.1",
70
71
  "cssnano": "^9.0.5",
71
72
  "cssnano-preset-default": "^9.0.5",
72
73
  "inquirer": "^14.2.2",
@@ -12,17 +12,17 @@ import {
12
12
  releaseRules,
13
13
  } from './config.js'
14
14
 
15
- const issuesReleaseNotesScript = join(
15
+ const releaseNotesFromIssuesScript = join(
16
16
  dirname(fileURLToPath(import.meta.url)),
17
- 'issuesReleaseNotes.js',
17
+ 'releaseNotesFromIssues.js',
18
18
  )
19
19
 
20
20
  // Ссылки на задачи из PR. Репозиторий задач и токен приходят из CI:
21
21
  // RELEASE_NOTES_ISSUES_REPO, RELEASE_NOTES_GH_TOKEN.
22
- const issuesReleaseNotesPlugin = [
22
+ const releaseNotesFromIssuesPlugin = [
23
23
  '@semantic-release/exec',
24
24
  {
25
- generateNotesCmd: `node ${JSON.stringify(issuesReleaseNotesScript)} "\${lastRelease.gitTag}" "\${nextRelease.gitTag}"`,
25
+ generateNotesCmd: `node ${JSON.stringify(releaseNotesFromIssuesScript)} "\${lastRelease.gitTag}" "\${nextRelease.gitTag}"`,
26
26
  },
27
27
  ]
28
28
 
@@ -132,6 +132,6 @@ function createSemanticReleaseConfig({
132
132
  export {
133
133
  commonPlugins,
134
134
  createSemanticReleaseConfig,
135
- issuesReleaseNotesPlugin,
135
+ releaseNotesFromIssuesPlugin,
136
136
  releaseNotesGeneratorConfig,
137
137
  }
@@ -0,0 +1,116 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { dirname, join } from 'node:path'
3
+ import { format, pathToFileURL } from 'node:url'
4
+
5
+ import { writeChangelogString } from 'conventional-changelog-writer'
6
+ import { filterRevertedCommitsSync } from 'conventional-commits-filter'
7
+ import { CommitParser } from 'conventional-commits-parser'
8
+
9
+ import { releaseNotesGeneratorConfig } from './common.js'
10
+
11
+ // Импорт из этого файла резолвит writer 9, зависимость @itcase/config.
12
+ // Writer 8, который semantic-release поднимает в корень монорепозитория,
13
+ // для preset 10 печатает только заголовок версии.
14
+
15
+ function resolvePackageEntry(cwd, packageName) {
16
+ let directory = cwd
17
+
18
+ while (true) {
19
+ const packageDir = join(directory, 'node_modules', packageName)
20
+ const packageJsonPath = join(packageDir, 'package.json')
21
+
22
+ if (existsSync(packageJsonPath)) {
23
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
24
+ const exportEntry = packageJson.exports
25
+ const relativeEntry =
26
+ exportEntry?.import ||
27
+ exportEntry?.['.']?.import ||
28
+ packageJson.module ||
29
+ packageJson.main
30
+
31
+ return pathToFileURL(join(packageDir, relativeEntry)).href
32
+ }
33
+
34
+ const parentDirectory = dirname(directory)
35
+ if (parentDirectory === directory) {
36
+ break
37
+ }
38
+ directory = parentDirectory
39
+ }
40
+
41
+ throw new Error(`Cannot resolve "${packageName}" from "${cwd}"`)
42
+ }
43
+
44
+ async function loadPreset(cwd, presetConfig) {
45
+ const presetUrl = resolvePackageEntry(
46
+ cwd,
47
+ 'conventional-changelog-conventionalcommits',
48
+ )
49
+ const presetModule = await import(presetUrl)
50
+
51
+ return presetModule.default(presetConfig)
52
+ }
53
+
54
+ async function generateReleaseNotes(pluginConfig, context) {
55
+ const { commits, cwd, lastRelease, nextRelease, options } = context
56
+ const repositoryUrl = options.repositoryUrl.replace(/\.git$/i, '')
57
+ const loadedPreset = await loadPreset(cwd, pluginConfig.presetConfig)
58
+ const parser = new CommitParser({
59
+ referenceActions: [
60
+ 'close',
61
+ 'closes',
62
+ 'closed',
63
+ 'fix',
64
+ 'fixes',
65
+ 'fixed',
66
+ 'resolve',
67
+ 'resolves',
68
+ 'resolved',
69
+ ],
70
+ issuePrefixes: ['#', 'gh-'],
71
+ ...loadedPreset.parser,
72
+ ...pluginConfig.parserOpts,
73
+ })
74
+ const parsedCommits = filterRevertedCommitsSync(
75
+ commits
76
+ .filter((commit) => commit.message?.trim())
77
+ .map((rawCommit) => ({
78
+ ...rawCommit,
79
+ ...parser.parse(rawCommit.message),
80
+ })),
81
+ )
82
+ const previousTag = lastRelease.gitTag || lastRelease.gitHead
83
+ const currentTag = nextRelease.gitTag || nextRelease.gitHead
84
+ const repository = new URL(repositoryUrl)
85
+ const protocol = repository.protocol === 'http:' ? 'http' : 'https'
86
+ const [, owner, repositoryName] =
87
+ /^\/(?<owner>[^/]+)?\/?(?<repository>.+)?$/.exec(repository.pathname) || []
88
+
89
+ return writeChangelogString(
90
+ parsedCommits,
91
+ {
92
+ commit: 'commit',
93
+ currentTag,
94
+ host: format({
95
+ hostname: repository.hostname,
96
+ port: repository.port,
97
+ protocol: `${protocol}:`,
98
+ }),
99
+ linkCompare: Boolean(currentTag && previousTag),
100
+ owner,
101
+ previousTag,
102
+ repository: repositoryName,
103
+ version: nextRelease.version,
104
+ issue: 'issues',
105
+ },
106
+ { ...loadedPreset.writer, ...pluginConfig.writerOpts },
107
+ )
108
+ }
109
+
110
+ const releaseNotesFromCommitsPlugin = {
111
+ async generateNotes(_pluginConfig, context) {
112
+ return generateReleaseNotes(releaseNotesGeneratorConfig, context)
113
+ },
114
+ }
115
+
116
+ export { releaseNotesFromCommitsPlugin }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSemanticReleaseConfig,
3
- issuesReleaseNotesPlugin,
3
+ releaseNotesFromIssuesPlugin,
4
4
  } from './common.js'
5
5
 
6
6
  const DEFAULT_VERSION_ASSETS = [
@@ -18,7 +18,7 @@ const DEFAULT_RELEASE_ASSETS = ['builds/*.apk']
18
18
  * - Не публикует в npm
19
19
  * - Коммитит package + Android version-файлы
20
20
  * - Прикрепляет к GitHub release APK из `builds/` (если есть; отсутствующие пропускаются)
21
- * - Заметки релиза: сначала задачи из issuesReleaseNotes.js, если они есть, потом обычные коммиты
21
+ * - Заметки релиза: сначала задачи из releaseNotesFromIssues.js, если они есть, потом обычные коммиты
22
22
  *
23
23
  * Сборку APK делайте в prepare проекта или отдельным CI job до publish.
24
24
  *
@@ -39,7 +39,7 @@ function createSemanticReleaseApp({
39
39
  versionAssets = DEFAULT_VERSION_ASSETS,
40
40
  } = {}) {
41
41
  return createSemanticReleaseConfig({
42
- additionalPlugins: [issuesReleaseNotesPlugin],
42
+ additionalPlugins: [releaseNotesFromIssuesPlugin],
43
43
  branches: branches,
44
44
  gitConfig: {
45
45
  message:
@@ -4,6 +4,7 @@ import { readFile, writeFile } from 'node:fs/promises'
4
4
  import { resolve } from 'node:path'
5
5
 
6
6
  import { createSemanticReleaseConfig } from './common.js'
7
+ import { releaseNotesFromCommitsPlugin } from './releaseNotesFromCommits.js'
7
8
 
8
9
  const RELEASE_ASSETS = [
9
10
  'lerna.json',
@@ -41,16 +42,14 @@ const semanticReleaseMonorepo = createSemanticReleaseConfig({
41
42
  [
42
43
  'semantic-release-lerna',
43
44
  {
44
- // Заметки пишет @semantic-release/release-notes-generator, не lerna.
45
- // В корне репозитория, который релизится, нужен override
46
- // "conventional-changelog-writer": "9.2.1". Writer 8 не печатает
47
- // коммиты для conventional-changelog-conventionalcommits 10.
48
45
  generateNotes: false,
49
46
  latch: false,
50
47
  },
51
48
  ],
49
+ releaseNotesFromCommitsPlugin,
52
50
  ],
53
51
  preparePlugins: [keepLernaIndependentPlugin],
52
+ skipReleaseNotesGenerator: true,
54
53
  })
55
54
 
56
55
  export { semanticReleaseMonorepo }
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createSemanticReleaseConfig,
3
- issuesReleaseNotesPlugin,
3
+ releaseNotesFromIssuesPlugin,
4
4
  } from './common.js'
5
5
 
6
6
  const semanticReleaseProject = createSemanticReleaseConfig({
7
- additionalPlugins: [issuesReleaseNotesPlugin],
7
+ additionalPlugins: [releaseNotesFromIssuesPlugin],
8
8
  branches: ['docker/master'],
9
9
  gitConfig: {
10
10
  message: 'release: publishing new version ${nextRelease.version} [skip ci]',