github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/internal/packager/fork.go (about)

     1  package packager
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/docker/app/internal"
    11  	"github.com/docker/app/internal/yaml"
    12  	"github.com/docker/app/pkg/resto"
    13  	"github.com/docker/app/types/metadata"
    14  	"github.com/pkg/errors"
    15  	log "github.com/sirupsen/logrus"
    16  )
    17  
    18  // Fork pulls an application and creates a local fork for the user to modify
    19  func Fork(originName, forkName, outputDir string, maintainers []string) error {
    20  	imgRef, err := splitImageName(originName)
    21  	if err != nil {
    22  		return errors.Wrapf(err, "origin %q is not a valid image name", originName)
    23  	}
    24  	if forkName == "" {
    25  		forkName = internal.AppNameFromDir(imgRef.Name)
    26  	}
    27  	log.Debugf("Pulling latest version of package %s", originName)
    28  	payload, err := resto.PullConfigMulti(context.Background(), originName, resto.RegistryOptions{})
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	// create app dir in output-dir
    34  	namespace, name := splitPackageName(forkName)
    35  	appDir := filepath.Join(outputDir, internal.DirNameFromAppName(name))
    36  	if err := os.MkdirAll(appDir, 0755); err != nil {
    37  		return errors.Wrap(err, "failed to create output application directory")
    38  	}
    39  
    40  	// Create all the files on disk
    41  	if err := ExtractImagePayloadToDiskFiles(appDir, payload); err != nil {
    42  		return err
    43  	}
    44  
    45  	// Update the metadata file
    46  	fullFilepath := filepath.Join(appDir, internal.MetadataFileName)
    47  	bytes, err := ioutil.ReadFile(fullFilepath)
    48  	if err != nil {
    49  		return errors.Wrapf(err, "failed to read metadata file from: %s", fullFilepath)
    50  	}
    51  	bytes, err = updateMetadata(bytes, namespace, name, maintainers)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	if err := ioutil.WriteFile(fullFilepath, bytes, 0644); err != nil {
    56  		return errors.Wrapf(err, "failed to write metadata file: %s", fullFilepath)
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  func updateMetadata(raw []byte, namespace, name string, maintainers []string) ([]byte, error) {
    63  	// retrieve original metadata (maintainer/app name/app tag)
    64  	meta, err := loadMetadata(raw)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	// insert retrieved data in fork history section
    69  	log.Debug("Generating fork metadata")
    70  	newMeta := metadata.From(
    71  		meta,
    72  		metadata.WithName(name),
    73  		metadata.WithNamespace(namespace),
    74  		metadata.WithMaintainers(parseMaintainersData(maintainers)),
    75  	)
    76  
    77  	// update metadata file
    78  	yamlMeta, err := yaml.Marshal(newMeta)
    79  	if err != nil {
    80  		return nil, errors.Wrap(err, "failed to render metadata structure")
    81  	}
    82  	return yamlMeta, nil
    83  }
    84  
    85  func loadMetadata(raw []byte) (metadata.AppMetadata, error) {
    86  	var meta metadata.AppMetadata
    87  	if err := yaml.Unmarshal(raw, &meta); err != nil {
    88  		return meta, errors.Wrap(err, "failed to parse application metadata")
    89  	}
    90  	return meta, nil
    91  }
    92  
    93  func splitPackageName(name string) (string, string) {
    94  	ls := strings.LastIndexByte(name, '/')
    95  	if ls == -1 {
    96  		return "", name
    97  	}
    98  
    99  	return name[:ls], name[ls+1:]
   100  }