Skip to content

Commit b0009a4

Browse files
feat: limit number of function sources based on usage tier (#666)
<img width="943" height="201" alt="CleanShot 2025-10-23 at 14 55 21@2x" src="https://github.com/user-attachments/assets/56f4bdae-6d37-4fbe-a910-daaa2e13f4df" /> <img width="2398" height="574" alt="CleanShot 2025-10-24 at 11 38 57@2x" src="https://github.com/user-attachments/assets/d0e8237b-0250-42b4-b93c-769807e16650" />
1 parent e179dde commit b0009a4

File tree

17 files changed

+261
-65
lines changed

17 files changed

+261
-65
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ gram.code-workspace
3434
dist/
3535
**/.claude/settings.local.json
3636
.vercel
37-
cover.*
37+
cover.*
38+
*gram.deploy.json

cli/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Gram CLI
2+
3+
## Local Development
4+
5+
1. Setup environment
6+
- `export GRAM_API_URL=https://localhost:8080`
7+
- `export GRAM_ORG=organization-123`
8+
- `export GRAM_PROJECT=default`
9+
- `export GRAM_API_KEY=<API-KEY>`
10+
11+
2. Run desired command
12+
- `cd cli`
13+
- `go run main.go status`
14+
15+
### Testing Gram Functions
16+
17+
1. Stage zip
18+
- `go run main.go stage function --slug test-fn --location fixtures/example.zip`
19+
- _You never need to do this again as long as you are using the same zip_
20+
21+
2. Push
22+
- `go run main.go push`

cli/fixtures/example.zip

1.24 KB
Binary file not shown.

cli/internal/api/assets.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (c *AssetsClient) UploadOpenAPIv3(
6262

6363
result, err := c.client.UploadOpenAPIv3(ctx, payload, req.Reader)
6464
if err != nil {
65-
return nil, fmt.Errorf("failed to upload OpenAPI asset: %w", err)
65+
return nil, fmt.Errorf("failed to upload OpenAPI source: %w", err)
6666
}
6767

6868
return result.Asset, nil
@@ -82,7 +82,7 @@ func (c *AssetsClient) UploadFunctions(
8282

8383
result, err := c.client.UploadFunctions(ctx, payload, req.Reader)
8484
if err != nil {
85-
return nil, fmt.Errorf("failed to upload OpenAPI asset: %w", err)
85+
return nil, fmt.Errorf("failed to upload Functions source: %w", err)
8686
}
8787

8888
return result.Asset, nil

cli/internal/app/app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func newApp() *cli.App {
3838
flags.APIKey(),
3939
flags.APIEndpoint(),
4040
flags.Project(),
41+
flags.Org(),
4142
&cli.StringFlag{
4243
Name: "log-level",
4344
Value: "info",

cli/internal/app/push.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ NOTE: Names and slugs must be unique across all sources.`[1:],
4444
flags.APIEndpoint(),
4545
flags.APIKey(),
4646
flags.Project(),
47+
flags.Org(),
4748
&cli.PathFlag{
4849
Name: "config",
4950
Usage: "Path to the deployment file",
@@ -142,13 +143,15 @@ NOTE: Names and slugs must be unique across all sources.`[1:],
142143
slogID := slog.String("deployment_id", result.Deployment.ID)
143144
status := result.Deployment.Status
144145

146+
deploymentLogsURL := fmt.Sprintf("https://localhost:5173/%s/%s/deployments/%s", workflowParams.OrgSlug, workflowParams.ProjectSlug, result.Deployment.ID)
147+
145148
switch status {
146149
case "completed":
147-
logger.InfoContext(ctx, "Deployment succeeded", slogID)
150+
logger.InfoContext(ctx, "Deployment succeeded", slogID, slog.String("logs_url", deploymentLogsURL))
148151
return nil
149152
case "failed":
150153
logger.ErrorContext(ctx, "Deployment failed", slogID)
151-
return fmt.Errorf("deployment failed")
154+
return fmt.Errorf("Deployment failed. Check the deployment logs for more information: %s", deploymentLogsURL)
152155
default:
153156
logger.InfoContext(
154157
ctx,

cli/internal/app/status.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ If no deployment ID is provided, shows the status of the latest deployment.`,
2727
flags.APIEndpoint(),
2828
flags.APIKey(),
2929
flags.Project(),
30+
flags.Org(),
3031
&cli.StringFlag{
3132
Name: "id",
3233
Usage: "The deployment ID to check status for (if not provided, shows latest deployment)",

cli/internal/app/upload.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Example:
2929
flags.APIEndpoint(),
3030
flags.APIKey(),
3131
flags.Project(),
32+
flags.Org(),
3233
&cli.StringFlag{
3334
Name: "type",
3435
Usage: fmt.Sprintf("The type of asset to upload: %+v", deploy.AllowedTypes),

cli/internal/flags/flags.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@ func APIEndpoint() *cli.StringFlag {
2121
}
2222
}
2323

24+
func Org() *cli.StringFlag {
25+
return &cli.StringFlag{
26+
Name: "org",
27+
Usage: "The target Gram organization (slug)",
28+
EnvVars: []string{"GRAM_ORG"},
29+
}
30+
}
31+
2432
func Project() *cli.StringFlag {
2533
return &cli.StringFlag{
2634
Name: "project",
27-
Usage: "The target Gram project",
35+
Usage: "The target Gram project (slug)",
2836
EnvVars: []string{"GRAM_PROJECT"},
2937
}
3038
}

cli/internal/workflow/params.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ func ResolveParams(
3030
return Params{}, fmt.Errorf("api-key required: not found in --api-key flag, $GRAM_API_KEY environment variable, or profile")
3131
}
3232

33+
orgSlug := ResolveOrgSlug(c, prof)
34+
if orgSlug == "" {
35+
return Params{}, fmt.Errorf("organization required: not found in --org flag, $GRAM_ORG environment variable, or profile")
36+
}
37+
3338
projectSlug := ResolveProject(c, prof)
3439
if projectSlug == "" {
3540
return Params{}, fmt.Errorf("project required: not found in --project flag, $GRAM_PROJECT environment variable, or profile")
@@ -43,6 +48,7 @@ func ResolveParams(
4348
return Params{
4449
APIKey: apiKey,
4550
APIURL: apiURL,
51+
OrgSlug: orgSlug,
4652
ProjectSlug: projectSlug,
4753
}, nil
4854
}
@@ -59,6 +65,18 @@ func ResolveKey(c *cli.Context, prof *profile.Profile) secret.Secret {
5965
return secret.Secret("")
6066
}
6167

68+
func ResolveOrgSlug(c *cli.Context, prof *profile.Profile) string {
69+
if c.IsSet("org") {
70+
return c.String("org")
71+
}
72+
73+
if prof != nil && prof.Org != nil {
74+
return prof.Org.Slug
75+
}
76+
77+
return ""
78+
}
79+
6280
func ResolveProject(c *cli.Context, prof *profile.Profile) string {
6381
if c.IsSet("project") {
6482
return c.String("project")

0 commit comments

Comments
 (0)