From 34e1ac6264302a7de2925f52105097b41abcfe47 Mon Sep 17 00:00:00 2001 From: Timi Date: Wed, 8 Apr 2026 12:00:52 +0800 Subject: [PATCH 1/2] remove gson --- src/test/java/test/SpringTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/test/SpringTest.java b/src/test/java/test/SpringTest.java index 98bdbd9..0fca9c3 100644 --- a/src/test/java/test/SpringTest.java +++ b/src/test/java/test/SpringTest.java @@ -1,6 +1,5 @@ package test; -import com.google.gson.Gson; import com.imyeyu.api.TimiServerAPI; import com.imyeyu.api.modules.blog.entity.Article; import com.imyeyu.api.modules.blog.mapper.ArticleMapper; @@ -118,6 +117,5 @@ public class SpringTest { for (int i = 0; i < icon.size(); i++) { map.put(icon.get(i).getName(), icon.get(i).getSvg()); } - System.out.println(new Gson().toJson(map)); } } -- 2.49.1 From b5e9da0e9bd0bcb375f76bea37dcab7a54f98dff Mon Sep 17 00:00:00 2001 From: Timi Date: Wed, 8 Apr 2026 16:30:10 +0800 Subject: [PATCH 2/2] v1.0.0 --- .gitea/workflows/ci.yml | 349 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 .gitea/workflows/ci.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..1ae4817 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,349 @@ +name: CI + +on: + pull_request: + branches: + - master + types: + - closed + +jobs: + build-deploy: + runs-on: act_runner_java + if: ${{ github.event.pull_request.merged == true }} + outputs: + deployment_status: ${{ steps.set_status.outputs.status }} + env: + JAVA_HOME: /usr/lib/jvm/java-21-openjdk + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up environment + run: | + echo "PR #${{ github.event.number }} merged into master" + echo "Source branch: ${{ github.event.pull_request.head.ref }}" + echo "Target branch: ${{ github.event.pull_request.base.ref }}" + + - name: Run tests + run: | + echo "Running test suite..." + + - name: Setup Maven settings + run: | + if [ -z "${{ vars.TIMI_NEXUS_USERNAME }}" ] || [ -z "${{ vars.TIMI_NEXUS_PASSWORD }}" ]; then + echo "Missing vars.TIMI_NEXUS_USERNAME or vars.TIMI_NEXUS_PASSWORD" + exit 1 + fi + mkdir -p ~/.m2 + cat > ~/.m2/settings.xml < + + + timi_nexus + ${{ vars.TIMI_NEXUS_USERNAME }} + ${{ vars.TIMI_NEXUS_PASSWORD }} + + + + EOF + + - name: Build project + run: | + mvn -B -DskipTests clean package + + - name: Deploy service + if: success() + env: + HOST: host.docker.internal + APP_PATH: ${{ vars.APP_PATH }} + DOCKER_CONTAINER_NAME: ${{ vars.DOCKER_CONTAINER_NAME }} + SSHPASS: ${{ secrets.TIMI_SERVER_SSH_PWD }} + MAX_RETRIES: 3 + RETRY_DELAY: 10 + run: | + if [ -z "$HOST" ] || [ -z "$APP_PATH" ] || [ -z "DOCKER_CONTAINER_NAME" ] || [ -z "$SSHPASS" ]; then + echo "Missing production environment variables" + echo "Required: APP_PATH, DOCKER_CONTAINER_NAME, TIMI_SERVER_SSH_PWD" + exit 1 + fi + + # 重试函数 + retry_command() { + local cmd="$1" + local desc="$2" + local attempt=1 + + while [ $attempt -le $MAX_RETRIES ]; do + echo "[$desc] Attempt $attempt/$MAX_RETRIES..." + if eval "$cmd"; then + echo "✓ $desc succeeded" + return 0 + fi + echo "✗ $desc failed (attempt $attempt/$MAX_RETRIES)" + if [ $attempt -lt $MAX_RETRIES ]; then + echo "Retrying in ${RETRY_DELAY}s..." + sleep $RETRY_DELAY + fi + attempt=$((attempt + 1)) + done + + echo "✗ $desc failed after $MAX_RETRIES attempts" + return 1 + } + + # SSH 配置(使用密码认证) + SSH_PORT="22" + SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o ServerAliveInterval=10 -o ServerAliveCountMax=3 -p $SSH_PORT" + SCP_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o ServerAliveInterval=10 -o ServerAliveCountMax=3 -P $SSH_PORT" + + # 获取构建产物信息 + version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version) + artifact_id=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.artifactId) + jar_file="target/${artifact_id}-${version}.jar" + + if [ ! -f "$jar_file" ]; then + echo "Build artifact not found: $jar_file" + exit 1 + fi + + # 目标文件名(去掉版本号) + target_jar="${artifact_id}.jar" + echo "Deploying $jar_file to $HOST:$APP_PATH/$target_jar" + + # 上传文件(带重试) + if ! retry_command "sshpass -e scp $SCP_OPTS \"$jar_file\" \"root@$HOST:$APP_PATH/$target_jar\"" "SCP upload"; then + exit 1 + fi + + # 重启 Docker 服务(带重试) + echo "Restarting Docker service: $DOCKER_SERVICE_NAME" + if ! retry_command "sshpass -e ssh $SSH_OPTS \"root@$HOST\" \"docker restart $DOCKER_SERVICE_NAME\"" "Docker restart"; then + exit 1 + fi + echo "Deployment completed successfully" + + - name: Create release + if: ${{ success() && startsWith(github.event.pull_request.title, 'v') }} + env: + GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }} + GITEA_SERVER_URL: ${{ github.server_url }} + GITEA_INTERNAL_URL: ${{ vars.TIMI_GITEA_INTERNAL_URL }} + GITEA_REPOSITORY: ${{ github.repository }} + RELEASE_TAG: ${{ github.event.pull_request.title }} + RELEASE_TARGET: ${{ github.sha }} + MAX_RETRIES: 3 + RETRY_DELAY: 10 + run: | + if [ -z "$GITEA_TOKEN" ]; then + echo "Missing secrets.RUNNER_TOKEN" + exit 1 + fi + + # Use internal URL if available, fallback to public URL + if [ -n "$GITEA_INTERNAL_URL" ]; then + api_base_url="$GITEA_INTERNAL_URL" + echo "Using internal Gitea URL: $api_base_url" + else + api_base_url="$GITEA_SERVER_URL" + echo "Using public Gitea URL: $api_base_url" + fi + + # 获取构建产物信息 + version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version) + artifact_id=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.artifactId) + jar_file="target/${artifact_id}-${version}.jar" + + if [ ! -f "$jar_file" ]; then + echo "Build artifact not found: $jar_file" + exit 1 + fi + + file_size=$(stat -c%s "$jar_file" 2>/dev/null || stat -f%z "$jar_file" 2>/dev/null || echo "unknown") + echo "Found fat jar: $jar_file (size: $file_size bytes)" + + api_url="$api_base_url/api/v1/repos/$GITEA_REPOSITORY/releases" + payload=$(cat < "$release_response_file" + + http_code=$(curl -sS -w "%{http_code}" -o "$release_response_file" -X POST "$api_url" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + --connect-timeout 30 \ + --max-time 60 \ + -d "$payload" 2>/dev/null) || http_code="000" + + response=$(cat "$release_response_file" 2>/dev/null || echo "{}") + echo "HTTP Status: $http_code" + + if [ "$http_code" = "201" ]; then + # 提取第一个 id 字段的值,确保去除换行符 + if command -v jq >/dev/null 2>&1; then + release_id=$(echo "$response" | jq -r '.id' 2>/dev/null) + else + release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 | tr -d '\n\r') + fi + echo "✓ Release created: id=$release_id" + elif [ "$http_code" = "409" ]; then + # HTTP 409 Conflict: Release 已存在,获取现有的 release_id + echo "Release already exists (HTTP 409), fetching existing release..." + existing=$(curl -sS "$api_url" -H "Authorization: token $GITEA_TOKEN" --connect-timeout 30 2>/dev/null || echo "[]") + # 使用 jq 解析 JSON,如果没有 jq 则用 grep + if command -v jq >/dev/null 2>&1; then + release_id=$(echo "$existing" | jq -r ".[] | select(.tag_name==\"$RELEASE_TAG\") | .id" 2>/dev/null | head -1) + else + release_id=$(echo "$existing" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 | tr -d '\n\r') + fi + if [ -n "$release_id" ]; then + echo "✓ Found existing release: id=$release_id" + else + echo "✗ Could not find existing release id" + fi + else + echo "✗ Failed (HTTP $http_code)" + if [ $attempt -lt $MAX_RETRIES ]; then + echo "Retrying in ${RETRY_DELAY}s..." + sleep $RETRY_DELAY + fi + fi + attempt=$((attempt + 1)) + done + + if [ -z "$release_id" ]; then + echo "✗ Failed to create/find release after $MAX_RETRIES attempts" + exit 1 + fi + + # 上传 fat jar(带重试) + asset_name=$(basename "$jar_file") + echo "Uploading asset: $asset_name (size: $file_size bytes)" + upload_url="$api_url/$release_id/assets?name=$asset_name" + echo "Upload URL: $upload_url" + + # 使用唯一临时文件避免跨 job 污染 + asset_response_file=$(mktemp /tmp/asset_response_XXXXXX.json) + trap "rm -f $release_response_file $asset_response_file" EXIT + + upload_success=false + attempt=1 + while [ $attempt -le $MAX_RETRIES ] && [ "$upload_success" = "false" ]; do + echo "[Upload asset] Attempt $attempt/$MAX_RETRIES..." + + # 清空临时文件 + > "$asset_response_file" + + # Gitea API 要求使用 multipart/form-data 格式上传文件 + http_code=$(curl -sS -w "%{http_code}" -o "$asset_response_file" -X POST "$upload_url" \ + -H "Authorization: token $GITEA_TOKEN" \ + --connect-timeout 30 \ + --max-time 300 \ + -F "attachment=@$jar_file" 2>/dev/null) || http_code="000" + + if [ "$http_code" = "201" ]; then + upload_success=true + echo "✓ Successfully uploaded: $asset_name" + else + echo "✗ Upload failed (HTTP $http_code)" + cat "$asset_response_file" 2>/dev/null || true + fi + + if [ "$upload_success" = "false" ] && [ $attempt -lt $MAX_RETRIES ]; then + echo "Retrying in ${RETRY_DELAY}s..." + sleep $RETRY_DELAY + fi + attempt=$((attempt + 1)) + done + + if [ "$upload_success" = "false" ]; then + echo "✗ Failed to upload asset after $MAX_RETRIES attempts" + exit 1 + fi + + - name: Mark deployment success + id: set_status + if: always() + run: | + echo "status=success" >> $GITHUB_OUTPUT + + notify-on-failure: + runs-on: act_runner_java + needs: build-deploy + if: ${{ always() && github.event.pull_request.merged == true && needs.build-deploy.result == 'failure' }} + steps: + - name: Notify CI failure + env: + PR_NUMBER: ${{ github.event.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_URL: ${{ github.event.pull_request.html_url }} + SOURCE_BRANCH: ${{ github.event.pull_request.head.ref }} + AUTHOR: ${{ github.event.pull_request.user.login }} + COMMIT_SHA: ${{ github.sha }} + REPO: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + # 通知配置(按需启用) + WEBHOOK_URL: ${{ vars.NOTIFY_WEBHOOK_URL }} + run: | + echo "=========================================" + echo "CI Pipeline Failed - Manual Review Required" + echo "=========================================" + echo "" + echo "PR: #$PR_NUMBER - $PR_TITLE" + echo "Branch: $SOURCE_BRANCH" + echo "Author: $AUTHOR" + echo "Commit: $COMMIT_SHA" + echo "" + echo "Actions:" + echo " 1. Re-run CI: $SERVER_URL/$REPO/actions" + echo " 2. Revert PR: $PR_URL (click 'Revert' button)" + echo "" + echo "=========================================" + + # 发送 Webhook 通知(钉钉/企业微信/Slack 等) + if [ -n "$WEBHOOK_URL" ]; then + message="🚨 CI 部署失败\n\nPR: #$PR_NUMBER - $PR_TITLE\n分支: $SOURCE_BRANCH\n提交者: $AUTHOR\n\n请检查并决定:\n• 重试 CI\n• 回滚合并" + + # 通用 JSON 格式(适配大多数 Webhook) + payload=$(cat <