blob: 2515a8d1c8d8cd56be27cf760aa45b9374f4d745 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"fuchsia.googlesource.com/jiri"
"fuchsia.googlesource.com/jiri/git"
)
const (
SourceManifestVersion = int32(0)
)
type SourceManifest_GitCheckout struct {
// The canonicalized URL of the original repo that is considered the “source
// of truth” for the source code. Ex.
// https://chromium.googlesource.com/chromium/tools/build.git
// https://github.com/luci/recipes-py
RepoUrl string `json:"repo_url,omitempty"`
// If different from repo_url, this can be the URL of the repo that the source
// was actually fetched from (i.e. a mirror). Ex.
// https://chromium.googlesource.com/external/github.com/luci/recipes-py
//
// If this is empty, it's presumed to be equal to repo_url.
FetchUrl string `json:"fetch_url,omitempty"`
// The fully resolved revision (commit hash) of the source. Ex.
// 3617b0eea7ec74b8e731a23fed2f4070cbc284c4
//
// Note that this is the raw revision bytes, not their hex-encoded form.
Revision []byte `json:"revision,omitempty"`
// The ref that the task used to resolve the revision of the source (if any). Ex.
// refs/heads/master
// refs/changes/04/511804/4
//
// This should always be a ref on the hosted repo (not any local alias
// like 'refs/remotes/...').
//
// This should always be an absolute ref (i.e. starts with 'refs/'). An
// example of a non-absolute ref would be 'master'.
TrackingRef string `json:"tracking_ref,omitempty"`
}
type SourceManifest_Directory struct {
GitCheckout *SourceManifest_GitCheckout `json:"git_checkout,omitempty"`
}
type SourceManifest struct {
// Version will increment on backwards-incompatible changes only. Backwards
// compatible changes will not alter this version number.
//
// Currently, the only valid version number is 0.
Version int32 `json:"version"`
// Map of local file system directory path (with forward slashes) to
// a Directory message containing one or more deployments.
//
// The local path is relative to some job-specific root. This should be used
// for informational/display/organization purposes, and should not be used as
// a global primary key. i.e. if you depend on chromium/src.git being in
// a folder called “src”, I will find you and make really angry faces at you
// until you change it...(╬ಠ益ಠ). Instead, implementations should consider
// indexing by e.g. git repository URL or cipd package name as more better
// primary keys.
Directories map[string]*SourceManifest_Directory `json:"directories"`
}
func NewSourceManifest(jirix *jiri.X, projects Projects) (*SourceManifest, error) {
p := make([]Project, len(projects))
i := 0
for _, proj := range projects {
if err := proj.relativizePaths(jirix.Root); err != nil {
return nil, err
}
p[i] = proj
i++
}
sm := &SourceManifest{
Version: SourceManifestVersion,
Directories: make(map[string]*SourceManifest_Directory),
}
sort.Sort(ProjectsByPath(p))
for _, proj := range p {
gc := &SourceManifest_GitCheckout{
RepoUrl: proj.Remote,
}
g := git.NewGit(filepath.Join(jirix.Root, proj.Path))
if rev, err := g.CurrentRevisionRaw(); err != nil {
return nil, err
} else {
gc.Revision = rev
}
if proj.RemoteBranch == "" {
proj.RemoteBranch = "master"
}
gc.TrackingRef = "refs/heads/" + proj.RemoteBranch
sm.Directories[proj.Path] = &SourceManifest_Directory{GitCheckout: gc}
}
return sm, nil
}
func (sm *SourceManifest) ToFile(jirix *jiri.X, filename string) error {
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return fmtError(err)
}
out, err := json.MarshalIndent(sm, "", " ")
if err != nil {
return fmt.Errorf("failed to serialize JSON output: %s\n", err)
}
err = ioutil.WriteFile(filename, out, 0600)
if err != nil {
return fmt.Errorf("failed write JSON output to %s: %s\n", filename, err)
}
return nil
}