Browse Source
Stage 4 - documented overlay semantics implemented (commandset::layout 0.2.0): layout_materialize resolves a layout to its overlay chain (vendor tier then custom tier per store+vendor, <name>@<base> derived-layout folders expanding their base chain first, cycle-guarded) and composes multi-folder chains into a disposable staging dir applying <item>.anti deletion markers (zero-byte marker files recommended - committable, unlike the originally documented empty marker folders, which are also accepted). Single-folder chains with no markers are used in place, so punk.project (store and zipfs modpod) behaviour is unchanged. project.new (commandset::project 0.4.0) copies from the materialized path and the layout listing/scanning commands show the effective merged view. Fixtures: punk.othersample is now a live derived layout (new custom-deck fauxlink ref + .gitignore.anti demo marker); extra.txt/store README rewritten to the implemented semantics. New tcltest suite src/tests/modules/punk/mix/testsuites/layout/materialize.test (20 tests). Anti markers deliberately stay a plain filename convention rather than tag-bearing fauxlinks: a fauxlink is a reference tooling may follow (fcat auto-follows), a marker is a tombstone with no target. Stage 5 - retirements and generated-project version seeding: - The empty unimplemented child-side src/PROJECT_LAYOUTS_CREATE_FROM_... marker folder is deleted along with its project.new and make.tcl sync exclusions. - The vendor/custom x _project matrix collapses for layouts: custom/<vendor> is the one override level; _project folders join no overlay chain (the pseudo-vendor remains for decktemplates module/utility templates only). - project.new seeds generated projects post-materialization (only-if-missing, never from payload): punkproject.toml ([project] name + 0.1.0), a matching CHANGELOG.md ## [0.1.0] header, and punkorigin.toml - an interim generation provenance baseline (utc, layout name + resolved store target, punk::mix::templates version, punkshell version when derivable, origin path) for the G-027 pull design to adopt (noted in G-027). Also: script-mode/programmatic project.new robustness (explicit shellrun require; punk::mix::util askuser/do_in_path/foreach-file fully qualified - the util:: shorthand exists only after cli commandset import). Includes the concurrently-landed (other session, previously uncommitted) 0.3.1 early module-name validation fix in project.new, shipped within the 0.4.0 bump. G-087 flipped achieved 2026-07-19 (verification in the detail file; archive move follows in the next commit). punkshell 0.16.0 -> 0.17.0. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
19 changed files with 1193 additions and 100 deletions
@ -1,4 +1,4 @@ |
|||||||
[project] |
[project] |
||||||
name = "punkshell" |
name = "punkshell" |
||||||
version = "0.16.0" |
version = "0.17.0" |
||||||
license = "BSD-2-Clause" |
license = "BSD-2-Clause" |
||||||
|
|||||||
@ -1,4 +1,6 @@ |
|||||||
0.1.1 |
0.2.0 |
||||||
#First line must be a semantic version number |
#First line must be a semantic version number |
||||||
#all other lines are ignored. |
#all other lines are ignored. |
||||||
|
#0.2.0 - G-087 stage 4: overlay merge primitive in lib - layout_materialize (entry point returning the effective content folder; fast path returns the store folder untouched for single-source chains with no .anti markers), layout_resolve_chain (vendor-then-custom tier chain with <name>@<base> derived-layout expansion, cycle-guarded), layout_stage_chain (staging-dir composition applying .anti deletion markers; #layout_staging marker guards against clobbering non-staging dirs), plus private helpers _dir_entries/_find_anti_relpaths/_merge_copy_into. The _project pseudo-vendor participates in no chain (retired for layouts - G-087 stage 5 decision). |
||||||
|
#0.2.0 - new lib::layoutfolder_scan_for_template_files (path-based core of layout_scan_for_template_files); layout_scan_for_template_files, layout_all_files and templatefiles.relative now operate on the overlay-materialized content rather than the primary store folder only (the staging marker file is excluded from listings/scans). |
||||||
#0.1.1 - fix: no-handler warning paths in collection::references_as_dict and lib::layouts_dict used 'put' instead of 'puts' and then errored on an unset dict variable; they now warn correctly and return an empty dict. references_as_dict warning message no longer misattributes itself to layouts_dict. |
#0.1.1 - fix: no-handler warning paths in collection::references_as_dict and lib::layouts_dict used 'put' instead of 'puts' and then errored on an unset dict variable; they now warn correctly and return an empty dict. references_as_dict warning message no longer misattributes itself to layouts_dict. |
||||||
|
|||||||
@ -1,11 +1,33 @@ |
|||||||
example of an additional file added to a vendor layout |
example of an additional file added to a vendor layout |
||||||
|
|
||||||
All files/folders from the vendor/layoutname tree are included unless they are over-ridden by a file of the same name in custom/vendor/layoutname or unless there is a file/folder of the same name except suffixed with .anti |
Overlay/merge semantics (implemented by punk::mix::commandset::layout::lib::layout_materialize, |
||||||
|
used by dev project.new - G-087 stage 4): |
||||||
For example, to completely replace src/modules in a vendor layout; |
|
||||||
create an empty custom/vendor/layoutname/src/modules.anti folder |
All files/folders from the vendor/<vendorname>/<layoutname> tree are included unless they are |
||||||
Then create a custom/vendor/layoutname/src/modules folder with your custom contents. |
over-ridden by a file of the same name in custom/<vendorname>/<layoutname> or deleted by a |
||||||
|
marker of the same name suffixed with .anti. Same-name folders merge recursively; the layer |
||||||
To just override a single module in vendor/layoutname/src/modules - just put a replacement module with the same name (or named with a later version) in custom/vendor/layoutname/src/modules. |
order is vendor then custom, so custom content wins. |
||||||
All the existing modules from vendor/layoutname/src/modules will then be present in the resulting layout along with your modified/new module. |
|
||||||
|
.anti markers: |
||||||
|
- A zero-byte FILE named <item>.anti is the recommended marker form (empty folders cannot be |
||||||
|
committed to git/fossil). A marker FOLDER named <item>.anti is also accepted, matching the |
||||||
|
originally documented form. Either deletes <item> - file or folder - as contributed by |
||||||
|
lower layers, and the marker itself never appears in the composed layout. |
||||||
|
- To completely replace src/modules from a vendor layout: put a zero-byte |
||||||
|
custom/<vendorname>/<layoutname>/src/modules.anti marker file alongside a |
||||||
|
custom/<vendorname>/<layoutname>/src/modules folder with your custom contents. The marker |
||||||
|
is applied before the same layer's own content is copied. |
||||||
|
- To just override a single module in vendor/<layoutname>/src/modules - put a replacement |
||||||
|
module with the same name in custom/<vendorname>/<layoutname>/src/modules. All the existing |
||||||
|
modules from the vendor layer will then be present in the resulting layout along with your |
||||||
|
modified/new module. |
||||||
|
|
||||||
|
Derived layouts: |
||||||
|
- A folder named <newname>@<baselayoutname> presents as its own layout (given a layout ref |
||||||
|
naming it). Its content starts from the base layout's full overlay chain with the derived |
||||||
|
folder's own content (and .anti markers) applied on top. See ../othersample@sample-0.1 for |
||||||
|
a working example (it also demonstrates a .anti deletion). |
||||||
|
|
||||||
|
Composition happens in a disposable staging directory at generation time - the store folders |
||||||
|
are never modified, and a layout whose chain is a single folder with no .anti markers is used |
||||||
|
in place without any copying. |
||||||
|
|||||||
@ -0,0 +1,370 @@ |
|||||||
|
package require tcltest |
||||||
|
|
||||||
|
package require punk::mix::commandset::layout |
||||||
|
|
||||||
|
#added 2026-07-19 (agent, G-087) - stage 4 overlay merge primitive: layout_resolve_chain / |
||||||
|
#layout_stage_chain / layout_materialize in punk::mix::commandset::layout::lib, exercised |
||||||
|
#against synthetic stores (no punk.templates capability context needed - layout_materialize |
||||||
|
#is fed -layoutinfo directly). The repo fixtures (vendor/custom punk/sample-0.1 + |
||||||
|
#othersample@sample-0.1) are exercised end-to-end via dev project.new - see the G-087 detail |
||||||
|
#file verification records. |
||||||
|
|
||||||
|
namespace eval ::testspace { |
||||||
|
namespace import ::tcltest::* |
||||||
|
|
||||||
|
variable BASE [makeDirectory g087_materialize] |
||||||
|
|
||||||
|
proc fwrite {path content} { |
||||||
|
file mkdir [file dirname $path] |
||||||
|
set fd [open $path w] |
||||||
|
fconfigure $fd -translation lf |
||||||
|
puts -nonewline $fd $content |
||||||
|
close $fd |
||||||
|
} |
||||||
|
proc fread {path} { |
||||||
|
set fd [open $path r] |
||||||
|
set data [read $fd] |
||||||
|
close $fd |
||||||
|
return $data |
||||||
|
} |
||||||
|
#relative sorted listing of all files (not dirs) below a folder - forward slashes |
||||||
|
proc treefiles {folder} { |
||||||
|
set files [list] |
||||||
|
set queue [list ""] |
||||||
|
while {[llength $queue]} { |
||||||
|
set rel [lindex $queue 0] |
||||||
|
set queue [lrange $queue 1 end] |
||||||
|
set here [expr {$rel eq "" ? $folder : [file join $folder $rel]}] |
||||||
|
foreach entry [lsort -unique [glob -nocomplain -dir $here -tails * .*]] { |
||||||
|
if {$entry in {. ..}} {continue} |
||||||
|
set relentry [expr {$rel eq "" ? $entry : "$rel/$entry"}] |
||||||
|
if {[file isdirectory [file join $here $entry]]} { |
||||||
|
lappend queue $relentry |
||||||
|
} else { |
||||||
|
lappend files $relentry |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return [lsort $files] |
||||||
|
} |
||||||
|
#build a fresh synthetic store under $BASE/<name> and return its path |
||||||
|
proc make_store {name} { |
||||||
|
variable BASE |
||||||
|
set store [file join $BASE $name] |
||||||
|
file delete -force $store |
||||||
|
file mkdir $store |
||||||
|
return $store |
||||||
|
} |
||||||
|
|
||||||
|
variable common { |
||||||
|
set result "" |
||||||
|
} |
||||||
|
|
||||||
|
# -- --- --- layout_resolve_chain --- --- -- |
||||||
|
|
||||||
|
test resolve_chain_vendor_only {single vendor folder yields single-entry chain}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s1] |
||||||
|
file mkdir $store/vendor/punk/basic |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk basic] |
||||||
|
lappend result [llength $chain] |
||||||
|
lappend result [expr {[lindex $chain 0] eq [file join $store vendor punk basic]}] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 1 1] |
||||||
|
|
||||||
|
test resolve_chain_custom_over_vendor {vendor tier precedes custom tier in chain order}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s2] |
||||||
|
file mkdir $store/vendor/punk/sample $store/custom/punk/sample |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
lappend result [llength $chain] |
||||||
|
lappend result [expr {[lindex $chain 0] eq [file join $store vendor punk sample]}] |
||||||
|
lappend result [expr {[lindex $chain 1] eq [file join $store custom punk sample]}] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 2 1 1] |
||||||
|
|
||||||
|
test resolve_chain_derived {name@base folder expands base chain first, derived folder last}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s3] |
||||||
|
file mkdir $store/vendor/punk/sample $store/custom/punk/sample "$store/custom/punk/other@sample" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk other@sample] |
||||||
|
lappend result [llength $chain] |
||||||
|
lappend result [expr {[lindex $chain 0] eq [file join $store vendor punk sample]}] |
||||||
|
lappend result [expr {[lindex $chain 1] eq [file join $store custom punk sample]}] |
||||||
|
lappend result [expr {[lindex $chain 2] eq [file join $store custom punk other@sample]}] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 3 1 1 1] |
||||||
|
|
||||||
|
test resolve_chain_recursive_base {derivation chains recurse (a@b where b is b@c)}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s4] |
||||||
|
file mkdir $store/vendor/punk/c "$store/custom/punk/b@c" "$store/custom/punk/a@b" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk a@b] |
||||||
|
lappend result [llength $chain] |
||||||
|
lappend result [expr {[lindex $chain 0] eq [file join $store vendor punk c]}] |
||||||
|
lappend result [expr {[lindex $chain 1] eq [file join $store custom punk b@c]}] |
||||||
|
lappend result [expr {[lindex $chain 2] eq [file join $store custom punk a@b]}] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 3 1 1 1] |
||||||
|
|
||||||
|
test resolve_chain_cycle {derivation cycle terminates with each folder listed once}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s5] |
||||||
|
file mkdir "$store/custom/punk/a@b" "$store/custom/punk/b@a" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk a@b] |
||||||
|
#cycle guard stops the b->a expansion; both folders appear exactly once |
||||||
|
lappend result [llength $chain] |
||||||
|
lappend result [llength [lsort -unique $chain]] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 2 2] |
||||||
|
|
||||||
|
test resolve_chain_missing {nonexistent layout name yields empty chain}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s6] |
||||||
|
file mkdir $store/vendor/punk |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk nosuch] |
||||||
|
llength $chain |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result 0 |
||||||
|
|
||||||
|
# -- --- --- layout_stage_chain --- --- -- |
||||||
|
|
||||||
|
test stage_override_and_add {custom same-name file wins, extra file added, folders merge}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s7] |
||||||
|
fwrite $store/vendor/punk/sample/README.md "vendor-readme" |
||||||
|
fwrite $store/vendor/punk/sample/src/modules/mod.txt "vendor-mod" |
||||||
|
fwrite $store/custom/punk/sample/README.md "custom-readme" |
||||||
|
fwrite $store/custom/punk/sample/extra.txt "extra" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
set sres [punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging] |
||||||
|
lappend result [fread $staging/README.md] |
||||||
|
lappend result [fread $staging/extra.txt] |
||||||
|
lappend result [fread $staging/src/modules/mod.txt] |
||||||
|
lappend result [dict get $sres copied] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list custom-readme extra vendor-mod 4] |
||||||
|
|
||||||
|
test stage_anti_file_deletes_file {zero-byte <name>.anti file marker deletes lower-layer file}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s8] |
||||||
|
fwrite $store/vendor/punk/sample/.gitignore "ignore-content" |
||||||
|
fwrite $store/vendor/punk/sample/keep.txt "keep" |
||||||
|
file mkdir $store/custom/punk/sample |
||||||
|
fwrite $store/custom/punk/sample/.gitignore.anti "" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
set sres [punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging] |
||||||
|
lappend result [file exists $staging/.gitignore] |
||||||
|
lappend result [file exists $staging/keep.txt] |
||||||
|
lappend result [dict get $sres deletions] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 1 .gitignore] |
||||||
|
|
||||||
|
test stage_anti_markers_never_copied {no .anti entries appear in the composed staging}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s9] |
||||||
|
fwrite $store/vendor/punk/sample/a.txt "a" |
||||||
|
fwrite $store/custom/punk/sample/a.txt.anti "" |
||||||
|
fwrite $store/custom/punk/sample/sub/b.txt.anti "" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
set antis [list] |
||||||
|
foreach f [treefiles $staging] { |
||||||
|
if {[string match *.anti $f]} {lappend antis $f} |
||||||
|
} |
||||||
|
lappend result $antis |
||||||
|
lappend result [file exists $staging/a.txt] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list {} 0] |
||||||
|
|
||||||
|
test stage_anti_folder_replace {folder.anti marker plus replacement folder in same layer fully replaces}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s10] |
||||||
|
fwrite $store/vendor/punk/sample/src/modules/old1.txt "old1" |
||||||
|
fwrite $store/vendor/punk/sample/src/modules/old2.txt "old2" |
||||||
|
#marker as zero-byte FILE (recommended - committable), replacement folder alongside |
||||||
|
fwrite $store/custom/punk/sample/src/modules.anti "" |
||||||
|
fwrite $store/custom/punk/sample/src/modules/new.txt "new" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
lappend result [treefiles $staging/src/modules] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list new.txt] |
||||||
|
|
||||||
|
test stage_anti_dir_marker_form {a directory named <name>.anti also acts as a deletion marker}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s11] |
||||||
|
fwrite $store/vendor/punk/sample/src/modules/old.txt "old" |
||||||
|
file mkdir $store/custom/punk/sample/src/modules.anti |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
lappend result [file exists $staging/src/modules] |
||||||
|
lappend result [file exists $staging/src/modules.anti] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 0] |
||||||
|
|
||||||
|
test stage_type_conflicts {higher-layer entry wins on file/folder type conflicts}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s12] |
||||||
|
fwrite $store/vendor/punk/sample/thing "vendor-file" |
||||||
|
fwrite $store/vendor/punk/sample/other/nested.txt "vendor-nested" |
||||||
|
fwrite $store/custom/punk/sample/thing/inner.txt "custom-inner" |
||||||
|
fwrite $store/custom/punk/sample/other "custom-file" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
lappend result [file isdirectory $staging/thing] |
||||||
|
lappend result [fread $staging/thing/inner.txt] |
||||||
|
lappend result [expr {![file isdirectory $staging/other]}] |
||||||
|
lappend result [fread $staging/other] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 1 custom-inner 1 custom-file] |
||||||
|
|
||||||
|
test stage_empty_dir_preserved {empty folders in a layer are created in staging}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s13] |
||||||
|
file mkdir $store/vendor/punk/sample/emptydir |
||||||
|
fwrite $store/vendor/punk/sample/a.txt "a" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
file isdirectory $staging/emptydir |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result 1 |
||||||
|
|
||||||
|
test stage_refuse_clobber {existing non-empty dir without staging marker is refused}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s14] |
||||||
|
fwrite $store/vendor/punk/sample/a.txt "a" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store precious] |
||||||
|
fwrite $staging/important.txt "do-not-delete" |
||||||
|
set code [catch {punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging} emsg] |
||||||
|
lappend result $code |
||||||
|
lappend result [file exists $staging/important.txt] |
||||||
|
lappend result [string match "*refusing to delete*" $emsg] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 1 1 1] |
||||||
|
|
||||||
|
test stage_restage_over_marker {a previous staging composition (marker present) is replaced}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s15] |
||||||
|
fwrite $store/vendor/punk/sample/a.txt "one" |
||||||
|
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample] |
||||||
|
set staging [file join $store staging] |
||||||
|
punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging |
||||||
|
fwrite $store/vendor/punk/sample/b.txt "two" |
||||||
|
set code [catch {punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging} sres] |
||||||
|
lappend result $code |
||||||
|
lappend result [file exists $staging/a.txt] |
||||||
|
lappend result [file exists $staging/b.txt] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 1 1] |
||||||
|
|
||||||
|
# -- --- --- layout_materialize --- --- -- |
||||||
|
|
||||||
|
test materialize_fastpath {single-folder chain with no .anti markers returns the store folder unstaged}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s16] |
||||||
|
fwrite $store/vendor/punk/solo/a.txt "a" |
||||||
|
set linfo [list path [file join $store vendor punk solo] basefolder $store sourceinfo {}] |
||||||
|
set mat [punk::mix::commandset::layout::lib::layout_materialize punk.solo -layoutinfo $linfo] |
||||||
|
lappend result [dict get $mat staged] |
||||||
|
lappend result [expr {[dict get $mat path] eq [file join $store vendor punk solo]}] |
||||||
|
lappend result [llength [dict get $mat chain]] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 1 1] |
||||||
|
|
||||||
|
test materialize_single_with_antis {a single folder containing .anti markers is staged with markers stripped}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s17] |
||||||
|
fwrite $store/vendor/punk/solo/a.txt "a" |
||||||
|
fwrite $store/vendor/punk/solo/ghost.txt.anti "" |
||||||
|
set linfo [list path [file join $store vendor punk solo] basefolder $store sourceinfo {}] |
||||||
|
set staging [file join $store staging] |
||||||
|
set mat [punk::mix::commandset::layout::lib::layout_materialize punk.solo -layoutinfo $linfo -stagingdir $staging] |
||||||
|
lappend result [dict get $mat staged] |
||||||
|
lappend result [file exists [dict get $mat path]/a.txt] |
||||||
|
lappend result [file exists [dict get $mat path]/ghost.txt.anti] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 1 1 0] |
||||||
|
|
||||||
|
test materialize_opaque_path {a layout folder outside the tier/vendor store shape stays an opaque single-folder chain}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s18] |
||||||
|
fwrite $store/somewhere/deep/layoutx/a.txt "a" |
||||||
|
set linfo [list path [file join $store somewhere deep layoutx] basefolder $store sourceinfo {}] |
||||||
|
set mat [punk::mix::commandset::layout::lib::layout_materialize punk.layoutx -layoutinfo $linfo] |
||||||
|
lappend result [dict get $mat staged] |
||||||
|
lappend result [llength [dict get $mat chain]] |
||||||
|
lappend result [expr {[dict get $mat path] eq [file join $store somewhere deep layoutx]}] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 1 1] |
||||||
|
|
||||||
|
test materialize_project_pseudovendor_opaque {_project pseudo-vendor folders take no part in overlay chains}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s19] |
||||||
|
fwrite $store/vendor/_project/sample/a.txt "proj-a" |
||||||
|
fwrite $store/custom/_project/sample/a.txt "proj-custom-a" |
||||||
|
set linfo [list path [file join $store vendor _project sample] basefolder $store sourceinfo {}] |
||||||
|
set mat [punk::mix::commandset::layout::lib::layout_materialize sample -layoutinfo $linfo] |
||||||
|
lappend result [dict get $mat staged] |
||||||
|
lappend result [llength [dict get $mat chain]] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 0 1] |
||||||
|
|
||||||
|
test materialize_derived_full {derived layout materializes base chain content with overrides and .anti deletions}\ |
||||||
|
-setup $common -body { |
||||||
|
set store [make_store s20] |
||||||
|
fwrite $store/vendor/punk/sample/README.md "vendor-readme" |
||||||
|
fwrite $store/vendor/punk/sample/.gitignore "gitignore" |
||||||
|
fwrite $store/vendor/punk/sample/src/modules/README.md "modules-readme" |
||||||
|
fwrite $store/custom/punk/sample/README.md "custom-readme" |
||||||
|
fwrite $store/custom/punk/sample/extra.txt "extra" |
||||||
|
fwrite "$store/custom/punk/other@sample/README.md" "derived-readme" |
||||||
|
fwrite "$store/custom/punk/other@sample/.gitignore.anti" "" |
||||||
|
set linfo [list path [file join $store custom punk other@sample] basefolder $store sourceinfo {}] |
||||||
|
set staging [file join $store staging] |
||||||
|
set mat [punk::mix::commandset::layout::lib::layout_materialize punk.other -layoutinfo $linfo -stagingdir $staging] |
||||||
|
set effective [dict get $mat path] |
||||||
|
lappend result [dict get $mat staged] |
||||||
|
lappend result [llength [dict get $mat chain]] |
||||||
|
lappend result [fread $effective/README.md] |
||||||
|
lappend result [fread $effective/extra.txt] |
||||||
|
lappend result [fread $effective/src/modules/README.md] |
||||||
|
lappend result [file exists $effective/.gitignore] |
||||||
|
lappend result [dict get $mat deletions] |
||||||
|
}\ |
||||||
|
-cleanup {}\ |
||||||
|
-result [list 1 3 derived-readme extra modules-readme 0 .gitignore] |
||||||
|
|
||||||
|
variable BASE |
||||||
|
file delete -force $BASE |
||||||
|
|
||||||
|
cleanupTests |
||||||
|
} |
||||||
|
namespace delete ::testspace |
||||||
Loading…
Reference in new issue