{"id":54,"date":"2026-03-19T10:00:00","date_gmt":"2026-03-19T02:00:00","guid":{"rendered":"http:\/\/localhost:8888\/?p=54"},"modified":"2026-07-09T16:17:45","modified_gmt":"2026-07-09T08:17:45","slug":"runnergo%e9%9b%86%e6%88%90ci-cd%e5%ae%9e%e6%88%98","status":"publish","type":"post","link":"https:\/\/blog.runnergo.cn\/?p=54","title":{"rendered":"RunnerGo\u96c6\u6210CI\/CD\u5b9e\u6218 &#8211; \u6d4b\u8bd5\u6d41\u6c34\u7ebf\u81ea\u52a8\u5316\u90e8\u7f72\u6307\u5357"},"content":{"rendered":"<h1>RunnerGo\u96c6\u6210CI\/CD\u5b9e\u6218 - \u6d4b\u8bd5\u6d41\u6c34\u7ebf\u81ea\u52a8\u5316\u90e8\u7f72\u6307\u5357<\/h1>\n\n&gt; \u4f5c\u8005\uff1aCI\u96c6\u6210\u4e13\u5bb6\n---\n\n<h2>\u524d\u8a00<\/h2>\n\n\u5927\u5bb6\u597d\uff0c\u6211\u662fCI\u96c6\u6210\u4e13\u5bb6\u3002\u505aCI\/CD\u5b9e\u8df58\u5e74\uff0c\u6d4b\u8bd5\u96c6\u6210\u662f\u6700\u5173\u952e\u7684\u73af\u8282\u4e4b\u4e00\u3002RunnerGo\u63d0\u4f9b\u4e86\u5b8c\u5584\u7684API\u63a5\u53e3\uff0c\u53ef\u4ee5\u8f7b\u677e\u96c6\u6210\u5230\u5404\u79cdCI\/CD\u5e73\u53f0\u3002\u4eca\u5929\u5206\u4eabRunnerGo\u96c6\u6210CI\/CD\u7684\u5b9e\u6218\u7ecf\u9a8c\u3002\n\n&gt; \u672c\u6587\u7ae0\u53c2\u8003RunnerGo v2.3\u5b98\u65b9\u6587\u6863\n\n---\n\n<h2>\u4e00\u3001RunnerGo API\u4ecb\u7ecd<\/h2>\n\n<h3>API\u80fd\u529b<\/h3>\n\nRunnerGo\u63d0\u4f9b\u4e30\u5bcc\u7684API\u63a5\u53e3\uff1a\n\n| API | \u529f\u80fd |\n|-----|------|\n| \u6267\u884c\u6d4b\u8bd5\u8ba1\u5212 | \u89e6\u53d1\u81ea\u52a8\u5316\/\u6027\u80fd\u6d4b\u8bd5 |\n| \u67e5\u8be2\u6267\u884c\u72b6\u6001 | \u83b7\u53d6\u6d4b\u8bd5\u8fdb\u5ea6 |\n| \u83b7\u53d6\u6d4b\u8bd5\u62a5\u544a | \u4e0b\u8f7d\u6d4b\u8bd5\u62a5\u544a |\n| \u7ba1\u7406\u573a\u666f | \u521b\u5efa\/\u4fee\u6539\u6d4b\u8bd5\u573a\u666f |\n\n<h3>API\u8ba4\u8bc1<\/h3>\n\nRunnerGo\u4f7f\u7528Token\u8ba4\u8bc1\uff1a\n\n<code>`<\/code>bash\n<h1>\u83b7\u53d6Token<\/h1>\ncurl -X POST ${RUNNERGO_URL}\/api\/auth\/login \n  -H \"Content-Type: application\/json\" \n  -d '{\"username\":\"admin\",\"password\":\"password\"}'\n\n<h1>\u4f7f\u7528Token<\/h1>\ncurl -X GET ${RUNNERGO_URL}\/api\/plans \n  -H \"Authorization: Bearer ${TOKEN}\"\n<code>`<\/code>\n\n---\n\n<h2>\u4e8c\u3001Jenkins\u96c6\u6210<\/h2>\n\n<h3>\u521b\u5efaPipeline<\/h3>\n\n<code>`<\/code>groovy\npipeline {\n    agent any\n\n    environment {\n        RUNNERGO_URL = 'http:\/\/runnergo.example.com'\n        RUNNERGO_TOKEN = credentials('runnergo-token')\n        PLAN_ID = 'plan-xxx-xxx'\n    }\n\n    stages {\n        stage('RunnerGo\u81ea\u52a8\u5316\u6d4b\u8bd5') {\n            steps {\n                script {\n                    \/\/ \u89e6\u53d1\u6d4b\u8bd5\n                    def response = sh(\n                        script: \"\"\"\n                            curl -s -X POST ${RUNNERGO_URL}\/api\/plan\/run \n                                -H \"Authorization: Bearer ${RUNNERGO_TOKEN}\" \n                                -H \"Content-Type: application\/json\" \n                                -d '{\"plan_id\":\"${PLAN_ID}\"}'\n                        \"\"\",\n                        returnStdout: true\n                    ).trim()\n\n                    def json = new groovy.json.JsonSlurper().parseText(response)\n                    def reportId = json.report_id\n\n                    \/\/ \u7b49\u5f85\u6d4b\u8bd5\u5b8c\u6210\n                    def status = 'running'\n                    while (status == 'running') {\n                        sleep(time: 10, unit: 'SECONDS')\n\n                        def statusResponse = sh(\n                            script: \"\"\"\n                                curl -s ${RUNNERGO_URL}\/api\/report\/${reportId}\/status \n                                    -H \"Authorization: Bearer ${RUNNERGO_TOKEN}\"\n                            \"\"\",\n                            returnStdout: true\n                        ).trim()\n\n                        def statusJson = new groovy.json.JsonSlurper().parseText(statusResponse)\n                        status = statusJson.status\n                    }\n\n                    \/\/ \u68c0\u67e5\u7ed3\u679c\n                    if (status != 'success') {\n                        error(\"\u6d4b\u8bd5\u5931\u8d25\")\n                    }\n                }\n            }\n        }\n    }\n\n    post {\n        always {\n            \/\/ \u83b7\u53d6\u62a5\u544a\u94fe\u63a5\n            sh \"\"\"\n                curl -s ${RUNNERGO_URL}\/api\/report\/${reportId} \n                    -H \"Authorization: Bearer ${RUNNERGO_TOKEN}\" \n                    &gt; reports\/runnergo-report.json\n            \"\"\"\n        }\n    }\n}\n<code>`<\/code>\n\n---\n\n<h2>\u4e09\u3001GitLab CI\u96c6\u6210<\/h2>\n\n<code>`<\/code>yaml\n<h1>.gitlab-ci.yml<\/h1>\nstages:\n  - build\n  - test\n\nrunnergo_test:\n  stage: test\n  image: curlimages\/curl:latest\n  script:\n    # \u89e6\u53d1\u6d4b\u8bd5\n    - |\n      RESPONSE=$(curl -s -X POST ${RUNNERGO_URL}\/api\/plan\/run \n        -H \"Authorization: Bearer ${RUNNERGO_TOKEN}\" \n        -H \"Content-Type: application\/json\" \n        -d '{\"plan_id\":\"${PLAN_ID}\"}')\n\n      REPORT_ID=$(echo $RESPONSE | jq -r '.report_id')\n\n    # \u7b49\u5f85\u6d4b\u8bd5\u5b8c\u6210\n    - |\n      while true; do\n        STATUS=$(curl -s ${RUNNERGO_URL}\/api\/report\/${REPORT_ID}\/status \n          -H \"Authorization: Bearer ${RUNNERGO_TOKEN}\" | jq -r '.status')\n\n        if [ \"$STATUS\" != \"running\" ]; then\n          break\n        fi\n\n        sleep 10\n      done\n\n    # \u68c0\u67e5\u7ed3\u679c\n    - |\n      if [ \"$STATUS\" != \"success\" ]; then\n        echo \"\u6d4b\u8bd5\u5931\u8d25\"\n        exit 1\n      fi\n\n  variables:\n    RUNNERGO_URL: \"http:\/\/runnergo.example.com\"\n    RUNNERGO_TOKEN: ${RUNNERGO_API_TOKEN}\n    PLAN_ID: \"plan-xxx-xxx\"\n<code>`<\/code>\n\n---\n\n<h2>\u56db\u3001GitHub Actions\u96c6\u6210<\/h2>\n\n<code>`<\/code>yaml\n<h1>.github\/workflows\/test.yml<\/h1>\nname: RunnerGo Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Trigger RunnerGo Test\n        run: |\n          RESPONSE=$(curl -s -X POST ${{ secrets.RUNNERGO_URL }}\/api\/plan\/run \n            -H \"Authorization: Bearer ${{ secrets.RUNNERGO_TOKEN }}\" \n            -H \"Content-Type: application\/json\" \n            -d '{\"plan_id\":\"${{ secrets.PLAN_ID }}\"}')\n\n          REPORT_ID=$(echo $RESPONSE | jq -r '.report_id')\n          echo \"report_id=$REPORT_ID\" &gt;&gt; $GITHUB_ENV\n\n      - name: Wait for Test Completion\n        run: |\n          while true; do\n            STATUS=$(curl -s ${{ secrets.RUNNERGO_URL }}\/api\/report\/${{ env.report_id }}\/status \n              -H \"Authorization: Bearer ${{ secrets.RUNNERGO_TOKEN }}\" | jq -r '.status')\n\n            if [ \"$STATUS\" != \"running\" ]; then\n              echo \"Test completed with status: $STATUS\"\n              if [ \"$STATUS\" != \"success\" ]; then\n                exit 1\n              fi\n              break\n            fi\n\n            sleep 10\n          done\n\n      - name: Download Report\n        if: always()\n        run: |\n          curl -s ${{ secrets.RUNNERGO_URL }}\/api\/report\/${{ env.report_id }} \n            -H \"Authorization: Bearer ${{ secrets.RUNNERGO_TOKEN }}\" \n            &gt; runnergo-report.json\n\n      - name: Upload Report\n        if: always()\n        uses: actions\/upload-artifact@v3\n        with:\n          name: runnergo-report\n          path: runnergo-report.json\n<code>`<\/code>\n\n---\n\n<h2>\u4e94\u3001\u8d28\u91cf\u95e8\u7981\u914d\u7f6e<\/h2>\n\n<h3>\u6d4b\u8bd5\u5931\u8d25\u963b\u65ad\u90e8\u7f72<\/h3>\n\n<code>`<\/code>groovy\nstage('\u8d28\u91cf\u68c0\u67e5') {\n    steps {\n        script {\n            \/\/ \u6267\u884cRunnerGo\u6d4b\u8bd5\n            def success = triggerRunnerGoTest()\n\n            if (!success) {\n                error('\u8d28\u91cf\u95e8\u7981\u672a\u901a\u8fc7\uff0c\u963b\u65ad\u90e8\u7f72')\n            }\n        }\n    }\n}\n\nstage('\u90e8\u7f72') {\n    \/\/ \u53ea\u6709\u8d28\u91cf\u68c0\u67e5\u901a\u8fc7\u624d\u6267\u884c\n    steps {\n        sh 'deploy.sh'\n    }\n}\n<code>`<\/code>\n\n---\n\n<h2>\u603b\u7ed3<\/h2>\n\nRunnerGo CI\/CD\u96c6\u6210\u8981\u70b9\uff1a\n\n| \u73af\u8282 | \u914d\u7f6e |\n|------|------|\n| API\u8ba4\u8bc1 | Token\u83b7\u53d6\u548c\u4f7f\u7528 |\n| Jenkins | Pipeline\u811a\u672c |\n| GitLab CI | YAML\u914d\u7f6e |\n| GitHub Actions | Workflow\u914d\u7f6e |\n| \u8d28\u91cf\u95e8\u7981 | \u6d4b\u8bd5\u5931\u8d25\u963b\u65ad |\n\n---\n\n<strong>\u672c\u7cfb\u5217\u6587\u7ae0\u5b8c\u7ed3\uff01\u611f\u8c22\u5927\u5bb6\u6301\u7eed\u5173\u6ce8RunnerGo\u535a\u5ba2\u3002<\/strong>\n\n&gt; \u4f5c\u8005\uff1aCI\u96c6\u6210\u4e13\u5bb6\uff0c8\u5e74CI\/CD\u5b9e\u8df5\u7ecf\u9a8c\uff0c\u4e13\u6ce8\u6d4b\u8bd5\u81ea\u52a8\u5316\u96c6\u6210","protected":false},"excerpt":{"rendered":"<p>RunnerGo\u96c6\u6210CI\/CD\u5b9e\u6218 &#8211; \u6d4b\u8bd5\u6d41\u6c34\u7ebf\u81ea\u52a8\u5316\u90e8\u7f72\u6307\u5357 &gt; \u4f5c\u8005\uff1aCI\u96c6\u6210\u4e13\u5bb6 &#8212; \u524d\u8a00 \u5927\u5bb6\u597d\uff0c\u6211\u662fCI\u96c6\u6210\u4e13\u5bb6\u3002\u505aCI\/CD\u5b9e\u8df58\u5e74\uff0c\u6d4b\u8bd5\u96c6\u6210\u662f\u6700\u5173\u952e\u7684\u73af\u8282\u4e4b\u4e00\u3002RunnerG&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-54","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=\/wp\/v2\/posts\/54","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=54"}],"version-history":[{"count":0,"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=\/wp\/v2\/posts\/54\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=54"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=54"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.runnergo.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=54"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}