Skip to content

Commit 7092811

Browse files
committed
feat: allow multplie language support
1 parent 900bc60 commit 7092811

3 files changed

Lines changed: 133 additions & 6 deletions

File tree

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Crunchy is a deployment helper that builds the current project, publishes a Docker image, and updates one or more Cloud Run services.
44

55
## Features
6-
- Runs the local TypeScript build (`pnpm run build`) before packaging.
6+
- Runs the project build using the requested language/tooling (pnpm, npm, yarn, Go, or Rust) with auto-detection when not specified.
77
- Builds and pushes a `gcr.io/<project>/<image>` (or custom registry host) Docker image.
88
- Deploys the new image to multiple Cloud Run services in parallel and shifts traffic to the latest revision.
99
- Cleans up stale Cloud Run revisions and image digests with configurable retention limits.
@@ -37,7 +37,8 @@ crunchy --image <name> \
3737
--region <gcp-region> \
3838
[--beta] \
3939
[--keep-images <count>] \
40-
[--keep-revisions <count>]
40+
[--keep-revisions <count>] \
41+
[--lang <pnpm|npm|yarn|go|rust>]
4142
```
4243

4344
### Required flags
@@ -51,6 +52,18 @@ crunchy --image <name> \
5152
- `--keep-images`: Number of image digests to retain (default: `10`).
5253
- `--keep-revisions`: Number of Cloud Run revisions to retain (default: `10`).
5354
- `--registry-host`: Container registry host to target. Defaults to a region-specific host (e.g. `asia-northeast3``asia.gcr.io`, `europe-west1``eu.gcr.io`, `us-central1``us.gcr.io`, otherwise `gcr.io`).
55+
- `--lang`: Manually select the project language/build tool. Accepted values: `pnpm`, `npm`, `yarn`, `go`, `rust`. Historical aliases like `node` still map to the pnpm build.
56+
57+
### Build tool detection
58+
When `--lang` is not provided, Crunchy checks for well-known files in the working directory:
59+
- `pnpm-lock.yaml`: run `pnpm run build`.
60+
- `yarn.lock`: run `yarn build`.
61+
- `package-lock.json`: run `npm run build`.
62+
- `go.mod`: run `go build ./...`.
63+
- `Cargo.toml`: run `cargo build --release`.
64+
- `package.json` with none of the above locks: default to `pnpm run build` for backward compatibility.
65+
66+
If none of the indicators are present, Crunchy defaults to the Node (pnpm) build to preserve historical behavior.
5467

5568
### Example
5669

@@ -65,6 +78,6 @@ crunchy \
6578
```
6679

6780
## Requirements
68-
- `pnpm` installed and a working `pnpm run build`.
81+
- Matching tooling for your project (e.g. `pnpm`, `npm`, or `yarn` for Node/TypeScript builds; `go` for Go builds; `cargo` for Rust builds).
6982
- Docker CLI with access to the inferred (or specified) registry host.
7083
- Google Cloud SDK (`gcloud`) authenticated for the provided project and region.

language.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
)
9+
10+
type language string
11+
12+
const (
13+
langPNPM language = "pnpm"
14+
langNPM language = "npm"
15+
langYarn language = "yarn"
16+
langGo language = "go"
17+
langRust language = "rust"
18+
)
19+
20+
func buildProject(cfg *config) error {
21+
switch cfg.lang {
22+
case langPNPM:
23+
if err := runCommandStreaming(cfg.workdir, "pnpm", "run", "build"); err != nil {
24+
return fmt.Errorf("build project with pnpm: %w", err)
25+
}
26+
case langNPM:
27+
if err := runCommandStreaming(cfg.workdir, "npm", "run", "build"); err != nil {
28+
return fmt.Errorf("build project with npm: %w", err)
29+
}
30+
case langYarn:
31+
if err := runCommandStreaming(cfg.workdir, "yarn", "build"); err != nil {
32+
return fmt.Errorf("build project with yarn: %w", err)
33+
}
34+
case langGo:
35+
if err := runCommandStreaming(cfg.workdir, "go", "build", "./..."); err != nil {
36+
return fmt.Errorf("build project with go: %w", err)
37+
}
38+
case langRust:
39+
if err := runCommandStreaming(cfg.workdir, "cargo", "build", "--release"); err != nil {
40+
return fmt.Errorf("build project with cargo: %w", err)
41+
}
42+
default:
43+
return fmt.Errorf("unsupported language %q", cfg.lang)
44+
}
45+
46+
return nil
47+
}
48+
49+
func determineLanguage(workdir, requested string) (language, error) {
50+
requested = strings.TrimSpace(strings.ToLower(requested))
51+
if requested == "" {
52+
return detectLanguage(workdir)
53+
}
54+
55+
switch requested {
56+
case string(langNPM), "node", "ts", "typescript", "javascript", "nodejs":
57+
return langNPM, nil
58+
case string(langPNPM):
59+
return langPNPM, nil
60+
case string(langYarn):
61+
return langYarn, nil
62+
case string(langGo), "golang":
63+
return langGo, nil
64+
case string(langRust), "cargo":
65+
return langRust, nil
66+
default:
67+
return "", fmt.Errorf("unsupported --lang value %q", requested)
68+
}
69+
}
70+
71+
func detectLanguage(workdir string) (language, error) {
72+
type indicator struct {
73+
lang language
74+
file string
75+
}
76+
77+
orderedIndicators := []indicator{
78+
{lang: langPNPM, file: "pnpm-lock.yaml"},
79+
{lang: langYarn, file: "yarn.lock"},
80+
{lang: langNPM, file: "package-lock.json"},
81+
{lang: langGo, file: "go.mod"},
82+
{lang: langRust, file: "Cargo.toml"},
83+
}
84+
85+
for _, ind := range orderedIndicators {
86+
if fileExists(filepath.Join(workdir, ind.file)) {
87+
return ind.lang, nil
88+
}
89+
}
90+
91+
if fileExists(filepath.Join(workdir, "package.json")) {
92+
return langPNPM, nil
93+
}
94+
95+
return langPNPM, nil
96+
}
97+
98+
func fileExists(path string) bool {
99+
if path == "" {
100+
return false
101+
}
102+
if _, err := os.Stat(path); err == nil {
103+
return true
104+
}
105+
return false
106+
}

main.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type config struct {
2424
project string
2525
region string
2626
registryHost string
27+
lang language
2728
}
2829

2930
func main() {
@@ -50,6 +51,7 @@ func parseConfig() (*config, error) {
5051
project := flag.String("project", "", "GCP project to deploy to (required)")
5152
region := flag.String("region", "", "Cloud Run region (required)")
5253
registryHost := flag.String("registry-host", "", "Container registry host (e.g. gcr.io, asia.gcr.io). If omitted, inferred from --region")
54+
lang := flag.String("lang", "", "Project language/build tool to use (e.g. node, go). Defaults to auto-detection")
5355
flag.Parse()
5456

5557
if *image == "" {
@@ -91,6 +93,11 @@ func parseConfig() (*config, error) {
9193
host = inferRegistryHost(*region)
9294
}
9395

96+
projectLang, err := determineLanguage(workdir, *lang)
97+
if err != nil {
98+
return nil, err
99+
}
100+
94101
return &config{
95102
image: *image,
96103
beta: *beta,
@@ -102,6 +109,7 @@ func parseConfig() (*config, error) {
102109
project: *project,
103110
region: *region,
104111
registryHost: host,
112+
lang: projectLang,
105113
}, nil
106114
}
107115

@@ -132,9 +140,9 @@ func inferRegistryHost(region string) string {
132140
}
133141

134142
func run(cfg *config) error {
135-
logStep(cfg, "Building TS...")
136-
if err := runCommandStreaming(cfg.workdir, "pnpm", "run", "build"); err != nil {
137-
return fmt.Errorf("build project: %w", err)
143+
logStep(cfg, fmt.Sprintf("Building (%s)...", cfg.lang))
144+
if err := buildProject(cfg); err != nil {
145+
return err
138146
}
139147

140148
imageRepo := fmt.Sprintf("%s/%s/%s", cfg.registryHost, cfg.project, cfg.image)

0 commit comments

Comments
 (0)