rehash a bunch of object types to make things easier to deal with
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fs/copy.go Wed May 27 20:44:56 2020 -0500
@@ -0,0 +1,100 @@
+// Copyright 2016-2019 Gary Kramlich <grim@reaperworld.com> +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + log "github.com/sirupsen/logrus" +// CopyFile copies the contents from src to dst atomically. +// If dst does not exist, CopyFile creates it with permissions perm. +// If the copy fails, copyFile aborts and dst is preserved. +func copyFile(src, dst string, perm os.FileMode) error { + in, err := os.Open(src) + tmp, err := ioutil.TempFile(filepath.Dir(dst), "") + _, err = io.Copy(tmp, in) + if err = tmp.Close(); err != nil { + if err = os.Chmod(tmp.Name(), perm); err != nil { + return os.Rename(tmp.Name(), dst) +func copyDir(src, dst string, perm os.FileMode) error { + absDst, err := filepath.Abs(dst) + err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + log.Debugf("walk: path %q", path) + // make sure we're don't descend into our destination directory. + if strings.HasPrefix(path, absDst) { + log.Debugf("%q is in the destination %q", path, absDst) + // at this point we're going to do something so we need to the real + realDst := filepath.Join(dst, strings.TrimPrefix(path, src)) + log.Debugf("src %q; dst %q; realDst %q", path, dst, realDst) + // if path is a directory, create it + err := os.Mkdir(realDst, info.Mode()) + // this will get called with the root of the volume, so we need to + // handle the error if it exists. + // at this point we have a file so let's copy it + return copyFile(path, realDst, info.Mode()) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fs/directory.go Wed May 27 20:44:56 2020 -0500
@@ -0,0 +1,87 @@
+// Copyright 2016-2020 Gary Kramlich <grim@reaperworld.com> +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// Package path provides utilities for handling filesystem paths and + log "github.com/sirupsen/logrus" +func NewDirectory(path string) (*Directory, error) { + if !strings.HasSuffix(path, "/") { + err := os.MkdirAll(path, 0700) + return &Directory{path: path}, nil +func (d *Directory) Path() string { +func (d *Directory) Import(src, dst string) error { + file, err := os.Stat(src) + realDst := filepath.Join(d.path, dst) + log.Debugf("importing directory %q to %q", src, realDst) + return copyDir(src, realDst, file.Mode()) + log.Debugf("importing file %q to %q", src, realDst) + return copyFile(src, realDst, file.Mode()) +func (d *Directory) Export(src, dst string) error { + realSrc := filepath.Join(d.path, src) + file, err := os.Stat(realSrc) + log.Debugf("exporting directory %q to %q", realSrc, dst) + return copyDir(realSrc, dst, file.Mode()) + log.Debugf("exporting file %q to %q", realSrc, dst) + return copyFile(realSrc, dst, file.Mode()) --- a/workspace/fileio.go Wed May 27 19:45:40 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-// Copyright 2016-2019 Gary Kramlich <grim@reaperworld.com>
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
- log "github.com/sirupsen/logrus"
-// CopyFile copies the contents from src to dst atomically.
-// If dst does not exist, CopyFile creates it with permissions perm.
-// If the copy fails, CopyFile aborts and dst is preserved.
-func (ws *Workspace) copyFile(src, dst string, perm os.FileMode) error {
- in, err := os.Open(src)
- tmp, err := ioutil.TempFile(filepath.Dir(dst), "")
- _, err = io.Copy(tmp, in)
- if err = tmp.Close(); err != nil {
- if err = os.Chmod(tmp.Name(), perm); err != nil {
- return os.Rename(tmp.Name(), dst)
-func (ws *Workspace) copyDir(src, dst string, perm os.FileMode) error {
- absDst, err := filepath.Abs(dst)
- err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
- log.Debugf("walk: path %q", path)
- // make sure we're copying files that are at the same level as convey.yml or
- if !strings.HasPrefix(path, ws.parent) {
- "attempted to copy %q which is outside of the run context of %q",
- // make sure we're don't descend into our destination directory.
- if strings.HasPrefix(path, absDst) {
- log.Debugf("%q is in our destination %q", path, absDst)
- // at this point we're going to do something so we need to the real
- realDst := filepath.Join(dst, strings.TrimPrefix(path, ws.parent))
- log.Debugf("src %q; dst %q; realDst %q", path, dst, realDst)
- // if path is a directory, create it
- err := os.Mkdir(realDst, info.Mode())
- // this will get called with the root of the volume, so we need to
- // handle the error if it exists.
- // at this point we have a file so let's copy it
- return ws.copyFile(path, realDst, info.Mode())
--- a/workspace/taskdir.go Wed May 27 19:45:40 2020 -0500
+++ b/workspace/taskdir.go Wed May 27 20:44:56 2020 -0500
@@ -17,70 +17,11 @@
- log "github.com/sirupsen/logrus"
+ "keep.imfreedom.org/grim/convey/fs" -type TaskDirectory struct {
-func (ws *Workspace) CreateTaskDirectory(name string) (*TaskDirectory, error) {
- dir := filepath.Join(ws.path, name)
- if !strings.HasSuffix(dir, "/") {
- err := os.MkdirAll(dir, 0700)
+func (ws *Workspace) CreateTaskDirectory(name string) (*fs.Directory, error) { + return fs.NewDirectory(filepath.Join(ws.path, name))
-func (td *TaskDirectory) Import(src, dst string) error {
- file, err := os.Stat(src)
- realDst := filepath.Join(td.path, dst)
- log.Debugf("importing directory %q to %q", src, realDst)
- return td.ws.copyDir(src, realDst, file.Mode())
- log.Debugf("importing file %q to %q", src, realDst)
- return td.ws.copyFile(src, realDst, file.Mode())
-func (td *TaskDirectory) Export(src, dst string) error {
- realSrc := filepath.Join(td.path, src)
- file, err := os.Stat(realSrc)
- log.Debugf("importing directory %q to %q", realSrc, dst)
- return td.ws.copyDir(realSrc, dst, file.Mode())
- log.Debugf("importing file %q to %q", realSrc, dst)
- return td.ws.copyFile(realSrc, dst, file.Mode())
-func (td *TaskDirectory) Path() string {
--- a/workspace/volume.go Wed May 27 19:45:40 2020 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-// Copyright 2016-2019 Gary Kramlich <grim@reaperworld.com>
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
- log "github.com/sirupsen/logrus"
-func (vol *Volume) Import(src, dst string) error {
- file, err := os.Stat(src)
- realDst := filepath.Join(vol.path, dst)
- log.Debugf("importing directory %q to %q", src, realDst)
- return vol.ws.copyDir(src, realDst, file.Mode())
- log.Debugf("importing file %q to %q", src, realDst)
- return vol.ws.copyFile(src, realDst, file.Mode())
-func (vol *Volume) Export(src, dst string) error {
- realSrc := filepath.Join(vol.path, src)
- file, err := os.Stat(realSrc)
- log.Debugf("importing directory %q to %q", realSrc, dst)
- return vol.ws.copyDir(realSrc, dst, file.Mode())
- log.Debugf("importing file %q to %q", realSrc, dst)
- return vol.ws.copyFile(realSrc, dst, file.Mode())
-func (vol *Volume) Path() string {
--- a/workspace/workspace.go Wed May 27 19:45:40 2020 -0500
+++ b/workspace/workspace.go Wed May 27 20:44:56 2020 -0500
@@ -22,13 +22,15 @@
log "github.com/sirupsen/logrus"
+ "keep.imfreedom.org/grim/convey/fs"
func New(parent string) (*Workspace, error) {
@@ -43,8 +45,7 @@
- volumePath := filepath.Join(path, "volume")
- err = os.Mkdir(volumePath, 0700)
+ volume, err := fs.NewDirectory(filepath.Join(path, "volume")) @@ -53,11 +54,7 @@
- workspace.volume = &Volume{
@@ -80,6 +77,6 @@
-func (ws *Workspace) Volume() *Volume {
+func (ws *Workspace) Volume() *fs.Directory {