diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..d047f58 --- /dev/null +++ b/notes.txt @@ -0,0 +1,161 @@ +CLI Test + + +Version + +Command: gy version +Result: Successfully displayed the installed CLI version and confirmed that it is up to date. +Output: +Ghaymah CLI v0.0.24 +You're up to date! +Verdict: Works as expected. The CLI correctly reports its current version and update status + +Help + +Command: gy --help +Result: Successfully displayed the CLI description, available commands, usage examples, and global flags. +Verdict: Works as expected. The help command provides clear information about the available commands and CLI options. +Login +Command: gy login +Result: Logged in successfully (or gy signup if no account yet — email is the one used for the Ghaymah account). + +Logout + +Command: gy logout +Result: Logout succeeded, but a warning appeared first: +Remote sign-out call failed with 400 error: "refreshToken" is required +CLI still cleared local config and confirmed: ✅ Logged out +Verdict: Non-blocking issue. Logout succeeds locally, but the remote sign-out request should be fixed to include the required refreshToken. + +Signup + +Command: gy signup +Result: Account created successfully and auto-logged in +Same info log appeared: "config file not found, using defaults" (normal, non-issue) +Output: ✅ Account created! Logged in as username@gmail.com +Verdict: Works as expected. Signup creates account + logs in in one step. + +Whoami + +Command: gy whoami +Result: Successfully displayed the currently authenticated user's email and User ID. +Output: +✅ Logged in as username@gmail.com +User ID: also shown +Verdict: Works as expected. The command correctly identifies the currently logged-in user and displays the associated User ID. No security issue since no authentication tokens or secrets are exposed. + +List + +Command: gy list +Result: Successfully retrieved the projects associated with the authenticated account. +Output: +[proj] synced ghaymah-exam-mahmoud-secops +Verdict: Works as expected. The command successfully lists the available projects and correctly shows their synchronization status. + +Tunneling + +Command: gy tunnel start +Requirements: A successfully deployed application with a reachable configured port. +Steps: Started the tunnel for the deployed application. The tunnel incorrectly targeted localhost:8080 instead of the configured application port, then returned an empty tunnel URL. +Result: Tunnel was reported as live, but the endpoint was unreachable. +Output: Tunnel is live at: https:// +Verdict: Does not work as expected due to incorrect port handling and an invalid tunnel URL + +Delete + +project +Command: gy delete project ghaymah-exam-mahmoud-secops +Result: Confirmation prompt appeared → confirmed with y +Output: ✅ Deleted project 'ghaymah-exam-mahmoud-secops' +Verdict: Works as expected. Project and all its apps deleted successfully with confirmation step. + +app +Command: gy delete app +Requirements: An existing app with a unique name. +Steps: Tested deleting an app using its name. The delete operation works correctly, but duplicate app names can cause ambiguity when identifying the target app. +Result: App deletion works as expected, but duplicate names make app selection unreliable. +Output: Successful deletion / ambiguous target when duplicate names exist. +Verdict: Works as expected for unique app names; duplicate names should be prevented or handled to avoid ambiguity. + +Deployment + +Node.js Deployment + +Command: gy deploy +Requirements: A package.json, package-lock.json, and a web server listening on port 8080. +Steps: Created a minimal Node.js Hello World project, initially tested without a lockfile and got an npm ci error, then generated package-lock.json. The first runtime test also failed because index.js was missing and later returned 502 because the app was not listening on port 8080. Updated index.js to run an HTTP server on port 8080, then redeployed and verified with gy config, gy logs, and the deployed URL. +Result: Successfully detected, built, and deployed the Node.js application. +Output: Hello from Node.js! +Verdict: Works as expected after meeting the required Node.js project and runtime requirements. + +Flask Deployment + +Command: gy deploy --port=5000 +Requirements: A Flask application with a web server listening on the configured port. +Steps: Initially tested the Flask application using the default port 8080, which resulted in a 502 Bad Gateway. Changed the application to listen on port 5000, then deployed using gy deploy --port=5000 and verified that the deployment completed successfully. +Result: Successfully detected, built, and deployed the Flask application after configuring the correct port. +Output: Deployed successfully with the configured port 5000. +Verdict: Works as expected when the application port matches the configured deployment port + +Go Deployment + +Command: gy deploy +Requirements: A valid Go project. The test application is a simple non-web Go program that prints Hello from Go!. +Steps: Deployed the Go application using the CLI-generated Dockerfile. The project was created successfully, and the deployment artifact was built and uploaded successfully. However, resource creation failed with a resources_pkey uniqueness violation. In an earlier deployment attempt, the CLI also waited for 15 minutes before timing out, while gy logs go-test showed Hello from Go!. +Result: The application produced logs, but the deployment could not be confirmed as successfully completed in the cloud. +Output: resources_pkey uniqueness violation / deployment timeout after 15 minutes / Hello from Go! +Verdict: Does not work as expected: the CLI successfully detects, builds, and uploads the Go application, but deployment does not complete successfully and the CLI leaves the deployment state inconsistent. + +Go Web Deployment + +Command: gy deploy --port=8000 + +Requirements: A Go web application with an HTTP server listening on the configured port. + +Steps: Created a minimal Go HTTP server using net/http that listens on port 8000 and returns a Hello World response. Deployed the application without providing a Dockerfile to test the CLI's Go detection and automatic Dockerfile generation. + +Result: The CLI successfully detected the Go web application, generated the Dockerfile, built the deployment artifact, and deployed the application successfully. + +Output: Hello from Go Web! + +Verdict: Works as expected. The CLI successfully detects, builds, and deploys a Go web application when the application listens on the configured port. + +FastAPI Deployment + +Command: gy deploy --port=8000 +Requirements: A FastAPI application with Uvicorn listening on 0.0.0.0:8000. + +Steps: Initially deployed the FastAPI application using the CLI-generated Dockerfile. Deployment completed successfully, but accessing the generated URL resulted in a 502 Bad Gateway. Investigation showed that the auto-generated Dockerfile used CMD ["sh", "-c", "python main.py"], while main.py only defines the FastAPI application and does not start a web server. The application was then verified locally using Uvicorn with uvicorn main:app --host 0.0.0.0 --port 8000, which successfully served the application. + +Result: Deployment infrastructure completed successfully, but the generated Dockerfile did not start the FastAPI server correctly, resulting in a 502 Bad Gateway. + +Output: Deployment completed successfully, but the generated URL returned 502 Bad Gateway. + +Verdict: Partial / CLI issue — FastAPI deployment succeeds, but the auto-generated Dockerfile incorrectly starts python main.py instead of Uvicorn, causing the deployed application to be unreachable. + +Problems + +Duplicate Names Problem + +Duplicate app names cause ambiguity during deletion: gy delete app relies only on the friendly name, so multiple apps with the same name cannot be uniquely targeted. The CLI should either prevent duplicate app names or support deletion by a unique resource ID / interactive selection. + +Auto listening port problem + +Incorrect port detection: The CLI assumes port 8080 by default, but applications may listen on a different port, causing deployment failures such as 502 Bad Gateway. The CLI should detect the actual application port or provide a clearer configuration mechanism. + +Tunneling problem + +Hardcoded 8080 port instead of using the configured application port. +Empty tunnel URL returned (https://). +Tunnel is reported as live despite being unreachable. +Tunnel port is not synchronized with the deployment configuration. + +Deployment Timeout / Inconsistent State + +The Go deployment timed out after 15 minutes, even though the application appears to be running and producing logs. The CLI does not clearly confirm the app's deployment status in the cloud, leaving the deployment state inconsistent. + +FastAPI Auto-Generated Dockerfile Problem + +The CLI successfully detects FastAPI and generates a Dockerfile, but the generated Dockerfile uses CMD ["sh", "-c", "python main.py"]. Since the FastAPI application only defines the app object and does not start a web server, the container does not listen on the configured port, resulting in 502 Bad Gateway. The CLI should generate a FastAPI-compatible command such as Uvicorn with 0.0.0.0 and the configured port. + + diff --git a/screenshots/fast-api/Screenshot from 2026-08-20 20-11-10.png b/screenshots/fast-api/Screenshot from 2026-08-20 20-11-10.png new file mode 100644 index 0000000..446444d Binary files /dev/null and b/screenshots/fast-api/Screenshot from 2026-08-20 20-11-10.png differ diff --git a/screenshots/fast-api/Screenshot from 2026-08-20 20-34-59.png b/screenshots/fast-api/Screenshot from 2026-08-20 20-34-59.png new file mode 100644 index 0000000..2ce4da5 Binary files /dev/null and b/screenshots/fast-api/Screenshot from 2026-08-20 20-34-59.png differ diff --git a/screenshots/fast-api/Screenshot from 2026-08-20 20-45-21.png b/screenshots/fast-api/Screenshot from 2026-08-20 20-45-21.png new file mode 100644 index 0000000..a1f990b Binary files /dev/null and b/screenshots/fast-api/Screenshot from 2026-08-20 20-45-21.png differ diff --git a/screenshots/flask-test/Screenshot from 2026-08-20 18-51-23.png b/screenshots/flask-test/Screenshot from 2026-08-20 18-51-23.png new file mode 100644 index 0000000..b5dfb59 Binary files /dev/null and b/screenshots/flask-test/Screenshot from 2026-08-20 18-51-23.png differ diff --git a/screenshots/go-web/Screenshot from 2026-08-20 21-26-58.png b/screenshots/go-web/Screenshot from 2026-08-20 21-26-58.png new file mode 100644 index 0000000..46924cd Binary files /dev/null and b/screenshots/go-web/Screenshot from 2026-08-20 21-26-58.png differ diff --git a/screenshots/go-web/Screenshot from 2026-08-20 21-27-56.png b/screenshots/go-web/Screenshot from 2026-08-20 21-27-56.png new file mode 100644 index 0000000..8a29cff Binary files /dev/null and b/screenshots/go-web/Screenshot from 2026-08-20 21-27-56.png differ diff --git a/screenshots/node-test/Screenshot from 2026-08-20 17-46-45.png b/screenshots/node-test/Screenshot from 2026-08-20 17-46-45.png new file mode 100644 index 0000000..5d6c8bb Binary files /dev/null and b/screenshots/node-test/Screenshot from 2026-08-20 17-46-45.png differ diff --git a/screenshots/node-test/Screenshot from 2026-08-20 17-46-51.png b/screenshots/node-test/Screenshot from 2026-08-20 17-46-51.png new file mode 100644 index 0000000..85b793a Binary files /dev/null and b/screenshots/node-test/Screenshot from 2026-08-20 17-46-51.png differ diff --git a/screenshots/node-test/Screenshot from 2026-08-20 17-47-12.png b/screenshots/node-test/Screenshot from 2026-08-20 17-47-12.png new file mode 100644 index 0000000..4cd89e5 Binary files /dev/null and b/screenshots/node-test/Screenshot from 2026-08-20 17-47-12.png differ diff --git a/screenshots/node-test/Screenshot from 2026-08-20 17-47-44.png b/screenshots/node-test/Screenshot from 2026-08-20 17-47-44.png new file mode 100644 index 0000000..6ed86ce Binary files /dev/null and b/screenshots/node-test/Screenshot from 2026-08-20 17-47-44.png differ diff --git a/screenshots/problems/Screenshot from 2026-08-20 18-13-43.png b/screenshots/problems/Screenshot from 2026-08-20 18-13-43.png new file mode 100644 index 0000000..a8527f0 Binary files /dev/null and b/screenshots/problems/Screenshot from 2026-08-20 18-13-43.png differ diff --git a/test-projects/fastapi-test/.gy.json b/test-projects/fastapi-test/.gy.json new file mode 100644 index 0000000..37435fa --- /dev/null +++ b/test-projects/fastapi-test/.gy.json @@ -0,0 +1,13 @@ +{ + "app": "fastapi-test", + "project": "fastapi-test", + "_app_id": "2a0c0168-dbf0-4f95-a5e9-a7e330bc56d9", + "_project_id": "e3d04bd4-e8cd-4c19-9a32-5393be67587f", + "port": 8000, + "publicAccess": { + "enabled": true, + "domain": "fastapi-test-f38b02d3c679.hosted.ghaymah.systems" + }, + "resourceTier": "t1", + "_detected": "python" +} \ No newline at end of file diff --git a/test-projects/fastapi-test/Dockerfile b/test-projects/fastapi-test/Dockerfile new file mode 100644 index 0000000..124d068 --- /dev/null +++ b/test-projects/fastapi-test/Dockerfile @@ -0,0 +1,23 @@ +# 🐍 Auto-generated Dockerfile for Python 3.12 +# Ghaymah detected: FastAPI +FROM python:3.12-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +# Copy and install Python dependencies +COPY requirements.txt* setup.py* pyproject.toml* ./ +RUN pip install --no-cache-dir -r requirements.txt 2>/dev/null || true + +# Copy application code +COPY . . + +# Expose port +EXPOSE 8080 + +# Start the application +CMD ["sh", "-c", "python main.py"] diff --git a/test-projects/fastapi-test/main.py b/test-projects/fastapi-test/main.py new file mode 100644 index 0000000..24b25f3 --- /dev/null +++ b/test-projects/fastapi-test/main.py @@ -0,0 +1,7 @@ +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/") +def hello(): + return {"message": "Hello from FastAPI!"} diff --git a/test-projects/fastapi-test/requirements.txt b/test-projects/fastapi-test/requirements.txt new file mode 100644 index 0000000..97dc7cd --- /dev/null +++ b/test-projects/fastapi-test/requirements.txt @@ -0,0 +1,2 @@ +fastapi +uvicorn diff --git a/test-projects/flask-test/.gy.json b/test-projects/flask-test/.gy.json new file mode 100644 index 0000000..cf8f541 --- /dev/null +++ b/test-projects/flask-test/.gy.json @@ -0,0 +1,13 @@ +{ + "app": "flask-test", + "project": "flask-test", + "_app_id": "9c0e8d09-476f-4734-8667-c1ae9f071f8b", + "_project_id": "11cab4ad-fd7a-45e6-b6a4-ba7ec05831e3", + "port": 5000, + "publicAccess": { + "enabled": true, + "domain": "flask-test-27a4a0302d14.hosted.ghaymah.systems" + }, + "resourceTier": "t1", + "_detected": "dockerfile" +} \ No newline at end of file diff --git a/test-projects/flask-test/Dockerfile b/test-projects/flask-test/Dockerfile new file mode 100644 index 0000000..e86c24f --- /dev/null +++ b/test-projects/flask-test/Dockerfile @@ -0,0 +1,23 @@ +# 🐍 Auto-generated Dockerfile for Python 3.12 +# Ghaymah detected: Flask +FROM python:3.12-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +# Copy and install Python dependencies +COPY requirements.txt* setup.py* pyproject.toml* ./ +RUN pip install --no-cache-dir -r requirements.txt 2>/dev/null || true + +# Copy application code +COPY . . + +# Expose port +EXPOSE 8080 + +# Start the application +CMD ["sh", "-c", "python app.py"] diff --git a/test-projects/flask-test/app.py b/test-projects/flask-test/app.py new file mode 100644 index 0000000..df54361 --- /dev/null +++ b/test-projects/flask-test/app.py @@ -0,0 +1,10 @@ +from flask import Flask + +app = Flask(__name__) + +@app.route("/") +def hello(): + return "Hello from Flask!" + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000) diff --git a/test-projects/flask-test/requirements.txt b/test-projects/flask-test/requirements.txt new file mode 100644 index 0000000..e3e9a71 --- /dev/null +++ b/test-projects/flask-test/requirements.txt @@ -0,0 +1 @@ +Flask diff --git a/test-projects/go-test/.gy.json b/test-projects/go-test/.gy.json new file mode 100644 index 0000000..05a8ff9 --- /dev/null +++ b/test-projects/go-test/.gy.json @@ -0,0 +1,13 @@ +{ + "app": "go-test", + "project": "go-test", + "_app_id": "63e76372-f177-4401-8868-996406084240", + "_project_id": "9e724fe9-47d7-4dc3-b669-54294561ca36", + "port": 8000, + "publicAccess": { + "enabled": true, + "domain": "go-test-fbcdc461325b.hosted.ghaymah.systems" + }, + "resourceTier": "t1", + "_detected": "go" +} diff --git a/test-projects/go-test/Dockerfile b/test-projects/go-test/Dockerfile new file mode 100644 index 0000000..59ea5ed --- /dev/null +++ b/test-projects/go-test/Dockerfile @@ -0,0 +1,15 @@ +# 🔷 Auto-generated Dockerfile for Go +# Ghaymah detected: +FROM golang:1.22-alpine AS builder + +WORKDIR /app +COPY go.mod go.sum* ./ +RUN go mod download 2>/dev/null || true +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/app . + +FROM alpine:3.19 +RUN apk --no-cache add ca-certificates +COPY --from=builder /app/bin/app /app +EXPOSE 8080 +CMD ["/app"] diff --git a/test-projects/go-test/Screenshot from 2026-08-20 21-02-56.png b/test-projects/go-test/Screenshot from 2026-08-20 21-02-56.png new file mode 100644 index 0000000..3f307ca Binary files /dev/null and b/test-projects/go-test/Screenshot from 2026-08-20 21-02-56.png differ diff --git a/test-projects/go-test/Screenshot from 2026-08-20 21-03-03.png b/test-projects/go-test/Screenshot from 2026-08-20 21-03-03.png new file mode 100644 index 0000000..bd49cae Binary files /dev/null and b/test-projects/go-test/Screenshot from 2026-08-20 21-03-03.png differ diff --git a/test-projects/go-test/go.mod b/test-projects/go-test/go.mod new file mode 100644 index 0000000..9c11f0b --- /dev/null +++ b/test-projects/go-test/go.mod @@ -0,0 +1,3 @@ +module go-test + +go 1.22 diff --git a/test-projects/go-test/main.go b/test-projects/go-test/main.go new file mode 100644 index 0000000..85765ec --- /dev/null +++ b/test-projects/go-test/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello from Go!") +} diff --git a/test-projects/go-web-test/.gy.json b/test-projects/go-web-test/.gy.json new file mode 100644 index 0000000..af88aa6 --- /dev/null +++ b/test-projects/go-web-test/.gy.json @@ -0,0 +1,13 @@ +{ + "app": "go-web-test", + "project": "go-web-test", + "_app_id": "7f5a4300-a098-4433-8b8c-200f29da51f0", + "_project_id": "7a31cf5a-82f1-4762-8c1f-905c3c64ebc9", + "port": 8000, + "publicAccess": { + "enabled": true, + "domain": "go-web-test-246327147195.hosted.ghaymah.systems" + }, + "resourceTier": "t1", + "_detected": "dockerfile" +} \ No newline at end of file diff --git a/test-projects/go-web-test/Dockerfile b/test-projects/go-web-test/Dockerfile new file mode 100644 index 0000000..59ea5ed --- /dev/null +++ b/test-projects/go-web-test/Dockerfile @@ -0,0 +1,15 @@ +# 🔷 Auto-generated Dockerfile for Go +# Ghaymah detected: +FROM golang:1.22-alpine AS builder + +WORKDIR /app +COPY go.mod go.sum* ./ +RUN go mod download 2>/dev/null || true +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/app . + +FROM alpine:3.19 +RUN apk --no-cache add ca-certificates +COPY --from=builder /app/bin/app /app +EXPOSE 8080 +CMD ["/app"] diff --git a/test-projects/go-web-test/go.mod b/test-projects/go-web-test/go.mod new file mode 100644 index 0000000..9ddc1cf --- /dev/null +++ b/test-projects/go-web-test/go.mod @@ -0,0 +1,3 @@ +module go-web-test + +go 1.22 diff --git a/test-projects/go-web-test/main.go b/test-projects/go-web-test/main.go new file mode 100644 index 0000000..4a6dc40 --- /dev/null +++ b/test-projects/go-web-test/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "net/http" + "os" +) + +func main() { + port := os.Getenv("PORT") + if port == "" { + port = "8000" + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello from Go!") + }) + + fmt.Println("Server listening on :" + port) + http.ListenAndServe(":"+port, nil) +} diff --git a/test-projects/node-test/.dockerignore b/test-projects/node-test/.dockerignore new file mode 100644 index 0000000..8e52e36 --- /dev/null +++ b/test-projects/node-test/.dockerignore @@ -0,0 +1,4 @@ +node_modules +.git +.env +*.md diff --git a/test-projects/node-test/.gy.json b/test-projects/node-test/.gy.json new file mode 100644 index 0000000..bb9e4d7 --- /dev/null +++ b/test-projects/node-test/.gy.json @@ -0,0 +1,13 @@ +{ + "app": "node-test", + "project": "node-test", + "_app_id": "abcbe7ef-8d64-47ce-81a9-177c14581b58", + "_project_id": "ad16ddf1-d289-46f5-bb72-f94091b8b583", + "port": 8080, + "publicAccess": { + "enabled": true, + "domain": "node-test-3f482b7ce6b2.hosted.ghaymah.systems" + }, + "resourceTier": "t1", + "_detected": "dockerfile" +} \ No newline at end of file diff --git a/test-projects/node-test/Dockerfile b/test-projects/node-test/Dockerfile new file mode 100644 index 0000000..203eb2f --- /dev/null +++ b/test-projects/node-test/Dockerfile @@ -0,0 +1,8 @@ +# 🟢 Auto-generated Dockerfile for Node.js 22 +FROM node:22-alpine +WORKDIR /app +COPY package*.json pnpm-lock.yaml* yarn.lock* ./ +RUN npm ci +COPY . . +EXPOSE 8080 +CMD ["sh", "-c", "npm run start"] diff --git a/test-projects/node-test/index.js b/test-projects/node-test/index.js new file mode 100644 index 0000000..370b385 --- /dev/null +++ b/test-projects/node-test/index.js @@ -0,0 +1,12 @@ +const http = require("http"); + +const PORT = process.env.PORT || 8080; + +const server = http.createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("Hello from Node.js!"); +}); + +server.listen(PORT, "0.0.0.0", () => { + console.log(`Server listening on port ${PORT}`); +}); diff --git a/test-projects/node-test/package-lock.json b/test-projects/node-test/package-lock.json new file mode 100644 index 0000000..cf8fc79 --- /dev/null +++ b/test-projects/node-test/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "node-test", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-test", + "version": "1.0.0" + } + } +} diff --git a/test-projects/node-test/package.json b/test-projects/node-test/package.json new file mode 100644 index 0000000..55eda73 --- /dev/null +++ b/test-projects/node-test/package.json @@ -0,0 +1,7 @@ +{ + "name": "node-test", + "version": "1.0.0", + "scripts": { + "start": "node index.js" + } +}