2016-12-01 16:45:33 +03:00
|
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2016-11-30 03:41:16 +03:00
|
|
|
|
package main
|
|
|
|
|
|
2016-11-30 04:03:08 +03:00
|
|
|
|
import (
|
2016-12-01 05:05:22 +03:00
|
|
|
|
"encoding/json"
|
2016-11-30 04:03:08 +03:00
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2016-11-30 05:00:11 +03:00
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
"github.com/sdboyer/gps"
|
2016-11-30 04:03:08 +03:00
|
|
|
|
)
|
|
|
|
|
|
2016-11-30 03:41:16 +03:00
|
|
|
|
var initCmd = &command{
|
2016-11-30 04:03:08 +03:00
|
|
|
|
fn: runInit,
|
2016-11-30 03:41:16 +03:00
|
|
|
|
name: "init",
|
|
|
|
|
short: `
|
|
|
|
|
Write Manifest file in the root of the project directory.
|
|
|
|
|
`,
|
|
|
|
|
long: `
|
|
|
|
|
Populates Manifest file with current deps of this project.
|
|
|
|
|
The specified version of each dependent repository is the version
|
|
|
|
|
available in the user's workspaces (as specified by GOPATH).
|
|
|
|
|
If the dependency is not present in any workspaces it is not be
|
|
|
|
|
included in the Manifest.
|
|
|
|
|
Writes Lock file(?)
|
|
|
|
|
Creates vendor/ directory(?)
|
2016-11-30 05:00:11 +03:00
|
|
|
|
|
|
|
|
|
Notes from DOC:
|
|
|
|
|
Reads existing dependency information written by other tools.
|
|
|
|
|
Noting any information that is lost (unsupported features, etc).
|
|
|
|
|
This functionality will be removed after a transition period (1 year?).
|
|
|
|
|
Write Manifest file in the root of the project directory.
|
|
|
|
|
* Populates Manifest file with current deps of this project.
|
|
|
|
|
The specified version of each dependent repository is the version available in the user's workspaces (including vendor/ directories, if present).
|
|
|
|
|
If the dependency is not present in any workspaces it will not be included in the Manifest. A warning will be issued for these dependencies.
|
|
|
|
|
Creates vendor/ directory (if it does not exist)
|
|
|
|
|
Copies the project’s dependencies from the workspace to the vendor/ directory (if they’re not already there).
|
|
|
|
|
Writes a Lockfile in the root of the project directory.
|
|
|
|
|
Invoke “dep status”.
|
2016-11-30 03:41:16 +03:00
|
|
|
|
`,
|
|
|
|
|
}
|
2016-11-30 04:03:08 +03:00
|
|
|
|
|
2016-12-02 07:24:16 +03:00
|
|
|
|
// determineProjectRoot takes an absolute path and compares it against declared
|
|
|
|
|
// GOPATH(s) to determine what portion of the input path should be treated as an
|
|
|
|
|
// import path - as a project root.
|
|
|
|
|
//
|
|
|
|
|
// The second returned string indicates which GOPATH value was used.
|
|
|
|
|
func determineProjectRoot(path string) (string, string, error) {
|
2016-11-30 05:00:11 +03:00
|
|
|
|
gopath := os.Getenv("GOPATH")
|
|
|
|
|
for _, gp := range filepath.SplitList(gopath) {
|
|
|
|
|
srcprefix := filepath.Join(gp, "src") + string(filepath.Separator)
|
|
|
|
|
if strings.HasPrefix(path, srcprefix) {
|
|
|
|
|
// filepath.ToSlash because we're dealing with an import path now,
|
|
|
|
|
// not an fs path
|
2016-12-02 07:24:16 +03:00
|
|
|
|
return filepath.ToSlash(strings.TrimPrefix(path, srcprefix)), gp, nil
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-02 07:24:16 +03:00
|
|
|
|
return "", "", fmt.Errorf("%s not in any $GOPATH", path)
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-30 04:03:08 +03:00
|
|
|
|
func runInit(args []string) error {
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
|
return fmt.Errorf("Too many args: %d", len(args))
|
|
|
|
|
}
|
|
|
|
|
var p string
|
|
|
|
|
var err error
|
|
|
|
|
if len(args) == 0 {
|
|
|
|
|
p, err = os.Getwd()
|
|
|
|
|
if err != nil {
|
2016-11-30 05:00:11 +03:00
|
|
|
|
return errors.Wrap(err, "os.Getwd")
|
2016-11-30 04:03:08 +03:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
p = args[0]
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-30 05:00:11 +03:00
|
|
|
|
mf := filepath.Join(p, manifestName)
|
2016-12-01 05:05:22 +03:00
|
|
|
|
lf := filepath.Join(p, lockName)
|
2016-11-30 04:03:08 +03:00
|
|
|
|
|
|
|
|
|
// TODO: Lstat ? Do we care?
|
2016-12-01 17:02:17 +03:00
|
|
|
|
_, merr := os.Stat(mf)
|
|
|
|
|
if merr == nil {
|
|
|
|
|
return fmt.Errorf("Manifest file %q already exists", mf)
|
2016-12-01 05:05:22 +03:00
|
|
|
|
}
|
2016-12-01 17:02:17 +03:00
|
|
|
|
_, lerr := os.Stat(lf)
|
|
|
|
|
|
|
|
|
|
if os.IsNotExist(merr) {
|
|
|
|
|
if lerr == nil {
|
|
|
|
|
return fmt.Errorf("Invalid state: manifest %q does not exist, but lock %q does.", mf, lf)
|
|
|
|
|
} else if !os.IsNotExist(lerr) {
|
|
|
|
|
return errors.Wrap(lerr, "stat lockfile")
|
|
|
|
|
}
|
2016-12-01 05:05:22 +03:00
|
|
|
|
|
2016-12-02 07:24:16 +03:00
|
|
|
|
cpr, gopath, err := determineProjectRoot(p)
|
2016-11-30 05:14:35 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "determineProjectRoot")
|
|
|
|
|
}
|
2016-12-01 05:05:22 +03:00
|
|
|
|
pkgT, err := gps.ListPackages(p, cpr)
|
2016-11-30 05:14:35 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "gps.ListPackages")
|
|
|
|
|
}
|
|
|
|
|
sm, err := getSourceManager()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "getSourceManager")
|
|
|
|
|
}
|
|
|
|
|
defer sm.Release()
|
2016-12-01 04:15:13 +03:00
|
|
|
|
|
|
|
|
|
// TODO: This is just wrong, need to figure out manifest file structure
|
|
|
|
|
m := manifest{
|
|
|
|
|
Dependencies: make(gps.ProjectConstraints),
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 04:49:33 +03:00
|
|
|
|
processed := make(map[gps.ProjectRoot][]string)
|
2016-12-02 06:24:13 +03:00
|
|
|
|
packages := make(map[string]bool)
|
2016-12-01 05:05:22 +03:00
|
|
|
|
notondisk := make(map[gps.ProjectRoot]bool)
|
2016-12-01 04:15:13 +03:00
|
|
|
|
ondisk := make(map[gps.ProjectRoot]gps.Version)
|
2016-11-30 05:14:35 +03:00
|
|
|
|
for _, v := range pkgT.Packages {
|
|
|
|
|
// TODO: Some errors maybe should not be skipped ;-)
|
|
|
|
|
if v.Err != nil {
|
|
|
|
|
continue
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
2016-11-30 05:14:35 +03:00
|
|
|
|
|
|
|
|
|
for _, i := range v.P.Imports {
|
|
|
|
|
if isStdLib(i) { // TODO: Replace with non stubbed version
|
2016-11-30 05:00:11 +03:00
|
|
|
|
continue
|
|
|
|
|
}
|
2016-11-30 05:14:35 +03:00
|
|
|
|
pr, err := sm.DeduceProjectRoot(i)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "sm.DeduceProjectRoot") // TODO: Skip and report ?
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
2016-12-01 04:15:13 +03:00
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
packages[i] = true
|
2016-12-02 04:49:33 +03:00
|
|
|
|
if _, ok := processed[pr]; ok {
|
|
|
|
|
if !contains(processed[pr], i) {
|
|
|
|
|
processed[pr] = append(processed[pr], i)
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 04:15:13 +03:00
|
|
|
|
continue
|
|
|
|
|
}
|
2016-12-02 04:49:33 +03:00
|
|
|
|
processed[pr] = []string{i}
|
2016-12-01 04:15:13 +03:00
|
|
|
|
|
2016-12-01 17:02:17 +03:00
|
|
|
|
v, err := versionInWorkspace(pr)
|
2016-12-01 04:15:13 +03:00
|
|
|
|
if err != nil {
|
2016-12-01 05:05:22 +03:00
|
|
|
|
notondisk[pr] = true
|
2016-12-01 04:15:13 +03:00
|
|
|
|
fmt.Printf("Could not determine version for %q, omitting from generated manifest\n", pr)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ondisk[pr] = v
|
|
|
|
|
pp := gps.ProjectProperties{}
|
|
|
|
|
switch v.Type() {
|
|
|
|
|
case "branch", "version", "rev":
|
|
|
|
|
pp.Constraint = v
|
|
|
|
|
case "semver":
|
|
|
|
|
c, _ := gps.NewSemverConstraint("^" + v.String())
|
|
|
|
|
pp.Constraint = c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m.Dependencies[pr] = pp
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
2016-11-30 04:03:08 +03:00
|
|
|
|
}
|
2016-12-01 04:15:13 +03:00
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
// Explore the packages we've found for transitive deps, either
|
|
|
|
|
// completing the lock or identifying (more) missing projects that we'll
|
|
|
|
|
// need to ask gps to solve for us.
|
|
|
|
|
colors := make(map[string]uint8)
|
|
|
|
|
const (
|
|
|
|
|
white uint8 = iota
|
|
|
|
|
grey
|
|
|
|
|
black
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// cache of PackageTrees, so we don't parse projects more than once
|
|
|
|
|
ptrees := make(map[gps.ProjectRoot]gps.PackageTree)
|
|
|
|
|
|
|
|
|
|
// depth-first traverser
|
|
|
|
|
var dft func(string) error
|
|
|
|
|
dft = func(pkg string) error {
|
|
|
|
|
switch colors[pkg] {
|
|
|
|
|
case white:
|
|
|
|
|
colors[pkg] = grey
|
|
|
|
|
|
|
|
|
|
pr, err := sm.DeduceProjectRoot(pkg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "could not deduce project root for "+pkg)
|
|
|
|
|
}
|
2016-12-02 04:49:33 +03:00
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
// We already visited this project root earlier via some other
|
|
|
|
|
// pkg within it, and made the decision that it's not on disk.
|
|
|
|
|
// Respect that decision, and pop the stack.
|
|
|
|
|
if notondisk[pr] {
|
|
|
|
|
colors[pkg] = black
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptree, has := ptrees[pr]
|
|
|
|
|
if !has {
|
|
|
|
|
// It's fine if the root does not exist - it indicates that this
|
|
|
|
|
// project is not present in the workspace, and so we need to
|
|
|
|
|
// solve to deal with this dep.
|
2016-12-02 07:24:16 +03:00
|
|
|
|
r := filepath.Join(gopath, "src", string(pr))
|
2016-12-02 06:24:13 +03:00
|
|
|
|
_, err := os.Lstat(r)
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
colors[pkg] = black
|
|
|
|
|
notondisk[pr] = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2016-12-02 04:49:33 +03:00
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
ptree, err = gps.ListPackages(r, string(pr))
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Any error here other than an a nonexistent dir (which
|
|
|
|
|
// can't happen because we covered that case above) is
|
|
|
|
|
// probably critical, so bail out.
|
|
|
|
|
return errors.Wrap(err, "gps.ListPackages")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rm := ptree.ExternalReach(false, false, nil)
|
|
|
|
|
reached, ok := rm[pkg]
|
2016-12-02 04:49:33 +03:00
|
|
|
|
if !ok {
|
2016-12-02 06:24:13 +03:00
|
|
|
|
colors[pkg] = black
|
2016-12-02 04:49:33 +03:00
|
|
|
|
// not on disk...
|
2016-12-02 06:24:13 +03:00
|
|
|
|
notondisk[pr] = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := processed[pr]; ok {
|
|
|
|
|
if !contains(processed[pr], pkg) {
|
|
|
|
|
processed[pr] = append(processed[pr], pkg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
processed[pr] = []string{pkg}
|
2016-12-02 04:49:33 +03:00
|
|
|
|
}
|
2016-12-02 06:24:13 +03:00
|
|
|
|
|
2016-12-02 07:24:16 +03:00
|
|
|
|
// project must be on disk at this point; question is
|
|
|
|
|
// whether we're first seeing it here, in the transitive
|
|
|
|
|
// exploration, or if it arose in the direct dep parts
|
|
|
|
|
if _, in := ondisk[pr]; !in {
|
|
|
|
|
v, err := versionInWorkspace(pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
colors[pkg] = black
|
|
|
|
|
notondisk[pr] = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
ondisk[pr] = v
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 07:37:10 +03:00
|
|
|
|
// recurse
|
2016-12-02 06:24:13 +03:00
|
|
|
|
for _, rpkg := range reached {
|
2016-12-02 07:24:16 +03:00
|
|
|
|
if isStdLib(rpkg) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
err := dft(rpkg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
colors[pkg] = black
|
|
|
|
|
case grey:
|
|
|
|
|
return fmt.Errorf("Import cycle detected on %s", pkg)
|
2016-12-02 04:49:33 +03:00
|
|
|
|
}
|
2016-12-02 07:24:16 +03:00
|
|
|
|
return nil
|
2016-12-02 04:49:33 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
// run the depth-first traversal from the set of immediate external
|
|
|
|
|
// package imports we found in the current project
|
|
|
|
|
for pkg := range packages {
|
|
|
|
|
err := dft(pkg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err // already errors.Wrap()'d internally
|
|
|
|
|
}
|
2016-12-01 05:05:22 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 07:37:10 +03:00
|
|
|
|
// Make an initial lock from what knowledge we've collected about the
|
|
|
|
|
// versions on disk
|
2016-12-01 05:05:22 +03:00
|
|
|
|
l := lock{
|
|
|
|
|
P: make([]gps.LockedProject, 0, len(ondisk)),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for pr, v := range ondisk {
|
2016-12-02 07:24:55 +03:00
|
|
|
|
// That we have to chop off these path prefixes is a symptom of
|
|
|
|
|
// a problem in gps itself
|
|
|
|
|
pkgs := make([]string, 0, len(processed[pr]))
|
|
|
|
|
prslash := string(pr) + "/"
|
|
|
|
|
for _, pkg := range processed[pr] {
|
|
|
|
|
if pkg == string(pr) {
|
|
|
|
|
pkgs = append(pkgs, ".")
|
|
|
|
|
} else {
|
|
|
|
|
pkgs = append(pkgs, strings.TrimPrefix(pkg, prslash))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 05:05:22 +03:00
|
|
|
|
l.P = append(l.P, gps.NewLockedProject(
|
2016-12-02 07:24:55 +03:00
|
|
|
|
gps.ProjectIdentifier{ProjectRoot: pr}, v, pkgs),
|
2016-12-01 05:05:22 +03:00
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
var l2 *lock
|
|
|
|
|
if len(notondisk) > 0 {
|
|
|
|
|
params := gps.SolveParameters{
|
|
|
|
|
RootDir: p,
|
|
|
|
|
RootPackageTree: pkgT,
|
|
|
|
|
Manifest: &m,
|
|
|
|
|
Lock: &l,
|
|
|
|
|
}
|
|
|
|
|
s, err := gps.Prepare(params, sm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "prepare solver")
|
|
|
|
|
}
|
2016-12-01 05:05:22 +03:00
|
|
|
|
|
2016-12-02 06:24:13 +03:00
|
|
|
|
soln, err := s.Solve()
|
|
|
|
|
if err != nil {
|
|
|
|
|
handleAllTheFailuresOfTheWorld(err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
l2 = lockFromInterface(soln)
|
|
|
|
|
} else {
|
|
|
|
|
l2 = &l
|
2016-12-01 05:05:22 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := writeFile(mf, &m); err != nil {
|
|
|
|
|
return errors.Wrap(err, "writeFile for manifest")
|
|
|
|
|
}
|
2016-12-01 17:02:17 +03:00
|
|
|
|
if err := writeFile(lf, l2); err != nil {
|
2016-12-01 05:05:22 +03:00
|
|
|
|
return errors.Wrap(err, "writeFile for lock")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2016-11-30 04:03:08 +03:00
|
|
|
|
}
|
2016-12-01 17:02:17 +03:00
|
|
|
|
|
2016-11-30 05:14:35 +03:00
|
|
|
|
return errors.Wrap(err, "runInit fall through")
|
2016-11-30 04:03:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 04:49:33 +03:00
|
|
|
|
// contains checks if a array of strings contains a value
|
|
|
|
|
func contains(a []string, b string) bool {
|
2016-12-02 06:24:13 +03:00
|
|
|
|
for _, v := range a {
|
2016-12-02 04:49:33 +03:00
|
|
|
|
if b == v {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-03 02:44:00 +03:00
|
|
|
|
// isStdLib reports whether $GOROOT/src/path should be considered
|
|
|
|
|
// part of the standard distribution. For historical reasons we allow people to add
|
|
|
|
|
// their own code to $GOROOT instead of using $GOPATH, but we assume that
|
|
|
|
|
// code will start with a domain name (dot in the first element).
|
|
|
|
|
// This was loving taken from src/cmd/go/pkg.go in Go's code (isStandardImportPath).
|
|
|
|
|
func isStdLib(path string) bool {
|
|
|
|
|
i := strings.Index(path, "/")
|
|
|
|
|
if i < 0 {
|
|
|
|
|
i = len(path)
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
2016-12-03 02:44:00 +03:00
|
|
|
|
elem := path[:i]
|
|
|
|
|
return !strings.Contains(elem, ".")
|
2016-11-30 05:00:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 17:02:17 +03:00
|
|
|
|
// TODO stub; considerable effort required for the real impl
|
|
|
|
|
func versionInWorkspace(pr gps.ProjectRoot) (gps.Version, error) {
|
2016-12-01 04:15:13 +03:00
|
|
|
|
switch pr {
|
|
|
|
|
case "github.com/sdboyer/gps":
|
|
|
|
|
return gps.NewVersion("v0.12.0").Is("9ca61cb4e9851c80bb537e7d8e1be56e18e03cc9"), nil
|
|
|
|
|
case "github.com/Masterminds/semver":
|
|
|
|
|
return gps.NewBranch("2.x").Is("b3ef6b1808e9889dfb8767ce7068db923a3d07de"), nil
|
2016-12-02 07:24:16 +03:00
|
|
|
|
case "github.com/Masterminds/vcs":
|
|
|
|
|
return gps.Revision("fbe9fb6ad5b5f35b3e82a7c21123cfc526cbf895"), nil
|
2016-12-01 04:15:13 +03:00
|
|
|
|
case "github.com/pkg/errors":
|
|
|
|
|
return gps.NewVersion("v0.8.0").Is("645ef00459ed84a119197bfb8d8205042c6df63d"), nil
|
2016-12-02 07:24:16 +03:00
|
|
|
|
case "github.com/armon/go-radix":
|
|
|
|
|
return gps.NewBranch("master").Is("4239b77079c7b5d1243b7b4736304ce8ddb6f0f2"), nil
|
|
|
|
|
case "github.com/termie/go-shutil":
|
|
|
|
|
return gps.NewBranch("master").Is("4239b77079c7b5d1243b7b4736304ce8ddb6f0f2"), nil
|
2016-12-01 04:15:13 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unknown project")
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-01 05:05:22 +03:00
|
|
|
|
// TODO solve failures can be really creative - we need to be similarly creative
|
|
|
|
|
// in handling them and informing the user appropriately
|
|
|
|
|
func handleAllTheFailuresOfTheWorld(err error) {
|
|
|
|
|
fmt.Println("ouchie, solve error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeFile(path string, in json.Marshaler) error {
|
2016-11-30 04:03:08 +03:00
|
|
|
|
f, err := os.Create(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
2016-11-30 05:00:11 +03:00
|
|
|
|
|
2016-12-01 05:05:22 +03:00
|
|
|
|
b, err := in.MarshalJSON()
|
2016-12-01 04:15:13 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = f.Write(b)
|
|
|
|
|
return err
|
2016-11-30 04:03:08 +03:00
|
|
|
|
}
|