dgd-tools 0.1.1 → 0.1.6
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile.lock +5 -1
- data/TODO +1 -0
- data/bin/console +1 -0
- data/dgd-tools.gemspec +2 -0
- data/example_xml/CraftDaemon.xml +42 -0
- data/example_xml/Materials.xml +3 -0
- data/example_xml/PropertyTypes.xml +49 -0
- data/example_xml/Thing.xml +41 -0
- data/example_xml/Thing2.xml +42 -0
- data/example_xml/t1/Thing.xml +41 -0
- data/example_xml/t2/Thing.xml +42 -0
- data/exe/dgd-manifest +31 -2
- data/exe/skotos-xml-diff +46 -0
- data/lib/dgd-tools/manifest.rb +227 -45
- data/lib/dgd-tools/skotos_xml_obj.rb +183 -0
- data/lib/dgd-tools/version.rb +1 -1
- metadata +28 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f64aa1b29bc2fb6a0c10292a25246ce4fbe5a43b7fa23dc19a84ab01abc58013
|
|
4
|
+
data.tar.gz: 60fb058d6f25ca80348e5236450ed3c5fe812137d04e0fce237d36b6b252dfca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30b54b93e1b4e5da44c7cf0bc6e526dd489d74301df20cd4d9644c69aab509416d3082d2646dd6394331f81643a92cbc24933141fdca024057ed6e003866b37a
|
|
7
|
+
data.tar.gz: 525305f71a39842d01f7ce99f3bce1d85d7dc0e5493411e09759cf003a8632609130281f40ba8ff25bf7edb8ffa73de5e9b53d3c3022c4143a8b99be70a9c4f9
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
dgd-tools (0.1.
|
|
4
|
+
dgd-tools (0.1.5)
|
|
5
|
+
nokogiri (~> 1.10.5)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
10
|
+
mini_portile2 (2.4.0)
|
|
9
11
|
minitest (5.14.2)
|
|
12
|
+
nokogiri (1.10.10)
|
|
13
|
+
mini_portile2 (~> 2.4.0)
|
|
10
14
|
rake (12.3.3)
|
|
11
15
|
|
|
12
16
|
PLATFORMS
|
data/bin/console
CHANGED
data/dgd-tools.gemspec
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<object program="/base/sys/craftd">
|
|
2
|
+
<Base:CraftDaemon>
|
|
3
|
+
<Base:Craftables>
|
|
4
|
+
<Base:Craftable item="OBJ(Regent:Props:Fabric:blanket)"/>
|
|
5
|
+
<Base:Craftable item="OBJ(Regent:Staff:Evett:Items:Clothes:Top)"/>
|
|
6
|
+
<Base:Craftable item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
|
|
7
|
+
<Base:Craftable item="OBJ(Regent:Staff:Debbie:IC:General:orderbook)"/>
|
|
8
|
+
</Base:Craftables>
|
|
9
|
+
<Base:AllTools>
|
|
10
|
+
<Base:AllToolDependents tool="mop floor"/>
|
|
11
|
+
<Base:AllToolDependents tool="new">
|
|
12
|
+
<Base:OneToolDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
|
|
13
|
+
<Base:OneToolDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
|
|
14
|
+
<Base:OneToolDependent item="OBJ(Regent:Staff:Debbie:IC:General:orderbook)"/>
|
|
15
|
+
</Base:AllToolDependents>
|
|
16
|
+
<Base:AllToolDependents tool="regent:props:sewing:tools:needle"/>
|
|
17
|
+
<Base:AllToolDependents tool="regent:props:sewing:tools:scissors"/>
|
|
18
|
+
<Base:AllToolDependents tool="tool:heatable:container:watertight"/>
|
|
19
|
+
<Base:AllToolDependents tool="tool:heatsource:fire"/>
|
|
20
|
+
<Base:AllToolDependents tool="tools:needle">
|
|
21
|
+
<Base:OneToolDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
|
|
22
|
+
<Base:OneToolDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
|
|
23
|
+
</Base:AllToolDependents>
|
|
24
|
+
</Base:AllTools>
|
|
25
|
+
<Base:AllIngredients>
|
|
26
|
+
<Base:AllIngredientDependents ingredient="bolt">
|
|
27
|
+
<Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
|
|
28
|
+
<Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
|
|
29
|
+
</Base:AllIngredientDependents>
|
|
30
|
+
<Base:AllIngredientDependents ingredient="ingredient:candle"/>
|
|
31
|
+
<Base:AllIngredientDependents ingredient="ingredient:lantern-candle-empty"/>
|
|
32
|
+
<Base:AllIngredientDependents ingredient="ingredient:lantern:candle"/>
|
|
33
|
+
<Base:AllIngredientDependents ingredient="ingredients:tallow"/>
|
|
34
|
+
<Base:AllIngredientDependents ingredient="ingredients:wick:hemp"/>
|
|
35
|
+
<Base:AllIngredientDependents ingredient="new">
|
|
36
|
+
<Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
|
|
37
|
+
<Base:OneIngredientDependent item="OBJ(Regent:Staff:Evett:Items:Clothes:Top)"/>
|
|
38
|
+
<Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
|
|
39
|
+
</Base:AllIngredientDependents>
|
|
40
|
+
</Base:AllIngredients>
|
|
41
|
+
</Base:CraftDaemon>
|
|
42
|
+
</object>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<object program="/base/sys/properties">
|
|
2
|
+
<Base:PropertyTypes>
|
|
3
|
+
<Base:PropertyTypeIntList>
|
|
4
|
+
<Base:PropertyTypeInt property="page:allowall"/>
|
|
5
|
+
<Base:PropertyTypeInt property="skill:advancedefensive"/>
|
|
6
|
+
<Base:PropertyTypeInt property="skill:attackingdefensive"/>
|
|
7
|
+
<Base:PropertyTypeInt property="skill:cutoffensive"/>
|
|
8
|
+
<Base:PropertyTypeInt property="skill:dodgedefensive"/>
|
|
9
|
+
<Base:PropertyTypeInt property="skill:feintoffensive"/>
|
|
10
|
+
<Base:PropertyTypeInt property="skill:fumbledefensive"/>
|
|
11
|
+
<Base:PropertyTypeInt property="skill:guarddefensive"/>
|
|
12
|
+
<Base:PropertyTypeInt property="skill:idledefensive"/>
|
|
13
|
+
<Base:PropertyTypeInt property="skill:lang1"/>
|
|
14
|
+
<Base:PropertyTypeInt property="skill:lang2"/>
|
|
15
|
+
<Base:PropertyTypeInt property="skill:lang3"/>
|
|
16
|
+
<Base:PropertyTypeInt property="skill:lang4"/>
|
|
17
|
+
<Base:PropertyTypeInt property="skill:lang5"/>
|
|
18
|
+
<Base:PropertyTypeInt property="skill:lang6"/>
|
|
19
|
+
<Base:PropertyTypeInt property="skill:lang7"/>
|
|
20
|
+
<Base:PropertyTypeInt property="skill:recoverdefensive"/>
|
|
21
|
+
<Base:PropertyTypeInt property="skill:restdefensive"/>
|
|
22
|
+
<Base:PropertyTypeInt property="skill:retiredefensive"/>
|
|
23
|
+
<Base:PropertyTypeInt property="skill:salutedefensive"/>
|
|
24
|
+
<Base:PropertyTypeInt property="skill:teaching"/>
|
|
25
|
+
<Base:PropertyTypeInt property="skill:thrustoffensive"/>
|
|
26
|
+
<Base:PropertyTypeInt property="skill_study"/>
|
|
27
|
+
<Base:PropertyTypeInt property="skotos:currentlang"/>
|
|
28
|
+
</Base:PropertyTypeIntList>
|
|
29
|
+
<Base:PropertyTypeFloatList>
|
|
30
|
+
<Base:PropertyTypeFloat property="skill:fatigue"/>
|
|
31
|
+
</Base:PropertyTypeFloatList>
|
|
32
|
+
<Base:PropertyTypeStringList>
|
|
33
|
+
<Base:PropertyTypeString property="skotos:charname"/>
|
|
34
|
+
<Base:PropertyTypeString property="skotos:creator"/>
|
|
35
|
+
</Base:PropertyTypeStringList>
|
|
36
|
+
<Base:PropertyTypeObjectList>
|
|
37
|
+
<Base:PropertyTypeObject property="virtualhome:home"/>
|
|
38
|
+
<Base:PropertyTypeObject property="virtualhome:oubliette"/>
|
|
39
|
+
</Base:PropertyTypeObjectList>
|
|
40
|
+
<Base:PropertyTypeArrayList>
|
|
41
|
+
<Base:PropertyTypeArray property="startstory:skilllist:done"/>
|
|
42
|
+
<Base:PropertyTypeArray property="startstory:skilllist:left"/>
|
|
43
|
+
</Base:PropertyTypeArrayList>
|
|
44
|
+
<Base:PropertyTypeMappingList>
|
|
45
|
+
<Base:PropertyTypeMapping property="page:allow"/>
|
|
46
|
+
<Base:PropertyTypeMapping property="page:pending"/>
|
|
47
|
+
</Base:PropertyTypeMappingList>
|
|
48
|
+
</Base:PropertyTypes>
|
|
49
|
+
</object>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<object program="/base/obj/thing">
|
|
2
|
+
<Base:Thing>
|
|
3
|
+
<Ur:UrObject/>
|
|
4
|
+
<Base:Bulk immobile="false" mass="1" density="1"/>
|
|
5
|
+
<Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
|
|
6
|
+
<Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false">
|
|
7
|
+
<Base:Edible value="false"/>
|
|
8
|
+
<Base:Potable value="false"/>
|
|
9
|
+
<Base:DrinkMessageFirst/>
|
|
10
|
+
<Base:DrinkMessageThird/>
|
|
11
|
+
<Base:Transparency value="false"/>
|
|
12
|
+
<Base:Unsafe value="false"/>
|
|
13
|
+
<Base:Safe value="false"/>
|
|
14
|
+
<Base:ClothesExpected value="false"/>
|
|
15
|
+
<Base:DieMessageFirst/>
|
|
16
|
+
<Base:DieMessageThird/>
|
|
17
|
+
</Base:Misc>
|
|
18
|
+
<Base:Details/>
|
|
19
|
+
<Base:Combat>
|
|
20
|
+
<Base:Strength value="1"/>
|
|
21
|
+
<Base:MaxFatigue value="1"/>
|
|
22
|
+
</Base:Combat>
|
|
23
|
+
<Base:Clothing>
|
|
24
|
+
<Base:SingleWear value="false"/>
|
|
25
|
+
</Base:Clothing>
|
|
26
|
+
<Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
|
|
27
|
+
<Base:Ingredients/>
|
|
28
|
+
<Base:Tools/>
|
|
29
|
+
<Base:CraftVerbs/>
|
|
30
|
+
</Base:Crafting>
|
|
31
|
+
<Base:InitialContents/>
|
|
32
|
+
<Base:InitialProperties/>
|
|
33
|
+
<Core:Properties>
|
|
34
|
+
<Core:Property property="revisions">
|
|
35
|
+
(\{ 1065557891, "the_exiled", "E" \})
|
|
36
|
+
</Core:Property>
|
|
37
|
+
<Core:Property property="skill:fatigue">1.0</Core:Property>
|
|
38
|
+
</Core:Properties>
|
|
39
|
+
<Notes:Notes/>
|
|
40
|
+
</Base:Thing>
|
|
41
|
+
</object>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<object program="/base/obj/thing" owner="bobo">
|
|
2
|
+
<Base:Thing>
|
|
3
|
+
<Ur:UrObject/>
|
|
4
|
+
<Base:Bulk immobile="false" mass="1" density="1"/>
|
|
5
|
+
<Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
|
|
6
|
+
<Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false" bogusfield="nope">
|
|
7
|
+
<Base:Edible value="false"/>
|
|
8
|
+
<Base:Potable value="false"/>
|
|
9
|
+
<Base:DrinkMessageFirst/>
|
|
10
|
+
<Base:DrinkMessageThird/>
|
|
11
|
+
<Base:Transparency value="false"/>
|
|
12
|
+
<Base:Unsafe value="false"/>
|
|
13
|
+
<Base:Safe value="false"/>
|
|
14
|
+
<Base:ClothesExpected value="false"/>
|
|
15
|
+
<Base:DieMessageFirst/>
|
|
16
|
+
<Base:DieMessageThird/>
|
|
17
|
+
<Base:DieMessageFortySecond/>
|
|
18
|
+
</Base:Misc>
|
|
19
|
+
<Base:Details/>
|
|
20
|
+
<Base:Combat>
|
|
21
|
+
<Base:Strength value="1"/>
|
|
22
|
+
<Base:MaxFatigue value="1"/>
|
|
23
|
+
</Base:Combat>
|
|
24
|
+
<Base:Clothing>
|
|
25
|
+
<Base:SingleWear value="false"/>
|
|
26
|
+
</Base:Clothing>
|
|
27
|
+
<Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
|
|
28
|
+
<Base:Ingredients/>
|
|
29
|
+
<Base:Tools/>
|
|
30
|
+
<Base:CraftVerbs/>
|
|
31
|
+
</Base:Crafting>
|
|
32
|
+
<Base:InitialContents/>
|
|
33
|
+
<Base:InitialProperties/>
|
|
34
|
+
<Core:Properties>
|
|
35
|
+
<Core:Property property="revisions">
|
|
36
|
+
(\{ 1065557891, "the_exiled", "E" \})
|
|
37
|
+
</Core:Property>
|
|
38
|
+
<Core:Property property="skill:fatigue">1.0</Core:Property>
|
|
39
|
+
</Core:Properties>
|
|
40
|
+
<Notes:Notes/>
|
|
41
|
+
</Base:Thing>
|
|
42
|
+
</object>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<object program="/base/obj/thing">
|
|
2
|
+
<Base:Thing>
|
|
3
|
+
<Ur:UrObject/>
|
|
4
|
+
<Base:Bulk immobile="false" mass="1" density="1"/>
|
|
5
|
+
<Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
|
|
6
|
+
<Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false">
|
|
7
|
+
<Base:Edible value="false"/>
|
|
8
|
+
<Base:Potable value="false"/>
|
|
9
|
+
<Base:DrinkMessageFirst/>
|
|
10
|
+
<Base:DrinkMessageThird/>
|
|
11
|
+
<Base:Transparency value="false"/>
|
|
12
|
+
<Base:Unsafe value="false"/>
|
|
13
|
+
<Base:Safe value="false"/>
|
|
14
|
+
<Base:ClothesExpected value="false"/>
|
|
15
|
+
<Base:DieMessageFirst/>
|
|
16
|
+
<Base:DieMessageThird/>
|
|
17
|
+
</Base:Misc>
|
|
18
|
+
<Base:Details/>
|
|
19
|
+
<Base:Combat>
|
|
20
|
+
<Base:Strength value="1"/>
|
|
21
|
+
<Base:MaxFatigue value="1"/>
|
|
22
|
+
</Base:Combat>
|
|
23
|
+
<Base:Clothing>
|
|
24
|
+
<Base:SingleWear value="false"/>
|
|
25
|
+
</Base:Clothing>
|
|
26
|
+
<Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
|
|
27
|
+
<Base:Ingredients/>
|
|
28
|
+
<Base:Tools/>
|
|
29
|
+
<Base:CraftVerbs/>
|
|
30
|
+
</Base:Crafting>
|
|
31
|
+
<Base:InitialContents/>
|
|
32
|
+
<Base:InitialProperties/>
|
|
33
|
+
<Core:Properties>
|
|
34
|
+
<Core:Property property="revisions">
|
|
35
|
+
(\{ 1065557891, "the_exiled", "E" \})
|
|
36
|
+
</Core:Property>
|
|
37
|
+
<Core:Property property="skill:fatigue">1.0</Core:Property>
|
|
38
|
+
</Core:Properties>
|
|
39
|
+
<Notes:Notes/>
|
|
40
|
+
</Base:Thing>
|
|
41
|
+
</object>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<object program="/base/obj/thing" owner="bobo">
|
|
2
|
+
<Base:Thing>
|
|
3
|
+
<Ur:UrObject/>
|
|
4
|
+
<Base:Bulk immobile="false" mass="1" density="1"/>
|
|
5
|
+
<Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
|
|
6
|
+
<Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false" bogusfield="nope">
|
|
7
|
+
<Base:Edible value="false"/>
|
|
8
|
+
<Base:Potable value="false"/>
|
|
9
|
+
<Base:DrinkMessageFirst/>
|
|
10
|
+
<Base:DrinkMessageThird/>
|
|
11
|
+
<Base:Transparency value="false"/>
|
|
12
|
+
<Base:Unsafe value="false"/>
|
|
13
|
+
<Base:Safe value="false"/>
|
|
14
|
+
<Base:ClothesExpected value="false"/>
|
|
15
|
+
<Base:DieMessageFirst/>
|
|
16
|
+
<Base:DieMessageThird/>
|
|
17
|
+
<Base:DieMessageFortySecond/>
|
|
18
|
+
</Base:Misc>
|
|
19
|
+
<Base:Details/>
|
|
20
|
+
<Base:Combat>
|
|
21
|
+
<Base:Strength value="1"/>
|
|
22
|
+
<Base:MaxFatigue value="1"/>
|
|
23
|
+
</Base:Combat>
|
|
24
|
+
<Base:Clothing>
|
|
25
|
+
<Base:SingleWear value="false"/>
|
|
26
|
+
</Base:Clothing>
|
|
27
|
+
<Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
|
|
28
|
+
<Base:Ingredients/>
|
|
29
|
+
<Base:Tools/>
|
|
30
|
+
<Base:CraftVerbs/>
|
|
31
|
+
</Base:Crafting>
|
|
32
|
+
<Base:InitialContents/>
|
|
33
|
+
<Base:InitialProperties/>
|
|
34
|
+
<Core:Properties>
|
|
35
|
+
<Core:Property property="revisions">
|
|
36
|
+
(\{ 1065557891, "the_exiled", "E" \})
|
|
37
|
+
</Core:Property>
|
|
38
|
+
<Core:Property property="skill:fatigue">1.0</Core:Property>
|
|
39
|
+
</Core:Properties>
|
|
40
|
+
<Notes:Notes/>
|
|
41
|
+
</Base:Thing>
|
|
42
|
+
</object>
|
data/exe/dgd-manifest
CHANGED
|
@@ -6,7 +6,31 @@ if ARGV.size == 0
|
|
|
6
6
|
ARGV.push "install"
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
if ARGV == ["--version"]
|
|
10
|
+
puts "dgd-tools version #{DGD::VERSION}"
|
|
11
|
+
exit
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if ARGV.size == 1 && ["-h", "--help"].include?(ARGV[0])
|
|
15
|
+
puts <<HELP_INFO
|
|
16
|
+
dgd-manifest commands:
|
|
17
|
+
|
|
18
|
+
new [project_name]: create a new DGD-manifest project
|
|
19
|
+
test: make sure the dgd.manifest file is well-formed and usable
|
|
20
|
+
install: compile the DGD application to a config file and a root directory
|
|
21
|
+
HELP_INFO
|
|
22
|
+
exit
|
|
23
|
+
end
|
|
24
|
+
|
|
9
25
|
case ARGV[0]
|
|
26
|
+
when "new"
|
|
27
|
+
unless ARGV.size == 2
|
|
28
|
+
puts "Usage: dgd-manifest new [project name]"
|
|
29
|
+
raise "Must supply exactly one argument to dgd-manifest new!"
|
|
30
|
+
end
|
|
31
|
+
appdir = DGD::Manifest::AppDirectory.new(File.expand_path ARGV[1])
|
|
32
|
+
appdir.name = ARGV[1]
|
|
33
|
+
appdir.create!
|
|
10
34
|
when "test"
|
|
11
35
|
unless File.exist?("dgd.manifest")
|
|
12
36
|
raise "I don't see a dgd.manifest file in this directory!"
|
|
@@ -14,6 +38,7 @@ when "test"
|
|
|
14
38
|
puts "Running dgd.manifest installer..."
|
|
15
39
|
repo = DGD::Manifest::Repo.new
|
|
16
40
|
repo.manifest_file("dgd.manifest")
|
|
41
|
+
repo.precheck(".")
|
|
17
42
|
puts "Verified Manifest packages: this looks likely correct."
|
|
18
43
|
when "install"
|
|
19
44
|
unless File.exist?("dgd.manifest")
|
|
@@ -22,9 +47,13 @@ when "install"
|
|
|
22
47
|
puts "Running DGD Manifest installer..."
|
|
23
48
|
repo = DGD::Manifest::Repo.new
|
|
24
49
|
repo.manifest_file("dgd.manifest")
|
|
25
|
-
|
|
50
|
+
current_dir = File.expand_path(".")
|
|
51
|
+
repo.precheck(current_dir)
|
|
52
|
+
repo.assemble_app(current_dir)
|
|
53
|
+
puts "Assembled DGD application into #{current_dir}"
|
|
26
54
|
when "server"
|
|
27
|
-
puts "Starting DGD server...
|
|
55
|
+
puts "Starting DGD server..."
|
|
56
|
+
DGD::Manifest.system_call("~/.dgd-tools/dgd/bin/dgd dgd.config")
|
|
28
57
|
else
|
|
29
58
|
raise "Unrecognised #{$0} command: #{ARGV[0].inspect}!"
|
|
30
59
|
end
|
data/exe/skotos-xml-diff
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Diff multiple Skotos-XML-format objects dumped from a
|
|
4
|
+
# SkotOS-or-similar game to find out what updates should
|
|
5
|
+
# be merged between them.
|
|
6
|
+
|
|
7
|
+
require "dgd-tools/skotos_xml_obj"
|
|
8
|
+
|
|
9
|
+
if ARGV.size == 1 && ["-h", "--help"].include?(ARGV[0])
|
|
10
|
+
print <<HELP_INFO
|
|
11
|
+
skotos-xml-diff [file1] [file2]
|
|
12
|
+
|
|
13
|
+
OR
|
|
14
|
+
|
|
15
|
+
skotos-xml-diff [dir1] [dir2]
|
|
16
|
+
HELP_INFO
|
|
17
|
+
exit
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if ARGV.size != 2
|
|
21
|
+
STDERR.puts "Usage: skotos-xml-diff <obj_or_dir_1> <obj_or_dir_2>"
|
|
22
|
+
exit -1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if File.directory?(ARGV[0]) ^ File.directory?(ARGV[1])
|
|
26
|
+
STDERR.puts "Please give two files or two directories, not one of each!"
|
|
27
|
+
exit -1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if File.directory?(ARGV[0])
|
|
31
|
+
diff = SkotOS::XMLObject.diff_dirs(*ARGV)
|
|
32
|
+
if diff.empty?
|
|
33
|
+
puts "No difference."
|
|
34
|
+
else
|
|
35
|
+
puts diff.join("\n\n=====\n\n")
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
obj1 = SkotOS::XMLObject.from_file(ARGV[0])
|
|
39
|
+
obj2 = SkotOS::XMLObject.from_file(ARGV[1])
|
|
40
|
+
diff = SkotOS::XMLObject.diff_between(obj1, obj2)
|
|
41
|
+
if diff.empty?
|
|
42
|
+
puts "No difference."
|
|
43
|
+
else
|
|
44
|
+
puts diff
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/dgd-tools/manifest.rb
CHANGED
|
@@ -9,7 +9,12 @@ module DGD; end
|
|
|
9
9
|
module DGD::Manifest
|
|
10
10
|
DGD_BUILD_COMMAND = %(make DEFINES='-DUINDEX_TYPE="unsigned int" -DUINDEX_MAX=UINT_MAX -DEINDEX_TYPE="unsigned short" -DEINDEX_MAX=USHRT_MAX -DSSIZET_TYPE="unsigned int" -DSSIZET_MAX=1048576' install
|
|
11
11
|
)
|
|
12
|
-
|
|
12
|
+
KERNEL_PATH_MAP = {
|
|
13
|
+
"src/kernel" => "/kernel",
|
|
14
|
+
"src/include" => "/include",
|
|
15
|
+
"src/doc/kernel" => "/doc/kernel"
|
|
16
|
+
}
|
|
17
|
+
KERNEL_PATHS = KERNEL_PATH_MAP.values
|
|
13
18
|
DEFAULT_KERNELLIB_URL = "https://github.com/ChatTheatre/kernellib"
|
|
14
19
|
|
|
15
20
|
GENERATED_ROOT = ".root"
|
|
@@ -25,24 +30,26 @@ module DGD::Manifest
|
|
|
25
30
|
# This is a repo of everything DGD Manifest saves between runs.
|
|
26
31
|
# It includes downloaded Git repos, Goods files and more.
|
|
27
32
|
class Repo
|
|
28
|
-
attr_reader :
|
|
33
|
+
attr_reader :shared_dir
|
|
29
34
|
|
|
30
35
|
def initialize
|
|
36
|
+
@no_manifest_file = true
|
|
31
37
|
@home = ENV["HOME"]
|
|
32
|
-
@
|
|
33
|
-
Dir.mkdir(@
|
|
38
|
+
@shared_dir = "#{@home}/.dgd-tools"
|
|
39
|
+
Dir.mkdir(@shared_dir) unless File.directory?(@shared_dir)
|
|
40
|
+
|
|
34
41
|
["git", "goods"].each do |subdir|
|
|
35
|
-
full_subdir = "#{@
|
|
42
|
+
full_subdir = "#{@shared_dir}/#{subdir}"
|
|
36
43
|
Dir.mkdir(full_subdir) unless File.directory?(full_subdir)
|
|
37
44
|
end
|
|
38
45
|
|
|
39
|
-
unless File.exist?("#{@
|
|
40
|
-
dgd_dir = "#{@
|
|
46
|
+
unless File.exist?("#{@shared_dir}/dgd/bin/dgd")
|
|
47
|
+
dgd_dir = "#{@shared_dir}/dgd"
|
|
41
48
|
if File.directory?(dgd_dir)
|
|
42
49
|
# Not clear to me what to do here...
|
|
43
50
|
else
|
|
44
51
|
DGD::Manifest.system_call("git clone https://github.com/ChatTheatre/dgd.git #{dgd_dir}")
|
|
45
|
-
Dir.chdir("#{@
|
|
52
|
+
Dir.chdir("#{@shared_dir}/dgd/src") do
|
|
46
53
|
DGD::Manifest.system_call(DGD_BUILD_COMMAND)
|
|
47
54
|
end
|
|
48
55
|
end
|
|
@@ -55,45 +62,112 @@ module DGD::Manifest
|
|
|
55
62
|
end
|
|
56
63
|
|
|
57
64
|
def manifest_file(path)
|
|
58
|
-
raise "Already have a dgd.manifest file!"
|
|
65
|
+
raise "Already have a dgd.manifest file!" unless @no_manifest_file
|
|
59
66
|
|
|
67
|
+
@no_manifest_file = false
|
|
60
68
|
@manifest_file ||= AppFile.new(self, path)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
protected
|
|
72
|
+
|
|
73
|
+
# This includes files to assemble... But also subdirectories and commands. This format is
|
|
74
|
+
# unstable and ugly, and should not be exposed to outside parties who might later depend on it.
|
|
75
|
+
def assembly_operations(location)
|
|
76
|
+
operations = []
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
specs = @manifest_file.specs
|
|
78
|
+
raise("No manifest file!") if @no_manifest_file
|
|
71
79
|
|
|
72
|
-
specs.each do |spec|
|
|
80
|
+
@manifest_file.specs.each do |spec|
|
|
73
81
|
git_repo = spec.source
|
|
74
|
-
git_repo.use_details(spec.source_details)
|
|
82
|
+
git_repo.use_details(spec.source_details) # This sets things like checked-out branch
|
|
75
83
|
|
|
76
84
|
spec.paths.each do |from, to|
|
|
85
|
+
# Note: git_repo.local_dir is an absolute path.
|
|
77
86
|
from_path = "#{git_repo.local_dir}/#{from}"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
if File.directory?(from_path)
|
|
88
|
+
files = Dir["#{from_path}/**/*"].to_a + Dir["#{from_path}/**/.*"].to_a
|
|
89
|
+
dirs = files.select { |file| File.directory?(file) }
|
|
90
|
+
non_dirs = files - dirs
|
|
91
|
+
operations << { cmd: "cp", from: from_path, to: to, dirs: dirs, non_dirs: non_dirs, comment: :single_dir }
|
|
92
|
+
elsif from_path["*"] # If from_path contains at least one asterisk
|
|
93
|
+
components = from.split("/")
|
|
94
|
+
first_wild_idx = components.index { |item| item["*"] }
|
|
95
|
+
no_wild_from_path = components[0..(first_wild_idx-1)].join("/")
|
|
96
|
+
wild_path = components[first_wild_idx..-1].join("/")
|
|
97
|
+
|
|
98
|
+
files = Dir["#{git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
|
|
99
|
+
dirs = files.select { |file| File.directory?(file) }
|
|
100
|
+
dirs += files.map { |f| File.dirname(f) }
|
|
101
|
+
dirs.uniq!
|
|
102
|
+
|
|
103
|
+
non_dirs = files - dirs
|
|
104
|
+
operations << { cmd: "cp", from: "#{git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, comment: :path_wildcard }
|
|
105
|
+
else
|
|
106
|
+
# A single file
|
|
107
|
+
operations << { cmd: "cp", from: from_path, to: to, dirs: [], non_dirs: [from_path], comment: :single_file }
|
|
108
|
+
end
|
|
83
109
|
end
|
|
84
110
|
end
|
|
111
|
+
|
|
112
|
+
app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
|
|
113
|
+
app_files = Dir["#{app_path}/**/*"].to_a
|
|
114
|
+
app_dirs = app_files.select { |f| File.directory?(f) }
|
|
115
|
+
app_non_dirs = app_files - app_dirs
|
|
116
|
+
unless app_dirs.empty? && app_non_dirs.empty?
|
|
117
|
+
operations << { cmd: "cp", from: app_path, to: ".", dirs: app_dirs, non_dirs: app_non_dirs, comment: :app_files } # No source
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
operations
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
public
|
|
124
|
+
|
|
125
|
+
def assemble_app(location)
|
|
126
|
+
dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
|
|
127
|
+
FileUtils.rm_rf(dgd_root)
|
|
128
|
+
|
|
129
|
+
Dir.chdir(location) do
|
|
130
|
+
write_config_file("#{location}/dgd.config")
|
|
131
|
+
FileUtils.mkdir_p("#{location}/state") # Statedir for statedumps, editor files, etc.
|
|
132
|
+
|
|
133
|
+
assembly_operations(location).each do |sd_hash|
|
|
134
|
+
to_path = "#{dgd_root}/#{sd_hash[:to]}"
|
|
135
|
+
|
|
136
|
+
# Make appropriate dirs, including empty ones
|
|
137
|
+
sd_hash[:dirs].each do |dir|
|
|
138
|
+
FileUtils.mkdir_p dir.sub(sd_hash[:from], to_path)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Copy all files
|
|
142
|
+
sd_hash[:non_dirs].each do |from_file|
|
|
143
|
+
to_file = from_file.sub(sd_hash[:from], "#{dgd_root}/#{sd_hash[:to]}")
|
|
144
|
+
to_dir = File.dirname(to_file)
|
|
145
|
+
FileUtils.mkdir_p to_dir
|
|
146
|
+
FileUtils.cp from_file, to_file
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def precheck(location)
|
|
153
|
+
all_files = assembly_operations(location).flat_map { |sd| sd[:non_dirs] }
|
|
154
|
+
|
|
155
|
+
if all_files.size != all_files.uniq.size
|
|
156
|
+
repeated = all_files.uniq.select { |f| all_files.count(f) > 1 }
|
|
157
|
+
raise "Error in dgd.manifest! Repeated files: #{repeated.inspect} / #{all_files.inspect}"
|
|
158
|
+
end
|
|
85
159
|
end
|
|
86
160
|
|
|
87
161
|
def write_config_file(path)
|
|
88
162
|
File.open(path, "wb") do |f|
|
|
89
163
|
f.write <<CONTENTS
|
|
90
|
-
/* These are SkotOS limits. They are
|
|
164
|
+
/* These are SkotOS limits. They are larger than you are likely to need. They should
|
|
91
165
|
be configurable but they are not yet. */
|
|
92
166
|
telnet_port = ([
|
|
93
167
|
"*":50100 /* telnet port number */
|
|
94
168
|
]);
|
|
95
169
|
binary_port = ([
|
|
96
|
-
"*":50110
|
|
170
|
+
"*":50110 /* Failsafe */
|
|
97
171
|
]); /* binary ports */
|
|
98
172
|
directory = "./#{GENERATED_ROOT}";
|
|
99
173
|
|
|
@@ -134,7 +208,7 @@ CONTENTS
|
|
|
134
208
|
@git_url = git_url
|
|
135
209
|
@repo = repo
|
|
136
210
|
local_path = git_url.tr("/\\", "_")
|
|
137
|
-
@local_dir = "#{@repo.
|
|
211
|
+
@local_dir = "#{@repo.shared_dir}/git/#{local_path}"
|
|
138
212
|
|
|
139
213
|
if File.directory?(@local_dir)
|
|
140
214
|
Dir.chdir(@local_dir) do
|
|
@@ -146,9 +220,7 @@ CONTENTS
|
|
|
146
220
|
end
|
|
147
221
|
|
|
148
222
|
def default_branch
|
|
149
|
-
|
|
150
|
-
output = `git rev-parse --abbrev-ref origin/HEAD`.chomp
|
|
151
|
-
@default_branch = output.gsub(/^origin\//, "")
|
|
223
|
+
@default_branch ||= `git rev-parse --abbrev-ref origin/HEAD`.chomp.gsub(/^origin\//, "")
|
|
152
224
|
end
|
|
153
225
|
|
|
154
226
|
def use_details(details)
|
|
@@ -174,7 +246,7 @@ CONTENTS
|
|
|
174
246
|
@path = path
|
|
175
247
|
@repo = repo
|
|
176
248
|
raise("No such dgd.manifest file as #{path.inspect}!") unless File.exist?(path)
|
|
177
|
-
contents =
|
|
249
|
+
contents = AppFile.parse_manifest_file(path)
|
|
178
250
|
|
|
179
251
|
read_manifest_file(contents)
|
|
180
252
|
|
|
@@ -186,25 +258,41 @@ CONTENTS
|
|
|
186
258
|
raise "Repeated (conflicting?) paths in dgd.manifest! #{repeated_paths.inspect}"
|
|
187
259
|
end
|
|
188
260
|
|
|
189
|
-
|
|
190
|
-
if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
else
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
end
|
|
261
|
+
## Make sure the dgd.manifest file overrides either no kernel paths or both/all
|
|
262
|
+
#if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
|
|
263
|
+
# unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
|
|
264
|
+
# raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
|
|
265
|
+
# end
|
|
266
|
+
# puts "This dgd.manifest file overrides the Kernel Library with its own."
|
|
267
|
+
#else
|
|
268
|
+
# puts "This dgd.manifest needs the default Kernel Library."
|
|
269
|
+
# # This app has specified no kernellib paths -- add them
|
|
270
|
+
# git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
|
|
271
|
+
# klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
|
|
272
|
+
# source: git_repo, paths: KERNEL_PATH_MAP
|
|
273
|
+
# specs.unshift klib_spec
|
|
274
|
+
#end
|
|
204
275
|
|
|
205
276
|
nil
|
|
206
277
|
end
|
|
207
278
|
|
|
279
|
+
# Load the JSON and then remove comments
|
|
280
|
+
def self.parse_manifest_file(path)
|
|
281
|
+
contents = JSON.parse(File.read path)
|
|
282
|
+
remove_comments!(contents)
|
|
283
|
+
contents
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def self.remove_comments!(items)
|
|
287
|
+
if items.is_a?(Hash)
|
|
288
|
+
items.delete_if { |k, v| k[0] == "#" }
|
|
289
|
+
items.values.each { |v| remove_comments!(v) }
|
|
290
|
+
elsif items.is_a?(Array)
|
|
291
|
+
items.delete_if { |i| i.is_a?(String) && i[0] == "#" }
|
|
292
|
+
items.each { |i| remove_comments!(i) }
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
208
296
|
def read_manifest_file(contents)
|
|
209
297
|
raise "Expected a top-level JSON object in dgd.manifest!" unless contents.is_a?(Hash)
|
|
210
298
|
|
|
@@ -273,4 +361,98 @@ CONTENTS
|
|
|
273
361
|
@paths = cleaned_paths
|
|
274
362
|
end
|
|
275
363
|
end
|
|
364
|
+
|
|
365
|
+
class AppDirectory
|
|
366
|
+
attr_reader :location
|
|
367
|
+
attr_accessor :name
|
|
368
|
+
|
|
369
|
+
DEFAULT_FILE_LOCATIONS = {
|
|
370
|
+
"manifest" => "dgd.manifest",
|
|
371
|
+
"gitignore" => ".gitignore",
|
|
372
|
+
"gems_rb" => "gems.rb",
|
|
373
|
+
}
|
|
374
|
+
DEFAULT_EMPTY_DIRS = [ "app", "state" ]
|
|
375
|
+
|
|
376
|
+
def initialize(directory)
|
|
377
|
+
@location = directory
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def gitignore_contents
|
|
381
|
+
<<~FILE_CONTENTS
|
|
382
|
+
# DGD Manifest files
|
|
383
|
+
.root
|
|
384
|
+
dgd.config
|
|
385
|
+
state/*
|
|
386
|
+
FILE_CONTENTS
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def manifest_contents
|
|
390
|
+
<<FILE_CONTENTS
|
|
391
|
+
{
|
|
392
|
+
"name": "#{@name}",
|
|
393
|
+
"version": "0.1.0",
|
|
394
|
+
"description": "TODO: put description here",
|
|
395
|
+
"app_root": "app",
|
|
396
|
+
"goods": [
|
|
397
|
+
"# This is an example goods file - substitute your own.",
|
|
398
|
+
"https://raw.githubusercontent.com/noahgibbs/dgd-tools/main/goods/skotos_httpd.goods"
|
|
399
|
+
],
|
|
400
|
+
"unbundled_goods": [
|
|
401
|
+
{
|
|
402
|
+
"#": "this is an example of unbundled goods - substitute your own",
|
|
403
|
+
"name": "kernellib",
|
|
404
|
+
"git": {
|
|
405
|
+
"url": "https://github.com/ChatTheatre/kernellib.git",
|
|
406
|
+
"branch": "master"
|
|
407
|
+
},
|
|
408
|
+
"paths": {
|
|
409
|
+
"src/doc/kernel": "doc/kernel",
|
|
410
|
+
"src/include/kernel": "include/kernel",
|
|
411
|
+
"src/include/*.h": "include",
|
|
412
|
+
"src/kernel": "kernel"
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
FILE_CONTENTS
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def gems_rb_contents
|
|
421
|
+
<<~FILE_CONTENTS
|
|
422
|
+
source "https://rubygems.org"
|
|
423
|
+
|
|
424
|
+
gem "dgd-tools", ">= #{DGD::VERSION}"
|
|
425
|
+
FILE_CONTENTS
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def create!
|
|
429
|
+
if File.exist?(@location) && (!File.directory?(@location) || Dir["#{@location}/**"].size != 0)
|
|
430
|
+
raise "Cannot create a new DGD manifest project over a file or in an existing non-empty directory!"
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
puts "Creating new DGD manifest project at #{@location}..."
|
|
434
|
+
FileUtils.mkdir_p @location
|
|
435
|
+
Dir.chdir @location do
|
|
436
|
+
DEFAULT_FILE_LOCATIONS.each do |file_desc, file_location|
|
|
437
|
+
File.open(file_location, "wb") do |f|
|
|
438
|
+
contents = send("#{file_desc}_contents")
|
|
439
|
+
f.write(contents)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
DEFAULT_EMPTY_DIRS.each do |dir|
|
|
444
|
+
FileUtils.mkdir dir
|
|
445
|
+
FileUtils.touch File.join(dir, ".keep")
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
result = system "bundle"
|
|
449
|
+
raise("Could not run bundler to install dgd-tools for #{@location}!") unless result
|
|
450
|
+
|
|
451
|
+
result = system "bundle exec dgd-manifest install"
|
|
452
|
+
raise("Error when running dgd-manifest for #{@location}!") unless result
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
puts "Successfully created project at #{@location}."
|
|
456
|
+
end
|
|
457
|
+
end
|
|
276
458
|
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
require "dgd-tools/version"
|
|
2
|
+
|
|
3
|
+
# Nokogiri is unusually permissive as an XML parser, which is
|
|
4
|
+
# good - SkotOS XML objects don't parse with most XML parsers.
|
|
5
|
+
require "nokogiri"
|
|
6
|
+
|
|
7
|
+
require "tempfile"
|
|
8
|
+
|
|
9
|
+
module SkotOS; end
|
|
10
|
+
|
|
11
|
+
# TODO: remove <Core:Property property="revisions"> from anywhere in the XML tree
|
|
12
|
+
|
|
13
|
+
class SkotOS::XMLObject
|
|
14
|
+
attr_reader :pretty
|
|
15
|
+
|
|
16
|
+
def initialize(pretty)
|
|
17
|
+
@pretty = pretty
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.from_file(filename)
|
|
21
|
+
# SkotOS files often have references to undefined namespaces,
|
|
22
|
+
# but we can get Nokogiri to parse it.
|
|
23
|
+
doc = Nokogiri::XML(File.read filename)
|
|
24
|
+
|
|
25
|
+
remove_undiffed(doc)
|
|
26
|
+
|
|
27
|
+
pretty = doc.to_xml(indent:3)
|
|
28
|
+
#data = doc.to_hash
|
|
29
|
+
#prune_whitespace(data)
|
|
30
|
+
SkotOS::XMLObject.new pretty
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.diff_between(obj1, obj2, o1_name: "Object 1", o2_name: "Object 2")
|
|
34
|
+
of1 = Tempfile.new("skotos_xml_diff1_")
|
|
35
|
+
of2 = Tempfile.new("skotos_xml_diff2_")
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
of1.write(obj1.pretty)
|
|
39
|
+
of2.write(obj2.pretty)
|
|
40
|
+
of1.close
|
|
41
|
+
of2.close
|
|
42
|
+
|
|
43
|
+
# Diff 'fails' if there's a difference between the two files.
|
|
44
|
+
diff = system_call("diff -c #{of1.path} #{of2.path}", fail_ok: true)
|
|
45
|
+
diff.sub!(of1.path, o1_name)
|
|
46
|
+
diff.sub!(of2.path, o2_name)
|
|
47
|
+
ensure
|
|
48
|
+
of1.unlink
|
|
49
|
+
of2.unlink
|
|
50
|
+
end
|
|
51
|
+
diff
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.skip_ignored_files(list)
|
|
55
|
+
list.select do |path|
|
|
56
|
+
!path[/,v$/] && # Ignore files ending in comma-v
|
|
57
|
+
!path[/-backup-\d+-\d+-\d+\.xml/] # Ignore files ending in -backup-[DATE].xml
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.diff_dirs(dir1, dir2)
|
|
62
|
+
entries1 = skip_ignored_files(Dir.glob("*", base: dir1).to_a)
|
|
63
|
+
entries2 = skip_ignored_files(Dir.glob("*", base: dir2).to_a)
|
|
64
|
+
|
|
65
|
+
only_in_1 = entries1 - entries2
|
|
66
|
+
only_in_2 = entries2 - entries1
|
|
67
|
+
in_both = entries1 & entries2
|
|
68
|
+
|
|
69
|
+
diff = []
|
|
70
|
+
diff << "Only in first: #{only_in_1.join(", ")}" unless only_in_1.empty?
|
|
71
|
+
diff << "Only in second: #{only_in_2.join(", ")}" unless only_in_2.empty?
|
|
72
|
+
|
|
73
|
+
in_both.each do |file|
|
|
74
|
+
in_1 = "#{dir1}/#{file}"
|
|
75
|
+
in_2 = "#{dir2}/#{file}"
|
|
76
|
+
if File.directory?(in_1) ^ File.directory?(in_2)
|
|
77
|
+
diff << "Only a directory in one, not both: #{dir1}/#{file}"
|
|
78
|
+
elsif File.directory?(in_1)
|
|
79
|
+
d = diff_dirs(in_1, in_2)
|
|
80
|
+
diff.concat(d)
|
|
81
|
+
else
|
|
82
|
+
o1 = from_file(in_1)
|
|
83
|
+
o2 = from_file(in_2)
|
|
84
|
+
this_diff = diff_between(o1, o2, o1_name: in_1, o2_name: in_2)
|
|
85
|
+
diff << this_diff unless this_diff.strip == ""
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
diff
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.remove_undiffed(doc)
|
|
92
|
+
if doc.root && doc.root.element?
|
|
93
|
+
ignored_top_elements = ["program", "clone", "owner"]
|
|
94
|
+
ignored_top_elements.each do |attr|
|
|
95
|
+
if doc.root.attribute(attr)
|
|
96
|
+
doc.root.remove_attribute(attr)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.system_call(cmd, fail_ok: false)
|
|
103
|
+
f = Tempfile.new("system_call_xml_diff_")
|
|
104
|
+
begin
|
|
105
|
+
system(cmd, out: f)
|
|
106
|
+
unless fail_ok || $?.success?
|
|
107
|
+
f.rewind
|
|
108
|
+
out = f.read
|
|
109
|
+
raise "Error running command: #{cmd.inspect}!\n\nOutput:\n#{out}\n\n"
|
|
110
|
+
end
|
|
111
|
+
f.rewind
|
|
112
|
+
return f.read
|
|
113
|
+
ensure
|
|
114
|
+
f.close
|
|
115
|
+
f.unlink
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
=begin
|
|
121
|
+
# Abandoned approach follows
|
|
122
|
+
# Some code taken from: https://stackoverflow.com/a/10144623
|
|
123
|
+
class Nokogiri::XML::Node
|
|
124
|
+
TYPENAMES = {1=>'element',2=>'attribute',3=>'plaintext',4=>'cdata',8=>'comment'}
|
|
125
|
+
def to_hash
|
|
126
|
+
{kind:TYPENAMES[node_type],name:name}.tap do |h|
|
|
127
|
+
h.merge! nshref:namespace.href, nsprefix:namespace.prefix if namespace
|
|
128
|
+
h.merge! text:text
|
|
129
|
+
h.merge! attr:attribute_nodes.map(&:to_hash) if element?
|
|
130
|
+
h.merge! kids:children.map(&:to_hash) if element?
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
class Nokogiri::XML::Document
|
|
135
|
+
def to_hash; root.to_hash; end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class SkotOS::XMLObject
|
|
139
|
+
OBJ_FIELDS = [:kind, :name, :text, :attr, :nshref, :nsprefix]
|
|
140
|
+
def self.diff_between(obj1, obj2, diff = [])
|
|
141
|
+
single_obj1 = obj1.slice(*OBJ_FIELDS)
|
|
142
|
+
single_obj2 = obj2.slice(*OBJ_FIELDS)
|
|
143
|
+
|
|
144
|
+
this_diff = []
|
|
145
|
+
OBJ_FIELDS.each do |field|
|
|
146
|
+
if single_obj1[field] != single_obj2[field]
|
|
147
|
+
this_diff.concat ["+#{field}: #{single_obj2[field]}", "-#{field}: #{single_obj1[field]}"]
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
single_obj1[:kids]
|
|
152
|
+
|
|
153
|
+
diff
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def self.prune_whitespace(data)
|
|
157
|
+
data[:text].gsub!(/\W+/, " ")
|
|
158
|
+
data[:text].strip!
|
|
159
|
+
new_kids = data[:kids].flat_map do |node|
|
|
160
|
+
if node[:kind] == "comment"
|
|
161
|
+
[]
|
|
162
|
+
elsif node[:kind] == "plaintext"
|
|
163
|
+
new_text = node[:text].gsub(/\W+/, " ").strip
|
|
164
|
+
if new_text == ""
|
|
165
|
+
[]
|
|
166
|
+
else
|
|
167
|
+
node[:text] = new_text
|
|
168
|
+
[node]
|
|
169
|
+
end
|
|
170
|
+
elsif node[:kind] == "element" || node[:kind] == "attribute"
|
|
171
|
+
node[:text].gsub!(/\W+/, " ")
|
|
172
|
+
node[:text].strip!
|
|
173
|
+
prune_whitespace(node)
|
|
174
|
+
[node]
|
|
175
|
+
else
|
|
176
|
+
raise "Is this illegal or did I just not anticipate it?"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
data[:kids] = new_kids
|
|
180
|
+
nil
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
=end
|
data/lib/dgd-tools/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dgd-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Noah Gibbs
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2021-02-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: nokogiri
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.10.5
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.10.5
|
|
13
27
|
description: dgd-tools supplies DGD Manifest and eventually perhaps other tools. DGD
|
|
14
28
|
Manifest is an experimental DGD library and packaging system.
|
|
15
29
|
email:
|
|
16
30
|
- the.codefolio.guy@gmail.com
|
|
17
31
|
executables:
|
|
18
32
|
- dgd-manifest
|
|
33
|
+
- skotos-xml-diff
|
|
19
34
|
extensions: []
|
|
20
35
|
extra_rdoc_files: []
|
|
21
36
|
files:
|
|
@@ -25,12 +40,22 @@ files:
|
|
|
25
40
|
- Gemfile.lock
|
|
26
41
|
- README.md
|
|
27
42
|
- Rakefile
|
|
43
|
+
- TODO
|
|
28
44
|
- bin/console
|
|
29
45
|
- bin/setup
|
|
30
46
|
- dgd-tools.gemspec
|
|
47
|
+
- example_xml/CraftDaemon.xml
|
|
48
|
+
- example_xml/Materials.xml
|
|
49
|
+
- example_xml/PropertyTypes.xml
|
|
50
|
+
- example_xml/Thing.xml
|
|
51
|
+
- example_xml/Thing2.xml
|
|
52
|
+
- example_xml/t1/Thing.xml
|
|
53
|
+
- example_xml/t2/Thing.xml
|
|
31
54
|
- exe/dgd-manifest
|
|
55
|
+
- exe/skotos-xml-diff
|
|
32
56
|
- goods/skotos_httpd.goods
|
|
33
57
|
- lib/dgd-tools/manifest.rb
|
|
58
|
+
- lib/dgd-tools/skotos_xml_obj.rb
|
|
34
59
|
- lib/dgd-tools/version.rb
|
|
35
60
|
homepage: https://github.com/noahgibbs/dgd-tools
|
|
36
61
|
licenses:
|