Skip to content

Commit 028d56c

Browse files
committed
Add DigitalOcean Marketplace 1-click scaffolding
1 parent c89ad75 commit 028d56c

13 files changed

Lines changed: 383 additions & 10 deletions

File tree

.github/workflows/ci.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ jobs:
3232
- name: Check formatting
3333
run: cargo fmt --all -- --check
3434

35+
marketplace-validate:
36+
name: Marketplace Packer Validate
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@v6
40+
41+
- name: Setup Packer
42+
uses: hashicorp/setup-packer@v3
43+
with:
44+
version: "1.10.0"
45+
46+
- name: Packer init
47+
run: packer init infra/digitalocean/packer/opencode-marketplace.pkr.hcl
48+
49+
- name: Packer fmt
50+
run: packer fmt -check infra/digitalocean/packer/opencode-marketplace.pkr.hcl
51+
52+
- name: Packer validate
53+
run: packer validate -var-file=infra/digitalocean/packer/variables.pkr.hcl infra/digitalocean/packer/opencode-marketplace.pkr.hcl
54+
3555
# Build, lint, and test on multiple platforms
3656
# Clippy/build/test must run on each platform because #[cfg(target_os)] causes
3757
# different code to compile (e.g., systemd.rs on Linux, launchd.rs on macOS)

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
[![CI](https://github.com/pRizz/opencode-cloud/actions/workflows/ci.yml/badge.svg)](https://github.com/pRizz/opencode-cloud/actions/workflows/ci.yml)
44
[![Mirror](https://img.shields.io/badge/mirror-gitea-blue?logo=gitea)](https://gitea.com/pRizz/opencode-cloud)
55
[![crates.io](https://img.shields.io/crates/v/opencode-cloud.svg)](https://crates.io/crates/opencode-cloud)
6-
[![GHCR](https://img.shields.io/badge/ghcr.io-sandbox-blue?logo=github)](https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox)
76
[![Docker Hub](https://img.shields.io/docker/v/prizz/opencode-cloud-sandbox?label=docker&sort=semver)](https://hub.docker.com/r/prizz/opencode-cloud-sandbox)
87
[![Docker Pulls](https://img.shields.io/docker/pulls/prizz/opencode-cloud-sandbox)](https://hub.docker.com/r/prizz/opencode-cloud-sandbox)
8+
[![GHCR](https://img.shields.io/badge/ghcr.io-sandbox-blue?logo=github)](https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox)
99
[![docs.rs](https://docs.rs/opencode-cloud/badge.svg)](https://docs.rs/opencode-cloud)
1010
[![MSRV](https://img.shields.io/badge/MSRV-1.85-blue.svg)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html)
1111
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -51,6 +51,15 @@ Quick deploy provisions a private EC2 instance behind a public ALB with HTTPS.
5151
Docs: `docs/deploy/aws.md` (includes teardown steps and S3 hosting setup for forks)
5252
Credentials: `docs/deploy/aws.md#retrieving-credentials`
5353

54+
## Deploy to DigitalOcean
55+
56+
[![Deploy on DigitalOcean](https://img.shields.io/badge/Deploy-DigitalOcean-0080FF?logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/opencode-cloud)
57+
58+
Marketplace one-click deploy provisions a Droplet that bootstraps opencode-cloud
59+
on first boot.
60+
61+
Docs: `docs/deploy/digitalocean-marketplace.md`
62+
5463
## Features
5564

5665
- **Sandboxed execution** - opencode runs inside a Docker container, isolated from your host system
@@ -77,12 +86,12 @@ The sandbox container image is named **`opencode-cloud-sandbox`** (not `opencode
7786

7887
**Why use the CLI?** It configures volumes, ports, and upgrades safely, so you don’t have to manage `docker run` flags or image updates yourself.
7988

80-
The image is published to both registries:
89+
The image is published to both registries (Docker Hub is the primary distribution):
8190

8291
| Registry | Image |
8392
|----------|-------|
84-
| GitHub Container Registry | [`ghcr.io/prizz/opencode-cloud-sandbox`](https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox) |
8593
| Docker Hub | [`prizz/opencode-cloud-sandbox`](https://hub.docker.com/r/prizz/opencode-cloud-sandbox) |
94+
| GitHub Container Registry | [`ghcr.io/prizz/opencode-cloud-sandbox`](https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox) |
8695

8796
Pull commands:
8897

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# DigitalOcean Marketplace (1-Click Droplet)
2+
3+
This deployment path packages opencode-cloud as a DigitalOcean Marketplace 1-click
4+
Droplet image. Users deploy from the Marketplace listing and the instance
5+
bootstraps itself on first boot.
6+
7+
## Deploy from the Marketplace
8+
9+
1. Click the **Deploy on DigitalOcean** button in the root README.
10+
2. Select a region/size and create the Droplet.
11+
3. Wait for cloud-init to finish (usually a few minutes).
12+
4. Open `http://<droplet-public-ip>:3000`.
13+
14+
> Note: update the Marketplace URL in `README.md` and `packages/core/README.md`
15+
> once the listing is approved and a final slug is assigned.
16+
17+
### Retrieve credentials
18+
19+
Credentials are written to `/var/lib/opencode-cloud/deploy-status.json` (root-only):
20+
21+
```bash
22+
sudo cat /var/lib/opencode-cloud/deploy-status.json
23+
```
24+
25+
Logs:
26+
27+
- `/var/log/opencode-cloud-setup.log`
28+
- `/var/log/cloud-init-output.log`
29+
- `/var/log/cloud-init.log`
30+
31+
## Optional user-data overrides
32+
33+
You can supply cloud-init user-data to override defaults by writing
34+
`/etc/opencode-cloud/stack.env`.
35+
36+
Example:
37+
38+
```yaml
39+
#cloud-config
40+
write_files:
41+
- path: /etc/opencode-cloud/stack.env
42+
permissions: "0600"
43+
content: |
44+
HOST_CONTAINER_IMAGE=prizz/opencode-cloud-sandbox:15.2.0
45+
HOST_CONTAINER_NAME=opencode-cloud-sandbox
46+
CONTAINER_USERNAME=opencode
47+
```
48+
49+
## Building the Marketplace image
50+
51+
The Packer template lives at:
52+
53+
- `infra/digitalocean/packer/opencode-marketplace.pkr.hcl`
54+
- `infra/digitalocean/packer/variables.pkr.hcl`
55+
56+
`variables.pkr.hcl` pins the Marketplace container tag for reproducibility.
57+
Update `container_image` and `application_version` for each release snapshot.
58+
59+
Run:
60+
61+
```bash
62+
packer init infra/digitalocean/packer/opencode-marketplace.pkr.hcl
63+
packer validate -var-file=infra/digitalocean/packer/variables.pkr.hcl \
64+
infra/digitalocean/packer/opencode-marketplace.pkr.hcl
65+
packer build -var-file=infra/digitalocean/packer/variables.pkr.hcl \
66+
infra/digitalocean/packer/opencode-marketplace.pkr.hcl
67+
```
68+
69+
Or use the `justfile` helpers:
70+
71+
```bash
72+
just do-marketplace-validate
73+
just do-marketplace-build
74+
```
75+
76+
### Marketplace validation scripts
77+
78+
DigitalOcean provides `img_check.sh` and `cleanup.sh` in their Marketplace
79+
partners repo. Copy them into `infra/digitalocean/marketplace/` and run them
80+
against your built snapshot before submitting the image.

infra/aws/cloudformation/opencode-cloud-quick.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ Resources:
477477
- path: /etc/opencode-cloud/stack.env
478478
permissions: "0600"
479479
content: |
480-
HOST_CONTAINER_IMAGE=ghcr.io/prizz/opencode-cloud-sandbox:latest
480+
HOST_CONTAINER_IMAGE=prizz/opencode-cloud-sandbox:latest
481481
HOST_CONTAINER_NAME=opencode-cloud-sandbox
482482
CONTAINER_USERNAME=${OpencodeUsername}
483483
PUBLIC_OPENCODE_DOMAIN_URL=https://${DomainName}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Marketplace Validation Scripts
2+
3+
DigitalOcean Marketplace provides `img_check.sh` and `cleanup.sh` scripts as part
4+
of the vendor submission workflow. Copy those scripts into this directory and
5+
run them against the built snapshot before submitting for review.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
packer {
2+
required_plugins {
3+
digitalocean = {
4+
version = ">= 1.1.0"
5+
source = "github.com/digitalocean/digitalocean"
6+
}
7+
}
8+
}
9+
10+
variable "do_token" {
11+
type = string
12+
sensitive = true
13+
}
14+
15+
variable "region" {
16+
type = string
17+
default = "nyc3"
18+
}
19+
20+
variable "size" {
21+
type = string
22+
default = "s-2vcpu-4gb"
23+
}
24+
25+
variable "source_image" {
26+
type = string
27+
default = "ubuntu-24-04-x64"
28+
}
29+
30+
variable "snapshot_name" {
31+
type = string
32+
}
33+
34+
variable "droplet_name" {
35+
type = string
36+
default = "opencode-cloud-marketplace"
37+
}
38+
39+
variable "application_name" {
40+
type = string
41+
default = "opencode-cloud"
42+
}
43+
44+
variable "application_version" {
45+
type = string
46+
default = "15.2.0"
47+
}
48+
49+
variable "container_image" {
50+
type = string
51+
default = "prizz/opencode-cloud-sandbox:15.2.0"
52+
}
53+
54+
variable "container_name" {
55+
type = string
56+
default = "opencode-cloud-sandbox"
57+
}
58+
59+
variable "container_username" {
60+
type = string
61+
default = "opencode"
62+
}
63+
64+
source "digitalocean" "marketplace" {
65+
api_token = var.do_token
66+
region = var.region
67+
size = var.size
68+
image = var.source_image
69+
droplet_name = var.droplet_name
70+
snapshot_name = var.snapshot_name
71+
ssh_username = "root"
72+
ssh_timeout = "10m"
73+
}
74+
75+
build {
76+
sources = ["source.digitalocean.marketplace"]
77+
78+
provisioner "shell" {
79+
inline = [
80+
"set -euo pipefail",
81+
"export DEBIAN_FRONTEND=noninteractive",
82+
"apt-get update -y",
83+
"apt-get install -y docker.io curl jq build-essential pkg-config libssl-dev ufw",
84+
"systemctl enable --now docker",
85+
"ufw allow OpenSSH",
86+
"ufw allow 3000/tcp",
87+
"ufw --force enable"
88+
]
89+
}
90+
91+
provisioner "file" {
92+
source = "scripts/provisioning/opencode-cloud-setup.sh"
93+
destination = "/tmp/opencode-cloud-setup.sh"
94+
}
95+
96+
provisioner "file" {
97+
source = "scripts/provisioning/opencode-cloud-setup-cloud-init.sh"
98+
destination = "/tmp/opencode-cloud-setup-cloud-init.sh"
99+
}
100+
101+
provisioner "file" {
102+
source = "scripts/provisioning/opencode-cloud-setup-digitalocean.sh"
103+
destination = "/tmp/opencode-cloud-setup-digitalocean.sh"
104+
}
105+
106+
provisioner "shell" {
107+
inline = [
108+
"install -m 0755 /tmp/opencode-cloud-setup.sh /usr/local/bin/opencode-cloud-setup.sh",
109+
"install -m 0755 /tmp/opencode-cloud-setup-cloud-init.sh /usr/local/bin/opencode-cloud-setup-cloud-init.sh",
110+
"install -m 0755 /tmp/opencode-cloud-setup-digitalocean.sh /usr/local/bin/opencode-cloud-setup-digitalocean.sh"
111+
]
112+
}
113+
114+
provisioner "file" {
115+
source = "infra/digitalocean/scripts/per-instance-opencode-cloud.sh"
116+
destination = "/tmp/001-opencode-cloud.sh"
117+
}
118+
119+
provisioner "shell" {
120+
inline = [
121+
"install -d -m 0755 /var/lib/cloud/scripts/per-instance",
122+
"install -m 0755 /tmp/001-opencode-cloud.sh /var/lib/cloud/scripts/per-instance/001-opencode-cloud.sh"
123+
]
124+
}
125+
126+
provisioner "shell" {
127+
environment_vars = [
128+
"HOST_CONTAINER_IMAGE=${var.container_image}",
129+
"HOST_CONTAINER_NAME=${var.container_name}",
130+
"CONTAINER_USERNAME=${var.container_username}"
131+
]
132+
inline = [
133+
"install -d -m 0700 /etc/opencode-cloud",
134+
"printf '%s\\n' \"HOST_CONTAINER_IMAGE=$HOST_CONTAINER_IMAGE\" \"HOST_CONTAINER_NAME=$HOST_CONTAINER_NAME\" \"CONTAINER_USERNAME=$CONTAINER_USERNAME\" \"PUBLIC_OPENCODE_DOMAIN_URL=\" \"PUBLIC_OPENCODE_ALB_URL=\" > /etc/opencode-cloud/stack.env",
135+
"chmod 0600 /etc/opencode-cloud/stack.env"
136+
]
137+
}
138+
139+
provisioner "shell" {
140+
environment_vars = [
141+
"APPLICATION_NAME=${var.application_name}",
142+
"APPLICATION_VERSION=${var.application_version}"
143+
]
144+
inline = [
145+
"install -d -m 0755 /var/lib/digitalocean",
146+
"printf '%s\\n' \"application_name=$APPLICATION_NAME\" \"application_version=$APPLICATION_VERSION\" > /var/lib/digitalocean/application.info"
147+
]
148+
}
149+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
do_token = "DUMMY"
2+
region = "nyc3"
3+
size = "s-2vcpu-4gb"
4+
source_image = "ubuntu-24-04-x64"
5+
snapshot_name = "opencode-cloud-marketplace-dev"
6+
droplet_name = "opencode-cloud-marketplace"
7+
application_name = "opencode-cloud"
8+
application_version = "15.2.0"
9+
container_image = "prizz/opencode-cloud-sandbox:15.2.0"
10+
container_name = "opencode-cloud-sandbox"
11+
container_username = "opencode"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
STACK_ENV="/etc/opencode-cloud/stack.env"
5+
6+
if [ ! -f "$STACK_ENV" ]; then
7+
install -d -m 0700 /etc/opencode-cloud
8+
cat > "$STACK_ENV" <<EOF_STACK
9+
# Keep in sync with infra/digitalocean/packer/variables.pkr.hcl
10+
HOST_CONTAINER_IMAGE=prizz/opencode-cloud-sandbox:15.2.0
11+
HOST_CONTAINER_NAME=opencode-cloud-sandbox
12+
CONTAINER_USERNAME=opencode
13+
PUBLIC_OPENCODE_DOMAIN_URL=
14+
PUBLIC_OPENCODE_ALB_URL=
15+
EOF_STACK
16+
chmod 0600 "$STACK_ENV"
17+
fi
18+
19+
exec /usr/local/bin/opencode-cloud-setup-digitalocean.sh

justfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,21 @@ lint-shell:
145145
check-updates:
146146
./scripts/check-dockerfile-updates.sh
147147

148+
# --- DigitalOcean Marketplace ---
149+
150+
# Validate the DigitalOcean Marketplace Packer template
151+
do-marketplace-validate:
152+
packer init infra/digitalocean/packer/opencode-marketplace.pkr.hcl
153+
packer fmt -check infra/digitalocean/packer/opencode-marketplace.pkr.hcl
154+
packer validate -var-file=infra/digitalocean/packer/variables.pkr.hcl \
155+
infra/digitalocean/packer/opencode-marketplace.pkr.hcl
156+
157+
# Build the DigitalOcean Marketplace snapshot
158+
do-marketplace-build:
159+
packer init infra/digitalocean/packer/opencode-marketplace.pkr.hcl
160+
packer build -var-file=infra/digitalocean/packer/variables.pkr.hcl \
161+
infra/digitalocean/packer/opencode-marketplace.pkr.hcl
162+
148163
# Pre-commit checks (without Docker build - faster, works without Docker)
149164
pre-commit: fmt lint build test-all-fast
150165

0 commit comments

Comments
 (0)