first commit
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
name: Agent Build & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths:
|
||||
- 'agent/**'
|
||||
- 'server/templates/agent-install.sh'
|
||||
- 'server/src/routes/agent.ts'
|
||||
- 'server/src/lib/agent-auth.ts'
|
||||
- 'server/src/lib/host-agent-credentials.ts'
|
||||
- 'server/src/services/agent-instance-report.ts'
|
||||
- '.github/workflows/agent-release.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'agent/**'
|
||||
- 'server/templates/agent-install.sh'
|
||||
- 'server/src/routes/agent.ts'
|
||||
- 'server/src/lib/agent-auth.ts'
|
||||
- 'server/src/lib/host-agent-credentials.ts'
|
||||
- 'server/src/services/agent-instance-report.ts'
|
||||
- '.github/workflows/agent-release.yml'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish_release:
|
||||
description: 'Publish an Agent GitHub Release using the version in agent/VERSION.'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.22.x'
|
||||
|
||||
jobs:
|
||||
version:
|
||||
name: Resolve Agent Version
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
version_changed: ${{ steps.changed.outputs.version_changed }}
|
||||
should_release: ${{ steps.release.outputs.should_release }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Read Agent version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="$(tr -d '[:space:]' < agent/VERSION)"
|
||||
|
||||
if [[ ! "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
|
||||
echo "Invalid Agent version: ${VERSION}" >&2
|
||||
echo "Expected vMAJOR.MINOR.PATCH or vMAJOR.MINOR.PATCH-suffix" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
echo "Agent version: ${VERSION}"
|
||||
|
||||
- name: Detect agent/VERSION change
|
||||
id: changed
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
VERSION_CHANGED=false
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
|
||||
BEFORE="${{ github.event.before }}"
|
||||
AFTER="${{ github.sha }}"
|
||||
|
||||
if [[ -z "${BEFORE}" || "${BEFORE}" =~ ^0+$ ]]; then
|
||||
VERSION_CHANGED=true
|
||||
elif git diff --name-only "${BEFORE}" "${AFTER}" | grep -qx 'agent/VERSION'; then
|
||||
VERSION_CHANGED=true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "version_changed=${VERSION_CHANGED}" >> "${GITHUB_OUTPUT}"
|
||||
echo "agent/VERSION changed: ${VERSION_CHANGED}"
|
||||
|
||||
- name: Decide release publishing
|
||||
id: release
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
SHOULD_RELEASE=false
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${{ steps.changed.outputs.version_changed }}" == "true" ]]; then
|
||||
SHOULD_RELEASE=true
|
||||
elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${{ inputs.publish_release }}" == "true" ]]; then
|
||||
SHOULD_RELEASE=true
|
||||
fi
|
||||
|
||||
echo "should_release=${SHOULD_RELEASE}" >> "${GITHUB_OUTPUT}"
|
||||
echo "should release: ${SHOULD_RELEASE}"
|
||||
|
||||
build-agent:
|
||||
name: Build Agent
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- version
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: agent/go.mod
|
||||
|
||||
- name: Test Agent
|
||||
working-directory: agent
|
||||
run: go test ./...
|
||||
|
||||
- name: Build Agent release files
|
||||
run: VERSION="${{ needs.version.outputs.version }}" bash agent/scripts/build-release.sh
|
||||
|
||||
- name: Prepare release assets
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ needs.version.outputs.version }}"
|
||||
mkdir -p agent/release
|
||||
|
||||
cp agent/dist/incudal-agent-linux-amd64 "agent/release/incudal-agent-x86_64-${VERSION}"
|
||||
cp agent/dist/incudal-agent-linux-arm64 "agent/release/incudal-agent-aarch64-${VERSION}"
|
||||
|
||||
- name: Verify release assets
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ needs.version.outputs.version }}"
|
||||
test -s "agent/release/incudal-agent-x86_64-${VERSION}"
|
||||
test -s "agent/release/incudal-agent-aarch64-${VERSION}"
|
||||
|
||||
- name: Upload Agent artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: incudal-agent-${{ needs.version.outputs.version }}
|
||||
path: |
|
||||
agent/release/incudal-agent-x86_64-${{ needs.version.outputs.version }}
|
||||
agent/release/incudal-agent-aarch64-${{ needs.version.outputs.version }}
|
||||
retention-days: 30
|
||||
|
||||
release-agent:
|
||||
name: Publish Agent Release
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- version
|
||||
- build-agent
|
||||
if: needs.version.outputs.should_release == 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download Agent artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: incudal-agent-${{ needs.version.outputs.version }}
|
||||
path: agent-release
|
||||
|
||||
- name: Publish GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: agent-${{ needs.version.outputs.version }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
name: Incudal Agent ${{ needs.version.outputs.version }}
|
||||
files: |
|
||||
agent-release/incudal-agent-x86_64-${{ needs.version.outputs.version }}
|
||||
agent-release/incudal-agent-aarch64-${{ needs.version.outputs.version }}
|
||||
generate_release_notes: true
|
||||
draft: false
|
||||
prerelease: ${{ contains(needs.version.outputs.version, '-') }}
|
||||
fail_on_unmatched_files: true
|
||||
overwrite_files: true
|
||||
@@ -0,0 +1,81 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lint-and-typecheck:
|
||||
name: Lint & Type Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Generate Prisma Client
|
||||
run: pnpm --filter server exec prisma generate
|
||||
env:
|
||||
DATABASE_URL: postgresql://user:pass@localhost:5432/db
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Type check (client)
|
||||
run: pnpm --filter client type-check
|
||||
|
||||
- name: Type check (server)
|
||||
run: pnpm --filter server type-check
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22.x'
|
||||
cache-dependency-path: agent/go.mod
|
||||
|
||||
- name: Test Agent
|
||||
working-directory: agent
|
||||
run: go test ./...
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint-and-typecheck
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Generate Prisma Client
|
||||
run: pnpm --filter server exec prisma generate
|
||||
env:
|
||||
DATABASE_URL: postgresql://user:pass@localhost:5432/db
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Docker Build & Push
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Image tag (e.g., latest, v1.0.0)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build & Push Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
# For tags: v1.0.0 -> 1.0.0, latest
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
# For manual trigger
|
||||
type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
||||
# Always tag latest for version tags
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
# ============================================================================
|
||||
# Incudal 预构建产物包发布工作流
|
||||
# 在推送版本 tag (v*) 或手动触发时,自动构建前后端并打包为 .tar.gz 发行包
|
||||
# 支持 amd64 和 arm64 双架构
|
||||
# ============================================================================
|
||||
|
||||
name: Build & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: '发行版本号(例如 v1.0.0),留空则使用 git SHA 作为预览版'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
NODE_VERSION: '22'
|
||||
|
||||
jobs:
|
||||
# ===== 构建发行包(多架构矩阵) =====
|
||||
build:
|
||||
name: 构建 (${{ matrix.arch }})
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
runner: ubuntu-latest
|
||||
- arch: arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 安装 pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: 安装 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'pnpm'
|
||||
|
||||
# ===== 安装依赖 =====
|
||||
- name: 安装全部依赖
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: 重新构建 esbuild(Vite 构建需要)
|
||||
run: pnpm rebuild esbuild
|
||||
|
||||
# ===== 生成 Prisma Client =====
|
||||
- name: 生成 Prisma Client
|
||||
run: pnpm --filter server exec prisma generate
|
||||
env:
|
||||
DATABASE_URL: postgresql://user:pass@localhost:5432/db
|
||||
|
||||
# ===== 构建前端 =====
|
||||
- name: 构建前端 (Vite)
|
||||
run: pnpm --filter client build
|
||||
|
||||
# ===== 构建后端 =====
|
||||
- name: 编译后端 (TypeScript)
|
||||
run: pnpm --filter server build
|
||||
|
||||
# ===== 准备发行目录 =====
|
||||
- name: 准备发行目录结构
|
||||
run: |
|
||||
mkdir -p release/client/dist
|
||||
mkdir -p release/server
|
||||
|
||||
# 复制前端构建产物
|
||||
cp -r client/dist/* release/client/dist/
|
||||
|
||||
# 复制后端构建产物
|
||||
cp -r server/dist release/server/
|
||||
cp -r server/prisma release/server/
|
||||
cp server/prisma.config.ts release/server/
|
||||
cp server/package.json release/server/
|
||||
cp -r server/templates release/server/
|
||||
|
||||
# 复制根配置文件
|
||||
cp package.json release/
|
||||
cp pnpm-workspace.yaml release/
|
||||
|
||||
# ===== 安装生产依赖 =====
|
||||
- name: 安装后端生产依赖
|
||||
working-directory: release/server
|
||||
run: |
|
||||
# 在发行目录中只安装生产依赖
|
||||
npm install --omit=dev --ignore-scripts
|
||||
|
||||
# 在当前架构的 runner 上原生生成 Prisma Client
|
||||
DATABASE_URL="postgresql://user:pass@localhost:5432/db" npx prisma generate
|
||||
|
||||
# ===== 创建启动辅助脚本 =====
|
||||
- name: 创建管理脚本
|
||||
run: |
|
||||
cat > release/incudal.sh << 'SCRIPT'
|
||||
#!/bin/bash
|
||||
# Incudal 快捷管理脚本
|
||||
set -e
|
||||
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
case "${1:-}" in
|
||||
start)
|
||||
echo "🚀 启动 Incudal..."
|
||||
cd "$APP_DIR/server"
|
||||
npx prisma migrate deploy
|
||||
cd "$APP_DIR"
|
||||
exec node server/dist/app.js
|
||||
;;
|
||||
migrate)
|
||||
echo "🔄 执行数据库迁移..."
|
||||
cd "$APP_DIR/server"
|
||||
npx prisma migrate deploy
|
||||
echo "✅ 迁移完成"
|
||||
;;
|
||||
version)
|
||||
echo "Incudal $(node -e "console.log(require('./package.json').version)")"
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 {start|migrate|version}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
SCRIPT
|
||||
chmod +x release/incudal.sh
|
||||
|
||||
# ===== 确定版本号 =====
|
||||
- name: 确定版本号
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then
|
||||
VERSION="${{ github.ref_name }}"
|
||||
elif [[ -n "${{ github.event.inputs.tag }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.tag }}"
|
||||
else
|
||||
VERSION="preview-$(echo ${{ github.sha }} | head -c 7)"
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "📦 版本号: $VERSION"
|
||||
|
||||
# ===== 打包 =====
|
||||
- name: 打包为 tar.gz
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
cd release
|
||||
tar -czf "../incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz" .
|
||||
cd ..
|
||||
ls -lh "incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz"
|
||||
echo "📦 发行包大小: $(du -h "incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz" | cut -f1)"
|
||||
|
||||
# ===== 上传构建产物 =====
|
||||
- name: 上传构建产物
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: incudal-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}
|
||||
path: incudal-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
# ===== 发布 Release(等待所有架构构建完成) =====
|
||||
release:
|
||||
name: 发布 Release
|
||||
needs:
|
||||
- build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 确定版本号
|
||||
id: version
|
||||
run: echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
|
||||
# 下载所有架构的构建产物
|
||||
- name: 下载 amd64 产物
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: incudal-${{ steps.version.outputs.version }}-linux-amd64
|
||||
|
||||
- name: 下载 arm64 产物
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: incudal-${{ steps.version.outputs.version }}-linux-arm64
|
||||
|
||||
- name: 列出发行文件
|
||||
run: ls -lh incudal-*.tar.gz
|
||||
|
||||
# 创建 Release 并上传所有架构的产物
|
||||
- name: 创建 GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
incudal-*.tar.gz
|
||||
generate_release_notes: true
|
||||
draft: false
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }}
|
||||
|
||||
# ===== 构建并推送 Docker 镜像(多架构) =====
|
||||
docker:
|
||||
name: 构建 Docker 镜像
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 设置 QEMU(用于跨架构构建 arm64)
|
||||
- name: 设置 QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# 设置 Docker Buildx(多架构构建引擎)
|
||||
- name: 设置 Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# 登录 GitHub Container Registry
|
||||
- name: 登录 GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# 提取版本号和元数据
|
||||
- name: 提取 Docker 元数据
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
# 构建并推送多架构镜像
|
||||
- name: 构建并推送 Docker 镜像
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
Reference in New Issue
Block a user