// gitCookies attempts to read and parse cookies from the .gitcookies file in // the users home directory. func gitCookies(ctx *tool.Context) []*http.Cookie { cookies := []*http.Cookie{} homeDir := os.Getenv("HOME") if homeDir == "" { return cookies } cookieFile := filepath.Join(homeDir, ".gitcookies") bytes, err := ctx.Run().ReadFile(cookieFile) if err != nil { return cookies } lines := strings.Split(string(bytes), "\n") for _, line := range lines { if strings.TrimSpace(line) == "" { continue } cookie, err := parseCookie(line) if err != nil { fmt.Fprintf(ctx.Stderr(), "error parsing cookie in .gitcookies: %v\n", err) } else { cookies = append(cookies, cookie) } } return cookies }
// Function retries the given function for the given number of // attempts at the given interval. func Function(ctx *tool.Context, fn func() error, opts ...RetryOpt) error { attempts, interval := defaultAttempts, defaultInterval for _, opt := range opts { switch typedOpt := opt.(type) { case AttemptsOpt: attempts = int(typedOpt) case IntervalOpt: interval = time.Duration(typedOpt) } } var err error for i := 1; i <= attempts; i++ { if i > 1 { fmt.Fprintf(ctx.Stdout(), "Attempt %d/%d:\n", i, attempts) } if err = fn(); err == nil { return nil } fmt.Fprintf(ctx.Stderr(), "%v\n", err) if i < attempts { fmt.Fprintf(ctx.Stdout(), "Wait for %v before next attempt...\n", interval) time.Sleep(interval) } } return fmt.Errorf("Failed %d times in a row. Last error:\n%v", attempts, err) }
// WARNING: this function is in the proces of being removed and replaced // by the profiles based configuration (see jiri/profiles). Use // profiles.ConfigHelper instead of this for new code. // // JiriLegacyEnvironment returns the environment variables setting for the project. // The util package captures the original state of the relevant environment // variables when the tool is initialized and every invocation of this function // updates this original state according to the jiri tool configuration. // // By default, the Go and VDL workspaces are added to the GOPATH and VDLPATH // environment variables respectively. In addition, the JIRI_PROFILE // environment variable can be used to activate an environment variable setting // for various development profiles of the project (e.g. arm, android, java, or // nacl). Unlike the default setting, the setting enabled by the JIRI_PROFILE // environment variable can override existing environment. func JiriLegacyEnvironment(ctx *tool.Context) (*envvar.Vars, error) { env := envvar.VarsFromOS() root, err := project.JiriRoot() if err != nil { return nil, err } config, err := LoadConfig(ctx) if err != nil { return nil, err } env.Set("CGO_ENABLED", "1") if err := setGoPath(ctx, env, root, config); err != nil { return nil, err } if err := setVdlPath(ctx, env, root, config); err != nil { return nil, err } if profile := os.Getenv(jiriProfileEnv); profile != "" { fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q. This can override values of existing environment variables. `, profile) switch profile { case "android": // Cross-compilation for android on linux. if err := setAndroidEnv(ctx, env, root); err != nil { return nil, err } case "arm": // Cross-compilation for arm on linux. if err := setArmEnv(ctx, env, root); err != nil { return nil, err } case "java": // Building of a Go shared library for Java. if err := setJavaEnv(ctx, env, root); err != nil { return nil, err } case "nacl": // Cross-compilation for nacl. if err := setNaclEnv(ctx, env, root); err != nil { return nil, err } default: fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile) } } if err := setSyncbaseEnv(ctx, env, root); err != nil { return nil, err } return env, nil }
// InstallTools installs the tools from the given directory into // $JIRI_ROOT/devtools/bin. func InstallTools(ctx *tool.Context, dir string) error { ctx.TimerPush("install tools") defer ctx.TimerPop() if ctx.DryRun() { // In "dry run" mode, no binaries are built. return nil } binDir, err := ToAbs(devtoolsBinDir) if err != nil { return err } fis, err := ioutil.ReadDir(dir) if err != nil { return fmt.Errorf("ReadDir(%v) failed: %v", dir, err) } failed := false for _, fi := range fis { installFn := func() error { src := filepath.Join(dir, fi.Name()) dst := filepath.Join(binDir, fi.Name()) if err := ctx.Run().Rename(src, dst); err != nil { return err } return nil } opts := runutil.Opts{Verbose: true} if err := ctx.Run().FunctionWithOpts(opts, installFn, "install tool %q", fi.Name()); err != nil { fmt.Fprintf(ctx.Stderr(), "%v\n", err) failed = true } } if failed { return cmdline.ErrExitCode(2) } // Delete old "v23" tool, and the old jiri-xprofile command. // TODO(nlacasse): Once everybody has had a chance to update, remove this // code. v23SubCmds := []string{ "jiri-xprofile", "v23", } for _, subCmd := range v23SubCmds { subCmdPath := filepath.Join(binDir, subCmd) if err := ctx.Run().RemoveAll(subCmdPath); err != nil { return err } } return nil }
func initCommand(ctx *tool.Context, args []string) error { if len(args) == 0 { return fmt.Errorf("no profiles specified") } for _, n := range args { if mgr := profiles.LookupManager(n); mgr == nil { return fmt.Errorf("profile %v is not available, use \"list --available\" to see the list of available profiles", n) } } if err := profiles.Read(ctx, manifestFlag); err != nil { fmt.Fprintf(ctx.Stderr(), "Failed to read manifest: %v", err) return err } return nil }
// buildToolsFromMaster builds and installs all jiri tools using the version // available in the local master branch of the tools repository. Notably, this // function does not perform any version control operation on the master // branch. func buildToolsFromMaster(ctx *tool.Context, tools Tools, outputDir string) error { localProjects, err := LocalProjects(ctx, FastScan) if err != nil { return err } failed := false toolsToBuild, toolProjects := Tools{}, Projects{} toolNames := []string{} // Used for logging purposes. for _, tool := range tools { // Skip tools with no package specified. Besides increasing // robustness, this step also allows us to create jiri root // fakes without having to provide an implementation for the "jiri" // tool, which every manifest needs to specify. if tool.Package == "" { continue } project, ok := localProjects[tool.Project] if !ok { fmt.Errorf("unknown project %v for tool %v", tool.Project, tool.Name) } toolProjects[tool.Project] = project toolsToBuild[tool.Name] = tool toolNames = append(toolNames, tool.Name) } updateFn := func() error { return ApplyToLocalMaster(ctx, toolProjects, func() error { return BuildTools(ctx, toolsToBuild, outputDir) }) } // Always log the output of updateFn, irrespective of // the value of the verbose flag. opts := runutil.Opts{Verbose: true} if err := ctx.Run().FunctionWithOpts(opts, updateFn, "build tools: %v", strings.Join(toolNames, " ")); err != nil { fmt.Fprintf(ctx.Stderr(), "%v\n", err) failed = true } if failed { return cmdline.ErrExitCode(2) } return nil }
// ParseNames identifies the set of projects that a jiri command should // be applied to. func ParseNames(ctx *tool.Context, args []string, defaultProjects map[string]struct{}) (map[string]Project, error) { projects, _, err := ReadManifest(ctx) if err != nil { return nil, err } result := map[string]Project{} if len(args) == 0 { // Use the default set of projects. args = set.String.ToSlice(defaultProjects) } for _, name := range args { if project, ok := projects[name]; ok { result[name] = project } else { // Issue a warning if the target project does not exist in the // project manifest. fmt.Fprintf(ctx.Stderr(), "WARNING: project %q does not exist in the project manifest and will be skipped\n", name) } } return result, nil }
// CurrentManifest returns a manifest that identifies the result of // the most recent "jiri update" invocation. func CurrentManifest(ctx *tool.Context) (*Manifest, error) { currentManifestPath, err := ToAbs(currentManifestFileName) if err != nil { return nil, err } bytes, err := ctx.Run().ReadFile(currentManifestPath) if err != nil { if os.IsNotExist(err) { fmt.Fprintf(ctx.Stderr(), `WARNING: Could not find %s. The contents of this file are stored as metadata in binaries the jiri tool builds. To fix this problem, please run "jiri update". `, currentManifestPath) return &Manifest{}, nil } return nil, err } var m Manifest if err := xml.Unmarshal(bytes, &m); err != nil { return nil, fmt.Errorf("Unmarshal(%v) failed: %v", string(bytes), err) } return &m, nil }
func updateProjects(ctx *tool.Context, remoteProjects Projects, gc bool) error { ctx.TimerPush("update projects") defer ctx.TimerPop() scanMode := FastScan if gc { scanMode = FullScan } localProjects, err := LocalProjects(ctx, scanMode) if err != nil { return err } gitHost, gitHostErr := GitHost(ctx) if gitHostErr == nil && googlesource.IsGoogleSourceHost(gitHost) { // Attempt to get the repo statuses from remote so we can detect when a // local project is already up-to-date. if repoStatuses, err := googlesource.GetRepoStatuses(ctx, gitHost); err != nil { // Log the error but don't fail. fmt.Fprintf(ctx.Stderr(), "Error fetching repo statuses from remote: %v\n", err) } else { for name, rp := range remoteProjects { status, ok := repoStatuses[rp.Name] if !ok { continue } masterRev, ok := status.Branches["master"] if !ok || masterRev == "" { continue } rp.Revision = masterRev remoteProjects[name] = rp } } } ops, err := computeOperations(localProjects, remoteProjects, gc) if err != nil { return err } for _, op := range ops { if err := op.Test(ctx); err != nil { return err } } failed := false manifest := &Manifest{Label: ctx.Manifest()} for _, op := range ops { updateFn := func() error { return op.Run(ctx, manifest) } // Always log the output of updateFn, irrespective of // the value of the verbose flag. opts := runutil.Opts{Verbose: true} if err := ctx.Run().FunctionWithOpts(opts, updateFn, "%v", op); err != nil { fmt.Fprintf(ctx.Stderr(), "%v\n", err) failed = true } } if failed { return cmdline.ErrExitCode(2) } if err := writeCurrentManifest(ctx, manifest); err != nil { return err } return nil }