diff --git a/.gitignore b/.gitignore
index 17b956ba..fe3c001b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,6 @@ Dockerfile.initium
*.init_test*
initium_onmain.yaml
initium_onbranch.yaml
-dist/
+/dist
node_modules
diff --git a/README.md b/README.md
index e7ce75f8..1180a1ab 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ Following we have a matrix related to which application runtime our CLI is curre
|----------------------|:------------------:|
| [Nodejs](https://github.com/nearform/initium-cli/blob/main/assets/docker/Dockerfile.node.tmpl) | :white_check_mark: |
| [GoLang](https://github.com/nearform/initium-cli/blob/main/assets/docker/Dockerfile.go.tmpl) | :white_check_mark: |
+| [JS Frontend](https://github.com/nearform/initium-cli/blob/main/assets/docker/Dockerfile.frontend-js.tmpl) | :white_check_mark: |
| Python | Coming Soon |
| More will be added...| |
diff --git a/assets/docker/Dockerfile.frontend-js.tmpl b/assets/docker/Dockerfile.frontend-js.tmpl
new file mode 100644
index 00000000..87f06a33
--- /dev/null
+++ b/assets/docker/Dockerfile.frontend-js.tmpl
@@ -0,0 +1,24 @@
+FROM node:{{ or .RuntimeVersion .DefaultRuntimeVersion }} AS build-env
+
+ENV CI=true
+
+WORKDIR /app
+
+COPY package*.json tsconfig*.json ./
+
+RUN {{ .NodeInstallCommand }}
+
+COPY . .
+
+RUN npm run build --if-present
+RUN npm test
+
+FROM node:{{ or .RuntimeVersion .DefaultRuntimeVersion }}
+
+COPY --from=build-env /app /app
+
+WORKDIR /app
+
+USER node
+
+ENTRYPOINT npx http-server ./build
diff --git a/assets/docker/Dockerfile.go.tmpl b/assets/docker/Dockerfile.go.tmpl
index 115fd187..91f9b7e7 100644
--- a/assets/docker/Dockerfile.go.tmpl
+++ b/assets/docker/Dockerfile.go.tmpl
@@ -12,4 +12,4 @@ RUN CGO_ENABLED=0 go build -o /go/bin/app
FROM gcr.io/distroless/static-debian11
COPY --from=build /go/bin/app /
-ENTRYPOINT ["/app"]
\ No newline at end of file
+ENTRYPOINT ["/app"]
diff --git a/assets/docker/Dockerfile.node.tmpl b/assets/docker/Dockerfile.node.tmpl
index c871a9ca..00199f50 100644
--- a/assets/docker/Dockerfile.node.tmpl
+++ b/assets/docker/Dockerfile.node.tmpl
@@ -15,4 +15,4 @@ FROM gcr.io/distroless/nodejs20-debian11
COPY --from=build-env /app /app
WORKDIR /app
USER nonroot
-CMD ["index.js"]
\ No newline at end of file
+CMD ["index.js"]
diff --git a/example/frontend-js/build/index.html b/example/frontend-js/build/index.html
new file mode 100644
index 00000000..ee7a0cda
--- /dev/null
+++ b/example/frontend-js/build/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Initium Frontend Sample
+
+
+
+ Welcome to Initium!
+
+
+
diff --git a/example/frontend-js/package.json b/example/frontend-js/package.json
new file mode 100644
index 00000000..5b86e0fc
--- /dev/null
+++ b/example/frontend-js/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "frontend-js",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-scripts": "5.0.1",
+ "web-vitals": "^2.1.4"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/example/.env.sample b/example/node/.env.sample
similarity index 100%
rename from example/.env.sample
rename to example/node/.env.sample
diff --git a/example/index.js b/example/node/index.js
similarity index 100%
rename from example/index.js
rename to example/node/index.js
diff --git a/example/package.json b/example/node/package.json
similarity index 100%
rename from example/package.json
rename to example/node/package.json
diff --git a/src/cli/template_test.go b/src/cli/template_test.go
index 8b0a4311..7400856e 100644
--- a/src/cli/template_test.go
+++ b/src/cli/template_test.go
@@ -29,6 +29,7 @@ FROM gcr.io/distroless/static-debian11
COPY --from=build /go/bin/app /
ENTRYPOINT ["/app"]
+
`
const expectedNodeAppDockerTemplate = `FROM node:20.2.0 AS build-env
@@ -49,11 +50,40 @@ COPY --from=build-env /app /app
WORKDIR /app
USER nonroot
CMD ["index.js"]
+
+`
+
+const expectedFrontendJsAppDockerTemplate = `FROM node:20.2.0 AS build-env
+
+ENV CI=true
+
+WORKDIR /app
+
+COPY package*.json tsconfig*.json ./
+
+RUN npm i
+
+COPY . .
+
+RUN npm run build --if-present
+RUN npm test
+
+FROM node:20.2.0
+
+COPY --from=build-env /app /app
+
+WORKDIR /app
+
+USER node
+
+CMD npx http-server -p 8080 ./build
+
`
var projects = map[project.ProjectType]map[string]string{
- project.NodeProject: {"directory": "example", "expectedTemplate": expectedNodeAppDockerTemplate},
- project.GoProject: {"directory": ".", "expectedTemplate": expectedGoAppDockerTemplate},
+ project.NodeProject: {"directory": "example/node", "expectedTemplate": expectedNodeAppDockerTemplate},
+ project.GoProject: {"directory": ".", "expectedTemplate": expectedGoAppDockerTemplate},
+ project.FrontendJsProject: {"directory": "example/frontend-js", "expectedTemplate": expectedFrontendJsAppDockerTemplate},
}
func TestShouldRenderDockerTemplate(t *testing.T) {
diff --git a/src/services/k8s/knative_test.go b/src/services/k8s/knative_test.go
index 47704357..e5e95db2 100644
--- a/src/services/k8s/knative_test.go
+++ b/src/services/k8s/knative_test.go
@@ -72,7 +72,7 @@ func TestLoadManifestForPrivateService(t *testing.T) {
Tag: "v1.1.0",
}
- serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))
+ serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/node/.env.sample"))
if err != nil {
t.Fatalf(fmt.Sprintf("Error: %v", err))
@@ -93,7 +93,7 @@ func TestLoadManifestForPublicService(t *testing.T) {
imagePullSecrets := []string{"secretPassword123"}
proj := &project.Project{Name: "knative_test",
- Directory: path.Join(root, "example"),
+ Directory: path.Join(root, "example/node"),
Resources: os.DirFS(root),
ImagePullSecrets: imagePullSecrets,
IsPrivate: true,
@@ -106,7 +106,7 @@ func TestLoadManifestForPublicService(t *testing.T) {
Tag: "v1.1.0",
}
- serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/.env.sample"))
+ serviceManifest, err := LoadManifest(namespace, commitSha, proj, dockerImage, path.Join(root, "example/node/.env.sample"))
if err != nil {
t.Fatalf(fmt.Sprintf("Error: %v", err))
diff --git a/src/services/project/project.go b/src/services/project/project.go
index ce42c300..d58b3eb0 100644
--- a/src/services/project/project.go
+++ b/src/services/project/project.go
@@ -2,10 +2,12 @@ package project
import (
"bytes"
+ "encoding/json"
"fmt"
"io/fs"
"os"
"path"
+ "strings"
"text/template"
"github.com/nearform/initium-cli/src/services/git"
@@ -15,8 +17,9 @@ import (
type ProjectType string
const (
- NodeProject ProjectType = "node"
- GoProject ProjectType = "go"
+ NodeProject ProjectType = "node"
+ GoProject ProjectType = "go"
+ FrontendJsProject ProjectType = "frontend-js"
)
type Project struct {
@@ -65,6 +68,8 @@ func IsValidProjectType(projectType string) bool {
return true
case string(GoProject):
return true
+ case string(FrontendJsProject):
+ return true
default:
return false
}
@@ -74,8 +79,28 @@ func DetectType(directory string) (ProjectType, error) {
var detectedRuntimes []ProjectType
var projectType ProjectType
if _, err := os.Stat(path.Join(directory, "package.json")); err == nil {
- detectedRuntimes = append(detectedRuntimes, NodeProject)
- projectType = NodeProject
+ bytes, err := os.ReadFile(path.Join(directory, "package.json"))
+ var result map[string]any
+ json.Unmarshal(bytes, &result)
+ dependencies := result["dependencies"].(map[string]any)
+
+ if err != nil {
+ fmt.Print(err)
+ }
+
+ frontendDetected := false
+ for dependency := range dependencies {
+ if strings.Contains(string(dependency), "react") || strings.Contains(string(dependency), "angular") || strings.Contains(string(dependency), "vue") {
+ frontendDetected = true
+ }
+ }
+ if frontendDetected {
+ detectedRuntimes = append(detectedRuntimes, FrontendJsProject)
+ projectType = FrontendJsProject
+ } else {
+ detectedRuntimes = append(detectedRuntimes, NodeProject)
+ projectType = NodeProject
+ }
}
if _, err := os.Stat(path.Join(directory, "go.mod")); err == nil {
detectedRuntimes = append(detectedRuntimes, GoProject)
@@ -98,6 +123,9 @@ func (proj *Project) setRuntimeVersion() error {
case GoProject:
proj.DefaultRuntimeVersion = defaults.DefaultGoRuntimeVersion
return nil
+ case FrontendJsProject:
+ proj.DefaultRuntimeVersion = defaults.DefaultFrontendJsRuntimeVersion
+ return nil
default:
return fmt.Errorf("cannot detect runtime version for project type %s", proj.Type)
}
diff --git a/src/services/project/project_test.go b/src/services/project/project_test.go
index 22992b2d..8572579c 100644
--- a/src/services/project/project_test.go
+++ b/src/services/project/project_test.go
@@ -11,9 +11,10 @@ import (
)
var projects = map[ProjectType]map[string]string{
- NodeProject: {"directory": "example"},
- GoProject: {"directory": "."},
- "invalid": {"directory": "src"},
+ NodeProject: {"directory": "example/node"},
+ FrontendJsProject: {"directory": "example/frontend-js"},
+ GoProject: {"directory": "."},
+ "invalid": {"directory": "src"},
}
var root = "../../../"
@@ -139,7 +140,7 @@ func TestNodeInstallCommand(t *testing.T) {
Resources: os.DirFS(root),
}
- if project_type == NodeProject {
+ if project_type == NodeProject || project_type == FrontendJsProject {
expected := "npm i"
installCommand := proj.NodeInstallCommand()
if installCommand != expected {
diff --git a/src/utils/defaults/defaults.go b/src/utils/defaults/defaults.go
index 78c67281..07cd2dff 100644
--- a/src/utils/defaults/defaults.go
+++ b/src/utils/defaults/defaults.go
@@ -16,3 +16,5 @@ const DefaultNodeRuntimeVersion string = "20.2.0"
// renovate: datasource=docker depName=golang
const DefaultGoRuntimeVersion string = "1.20.4"
+
+const DefaultFrontendJsRuntimeVersion string = "20.2.0"