release-me-sh 0.11.10 → 0.11.12

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.
Files changed (2) hide show
  1. package/package.json +14 -12
  2. package/release.sh +451 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-me-sh",
3
- "version": "0.11.10",
3
+ "version": "0.11.12",
4
4
  "description": "Fast minimal release workflow script written in Bash with plugins and presets support",
5
5
  "keywords": [
6
6
  "bash",
@@ -19,8 +19,11 @@
19
19
  },
20
20
  "license": "Apache-2.0",
21
21
  "author": "Davlatjon Shavkatov <opensource@dalisoft.uz>",
22
- "bin": "release-me.sh",
23
- "files": ["presets", "plugins", "release-me.sh"],
22
+ "bin": {
23
+ "release-me": "release.sh",
24
+ "release-me-sh": "release.sh"
25
+ },
26
+ "files": ["presets", "plugins", "release.sh"],
24
27
  "browserslist": {
25
28
  "production": [">0.5%", "not dead", "not op_mini all"],
26
29
  "development": [
@@ -42,19 +45,18 @@
42
45
  },
43
46
  "devDependencies": {
44
47
  "@biomejs/biome": "^1.8.3",
45
- "@docusaurus/core": "3.4.0",
46
- "@docusaurus/module-type-aliases": "^3.4.0",
47
- "@docusaurus/preset-classic": "3.4.0",
48
- "@docusaurus/types": "^3.4.0",
49
- "@evilmartians/lefthook": "^1.6.18",
48
+ "@docusaurus/core": "3.5.1",
49
+ "@docusaurus/module-type-aliases": "^3.5.1",
50
+ "@docusaurus/preset-classic": "3.5.1",
51
+ "@docusaurus/types": "^3.5.1",
52
+ "@evilmartians/lefthook": "^1.7.12",
50
53
  "@ls-lint/ls-lint": "^2.2.3",
51
- "@swc/core": "^1.6.13",
54
+ "@swc/core": "^1.7.10",
52
55
  "commitlint-rs-npm": "dalisoft/commitlint-rs-npm#v0.1.11",
53
- "dprint": "^0.47.1",
56
+ "dprint": "^0.47.2",
54
57
  "prism-react-renderer": "^2.3.1",
55
58
  "prismjs": "^1.29.0",
56
- "swc-loader": "^0.2.6",
57
- "typos-rs-npm": "dalisoft/typos-rs-npm#v1.23.1"
59
+ "swc-loader": "^0.2.6"
58
60
  },
59
61
  "engines": {
60
62
  "node": ">=18.19.1"
package/release.sh ADDED
@@ -0,0 +1,451 @@
1
+ #!/usr/bin/env bash
2
+ set -eu
3
+ shopt -s inherit_errexit
4
+
5
+ readonly CLI_PREFIX="[release-me]"
6
+ readonly DESCRIPTION="Fast minimal release workflow script written in Bash with plugins and presets support"
7
+ readonly USAGE="${CLI_PREFIX} Usage: release-me [options]
8
+ ${DESCRIPTION}
9
+
10
+ Options:
11
+ -d, --dry-run Dry run. Skip tag creation, only show logs (if exists).
12
+ -w, --workspace Use in workspace environment for publishing workspaces separately.
13
+ --use-version Use project version from manifest. Requires workspace and a valid manifest.
14
+ --verbose Verbose mode, shows more detailed logs.
15
+ --quiet Quiet mode, shows less logs than default behavior.
16
+ --plugins=* Plugins option for loading plugins.
17
+ --preset= Presets option for parsing commits.
18
+ --stable If project has a \`0.x\` version, it will bump to \`1.x\`.
19
+ --pre-release Mark project release as non-production ready.
20
+ -h, --help Show this help.
21
+ -v, --version Show version.
22
+ "
23
+
24
+ ##############################
25
+ ####### Root variables #######
26
+ ##############################
27
+ READLINK=$(readlink -f -- "$0")
28
+ SCRIPT_DIR=$(dirname -- "${READLINK}")
29
+ CURRENT_DATE=$(date +'%Y-%m-%d')
30
+ IS_GIT_REPO=$(git rev-parse --is-inside-work-tree 2>/dev/null || printf "%s" "")
31
+ GIT_LOG_ENTRY_SEPARATOR='____'
32
+ GIT_LOG_COMMIT_SEPARATOR='START_OF_COMMIT'
33
+ GIT_LOG_FORMAT="${GIT_LOG_COMMIT_SEPARATOR}%n%h${GIT_LOG_ENTRY_SEPARATOR}%H${GIT_LOG_ENTRY_SEPARATOR}%s${GIT_LOG_ENTRY_SEPARATOR}%b"
34
+ GIT_LOG_PARSE_REGEX="(.*)${GIT_LOG_ENTRY_SEPARATOR}(.*)${GIT_LOG_ENTRY_SEPARATOR}(.*)(${GIT_LOG_ENTRY_SEPARATOR}(.*)?)"
35
+ GIT_REMOTE_ORIGIN=$(git remote get-url origin 2>/dev/null || printf "%s" "")
36
+ GIT_REPO_NAME=
37
+
38
+ if [[ "${GIT_REMOTE_ORIGIN}" == "git@"* ]]; then
39
+ GIT_REPO_NAME=$(git remote get-url origin | cut -d ':' -f 2 | sed s/.git//)
40
+ elif [[ "${GIT_REMOTE_ORIGIN}" == "https"* ]]; then
41
+ GIT_REPO_NAME=$(git remote get-url origin | cut -d ':' -f 2 | sed s/\\/\\/github.com\\///)
42
+ fi
43
+
44
+ IS_WORKSPACE=false
45
+ IS_USE_PKG_VERSION=false
46
+ IS_DRY_RUN=false
47
+ IS_QUIET=false
48
+ IS_VERBOSE=false
49
+ IS_STABLE_VERSION=false
50
+ PRE_RELEASE_VERSION=false
51
+ PLUGINS=("git")
52
+ PRESET="conventional-commits"
53
+
54
+ # set `verbose` on `CI`
55
+ if [[ -n "${CI:-}" ]]; then
56
+ IS_VERBOSE=true
57
+ fi
58
+
59
+ ##############################
60
+ ###### Helpers & Utils #######
61
+ ##############################
62
+
63
+ # proper handling of lines counter
64
+ # as `grep -c '^'` fails if there nothing
65
+ # and `wc -l` if there 1 commit
66
+ # so this utility was made
67
+
68
+ glc() {
69
+ local COUNT=0
70
+ while read -r line; do
71
+ if [[ -n "${line}" ]]; then
72
+ COUNT=$((COUNT + 1))
73
+ fi
74
+ done <<<"$(cat /dev/stdin)"
75
+
76
+ printf "%s" "${COUNT}"
77
+ }
78
+
79
+ parse_options() {
80
+ while :; do
81
+ local KEY="${1-}"
82
+ case "${KEY}" in
83
+ -v | --version)
84
+ printf "%s" "${CLI_PREFIX} last version available at GitHub"
85
+ exit 0
86
+ ;;
87
+ -h | -\? | --help)
88
+ printf "%s" "${USAGE}"
89
+ exit 0
90
+ ;;
91
+ -w | --workspace)
92
+ IS_WORKSPACE=true
93
+ ;;
94
+ --plugins=*)
95
+ local IFS=','
96
+ read -ra PLUGINS <<<"${KEY#*=}"
97
+ ;;
98
+ --preset=*)
99
+ read -r PRESET <<<"${KEY#*=}"
100
+ ;;
101
+ --stable)
102
+ IS_STABLE_VERSION=true
103
+ PRE_RELEASE_VERSION=false
104
+ ;;
105
+ --pre-release)
106
+ # shellcheck disable=2034
107
+ PRE_RELEASE_VERSION=true
108
+ IS_STABLE_VERSION=false
109
+ ;;
110
+ --use-version)
111
+ IS_USE_PKG_VERSION=true
112
+ ;;
113
+ -d | --dry-run)
114
+ # shellcheck disable=2034
115
+ IS_DRY_RUN=true
116
+ ;;
117
+ -q | --quiet)
118
+ IS_QUIET=true
119
+ IS_VERBOSE=false
120
+ ;;
121
+ --verbose)
122
+ IS_VERBOSE=true
123
+ IS_QUIET=false
124
+ # export GIT_TRACE=1
125
+ ;;
126
+ -?*)
127
+ printf "%s" "${CLI_PREFIX} Unknown option: ${KEY}"
128
+ exit 1
129
+ ;;
130
+ ?*)
131
+ printf "%s" "${CLI_PREFIX} Unknown argument: ${KEY}"
132
+ exit 1
133
+ ;;
134
+ *)
135
+ break
136
+ ;;
137
+ esac
138
+ shift
139
+ done
140
+ }
141
+
142
+ is_valid_commit_type() {
143
+ local key="$1"
144
+ shift
145
+ local arr=("$@")
146
+
147
+ for element in "${arr[@]}"; do
148
+ if [[ "${key}" == "${element}"* ]]; then
149
+ return 0
150
+ fi
151
+ done
152
+ return 1
153
+ }
154
+
155
+ ##############################
156
+ ##### Logging helpers #######
157
+ ##############################
158
+ log() {
159
+ if ! ${IS_QUIET}; then
160
+ if [[ "${2-}" == "-q" ]]; then
161
+ printf "%b\n" "$1"
162
+ else
163
+ printf "%b\n" "${CLI_PREFIX} $1"
164
+ fi
165
+ fi
166
+ }
167
+ log_verbose() {
168
+ if ${IS_VERBOSE}; then
169
+ if [[ "${2-}" == "-q" ]]; then
170
+ printf "%b\n" "$1"
171
+ else
172
+ printf "%b\n" "${CLI_PREFIX} $1"
173
+ fi
174
+ fi
175
+ }
176
+
177
+ ##############################
178
+ ##### Early exit errors ######
179
+ ##############################
180
+
181
+ if [[ "${IS_GIT_REPO}" != true ]]; then
182
+ log "Current directory is not a Git repository!"
183
+ exit 1
184
+ fi
185
+
186
+ parse_options "$@"
187
+
188
+ up_to_date() {
189
+ log "${CLI_PREFIX} $1" -q
190
+ printf "%s\n" "${CLI_PREFIX} Your project is up-to-date"
191
+ exit 0
192
+ }
193
+
194
+ if [[ "${PRESET}" != "" ]]; then
195
+ SOURCE_PRESET_FILE="${SCRIPT_DIR}/presets/${PRESET}.sh"
196
+ # shellcheck disable=SC1090 source=/dev/null
197
+ source "${SOURCE_PRESET_FILE}"
198
+ fi
199
+
200
+ ##############################
201
+ ##### Package variables ######
202
+ ##############################
203
+
204
+ PKG_NAME=""
205
+ NEXT_VERSION=(0 0 0)
206
+ CURRENT_VERSION=(0 0 0)
207
+
208
+ parse_packages() {
209
+ if ! ${IS_WORKSPACE}; then
210
+ return 0
211
+ fi
212
+
213
+ if [[ -f "./package.json" ]]; then
214
+ PKG_NAME=$(awk -F': ' '/"name":/ {gsub(/[",]/, "", $2); print $2}' "./package.json")
215
+ if ${IS_USE_PKG_VERSION}; then
216
+ PKG_VERSION=$(awk -F': ' '/"version":/ {gsub(/[",]/, "", $2); print $2}' "./package.json")
217
+ fi
218
+ elif [[ -f "./Cargo.toml" ]]; then
219
+ PKG_NAME=$(sed -n 's/^name = "\(.*\)"/\1/p' "./Cargo.toml")
220
+ if ${IS_USE_PKG_VERSION}; then
221
+ PKG_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' "./Cargo.toml")
222
+ fi
223
+ elif [[ -f "./setup.py" ]]; then
224
+ PKG_NAME=$(sed -n "s/.*name=['\"]\([^'\"]*\)['\"].*/\1/p" "./setup.py")
225
+ if ${IS_USE_PKG_VERSION}; then
226
+ PKG_VERSION=$(sed -n 's/^ *version\s*=\s*["'\'']\([^"'\'']*\)["'\''].*/\1/p' "./setup.py")
227
+ fi
228
+ else
229
+ cat <<EOF
230
+ This project currently supports only Node.js, Rust and Python projects.
231
+ Please wait for updates to get support in other languages!
232
+ EOF
233
+ exit 1
234
+ fi
235
+
236
+ if ${IS_WORKSPACE} && [[ -z "${PKG_NAME}" ]]; then
237
+ cat <<EOF
238
+ This release aims to being workspace release
239
+ but missing name and could not be release
240
+ EOF
241
+ exit 1
242
+ fi
243
+
244
+ if ${IS_USE_PKG_VERSION}; then
245
+ local IFS='.'
246
+ read -ra NEXT_VERSION <<<"${PKG_VERSION}"
247
+ CURRENT_VERSION=("${NEXT_VERSION[@]}")
248
+ fi
249
+
250
+ log_verbose "Workspace mode is enabled"
251
+ log_verbose "Workspace project name: ${PKG_NAME}"
252
+ }
253
+
254
+ ##############################
255
+ ####### Git variables ########
256
+ ##############################
257
+
258
+ GIT_LAST_PROJECT_TAG=""
259
+ GIT_LAST_PROJECT_TAG_VER=""
260
+
261
+ get_git_variables() {
262
+ GIT_TAGS_LIST=$(git for-each-ref --sort=creatordate --format '%(refname)' refs/tags)
263
+
264
+ if ${IS_WORKSPACE}; then
265
+ GIT_LAST_PROJECT_TAG=$(printf "%b" "${GIT_TAGS_LIST}" | grep "${PKG_NAME}" | tail -1 | cut -d '/' -f 3)
266
+ else
267
+ GIT_LAST_PROJECT_TAG=$(printf "%s" "${GIT_TAGS_LIST}" | tail -1 | cut -d '/' -f 3)
268
+ fi
269
+
270
+ if [[ ${GIT_LAST_PROJECT_TAG} != "" ]]; then
271
+ GIT_LAST_PROJECT_TAG_VER=$(printf "%s" "${GIT_LAST_PROJECT_TAG}" | rev | cut -d 'v' -f 1 | rev)
272
+ fi
273
+
274
+ if [[ -n "${GIT_LAST_PROJECT_TAG_VER}" ]]; then
275
+ if ! ${IS_USE_PKG_VERSION}; then
276
+ mapfile -d '.' -t NEXT_VERSION < <(printf '%s' "${GIT_LAST_PROJECT_TAG_VER}")
277
+ CURRENT_VERSION=("${NEXT_VERSION[@]}")
278
+ fi
279
+
280
+ if [[ "${IS_STABLE_VERSION}" == false && ${PRE_RELEASE_VERSION} == false && "${NEXT_VERSION[0]}" -gt 0 ]]; then
281
+ IS_STABLE_VERSION=true
282
+ fi
283
+ fi
284
+ }
285
+
286
+ ##############################
287
+ ######## Git handling ########
288
+ ##############################
289
+
290
+ GIT_LOGS=""
291
+
292
+ get_git_commits() {
293
+ local IFS=
294
+ local GIT_LOGS_LENGTH=0
295
+
296
+ # Fast early-catch empty repository
297
+ if ! git rev-parse HEAD 1>/dev/null 2>&1; then
298
+ log "You have not committed yet
299
+ You have to commit your initial/first commit."
300
+ exit 1
301
+ fi
302
+
303
+ # Check if exists last tag and is not empty
304
+ if [[ -n "${GIT_LAST_PROJECT_TAG}" ]]; then
305
+ log_verbose "Last project tag [${GIT_LAST_PROJECT_TAG}] found"
306
+ # Get and cache commits variable, so later
307
+ # can be checked for commits length and avaibality
308
+ if ${IS_WORKSPACE}; then
309
+ GIT_LOGS=$(git log "${GIT_LAST_PROJECT_TAG}...HEAD" --grep "${PKG_NAME}" --pretty=format:"${GIT_LOG_FORMAT}" --reverse)
310
+ GIT_LOGS_LENGTH=$(git log "${GIT_LAST_PROJECT_TAG}...HEAD" --grep "${PKG_NAME}" --pretty=format:"%s" | glc -)
311
+ else
312
+ GIT_LOGS=$(git log "${GIT_LAST_PROJECT_TAG}...HEAD" --pretty=format:"${GIT_LOG_FORMAT}" --reverse)
313
+ GIT_LOGS_LENGTH=$(git rev-list --count "${GIT_LAST_PROJECT_TAG}...HEAD")
314
+ fi
315
+ else
316
+ log_verbose "Last project tag not found"
317
+ if ${IS_WORKSPACE}; then
318
+ GIT_LOGS=$(git log HEAD --grep "${PKG_NAME}" --pretty=format:"${GIT_LOG_FORMAT}" --reverse)
319
+ GIT_LOGS_LENGTH=$(git log HEAD --grep "${PKG_NAME}" --pretty=format:"%s" | glc -)
320
+ else
321
+ GIT_LOGS=$(git log HEAD --pretty=format:"${GIT_LOG_FORMAT}" --reverse)
322
+ GIT_LOGS_LENGTH=$(git rev-list --count HEAD)
323
+ fi
324
+ fi
325
+
326
+ if [[ ${GIT_LOGS_LENGTH} -eq 0 ]]; then
327
+ up_to_date "Your project has no new commits"
328
+ else
329
+ if [[ -n "${GIT_LAST_PROJECT_TAG}" ]]; then
330
+ log_verbose "Found ${GIT_LOGS_LENGTH} commits since last release"
331
+ else
332
+ log_verbose "Found ${GIT_LOGS_LENGTH} commits but did not found any release"
333
+ fi
334
+ fi
335
+ }
336
+
337
+ ##############################
338
+ #### Git commits handling ####
339
+ ##############################
340
+ NEXT_BUILD_VERSION=""
341
+ NEXT_RELEASE_VERSION=""
342
+ NEXT_RELEASE_TAG=""
343
+ RELEASE_BODY=""
344
+
345
+ PATCH_UPGRADED=false
346
+ MINOR_UPGRADED=false
347
+ MAJOR_UPGRADED=false
348
+
349
+ handle_git_commits() {
350
+ log_verbose "Analyzing commits...\n"
351
+ local IFS=
352
+ while read -r line; do
353
+ if [[ "${line}" == "${GIT_LOG_COMMIT_SEPARATOR}" ]]; then
354
+ read -r commit
355
+
356
+ if [[ "${commit}" =~ ${GIT_LOG_PARSE_REGEX} ]]; then
357
+ log_verbose "${BASH_REMATCH[3]}" "-q"
358
+ CHECKOUT_SHA=${BASH_REMATCH[1]}
359
+
360
+ preset_command=$(command -v parse_commit)
361
+ if [[ -n "${preset_command}" ]]; then
362
+ parse_commit BASH_REMATCH
363
+ fi
364
+ fi
365
+ fi
366
+ done <<<"${GIT_LOGS}"
367
+ log_verbose "" "-q"
368
+ log_verbose "Analyzed commits!"
369
+
370
+ log_verbose "Preparing changes diff..."
371
+ build_release
372
+ log_verbose "Prepared changes diff!"
373
+
374
+ log_verbose "Analyzing updates..."
375
+ if [[ "${IS_STABLE_VERSION}" == true && "${NEXT_VERSION[0]}" -eq 0 ]]; then
376
+ NEXT_VERSION=(1 0 0)
377
+ elif [[ ${MAJOR_UPGRADED} == true && "${NEXT_VERSION[0]}" -gt 0 ]]; then
378
+ NEXT_VERSION[0]=$((NEXT_VERSION[0] + 1))
379
+ NEXT_VERSION[1]=0
380
+ NEXT_VERSION[2]=0
381
+ elif ${MINOR_UPGRADED} || [[ ${MAJOR_UPGRADED} == true && "${NEXT_VERSION[0]}" -eq 0 ]]; then
382
+ NEXT_VERSION[1]=$((NEXT_VERSION[1] + 1))
383
+ NEXT_VERSION[2]=0
384
+ elif ${PATCH_UPGRADED}; then
385
+ NEXT_VERSION[2]=$((NEXT_VERSION[2] + 1))
386
+ fi
387
+
388
+ local IFS='.'
389
+ NEXT_BUILD_VERSION="${NEXT_VERSION[*]}"
390
+ CURRENT_BUILD_VERSION="${CURRENT_VERSION[*]}"
391
+
392
+ if [[ "${NEXT_BUILD_VERSION}" == "${CURRENT_BUILD_VERSION}" ]]; then
393
+ up_to_date "Your project has no incremental update"
394
+ else
395
+ log_verbose "Analyzing updates done!"
396
+ fi
397
+
398
+ CURRENT_RELEASE_TAG=""
399
+
400
+ if ${IS_WORKSPACE}; then
401
+ NEXT_RELEASE_VERSION="v${NEXT_BUILD_VERSION}"
402
+ NEXT_RELEASE_TAG="${PKG_NAME}-${NEXT_RELEASE_VERSION}"
403
+ CURRENT_RELEASE_TAG="${PKG_NAME}-v${CURRENT_BUILD_VERSION}"
404
+ else
405
+ NEXT_RELEASE_VERSION="v${NEXT_BUILD_VERSION}"
406
+ NEXT_RELEASE_TAG="${NEXT_RELEASE_VERSION}"
407
+ CURRENT_RELEASE_TAG="v${CURRENT_BUILD_VERSION}"
408
+ fi
409
+
410
+ if [[ -n "${GIT_LAST_PROJECT_TAG_VER}" ]]; then
411
+ RELEASE_DIFF_URL="https://github.com/${GIT_REPO_NAME}/compare/${CURRENT_RELEASE_TAG}...${NEXT_RELEASE_TAG}"
412
+ RELEASE_BODY_TITLE="[${NEXT_RELEASE_TAG}](${RELEASE_DIFF_URL}) (${CURRENT_DATE})"
413
+ else
414
+ RELEASE_BODY_TITLE="${NEXT_RELEASE_TAG} (${CURRENT_DATE})"
415
+ fi
416
+
417
+ RELEASE_BODY="# ${RELEASE_BODY_TITLE}\n${RELEASE_BODY}"
418
+ }
419
+
420
+ ##############################
421
+ ######## Handle push ########
422
+ ##############################
423
+
424
+ handle_pushes() {
425
+ log_verbose "Applying changes..."
426
+ log_verbose "Found tag commit [${CHECKOUT_SHA}]"
427
+
428
+ for plugin in "${PLUGINS[@]}"; do
429
+ log_verbose "Loading plugin \`${plugin}\`..."
430
+ local SOURCE_PLUGIN_FILE="${SCRIPT_DIR}/plugins/${plugin}.sh"
431
+ # shellcheck disable=SC1090 source=/dev/null
432
+ source "${SOURCE_PLUGIN_FILE}"
433
+ release_command=$(command -v release)
434
+ if [[ -n "${release_command}" ]]; then
435
+ release
436
+ fi
437
+ unset release
438
+ log_verbose "Applied plugin ${plugin}!"
439
+ done
440
+
441
+ log_verbose "Applied changes!"
442
+ }
443
+
444
+ ##############################
445
+ ######### Initialize #########
446
+ ##############################
447
+ parse_packages
448
+ get_git_variables
449
+ get_git_commits
450
+ handle_git_commits
451
+ handle_pushes