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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2637c36239c1aabf37dbac03f0404cab8bb2ea493753bebc2710d44e03f78e11
4
- data.tar.gz: f544c13905cceb2c080385edc9122a4b89905700ac8195b97410ae58d829e5b6
3
+ metadata.gz: f64aa1b29bc2fb6a0c10292a25246ce4fbe5a43b7fa23dc19a84ab01abc58013
4
+ data.tar.gz: 60fb058d6f25ca80348e5236450ed3c5fe812137d04e0fce237d36b6b252dfca
5
5
  SHA512:
6
- metadata.gz: ce3e707b25251764367464866d71c64ece85e69cb8c39d2589d3342f263f9ba3c98270d3f130c05b481fac04d0c06e613d9992b87ab54bf99851e3fcd98ebc58
7
- data.tar.gz: 41acb15b29ca4667590ca59cace0a5563f1129845d205c32dbe662f6222aad9c174a09548e98f0e0b450c63b3e073cb5baa614eddbe390ab9e4084f1a8ca9e7d
6
+ metadata.gz: 30b54b93e1b4e5da44c7cf0bc6e526dd489d74301df20cd4d9644c69aab509416d3082d2646dd6394331f81643a92cbc24933141fdca024057ed6e003866b37a
7
+ data.tar.gz: 525305f71a39842d01f7ce99f3bce1d85d7dc0e5493411e09759cf003a8632609130281f40ba8ff25bf7edb8ffa73de5e9b53d3c3022c4143a8b99be70a9c4f9
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  *.gem
10
+ test/data/**/dgd.config
11
+ test/data/*/.root
data/Gemfile.lock CHANGED
@@ -1,12 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dgd-tools (0.1.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/TODO ADDED
@@ -0,0 +1 @@
1
+ * Persistent data dirs. Symlinks in app-root dir for now?
data/bin/console CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "dgd-tools/manifest"
5
+ require "dgd-tools/skotos_xml_obj"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
data/dgd-tools.gemspec CHANGED
@@ -23,4 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.bindir = "exe"
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
+
27
+ spec.add_runtime_dependency "nokogiri", "~>1.10.5"
26
28
  end
@@ -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,3 @@
1
+ <object program="/base/sys/materials">
2
+ <Base:Materials/>
3
+ </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
- repo.assemble_app(".")
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... (not yet)"
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
@@ -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
@@ -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
- KERNEL_PATHS = ["include/kernel", "kernel"]
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 :manifest_dir
33
+ attr_reader :shared_dir
29
34
 
30
35
  def initialize
36
+ @no_manifest_file = true
31
37
  @home = ENV["HOME"]
32
- @manifest_dir = "#{@home}/.dgd-tools"
33
- Dir.mkdir(@manifest_dir) unless File.directory?(@manifest_dir)
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 = "#{@manifest_dir}/#{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?("#{@manifest_dir}/dgd/bin/dgd")
40
- dgd_dir = "#{@manifest_dir}/dgd"
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("#{@manifest_dir}/dgd/src") do
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!" if @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
- def assemble_app(location)
64
- dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
65
- app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
66
- FileUtils.rm_rf(dgd_root)
67
- FileUtils.cp_r(app_path, dgd_root)
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
- write_config_file("#{location}/dgd.config")
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
- to_path = "#{dgd_root}/#{to}"
79
- to_dir = to_path.split("/")[0..-2].join("/")
80
- FileUtils.mkdir_p to_dir
81
- STDERR.puts "COPYING #{from_path.inspect} #{to_path.inspect}"
82
- FileUtils.cp_r(from_path, to_path)
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 enormous. They should
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, /* Failsafe */
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.manifest_dir}/git/#{local_path}"
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
- return @default_branch if @default_branch
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 = JSON.load(File.read(path))
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
- # Make sure the dgd.manifest file overrides either no kernel paths or both/all
190
- if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
191
- unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
192
- raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
193
- end
194
- puts "This dgd.manifest file overrides the Kernel Library with its own."
195
- else
196
- puts "This dgd.manifest needs the default Kernel Library."
197
- # This app has specified no kernellib paths -- add them
198
- git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
199
- kl_paths = { "src/kernel" => "/kernel", "src/include/kernel" => "/include/kernel", "src/doc/kernel" => "/doc/kernel" }
200
- klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
201
- source: git_repo, paths: kl_paths
202
- specs.push klib_spec
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
@@ -1,3 +1,3 @@
1
1
  module DGD
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.6"
3
3
  end
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.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: 2020-10-23 00:00:00.000000000 Z
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: