Browse Source
Move project-version helper procs (parse_punkproject_version, read_punkproject_version, read_changelog_latest_version) out of make.tcl inline definitions into a loadable punkboot::utils module so the parsing logic is unit-testable. - src/modules/punkboot/utils-999999.0a1.0.tm: new module (0.1.0) with the 3 procs in punkboot::utils namespace, PUNKARGS argdoc blocks. - src/modules/punkboot/utils-buildversion.txt: initial 0.1.0. - src/tests/modules/punkboot/utils/testsuites/utils/utils.test: 19 tests covering section tracking, missing fields, wrong sections, whitespace, quote styles, changelog header parsing, pre-release suffixes. All pass. - src/make.tcl: inline procs removed; projectversion block now does package require punkboot::utils and calls punkboot::utils:: procs. - Layout make.tcl files (punk.shell-0.1, punk.project-0.1): same edit; punk.basic keeps inline procs (no bootsupport tree). - include_modules.config: added punkboot::utils entry (main + 2 layouts). - Bootsupport snapshots propagated via make.tcl bootsupport. - AGENTS.md: project version bump 0.2.0 -> 0.2.1 (patch: new make.tcl projectversion subcommand is part of the product surface); added clarifying line that make.tcl command interface warrants at least a patch bump; src/project_layouts/ added to directories agents should not directly modify. - CHANGELOG.md: 0.2.1 entry. Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.aimaster
16 changed files with 837 additions and 162 deletions
@ -1,3 +1,3 @@ |
|||||||
[project] |
[project] |
||||||
name = "punkshell" |
name = "punkshell" |
||||||
version = "0.2.0" |
version = "0.2.1" |
||||||
|
|||||||
@ -0,0 +1,132 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2023 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punkboot::utils 0.1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license BSD |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
##e.g package require frobz |
||||||
|
package require Tcl 8.6- |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
namespace eval punkboot::utils { |
||||||
|
variable version 0.1.0 |
||||||
|
namespace export parse_punkproject_version read_punkproject_version read_changelog_latest_version |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::parse_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::parse_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Parse the [project] version from TOML content string"\ |
||||||
|
-help\ |
||||||
|
"Scans TOML content for the [project] section and returns |
||||||
|
the value of the version key as a string. Returns an empty |
||||||
|
string if the section or key is not found." |
||||||
|
@leaders |
||||||
|
content -type string -optional 0 -help\ |
||||||
|
"TOML content string to parse" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc parse_punkproject_version {content} { |
||||||
|
set in_project 0 |
||||||
|
foreach line [split $content \n] { |
||||||
|
set trimmed [string trim $line] |
||||||
|
if {[string index $trimmed 0] eq {[} && [string index $trimmed end] eq {]}} { |
||||||
|
set in_project [expr {$trimmed eq "\[project\]"}] |
||||||
|
continue |
||||||
|
} |
||||||
|
if {$in_project && [regexp {^version\s*=\s*"([^"]+)"} $trimmed _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::read_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the [project] version from a TOML file"\ |
||||||
|
-help\ |
||||||
|
"Reads the file at tomlfile and returns the [project] |
||||||
|
version value as a string. Returns an empty string if the |
||||||
|
file does not exist or the version is not found." |
||||||
|
@leaders |
||||||
|
tomlfile -type string -optional 0 -help\ |
||||||
|
"Path to the TOML file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_punkproject_version {tomlfile} { |
||||||
|
if {![file exists $tomlfile]} {return ""} |
||||||
|
set fh [open $tomlfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
return [::punkboot::utils::parse_punkproject_version $content] |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_changelog_latest_version |
||||||
|
@cmd -name "::punkboot::utils::read_changelog_latest_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the latest ## [X.Y.Z] version header from a changelog file"\ |
||||||
|
-help\ |
||||||
|
"Scans the changelog file from the top and returns the |
||||||
|
version string from the first '## [X.Y.Z]' header line. |
||||||
|
Returns an empty string if the file does not exist or no |
||||||
|
matching header is found." |
||||||
|
@leaders |
||||||
|
changelogfile -type string -optional 0 -help\ |
||||||
|
"Path to the changelog file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_changelog_latest_version {changelogfile} { |
||||||
|
if {![file exists $changelogfile]} {return ""} |
||||||
|
set fh [open $changelogfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
foreach line [split $content \n] { |
||||||
|
if {[regexp {^##\s*\[([0-9][^\]]*)\]} $line _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punkboot::utils ::punkboot::utils::argdoc |
||||||
|
} |
||||||
|
|
||||||
|
## Ready |
||||||
|
package provide punkboot::utils [tcl::namespace::eval punkboot::utils { |
||||||
|
variable version |
||||||
|
#- this version number, exactly 0.1.0, is a literal used in src module folders |
||||||
|
#- we refer to this sometimes as the magic version number |
||||||
|
set version 0.1.0 |
||||||
|
}] |
||||||
|
return |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2023 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punkboot::utils 999999.0a1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license BSD |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
##e.g package require frobz |
||||||
|
package require Tcl 8.6- |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
namespace eval punkboot::utils { |
||||||
|
variable version 0.1.0 |
||||||
|
namespace export parse_punkproject_version read_punkproject_version read_changelog_latest_version |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::parse_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::parse_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Parse the [project] version from TOML content string"\ |
||||||
|
-help\ |
||||||
|
"Scans TOML content for the [project] section and returns |
||||||
|
the value of the version key as a string. Returns an empty |
||||||
|
string if the section or key is not found." |
||||||
|
@leaders |
||||||
|
content -type string -optional 0 -help\ |
||||||
|
"TOML content string to parse" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc parse_punkproject_version {content} { |
||||||
|
set in_project 0 |
||||||
|
foreach line [split $content \n] { |
||||||
|
set trimmed [string trim $line] |
||||||
|
if {[string index $trimmed 0] eq {[} && [string index $trimmed end] eq {]}} { |
||||||
|
set in_project [expr {$trimmed eq "\[project\]"}] |
||||||
|
continue |
||||||
|
} |
||||||
|
if {$in_project && [regexp {^version\s*=\s*"([^"]+)"} $trimmed _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::read_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the [project] version from a TOML file"\ |
||||||
|
-help\ |
||||||
|
"Reads the file at tomlfile and returns the [project] |
||||||
|
version value as a string. Returns an empty string if the |
||||||
|
file does not exist or the version is not found." |
||||||
|
@leaders |
||||||
|
tomlfile -type string -optional 0 -help\ |
||||||
|
"Path to the TOML file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_punkproject_version {tomlfile} { |
||||||
|
if {![file exists $tomlfile]} {return ""} |
||||||
|
set fh [open $tomlfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
return [::punkboot::utils::parse_punkproject_version $content] |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_changelog_latest_version |
||||||
|
@cmd -name "::punkboot::utils::read_changelog_latest_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the latest ## [X.Y.Z] version header from a changelog file"\ |
||||||
|
-help\ |
||||||
|
"Scans the changelog file from the top and returns the |
||||||
|
version string from the first '## [X.Y.Z]' header line. |
||||||
|
Returns an empty string if the file does not exist or no |
||||||
|
matching header is found." |
||||||
|
@leaders |
||||||
|
changelogfile -type string -optional 0 -help\ |
||||||
|
"Path to the changelog file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_changelog_latest_version {changelogfile} { |
||||||
|
if {![file exists $changelogfile]} {return ""} |
||||||
|
set fh [open $changelogfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
foreach line [split $content \n] { |
||||||
|
if {[regexp {^##\s*\[([0-9][^\]]*)\]} $line _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punkboot::utils ::punkboot::utils::argdoc |
||||||
|
} |
||||||
|
|
||||||
|
## Ready |
||||||
|
package provide punkboot::utils [tcl::namespace::eval punkboot::utils { |
||||||
|
variable version |
||||||
|
#- this version number, exactly 999999.0a1.0, is a literal used in src module folders |
||||||
|
#- we refer to this sometimes as the magic version number |
||||||
|
set version 999999.0a1.0 |
||||||
|
}] |
||||||
|
return |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
0.1.0 |
||||||
|
#First line must be a semantic version number |
||||||
|
#all other lines are ignored. |
||||||
|
#0.1.0 - initial: parse_punkproject_version, read_punkproject_version, read_changelog_latest_version extracted from make.tcl inline procs |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2023 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punkboot::utils 0.1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license BSD |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
##e.g package require frobz |
||||||
|
package require Tcl 8.6- |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
namespace eval punkboot::utils { |
||||||
|
variable version 0.1.0 |
||||||
|
namespace export parse_punkproject_version read_punkproject_version read_changelog_latest_version |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::parse_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::parse_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Parse the [project] version from TOML content string"\ |
||||||
|
-help\ |
||||||
|
"Scans TOML content for the [project] section and returns |
||||||
|
the value of the version key as a string. Returns an empty |
||||||
|
string if the section or key is not found." |
||||||
|
@leaders |
||||||
|
content -type string -optional 0 -help\ |
||||||
|
"TOML content string to parse" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc parse_punkproject_version {content} { |
||||||
|
set in_project 0 |
||||||
|
foreach line [split $content \n] { |
||||||
|
set trimmed [string trim $line] |
||||||
|
if {[string index $trimmed 0] eq {[} && [string index $trimmed end] eq {]}} { |
||||||
|
set in_project [expr {$trimmed eq "\[project\]"}] |
||||||
|
continue |
||||||
|
} |
||||||
|
if {$in_project && [regexp {^version\s*=\s*"([^"]+)"} $trimmed _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::read_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the [project] version from a TOML file"\ |
||||||
|
-help\ |
||||||
|
"Reads the file at tomlfile and returns the [project] |
||||||
|
version value as a string. Returns an empty string if the |
||||||
|
file does not exist or the version is not found." |
||||||
|
@leaders |
||||||
|
tomlfile -type string -optional 0 -help\ |
||||||
|
"Path to the TOML file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_punkproject_version {tomlfile} { |
||||||
|
if {![file exists $tomlfile]} {return ""} |
||||||
|
set fh [open $tomlfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
return [::punkboot::utils::parse_punkproject_version $content] |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_changelog_latest_version |
||||||
|
@cmd -name "::punkboot::utils::read_changelog_latest_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the latest ## [X.Y.Z] version header from a changelog file"\ |
||||||
|
-help\ |
||||||
|
"Scans the changelog file from the top and returns the |
||||||
|
version string from the first '## [X.Y.Z]' header line. |
||||||
|
Returns an empty string if the file does not exist or no |
||||||
|
matching header is found." |
||||||
|
@leaders |
||||||
|
changelogfile -type string -optional 0 -help\ |
||||||
|
"Path to the changelog file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_changelog_latest_version {changelogfile} { |
||||||
|
if {![file exists $changelogfile]} {return ""} |
||||||
|
set fh [open $changelogfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
foreach line [split $content \n] { |
||||||
|
if {[regexp {^##\s*\[([0-9][^\]]*)\]} $line _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punkboot::utils ::punkboot::utils::argdoc |
||||||
|
} |
||||||
|
|
||||||
|
## Ready |
||||||
|
package provide punkboot::utils [tcl::namespace::eval punkboot::utils { |
||||||
|
variable version |
||||||
|
#- this version number, exactly 0.1.0, is a literal used in src module folders |
||||||
|
#- we refer to this sometimes as the magic version number |
||||||
|
set version 0.1.0 |
||||||
|
}] |
||||||
|
return |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2023 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punkboot::utils 0.1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license BSD |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
##e.g package require frobz |
||||||
|
package require Tcl 8.6- |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
namespace eval punkboot::utils { |
||||||
|
variable version 0.1.0 |
||||||
|
namespace export parse_punkproject_version read_punkproject_version read_changelog_latest_version |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::parse_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::parse_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Parse the [project] version from TOML content string"\ |
||||||
|
-help\ |
||||||
|
"Scans TOML content for the [project] section and returns |
||||||
|
the value of the version key as a string. Returns an empty |
||||||
|
string if the section or key is not found." |
||||||
|
@leaders |
||||||
|
content -type string -optional 0 -help\ |
||||||
|
"TOML content string to parse" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc parse_punkproject_version {content} { |
||||||
|
set in_project 0 |
||||||
|
foreach line [split $content \n] { |
||||||
|
set trimmed [string trim $line] |
||||||
|
if {[string index $trimmed 0] eq {[} && [string index $trimmed end] eq {]}} { |
||||||
|
set in_project [expr {$trimmed eq "\[project\]"}] |
||||||
|
continue |
||||||
|
} |
||||||
|
if {$in_project && [regexp {^version\s*=\s*"([^"]+)"} $trimmed _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_punkproject_version |
||||||
|
@cmd -name "::punkboot::utils::read_punkproject_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the [project] version from a TOML file"\ |
||||||
|
-help\ |
||||||
|
"Reads the file at tomlfile and returns the [project] |
||||||
|
version value as a string. Returns an empty string if the |
||||||
|
file does not exist or the version is not found." |
||||||
|
@leaders |
||||||
|
tomlfile -type string -optional 0 -help\ |
||||||
|
"Path to the TOML file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_punkproject_version {tomlfile} { |
||||||
|
if {![file exists $tomlfile]} {return ""} |
||||||
|
set fh [open $tomlfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
return [::punkboot::utils::parse_punkproject_version $content] |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punkboot::utils::read_changelog_latest_version |
||||||
|
@cmd -name "::punkboot::utils::read_changelog_latest_version"\ |
||||||
|
-summary\ |
||||||
|
"Read the latest ## [X.Y.Z] version header from a changelog file"\ |
||||||
|
-help\ |
||||||
|
"Scans the changelog file from the top and returns the |
||||||
|
version string from the first '## [X.Y.Z]' header line. |
||||||
|
Returns an empty string if the file does not exist or no |
||||||
|
matching header is found." |
||||||
|
@leaders |
||||||
|
changelogfile -type string -optional 0 -help\ |
||||||
|
"Path to the changelog file to read" |
||||||
|
}] |
||||||
|
} |
||||||
|
proc read_changelog_latest_version {changelogfile} { |
||||||
|
if {![file exists $changelogfile]} {return ""} |
||||||
|
set fh [open $changelogfile r] |
||||||
|
try { |
||||||
|
set content [read $fh] |
||||||
|
} finally { |
||||||
|
close $fh |
||||||
|
} |
||||||
|
foreach line [split $content \n] { |
||||||
|
if {[regexp {^##\s*\[([0-9][^\]]*)\]} $line _ v]} { |
||||||
|
return $v |
||||||
|
} |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punkboot::utils ::punkboot::utils::argdoc |
||||||
|
} |
||||||
|
|
||||||
|
## Ready |
||||||
|
package provide punkboot::utils [tcl::namespace::eval punkboot::utils { |
||||||
|
variable version |
||||||
|
#- this version number, exactly 0.1.0, is a literal used in src module folders |
||||||
|
#- we refer to this sometimes as the magic version number |
||||||
|
set version 0.1.0 |
||||||
|
}] |
||||||
|
return |
||||||
@ -0,0 +1,198 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Tests for punkboot::utils project-version helpers: |
||||||
|
# - parse_punkproject_version: TOML content string -> version string |
||||||
|
# - read_changelog_latest_version: changelog file -> latest ## [X.Y.Z] header |
||||||
|
# Run: tclsh src/tests/runtests.tcl -report compact -show-passes 0 -include-paths modules/punkboot/utils/** utils.test |
||||||
|
|
||||||
|
package require tcltest |
||||||
|
package require punk::lib |
||||||
|
package require punkboot::utils |
||||||
|
|
||||||
|
namespace eval ::testspace { |
||||||
|
namespace import ::tcltest::* |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Helper: write content to a temp file. Returns the file path. |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
proc write_temp_file {prefix content} { |
||||||
|
set dir [punk::lib::tempdir_newfolder -prefix $prefix] |
||||||
|
set path [file join $dir data] |
||||||
|
set fd [open $path w] |
||||||
|
try { puts -nonewline $fd $content } finally { close $fd } |
||||||
|
return $path |
||||||
|
} |
||||||
|
|
||||||
|
# ========================================================================= |
||||||
|
# parse_punkproject_version |
||||||
|
# ========================================================================= |
||||||
|
|
||||||
|
test parse_ppv_standard {Standard [project] version line} -body { |
||||||
|
set toml {[project] |
||||||
|
name = "punkshell" |
||||||
|
version = "0.2.0" |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "0.2.0" |
||||||
|
|
||||||
|
test parse_ppv_no_version_field {Missing version key in [project] section} -body { |
||||||
|
set toml {[project] |
||||||
|
name = "punkshell" |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test parse_ppv_version_in_wrong_section {Version in a non-[project] section is ignored} -body { |
||||||
|
set toml {[project] |
||||||
|
name = "punkshell" |
||||||
|
|
||||||
|
[other] |
||||||
|
version = "9.9.9" |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test parse_ppv_empty_content {Empty string returns empty} -body { |
||||||
|
::punkboot::utils::parse_punkproject_version "" |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test parse_ppv_no_section {No section header at all} -body { |
||||||
|
::punkboot::utils::parse_punkproject_version {version = "1.0.0"} |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test parse_ppv_whitespace_around_equals {Whitespace variations around = are tolerated} -body { |
||||||
|
set toml {[project] |
||||||
|
version = "0.3.1" |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "0.3.1" |
||||||
|
|
||||||
|
test parse_ppv_section_after_project {A later section does not override [project] version} -body { |
||||||
|
set toml {[project] |
||||||
|
version = "0.2.0" |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
version = "1.5.0" |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "0.2.0" |
||||||
|
|
||||||
|
test parse_ppv_singlequoted_version {Single-quoted value is not matched (TOML requires double quotes)} -body { |
||||||
|
set toml {[project] |
||||||
|
version = '0.2.0' |
||||||
|
} |
||||||
|
::punkboot::utils::parse_punkproject_version $toml |
||||||
|
} -result "" |
||||||
|
|
||||||
|
# ========================================================================= |
||||||
|
# read_punkproject_version |
||||||
|
# ========================================================================= |
||||||
|
|
||||||
|
test read_ppv_file_not_found {Non-existent file returns empty} -body { |
||||||
|
::punkboot::utils::read_punkproject_version /nonexistent/path/to/punkproject.toml |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test read_ppv_file_standard {Read a real-ish TOML file} -body { |
||||||
|
set path [write_temp_file ppvread {[project] |
||||||
|
name = "punkshell" |
||||||
|
version = "1.2.3" |
||||||
|
}] |
||||||
|
set result [::punkboot::utils::read_punkproject_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "1.2.3" |
||||||
|
|
||||||
|
# ========================================================================= |
||||||
|
# read_changelog_latest_version |
||||||
|
# ========================================================================= |
||||||
|
|
||||||
|
test read_clv_standard {Standard changelog with one entry} -body { |
||||||
|
set content {# Changelog |
||||||
|
|
||||||
|
## [0.2.0] - 2026-07-05 |
||||||
|
|
||||||
|
- Initial tracking. |
||||||
|
} |
||||||
|
set path [write_temp_file clvstd $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "0.2.0" |
||||||
|
|
||||||
|
test read_clv_multiple_entries {First (newest) header returned when multiple exist} -body { |
||||||
|
set content {# Changelog |
||||||
|
|
||||||
|
## [0.2.1] - 2026-07-06 |
||||||
|
|
||||||
|
- New subcommand. |
||||||
|
|
||||||
|
## [0.2.0] - 2026-07-05 |
||||||
|
|
||||||
|
- Initial tracking. |
||||||
|
} |
||||||
|
set path [write_temp_file clvmulti $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "0.2.1" |
||||||
|
|
||||||
|
test read_clv_file_not_found {Non-existent file returns empty} -body { |
||||||
|
::punkboot::utils::read_changelog_latest_version /nonexistent/path/to/CHANGELOG.md |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test read_clv_empty_file {Empty file returns empty} -body { |
||||||
|
set path [write_temp_file clvempty ""] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test read_clv_no_headers {File with no ## [X.Y.Z] headers returns empty} -body { |
||||||
|
set content {# Changelog |
||||||
|
|
||||||
|
Some prose without a version header. |
||||||
|
} |
||||||
|
set path [write_temp_file clvnohdr $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test read_clv_extra_spaces {Extra whitespace after ## is tolerated} -body { |
||||||
|
set content {## [0.5.0] - 2026-08-01} |
||||||
|
set path [write_temp_file clvspc $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "0.5.0" |
||||||
|
|
||||||
|
test read_clv_prerelease_suffix {Version with pre-release suffix is captured} -body { |
||||||
|
set content {## [0.2.1-rc1] - 2026-07-06} |
||||||
|
set path [write_temp_file clvpre $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "0.2.1-rc1" |
||||||
|
|
||||||
|
test read_clv_single_hash {Single # header is not matched (only ##)} -body { |
||||||
|
set content {# [0.1.0] this is a single hash} |
||||||
|
set path [write_temp_file clv1h $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "" |
||||||
|
|
||||||
|
test read_clv_triple_hash {Triple ### subheader is not matched} -body { |
||||||
|
set content {## [0.2.0] - 2026-07-05 |
||||||
|
|
||||||
|
### Fixed |
||||||
|
|
||||||
|
- something |
||||||
|
} |
||||||
|
set path [write_temp_file clv3h $content] |
||||||
|
set result [::punkboot::utils::read_changelog_latest_version $path] |
||||||
|
file delete -force [file dirname $path] |
||||||
|
set result |
||||||
|
} -result "0.2.0" |
||||||
|
|
||||||
|
tcltest::cleanupTests |
||||||
|
} |
||||||
Loading…
Reference in new issue