Compare commits
1
Commits
a0253e118f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef20c8c54c |
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
**/node_modules
|
||||||
|
**/.env*
|
||||||
|
**/config.yaml
|
||||||
|
**/data
|
||||||
|
**/*.bundle
|
||||||
|
.claude
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Local development agreement
|
||||||
|
|
||||||
|
- Source lives locally under ~/projects/fzc-src/cube; fzc is the deployment target, not an interactive development workspace.
|
||||||
|
- Use the root Makefile for routine deployment. Preserve existing data, credentials, ingress, service names and schedule.
|
||||||
|
- Never bulk-deploy, trigger real notifications, or import production data merely to verify a migration.
|
||||||
|
- Original unrelated dirty files belong to the user; preserve them and review before committing/publishing.
|
||||||
|
- Do not commit local config files, caches, login images, backup credentials or runtime records.
|
||||||
|
- Read README.md for the exact deployment boundary. Other projects/hosts are outside this repository's scope.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
.SHELLFLAGS := -eu -o pipefail -c
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
DEPLOY_HOST ?= fzc
|
||||||
|
APP ?= cube
|
||||||
|
APPS := cube articulate karaoke notes simpleasm video2slides webgl werewolf llm-proxy
|
||||||
|
NAMESPACE = $(if $(filter llm-proxy,$(APP)),llm-proxy,cube-$(APP))
|
||||||
|
RELEASE_TAG := $(shell git rev-parse --short=12 HEAD)-$(shell date -u +%Y%m%d%H%M%S)
|
||||||
|
IMAGE ?= cube-local-$(APP):$(RELEASE_TAG)
|
||||||
|
.PHONY: help guard build deploy deploy-write
|
||||||
|
help:
|
||||||
|
@echo 'make deploy APP=<$(APPS)>; make deploy-write'
|
||||||
|
guard:
|
||||||
|
@case ' $(APPS) ' in *' $(APP) '*) ;; *) echo 'Unknown/decommissioned app'; exit 1;; esac
|
||||||
|
@test -z "$$(git status --porcelain)" || { echo 'Review/commit source changes first'; exit 1; }
|
||||||
|
build: guard
|
||||||
|
cargo test -p $(APP)
|
||||||
|
CC_x86_64_unknown_linux_musl=musl-gcc cargo build --release --target x86_64-unknown-linux-musl -p $(APP)
|
||||||
|
@if [ -f apps/$(APP)/frontend/package.json ]; then cd apps/$(APP)/frontend && npm ci && npm run build; fi
|
||||||
|
docker build -f apps/$(APP)/Dockerfile -t $(IMAGE) .
|
||||||
|
deploy: guard
|
||||||
|
$(MAKE) build APP=$(APP) IMAGE=$(IMAGE)
|
||||||
|
docker save $(IMAGE) | ssh $(DEPLOY_HOST) 'sudo k3s ctr -n k8s.io images import -'
|
||||||
|
python3 scripts/release_remote.py --host $(DEPLOY_HOST) --namespace $(NAMESPACE) --deployment $(APP) --container $(APP) --image $(IMAGE)
|
||||||
|
deploy-write:
|
||||||
|
$(MAKE) -C apps/write deploy HOST=$(DEPLOY_HOST)
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
# cube
|
# cube
|
||||||
|
|
||||||
|
## 本地开发与发布(2026-09-21)
|
||||||
|
|
||||||
|
源码迁至本机 `~/projects/fzc-src/cube`。使用 `make deploy APP=<应用名>` 在本机测试/构建,再通过 SSH fzc 更新单个现有工作负载;`make deploy-write` 沿用 user systemd 方式。旧 CI 配置保留,不在这次迁移中批量重发各应用。
|
||||||
|
|
||||||
|
部署脚本已经做命令及语法检查;迁移本身不重启现有服务。
|
||||||
|
|
||||||
Fam 的小 app 平台。
|
Fam 的小 app 平台。
|
||||||
|
|
||||||
## 是什么
|
## 是什么
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
"""Local-built image release to an existing fzc workload; never provisions infrastructure."""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
def run(args, capture=False):
|
||||||
|
return subprocess.run(args, check=True, text=True, capture_output=capture).stdout
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser=argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--host', default='fzc')
|
||||||
|
parser.add_argument('--namespace', required=True)
|
||||||
|
parser.add_argument('--deployment', required=True)
|
||||||
|
parser.add_argument('--container', required=True)
|
||||||
|
parser.add_argument('--image', required=True)
|
||||||
|
parser.add_argument('--cron', action='append', default=[], help='name:container')
|
||||||
|
parser.add_argument('--health')
|
||||||
|
args=parser.parse_args()
|
||||||
|
def kube(*parts, capture=False):
|
||||||
|
return run(['ssh',args.host,shlex.join(['kubectl','-n',args.namespace,*parts])],capture)
|
||||||
|
targets=[('deployment',args.deployment,args.container)]+[('cronjob',*c.split(':')) for c in args.cron]
|
||||||
|
previous=[]
|
||||||
|
for kind,name,container in targets:
|
||||||
|
obj=json.loads(kube('get',kind,name,'-o','json',capture=True))
|
||||||
|
spec=obj['spec']['template']['spec'] if kind=='deployment' else obj['spec']['jobTemplate']['spec']['template']['spec']
|
||||||
|
image=next(c['image'] for c in spec['containers'] if c['name']==container)
|
||||||
|
previous.append((kind,name,container,image))
|
||||||
|
changed=[]
|
||||||
|
try:
|
||||||
|
for kind,name,container,image in previous:
|
||||||
|
changed.append((kind,name,container,image))
|
||||||
|
kube('set','image',kind+'/'+name,container+'='+args.image)
|
||||||
|
if kind=='deployment': kube('rollout','status',kind+'/'+name,'--timeout=180s')
|
||||||
|
if args.health:
|
||||||
|
with urllib.request.urlopen(args.health,timeout=20) as response:
|
||||||
|
assert response.status == 200
|
||||||
|
except Exception:
|
||||||
|
for kind,name,container,image in reversed(changed):
|
||||||
|
kube('set','image',kind+'/'+name,container+'='+image)
|
||||||
|
kube('rollout','status','deployment/'+args.deployment,'--timeout=180s')
|
||||||
|
raise
|
||||||
|
print('Released',args.image,'to',args.host,args.namespace, '; previous images:',previous)
|
||||||
|
|
||||||
|
if __name__ == '__main__': main()
|
||||||
Reference in New Issue
Block a user