Skip to content
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

Add separate checkout info block to buildscripts. #3514

Closed
wants to merge 12 commits into from

Conversation

mitchell-as
Copy link
Contributor

@mitchell-as mitchell-as commented Sep 26, 2024

StoryDX-3024 Non-versioned buildscript information is clearly denoted

  • Use triple-backticks to separate checkout info from buildscript. Using triple-quotes will require a new parser and/or lexer. Since we're experimenting, let's use what we have.
  • Buildscripts always have an AtTime (it's no longer optional). I believe we had it optional for compatiblity with build expressions that didn't have a separate commit time, but all build expressions now have an associated commit time from what I can tell.
  • Renamed localcommit package to checkoutinfo
    • checkoutinfo is a primer property and all runners go through it to get commit IDs and set commit info like owner, name, and branch.
  • Changed buildplanner.GetBuildScript() to additionally accept an owner, project, branch, and commitID. It uses these fields to construct the "Project" field in the commit info section of buildscripts.
  • state reset LOCAL will always re-initialize a build script.
  • Internal change in the raw buildscript format to not store strings with quotes. (Unmarshaling strips quotes, and marshaling adds them back.)
  • Users are shown a warning if they attempt to use an outdate build script (state install, etc.).

@mitchell-as mitchell-as force-pushed the mitchell/dx-3024-2 branch 2 times, most recently from fb8182c to b19f652 Compare September 30, 2024 16:29
@mitchell-as mitchell-as requested a review from Naatan September 30, 2024 19:48
@mitchell-as mitchell-as marked this pull request as ready for review September 30, 2024 19:48
Copy link
Member

@Naatan Naatan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Nice work. Just need some slight iteration on smaller parts. The config change is probably the biggest, but ultimately should lead to an overall cleaner changeset (if not then I'm way off on that review comment; by all means call me on that).

Comment on lines 136 to 144
pj, err := model.FetchProjectByName(namespace.Owner, namespace.Project, auth)
if err != nil {
return nil, locale.WrapExternalError(err, "err_fetch_project", "", namespace.String())
}

branch, err := model.DefaultBranchForProject(pj)
if err != nil {
return nil, errs.Wrap(err, "Could not grab branch for project")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should provide this explicitly. Because we may not be on the default branch, and at this level we have on idea what the intend was. Consuming code won't know that this is setting up any branch related stuff.

Note we could just add an argument for the branch which can be nil, and then we default to this logic if it is nil. My concern is primarily about making the contract with the branch explicit with consuming code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can actually just pass "", and the build script checkout info will silently ignore it (like activestate.yaml with just a commitID and no branch). We cannot explicitly provide a branch argument because state export buildplan, state artifacts and state artifacts download only know about commit ID. We'd have to do branch lookup or ask the user to supply it on the command line. Either one is messy.

Let me know if you disagree.

return nil
}

// Note: cannot use functions from buildscript_runbit due to import cycle.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Note: cannot use functions from buildscript_runbit due to import cycle.

This is moot; we cannot (should not) use functions from runbits period.

Comment on lines 102 to 104
if !c.config.GetBool(constants.OptinBuildscriptsConfig) {
return nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a boolean argument passed to new (optinBuidscripts bool), instead of evaluating it here and having a dependency on config.

Comment on lines 22 to 34
return b.raw.CheckoutInfo.Project
}

func (b *BuildScript) SetProject(url string) {
b.raw.CheckoutInfo.Project = url
}

func (b *BuildScript) AtTime() time.Time {
return b.raw.CheckoutInfo.AtTime
}

func (b *BuildScript) SetAtTime(t time.Time) {
b.raw.AtTime = &t
b.raw.CheckoutInfo.AtTime = t
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized; this probably shouldn't be on raw at all. raw covers the build expression portion, which this is not a part of.

That said I don't recall what entanglements exist. I'd suggest changing this to the buildscript (non-raw) package itself if it is trivial, otherwise we can get to it later.

@@ -57,8 +57,8 @@ func (b *BuildScript) Merge(other *BuildScript, strategies *mono_models.MergeStr

// When merging build scripts we want to use the most recent timestamp
atTime := other.AtTime()
if atTime != nil && atTime.After(*b.AtTime()) {
b.SetAtTime(*atTime)
if atTime.After(b.AtTime()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need the nil check. Yes any new commits and realistically 99.9% of commits have the atTime var. But commits are final; we do not edit them. Meaning old commits without this are out there, and will forever be out there.

If this is a pain to support we could maybe look into erroring out, but in that scenario we need to outline to the user how they can get out of this broken state. I don't know what that would look like exactly, so if we want to remove support for older commits we'll need to at least file a story to address those older commits with an error.

Comment on lines 55 to 57
return nil, locale.NewExternalError(
"err_buildscript_checkoutinfo",
"Could not parse checkout information in the buildscript. The parser produced the following error: {{.V0}}", err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, locale.NewExternalError(
"err_buildscript_checkoutinfo",
"Could not parse checkout information in the buildscript. The parser produced the following error: {{.V0}}", err.Error())
return nil, locale.NewInputError(
"err_buildscript_checkoutinfo",
"Could not parse checkout information in the buildscript. The parser produced the following error: {{.V0}}", err.Error())

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it seems like this error should be more helpful. This isn't just any parsing error; we know it relates specifically to the checkoutinfo section.

// Owner returns the project owner from activestate.yaml.
// Note: cannot read this from buildscript because it may not exist yet.
func (c *CheckoutInfo) Owner() string {
return c.project.Owner()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update all the project methods as legacy, same as we did with the commitID. That extends to the Set..() ones also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You said in the previous PR that not everything should go into the checkoutinfo package. If we want to work with projects, we use the project package. If we want to work with buildscripts, we use the buildscript package. By making everything legacy, we're going to be forcing ourselves into buildscripts so that we end up with prime.buildscript.Owner(), etc. instead of prime.project.Owner(), etc.

That said, I have serious reservations about this. Not only is it going to balloon the PR by 4x, but I can see us walking back on this and undoing it is going to be really messy. We already walked back the CommitID() -> LegacyCommitID() transition. (That is, we went from project.CommitID() to localcommit.Get() and back to project.LegacyCommitID() via localcommit.Get().) It's a mess.

As I've been working on this PR, I've been thinking that if build script parse fails, we have no clue which project we're working on. state reset LOCAL cannot fix it because we need to parse it to get the project info. Since build scripts are far more brittle than activestate.yaml, this will probably happen pretty frequently as users make edits, or as build scripts change format over time.

As an example, start with a v46 build script and try to do something with this PR. It'll fail with a parse error. There's a lot we'll have to do to make it more robust, especially if we end up priming the primer with a buildscript instead of a projectfile.

Finally, my editor doesn't have method refactoring, so I'd either have to do this by hand or figure out how to use a new editor that has such functionality. If you really want this, can we make it a new ticket? :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's chat on Slack when you're around to figure out the approach. I totally feel you on this starting to feel messy. It's worth having another look at our use-case and seeing if this is in fact the right solution, before we commit to it.

The refactor itself isn't much of an issue here though, with tooling that's really easy to do. I'm more worrying about if this is actually going to save us pain during the transition from as.yaml > buildscripts, rather than cause pain.

Comment on lines +22 to +27
host := constants.DefaultAPIHost
if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" {
host = hostOverride
}
pjf := projectfile.NewProjectField()
err := pjf.LoadProject(fmt.Sprintf("https://%s/%s/%s", host, owner, project))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: might be nice to add the host stuff directly to the NewProjectField() constructor. Then just use the setters, and you don't need LoadProject here at all.

@mitchell-as
Copy link
Contributor Author

Per conversation on Zoom, closing this for a less invasive solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants