-
Notifications
You must be signed in to change notification settings - Fork 208
fix(cli): respect deployment intent and make template seeding retry-safe #1823
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
Changes from all commits
825d9a4
4703dbd
ad933a8
df4b413
7588057
470ad3b
548a057
8638a8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -680,12 +680,6 @@ func runPushDeployable( | |
| return nil | ||
| } | ||
|
|
||
| if resource.Name == "function" { | ||
| if err := context.materializePendingFunctionRepositories(entries); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| pushCode := options.Code | ||
| if pushCode { | ||
| confirmed, err := context.prompter.Confirm(prompt.Question{ | ||
|
|
@@ -699,6 +693,12 @@ func runPushDeployable( | |
| pushCode = confirmed | ||
| } | ||
|
|
||
| if pushCode && resource.Name == "function" { | ||
| if err := context.materializePendingFunctionRepositories(entries); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| activate := true | ||
| if pushCode && !options.ActivateSet { | ||
| // --force answers this rather than asking anyway: --force means "do not | ||
|
|
@@ -736,7 +736,17 @@ func runPushDeployable( | |
| } | ||
|
|
||
| summary := pushSummary{} | ||
| if resource.Name == "function" && len(entries) > 1 { | ||
| parallel := resource.Name == "function" && len(entries) > 1 | ||
| for _, entry := range entries { | ||
| if entry.GetString("templateRepository") != "" { | ||
| // Template deployment consumes and persists one-shot state. Keep that | ||
| // rare batch sequential so workers never write the shared config at | ||
| // the same time. | ||
| parallel = false | ||
| break | ||
| } | ||
| } | ||
| if parallel { | ||
| // Individual spinners own one terminal row and cannot safely redraw the | ||
| // same row from several goroutines. A synchronized writer keeps every | ||
| // line intact and deliberately makes parallel progress use the spinner's | ||
|
|
@@ -1074,11 +1084,13 @@ func (c *pushContext) pushDeployable( | |
| } | ||
|
|
||
| err = c.api.Call("PUT", resource.Path+"/"+url.PathEscape(id), | ||
| writeBody(entry, resource.WriteKeys, resource.OmitWhenEmpty, "", ""), nil) | ||
| pendingSafeBody(entry, writeBody( | ||
| entry, resource.WriteKeys, resource.OmitWhenEmpty, "", "")), nil) | ||
| } else { | ||
| err = c.api.Call("POST", resource.Path, | ||
| writeBody(entry, resource.WriteKeys, resource.OmitWhenEmpty, | ||
| resource.IDField, id), nil) | ||
| pendingSafeBody(entry, writeBody( | ||
| entry, resource.WriteKeys, resource.OmitWhenEmpty, | ||
| resource.IDField, id)), nil) | ||
| } | ||
| if err != nil { | ||
| recordPushFailure(command, resource, name, err.Error(), summary) | ||
|
|
@@ -1135,6 +1147,24 @@ func recordPushFailure( | |
| summary.Failed = append(summary.Failed, failedDeployment{Name: name, Reason: reason}) | ||
| } | ||
|
|
||
| // pendingSafeBody strips VCS fields while a new repository is still pending. | ||
| // A settings-only push must not connect the function before that repository | ||
| // exists. | ||
| func pendingSafeBody(entry, body *jsonx.Object) *jsonx.Object { | ||
| if !entry.GetBool("providerRepositoryPending") { | ||
| return body | ||
| } | ||
| for _, key := range []string{ | ||
| "installationId", "providerRepositoryId", "providerBranch", | ||
| "providerSilentMode", "providerRootDirectory", "providerBranches", | ||
| "providerPaths", | ||
| } { | ||
| body.Delete(key) | ||
| } | ||
|
|
||
| return body | ||
| } | ||
|
|
||
| // writeBody builds a create or update body from the config entry. | ||
| // | ||
| // Only keys the config actually carries are sent. An absent key is omitted | ||
|
|
@@ -1407,7 +1437,8 @@ func (c *pushContext) createGitFunctionDeployment( | |
| body.Set("activate", activate) | ||
|
|
||
| path := "/functions/" + functionID + "/deployments/vcs" | ||
| if entry.GetString("templateRepository") != "" { | ||
| template := entry.GetString("templateRepository") != "" | ||
| if template { | ||
| path = "/functions/" + functionID + "/deployments/template" | ||
| body.Set("repository", entry.GetString("templateRepository")) | ||
| body.Set("owner", entry.GetString("templateOwner")) | ||
|
|
@@ -1419,24 +1450,40 @@ func (c *pushContext) createGitFunctionDeployment( | |
| body.Set("reference", entry.GetString("providerBranch")) | ||
| } | ||
|
|
||
| deployment := jsonx.NewObject() | ||
| if err := c.api.Call("POST", path, body, deployment); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Template coordinates are one-shot. Keeping them would merge the starter | ||
| // into the repository again on every explicit CLI deployment. | ||
| if entry.GetString("templateRepository") != "" { | ||
| // Consume template coordinates before the request. Once an HTTP request is | ||
| // attempted its outcome can be ambiguous, so leaving them consumed is the | ||
| // only way a retry cannot seed the starter twice. | ||
| if template { | ||
| saved := map[string]any{} | ||
| for _, key := range []string{ | ||
| "templateRepository", "templateOwner", "templateRootDirectory", | ||
| "templateReference", "templateReferenceType", | ||
| } { | ||
| if value, ok := entry.Get(key); ok { | ||
| saved[key] = value | ||
| } | ||
| entry.Delete(key) | ||
| } | ||
| c.local.UpsertByID("functions", entry) | ||
| if err := c.local.Write(); err != nil { | ||
| return nil, fmt.Errorf("deployment created but template state could not be saved: %w", err) | ||
| for key, value := range saved { | ||
| entry.Set(key, value) | ||
| } | ||
| c.local.UpsertByID("functions", entry) | ||
|
|
||
| return nil, fmt.Errorf("template state could not be saved; nothing was deployed: %w", err) | ||
| } | ||
| } | ||
|
|
||
| deployment := jsonx.NewObject() | ||
| if err := c.api.Call("POST", path, body, deployment); err != nil { | ||
| if template { | ||
| return nil, fmt.Errorf( | ||
| "%w; template state was consumed before the request -- check the function's deployments before retrying", | ||
| err) | ||
| } | ||
|
|
||
| return nil, err | ||
| } | ||
|
Comment on lines
+1479
to
1487
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the template endpoint definitively rejects a deployment with a non-timeout 4xx response, this branch leaves the pre-request deletion persisted. The next push therefore selects Knowledge Base Used: CLI distribution template Prompt To Fix With AIThis is a comment left during a code review.
Path: templates/cli/internal/cmd/pushdeploy.go
Line: 1479-1487
Comment:
**Definite rejections consume template state**
When the template endpoint definitively rejects a deployment with a non-timeout 4xx response, this branch leaves the pre-request deletion persisted. The next push therefore selects `/deployments/vcs` instead of retrying the template deployment, so the starter template is never seeded.
**Knowledge Base Used:** [CLI distribution template](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/appwrite/sdk-generator/-/docs/cli-distribution-template.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly. |
||
|
|
||
| return deployment, nil | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.