Skip to content

full-stack-dev-workflow

背景

  • 如何轻量地快速搭建一个全栈化的开发环境?兼顾通用性?
  • AI 适配全栈开发工作流,如何兼顾成本和性能?

方案

基于 GitHub Codespace + OpenCode(Go 套餐 / 自备 DeepSeek API Key)来搭建通用的全栈开发环境。

OpenCode 终端配置脚本

文末提供了开箱即用的配置脚本,替换 API Key 后在终端直接运行即可。脚本会自动安装 OpenCode CLI、配置 Go 套餐和 DeepSeek API、启用 LSP 并授予最高权限(免去每次询问权限的步骤)。

整体架构


multica 项目全栈开发示例

1. Fork GitHub multica 项目,创建 Codespace:

2. 本地 VS Code 通过 Remote Explorer 远程连接 Codespace,执行配置脚本:

3. 启动 OpenCode,执行 prompt:

现在是在 github 的 codespace 中,帮我本地运行这个项目

4. OpenCode 输出启动成功,在 VS Code Codespace 中配置端口转发:

5. 浏览器访问:

6. 配置验证码登录:

opencode使用prompt: 在 .env 中设置 MULTICA_DEV_VERIFICATION_CODE=888888 使用固定验证码,然后重启后端。

用验证码登录报错后,又用opencode修复:

opencode显示修复成功:

浏览器访问,登录成功,进入 onboarding 页面:

7. 修改项目文案并验证:

opencode使用prompt: 现在已经登录成功,进入页面 /test1/issues 路由页面,帮我简单修改一个文案和增加一个 chrome 控制台能打印出的 log,看看是否生效了

8. 浏览器查看修改结果:

9. 查看所有运行中的项目进程:

opencode使用prompt: 帮我看看刚才总共帮我运行了哪些项目,列出各个项目的功能和状态

10. 清理: 不需要的 Codespace 直接在 GitHub Codespaces 或 VS Code 中删除即可。

开发流程总览


相关配置

OpenCode 终端配置脚本
sh
#!/usr/bin/env bash
set -euo pipefail

# ============================================================
# OpenCode Setup Script — safe to copy-paste into terminal
# ============================================================
# Usage: bash <(cat scripts/opencode-config-copy-to-run.sh)
#    Or:  curl -fsSL <url> | bash
#    Or:  just copy-paste the whole file into your terminal
#
# Uses heredocs (<<) instead of node -e "..." so that
# multi-line copy-paste into a terminal never breaks.
# ============================================================

# --- Configuration: Set your API keys and defaults here ---
OPENCODE_GO_API_KEY="sk-11uixq122pEm5TVP5vOqeIxsxEhxYzqAMLxHWzwDceedzCa5nvT8PniI9TkFJEIxjt85y"
DEEPSEEK_API_KEY="sk-a03be09222228a15742ff9f10fb83d7962bb5"
DEFAULT_MODEL="opencode-go/deepseek-v4-flash"
LSP_ENABLED="true"
PERMISSION="allow"

# --- Constants ---
AUTH_DIR="$HOME/.local/share/opencode"
AUTH_FILE="$AUTH_DIR/auth.json"
CONFIG_DIR="$HOME/.config/opencode"
CONFIG_FILE="$CONFIG_DIR/opencode.json"
NPM_PACKAGE="opencode-ai"

# --- Color output ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info()  { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }

# --- Pre-check: node/npm ---
if ! command -v npm &>/dev/null; then
  log_error "npm is required but not found. Please install Node.js first."
  exit 1
fi

# --- Step 1: Install opencode CLI if not present ---
log_info "Checking OpenCode CLI..."
if command -v opencode &>/dev/null; then
  log_info "OpenCode CLI already installed: $(opencode --version 2>/dev/null || echo 'ok')"
else
  log_info "Installing OpenCode CLI via npm..."
  npm install -g "$NPM_PACKAGE"
  log_info "OpenCode CLI installed successfully"
fi

# --- Step 2: Configure API keys ---
log_info "Configuring API keys..."
mkdir -p "$AUTH_DIR"

if [ -f "$AUTH_FILE" ]; then
  log_info "Existing auth.json found, merging keys..."
  node <<NODE_SCRIPT
const fs = require('fs');
const existing = JSON.parse(fs.readFileSync('$AUTH_FILE', 'utf8'));
const keysToAdd = {
  'opencode-go': { type: 'api', key: '$OPENCODE_GO_API_KEY' },
  'deepseek':     { type: 'api', key: '$DEEPSEEK_API_KEY' }
};
for (const [k, v] of Object.entries(keysToAdd)) {
  if (v.key && !v.key.includes('your-') && !v.key.includes('sk-your-')) {
    existing[k] = v;
  }
}
fs.writeFileSync('$AUTH_FILE', JSON.stringify(existing, null, 2) + '\n');
NODE_SCRIPT
else
  log_info "Creating fresh auth.json..."
  node <<NODE_SCRIPT
const fs = require('fs');
const auth = {};
const keysToAdd = {
  'opencode-go': { type: 'api', key: '$OPENCODE_GO_API_KEY' },
  'deepseek':     { type: 'api', key: '$DEEPSEEK_API_KEY' }
};
for (const [k, v] of Object.entries(keysToAdd)) {
  if (v.key && !v.key.includes('your-') && !v.key.includes('sk-your-')) {
    auth[k] = v;
  }
}
fs.writeFileSync('$AUTH_FILE', JSON.stringify(auth, null, 2) + '\n');
NODE_SCRIPT
fi

# --- Step 3: Configure default model ---
log_info "Configuring default model: $DEFAULT_MODEL..."
mkdir -p "$CONFIG_DIR"

if [ -f "$CONFIG_FILE" ]; then
  log_info "Existing opencode.json found, merging config..."
  node <<NODE_SCRIPT
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
config['\$schema'] = 'https://opencode.ai/config.json';
config.model = '$DEFAULT_MODEL';
config.lsp = '$LSP_ENABLED' === 'true';
config.permission = '$PERMISSION';
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2) + '\n');
NODE_SCRIPT
else
  log_info "Creating fresh opencode.json..."
  node <<NODE_SCRIPT
const fs = require('fs');
const config = {
  '\$schema': 'https://opencode.ai/config.json',
  model: '$DEFAULT_MODEL',
  lsp: '$LSP_ENABLED' === 'true',
  permission: '$PERMISSION'
};
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2) + '\n');
NODE_SCRIPT
fi

# --- Step 4: Install VS Code extension ---
log_info "Checking VS Code extension 'sst-dev.opencode'..."
if command -v code &>/dev/null; then
  if code --list-extensions 2>/dev/null | grep -q "sst-dev.opencode"; then
    log_info "VS Code extension 'sst-dev.opencode' already installed"
  else
    log_info "Installing VS Code extension 'sst-dev.opencode'..."
    code --install-extension sst-dev.opencode
    log_info "VS Code extension installed successfully"
  fi
else
  log_warn "'code' command not found. Skipping VS Code extension installation."
fi

# --- Step 5: Verify ---
log_info "Verifying configuration..."
if [ -f "$AUTH_FILE" ]; then
  log_info "auth.json written to: $AUTH_FILE"
  log_info "Configured providers:"
  node -e "const a=require('$AUTH_FILE');Object.keys(a).forEach(k=>console.log('  - '+k))"
else
  log_error "Failed to write auth.json"
  exit 1
fi

if [ -f "$CONFIG_FILE" ]; then
  log_info "opencode.json written to: $CONFIG_FILE"
  log_info "Default model: $(node -e "console.log(require('$CONFIG_FILE').model)")"
  log_info "LSP enabled: $(node -e "console.log(require('$CONFIG_FILE').lsp)")"
  log_info "Permission: $(node -e "console.log(require('$CONFIG_FILE').permission)")"
else
  log_error "Failed to write opencode.json"
  exit 1
fi

log_info "OpenCode setup complete!"
log_info "Run 'opencode' to start. Default model: $DEFAULT_MODEL"