nightshift-sdk 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nightshift_sdk-0.4.2/.github/workflows/release.yml +79 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/.gitignore +1 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/PKG-INFO +1 -1
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/deployment/provision.mdx +27 -12
- nightshift_sdk-0.4.2/examples/oai_agent.py +22 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/infra/bake-rootfs.sh +3 -43
- nightshift_sdk-0.4.2/infra/deploy.sh +362 -0
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/cron.sh +2 -2
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/deploy.sh +4 -4
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/production.sh +5 -5
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/setup.sh +7 -7
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/teardown.sh +2 -2
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/test.sh +6 -6
- nightshift_sdk-0.4.2/infra/upgrade.sh +112 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/pyproject.toml +1 -1
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/rootfs/build-rootfs.sh +27 -10
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/rootfs/init +3 -2
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/agent/entry.py +28 -12
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/run.py +7 -8
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/config.py +1 -1
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/server.py +4 -1
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/task.py +10 -4
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/vm/pool.py +11 -7
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/vm/rootfs.py +47 -34
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/uv.lock +1 -1
- nightshift_sdk-0.4.0/.claude/settings.local.json +0 -25
- nightshift_sdk-0.4.0/infra/.instance +0 -4
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/.claude/skills/opentui +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/CLAUDE.md +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/LICENSE +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/README.md +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/administration/api-keys.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/administration/backups.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/contributing/deploying-changes.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/contributing/dev-setup.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/deployment/architecture.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/deployment/cost-management.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/deployment/server.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/deployment/verify.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/docs.json +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/introduction.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/logo/kokapo.gif +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/logo/kokapo.svg +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/logo/nightshift-text.png +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/sdk/agent-config.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/docs/sdk/quickstart.mdx +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/basic_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/code_reviewer.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/edit_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/hooks_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/mcp_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/multi_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/net_test.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/pyproject.toml +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/smoke_test.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/stateful_agent.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/test-workspace/hello.txt +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/examples/web_researcher.py +0 -0
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/.gitignore +0 -0
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/lambda/start.py +0 -0
- {nightshift_sdk-0.4.0/infra → nightshift_sdk-0.4.2/infra/dev}/lambda/stop.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/__main__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/agent/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/agent/__main__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/auth.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/agents.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/api_key.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/deploy.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/login.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/commands/logs.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/config.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/cli/main.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/events.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/protocol/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/protocol/events.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/protocol/packaging.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/registry.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/sdk/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/sdk/app.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/sdk/config.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/vm/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/vm/manager.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/src/nightshift/vm/network.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/__init__.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_config.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_deploy_api.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_deploy_cli.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_events.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_protocol.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_registry.py +0 -0
- {nightshift_sdk-0.4.0 → nightshift_sdk-0.4.2}/tests/test_sdk.py +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-python:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- uses: astral-sh/setup-uv@v4
|
|
14
|
+
|
|
15
|
+
- run: uv build
|
|
16
|
+
|
|
17
|
+
- uses: actions/upload-artifact@v4
|
|
18
|
+
with:
|
|
19
|
+
name: dist
|
|
20
|
+
path: dist/
|
|
21
|
+
|
|
22
|
+
build-rootfs:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Install build dependencies
|
|
28
|
+
run: sudo apt-get update && sudo apt-get install -y squashfs-tools wget
|
|
29
|
+
|
|
30
|
+
- name: Build base rootfs
|
|
31
|
+
run: sudo ./rootfs/build-rootfs.sh rootfs.ext4
|
|
32
|
+
|
|
33
|
+
- name: Bake agent runtime
|
|
34
|
+
run: sudo ./infra/bake-rootfs.sh rootfs.ext4
|
|
35
|
+
|
|
36
|
+
- name: Compress rootfs
|
|
37
|
+
run: gzip rootfs.ext4
|
|
38
|
+
|
|
39
|
+
- uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: rootfs
|
|
42
|
+
path: rootfs.ext4.gz
|
|
43
|
+
|
|
44
|
+
publish-pypi:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
needs: [build-python]
|
|
47
|
+
environment: pypi
|
|
48
|
+
steps:
|
|
49
|
+
- uses: astral-sh/setup-uv@v4
|
|
50
|
+
|
|
51
|
+
- uses: actions/download-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
|
|
56
|
+
- run: uv publish dist/*
|
|
57
|
+
env:
|
|
58
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
59
|
+
|
|
60
|
+
publish-github:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
needs: [build-python, build-rootfs]
|
|
63
|
+
permissions:
|
|
64
|
+
contents: write
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/download-artifact@v4
|
|
67
|
+
with:
|
|
68
|
+
name: dist
|
|
69
|
+
path: dist/
|
|
70
|
+
|
|
71
|
+
- uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: rootfs
|
|
74
|
+
|
|
75
|
+
- uses: softprops/action-gh-release@v2
|
|
76
|
+
with:
|
|
77
|
+
files: |
|
|
78
|
+
dist/*
|
|
79
|
+
rootfs.ext4.gz
|
|
@@ -4,24 +4,40 @@ sidebarTitle: "Provision"
|
|
|
4
4
|
description: "Set up a c5.metal EC2 instance with Firecracker, KVM, and all dependencies."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Operator deploy
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
If you're an operator deploying Nightshift for your team, use the single-command deploy script. It provisions a c5.metal, pulls everything from PyPI and GitHub Releases, and starts serving — no repo checkout needed.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
```bash
|
|
12
|
+
./infra/deploy.sh --hostname api.example.com --api-key ns_abc123
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Optional flags:
|
|
16
|
+
- `--region <region>` — AWS region (default: us-east-1)
|
|
17
|
+
- `--version <tag>` — pin to a specific release (default: latest)
|
|
18
|
+
- `--port <port>` — backend port (default: 3000)
|
|
19
|
+
|
|
20
|
+
The script allocates an Elastic IP and prints DNS instructions at the end. Point your hostname's A record at the Elastic IP, and Caddy provisions TLS automatically.
|
|
21
|
+
|
|
22
|
+
To upgrade an existing deployment:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
./infra/upgrade.sh ubuntu@<ELASTIC_IP> --key ~/.ssh/nightshift.pem
|
|
26
|
+
./infra/upgrade.sh --version v0.3.0 # reads target from state file
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Dev environment setup
|
|
13
30
|
|
|
14
|
-
|
|
31
|
+
For development (requires a local repo checkout):
|
|
15
32
|
|
|
16
33
|
```bash
|
|
17
|
-
|
|
18
|
-
bash setup.sh
|
|
34
|
+
./infra/dev/setup.sh
|
|
19
35
|
```
|
|
20
36
|
|
|
21
37
|
For a production deployment with TLS in one step:
|
|
22
38
|
|
|
23
39
|
```bash
|
|
24
|
-
|
|
40
|
+
./infra/dev/setup.sh --production --hostname api.example.com
|
|
25
41
|
```
|
|
26
42
|
|
|
27
43
|
This opens ports 80/443, provisions the instance, and runs `production.sh` on the remote machine to install Caddy + systemd services. See [Server](/deployment/server) for details.
|
|
@@ -55,7 +71,7 @@ This opens ports 80/443, provisions the instance, and runs `production.sh` on th
|
|
|
55
71
|
Polls SSH and waits for `/opt/nightshift/.setup-done` marker.
|
|
56
72
|
</Step>
|
|
57
73
|
<Step title="Write connection details">
|
|
58
|
-
Writes connection details to `infra/.instance`.
|
|
74
|
+
Writes connection details to `infra/dev/.instance`.
|
|
59
75
|
</Step>
|
|
60
76
|
</Steps>
|
|
61
77
|
|
|
@@ -70,8 +86,7 @@ ssh -i ~/.ssh/nightshift-dev.pem ubuntu@<IP> 'tail -50 /var/log/nightshift-setup
|
|
|
70
86
|
## Tear down
|
|
71
87
|
|
|
72
88
|
```bash
|
|
73
|
-
|
|
74
|
-
bash teardown.sh
|
|
89
|
+
./infra/dev/teardown.sh
|
|
75
90
|
```
|
|
76
91
|
|
|
77
|
-
Terminates the instance and removes `infra/.instance`. The SSH keypair and security group are preserved for next time.
|
|
92
|
+
Terminates the instance and removes `infra/dev/.instance`. The SSH keypair and security group are preserved for next time.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""OpenAI agent — simple streaming assistant.
|
|
2
|
+
|
|
3
|
+
Demonstrates Nightshift with the OpenAI Agents SDK.
|
|
4
|
+
Uses forward_env to pass the API key from the server, or --env from the CLI.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
nightshift deploy examples/oai_agent.py
|
|
8
|
+
nightshift run oai_agent -p "hello" -f --env OPENAI_API_KEY=$OPENAI_API_KEY
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from agents import Agent, Runner
|
|
12
|
+
from nightshift import NightshiftApp, AgentConfig
|
|
13
|
+
|
|
14
|
+
app = NightshiftApp()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.agent(AgentConfig(workspace="", forward_env=["OPENAI_API_KEY"], max_concurrent_vms=3))
|
|
18
|
+
async def oai_agent(prompt: str):
|
|
19
|
+
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
|
20
|
+
result = Runner.run_streamed(agent, prompt)
|
|
21
|
+
async for event in result.stream_events():
|
|
22
|
+
yield event
|
|
@@ -76,53 +76,13 @@ echo " protocol/ → packaging.py, events.py"
|
|
|
76
76
|
# Replace systemd with a minimal shell init. Systemd is too heavy for
|
|
77
77
|
# Firecracker microVMs — it can panic or hang during boot. Our init
|
|
78
78
|
# sets up the essentials (filesystems, network, env) and starts the agent.
|
|
79
|
+
#
|
|
80
|
+
# The init script lives in rootfs/init (single source of truth).
|
|
79
81
|
echo "==> Installing init script"
|
|
80
82
|
# /sbin/init is a symlink to ../lib/systemd/systemd — remove it first
|
|
81
83
|
# so we don't overwrite the systemd binary.
|
|
82
84
|
rm -f "$MOUNT_DIR/sbin/init"
|
|
83
|
-
|
|
84
|
-
#!/bin/sh
|
|
85
|
-
#
|
|
86
|
-
# Nightshift VM init — PID 1 inside the Firecracker microVM.
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
# Mount essential filesystems (ignore errors if kernel already mounted them)
|
|
90
|
-
mount -t proc proc /proc 2>/dev/null
|
|
91
|
-
mount -t sysfs sysfs /sys 2>/dev/null
|
|
92
|
-
mount -t devtmpfs devtmpfs /dev 2>/dev/null
|
|
93
|
-
mount -t tmpfs tmpfs /tmp 2>/dev/null
|
|
94
|
-
mount -t tmpfs tmpfs /run 2>/dev/null
|
|
95
|
-
|
|
96
|
-
# Configure network from kernel command line
|
|
97
|
-
# ip=GUEST::GATEWAY:MASK::eth0:off
|
|
98
|
-
GUEST_IP=$(cat /proc/cmdline | tr ' ' '\n' | grep '^ip=' | cut -d= -f2 | cut -d: -f1)
|
|
99
|
-
GATEWAY=$(cat /proc/cmdline | tr ' ' '\n' | grep '^ip=' | cut -d= -f2 | cut -d: -f3)
|
|
100
|
-
MASK=$(cat /proc/cmdline | tr ' ' '\n' | grep '^ip=' | cut -d= -f2 | cut -d: -f4)
|
|
101
|
-
|
|
102
|
-
if [ -n "$GUEST_IP" ]; then
|
|
103
|
-
ip addr add "${GUEST_IP}/${MASK}" dev eth0 2>/dev/null
|
|
104
|
-
ip link set eth0 up 2>/dev/null
|
|
105
|
-
ip route add default via "$GATEWAY" 2>/dev/null
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# DNS
|
|
109
|
-
echo "nameserver 8.8.8.8" > /etc/resolv.conf
|
|
110
|
-
echo "nameserver 8.8.4.4" >> /etc/resolv.conf
|
|
111
|
-
|
|
112
|
-
# Load environment
|
|
113
|
-
if [ -f /etc/nightshift/env ]; then
|
|
114
|
-
set -a
|
|
115
|
-
. /etc/nightshift/env
|
|
116
|
-
set +a
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
export PATH="/root/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
|
120
|
-
export PYTHONPATH="/opt"
|
|
121
|
-
|
|
122
|
-
# Start the agent
|
|
123
|
-
cd /workspace
|
|
124
|
-
exec python3 -m nightshift.agent
|
|
125
|
-
INIT
|
|
85
|
+
cp "$PROJECT_DIR/rootfs/init" "$MOUNT_DIR/sbin/init"
|
|
126
86
|
chmod +x "$MOUNT_DIR/sbin/init"
|
|
127
87
|
|
|
128
88
|
# ── Required directories ─────────────────────────────────────
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Deploy Nightshift for operators.
|
|
4
|
+
#
|
|
5
|
+
# Provisions a c5.metal EC2 instance and bootstraps a production-ready
|
|
6
|
+
# Nightshift server. Everything is pulled from PyPI (nightshift-sdk) and
|
|
7
|
+
# GitHub Releases (rootfs.ext4.gz) — no local repo checkout needed.
|
|
8
|
+
#
|
|
9
|
+
# Prerequisites:
|
|
10
|
+
# - AWS CLI configured with EC2/VPC permissions
|
|
11
|
+
# - A hostname with DNS pointing to the Elastic IP (printed at the end)
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# ./infra/deploy.sh --hostname api.example.com --api-key ns_abc123
|
|
15
|
+
# ./infra/deploy.sh --hostname api.example.com --api-key ns_abc123 --region us-west-2
|
|
16
|
+
# ./infra/deploy.sh --hostname api.example.com --api-key ns_abc123 --version v0.2.0
|
|
17
|
+
#
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
STATE_FILE="$SCRIPT_DIR/.deploy-state"
|
|
22
|
+
|
|
23
|
+
INSTANCE_TYPE="c5.metal"
|
|
24
|
+
KEY_NAME="nightshift"
|
|
25
|
+
SG_NAME="nightshift"
|
|
26
|
+
REGION="${AWS_DEFAULT_REGION:-us-east-1}"
|
|
27
|
+
HOSTNAME=""
|
|
28
|
+
API_KEY=""
|
|
29
|
+
PORT=3000
|
|
30
|
+
VERSION=""
|
|
31
|
+
GITHUB_REPO="nightshiftco/nightshift"
|
|
32
|
+
|
|
33
|
+
# Parse arguments
|
|
34
|
+
while [[ $# -gt 0 ]]; do
|
|
35
|
+
case $1 in
|
|
36
|
+
--hostname) HOSTNAME="$2"; shift 2 ;;
|
|
37
|
+
--api-key) API_KEY="$2"; shift 2 ;;
|
|
38
|
+
--region) REGION="$2"; shift 2 ;;
|
|
39
|
+
--version) VERSION="$2"; shift 2 ;;
|
|
40
|
+
--port) PORT="$2"; shift 2 ;;
|
|
41
|
+
*)
|
|
42
|
+
echo "Unknown option: $1"
|
|
43
|
+
echo "Usage: $0 --hostname <FQDN> --api-key <key> [--region <region>] [--version <tag>] [--port <port>]"
|
|
44
|
+
exit 1
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
if [ -z "$HOSTNAME" ]; then
|
|
50
|
+
echo "Error: --hostname is required"
|
|
51
|
+
echo "Usage: $0 --hostname <FQDN> --api-key <key> [--region <region>] [--version <tag>] [--port <port>]"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [ -z "$API_KEY" ]; then
|
|
56
|
+
echo "Error: --api-key is required"
|
|
57
|
+
echo "Generate one with: python3 -c \"import secrets; print(f'ns_{secrets.token_hex(16)}')\""
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# If no version specified, fetch the latest release tag from GitHub.
|
|
62
|
+
if [ -z "$VERSION" ]; then
|
|
63
|
+
VERSION=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | jq -r '.tag_name')
|
|
64
|
+
if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
|
|
65
|
+
echo "Error: could not determine latest release from GitHub"
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo "==> Nightshift operator deploy"
|
|
71
|
+
echo " Hostname: $HOSTNAME"
|
|
72
|
+
echo " Version: $VERSION"
|
|
73
|
+
echo " Region: $REGION"
|
|
74
|
+
echo " Port: $PORT"
|
|
75
|
+
echo ""
|
|
76
|
+
|
|
77
|
+
# SSH key pair
|
|
78
|
+
if ! aws ec2 describe-key-pairs --key-names "$KEY_NAME" --region "$REGION" &>/dev/null; then
|
|
79
|
+
echo "==> Creating key pair: $KEY_NAME"
|
|
80
|
+
aws ec2 create-key-pair \
|
|
81
|
+
--key-name "$KEY_NAME" \
|
|
82
|
+
--region "$REGION" \
|
|
83
|
+
--query 'KeyMaterial' \
|
|
84
|
+
--output text > ~/.ssh/${KEY_NAME}.pem
|
|
85
|
+
chmod 400 ~/.ssh/${KEY_NAME}.pem
|
|
86
|
+
echo " Saved to ~/.ssh/${KEY_NAME}.pem"
|
|
87
|
+
else
|
|
88
|
+
echo "==> Key pair '$KEY_NAME' already exists in AWS"
|
|
89
|
+
if [ ! -f ~/.ssh/${KEY_NAME}.pem ]; then
|
|
90
|
+
echo " ERROR: Private key not found at ~/.ssh/${KEY_NAME}.pem"
|
|
91
|
+
echo " The key pair exists in AWS but the local private key is missing."
|
|
92
|
+
echo " Fix: delete the key pair in AWS and re-run this script."
|
|
93
|
+
echo " aws ec2 delete-key-pair --key-name $KEY_NAME --region $REGION"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Security group
|
|
99
|
+
SG_ID=$(aws ec2 describe-security-groups \
|
|
100
|
+
--filters "Name=group-name,Values=$SG_NAME" \
|
|
101
|
+
--region "$REGION" \
|
|
102
|
+
--query 'SecurityGroups[0].GroupId' \
|
|
103
|
+
--output text 2>/dev/null || echo "None")
|
|
104
|
+
|
|
105
|
+
if [ "$SG_ID" = "None" ] || [ -z "$SG_ID" ]; then
|
|
106
|
+
echo "==> Creating security group: $SG_NAME"
|
|
107
|
+
SG_ID=$(aws ec2 create-security-group \
|
|
108
|
+
--group-name "$SG_NAME" \
|
|
109
|
+
--description "Nightshift production - SSH, HTTP, HTTPS" \
|
|
110
|
+
--region "$REGION" \
|
|
111
|
+
--query 'GroupId' \
|
|
112
|
+
--output text)
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
MY_IP=$(curl -s https://checkip.amazonaws.com)
|
|
116
|
+
echo "==> Updating security group rules"
|
|
117
|
+
|
|
118
|
+
# Remove stale SSH rules
|
|
119
|
+
OLD_RULES=$(aws ec2 describe-security-groups \
|
|
120
|
+
--group-ids "$SG_ID" \
|
|
121
|
+
--region "$REGION" \
|
|
122
|
+
--query 'SecurityGroups[0].IpPermissions[?FromPort==`22`].IpRanges[].CidrIp' \
|
|
123
|
+
--output text 2>/dev/null || echo "")
|
|
124
|
+
for cidr in $OLD_RULES; do
|
|
125
|
+
aws ec2 revoke-security-group-ingress \
|
|
126
|
+
--group-id "$SG_ID" \
|
|
127
|
+
--protocol tcp --port 22 --cidr "$cidr" \
|
|
128
|
+
--region "$REGION" &>/dev/null || true
|
|
129
|
+
done
|
|
130
|
+
|
|
131
|
+
# SSH from operator IP + HTTP/HTTPS from anywhere
|
|
132
|
+
aws ec2 authorize-security-group-ingress \
|
|
133
|
+
--group-id "$SG_ID" \
|
|
134
|
+
--protocol tcp --port 22 --cidr "${MY_IP}/32" \
|
|
135
|
+
--region "$REGION" &>/dev/null || true
|
|
136
|
+
aws ec2 authorize-security-group-ingress \
|
|
137
|
+
--group-id "$SG_ID" \
|
|
138
|
+
--protocol tcp --port 80 --cidr "0.0.0.0/0" \
|
|
139
|
+
--region "$REGION" &>/dev/null || true
|
|
140
|
+
aws ec2 authorize-security-group-ingress \
|
|
141
|
+
--group-id "$SG_ID" \
|
|
142
|
+
--protocol tcp --port 443 --cidr "0.0.0.0/0" \
|
|
143
|
+
--region "$REGION" &>/dev/null || true
|
|
144
|
+
echo " Security group: $SG_ID (SSH: ${MY_IP}/32, HTTP/HTTPS: 0.0.0.0/0)"
|
|
145
|
+
|
|
146
|
+
# Find latest Ubuntu 22.04 AMI
|
|
147
|
+
AMI_ID=$(aws ec2 describe-images \
|
|
148
|
+
--owners 099720109477 \
|
|
149
|
+
--filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" \
|
|
150
|
+
"Name=state,Values=available" \
|
|
151
|
+
--region "$REGION" \
|
|
152
|
+
--query 'sort_by(Images, &CreationDate)[-1].ImageId' \
|
|
153
|
+
--output text)
|
|
154
|
+
echo "==> AMI: $AMI_ID (Ubuntu 22.04 latest)"
|
|
155
|
+
|
|
156
|
+
# Build user-data bootstrap script
|
|
157
|
+
# Variable substitution: VERSION, PORT, API_KEY, HOSTNAME, GITHUB_REPO
|
|
158
|
+
# are injected into the heredoc. The rest of the script runs as root on
|
|
159
|
+
# first boot.
|
|
160
|
+
|
|
161
|
+
USERDATA=$(cat << USERDATA_EOF
|
|
162
|
+
#!/bin/bash
|
|
163
|
+
set -euxo pipefail
|
|
164
|
+
exec > >(tee /var/log/nightshift-setup.log) 2>&1
|
|
165
|
+
|
|
166
|
+
# ── System packages ──────────────────────────────────────────────────
|
|
167
|
+
apt-get update
|
|
168
|
+
apt-get upgrade -y
|
|
169
|
+
apt-get install -y curl wget jq acl iptables iproute2 rsync nodejs npm
|
|
170
|
+
|
|
171
|
+
# We install claude code for convenience on the server
|
|
172
|
+
echo "==> Installing claude CLI..."
|
|
173
|
+
npm install -g @anthropic-ai/claude-code
|
|
174
|
+
|
|
175
|
+
# ── KVM access ───────────────────────────────────────────────────────
|
|
176
|
+
modprobe kvm
|
|
177
|
+
modprobe kvm_intel 2>/dev/null || modprobe kvm_amd 2>/dev/null || true
|
|
178
|
+
chmod 666 /dev/kvm
|
|
179
|
+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666"' > /etc/udev/rules.d/99-kvm.rules
|
|
180
|
+
|
|
181
|
+
# ── IP forwarding ────────────────────────────────────────────────────
|
|
182
|
+
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-firecracker.conf
|
|
183
|
+
sysctl -p /etc/sysctl.d/99-firecracker.conf
|
|
184
|
+
|
|
185
|
+
# ── uv (system-wide to /root/.local/bin) ─────────────────────────────
|
|
186
|
+
curl -LsSf https://astral.sh/uv/install.sh | bash
|
|
187
|
+
export PATH="/root/.local/bin:\$PATH"
|
|
188
|
+
|
|
189
|
+
# ── Firecracker + jailer ─────────────────────────────────────────────
|
|
190
|
+
ARCH=\$(uname -m)
|
|
191
|
+
FC_VERSION=\$(curl -s https://api.github.com/repos/firecracker-microvm/firecracker/releases/latest | jq -r '.tag_name')
|
|
192
|
+
cd /tmp
|
|
193
|
+
curl -LO "https://github.com/firecracker-microvm/firecracker/releases/download/\${FC_VERSION}/firecracker-\${FC_VERSION}-\${ARCH}.tgz"
|
|
194
|
+
tar xzf "firecracker-\${FC_VERSION}-\${ARCH}.tgz"
|
|
195
|
+
mv "release-\${FC_VERSION}-\${ARCH}/firecracker-\${FC_VERSION}-\${ARCH}" /usr/local/bin/firecracker
|
|
196
|
+
mv "release-\${FC_VERSION}-\${ARCH}/jailer-\${FC_VERSION}-\${ARCH}" /usr/local/bin/jailer
|
|
197
|
+
chmod +x /usr/local/bin/firecracker /usr/local/bin/jailer
|
|
198
|
+
|
|
199
|
+
# ── Kernel (vmlinux) from Firecracker CI S3 bucket ───────────────────
|
|
200
|
+
mkdir -p /opt/nightshift
|
|
201
|
+
cd /opt/nightshift
|
|
202
|
+
|
|
203
|
+
CI_VERSION=\$(echo "\$FC_VERSION" | sed 's/\.[0-9]*\$//')
|
|
204
|
+
latest_kernel_key=\$(curl -s "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/\${CI_VERSION}/\${ARCH}/vmlinux-&list-type=2" \
|
|
205
|
+
| grep -oP "(?<=<Key>)(firecracker-ci/\${CI_VERSION}/\${ARCH}/vmlinux-[0-9]+\.[0-9]+\.[0-9]{1,3})(?=</Key>)" \
|
|
206
|
+
| sort -V | tail -1)
|
|
207
|
+
wget -q -O vmlinux "https://s3.amazonaws.com/spec.ccfc.min/\${latest_kernel_key}"
|
|
208
|
+
|
|
209
|
+
# ── rootfs from GitHub Release ───────────────────────────────────────
|
|
210
|
+
curl -L -o rootfs.ext4.gz "https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/rootfs.ext4.gz"
|
|
211
|
+
gunzip rootfs.ext4.gz
|
|
212
|
+
|
|
213
|
+
# ── Install nightshift from PyPI via uvx ─────────────────────────────
|
|
214
|
+
# uvx installs and runs CLI tools from PyPI in isolated environments.
|
|
215
|
+
# Pin to the release version so operator knows exactly what's running.
|
|
216
|
+
SDK_VERSION=\$(echo "${VERSION}" | sed 's/^v//')
|
|
217
|
+
uvx --from "nightshift-sdk==\${SDK_VERSION}" nightshift --help
|
|
218
|
+
|
|
219
|
+
# ── Caddy (auto-TLS reverse proxy) ──────────────────────────────────
|
|
220
|
+
apt-get install -y debian-keyring debian-archive-keyring apt-transport-https
|
|
221
|
+
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
|
|
222
|
+
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
|
223
|
+
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
|
|
224
|
+
| tee /etc/apt/sources.list.d/caddy-stable.list
|
|
225
|
+
apt-get update -y
|
|
226
|
+
apt-get install -y caddy
|
|
227
|
+
|
|
228
|
+
cat > /etc/caddy/Caddyfile << 'CADDY'
|
|
229
|
+
${HOSTNAME} {
|
|
230
|
+
reverse_proxy localhost:${PORT}
|
|
231
|
+
}
|
|
232
|
+
CADDY
|
|
233
|
+
|
|
234
|
+
# ── systemd service for nightshift serve ─────────────────────────────
|
|
235
|
+
cat > /etc/systemd/system/nightshift-serve.service << 'SYSTEMD'
|
|
236
|
+
[Unit]
|
|
237
|
+
Description=Nightshift Platform Server
|
|
238
|
+
After=network.target
|
|
239
|
+
|
|
240
|
+
[Service]
|
|
241
|
+
Type=simple
|
|
242
|
+
User=root
|
|
243
|
+
ExecStart=/root/.local/bin/uvx --from nightshift-sdk nightshift serve --port ${PORT}
|
|
244
|
+
Restart=on-failure
|
|
245
|
+
RestartSec=5
|
|
246
|
+
Environment=NIGHTSHIFT_API_KEY=${API_KEY}
|
|
247
|
+
|
|
248
|
+
[Install]
|
|
249
|
+
WantedBy=multi-user.target
|
|
250
|
+
SYSTEMD
|
|
251
|
+
|
|
252
|
+
# ── Enable and start services ────────────────────────────────────────
|
|
253
|
+
systemctl daemon-reload
|
|
254
|
+
systemctl enable --now nightshift-serve
|
|
255
|
+
systemctl enable caddy
|
|
256
|
+
systemctl reload caddy
|
|
257
|
+
|
|
258
|
+
# ── Done marker ──────────────────────────────────────────────────────
|
|
259
|
+
echo "setup_complete \$(date -u +%Y-%m-%dT%H:%M:%SZ)" > /opt/nightshift/.setup-done
|
|
260
|
+
USERDATA_EOF
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Launch instance
|
|
264
|
+
echo "==> Launching $INSTANCE_TYPE instance..."
|
|
265
|
+
INSTANCE_ID=$(aws ec2 run-instances \
|
|
266
|
+
--image-id "$AMI_ID" \
|
|
267
|
+
--instance-type "$INSTANCE_TYPE" \
|
|
268
|
+
--key-name "$KEY_NAME" \
|
|
269
|
+
--security-group-ids "$SG_ID" \
|
|
270
|
+
--region "$REGION" \
|
|
271
|
+
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":100,"VolumeType":"gp3","DeleteOnTermination":true}}]' \
|
|
272
|
+
--user-data "$USERDATA" \
|
|
273
|
+
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=nightshift},{Key=Project,Value=nightshift},{Key=Version,Value=$VERSION}]" \
|
|
274
|
+
--query 'Instances[0].InstanceId' \
|
|
275
|
+
--output text)
|
|
276
|
+
echo " Instance ID: $INSTANCE_ID"
|
|
277
|
+
|
|
278
|
+
echo "==> Waiting for instance to enter 'running' state..."
|
|
279
|
+
aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" --region "$REGION"
|
|
280
|
+
|
|
281
|
+
# Allocate and associate Elastic IP
|
|
282
|
+
echo "==> Allocating Elastic IP..."
|
|
283
|
+
ALLOC_ID=$(aws ec2 allocate-address \
|
|
284
|
+
--domain vpc \
|
|
285
|
+
--region "$REGION" \
|
|
286
|
+
--tag-specifications "ResourceType=elastic-ip,Tags=[{Key=Name,Value=nightshift},{Key=Project,Value=nightshift}]" \
|
|
287
|
+
--query 'AllocationId' \
|
|
288
|
+
--output text)
|
|
289
|
+
|
|
290
|
+
ELASTIC_IP=$(aws ec2 describe-addresses \
|
|
291
|
+
--allocation-ids "$ALLOC_ID" \
|
|
292
|
+
--region "$REGION" \
|
|
293
|
+
--query 'Addresses[0].PublicIp' \
|
|
294
|
+
--output text)
|
|
295
|
+
|
|
296
|
+
aws ec2 associate-address \
|
|
297
|
+
--instance-id "$INSTANCE_ID" \
|
|
298
|
+
--allocation-id "$ALLOC_ID" \
|
|
299
|
+
--region "$REGION" \
|
|
300
|
+
--output text > /dev/null
|
|
301
|
+
echo " Elastic IP: $ELASTIC_IP"
|
|
302
|
+
|
|
303
|
+
# state
|
|
304
|
+
cat > "$STATE_FILE" << EOF
|
|
305
|
+
INSTANCE_ID=$INSTANCE_ID
|
|
306
|
+
ELASTIC_IP=$ELASTIC_IP
|
|
307
|
+
ALLOC_ID=$ALLOC_ID
|
|
308
|
+
KEY_PATH=~/.ssh/${KEY_NAME}.pem
|
|
309
|
+
REGION=$REGION
|
|
310
|
+
HOSTNAME=$HOSTNAME
|
|
311
|
+
VERSION=$VERSION
|
|
312
|
+
EOF
|
|
313
|
+
echo " State saved to $STATE_FILE"
|
|
314
|
+
|
|
315
|
+
# Wait for SSH
|
|
316
|
+
echo "==> Waiting for SSH..."
|
|
317
|
+
for i in $(seq 1 60); do
|
|
318
|
+
if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes \
|
|
319
|
+
-i ~/.ssh/${KEY_NAME}.pem ubuntu@${ELASTIC_IP} "echo ok" &>/dev/null; then
|
|
320
|
+
echo " SSH is up"
|
|
321
|
+
break
|
|
322
|
+
fi
|
|
323
|
+
if [ "$i" -eq 60 ]; then
|
|
324
|
+
echo " ERROR: SSH did not come up after 10 minutes"
|
|
325
|
+
exit 1
|
|
326
|
+
fi
|
|
327
|
+
sleep 10
|
|
328
|
+
done
|
|
329
|
+
|
|
330
|
+
# Wait for user-data setup to complete
|
|
331
|
+
echo "==> Waiting for instance setup to complete..."
|
|
332
|
+
echo " (installing firecracker, kernel, rootfs, nightshift, caddy)"
|
|
333
|
+
for i in $(seq 1 90); do
|
|
334
|
+
DONE=$(ssh -o StrictHostKeyChecking=no -i ~/.ssh/${KEY_NAME}.pem \
|
|
335
|
+
ubuntu@${ELASTIC_IP} "cat /opt/nightshift/.setup-done 2>/dev/null" 2>/dev/null || echo "")
|
|
336
|
+
if [ -n "$DONE" ]; then
|
|
337
|
+
echo " $DONE"
|
|
338
|
+
break
|
|
339
|
+
fi
|
|
340
|
+
if [ "$i" -eq 90 ]; then
|
|
341
|
+
echo " ERROR: Setup did not complete after 15 minutes"
|
|
342
|
+
echo " Debug: ssh -i ~/.ssh/${KEY_NAME}.pem ubuntu@${ELASTIC_IP} 'tail -50 /var/log/nightshift-setup.log'"
|
|
343
|
+
exit 1
|
|
344
|
+
fi
|
|
345
|
+
sleep 10
|
|
346
|
+
done
|
|
347
|
+
|
|
348
|
+
# Print connection info
|
|
349
|
+
echo ""
|
|
350
|
+
echo "=== Nightshift deployment complete ==="
|
|
351
|
+
echo ""
|
|
352
|
+
echo " Elastic IP: $ELASTIC_IP"
|
|
353
|
+
echo " Server: https://$HOSTNAME"
|
|
354
|
+
echo " Health: https://$HOSTNAME/health"
|
|
355
|
+
echo " SSH: ssh -i ~/.ssh/${KEY_NAME}.pem ubuntu@${ELASTIC_IP}"
|
|
356
|
+
echo " Version: $VERSION"
|
|
357
|
+
echo ""
|
|
358
|
+
echo " DNS: Create an A record pointing $HOSTNAME to $ELASTIC_IP"
|
|
359
|
+
echo " Caddy will automatically provision TLS once DNS propagates."
|
|
360
|
+
echo ""
|
|
361
|
+
echo " Logs: ssh -i ~/.ssh/${KEY_NAME}.pem ubuntu@${ELASTIC_IP} 'sudo journalctl -u nightshift-serve -f'"
|
|
362
|
+
echo " Upgrade: ./infra/upgrade.sh ubuntu@${ELASTIC_IP} --key ~/.ssh/${KEY_NAME}.pem"
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
# - EventBridge Schedules: start 8am ET Mon-Fri, stop midnight ET Tue-Sat
|
|
11
11
|
#
|
|
12
12
|
# Prerequisites:
|
|
13
|
-
# - infra/.instance must exist (run setup.sh first)
|
|
13
|
+
# - infra/dev/.instance must exist (run setup.sh first)
|
|
14
14
|
# - AWS CLI configured with appropriate permissions
|
|
15
15
|
#
|
|
16
|
-
# Usage: ./infra/cron.sh
|
|
16
|
+
# Usage: ./infra/dev/cron.sh
|
|
17
17
|
#
|
|
18
18
|
set -euo pipefail
|
|
19
19
|
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
# next run cold-starts with the new code.
|
|
8
8
|
#
|
|
9
9
|
# Usage:
|
|
10
|
-
# ./infra/deploy.sh # uses IP from .instance
|
|
11
|
-
# ./infra/deploy.sh ubuntu@100.49.58.14 # explicit host
|
|
12
|
-
# ./infra/deploy.sh --key ~/.ssh/my.pem ubuntu@host
|
|
10
|
+
# ./infra/dev/deploy.sh # uses IP from .instance
|
|
11
|
+
# ./infra/dev/deploy.sh ubuntu@100.49.58.14 # explicit host
|
|
12
|
+
# ./infra/dev/deploy.sh --key ~/.ssh/my.pem ubuntu@host
|
|
13
13
|
#
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
16
16
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
17
|
+
PROJECT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
18
18
|
STATE_FILE="$SCRIPT_DIR/.instance"
|
|
19
19
|
|
|
20
20
|
SSH_KEY=""
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
# It does NOT handle DNS; point your hostname at this machine first.
|
|
8
8
|
#
|
|
9
9
|
# Usage:
|
|
10
|
-
# ./infra/production.sh --hostname api.nightshift.sh
|
|
11
|
-
# ./infra/production.sh --hostname api.nightshift.sh --port 8080
|
|
12
|
-
# ./infra/production.sh --hostname api.nightshift.sh --api-key my-secret-key
|
|
10
|
+
# ./infra/dev/production.sh --hostname api.nightshift.sh
|
|
11
|
+
# ./infra/dev/production.sh --hostname api.nightshift.sh --port 8080
|
|
12
|
+
# ./infra/dev/production.sh --hostname api.nightshift.sh --api-key my-secret-key
|
|
13
13
|
#
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
@@ -75,7 +75,7 @@ fi
|
|
|
75
75
|
# Default to the directory containing this script's parent, but allow
|
|
76
76
|
# override via NIGHTSHIFT_PROJECT_DIR.
|
|
77
77
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
78
|
-
PROJECT_DIR="${NIGHTSHIFT_PROJECT_DIR:-$(dirname "$SCRIPT_DIR")}"
|
|
78
|
+
PROJECT_DIR="${NIGHTSHIFT_PROJECT_DIR:-$(dirname "$(dirname "$SCRIPT_DIR")")}"
|
|
79
79
|
|
|
80
80
|
# Verify uv is available
|
|
81
81
|
UV_PATH="$(command -v uv 2>/dev/null || echo "$HOME/.local/bin/uv")"
|
|
@@ -129,7 +129,7 @@ echo " Created /etc/caddy/Caddyfile"
|
|
|
129
129
|
# 5. Bake agent runtime into rootfs
|
|
130
130
|
# -------------------------------------------------------------------
|
|
131
131
|
echo "==> Baking agent runtime into rootfs"
|
|
132
|
-
sudo "$SCRIPT_DIR
|
|
132
|
+
sudo "$SCRIPT_DIR/../bake-rootfs.sh"
|
|
133
133
|
|
|
134
134
|
# -------------------------------------------------------------------
|
|
135
135
|
# 6. Enable and start services
|