Update README for the 0.1
Update README README typo README: fixe typos, add links to the wiki elements README: fixe typos, add links to the wiki elements Refactor deployment command for better error handling - Add buffered error channel in NewDeployCommand to prevent goroutine leak - Replace direct error prints with error channel sends in HTTP request, TUI deployment, and progress bar TUI sections - Implement loop to handle errors from the new error channel - Adjust message printing format after successful deployment Create go.yml Create docker-publish.yml Update go.yml Update go.yml Update Dockerfile Update go.yml Delete .github/workflows/docker-publish.yml Update go.yml Update go.yml load proxy url from .env Update go workflow releaser remove compose example Update go.yml Update go.yml Update go.yml Update go.yml Update go.yml revert back Dockerfile Only goreleaser Update go.yml Update go.yml Update github workflow builder fix unique env key in go .yml without goreleaser update without goreleaser fix dockerfile update goreleaser config update goreleaser config update goreleaser config update goreleaser config without manifest
- commit
- 71cac504c612bb6f3c01642d0df80a4d9408e0ec
- author
- bnema · · 2023-11-09 16:07 UTC
- committer
- bamen · · 2024-08-17 06:21 UTC
- parent
- e08379a
- signature
- unsigned
7 files · +127 -65
A .github/workflows/go.yml +63 -0
| @@ -0,0 +1,63 @@ | |||
| 1 | + | name: Release | |
| 2 | + | ||
| 3 | + | on: | |
| 4 | + | push: | |
| 5 | + | tags: | |
| 6 | + | - '*' | |
| 7 | + | ||
| 8 | + | jobs: | |
| 9 | + | release: | |
| 10 | + | runs-on: ubuntu-latest | |
| 11 | + | steps: | |
| 12 | + | - uses: actions/checkout@v3 | |
| 13 | + | ||
| 14 | + | - name: Set up Go | |
| 15 | + | uses: actions/setup-go@v3 | |
| 16 | + | with: | |
| 17 | + | go-version: 1.21 | |
| 18 | + | ||
| 19 | + | - name: Build | |
| 20 | + | run: | | |
| 21 | + | go mod tidy | |
| 22 | + | mkdir -p bin | |
| 23 | + | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-linux-amd64 ./cmd/cli/main.go | |
| 24 | + | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-windows-amd64.exe ./cmd/cli/main.go | |
| 25 | + | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-darwin-amd64 ./cmd/cli/main.go | |
| 26 | + | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-linux-arm64 ./cmd/cli/main.go | |
| 27 | + | CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-windows-arm64.exe ./cmd/cli/main.go | |
| 28 | + | CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-darwin-arm64 ./cmd/cli/main.go | |
| 29 | + | ||
| 30 | + | - name: Build Docker Images | |
| 31 | + | run: | | |
| 32 | + | docker buildx build --platform linux/amd64 --tag ghcr.io/bnema/gordon:${{ github.ref_name }}-amd64 --label org.opencontainers.image.created=$(date +'%Y-%m-%dT%H:%M:%SZ') --label org.opencontainers.image.title=gordon --label org.opencontainers.image.revision=${{ github.sha }} --label org.opencontainers.image.version=${{ github.ref_name }} --label org.opencontainers.image.source=https://github.com/bnema/gordon --build-arg BINARY=bin/gordon-linux-amd64 --push . | |
| 33 | + | docker buildx build --platform linux/arm64 --tag ghcr.io/bnema/gordon:${{ github.ref_name }}-arm64 --label org.opencontainers.image.created=$(date +'%Y-%m-%dT%H:%M:%SZ') --label org.opencontainers.image.title=gordon --label org.opencontainers.image.revision=${{ github.sha }} --label org.opencontainers.image.version=${{ github.ref_name }} --label org.opencontainers.image.source=https://github.com/bnema/gordon --build-arg BINARY=bin/gordon-linux-arm64 --push . | |
| 34 | + | env: | |
| 35 | + | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 36 | + | ||
| 37 | + | - name: Create Docker Manifest | |
| 38 | + | run: | | |
| 39 | + | docker manifest create ghcr.io/bnema/gordon:latest ghcr.io/bnema/gordon:${{ github.ref_name }}-amd64 ghcr.io/bnema/gordon:${{ github.ref_name }}-arm64 --amend | |
| 40 | + | docker manifest push ghcr.io/bnema/gordon:latest | |
| 41 | + | ||
| 42 | + | - name: Create Release Artifacts | |
| 43 | + | run: | | |
| 44 | + | zip -j gordon_${{ github.ref_name }}_linux_amd64.zip gordon-linux-amd64 | |
| 45 | + | zip -j gordon_${{ github.ref_name }}_windows_amd64.zip gordon-windows-amd64.exe | |
| 46 | + | zip -j gordon_${{ github.ref_name }}_darwin_amd64.zip gordon-darwin-amd64 | |
| 47 | + | zip -j gordon_${{ github.ref_name }}_linux_arm64.zip gordon-linux-arm64 | |
| 48 | + | zip -j gordon_${{ github.ref_name }}_windows_arm64.zip gordon-windows-arm64.exe | |
| 49 | + | zip -j gordon_${{ github.ref_name }}_darwin_arm64.zip gordon-darwin-arm64 | |
| 50 | + | ||
| 51 | + | - name: Install Cosign | |
| 52 | + | run: | | |
| 53 | + | curl -sfL https://raw.githubusercontent.com/sigstore/cosign/main/install.sh | sudo sh | |
| 54 | + | ||
| 55 | + | - name: Create Release | |
| 56 | + | uses: actions/create-release@v1 | |
| 57 | + | with: | |
| 58 | + | tag_name: ${{ github.ref }} | |
| 59 | + | release_name: Release ${{ github.ref }} | |
| 60 | + | draft: true | |
| 61 | + | prerelease: false | |
| 62 | + | env: | |
| 63 | + | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}⏎̸ | |
| @@ -0,0 +1,63 @@ | |||||
| 1 | + | name: Release | |||
| 2 | + | ||||
| 3 | + | on: | |||
| 4 | + | push: | |||
| 5 | + | tags: | |||
| 6 | + | - '*' | |||
| 7 | + | ||||
| 8 | + | jobs: | |||
| 9 | + | release: | |||
| 10 | + | runs-on: ubuntu-latest | |||
| 11 | + | steps: | |||
| 12 | + | - uses: actions/checkout@v3 | |||
| 13 | + | ||||
| 14 | + | - name: Set up Go | |||
| 15 | + | uses: actions/setup-go@v3 | |||
| 16 | + | with: | |||
| 17 | + | go-version: 1.21 | |||
| 18 | + | ||||
| 19 | + | - name: Build | |||
| 20 | + | run: | | |||
| 21 | + | go mod tidy | |||
| 22 | + | mkdir -p bin | |||
| 23 | + | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-linux-amd64 ./cmd/cli/main.go | |||
| 24 | + | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-windows-amd64.exe ./cmd/cli/main.go | |||
| 25 | + | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-darwin-amd64 ./cmd/cli/main.go | |||
| 26 | + | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-linux-arm64 ./cmd/cli/main.go | |||
| 27 | + | CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-windows-arm64.exe ./cmd/cli/main.go | |||
| 28 | + | CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -X main.build=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date +'%Y-%m-%dT%H:%M:%SZ')" -o bin/gordon-darwin-arm64 ./cmd/cli/main.go | |||
| 29 | + | ||||
| 30 | + | - name: Build Docker Images | |||
| 31 | + | run: | | |||
| 32 | + | docker buildx build --platform linux/amd64 --tag ghcr.io/bnema/gordon:${{ github.ref_name }}-amd64 --label org.opencontainers.image.created=$(date +'%Y-%m-%dT%H:%M:%SZ') --label org.opencontainers.image.title=gordon --label org.opencontainers.image.revision=${{ github.sha }} --label org.opencontainers.image.version=${{ github.ref_name }} --label org.opencontainers.image.source=https://github.com/bnema/gordon --build-arg BINARY=bin/gordon-linux-amd64 --push . | |||
| 33 | + | docker buildx build --platform linux/arm64 --tag ghcr.io/bnema/gordon:${{ github.ref_name }}-arm64 --label org.opencontainers.image.created=$(date +'%Y-%m-%dT%H:%M:%SZ') --label org.opencontainers.image.title=gordon --label org.opencontainers.image.revision=${{ github.sha }} --label org.opencontainers.image.version=${{ github.ref_name }} --label org.opencontainers.image.source=https://github.com/bnema/gordon --build-arg BINARY=bin/gordon-linux-arm64 --push . | |||
| 34 | + | env: | |||
| 35 | + | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |||
| 36 | + | ||||
| 37 | + | - name: Create Docker Manifest | |||
| 38 | + | run: | | |||
| 39 | + | docker manifest create ghcr.io/bnema/gordon:latest ghcr.io/bnema/gordon:${{ github.ref_name }}-amd64 ghcr.io/bnema/gordon:${{ github.ref_name }}-arm64 --amend | |||
| 40 | + | docker manifest push ghcr.io/bnema/gordon:latest | |||
| 41 | + | ||||
| 42 | + | - name: Create Release Artifacts | |||
| 43 | + | run: | | |||
| 44 | + | zip -j gordon_${{ github.ref_name }}_linux_amd64.zip gordon-linux-amd64 | |||
| 45 | + | zip -j gordon_${{ github.ref_name }}_windows_amd64.zip gordon-windows-amd64.exe | |||
| 46 | + | zip -j gordon_${{ github.ref_name }}_darwin_amd64.zip gordon-darwin-amd64 | |||
| 47 | + | zip -j gordon_${{ github.ref_name }}_linux_arm64.zip gordon-linux-arm64 | |||
| 48 | + | zip -j gordon_${{ github.ref_name }}_windows_arm64.zip gordon-windows-arm64.exe | |||
| 49 | + | zip -j gordon_${{ github.ref_name }}_darwin_arm64.zip gordon-darwin-arm64 | |||
| 50 | + | ||||
| 51 | + | - name: Install Cosign | |||
| 52 | + | run: | | |||
| 53 | + | curl -sfL https://raw.githubusercontent.com/sigstore/cosign/main/install.sh | sudo sh | |||
| 54 | + | ||||
| 55 | + | - name: Create Release | |||
| 56 | + | uses: actions/create-release@v1 | |||
| 57 | + | with: | |||
| 58 | + | tag_name: ${{ github.ref }} | |||
| 59 | + | release_name: Release ${{ github.ref }} | |||
| 60 | + | draft: true | |||
| 61 | + | prerelease: false | |||
| 62 | + | env: | |||
| 63 | + | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}⏎̸ | |||
M .goreleaser.yaml +1 -7
| @@ -49,12 +49,6 @@ dockers: | |||
| 49 | 49 | - "--label=org.opencontainers.image.source=https://github.com/bnema/gordon" | |
| 50 | 50 | extra_files: | |
| 51 | 51 | - .iscontainer | |
| 52 | - | docker_manifests: | |
| 53 | - | - name_template: "ghcr.io/bnema/gordon:latest" | |
| 54 | - | image_templates: | |
| 55 | - | - "ghcr.io/bnema/gordon:{{ .Tag }}-amd64" | |
| 56 | - | - "ghcr.io/bnema/gordon:{{ .Tag }}-arm64" | |
| 57 | - | create_flags: ["--amend"] | |
| 58 | 52 | ||
| 59 | 53 | archives: | |
| 60 | 54 | - format: zip | |
| @@ -65,4 +59,4 @@ changelog: | |||
| 65 | 59 | filters: | |
| 66 | 60 | exclude: | |
| 67 | 61 | - "^docs:" | |
| 68 | - | - "^test:" | |
| 62 | + | - "^test:"⏎̸ | |
| @@ -49,12 +49,6 @@ dockers: | |||||
| 49 | - "--label=org.opencontainers.image.source=https://github.com/bnema/gordon" | 49 | - "--label=org.opencontainers.image.source=https://github.com/bnema/gordon" | ||
| 50 | extra_files: | 50 | extra_files: | ||
| 51 | - .iscontainer | 51 | - .iscontainer | ||
| 52 | - | docker_manifests: | |||
| 53 | - | - name_template: "ghcr.io/bnema/gordon:latest" | |||
| 54 | - | image_templates: | |||
| 55 | - | - "ghcr.io/bnema/gordon:{{ .Tag }}-amd64" | |||
| 56 | - | - "ghcr.io/bnema/gordon:{{ .Tag }}-arm64" | |||
| 57 | - | create_flags: ["--amend"] | |||
| 58 | 52 | ||||
| 59 | archives: | 53 | archives: | ||
| 60 | - format: zip | 54 | - format: zip | ||
| @@ -65,4 +59,4 @@ changelog: | |||||
| 65 | filters: | 59 | filters: | ||
| 66 | exclude: | 60 | exclude: | ||
| 67 | - "^docs:" | 61 | - "^docs:" | ||
| 68 | - | - "^test:" | 62 | + | - "^test:"⏎̸ |
M Dockerfile +2 -5
| @@ -1,12 +1,9 @@ | |||
| 1 | 1 | # Use a small base image | |
| 2 | 2 | FROM alpine | |
| 3 | - | ||
| 3 | + | ARG BINARY | |
| 4 | 4 | COPY .iscontainer / | |
| 5 | - | # Copy the compiled binary from your build context into the container | |
| 6 | 5 | COPY gordon / | |
| 7 | - | ||
| 8 | - | # Set the binary as the entrypoint of the container | |
| 9 | 6 | ENTRYPOINT ["/gordon"] | |
| 10 | 7 | ||
| 11 | 8 | # Default command when running the container | |
| 12 | - | CMD ["serve"]⏎̸ | |
| 9 | + | CMD ["serve"] | |
| @@ -1,12 +1,9 @@ | |||||
| 1 | # Use a small base image | 1 | # Use a small base image | ||
| 2 | FROM alpine | 2 | FROM alpine | ||
| 3 | - | 3 | + | ARG BINARY | |
| 4 | COPY .iscontainer / | 4 | COPY .iscontainer / | ||
| 5 | - | # Copy the compiled binary from your build context into the container | |||
| 6 | COPY gordon / | 5 | COPY gordon / | ||
| 7 | - | ||||
| 8 | - | # Set the binary as the entrypoint of the container | |||
| 9 | ENTRYPOINT ["/gordon"] | 6 | ENTRYPOINT ["/gordon"] | ||
| 10 | 7 | ||||
| 11 | # Default command when running the container | 8 | # Default command when running the container | ||
| 12 | - | CMD ["serve"]⏎̸ | 9 | + | CMD ["serve"] |
M README.md +34 -15
| @@ -13,30 +13,49 @@ Gordon is a tool written in Go that aims to simplify the deployment of your cont | |||
| 13 | 13 | ||
| 14 | 14 | ## **Key Features** | |
| 15 | 15 | ||
| 16 | - | - **Quick Deployment**: Deploy container images effortlessly using the command **`gordon deploy <image:version> -p 8080:80 -t gordon.sld.tld`** | |
| 17 | - | - **Self-Hosted**: Operates within your existing self-hosted environment. | |
| 18 | - | - **Web UI**: Provides a simple web UI to manage your deployments, domains, exposed ports, and more. | |
| 16 | + | - **Ease of use**: Deploy container images effortlessly using the command **`gordon deploy`**. | |
| 17 | + | - **Self-Hosted**: Operates within your existing self-hosted environment and don't interfere with your setup. | |
| 18 | + | - **Self-Updated**: The client can update itself to the latest version with `gordon update`. The backend is updated by pulling the latest image from ghcr.io. | |
| 19 | + | - **Minimal WebUI**: Provides a simple web UI to manage your deployments, domains, exposed ports. Using a very minimal stack (sqlite3, Go templates, HTMX and Tailwind CSS). | |
| 20 | + | - **Simple authentication**: Use Github OAuth to authenticate with the web UI and a token for the CLI. | |
| 19 | 21 | - **Automated Routing**: Collaborates with Traefik to automatically route your application to your desired domain or subdomain. | |
| 20 | 22 | - **One binary**: Gordon follows the Golang philosophy of keeping things simple, offering a single binary for all functionalities. | |
| 21 | - | - **Minimal stack**: Sqlite3 is used as in memory database (no ORM), the web UI is built using Go templates, HTMX and Tailwind CSS | |
| 23 | + | ||
| 24 | + |  | |
| 22 | 25 | ||
| 23 | 26 | ## **Why ?** | |
| 24 | 27 | ||
| 25 | - | I found myself spending excessive time manually deploying my containerized applications using remote SSH. Being a lazy developer, I wanted a tool that would automate this process. However, I couldn't find one that met my requirements (dead simple), so I decided to make my own. | |
| 28 | + | I needed a simple tool that would automate the process of quickly spin up my web apps and preferably not on someone else's computer. And since I wanted to learn Go, I decided to build it myself. | |
| 29 | + | ||
| 30 | + | ## **Installation / Getting Started / Usage** | |
| 31 | + | ||
| 32 | + | For detailed instructions, please refer to the [wiki](https://github.com/bnema/gordon/wiki/) | |
| 33 | + | ||
| 34 | + | - [Setting up Gordon’s Backend](https://github.com/bnema/gordon/wiki/Setting-up-Gordon%E2%80%99s-Backend) | |
| 35 | + | - [Setting up Gordon's Client](https://github.com/bnema/gordon/wiki/Setting-up-Gordon's-Client) | |
| 36 | + | - [Deploying your first app](https://github.com/bnema/gordon/wiki/First-deployment) | |
| 37 | + | ||
| 38 | + | ||
| 39 | + | ## **Development Status & Disclaimer** | |
| 40 | + | ||
| 41 | + | 🛠️ **Note**: Gordon is under heavy development and is absolutely not ready for production. Thank you in advance for taking the time to test it and report any issues you may encounter. | |
| 26 | 42 | ||
| 27 | - | ## **Development Status** | |
| 43 | + | I also want to emphasize that I am far from being a Go expert yet. Hence, if you notice any poor practices, I welcome your feedback—respectfully, of course. It's a valuable part of the learning process! | |
| 28 | 44 | ||
| 29 | - | 🛠️ **Note**: This project is currently under heavy development. A 0.1 release with basic features will be available soon. | |
| 45 | + | ## **Roadmap beyond 0.1** | |
| 30 | 46 | ||
| 31 | - | ## **TODO for 0.1 release** | |
| 47 | + | - Bug fixes (obviously) | |
| 48 | + | - Add tests | |
| 49 | + | - Better error handling and logging | |
| 50 | + | - Sexier WebUI with more features while keeping it simple (spa) | |
| 51 | + | - Sexier CLI (💕 [Charm](https://github.com/charmbracelet)) | |
| 52 | + | - Refined CLI to backend authentication process (utilizing GitHub OAuth Device flow) | |
| 53 | + | - `deploy` command with no params | |
| 54 | + | - `push` command | |
| 55 | + | - Templates for databases (mysql, postgresql, redis, etc.) | |
| 32 | 56 | ||
| 33 | - | - [x] Basic web UI for managing deployments | |
| 34 | - | - [x] CRUD operations with the container engine | |
| 35 | - | - [x] Improve authentication security | |
| 36 | - | - [x] Interaction with the database | |
| 37 | - | - [ ] New version update mechanism (backup, rollback etc.) | |
| 38 | - | - [ ] Basic CLI to push images to the container engine | |
| 57 | + | Have suggestions? Feel free to open an issue! | |
| 39 | 58 | ||
| 40 | 59 | ## **License** | |
| 41 | 60 | ||
| 42 | - | Gordon is licensed under the GPL-3.0 license. Please see the LICENSE file for more details. | |
| 61 | + | Gordon is licensed under the GPL-3.0 license. Please see the LICENSE file for more details.⏎̸ | |
| @@ -13,30 +13,49 @@ Gordon is a tool written in Go that aims to simplify the deployment of your cont | |||||
| 13 | 13 | ||||
| 14 | ## **Key Features** | 14 | ## **Key Features** | ||
| 15 | 15 | ||||
| 16 | - | - **Quick Deployment**: Deploy container images effortlessly using the command **`gordon deploy <image:version> -p 8080:80 -t gordon.sld.tld`** | 16 | + | - **Ease of use**: Deploy container images effortlessly using the command **`gordon deploy`**. |
| 17 | - | - **Self-Hosted**: Operates within your existing self-hosted environment. | 17 | + | - **Self-Hosted**: Operates within your existing self-hosted environment and don't interfere with your setup. |
| 18 | - | - **Web UI**: Provides a simple web UI to manage your deployments, domains, exposed ports, and more. | 18 | + | - **Self-Updated**: The client can update itself to the latest version with `gordon update`. The backend is updated by pulling the latest image from ghcr.io. |
| 19 | + | - **Minimal WebUI**: Provides a simple web UI to manage your deployments, domains, exposed ports. Using a very minimal stack (sqlite3, Go templates, HTMX and Tailwind CSS). | |||
| 20 | + | - **Simple authentication**: Use Github OAuth to authenticate with the web UI and a token for the CLI. | |||
| 19 | - **Automated Routing**: Collaborates with Traefik to automatically route your application to your desired domain or subdomain. | 21 | - **Automated Routing**: Collaborates with Traefik to automatically route your application to your desired domain or subdomain. | ||
| 20 | - **One binary**: Gordon follows the Golang philosophy of keeping things simple, offering a single binary for all functionalities. | 22 | - **One binary**: Gordon follows the Golang philosophy of keeping things simple, offering a single binary for all functionalities. | ||
| 21 | - | - **Minimal stack**: Sqlite3 is used as in memory database (no ORM), the web UI is built using Go templates, HTMX and Tailwind CSS | 23 | + | |
| 24 | + |  | |||
| 22 | 25 | ||||
| 23 | ## **Why ?** | 26 | ## **Why ?** | ||
| 24 | 27 | ||||
| 25 | - | I found myself spending excessive time manually deploying my containerized applications using remote SSH. Being a lazy developer, I wanted a tool that would automate this process. However, I couldn't find one that met my requirements (dead simple), so I decided to make my own. | 28 | + | I needed a simple tool that would automate the process of quickly spin up my web apps and preferably not on someone else's computer. And since I wanted to learn Go, I decided to build it myself. |
| 29 | + | ||||
| 30 | + | ## **Installation / Getting Started / Usage** | |||
| 31 | + | ||||
| 32 | + | For detailed instructions, please refer to the [wiki](https://github.com/bnema/gordon/wiki/) | |||
| 33 | + | ||||
| 34 | + | - [Setting up Gordon’s Backend](https://github.com/bnema/gordon/wiki/Setting-up-Gordon%E2%80%99s-Backend) | |||
| 35 | + | - [Setting up Gordon's Client](https://github.com/bnema/gordon/wiki/Setting-up-Gordon's-Client) | |||
| 36 | + | - [Deploying your first app](https://github.com/bnema/gordon/wiki/First-deployment) | |||
| 37 | + | ||||
| 38 | + | ||||
| 39 | + | ## **Development Status & Disclaimer** | |||
| 40 | + | ||||
| 41 | + | 🛠️ **Note**: Gordon is under heavy development and is absolutely not ready for production. Thank you in advance for taking the time to test it and report any issues you may encounter. | |||
| 26 | 42 | ||||
| 27 | - | ## **Development Status** | 43 | + | I also want to emphasize that I am far from being a Go expert yet. Hence, if you notice any poor practices, I welcome your feedback—respectfully, of course. It's a valuable part of the learning process! |
| 28 | 44 | ||||
| 29 | - | 🛠️ **Note**: This project is currently under heavy development. A 0.1 release with basic features will be available soon. | 45 | + | ## **Roadmap beyond 0.1** |
| 30 | 46 | ||||
| 31 | - | ## **TODO for 0.1 release** | 47 | + | - Bug fixes (obviously) |
| 48 | + | - Add tests | |||
| 49 | + | - Better error handling and logging | |||
| 50 | + | - Sexier WebUI with more features while keeping it simple (spa) | |||
| 51 | + | - Sexier CLI (💕 [Charm](https://github.com/charmbracelet)) | |||
| 52 | + | - Refined CLI to backend authentication process (utilizing GitHub OAuth Device flow) | |||
| 53 | + | - `deploy` command with no params | |||
| 54 | + | - `push` command | |||
| 55 | + | - Templates for databases (mysql, postgresql, redis, etc.) | |||
| 32 | 56 | ||||
| 33 | - | - [x] Basic web UI for managing deployments | 57 | + | Have suggestions? Feel free to open an issue! |
| 34 | - | - [x] CRUD operations with the container engine | |||
| 35 | - | - [x] Improve authentication security | |||
| 36 | - | - [x] Interaction with the database | |||
| 37 | - | - [ ] New version update mechanism (backup, rollback etc.) | |||
| 38 | - | - [ ] Basic CLI to push images to the container engine | |||
| 39 | 58 | ||||
| 40 | ## **License** | 59 | ## **License** | ||
| 41 | 60 | ||||
| 42 | - | Gordon is licensed under the GPL-3.0 license. Please see the LICENSE file for more details. | 61 | + | Gordon is licensed under the GPL-3.0 license. Please see the LICENSE file for more details.⏎̸ |
M cmd/cli/main.go +4 -1
| @@ -10,6 +10,9 @@ import ( | |||
| 10 | 10 | "github.com/bnema/gordon/internal/common" | |
| 11 | 11 | "github.com/bnema/gordon/internal/server" | |
| 12 | 12 | "github.com/spf13/cobra" | |
| 13 | + | // env auto load | |
| 14 | + | _ "github.com/joho/godotenv/autoload" | |
| 15 | + | "os" | |
| 13 | 16 | ) | |
| 14 | 17 | ||
| 15 | 18 | var ( | |
| @@ -39,7 +42,7 @@ func main() { | |||
| 39 | 42 | BuildVersion: build, | |
| 40 | 43 | BuildCommit: commit, | |
| 41 | 44 | BuildDate: date, | |
| 42 | - | ProxyURL: "https://gordon-proxy.bnema.dev", | |
| 45 | + | ProxyURL: os.Getenv("PROXY_URL"), | |
| 43 | 46 | } | |
| 44 | 47 | ||
| 45 | 48 | a, err := cli.NewClientApp(buildInfo) | |
| @@ -10,6 +10,9 @@ import ( | |||||
| 10 | "github.com/bnema/gordon/internal/common" | 10 | "github.com/bnema/gordon/internal/common" | ||
| 11 | "github.com/bnema/gordon/internal/server" | 11 | "github.com/bnema/gordon/internal/server" | ||
| 12 | "github.com/spf13/cobra" | 12 | "github.com/spf13/cobra" | ||
| 13 | + | // env auto load | |||
| 14 | + | _ "github.com/joho/godotenv/autoload" | |||
| 15 | + | "os" | |||
| 13 | ) | 16 | ) | ||
| 14 | 17 | ||||
| 15 | var ( | 18 | var ( | ||
| @@ -39,7 +42,7 @@ func main() { | |||||
| 39 | BuildVersion: build, | 42 | BuildVersion: build, | ||
| 40 | BuildCommit: commit, | 43 | BuildCommit: commit, | ||
| 41 | BuildDate: date, | 44 | BuildDate: date, | ||
| 42 | - | ProxyURL: "https://gordon-proxy.bnema.dev", | 45 | + | ProxyURL: os.Getenv("PROXY_URL"), |
| 43 | } | 46 | } | ||
| 44 | 47 | ||||
| 45 | a, err := cli.NewClientApp(buildInfo) | 48 | a, err := cli.NewClientApp(buildInfo) | ||
D compose.yml.example +0 -28
| @@ -1,28 +0,0 @@ | |||
| 1 | - | version: '3' | |
| 2 | - | ||
| 3 | - | services: | |
| 4 | - | gordon: | |
| 5 | - | container_name: gordon | |
| 6 | - | image: bnema/gordon:latest | |
| 7 | - | ports: | |
| 8 | - | - "1323" | |
| 9 | - | environment: | |
| 10 | - | SESSION_SECRET: "secret" | |
| 11 | - | DEV: false | |
| 12 | - | USER: "${USER}" | |
| 13 | - | volumes: | |
| 14 | - | - ./data:/data | |
| 15 | - | - .config.yml:/config.yml | |
| 16 | - | - /run/user/1000/podman/podman.sock:/var/run/docker.sock | |
| 17 | - | restart: always | |
| 18 | - | networks: | |
| 19 | - | - traefik | |
| 20 | - | labels: | |
| 21 | - | - "traefik.enable=true" | |
| 22 | - | - "traefik.http.routers.gordon.rule=Host(`gordon.domain.tld)" | |
| 23 | - | - "traefik.http.routers.gordon.entrypoints=websecure" | |
| 24 | - | - "traefik.http.routers.gordon.tls.certresolver=letsencrypt" | |
| 25 | - | - "traefik.http.services.gordon.loadbalancer.server.port=1323" | |
| 26 | - | networks: | |
| 27 | - | traefik: | |
| 28 | - | external: true | |
| @@ -1,28 +0,0 @@ | |||||
| 1 | - | version: '3' | |||
| 2 | - | ||||
| 3 | - | services: | |||
| 4 | - | gordon: | |||
| 5 | - | container_name: gordon | |||
| 6 | - | image: bnema/gordon:latest | |||
| 7 | - | ports: | |||
| 8 | - | - "1323" | |||
| 9 | - | environment: | |||
| 10 | - | SESSION_SECRET: "secret" | |||
| 11 | - | DEV: false | |||
| 12 | - | USER: "${USER}" | |||
| 13 | - | volumes: | |||
| 14 | - | - ./data:/data | |||
| 15 | - | - .config.yml:/config.yml | |||
| 16 | - | - /run/user/1000/podman/podman.sock:/var/run/docker.sock | |||
| 17 | - | restart: always | |||
| 18 | - | networks: | |||
| 19 | - | - traefik | |||
| 20 | - | labels: | |||
| 21 | - | - "traefik.enable=true" | |||
| 22 | - | - "traefik.http.routers.gordon.rule=Host(`gordon.domain.tld)" | |||
| 23 | - | - "traefik.http.routers.gordon.entrypoints=websecure" | |||
| 24 | - | - "traefik.http.routers.gordon.tls.certresolver=letsencrypt" | |||
| 25 | - | - "traefik.http.services.gordon.loadbalancer.server.port=1323" | |||
| 26 | - | networks: | |||
| 27 | - | traefik: | |||
| 28 | - | external: true | |||
M internal/cli/cmd/deploy.go +23 -9
| @@ -65,6 +65,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 65 | 65 | } | |
| 66 | 66 | ||
| 67 | 67 | progressCh := make(chan mvu.ProgressMsg) | |
| 68 | + | errCh := make(chan error, 1) // Buffer of 1 to prevent goroutine leak in case of non-blocking send | |
| 68 | 69 | ||
| 69 | 70 | // Create a progress function to update the progress bar | |
| 70 | 71 | progressReader := &mvu.ProgressReader{ | |
| @@ -87,9 +88,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 87 | 88 | go func() { | |
| 88 | 89 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | |
| 89 | 90 | if err != nil { | |
| 90 | - | fmt.Println("Error sending HTTP request:", err) | |
| 91 | - | // Don't forget to close the channel if there's an error | |
| 92 | - | close(progressCh) | |
| 91 | + | errCh <- fmt.Errorf("error sending HTTP request: %w", err) | |
| 93 | 92 | return | |
| 94 | 93 | } | |
| 95 | 94 | ||
| @@ -116,28 +115,43 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||
| 116 | 115 | // Run the TUI program | |
| 117 | 116 | finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port) | |
| 118 | 117 | if err != nil { | |
| 119 | - | fmt.Println("Error running deployment TUI:", err) | |
| 118 | + | errCh <- fmt.Errorf("error running deployment TUI: %w", err) | |
| 120 | 119 | return | |
| 121 | 120 | } | |
| 122 | 121 | ||
| 123 | 122 | // Check if the deployment was successful | |
| 124 | 123 | if !finalModel.DeploymentDone { | |
| 125 | - | fmt.Println("Deployment failed check your configuration and try again.") | |
| 124 | + | errCh <- fmt.Errorf("deployment failed") | |
| 126 | 125 | return | |
| 127 | 126 | } | |
| 128 | 127 | ||
| 129 | 128 | // Print the final message | |
| 130 | - | color.Blue(" Deployment successful!") | |
| 131 | - | fmt.Println(" Your application is now available at:", targetDomain) | |
| 129 | + | color.Blue("Deployment successful!") | |
| 130 | + | fmt.Println("Your application is now available at:", targetDomain) | |
| 132 | 131 | close(progressCh) | |
| 133 | 132 | }() | |
| 134 | - | ||
| 135 | 133 | // Run the progress bar TUI | |
| 136 | 134 | m, err := mvu.RunProgressBarTUI(progressCh) | |
| 137 | 135 | if err != nil { | |
| 138 | - | fmt.Println("Error running progress bar TUI:", err) | |
| 136 | + | errCh <- fmt.Errorf("error running progress bar TUI: %w", err) | |
| 139 | 137 | return | |
| 140 | 138 | } | |
| 139 | + | done := false | |
| 140 | + | ||
| 141 | + | for !done { | |
| 142 | + | select { | |
| 143 | + | case err := <-errCh: | |
| 144 | + | if err != nil { | |
| 145 | + | fmt.Println(err) | |
| 146 | + | return | |
| 147 | + | } | |
| 148 | + | case <-time.After(1 * time.Second): | |
| 149 | + | // Check if the progress bar is done | |
| 150 | + | if m.Done { | |
| 151 | + | done = true | |
| 152 | + | } | |
| 153 | + | } | |
| 154 | + | } | |
| 141 | 155 | ||
| 142 | 156 | // Close the reader | |
| 143 | 157 | err = progressReader.Close() | |
| @@ -65,6 +65,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||||
| 65 | } | 65 | } | ||
| 66 | 66 | ||||
| 67 | progressCh := make(chan mvu.ProgressMsg) | 67 | progressCh := make(chan mvu.ProgressMsg) | ||
| 68 | + | errCh := make(chan error, 1) // Buffer of 1 to prevent goroutine leak in case of non-blocking send | |||
| 68 | 69 | ||||
| 69 | // Create a progress function to update the progress bar | 70 | // Create a progress function to update the progress bar | ||
| 70 | progressReader := &mvu.ProgressReader{ | 71 | progressReader := &mvu.ProgressReader{ | ||
| @@ -87,9 +88,7 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||||
| 87 | go func() { | 88 | go func() { | ||
| 88 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | 89 | resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push") | ||
| 89 | if err != nil { | 90 | if err != nil { | ||
| 90 | - | fmt.Println("Error sending HTTP request:", err) | 91 | + | errCh <- fmt.Errorf("error sending HTTP request: %w", err) |
| 91 | - | // Don't forget to close the channel if there's an error | |||
| 92 | - | close(progressCh) | |||
| 93 | return | 92 | return | ||
| 94 | } | 93 | } | ||
| 95 | 94 | ||||
| @@ -116,28 +115,43 @@ func NewDeployCommand(a *cli.App) *cobra.Command { | |||||
| 116 | // Run the TUI program | 115 | // Run the TUI program | ||
| 117 | finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port) | 116 | finalModel, err := mvu.RunDeploymentTUI(client, imageName, targetDomain, port) | ||
| 118 | if err != nil { | 117 | if err != nil { | ||
| 119 | - | fmt.Println("Error running deployment TUI:", err) | 118 | + | errCh <- fmt.Errorf("error running deployment TUI: %w", err) |
| 120 | return | 119 | return | ||
| 121 | } | 120 | } | ||
| 122 | 121 | ||||
| 123 | // Check if the deployment was successful | 122 | // Check if the deployment was successful | ||
| 124 | if !finalModel.DeploymentDone { | 123 | if !finalModel.DeploymentDone { | ||
| 125 | - | fmt.Println("Deployment failed check your configuration and try again.") | 124 | + | errCh <- fmt.Errorf("deployment failed") |
| 126 | return | 125 | return | ||
| 127 | } | 126 | } | ||
| 128 | 127 | ||||
| 129 | // Print the final message | 128 | // Print the final message | ||
| 130 | - | color.Blue(" Deployment successful!") | 129 | + | color.Blue("Deployment successful!") |
| 131 | - | fmt.Println(" Your application is now available at:", targetDomain) | 130 | + | fmt.Println("Your application is now available at:", targetDomain) |
| 132 | close(progressCh) | 131 | close(progressCh) | ||
| 133 | }() | 132 | }() | ||
| 134 | - | ||||
| 135 | // Run the progress bar TUI | 133 | // Run the progress bar TUI | ||
| 136 | m, err := mvu.RunProgressBarTUI(progressCh) | 134 | m, err := mvu.RunProgressBarTUI(progressCh) | ||
| 137 | if err != nil { | 135 | if err != nil { | ||
| 138 | - | fmt.Println("Error running progress bar TUI:", err) | 136 | + | errCh <- fmt.Errorf("error running progress bar TUI: %w", err) |
| 139 | return | 137 | return | ||
| 140 | } | 138 | } | ||
| 139 | + | done := false | |||
| 140 | + | ||||
| 141 | + | for !done { | |||
| 142 | + | select { | |||
| 143 | + | case err := <-errCh: | |||
| 144 | + | if err != nil { | |||
| 145 | + | fmt.Println(err) | |||
| 146 | + | return | |||
| 147 | + | } | |||
| 148 | + | case <-time.After(1 * time.Second): | |||
| 149 | + | // Check if the progress bar is done | |||
| 150 | + | if m.Done { | |||
| 151 | + | done = true | |||
| 152 | + | } | |||
| 153 | + | } | |||
| 154 | + | } | |||
| 141 | 155 | ||||
| 142 | // Close the reader | 156 | // Close the reader | ||
| 143 | err = progressReader.Close() | 157 | err = progressReader.Close() | ||