Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
store build context ; clone becomes a bool and require appname
Browse files Browse the repository at this point in the history
Signed-off-by: Oscar Ward <[email protected]>
  • Loading branch information
Oscar Ward committed Oct 11, 2023
1 parent b6cb7f0 commit 35dfb02
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 44 deletions.
4 changes: 3 additions & 1 deletion pkg/apis/internal.acorn.io/v1/appimage.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ type VCS struct {
Modified bool `json:"modified,omitempty"`
// Untracked a true value indicates the build contained untracked files according to git
Untracked bool `json:"untracked,omitempty"`
// Acornfile contains the path and filename within the git repository that was used to build the running app
// Acornfile the path and filename within the vcs repository that was used to build the running app
Acornfile string `json:"acornfile,omitempty"`
// BuildContext the context provided
BuildContext string `json:"buildContext,omitempty"`
}

type Platform struct {
Expand Down
21 changes: 14 additions & 7 deletions pkg/cli/dev.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"fmt"
"io"

"github.com/acorn-io/runtime/pkg/autoupgrade"
Expand Down Expand Up @@ -43,10 +44,10 @@ acorn dev --name wandering-sound <IMAGE>

type Dev struct {
RunArgs
BidirectionalSync bool `usage:"In interactive mode download changes in addition to uploading" short:"b"`
Replace bool `usage:"Replace the app with only defined values, resetting undefined fields to default values" json:"replace,omitempty"` // Replace sets patchMode to false, resulting in a full update, resetting all undefined fields to their defaults
Clone string `usage:"Clone a running app"`
HelpAdvanced bool `usage:"Show verbose help text"`
BidirectionalSync bool `usage:"In interactive mode download changes in addition to uploading" short:"b"`
Replace bool `usage:"Replace the app with only defined values, resetting undefined fields to default values" json:"replace,omitempty"` // Replace sets patchMode to false, resulting in a full update, resetting all undefined fields to their defaults
Clone bool `usage:"Clone the vcs repository for the given app"`
HelpAdvanced bool `usage:"Show verbose help text"`
out io.Writer
client ClientFactory
}
Expand All @@ -62,20 +63,26 @@ func (s *Dev) Run(cmd *cobra.Command, args []string) error {
}

var imageSource imagesource.ImageSource
if s.Clone == "" {
if !s.Clone {
imageSource = imagesource.NewImageSource(s.client.AcornConfigFile(), s.File, s.ArgsFile, args, nil, z.Dereference(s.AutoUpgrade))
} else if s.Name == "" {
return fmt.Errorf("clone option must be used when running dev on an existing app")
} else {
// Get info from the running app
app, err := c.AppGet(cmd.Context(), s.Clone)
app, err := c.AppGet(cmd.Context(), s.Name)
if err != nil {
return err
}

acornfile, err := vcs.AcornfileFromApp(cmd.Context(), app)
acornfile, buildContext, err := vcs.ImageInfoFromApp(cmd.Context(), app)
if err != nil {
return err
}

bc := app.Status.Staged.AppImage.VCS.BuildContext
if bc != "" {
args = append(args, buildContext)
}
imageSource = imagesource.NewImageSource(s.client.AcornConfigFile(), acornfile, s.ArgsFile, args, nil, z.Dereference(s.AutoUpgrade))
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (c *DefaultClient) AcornImageBuild(ctx context.Context, file string, opts *
return nil, err
}

vcs := vcs.VCS(file)
vcs := vcs.VCS(file, opts.Cwd)

builder, err := c.getOrCreateBuilder(ctx, opts.BuilderName)
if err != nil {
Expand Down
9 changes: 8 additions & 1 deletion pkg/openapi/generated/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 68 additions & 34 deletions pkg/vcs/vcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package vcs

import (
"context"
"errors"
"fmt"
"net/url"
"os"
Expand All @@ -12,14 +11,19 @@ import (
apiv1 "github.com/acorn-io/runtime/pkg/apis/api.acorn.io/v1"
v1 "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
)

func VCS(filePath string) (result v1.VCS) {
func VCS(filePath, buildContextPath string) (result v1.VCS) {
absPath, err := filepath.Abs(filePath)
if err != nil {
return
}
buildContext, err := filepath.Abs(buildContextPath)
if err != nil {
return
}
repo, err := git.PlainOpenWithOptions(filepath.Dir(absPath), &git.PlainOpenOptions{
DetectDotGit: true,
})
Expand All @@ -43,6 +47,11 @@ func VCS(filePath string) (result v1.VCS) {
sb.WriteString(w.Filesystem.Root())
sb.WriteRune(filepath.Separator)
acornfile := strings.TrimPrefix(absPath, sb.String())
if buildContext == w.Filesystem.Root() {
buildContext = "."
} else {
buildContext = strings.TrimPrefix(buildContext, sb.String())
}

var (
modified, untracked bool
Expand All @@ -59,11 +68,12 @@ func VCS(filePath string) (result v1.VCS) {
}

result = v1.VCS{
Revision: head.Hash().String(),
Clean: !modified && !untracked,
Modified: modified,
Untracked: untracked,
Acornfile: acornfile,
Revision: head.Hash().String(),
Clean: !modified && !untracked,
Modified: modified,
Untracked: untracked,
Acornfile: acornfile,
BuildContext: buildContext,
}

// Set optional remotes field
Expand All @@ -79,17 +89,19 @@ func VCS(filePath string) (result v1.VCS) {
return
}

func AcornfileFromApp(ctx context.Context, app *apiv1.App) (string, error) {

func ImageInfoFromApp(ctx context.Context, app *apiv1.App) (string, string, error) {
vcs := app.Status.Staged.AppImage.VCS

if len(vcs.Remotes) == 0 {
return "", fmt.Errorf("clone can only be done on an app built from a git repository")
return "", "", fmt.Errorf("clone can only be done on an app built from a git repository")
}
if vcs.Acornfile == "" {
return "", "", fmt.Errorf("app has no acornfile information in vcs")
}

// Create auth object to use when fetching and cloning git repos
auth, err := ssh.NewSSHAgentAuth("git")
if err != nil {
return "", err
return "", "", err
}

for _, remote := range vcs.Remotes {
Expand All @@ -101,42 +113,64 @@ func AcornfileFromApp(ctx context.Context, app *apiv1.App) (string, error) {
gitUrl = remote
}

// TODO workdir named after git repo, cloned app name, or just this app's name?
idx := strings.LastIndex(gitUrl, "/")
if idx < 0 || idx >= len(gitUrl) {
fmt.Printf("failed to determine repository name %q\n", gitUrl)
idx := strings.LastIndex(remote, "/")
if idx < 0 || idx >= len(remote) {
fmt.Printf("failed to determine repository name %q\n", remote)
continue
}
workdir := filepath.Clean(strings.TrimSuffix(gitUrl[idx+1:], ".git"))
workdir := filepath.Clean(strings.TrimSuffix(remote[idx+1:], ".git"))

// Clone git repo
_, err = git.PlainCloneContext(ctx, workdir, false, &git.CloneOptions{
// Clone git repo and checkout revision
fmt.Printf("# Cloning repository %q into directory %q\n", gitUrl, workdir)
repo, err := git.PlainCloneContext(ctx, workdir, false, &git.CloneOptions{
URL: gitUrl,
Progress: os.Stderr,
Auth: auth,
Progress: os.Stderr,
})
// TODO handle ErrRepositoryAlreadyExists some way
if err != nil {
fmt.Printf("failed to clone repository %q: %s\n", gitUrl, err.Error())
continue
}
w, err := repo.Worktree()
if err != nil {
fmt.Printf("failed to get worktree from repository %q: %s\n", workdir, err.Error())
continue
}
err = w.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(vcs.Revision),
})
if err != nil {
fmt.Printf("failed to checkout revision %q for repository %q: %s\n", vcs.Revision, workdir, err.Error())
continue
}

// Create the Acornfile in the repository
acornfile := filepath.Join(workdir, vcs.Acornfile)
// TODO if acornfile exists but is different than what is cached should we overwrite?
if _, err := os.Stat(acornfile); errors.Is(err, os.ErrNotExist) {
// Acornfile does not exist so we should create it
err = os.WriteFile(acornfile, []byte(app.Status.Staged.AppImage.Acornfile), 0666)
if err != nil {
fmt.Printf("failed to create file %q in repository %q: %s", acornfile, gitUrl, err.Error())
// TODO we hit an error state but already cloned the repo, should we clean up the repo we cloned?
continue
err = os.WriteFile(acornfile, []byte(app.Status.Staged.AppImage.Acornfile), 0666)
if err != nil {
fmt.Printf("failed to create file %q in repository %q: %s\n", acornfile, workdir, err.Error())
continue
}

// Determine if the Acornfile is dirty or not
s, err := w.Status()
if err == nil {
if !s.IsClean() {
fmt.Printf("running with a dirty Acornfile %q\n", acornfile)
}
} else {
fmt.Printf("could not check for file %q in repository %q: %s", acornfile, gitUrl, err.Error())
// TODO we hit an error state but already cloned the repo, should we clean up the repo we cloned?
continue
fmt.Printf("failed to get status from worktree %q: %s\n", workdir, err.Error())
}

// Get the build context
var buildContext string
if vcs.BuildContext == "." {
buildContext = workdir
} else {
buildContext = filepath.Join(workdir, vcs.BuildContext)
}
return acornfile, nil

return acornfile, buildContext, nil
}
return "", fmt.Errorf("failed to resolve an acornfile from the app")
return "", "", fmt.Errorf("failed to resolve an acornfile from the app")
}

0 comments on commit 35dfb02

Please sign in to comment.