github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/scriptpack/scriptpack.go (about) 1 // Package scriptpack is used to work with "ScriptPacks" which are 2 // packages of shell scripts that app types can define and use to get things 3 // done on the remote machine, whether its for development or deployment. 4 // 5 // ScriptPacks are 100% pure shell scripting. Any inputs must be received from 6 // environment variables. They aren't allowed to template at all. This is 7 // all done to ensure testability of the ScriptPacks. 8 // 9 // These are treated as first class elements within Otto to assist with 10 // testing. 11 // 12 // To create your own scriptpack, see the "template" folder within this 13 // directory. The folder structure and contents are important for scriptpacks 14 // to function correctly. 15 package scriptpack 16 17 import ( 18 "fmt" 19 "io" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "text/template" 24 25 "github.com/hashicorp/atlas-go/archive" 26 "github.com/hashicorp/otto/helper/bindata" 27 ) 28 29 // ScriptPack is a struct representing a single ScriptPack. This is exported 30 // from the various scriptpacks. 31 type ScriptPack struct { 32 // Name is an identifying name used to name the environment variables. 33 // For example the root of where the files are will always be 34 // SCRIPTPACK_<NAME>_ROOT. 35 Name string 36 37 // Data is the compiled bindata for this ScriptPack. The entire 38 // AssetDirectory will be copied starting from "/data" 39 Data bindata.Data 40 41 // Dependencies are a list of other ScriptPacks that will always be 42 // unpacked alongside this ScriptPack. By referencing actual ScriptPack 43 // pointers, the dependencies will also be statically compiled into 44 // the Go binaries that contain them. 45 // 46 // Dependencies can be accessed at the path specified by 47 // SCRIPTPACK_<DEP>_ROOT. Note that if you depend on a ScriptPack 48 // which itself has a conflicting named dependency, then the first 49 // one loaded will win. Be careful about this. 50 Dependencies []*ScriptPack 51 } 52 53 // Env returns the environment variables that should be set for this 54 // ScriptPack when it is executed. 55 // 56 // path is the path to the root of the directory where Write was called 57 // to write the ScriptPack output. 58 func (s *ScriptPack) Env(path string) map[string]string { 59 result := make(map[string]string) 60 result[fmt.Sprintf("SCRIPTPACK_%s_ROOT", s.Name)] = filepath.Join(path, s.Name) 61 for _, dep := range s.Dependencies { 62 for k, v := range dep.Env(path) { 63 result[k] = v 64 } 65 } 66 67 return result 68 } 69 70 // Write writes the contents of the ScriptPack and any dependencies into 71 // the given directory. 72 func (s *ScriptPack) Write(dst string) error { 73 // Build the names of all scriptpacks 74 spNames := make([]string, 1, len(s.Dependencies)+1) 75 spNames[0] = s.Name 76 for _, dep := range s.Dependencies { 77 spNames = append(spNames, dep.Name) 78 } 79 80 // Deps 81 for _, dep := range s.Dependencies { 82 if err := dep.Write(dst); err != nil { 83 return err 84 } 85 } 86 87 // Our own 88 if err := s.Data.CopyDir(filepath.Join(dst, s.Name), "data"); err != nil { 89 return err 90 } 91 92 // Write the main file which has the env vars in it 93 f, err := os.OpenFile( 94 filepath.Join(dst, "main.sh"), 95 os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 96 0755) 97 if err != nil { 98 return err 99 } 100 defer f.Close() 101 tpl := template.Must(template.New("root").Parse(mainShTpl)) 102 return tpl.Execute(f, map[string]interface{}{ 103 "root": s.Name, 104 "scriptpacks": spNames, 105 }) 106 } 107 108 // WriteArchive writes the contents of the ScriptPack as a tar gzip to the 109 // given path. 110 func (s *ScriptPack) WriteArchive(dst string) error { 111 // Let's just open the file we're going to write to first to verify 112 // we can write there since everything else is pointless if we can't. 113 f, err := os.Create(dst) 114 if err != nil { 115 return err 116 } 117 defer f.Close() 118 119 // Create a temporary directory to store the raw ScriptPack data 120 td, err := ioutil.TempDir("", "otto") 121 if err != nil { 122 return err 123 } 124 defer os.RemoveAll(td) 125 126 // Write the ScriptPack 127 if err := s.Write(td); err != nil { 128 return err 129 } 130 131 // Archive this ScriptPack 132 a, err := archive.CreateArchive(td, &archive.ArchiveOpts{ 133 VCS: false, 134 }) 135 if err != nil { 136 return err 137 } 138 defer a.Close() 139 140 // Write the archive to final path 141 _, err = io.Copy(f, a) 142 return err 143 } 144 145 const mainShTpl = ` 146 #!/bin/bash 147 148 # Determine the directory of this script 149 SOURCE="${BASH_SOURCE[0]}" 150 while [ -h "$SOURCE" ]; do 151 DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 152 SOURCE="$(readlink "$SOURCE")" 153 [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 154 done 155 DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 156 157 # Set the env vars 158 {{range .scriptpacks}} 159 export SCRIPTPACK_{{ . }}_ROOT="${DIR}/{{ . }}" 160 {{end}} 161 162 # Load the main of our entrypoint 163 . ${SCRIPTPACK_{{.root}}_ROOT}/main.sh 164 `