@@ -8,11 +8,11 @@ set -Eeuo pipefail
88# 🔧 CONFIG
99# ######################################
1010SCRIPT_NAME=" $( basename " $0 " ) "
11- SCRIPT_VERSION=" 2.5.0 "
11+ SCRIPT_VERSION=" 2.6.1 "
1212SCRIPT_AUTHOR=" Federico Girolami"
1313SCRIPT_TEAM=" Pytorchia™ Developers"
1414SCRIPT_LICENSE=" MIT License"
15- SCRIPT_DESCRIPTION=" Create a GitHub Release from package.json version, with guard-checks, optional auto-commit, tests, build, version bump, push + tags, and optional npm publish."
15+ SCRIPT_DESCRIPTION=" Create a GitHub Release from package.json version, with guard-checks, optional auto-commit, tests, build, version bump, push + tags, optional npm publish, and changelog extraction from README ."
1616REPO_ROOT_DIR=" ./"
1717
1818# Release
@@ -37,19 +37,23 @@ COMMIT_FILES=() # se vuoi selezionare file specifici
3737COMMIT_MESSAGE=" " # messaggio commit, se vuoto verrà chiesto
3838
3939RUN_TESTS=false
40- TEST_SCRIPT=" test:samples" # es . test o test:samples
40+ TEST_SCRIPT=" test:samples" # es. test o test:samples
4141
4242RUN_BUILD=false
4343BUILD_SCRIPT=" build"
4444
4545BUMP_KIND=" none" # none | patch | minor | major | prepatch | preminor | premajor | prerelease
46- BUMP_PREID=" " # es . alpha , beta , rc
46+ BUMP_PREID=" " # es. alpha , beta , rc
4747
4848PUSH_AFTER=true
4949
5050NPM_PUBLISH=false
5151NPM_ACCESS=" public" # public | restricted
52- NPM_TAG=" " # es . next
52+ NPM_TAG=" " # es. latest / next
53+
54+ # Changelog from README
55+ CHANGELOG_FROM_README=false
56+ CHANGELOG_FILE=" README.md"
5357
5458# ######################################
5559# 🎨 ANSI
@@ -95,11 +99,12 @@ die() {
9599}
96100
97101run () {
102+ # esegue i comandi preservando gli argomenti (niente eval)
98103 local -a cmd=(" $@ " )
99104 if [[ " $DRY_RUN " == true ]]; then
100105 warn " DRY-RUN → ${cmd[*]} "
101106 else
102- dbg " exec → ${cmd[*]} "
107+ # dbg "exec → ${cmd[*]}"
103108 " ${cmd[@]} "
104109 fi
105110}
@@ -112,7 +117,7 @@ trap 'err "Errore a riga $LINENO . Uscita ." ' ERR
112117banner () {
113118 cat << EOF
114119${GOLD}${BOLD} ┌──────────────────────────────────────────────────────────┐${RESET}
115- ${GOLD}${BOLD} │ ${SOFT} Pytorchia™ Release Helper${RESET}${GOLD}${BOLD} │${RESET}
120+ ${GOLD}${BOLD} │ ${SOFT} Pytorchia™ Release Helper${RESET}${GOLD}${BOLD} │${RESET}
116121${GOLD}${BOLD} └──────────────────────────────────────────────────────────┘${RESET}
117122${BOLD} $SCRIPT_NAME ${RESET}${ITA} v$SCRIPT_VERSION ${RESET} ${DIM} by $SCRIPT_AUTHOR · $SCRIPT_TEAM ${RESET}
118123${DIM} License:${RESET} $SCRIPT_LICENSE
@@ -142,39 +147,60 @@ ${BOLD}Flow / Guardie${RESET} :
142147 -d , --debug Log verboso
143148 --force Bypassa guardie
144149 --allow-dirty-tag Consente tag con tree sporco
145- --auto-chain Se tree sporco , esegui catena commit → test → build → bump → push
150+ --auto-chain Se tree sporco: commit → test → build → bump → push
151+ --auto-chain-nobump Se tree sporco: commit → test → build → push
146152
147153${BOLD} Commit / Test / Build${RESET} :
148- --commit-files "A B" File da aggiungere al commit ( disattiva --commit-all )
149- --no-commit-all Non usare git add -A ( default è on )
154+ --commit-files "A B" File per il commit (disattiva --commit-all)
155+ --no-commit-all Non usare git add -A
150156 --commit-message "msg" Messaggio commit
151- --run-tests Esegui test ( npm run <script> )
152- --test-script <name> Script test ( default : $TEST_SCRIPT )
153- --run-build Esegui build ( npm run <script> )
154- --build-script <name> Script build ( default : $BUILD_SCRIPT )
157+ --run-tests Esegui test (npm run <script>)
158+ --test-script <name> Script test (default: test:samples )
159+ --run-build Esegui build (npm run <script>)
160+ --build-script <name> Script build (default: build )
155161
156162${BOLD} Version bump${RESET} :
157163 --bump <kind> none | patch | minor | major | prepatch | preminor | premajor | prerelease
158- --preid <id> Pre-id per prerelease , es . alpha , beta , rc
164+ --preid <id> Pre-id per prerelease ( alpha/ beta/rc)
159165
160166${BOLD} Push / Publish${RESET} :
161- --no-push Non pushare dopo commit / bump
167+ --no-push Non pushare dopo commit/ bump
162168 --npm-publish Pubblica su npm
163- --npm-access <mode> public | restricted ( default : $NPM_ACCESS )
164- --npm-tag <tag> npm dist-tag , es . next
169+ --npm-access <mode> public | restricted (default: public)
170+ --npm-tag <tag> npm dist-tag (es. latest/next)
171+
172+ ${BOLD} Changelog${RESET} :
173+ --changelog-from-readme Estrai le note dal README per la versione (### vX.Y.Z o ### X.Y.Z)
174+ --changelog-file <path> README alternativo (default: README.md)
175+
176+ ${BOLD} Preset${RESET} :
177+ --preset node-lib Abilita: --auto-chain --run-tests --run-build --bump patch --npm-publish --npm-access public --npm-tag latest --yes
165178
166179${BOLD} Varie${RESET} :
167180 -V , --version-script Stampa versione script
168181 -h , --help Aiuto
169182
170183${BOLD} Esempio${RESET} :
171- $SCRIPT_NAME --auto-chain --run-tests --run-build --bump patch --npm-publish --yes
184+ $SCRIPT_NAME --preset node-lib --version 1.0.12 --changelog-from-readme --assets "dist/*"
172185EOF
173186}
174187
175188# ######################################
176- # 🧮 Arg parsing
189+ # 🧮 Arg parsing + preset
177190# ######################################
191+ set_preset_node_lib () {
192+ AUTO_CHAIN=true
193+ RUN_TESTS=true
194+ TEST_SCRIPT=" test:samples"
195+ RUN_BUILD=true
196+ BUILD_SCRIPT=" build"
197+ BUMP_KIND=" patch"
198+ NPM_PUBLISH=true
199+ NPM_ACCESS=" public"
200+ NPM_TAG=" latest"
201+ YES_ALL=true
202+ }
203+
178204parse_args () {
179205 while (( $# )) ; do
180206 case " $1 " in
@@ -238,7 +264,11 @@ parse_args() {
238264 AUTO_CHAIN=true
239265 shift
240266 ;;
241-
267+ --auto-chain-nobump)
268+ AUTO_CHAIN=true
269+ BUMP_KIND=" none"
270+ shift
271+ ;;
242272 --commit-files)
243273 IFS=' ' read -r -a COMMIT_FILES <<< " ${2:?}"
244274 COMMIT_ALL=false
@@ -296,6 +326,22 @@ parse_args() {
296326 shift 2
297327 ;;
298328
329+ --changelog-from-readme)
330+ CHANGELOG_FROM_README=true
331+ shift
332+ ;;
333+ --changelog-file)
334+ CHANGELOG_FILE=" ${2:? } "
335+ shift 2
336+ ;;
337+
338+ --preset)
339+ case " ${2:? } " in
340+ node-lib) set_preset_node_lib ;;
341+ * ) die " Preset sconosciuto: $2 " ;;
342+ esac
343+ shift 2
344+ ;;
299345 --)
300346 shift
301347 break
@@ -306,7 +352,7 @@ parse_args() {
306352}
307353
308354# ######################################
309- # 🧪 Guard-checks
355+ # 🧪 Guard-checks / helpers
310356# ######################################
311357require_bin () { command -v " $1 " > /dev/null 2>&1 || die " Manca dipendenza : $1 " ; }
312358
@@ -380,13 +426,9 @@ resolve_version() {
380426 is_semver " $VERSION_TAG " || die " Versione non in formato semver : $VERSION_TAG "
381427}
382428
383- detect_prerelease_flag () {
384- [[ " $VERSION_TAG " == * " -" * ]] && echo " --prerelease" || echo " "
385- }
429+ detect_prerelease_flag () { [[ " $VERSION_TAG " == * " -" * ]] && echo " --prerelease" || echo " " ; }
386430
387- tag_exists () {
388- git -C " $REPO_ROOT_DIR " show-ref --tags --verify --quiet " refs/tags/v$VERSION_TAG "
389- }
431+ tag_exists () { git -C " $REPO_ROOT_DIR " show-ref --tags --verify --quiet " refs/tags/v$VERSION_TAG " ; }
390432
391433ensure_tag () {
392434 if tag_exists; then
@@ -402,6 +444,33 @@ ensure_tag() {
402444 fi
403445}
404446
447+ # ######################################
448+ # 🧾 Changelog extraction (README)
449+ # ######################################
450+ extract_changelog_from_readme () {
451+ local file=" $1 " ver=" $2 "
452+ [[ -f " $file " ]] || die " README non trovato: $file "
453+
454+ # Estrae dalla riga '### vX.Y.Z' o '### X.Y.Z' fino alla riga della prossima '### '
455+ local content
456+ content=" $( awk -v ver=" $ver " '
457+ BEGIN{ start=0 }
458+ # Se abbiamo iniziato e troviamo una nuova sezione ###, fermiamo l output
459+ start==1 && $0 ~ /^###[[:space:]]/ { exit }
460+ # Riconosci l intestazione della versione (con o senza v)
461+ $0 ~ "^###[[:space:]]*(v)?" ver "[[:space:]]*$" { start=1; print; next }
462+ # Se siamo all interno della sezione, stampa
463+ start==1 { print }
464+ ' " $file " ) "
465+
466+ if [[ -z " $content " ]]; then
467+ warn " Nessuna sezione changelog trovata per ${ver} in $file (cerco anche v${ver} )"
468+ echo " "
469+ else
470+ echo " $content "
471+ fi
472+ }
473+
405474# ######################################
406475# 🔗 Catena su tree sporco
407476# ######################################
@@ -417,12 +486,12 @@ auto_chain_if_needed() {
417486 fi
418487
419488 if [[ " $AUTO_CHAIN " != true ]]; then
420- die " Working tree sporco . Committa o usa --allow-dirty-tag / --force . ( oppure passa --auto-chain )"
489+ die " Working tree sporco . Committa o usa --allow-dirty-tag / --force . (oppure passa --auto-chain)"
421490 fi
422491
423492 info " Avvio catena automatica su working tree sporco …"
424493
425- # 1 . Commit
494+ # Commit
426495 if [[ -z " $COMMIT_MESSAGE " ]]; then
427496 if [[ " $YES_ALL " == true ]]; then
428497 COMMIT_MESSAGE=" chore: prepare release"
@@ -442,34 +511,32 @@ auto_chain_if_needed() {
442511
443512 run git -C " $REPO_ROOT_DIR " commit -m " $COMMIT_MESSAGE " || warn " Nessuna modifica da committare"
444513
445- # 2 . Tests
514+ # Tests
446515 if [[ " $RUN_TESTS " == true ]]; then
447516 info " Eseguo test : npm run $TEST_SCRIPT "
448517 run bash -lc " cd \" $REPO_ROOT_DIR \" && npm run \" $TEST_SCRIPT \" "
449518 fi
450519
451- # 3 . Build
520+ # Build
452521 if [[ " $RUN_BUILD " == true ]]; then
453522 info " Eseguo build : npm run $BUILD_SCRIPT "
454523 run bash -lc " cd \" $REPO_ROOT_DIR \" && npm run \" $BUILD_SCRIPT \" "
455524 fi
456525
457- # 4 . Bump
526+ # Bump
458527 if [[ " $BUMP_KIND " != " none" ]]; then
459- local -a bump_cmd=(npm version " $BUMP_KIND " )
460- [[ -n " $BUMP_PREID " ]] && bump_cmd+=(--preid " $BUMP_PREID " )
461- bump_cmd+=(-m " chore(release): v%s — $COMMIT_MESSAGE " )
462- info " Bump versione : ${bump_cmd[*]} "
463- run bash -lc " cd \" $REPO_ROOT_DIR \" && ${bump_cmd[*]} "
464-
465- # aggiorna VERSION_TAG dalla nuova package.json
528+ local bump_cmd=" npm version \" $BUMP_KIND \" "
529+ [[ -n " $BUMP_PREID " ]] && bump_cmd+=" --preid \" $BUMP_PREID \" "
530+ bump_cmd+=" -m \" chore(release): v%s — $COMMIT_MESSAGE \" "
531+ info " Bump versione : $bump_cmd "
532+ run bash -lc " cd \" $REPO_ROOT_DIR \" && $bump_cmd "
466533 VERSION_TAG=" $( get_pkg_version) "
467534 ok " Nuova versione : v$VERSION_TAG "
468535 else
469536 info " Bump versione disattivato"
470537 fi
471538
472- # 5 . Push
539+ # Push
473540 if [[ " $PUSH_AFTER " == true ]]; then
474541 run git -C " $REPO_ROOT_DIR " push
475542 run git -C " $REPO_ROOT_DIR " push --tags || true
@@ -486,40 +553,61 @@ do_release() {
486553 require_bin node
487554 require_bin npm
488555
489- REPO_ROOT_DIR=" ${REPO_ROOT_DIR:- $(find_git_root)} "
556+ if [[ " $REPO_ROOT_DIR " == " ./" || -z " $REPO_ROOT_DIR " ]]; then
557+ REPO_ROOT_DIR=" $( find_git_root) "
558+ fi
490559 [[ -z " $REPO_ROOT_DIR " ]] && die " Non è stata trovata una repo Git valida"
491560 assert_repo
492561 cd " $REPO_ROOT_DIR "
493-
494562 assert_branch
495- # Se serve , esegue commit → test → build → bump → push
563+
564+ # Se serve, esegue commit → test → build → bump → push
496565 auto_chain_if_needed
497566
498- # Se non abbiamo bumpato , risolvi la versione attuale
567+ # Se non abbiamo bumpato, risolvi la versione corrente
499568 [[ -z " $VERSION_TAG " ]] && resolve_version
500569
570+ # Changelog extraction (se richiesto e non già passato via --notes/--notes-file)
571+ if [[ " $CHANGELOG_FROM_README " == true && -z " $RELEASE_NOTES " && -z " $RELEASE_NOTES_FILE " ]]; then
572+ local file_path=" $CHANGELOG_FILE "
573+ [[ ! -f " $file_path " ]] && file_path=" $REPO_ROOT_DIR /$CHANGELOG_FILE "
574+ local md
575+ md=" $( extract_changelog_from_readme " $file_path " " $VERSION_TAG " ) "
576+ if [[ -n " $md " ]]; then
577+ RELEASE_NOTES=" $md "
578+ ok " Note di rilascio estratte da $file_path → sezione ${BOLD} ### v$VERSION_TAG ${RESET} "
579+ else
580+ warn " Impossibile estrarre note da README . Valuta --notes-file o --generate-notes"
581+ fi
582+ fi
583+
501584 read_notes
502585 local preflag
503586 preflag=" $( detect_prerelease_flag) "
504587
505- # Tag GH : se npm version ha già creato il tag , lo vedremo esistente
588+ # Tag GH: se npm version ha già creato il tag, lo vedremo esistente
506589 ensure_tag
507590
508- # Assets
591+ # Assets (compat bash 3.2; niente mapfile, niente echo; safe con spazi)
509592 local -a assets_arg=()
510593 if [[ -n " $ASSETS_GLOB " ]]; then
511- shopt -s nullglob
512- mapfile -t files < <( cd " $REPO_ROOT_DIR " && compgen -G " $ASSETS_GLOB " || true)
513- shopt -u nullglob
514- if (( ${# files[@]} )) ; then
515- for f in " ${files[@]} " ; do assets_arg+=(--attach " $f " ); done
516- info " Allegati : ${files[*]} "
594+ # compgen -G espande il glob in maniera robusta (0..N righe, una per match)
595+ local -a matches=()
596+ while IFS= read -r line; do
597+ matches+=(" $line " )
598+ done < <( compgen -G -- " $ASSETS_GLOB " )
599+
600+ if (( ${# matches[@]} )) ; then
601+ for f in " ${matches[@]} " ; do
602+ assets_arg+=(--attach " $f " )
603+ done
604+ info " Allegati : ${matches[*]} "
517605 else
518606 warn " Nessun file combacia con glob : $ASSETS_GLOB "
519607 fi
520608 fi
521609
522- # Note
610+ # Notes
523611 local -a notes_arg=()
524612 if [[ -n " $RELEASE_NOTES " ]]; then
525613 notes_arg=(--notes " $RELEASE_NOTES " )
@@ -542,10 +630,10 @@ do_release() {
542630
543631 # NPM publish opzionale
544632 if [[ " $NPM_PUBLISH " == true ]]; then
545- local -a pub=( npm publish --access " $NPM_ACCESS " )
546- [[ -n " $NPM_TAG " ]] && pub+=( --tag " $NPM_TAG " )
547- info " Pubblico su npm : ${ pub[*]} "
548- run bash -lc " cd \" $REPO_ROOT_DIR \" && ${ pub[*]} "
633+ local pub=" npm publish --access \ "$NPM_ACCESS \" "
634+ [[ -n " $NPM_TAG " ]] && pub+=" --tag \ "$NPM_TAG \" "
635+ info " Pubblico su npm : $pub "
636+ run bash -lc " cd \" $REPO_ROOT_DIR \" && $pub "
549637 ok " npm publish completato"
550638 fi
551639}
@@ -557,5 +645,5 @@ main() {
557645 parse_args " $@ "
558646 do_release
559647}
560-
561648main " $@ "
649+ # ######################################
0 commit comments