blob: 476a1be677a4ec730d497a0c90811c353eba6f21 [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bufio"
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/google/slothfs/gitiles"
"github.com/google/slothfs/populate"
)
// findSlothFSMount guesses where slothfs might be mounted.
func findSlothFSMount() string {
f, err := os.Open("/proc/mounts")
if err != nil {
// We're probably on OSX.
return ""
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, " ")
if len(fields) >= 3 && fields[2] == "fuse.slothfs" {
return fields[1]
}
}
return ""
}
// syncManifest fetches a manifest file, and configures a workspace
// for it.
func syncManifest(opts *gitiles.Options, mountPoint, repo, branch string) (string, error) {
service, err := gitiles.NewService(*opts)
if err != nil {
return "", err
}
mf, err := populate.FetchManifest(service, repo, branch)
if err != nil {
return "", err
}
mf.Filter()
if err := populate.DerefManifest(service, mf); err != nil {
return "", err
}
xml, err := ioutil.TempFile("", "")
if err != nil {
return "", err
}
xmlBytes, err := mf.MarshalXML()
if err != nil {
return "", err
}
if err := ioutil.WriteFile(xml.Name(), xmlBytes, 0644); err != nil {
return "", err
}
name := strings.Replace(time.Now().Format("S"+time.RFC3339), ":", "_", -1)
log.Printf("fetched manifest; configuring workspace %s", name)
if err := os.Symlink(xml.Name(), filepath.Join(mountPoint, "config", name)); err != nil {
return "", err
}
return filepath.Join(mountPoint, name), nil
}
func main() {
gitilesOptions := gitiles.DefineFlags()
newROWorkspace := flag.String("ro", "", "Set path to slothfs-repofs mount.")
mount := flag.String("mount", "", "Set slothfs mountpoint for -sync option. Autodetected if empty.")
sync := flag.Bool("sync", false, "Sync checkout to latest manifest version.")
syncBranch := flag.String("sync_branch", "master", "Use this branch for -sync.")
syncRepo := flag.String("sync_repo", "platform/manifest", "Use this repo for -sync.")
flag.Parse()
dir := "."
if len(flag.Args()) == 1 {
dir = flag.Arg(0)
} else if len(flag.Args()) > 1 {
log.Fatal("too many arguments.")
}
if *sync {
if *mount == "" {
*mount = findSlothFSMount()
if *mount == "" {
log.Fatal("could not autodetect mount point. Pass --mount option.")
}
}
var err error
*newROWorkspace, err = syncManifest(gitilesOptions, *mount, *syncRepo, *syncBranch)
if err != nil {
log.Fatalf("syncManifest: %v", err)
}
}
if *newROWorkspace == "" {
log.Fatalf("no readonly checkout given. Specify -ro DIR or -sync.")
}
log.Printf("creating symlinks to %s", *newROWorkspace)
added, changed, err := populate.Checkout(*newROWorkspace, dir)
if err != nil {
log.Fatalf("populate.Checkout: %v", err)
}
if len(changed) > 0 {
now := time.Now()
n := 0
for _, slice := range [][]string{added, changed} {
for _, c := range slice {
err := os.Chtimes(c, now, now)
if os.IsNotExist(err) {
fi, statErr := os.Lstat(c)
if statErr == nil && fi.Mode()&os.ModeSymlink != 0 {
// Ignore broken symlinks.
err = nil
}
}
if err != nil {
log.Fatalf("Chtimes(%s): %v", c, err)
}
n++
}
}
log.Printf("touched %d files", n)
} else {
log.Printf("no files were changed, %d were added; assuming fresh checkout.", len(added))
}
}