-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation of state checkout --from-archive
.
#3444
Conversation
c2d9352
to
c896512
Compare
c896512
to
5b48da3
Compare
The lone test failure is a timeout unrelated to this PR. |
ff925c3
to
29e2bbb
Compare
…network to complete.
29e2bbb
to
78c04c7
Compare
{ | ||
Name: "from-archive", | ||
Description: locale.Tl("flag_state_checkout_from_archive", "Checkout from the given .tar.gz archive"), | ||
Value: ¶ms.FromArchive, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed the manual verification you're doing where essentially either namespace is required OR this flag is. This makes me think maybe we should treat the namespace arg more intelligently. eg. if you specify a filepath for the namespace then we use that. What do you think?
Since we use a special type for this arg we could introduce a new type that multiplexes to either namespace or filepath.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds reasonable, as long as the archive path always has an extension. Otherwise the argument is ambiguous, as it can be either org/project
, project
, or path/to/tar.gz
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I don't see any reason why it wouldn't have an extension.
internal/runbits/checkout/archive.go
Outdated
// readProject reads and returns a project namespace (with commitID) and branch from | ||
// "project.json", as well as a platformID. | ||
func readProject(dir string) (*project.Namespaced, string, strfmt.UUID, error) { | ||
projectBytes, err := fileutils.ReadFile(filepath.Join(dir, "project.json")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make project.json
a constant, and rename it to installer_config.json
.
internal/runbits/checkout/archive.go
Outdated
return nil, errs.Wrap(err, "Invalid archive: buildplan.json not found") | ||
} | ||
|
||
return buildplan.Unmarshal(buildplanBytes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still need to wrap this error.
commitID := ns.CommitID | ||
var language string | ||
if !fromArchive { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets extract the logic under this to its own function, as the bulk of the current function is inside this logic statement.
internal/runbits/runtime/runtime.go
Outdated
var buildPlan *buildplan.BuildPlan | ||
if opts.Archive != nil { | ||
buildPlan = opts.Archive.BuildPlan | ||
} else if opts.Commit != nil { | ||
buildPlan = opts.Commit.BuildPlan() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest making this a switch case, and making the default behaviour the logic from line 187. Just so the relations are clearly communicated.
internal/runbits/runtime/runtime.go
Outdated
); err != nil { | ||
} | ||
if opts.Archive != nil { | ||
rtOpts = append(rtOpts, runtime.WithFromArchive(opts.Archive.Dir, opts.Archive.PlatformID, checkout.ArtifactExt)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can rename this one to WithArchive
imo. The "From" isn't doing anything but trigger my OCD :p
func New(repo git.Repository, prime primeable) *Checkout { | ||
return &Checkout{repo, prime.Output(), prime.Config(), prime.Analytics(), "", prime.Auth()} | ||
} | ||
|
||
func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone bool) (_ string, rerr error) { | ||
func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, fromArchive bool) (_ string, rerr error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The checkout runbit doesn't seem to actually care about archives. I think we should name this according to the behaviour of the package, not according to the use-case we have external from this package. So perhaps we can name this validate
, as that's what the logic seems to do (with a little bit of cleanup
sprinkled in there, so maybe validateAndCleanup
?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone with onlyValidate
. With all of the requested changes, this makes sense to me, but let me know if you disagree and we can reconsider your suggestions.
pkg/runtime/setup.go
Outdated
// Options for setting up a runtime from an archive. | ||
FromArchiveDir string | ||
PlatformID *strfmt.UUID | ||
ArtifactExt string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make a struct to put these under. As they are all directly related and should never be used independently from one another.
pkg/runtime/setup.go
Outdated
var err error | ||
name := artifact.ArtifactID.String() + s.opts.ArtifactExt | ||
artifactFile := filepath.Join(s.opts.FromArchiveDir, name) | ||
logging.Debug("Reading file '%s' for '%s'", artifactFile, artifact.DisplayName) | ||
b, err = fileutils.ReadFile(artifactFile) | ||
if err != nil { | ||
return errs.Wrap(err, "read from archive failed") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think since this is bypassing the event reporting of downloads we will have a progress indicator for downloads that never moves.
We'll want to ensure that those events are still send, but we probably also want to update our runbit logic so that instead of "Downloading" we say "Unpacking" when using the archive approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I'll add the download events. In testing, this step went by so fast I didn't even see a download bar. I only saw a brief "Unpacking" bar since setting up from an archive does go through this step.
cp.Expect("Installing") | ||
cp.Expect("All dependencies have been installed and verified") | ||
cp.Expect("Checked out project ActiveState-CLI/AlmostEmpty") | ||
cp.ExpectExitCode(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also assert that the resulting runtime has some of the expected files. This test currently is just testing the UI/UX of the command itself, we should also verify that the end result is what we expect it to be.
Now the namespace argument can be either - org/project - project (org is inferred) - archive.tar.gz
dfbedd0
to
cbcfa75
Compare
cbcfa75
to
9ad4079
Compare
// checkout performs the checkout and returns the checkout's owner, project, commitID, branchName, | ||
// and language. | ||
func (r *Checkout) checkout( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't feel right to call this checkout
. That's what the main Run
function does (ie. creating the project file is effectively the checkout).
Suggest turning this into a function called sanitize()
that does all the cleaning up of parameters, and then separately call the clone behaviour from Run()
.
I'm not a big fan of the number of parameters this returns, but I also don't see an obvious solution that doesn't add boilerplate that I'm even less of a fan of. Just flagging it in case you have ideas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone with fetchProject
since I feel like that is the goal of this function. Fetch the project from the platform and return its complete set of parameters. If you don't like it, we'll go with sanitize
.
} | ||
|
||
func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, fromArchive bool) (_ string, rerr error) { | ||
func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, onlyValidate bool) (_ string, rerr error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onlyValidate
doesn't seem an appropriate name to me. If supplied it in fact does no validations, and it only does the checkout (ie. create the project files).
Perhaps bareCheckout
would be more appropriate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I don't want to spend too much time on a name.
err := ns.Set(params.Namespace) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err := ns.Set(params.Namespace) | |
if err != nil { | |
if err := ns.Set(params.Namespace); err != nil { |
if err != nil { | ||
return errs.Wrap(err, "Unable to read archive") | ||
} | ||
defer archive.Cleanup() | ||
params.Namespace = archive.Namespace | ||
ns = *archive.Namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ns = *archive.Namespace | |
ns = archive.Namespace |
Doesn't seem work dereferencing when all your use of it doesn't seem to care, and in fact you create a reference again when passing it to checkout.Run()
. May as well avoid the potential nil pointer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ns
cannot be a pointer because of the ns.Set()
function used earlier. The dereference here is needed, as is the passing of &ns
to checkout.Run()
. It's a product of our clever use the first argument being either a namespace or archive file.
pkg/runtime/options.go
Outdated
opts.ArtifactExt = ext | ||
} | ||
func WithArchive(dir string, platformID strfmt.UUID, ext string) SetOpt { | ||
return func(opts *Opts) { opts.FromArchive = &fromArchive{dir, platformID, ext} } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return func(opts *Opts) { opts.FromArchive = &fromArchive{dir, platformID, ext} } | |
return func(opts *Opts) { | |
opts.FromArchive = &fromArchive{dir, platformID, ext} | |
} |
pkg/runtime/setup.go
Outdated
|
||
if err := s.fireEvent(events.ArtifactDownloadSuccess{artifact.ArtifactID}); err != nil { | ||
return errs.Wrap(errs.Pack(err, err), "Could not handle ArtifactDownloadSuccess event") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized we are in fact handling unpack events separately. So I think it makes more sense that we don't send the download events and instead set the toDownload counter to 0 if we are installing from an archive, ie.
Line 114 in 6ff1b5a
artifactsToDownload := artifactsToInstall.Filter(func(a *buildplan.Artifact) bool { |
Apologies, I didn't have this in mind with my initial review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, well that complicated things. Let me know if my solution is sub-optimal.
proj, err := project.FromPath(filepath.Join(ts.Dirs.Work, "AlmostEmpty")) | ||
suite.Require().NoError(err) | ||
cachePath := filepath.Join(ts.Dirs.Cache, runtime_helpers.DirNameFromProjectDir(proj.Dir())) | ||
zlibH := filepath.Join(cachePath, "usr", "include", "zlib.h") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this will be different on mac/windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it can be whatever I say it is since I produced the test archives :) But seriously, I untarred the ingredients and looked for a common one that had the same path and used that. The tests on all three platforms are passing, so it's all good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh right, I forgot about that detail :p Ok we'll worry about this once we can test with proper payloads.
9ea49f1
to
08ebfdf
Compare
08ebfdf
to
c193c9f
Compare
Closing in favor of #3451 |
Review iteration DX-2981
@mitchell-as reopened as that other ticket was targeting this one so the review process was still workable. Though given I'm about to approve I guess this is just busywork 😅 Anyway now you have a signal to move the story along. |
We needed a
project.json
file, similar to offline installers, so here's a sample of the one I'm using in integration tests: