blob: 1209c4f7667461fbf1dedf828b4e93bbdb72838c [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 main
import (
"fmt"
"os"
"path/filepath"
"v.io/jiri/jiri"
"v.io/jiri/project"
"v.io/jiri/runutil"
"v.io/x/lib/cmdline"
)
// TODO(toddw): Remove the upgrade command after the transition to new-style
// manifests is complete.
var flagUpgradeRevert bool
func init() {
cmdUpgrade.Flags.BoolVar(&flagUpgradeRevert, "revert", false, `Revert the upgrade by deleting the $JIRI_ROOT/.jiri_manifest file.`)
}
var cmdUpgrade = &cmdline.Command{
Runner: jiri.RunnerFunc(runUpgrade),
Name: "upgrade",
Short: "Upgrade jiri to new-style manifests",
Long: `
Upgrades jiri to use new-style manifests.
The old (deprecated) behavior only allowed a single manifest repository, located
in $JIRI_ROOT/.manifest. The initial manifest file is located as follows:
1) Use -manifest flag, if non-empty. If it's empty...
2) Use $JIRI_ROOT/.local_manifest file. If it doesn't exist...
3) Use $JIRI_ROOT/.manifest/v2/default.
The new behavior allows multiple manifest repositories, by allowing imports to
specify project attributes describing the remote repository. The -manifest flag
is no longer allowed to be set; the initial manifest file is always located in
$JIRI_ROOT/.jiri_manifest. The .local_manifest file is ignored.
During the transition phase, both old and new behaviors are supported. The jiri
tool uses the existence of the $JIRI_ROOT/.jiri_manifest file as the signal; if
it exists we run the new behavior, otherwise we run the old behavior.
The new behavior includes a "jiri import" command, which writes or updates the
.jiri_manifest file. The new bootstrap procedure runs "jiri import", and it is
intended as a regular command to add imports to your jiri environment.
This upgrade command eases the transition by writing an initial .jiri_manifest
file for you. If you have an existing .local_manifest file, its contents will
be incorporated into the new .jiri_manifest file, and it will be renamed to
.local_manifest.BACKUP. The -revert flag deletes the .jiri_manifest file, and
restores the .local_manifest file.
`,
ArgsName: "<kind>",
ArgsLong: `
<kind> specifies the kind of upgrade, one of "v23" or "fuchsia".
`,
}
func runUpgrade(jirix *jiri.X, args []string) error {
localFile := filepath.Join(jirix.Root, ".local_manifest")
backupFile := localFile + ".BACKUP"
if flagUpgradeRevert {
// Restore .local_manifest.BACKUP if it exists.
switch _, err := jirix.NewSeq().Stat(backupFile); {
case err != nil && !runutil.IsNotExist(err):
return err
case err == nil:
if err := jirix.NewSeq().Rename(backupFile, localFile).Done(); err != nil {
return fmt.Errorf("couldn't restore %v to %v: %v", backupFile, localFile, err)
}
}
// Deleting the .jiri_manifest file reverts to the old behavior.
return jirix.NewSeq().Remove(jirix.JiriManifestFile()).Done()
}
if len(args) != 1 {
return jirix.UsageErrorf("must specify upgrade kind")
}
kind := args[0]
var argRemote, argRoot, argPath, argManifest string
switch kind {
case "v23":
argRemote = "https://vanadium.googlesource.com/manifest"
argRoot, argPath, argManifest = "", "manifest", "public"
case "fuchsia":
// TODO(toddw): Confirm these choices.
argRemote = "https://github.com/effenel/fnl-start.git"
argRoot, argPath, argManifest = "", "manifest", "default"
default:
return jirix.UsageErrorf("unknown upgrade kind %q", kind)
}
// Initialize manifest from .local_manifest.
hasLocalFile := false
manifest, err := project.ManifestFromFile(jirix, localFile)
switch {
case err != nil && !runutil.IsNotExist(err):
return err
case err == nil:
hasLocalFile = true
seenOldImport := false
var newImports []project.Import
for _, oldImport := range manifest.Imports {
if oldImport.Remote != "" {
// This is a new-style remote import, carry it over directly.
newImports = append(newImports, oldImport)
continue
}
// This is an old-style file import, convert it to the new style.
oldName := oldImport.Name
if kind == "v23" && oldName == "default" {
oldName = "public" // default no longer exists, now it's just public.
}
if !seenOldImport {
// This is the first old import, update the manifest name for the remote
// import we'll be adding later.
argManifest = oldName
seenOldImport = true
} else {
// Convert import from name="foo" to file="manifest/foo"
manifest.FileImports = append(manifest.FileImports, project.FileImport{
File: filepath.Join(argRoot, argPath, oldName),
})
}
}
manifest.Imports = newImports
}
if manifest == nil {
manifest = &project.Manifest{}
}
// Add remote import.
manifest.Imports = append(manifest.Imports, project.Import{
Manifest: argManifest,
Root: argRoot,
Project: project.Project{
Path: argPath,
Remote: argRemote,
},
})
// Write output to .jiri_manifest file.
outFile := jirix.JiriManifestFile()
if _, err := os.Stat(outFile); err == nil {
return fmt.Errorf("%v already exists", outFile)
}
if err := manifest.ToFile(jirix, outFile); err != nil {
return err
}
// Backup .local_manifest file, if it exists.
if hasLocalFile {
if err := jirix.NewSeq().Rename(localFile, backupFile).Done(); err != nil {
return fmt.Errorf("couldn't backup %v to %v: %v", localFile, backupFile, err)
}
}
return nil
}