From 60194306e125b626db9ae7ba1450395c5341bddb Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Thu, 1 Jan 2026 23:27:53 +0800 Subject: [PATCH] feat: add stable release branch support - Add release/stable branch to CI workflow - Create docker-compose.stable.yml with :stable tag - Create install-stable.sh for one-click deployment - Add stable tag creation in manifest step --- .github/workflows/docker-build.yml | 9 +++ docker-compose.stable.yml | 48 +++++++++++++ install-stable.sh | 106 +++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 docker-compose.stable.yml create mode 100755 install-stable.sh diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index eecdde65..59fca6eb 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -5,6 +5,7 @@ on: branches: - main - dev + - release/stable tags: - 'v*' pull_request: @@ -171,6 +172,14 @@ jobs: echo "✅ Created latest tag (main branch only)" fi + # release/stable branch gets the 'stable' tag + if [[ "${{ github.ref }}" == "refs/heads/release/stable" ]]; then + docker buildx imagetools create -t "${GHCR_IMAGE}:stable" \ + "${GHCR_IMAGE}:${REF_NAME}-amd64" \ + "${GHCR_IMAGE}:${REF_NAME}-arm64" + echo "✅ Created stable tag (release/stable branch)" + fi + if [[ -n "${{ secrets.DOCKERHUB_USERNAME }}" ]]; then DOCKERHUB_IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/nofx-${{ matrix.image_suffix }}" docker buildx imagetools create -t "${DOCKERHUB_IMAGE}:${REF_NAME}" \ diff --git a/docker-compose.stable.yml b/docker-compose.stable.yml new file mode 100644 index 00000000..46c083ab --- /dev/null +++ b/docker-compose.stable.yml @@ -0,0 +1,48 @@ +# NOFX Stable Release Deployment +# Production-ready stable version + +services: + nofx: + image: ghcr.io/nofxaios/nofx/nofx-backend:stable + container_name: nofx-trading + restart: unless-stopped + stop_grace_period: 30s + ports: + - "${NOFX_BACKEND_PORT:-8080}:8080" + volumes: + - ./data:/app/data + - /etc/localtime:/etc/localtime:ro + env_file: + - .env + environment: + - TZ=${TZ:-Asia/Shanghai} + - AI_MAX_TOKENS=8000 + networks: + - nofx-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + nofx-frontend: + image: ghcr.io/nofxaios/nofx/nofx-frontend:stable + container_name: nofx-frontend + restart: unless-stopped + ports: + - "${NOFX_FRONTEND_PORT:-3000}:80" + networks: + - nofx-network + depends_on: + - nofx + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + +networks: + nofx-network: + driver: bridge diff --git a/install-stable.sh b/install-stable.sh new file mode 100755 index 00000000..c1999442 --- /dev/null +++ b/install-stable.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# +# NOFX Stable Release Installation Script +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/NoFxAiOS/nofx/release/stable/install-stable.sh | bash +# + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +INSTALL_DIR="${1:-$HOME/nofx}" +COMPOSE_FILE="docker-compose.stable.yml" +GITHUB_RAW="https://raw.githubusercontent.com/NoFxAiOS/nofx/release/stable" + +echo -e "${BLUE}" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ NOFX Stable Release ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo -e "${NC}" + +check_docker() { + if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed.${NC}" + exit 1 + fi + if ! docker info &> /dev/null; then + echo -e "${RED}Error: Docker daemon is not running.${NC}" + exit 1 + fi + if docker compose version &> /dev/null; then + COMPOSE_CMD="docker compose" + elif command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" + else + echo -e "${RED}Error: Docker Compose is not available.${NC}" + exit 1 + fi + echo -e "${GREEN}✓ Docker ready${NC}" +} + +setup_directory() { + mkdir -p "$INSTALL_DIR" + cd "$INSTALL_DIR" + echo -e "${GREEN}✓ Directory: $INSTALL_DIR${NC}" +} + +download_files() { + curl -fsSL "$GITHUB_RAW/$COMPOSE_FILE" -o docker-compose.yml + echo -e "${GREEN}✓ Config downloaded${NC}" +} + +generate_env() { + if [ -f ".env" ]; then + echo -e "${GREEN}✓ .env exists${NC}" + return + fi + JWT_SECRET=$(openssl rand -base64 32) + DATA_ENCRYPTION_KEY=$(openssl rand -base64 32) + RSA_PRIVATE_KEY=$(openssl genrsa 2048 2>/dev/null | tr '\n' '\\' | sed 's/\\/\\n/g' | sed 's/\\n$//') + cat > .env << EOF +NOFX_BACKEND_PORT=8080 +NOFX_FRONTEND_PORT=3000 +TZ=Asia/Shanghai +JWT_SECRET=${JWT_SECRET} +DATA_ENCRYPTION_KEY=${DATA_ENCRYPTION_KEY} +RSA_PRIVATE_KEY=${RSA_PRIVATE_KEY} +EOF + echo -e "${GREEN}✓ Keys generated${NC}" +} + +start_services() { + $COMPOSE_CMD pull + $COMPOSE_CMD up -d + echo -e "${GREEN}✓ Services started${NC}" +} + +get_server_ip() { + local ip=$(curl -s --max-time 3 ifconfig.me 2>/dev/null || echo "") + echo "${ip:-127.0.0.1}" +} + +print_success() { + local IP=$(get_server_ip) + echo "" + echo -e "${GREEN}Installation Complete!${NC}" + echo -e " Web: http://${IP}:3000" + echo -e " API: http://${IP}:8080" + echo "" +} + +main() { + check_docker + setup_directory + download_files + generate_env + start_services + print_success +} + +main