blob: 5e1e125f7081956b425e3bc71908524519e21b9c [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package project
import (
"encoding/xml"
"fmt"
"os"
"path/filepath"
"v.io/jiri/collect"
"v.io/jiri/tool"
)
type FakeJiriRoot struct {
Dir string
Projects map[string]string
remote string
}
const (
defaultDataDir = "data"
defaultManifest = "default"
manifestProject = ".manifest"
manifestVersion = "v2"
toolsProject = "tools"
)
// NewFakeJiriRoot is the FakeJiriRoot factory.
func NewFakeJiriRoot(ctx *tool.Context) (*FakeJiriRoot, error) {
root := &FakeJiriRoot{
Projects: map[string]string{},
}
// Create fake remote manifest and tools projects.
remoteDir, err := ctx.Run().TempDir("", "")
if err != nil {
return nil, err
}
doCleanup := true
defer func() {
if doCleanup {
root.Cleanup(ctx)
}
}()
root.remote = remoteDir
if err := root.CreateRemoteProject(ctx, manifestProject); err != nil {
return nil, err
}
if err := root.CreateRemoteProject(ctx, toolsProject); err != nil {
return nil, err
}
// Create a fake manifest.
manifestDir := filepath.Join(remoteDir, manifestProject, manifestVersion)
if err := ctx.Run().MkdirAll(manifestDir, os.FileMode(0700)); err != nil {
return nil, err
}
if err := root.WriteRemoteManifest(ctx, &Manifest{}); err != nil {
return nil, err
}
// Create a fake JIRI_ROOT.
rootDir, err := ctx.Run().TempDir("", "")
if err != nil {
return nil, err
}
root.Dir = rootDir
if err := ctx.Git().Clone(root.Projects[manifestProject], filepath.Join(root.Dir, manifestProject)); err != nil {
return nil, err
}
// Add the "tools" project and a fake "jiri" tool to the
// manifests. This is necessary to make sure that the commonly
// invoked DataDirPath() function, which uses the "jiri" tool
// configuration for its default, works.
if err := root.AddProject(ctx, Project{
Name: toolsProject,
Path: toolsProject,
Remote: root.Projects[toolsProject],
}); err != nil {
return nil, err
}
if err := root.AddTool(ctx, Tool{
Name: "jiri",
Data: defaultDataDir,
Project: toolsProject,
}); err != nil {
return nil, err
}
// Add "gerrit" and "git" hosts to the manifest, as required by the "jiri"
// tool.
if err := root.AddHost(ctx, Host{
Name: "gerrit",
Location: "git://example.com/gerrit",
}); err != nil {
return nil, err
}
if err := root.AddHost(ctx, Host{
Name: "git",
Location: "git://example.com/git",
}); err != nil {
return nil, err
}
// Update the contents of the fake JIRI_ROOT instance based on
// the information recorded in the remote manifest.
if err := root.UpdateUniverse(ctx, false); err != nil {
return nil, err
}
doCleanup = false
return root, nil
}
// Cleanup cleans up the given Vanadium root fake.
func (root FakeJiriRoot) Cleanup(ctx *tool.Context) error {
var errs []error
collect.Errors(func() error {
if root.Dir == "" {
return nil
}
return ctx.Run().RemoveAll(root.Dir)
}, &errs)
collect.Errors(func() error { return ctx.Run().RemoveAll(root.remote) }, &errs)
if len(errs) != 0 {
return fmt.Errorf("Cleanup() failed: %v", errs)
}
return nil
}
// AddHost adds the given host to a remote manifest.
func (root FakeJiriRoot) AddHost(ctx *tool.Context, host Host) error {
manifest, err := root.ReadRemoteManifest(ctx)
if err != nil {
return err
}
manifest.Hosts = append(manifest.Hosts, host)
if err := root.WriteRemoteManifest(ctx, manifest); err != nil {
return err
}
return nil
}
// AddProject adds the given project to a remote manifest.
func (root FakeJiriRoot) AddProject(ctx *tool.Context, project Project) error {
manifest, err := root.ReadRemoteManifest(ctx)
if err != nil {
return err
}
manifest.Projects = append(manifest.Projects, project)
if err := root.WriteRemoteManifest(ctx, manifest); err != nil {
return err
}
return nil
}
// AddTool adds the given tool to a remote manifest.
func (root FakeJiriRoot) AddTool(ctx *tool.Context, tool Tool) error {
manifest, err := root.ReadRemoteManifest(ctx)
if err != nil {
return err
}
manifest.Tools = append(manifest.Tools, tool)
if err := root.WriteRemoteManifest(ctx, manifest); err != nil {
return err
}
return nil
}
// DisableRemoteManifestPush disables pushes to the remote manifest
// repository.
func (root FakeJiriRoot) DisableRemoteManifestPush(ctx *tool.Context) error {
dir := tool.RootDirOpt(filepath.Join(root.remote, manifestProject))
if err := ctx.Git(dir).CheckoutBranch("master"); err != nil {
return err
}
return nil
}
// EnableRemoteManifestPush enables pushes to the remote manifest
// repository.
func (root FakeJiriRoot) EnableRemoteManifestPush(ctx *tool.Context) error {
dir := tool.RootDirOpt(filepath.Join(root.remote, manifestProject))
if !ctx.Git(dir).BranchExists("non-master") {
if err := ctx.Git(dir).CreateBranch("non-master"); err != nil {
return err
}
}
if err := ctx.Git(dir).CheckoutBranch("non-master"); err != nil {
return err
}
return nil
}
// CreateRemoteProject creates a new remote project.
func (root FakeJiriRoot) CreateRemoteProject(ctx *tool.Context, name string) error {
projectDir := filepath.Join(root.remote, name)
if err := ctx.Run().MkdirAll(projectDir, os.FileMode(0700)); err != nil {
return err
}
if err := ctx.Git().Init(projectDir); err != nil {
return err
}
if err := ctx.Git(tool.RootDirOpt(projectDir)).CommitWithMessage("initial commit"); err != nil {
return err
}
root.Projects[name] = projectDir
return nil
}
func getManifest(ctx *tool.Context) string {
manifest := ctx.Manifest()
if manifest != "" {
return manifest
}
return defaultManifest
}
// ReadLocalManifest read a manifest from the local manifest project.
func (root FakeJiriRoot) ReadLocalManifest(ctx *tool.Context) (*Manifest, error) {
path := filepath.Join(root.Dir, manifestProject, manifestVersion, getManifest(ctx))
return root.readManifest(ctx, path)
}
// ReadRemoteManifest read a manifest from the remote manifest project.
func (root FakeJiriRoot) ReadRemoteManifest(ctx *tool.Context) (*Manifest, error) {
path := filepath.Join(root.remote, manifestProject, manifestVersion, getManifest(ctx))
return root.readManifest(ctx, path)
}
func (root FakeJiriRoot) readManifest(ctx *tool.Context, path string) (*Manifest, error) {
bytes, err := ctx.Run().ReadFile(path)
if err != nil {
return nil, err
}
var manifest Manifest
if err := xml.Unmarshal(bytes, &manifest); err != nil {
return nil, fmt.Errorf("Unmarshal(%v) failed: %v", string(bytes), err)
}
return &manifest, nil
}
// UpdateUniverse synchronizes the content of the Vanadium root based
// on the content of the remote manifest.
func (root FakeJiriRoot) UpdateUniverse(ctx *tool.Context, gc bool) error {
oldRoot := os.Getenv("JIRI_ROOT")
if err := os.Setenv("JIRI_ROOT", root.Dir); err != nil {
return fmt.Errorf("Setenv() failed: %v", err)
}
defer os.Setenv("JIRI_ROOT", oldRoot)
if err := UpdateUniverse(ctx, gc); err != nil {
return err
}
return nil
}
// WriteLocalManifest writes the given manifest to the local
// manifest project.
func (root FakeJiriRoot) WriteLocalManifest(ctx *tool.Context, manifest *Manifest) error {
dir := filepath.Join(root.Dir, manifestProject)
path := filepath.Join(dir, manifestVersion, getManifest(ctx))
return root.writeManifest(ctx, manifest, dir, path)
}
// WriteRemoteManifest writes the given manifest to the remote
// manifest project.
func (root FakeJiriRoot) WriteRemoteManifest(ctx *tool.Context, manifest *Manifest) error {
dir := filepath.Join(root.remote, manifestProject)
path := filepath.Join(dir, manifestVersion, getManifest(ctx))
return root.writeManifest(ctx, manifest, dir, path)
}
func (root FakeJiriRoot) writeManifest(ctx *tool.Context, manifest *Manifest, dir, path string) error {
bytes, err := xml.Marshal(manifest)
if err != nil {
return fmt.Errorf("Marshal(%v) failed: %v", manifest, err)
}
if err := ctx.Run().WriteFile(path, bytes, os.FileMode(0600)); err != nil {
return err
}
if err := ctx.Git(tool.RootDirOpt(dir)).Add(path); err != nil {
return err
}
if err := ctx.Git(tool.RootDirOpt(dir)).Commit(); err != nil {
return err
}
return nil
}