diff --git a/api_document.md b/api_document.md index e667cb17..a8b7ac79 100644 --- a/api_document.md +++ b/api_document.md @@ -461,7 +461,7 @@ curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror | jq #### 项目详情 ``` -GET /api/:namespace_id/:id +GET /api/:owner/:repo ``` *示例* ```bash @@ -471,8 +471,8 @@ curl -X GET http://localhost:3000/api/jasder/jasder_test | jq |参数名|必选|类型|说明| |-|-|-|-| -|namespace_id |是|string |用户登录名 | -|id |是|string |项目标识identifier | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | *返回参数说明:* @@ -503,7 +503,7 @@ curl -X GET http://localhost:3000/api/jasder/jasder_test | jq #### 项目详情(简版) ``` -GET /api/:namespace_id/:id/simple +GET /api/:owner/:repo/simple ``` *示例* ```bash @@ -513,7 +513,8 @@ curl -X GET http://localhost:3000/api/jasder/jasder_test/simple | jq |参数名|必选|类型|说明| |-|-|-|-| -|id |是|int |项目id | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | *返回参数说明:* @@ -876,7 +877,7 @@ curl -X POST http://localhost:3000/api/projects/3297/forks | jq #### 获取代码目录列表 ``` -POST /api/:namespace_id/:project_id/repository/entries +POST /api/:owner/:repo/repository/entries ``` *示例* ```bash @@ -888,7 +889,8 @@ http://localhost:3000//api/jasder/jasder_test/repository/entries | jq |参数名|必选|类型|说明| |-|-|-|-| -|id |是|int |项目id | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | |ref |否|string |分支名称、tag名称或是提交记录id,默认为master分支 | @@ -1378,7 +1380,7 @@ http://localhost:3000/api/projects/recommend | jq #### 项目主页 ``` -GET api/:namespace_id/:id/about +GET api/:owner/:repo/about ``` *示例* @@ -1391,8 +1393,8 @@ http://localhost:3000/api/:jason/forgeplus/about | jq |参数名|必选|类型|说明| |-|-|-|-| -|namespace_id |是|string |用户登录名 | -|id |是|string |项目标识identifier | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | *返回参数说明:* @@ -1419,7 +1421,7 @@ http://localhost:3000/api/:jason/forgeplus/about | jq #### 修改项目主页内容 ``` -POST api/:namespace_id/:id/about +POST api/:owner/:repo/about ``` *示例* @@ -1427,15 +1429,15 @@ POST api/:namespace_id/:id/about curl -X POST \ -d "content=内容" \ -d "attachment_ids=[1, 2, 2]" \ -http://localhost:3000/api/:jasder/forgeplus/about | jq +http://localhost:3000/api/jasder/forgeplus/about | jq ``` *请求参数说明:* |参数名|必选|类型|说明| |-|-|-|-| -|namespace_id |是|string |用户登录名 | -|id |是|string |项目标识identifier | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | |content |是|string |内容信息 | |attachment_ids |是|array |附件id | @@ -1463,7 +1465,7 @@ http://localhost:3000/api/:jasder/forgeplus/about | jq ### 获取分支列表 ``` -GET /api/:namespace_id/:id/branches +GET /api/:owner/:repo/branches ``` *示例* ```bash @@ -1473,7 +1475,8 @@ curl -X GET http://localhost:3000/api/jasder/jasder_test/branches | jq |参数名|必选|类型|说明| |-|-|-|-| -|id |是|id |项目id | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | *返回参数说明:* @@ -1630,7 +1633,7 @@ http://localhost:3000/api/repositories/5836/tags.json | jq ## 仓库详情 ``` -GET /api/:namespace_id/:project_id/repository +GET /api/:owner/:repo/repository ``` *示例* ```bash @@ -1641,8 +1644,8 @@ http://192.168.2.230:3000/api/jasder/forgeplus/repository | jq |参数名|必选|类型|说明| |-|-|-|-| -|namespace_id |是|string |用户登录名 | -|project_id |是|string |项目标识identifier | +|owner |是|string |用户登录名 | +|repo |是|string |项目标识identifier | *返回参数说明:* @@ -3096,6 +3099,78 @@ http://localhost:3000/api/trustie/truesite/protected_branches/master.json | jq ``` --- +#### 获取仓库README文件 +``` +GET api/:owner/:repo/readme +``` +*示例* +```bash +curl -X GET http://localhost:3000/api/trusite/trusite/readme | jq +``` + +*请求参数说明:* + +|参数名|类型|说明| +|-|-|-| +|owner |是|string |项目拥有者登录名 | +|repo |否|boolean |仓库名称 | +|ref |否|string |分支、tag或commit。默认: 仓库的默认分支(通常是master)| + + +*返回参数说明:* + +|参数名|类型|说明| +|-|-|-| +|name |string|文件名称| +|path |string|文件相对路径| +|type |string|文件类型, file:文件| +|size |int|文件大小 单位KB| +|content |string|文件内容,base64加密| + +返回值 +```json +{ + "type": "file", + "encoding": "base64", + "size": 13544, + "name": "README.md", + "path": "README.md", + "content": "Q2hpbmVzZSAmbmJzcDsgfCAmbmJzcDsgW0VuZ7i9yZWFkbWUvaW5kZXgucG5" +} +``` +--- + +#### 获库仓库的语言百分占比 +``` +GET api/:owner/:repo/languages +``` +*示例* +```bash +curl -X GET http://localhost:3000/api/jasder/trusite/languages | jq +``` + +*请求参数说明:* + +|参数名|类型|说明| +|-|-|-| +|owner |是|string |项目拥有者登录名 | +|repo |否|boolean |仓库名称 | + + +返回值 +```json +{ + "JavaScript": "90.2%", + "CSS": "6.1%", + "Java": "2.9%", + "HTML": "0.8%" +} +``` +--- + + + + ### DevOps相关api --- @@ -3320,9 +3395,10 @@ http://localhost:3000/api/jasder/forge/get_trustie_pipeline.json | jq PUT /api/:owner/:repo/update_trustie_pipeline ``` *示例* + ```bash curl -X GET \ -http://localhost:3000/api/jasder/forge/update_trustie_pipeline.json | jq +http://localhost:3000/api/jasder/forge/update_trustie_pipeline.json?pipeline_id=1 | jq ``` *请求参数说明:* @@ -3899,6 +3975,48 @@ http://localhost:3000/api/users/ci/cloud_account | jq } } ``` +------ + +#### 绑定CI服务器-Trustie提供服务器 + +``` +POST /api/users/ci/cloud_account/trustie_bind +``` + +*示例* + +```bash +curl -X POST \ +-d "account=xx" \ +https://localhost:3000/api/users/ci/cloud_account/trustie_bind.json | jq +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ------- | ---- | ------ | ---------- | +| account | 是 | string | 登录用户名 | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------------ | ------ | --------------------------------------- | +| step | int | 0: 未绑定;1: 未认证(已绑定),2: 已认证 | +| ip | string | ci服务器ip | +| redirect_url | string | 认证地址 | + +返回值 + +```json +{ + "step": 0, + "cloud_account": { + "ip": "xxx.xxx.xxx.x", + "redirect_url": "http://localhost:3000/login" + } +} +``` + --- #### 绑定CI服务器 @@ -3941,10 +4059,616 @@ https://localhost:3000/api/users/ci/cloud_account/bind.json | jq } } ``` +------ + +#### 流水线查询 + +``` +GET /api/ci/pipelines/list?identifier={identifier} +``` + +*示例* + +```bash +curl -X GET \ +http://localhost:3000/api/ci/pipelines/list.json?identifier="xxx" | jq +``` + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------------- | ------ | --------------- | +| id | int | 流水线id | +| pipeline_name | string | 流水线名称 | +| file_name | string | 流水线文件名 | +| created_at | string | 创建时间 | +| sync | int | 是否同步到gitea | + +返回值 + +```json +{ + "pipelines": [ + { + "id": 1, + "pipeline_name": "2020-01-08 流水线", + "file_name": ".trustie.pipeline.yaml", + "created_at": "2021-01-08 04:16:24", + "updated_at": "2021-01-08 04:16:24" + } + ] +} +``` + --- +#### 流水线新增 -### 解除CI服务器绑定 +``` +POST /api/ci/pipelines +``` + +*示例* + +```bash +curl --location --request POST 'http://localhost:3000/api/ci/pipelines' \ +--header 'Content-Type: application/json' \ +--data-raw ' { + "pipeline_name": "流水线 2021-01-12", + "file_name": ".trustie.pipeline.yaml", + "identifier": "xxx" +}' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ------------- | ---- | ------ | ---------------------------------------------- | +| pipeline_name | 是 | string | 流水线名称 | +| file_name | 是 | string | 文件名称(默认初始值:.trustie.pipeline.yaml) | +| identifier | 是 | string | 项目identifier | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | -------------- | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | +| id | int | 新增流水线的id | + +返回值 + +```json +{ + "status": 0, + "message": "success", + "id": 18 +} +``` + +------ + +#### 流水线更新 + +修改流水线名称时调用。 + +``` +PUT /api/ci/pipelines/{id} +``` + +*示例* + +```bash +curl --location --request PUT 'http://localhost:3000/api/ci/pipelines/3' \ +--header 'Content-Type: application/json' \ +--data-raw ' { + "pipeline_name": "2020-01-11 流水线" +}' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ------------- | ---- | ------ | ---------- | +| id | 是 | id | 流水线id | +| pipeline_name | 是 | string | 流水线名称 | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线删除 + +``` +DELETE /api/ci/pipelines/{id} +``` + +*示例* + +```bash +curl -X DELETE \ +https://localhost:3000/api/ci/pipelines/1 | jq +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ------ | ---- | ---- | -------- | +| id | 是 | int | 流水线id | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线的阶段查询 + +``` +GET /api/ci/pipelines/{id}/stages +``` + +*示例* + +```bash +curl --location --request GET 'http://localhost:3000/api/ci/pipelines/19/stages.json' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ------ | ---- | ---- | -------- | +| id | 是 | int | 流水线id | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ----------- | ------ | -------- | +| stages | arr | 阶段数组 | +| stage_name | string | 阶段名称 | +| stage_type | string | 阶段类型 | +| pipeline_id | int | 流水线id | +| show_index | int | 排序 | + +返回值 + +```json +{ + "stages": [ + { + "id": 37, + "stage_name": "初始化", + "stage_type": "init", + "pipeline_id": 19, + "show_index": 1, + "created_at": "2021-01-12T15:18:00.000+08:00", + "updated_at": "2021-01-12T15:18:00.000+08:00" + }, + { + "id": 38, + "stage_name": "编译构建", + "stage_type": "build", + "pipeline_id": 19, + "show_index": 2, + "created_at": "2021-01-12T15:18:00.000+08:00", + "updated_at": "2021-01-12T15:18:00.000+08:00" + } + ] +} +``` + +------ + +#### 确认阶段流水线完整内容查询 + +``` +GET /api/ci/pipelines/{id}/content?owner={owner}&repo={repo} +``` + +*示例* + +```bash +curl -X GET \ +http://localhost:3000/api/ci/pipelines/1/content.json?owner=xx&repo=xx | jq +``` + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ---------------- | +| content | String | 流水线内容 | +| sync | int | 同步状态 | +| owner | string | 用户登录名 | +| repo | string | 项目的identifier | + +返回值 + +```json +{ + "content": "#pipeline \nkind: pipeline\r\nname: maven项目-镜像仓库\r\n\r\nplatform:\r\n os: linux\r\n arch: arm64\nsteps:\n- name: Maven编译\r\n image: arm64v8/maven\r\n commands:\r\n - mvn install\n- name: 编译镜像-推送到仓库\r\n image: plugins/docker\r\n settings:\r\n username: moshenglv\r\n password: RL9UB5P7Jtzukka\r\n repo: docker.io/moshenglv/demo\r\n tags: latest\n", + "sync": 1, + "sha":"xxxxx" +} +``` + +------ + +#### 流水线阶段新增 + +``` +POST /api/ci/pipelines/{id}/create_stage +``` + +*示例* + +```bash +curl --location --request POST 'http://localhost:3000/api/ci/pipelines/19/create_stage.json' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "stage_name": "新阶段2", + "show_index": 2 +}' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ---------- | ---- | ------ | -------- | +| id | 是 | int | 流水线id | +| show_index | 是 | int | 阶段排序 | +| stage_name | 是 | string | 阶段名称 | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线阶段更新 + +``` +PUT /api/ci/pipelines/{id}/{stage_id}/update_stage +``` + +*示例* + +```bash +curl --location --request PUT 'http://localhost:3000/api/ci/pipelines/1/5/update_stage.json' \ +--header 'Content-Type: application/json' \ +--data-raw ' { + "stage_name": "新阶段-更新" +}' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ---------- | ---- | ------ | -------------------------------- | +| id | 是 | int | 流水线id | +| stage_name | 是 | string | 阶段名称(默认为 阶段名-模板名) | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线阶段删除 + +``` +DELETE /api/ci/pipelines/{id}/{stage_id}/delete_stage?show_index={index} +``` + +*示例* + +```bash +curl --location --request DELETE 'http://localhost:3000/api/ci/pipelines/19/42/delete_stage.json?show_index=2' \ +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ---------- | ---- | ---- | ---------------------- | +| id | 是 | int | 流水线id | +| stage_id | 是 | int | 阶段id | +| show_index | 是 | int | 被删除阶段的show_index | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线阶段步骤查询 + +``` +GET /api/ci/pipelines/{id}/{stage_id}/steps.json +``` + +*示例* + +```bash +curl -X GET \ +http://localhost:3000/api/ci/pipelines/1/2/steps.json | jq +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| -------- | ---- | ---- | -------- | +| id | 是 | int | 流水线id | +| stage_id | 是 | int | 阶段id | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ---------- | ------ | ------------------ | +| id | int | 步骤id | +| step_name | string | 步骤名称 | +| stage_id | int | 所属阶段id | +| show_index | int | 显示顺序 | +| content | String | 步骤内容 | +| template | Object | 步骤对应的模板对象 | + +返回值 + +```json +{ + "steps": [ + { + "id": 1, + "step_name": "编译构建-maven", + "stage_id": 2, + "show_index": 0, + "content": "- name: Maven编译\r\n image: arm64v8/maven\r\n", + "created_at": "2021-01-11T09:57:17.000+08:00", + "updated_at": "2021-01-11T09:57:17.000+08:00", + "template": { + "id": 3, + "template_name": "maven", + "stage_type": "build", + "category": "java", + "content": "- name: maven\r\n image: maven:3-jdk-10\r\n", + "created_at": "2021-01-11T17:28:34.000+08:00", + "updated_at": "2021-01-11T17:28:36.000+08:00" + } + } + ] +} +``` + +------ + +#### 流水线阶段步骤新增/更新 + +``` +POST /api/ci/pipelines/{id}/{stage_id}/stage_step +``` + +*示例* + +```bash +curl --location --request POST 'http://localhost:3000/api/ci/pipelines/1/2/stage_step.json' \ +--header 'Content-Type: application/json' \ +--data-raw ' {"steps":[{ + "id":7, + "step_name": "编译构建11-gradle", + "show_index": 1, + "content": "xxxxxxxxxxx", + "template_id":2 +} +] + }' +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ---------------- | ---- | ------ | -------------------------------- | +| steps | 是 | arr | 需要更新step数组 | +| id | 是 | int | 流水线id | +| stage_id | 是 | int | 阶段id | +| id(数组中的id) | 否 | int | 步骤id(存在则更新,不存在新增) | +| step_name | 是 | string | 阶段名称(阶段名-模板名) | +| content | 是 | string | 步骤内容 | +| template_id | 是 | int | 模板id | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 流水线阶段步骤删除 + +``` +DELETE /api/ci/pipelines/{id}/{stage_id}/{step_id}/delete_step +``` + +*示例* + +```bash +curl -X DELETE \ +https://localhost:3000/api/ci/pipelines/1/6/2/delete_stage.json | jq +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| -------- | ---- | ---- | -------- | +| id | 是 | int | 流水线id | +| stage_id | 是 | int | 阶段id | +| step_id | 是 | int | 步骤id | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------- | ------ | ------------ | +| status | int | 状态码 0成功 | +| message | string | 返回消息 | + +返回值 + +```json +{ + "status": 0, + "message": "success" +} +``` + +------ + +#### 阶段模板查询 + +``` +GET /api/ci/templates/templates_by_stage?stage_type={stage_type} +``` + +*示例* + +```bash +curl -X GET \ +http://localhost:3000/api/ci/templates/templates_by_stage.json?stage_type=build | jq +``` + +*请求参数说明:* + +| 参数名 | 必选 | 类型 | 说明 | +| ---------- | ---- | ------ | ------------------------------------- | +| stage_type | 是 | string | 阶段类型:init/build/deploy/customize | + +*返回参数说明:* + +| 参数名 | 类型 | 说明 | +| ------------- | ------ | ---------------- | +| category | string | 分类名称 | +| templates | arr | 分类下的模板列表 | +| id | int | 模板id | +| template_name | string | 模板名称 | +| content | String | 模板内容 | + +返回值 + +```json +[ + { + "category": "java", + "templates": [ + { + "id": 3, + "template_name": "maven", + "stage_type": "build", + "category": "java", + "content": "#maven", + "created_at": "2021-01-11T17:28:34.000+08:00", + "updated_at": "2021-01-11T17:28:36.000+08:00" + }, + { + "id": 4, + "template_name": "gradle", + "stage_type": "build", + "category": "java", + "content": "#gradle", + "created_at": "2021-01-11T17:28:34.000+08:00", + "updated_at": "2021-01-11T17:28:36.000+08:00" + } + ] + }, + { + "category": "c++", + "templates": [ + { + "id": 5, + "template_name": "make", + "stage_type": "build", + "category": "c++", + "content": "#make", + "created_at": "2021-01-11T17:29:17.000+08:00", + "updated_at": "2021-01-11T17:29:18.000+08:00" + } + ] + } +] +``` + +------ + + +#### 解除CI服务器绑定 ``` DELETE /api/users/ci/cloud_account/unbind ``` diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index aeaab0c6..c24ed60a 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -28,8 +28,8 @@ class AttachmentsController < ApplicationController update_downloads(@file) end - - def get_file + + def get_file normal_status(-1, "参数缺失") if params[:download_url].blank? url = URI.encode(params[:download_url].to_s.gsub("http:", "https:")) response = Faraday.get(url) @@ -46,7 +46,7 @@ class AttachmentsController < ApplicationController uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}") raise "未上传文件" unless upload_file - folder = edu_setting('attachment_folder') + folder = file_storage_directory raise "存储目录未定义" unless folder.present? month_folder = current_month_folder @@ -117,7 +117,7 @@ class AttachmentsController < ApplicationController end # 附件为视频时,点击播放 - def preview_attachment + def preview_attachment attachment = Attachment.find_by(id: params[:id]) dir_path = "#{Rails.root}/public/preview" Dir.mkdir(dir_path) unless Dir.exist?(dir_path) @@ -127,10 +127,10 @@ class AttachmentsController < ApplicationController else normal_status(-1, "出现错误,请稍后重试") end - else + else if system("rm -rf #{dir_path}/#{attachment.disk_filename}") normal_status(1, "操作成功") - else + else normal_status(-1, "出现错误,请稍后重试") end end diff --git a/app/controllers/ci/base_controller.rb b/app/controllers/ci/base_controller.rb index c17105cd..5aedbddb 100644 --- a/app/controllers/ci/base_controller.rb +++ b/app/controllers/ci/base_controller.rb @@ -63,6 +63,7 @@ class Ci::BaseController < ApplicationController if current.ci_cloud_account.server_type == Ci::CloudAccount::SERVER_TYPE_TRUSTIE connect_to_trustie_ci_database(options) else + options = options.merge(db_name: current.login) connect_to_ci_database(options) end diff --git a/app/controllers/ci/pipelines_controller.rb b/app/controllers/ci/pipelines_controller.rb new file mode 100644 index 00000000..19460fde --- /dev/null +++ b/app/controllers/ci/pipelines_controller.rb @@ -0,0 +1,253 @@ +class Ci::PipelinesController < Ci::BaseController + + before_action :require_login, only: %i[list create] + skip_before_action :connect_to_ci_db + before_action :load_project, only: %i[content create_trustie_pipeline] + before_action :load_repository, only: %i[create_trustie_pipeline] + + # ======流水线相关接口========== # + def list + @pipelines = Ci::Pipeline.where('identifier=?', params[:identifier]) + end + + def create + ActiveRecord::Base.transaction do + pipeline = Ci::Pipeline.new(pipeline_name: params[:pipeline_name], file_name: params[:file_name], login: current_user.login, identifier: params[:identifier]) + pipeline.save! + + # 默认创建四个初始阶段 + init_stages = Ci::PipelineStage::INIT_STAGES + index = 1 + init_stages.each do |type, name| + pipeline.pipeline_stages.build( + stage_name: name, + stage_type: type, + show_index: index + ).save! + index += 1 + end + render_ok({id: pipeline.id}) + end + rescue Exception => ex + render_error(ex.message) + end + + def update + pipeline = Ci::Pipeline.find(params[:id]) + if pipeline + pipeline.update!(pipeline_name: params[:pipeline_name]) + end + render_ok + rescue Exception => ex + render_error(ex.message) + end + + def destroy + pipeline = Ci::Pipeline.find(params[:id]) + if pipeline + pipeline.destroy! + end + render_ok + rescue Exception => ex + render_error(ex.message) + end + + def content + @yaml = "#pipeline \n" + pipeline = Ci::Pipeline.find(params[:id]) + @sync = pipeline.sync + @sha = '' + stages = pipeline.pipeline_stages + if stages && !stages.empty? + init_step = stages.first.pipeline_stage_steps.first + @yaml += init_step.content + "\n" + "steps:\n" + stages = stages.slice(1, stages.size - 1) + unless stages.empty? + stages.each do |stage| + steps = stage.pipeline_stage_steps + next unless steps && !steps.empty? + steps.each do |step| + @yaml += step.content + "\n" + end + end + end + end + if @sync == 1 + @sha = get_pipeline_file_sha(pipeline.file_name) + end + end + + def get_pipeline_file_sha(file_name) + file_path_uri = URI.parse(file_name) + interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: params[:ref] || "master") + if interactor.success? + file = interactor.result + return file['sha'] + end + end + + def create_trustie_pipeline + pipeline = Ci::Pipeline.find(params[:id]) + sha = get_pipeline_file_sha(pipeline.file_name) + if sha + pipeline.update!(sync: 1) + interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, params[:owner], params.merge(identifier: @project.identifier,sha: sha)) + if interactor.success? + render_ok + else + render_error(interactor.error) + end + else + interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params) + if interactor.success? + pipeline.update!(sync: 1) + render_ok + else + render_error(interactor.error) + end + end + end + + def content_params + { + filepath: params[:filepath], + branch: params[:branch], + new_branch: params[:new_branch], + content: params[:content], + message: params[:message], + committer: { + email: current_user.mail, + name: current_user.login + }, + identifier: @project.identifier + } + end + + # =========阶段相关接口========= # + def stages + pipeline_id = params[:id] + @pipeline_name = Ci::Pipeline.find(pipeline_id).pipeline_name + @pipeline_stages = Ci::PipelineStage.where('pipeline_id=?', pipeline_id).order('show_index asc') + end + + def create_stage + ActiveRecord::Base.transaction do + # 修改stage排序 + update_stage_index(params[:id], params[:show_index], 1) + pipeline_stage = Ci::PipelineStage.new(stage_name: params[:stage_name], + stage_type: params[:stage_type].blank? ? 'customize' : params[:stage_type], + pipeline_id: params[:id], show_index: params[:show_index]) + pipeline_stage.save! + render_ok + end + rescue Exception => ex + render_error(ex.message) + end + + def update_stage + pipeline_stage = Ci::PipelineStage.find(params[:stage_id]) + if pipeline_stage + pipeline_stage.update!(stage_name: params[:stage_name]) + end + render_ok + rescue Exception => ex + render_error(ex.message) + end + + def delete_stage + ActiveRecord::Base.transaction do + update_stage_index(params[:id], params[:show_index].to_i, -1) + pipeline_stage = Ci::PipelineStage.find(params[:stage_id]) + if pipeline_stage + pipeline_stage.destroy! + end + render_ok + end + rescue Exception => ex + render_error(ex.message) + end + + def update_stage_index(pipeline_id, show_index, diff) + stages = Ci::Pipeline.find(pipeline_id).pipeline_stages + stages.each do |stage| + if stage.show_index >= show_index + stage.update!(show_index: stage.show_index + diff) + end + end + end + + # ========步骤相关接口========= # + def steps + @stage_type = Ci::PipelineStage.find(params[:stage_id]).stage_type + @pipeline_stage_steps = Ci::PipelineStageStep.where('stage_id=?', params[:stage_id]).order('show_index asc') + end + + def stage_step + ActiveRecord::Base.transaction do + steps = params[:steps] + unless steps.empty? + steps.each do |step| + unless step[:template_id] + render_error("请选择模板!") + return + end + if !step[:id] + step = Ci::PipelineStageStep.new(step_name: step[:step_name], stage_id: params[:stage_id], + template_id: step[:template_id], content: step[:content], show_index: step[:show_index]) + step.save! + else + pipeline_stage_step = Ci::PipelineStageStep.find(step[:id]) + pipeline_stage_step.update(step_name: step[:step_name], content: step[:content], + show_index: step[:show_index], template_id: step[:template_id]) + end + end + end + render_ok + end + rescue Exception => ex + render_error(ex.message) + end + + def create_stage_step + ActiveRecord::Base.transaction do + steps = params[:steps] + unless steps.empty? + steps.each do |step| + step = Ci::PipelineStageStep.new(step_name: step[:step_name], stage_id: params[:stage_id], + template_id: step[:template_id], content: step[:content], show_index: step[:show_index]) + step.save! + end + end + render_ok + end + rescue Exception => ex + render_error(ex.message) + end + + def update_stage_step + ActiveRecord::Base.transaction do + steps = params[:steps] + unless steps.empty? + steps.each do |step| + pipeline_stage_step = Ci::PipelineStageStep.find(step[:id]) + if pipeline_stage_step + pipeline_stage_step.update(step_name: step[:step_name], content: step[:content], template_id: step[:template_id]) + end + end + end + render_ok + end + rescue Exception => ex + render_error(ex.message) + end + + def delete_stage_step + pipeline_stage_step = Ci::PipelineStageStep.find(params[:step_id]) + if pipeline_stage_step + pipeline_stage_step.destroy! + end + render_ok + rescue Exception => ex + render_error(ex.message) + end +end diff --git a/app/controllers/ci/templates_controller.rb b/app/controllers/ci/templates_controller.rb new file mode 100644 index 00000000..c77a9144 --- /dev/null +++ b/app/controllers/ci/templates_controller.rb @@ -0,0 +1,56 @@ +class Ci::TemplatesController < ApplicationController + + def list + @templates = Ci::Template.all + end + + def templates_by_stage + stage_type = params[:stage_type] + if stage_type != Ci::PipelineStage::CUSTOMIZE_STAGE_TYPE + @templates = Ci::Template.where("stage_type = ?", stage_type) + # 根据模板类别分组 + @category_templates = @templates.group_by{ |template| template.category } + else + # 自定义阶段,按阶段分类分类返回模板列表 + @templates = Ci::Template.where("stage_type != ?", Ci::PipelineStage::INIT_STAGE_TYPE) + @category_templates = @templates.group_by{ |template| template.parent_category } + end + end + + def create + template = Ci::Template.new(template_name: params[:template_name], + stage_type: params[:stage_type], + category: params[:category], + parent_category: params[:parent_category], + content: params[:content] + ) + template.save! + render_ok + rescue Exception => ex + render_error(ex.message) + end + + def update + template = Ci::Template.find(params[:id]) + template.update!(template_name: params[:template_name], + stage_type: params[:stage_type], + category: params[:category], + parent_category: params[:parent_category], + content: params[:content] + ) + render_ok + rescue Exception => ex + render_error(ex.message) + end + + def destroy + template = Ci::Template.find(params[:id]) + if template + template.destroy! + end + render_ok + rescue Exception => ex + render_error(ex.message) + end + +end diff --git a/app/controllers/concerns/ci/db_connectable.rb b/app/controllers/concerns/ci/db_connectable.rb index 559384a5..e2c8ffef 100644 --- a/app/controllers/concerns/ci/db_connectable.rb +++ b/app/controllers/concerns/ci/db_connectable.rb @@ -17,7 +17,8 @@ module Ci::DbConnectable password: db_config[:password], port: db_config[:port] } - req_params = req_params.merge(database: "#{current_user.login}_#{db_config[:database]}") unless master_db === true + db_name = options[:db_name].blank? ? current_user.login : options[:db_name] + req_params = req_params.merge(database: "#{db_name}_#{db_config[:database]}") unless master_db === true db_params = Ci::Database.get_connection_params(req_params) @connection = Ci::Database.set_connection(db_params).connection diff --git a/app/controllers/concerns/repository/languages_percentagable.rb b/app/controllers/concerns/repository/languages_percentagable.rb new file mode 100644 index 00000000..620d93bf --- /dev/null +++ b/app/controllers/concerns/repository/languages_percentagable.rb @@ -0,0 +1,19 @@ +module Repository::LanguagesPercentagable + extend ActiveSupport::Concern + + def languages_precentagable + result = Gitea::Repository::Languages::ListService.call(@owner.login, + @repository.identifier, current_user&.gitea_token) + + result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil + end + + # hash eq:{"JavaScript": 301681522,"Ruby": 1444004,"Roff": 578781} + def hash_transform_precentagable(hash) + total_byte_size = hash.values.sum + hash.transform_values { |v| + ActionController::Base.helpers + .number_to_percentage((v * 100.0 / total_byte_size), precision: 1) + } + end +end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index b4d14228..7e708f5e 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -1,6 +1,7 @@ class RepositoriesController < ApplicationController include ApplicationHelper include OperateProjectAbilityAble + include Repository::LanguagesPercentagable before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror] before_action :load_repository @@ -94,11 +95,22 @@ class RepositoriesController < ApplicationController if interactor.success? @file = interactor.result # create_new_pr(params) + #如果是更新流水线文件 + if params[:pipeline_id] + update_pipeline(params[:pipeline_id]) + end else render_error(interactor.error) end end + def update_pipeline(pipeline_id) + pipeline = Ci::Pipeline.find(pipeline_id) + if pipeline + pipeline.update!(sync: 1) + end + end + def update_file interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier)) if interactor.success? @@ -133,6 +145,17 @@ class RepositoriesController < ApplicationController render_ok end + def readme + result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) + + @readme = result[:status] === :success ? result[:body] : nil + render json: @readme + end + + def languages + render json: languages_precentagable + end + private def find_project diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5ad6363a..f0985d54 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,4 +1,5 @@ class UsersController < ApplicationController + include ApplicationHelper include Ci::DbConnectable before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users] @@ -79,7 +80,7 @@ class UsersController < ApplicationController def attachment_show file_name = params[:file_name] - path = params[:path] || edu_setting('attachment_folder') + path = params[:path] || file_storage_directory send_file "#{path}/#{file_name}", :filename => "#{file_name}", :type => 'game', :disposition => 'attachment' #inline can open in browser diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f4ffaf1f..c8409910 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -322,7 +322,15 @@ module ApplicationHelper end def absolute_path(file_path) - File.join(edu_setting('attachment_folder'), file_path) + file_root_directory + File.join(edu_setting('attachment_folder'), file_path) + end + + def file_root_directory + Rails.root.to_s + end + + def file_storage_directory + file_root_directory + edu_setting('attachment_folder') end def local_path(file) diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 10ad1377..07646c07 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -11,7 +11,7 @@ module RepositoriesHelper end def download_type(str) - default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx) + default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx dot) default_type.include?(str&.downcase) end @@ -20,8 +20,8 @@ module RepositoriesHelper default_type.include?(str&.downcase) end - def is_readme_type?(str) - return false if str.blank? + def is_readme?(type, str) + return false if type != 'file' || str.blank? readme_types = ["readme.md", "readme", "readme_en.md", "readme_zh.md", "readme_en", "readme_zh"] readme_types.include?(str.to_s.downcase) end @@ -72,4 +72,15 @@ module RepositoriesHelper def render_format_time_with_date(date) date.to_time.strftime("%Y-%m-%d %H:%M") end + + def decode64_content(entry, owner, repo, ref, path=nil) + if is_readme?(entry['type'], entry['name']) + content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content'] + readme_render_decode64_content(content, path) + else + file_type = entry['name'].to_s.split(".").last + return entry['content'] if download_type(file_type) + render_decode64_content(entry['content']) + end + end end diff --git a/app/jobs/sync_repo_update_time_job.rb b/app/jobs/sync_repo_update_time_job.rb index c2337ab9..4391f2c1 100644 --- a/app/jobs/sync_repo_update_time_job.rb +++ b/app/jobs/sync_repo_update_time_job.rb @@ -11,9 +11,9 @@ class SyncRepoUpdateTimeJob < ApplicationJob private def gitea_repo_updated_at(project) admin = User.where(admin: true).select(:id, :gitea_token, :gitea_uid).last + puts "########## project id: #{project.id}" return nil if project.gpid.blank? - result = Gitea::Repository::GetByIdService.call(project.gpid, admin.gitea_token) result[:status] === :success ? result[:body]['updated_at'] : nil diff --git a/app/libs/base64_image_converter.rb b/app/libs/base64_image_converter.rb index 2dc9aa41..b2563e43 100644 --- a/app/libs/base64_image_converter.rb +++ b/app/libs/base64_image_converter.rb @@ -1,5 +1,7 @@ class Base64ImageConverter - BASE64_HEAD = 'data:image/jpeg;base64,'.freeze + # BASE64_HEAD = 'data:image/jpeg;base64,'.freeze + BASE64_HEAD_ARRAY = ['data:image/jpeg;base64,', 'data:image/jpg;base64,', + 'data:image/png;base64,', 'data:image/gif;base64,'] Error = Class.new(StandardError) OutLimit = Class.new(Error) @@ -27,11 +29,20 @@ class Base64ImageConverter private def valid_base64?(data) - data&.start_with?(BASE64_HEAD) + # data&.start_with?(BASE64_HEAD) + BASE64_HEAD_ARRAY.include? base64_head_data(data) + end + + def base64_head_data(data) + data&.split(',')[0] + ',' + end + + def base64_head(data) + valid_base64?(data) ? base64_head_data(data) : '' end def image_data(data) - data[BASE64_HEAD.size..-1] + data[base64_head(data).size..-1] end def size_limit @@ -74,4 +85,4 @@ class Base64ImageConverter data[0,2] == 0x89.chr + 80.chr end end -end \ No newline at end of file +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb new file mode 100644 index 00000000..f4d5f2da --- /dev/null +++ b/app/models/ci/pipeline.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: ci_pipelines +# +# id :integer not null, primary key +# pipeline_name :string(255) not null +# pipeline_status :string(255) default("unknown"), not null +# file_name :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null +# login :string(255) +# sync :integer default("0"), not null +# identifier :string(255) +# + +class Ci::Pipeline < Ci::LocalBase + validates :pipeline_name, presence: {message: "流水线名称不能为空"} + validates :file_name, presence: {message: "流水线文件名称不能为空"} + validates :identifier, presence: {message: "项目identifier不能为空"} + + has_many :pipeline_stages, -> { reorder(show_index: :asc) }, foreign_key: "pipeline_id", :class_name => 'Ci::PipelineStage', dependent: :destroy + +end diff --git a/app/models/ci/pipeline_stage.rb b/app/models/ci/pipeline_stage.rb new file mode 100644 index 00000000..b34c3e71 --- /dev/null +++ b/app/models/ci/pipeline_stage.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: ci_pipeline_stages +# +# id :integer not null, primary key +# stage_name :string(255) not null +# stage_type :string(255) not null +# pipeline_id :integer not null +# show_index :integer default("0"), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class Ci::PipelineStage < Ci::LocalBase + + validates :stage_name, presence: {message: "阶段名称不能为空"} + validates :stage_type, presence: {message: "阶段类型不能为空"} + + belongs_to :pipeline, foreign_key: :pipeline_id, :class_name => 'Ci::Pipeline' + has_many :pipeline_stage_steps, -> { reorder(show_index: :asc) }, foreign_key: "stage_id", :class_name => 'Ci::PipelineStageStep', dependent: :destroy + + INIT_STAGES = {init:"初始化", build:"编译构建", deploy:"部署", confirm:"确认"}.freeze + CUSTOMIZE_STAGE_TYPE = 'customize' + INIT_STAGE_TYPE = 'init' +end diff --git a/app/models/ci/pipeline_stage_step.rb b/app/models/ci/pipeline_stage_step.rb new file mode 100644 index 00000000..48a6b332 --- /dev/null +++ b/app/models/ci/pipeline_stage_step.rb @@ -0,0 +1,22 @@ +# == Schema Information +# +# Table name: ci_pipeline_stage_steps +# +# id :integer not null, primary key +# step_name :string(255) not null +# stage_id :integer not null +# template_id :integer +# content :text(65535) +# show_index :integer default("0"), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class Ci::PipelineStageStep < Ci::LocalBase + + validates :step_name, presence: {message: "步骤名称不能为空"} + validates :stage_id, presence: {message: "阶段id不能为空"} + + belongs_to :pipeline_stage, foreign_key: :stage_id, :class_name => 'Ci::PipelineStage' + +end diff --git a/app/models/ci/template.rb b/app/models/ci/template.rb new file mode 100644 index 00000000..e7be34a3 --- /dev/null +++ b/app/models/ci/template.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: ci_templates +# +# id :integer not null, primary key +# template_name :string(255) not null +# stage_type :string(255) not null +# category :string(255) not null +# content :text(65535) not null +# created_at :datetime not null +# updated_at :datetime not null +# parent_category :string(255) +# +# Indexes +# +# index_ci_templates_on_stage_type (stage_type) +# + +class Ci::Template < Ci::LocalBase + validates :template_name, presence: {message: "模板名称不能为空"} + validates :stage_type, presence: {message: "阶段类型不能为空"} + validates :category, presence: {message: "模板类型不能为空"} +end diff --git a/app/models/project.rb b/app/models/project.rb index 6a5e9545..6699ad15 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -128,7 +128,7 @@ class Project < ApplicationRecord end def members_user_infos - members.joins(:roles).where("roles.name in ('Manager', 'Developer')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User") + members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User") # members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname") # .pluck("users.id", "users.login","users.lastname", "users.firstname") end diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb index 728a9809..1e9de823 100644 --- a/app/services/gitea/client_service.rb +++ b/app/services/gitea/client_service.rb @@ -178,8 +178,10 @@ class Gitea::ClientService < ApplicationService def get_body_by_status(status, body) body, message = case status + when 401 then [nil, "401"] when 404 then [nil, "404"] when 403 then [nil, "403"] + when 500 then [nil, "500"] else if body.present? body = JSON.parse(body) @@ -198,7 +200,7 @@ class Gitea::ClientService < ApplicationService end def fix_body(body) - return [body, nil] if body.is_a? Array + return [body, nil] if body.is_a?(Array) || body.is_a?(Hash) body['message'].blank? ? [body, nil] : [nil, body['message']] end diff --git a/app/services/gitea/repository/languages/list_service.rb b/app/services/gitea/repository/languages/list_service.rb new file mode 100644 index 00000000..88d5dcbe --- /dev/null +++ b/app/services/gitea/repository/languages/list_service.rb @@ -0,0 +1,39 @@ +# Get languages and number of bytes of code written +class Gitea::Repository::Languages::ListService < Gitea::ClientService + attr_reader :owner, :repo, :token + + # owner: owner of the repo + # repo: the name of repository + # token: token of gitea user + # eq: Gitea::Repository::Languages::ListService.call(@owner.identifier, + # @project.identifier, current_user&.gitea_token) + def initialize(owner, repo, token) + @owner = owner + @repo = repo + @args = token + end + + def call + response = get(url, params) + + status, message, body = render_response(response) + json_format(status, message, body) + end + + private + def params + {}.merge(token: token) + end + + def url + "/repos/#{owner}/#{repo}/languages".freeze + end + + def json_format(status, message, body) + case status + when 200 then success(body) + else + error(message, status) + end + end +end diff --git a/app/services/gitea/repository/readme/get_service.rb b/app/services/gitea/repository/readme/get_service.rb new file mode 100644 index 00000000..48e2d447 --- /dev/null +++ b/app/services/gitea/repository/readme/get_service.rb @@ -0,0 +1,39 @@ +# Gets the preferred README for a repository. +class Gitea::Repository::Readme::GetService < Gitea::ClientService + attr_reader :owner, :repo, :ref, :token + + # owner: owner of the repo + # repo: name of the repo + # name: The name of the commit/branch/tag. Default: the repository’s default branch (usually master) + # eg: + # Gitea::Repository::Readme::GetService.call(user.login, repo.identifier, ref, user.gitea_token) + def initialize(owner, repo, ref, token=nil) + @owner = owner + @repo = repo + @ref = ref || 'master' + @token = token + end + + def call + response = get(url, params) + status, message, body = render_response(response) + json_format(status, message, body) + end + + private + def params + Hash.new.merge(token: token, ref: ref) + end + + def url + "/repos/#{owner}/#{repo}/readme".freeze + end + + def json_format(status, message, body) + case status + when 200 then success(body) + when 404 then error(message, 404) + else error(message, status) + end + end +end diff --git a/app/views/ci/pipeline_stage_steps/_list.json.jbuilder b/app/views/ci/pipeline_stage_steps/_list.json.jbuilder new file mode 100644 index 00000000..13378397 --- /dev/null +++ b/app/views/ci/pipeline_stage_steps/_list.json.jbuilder @@ -0,0 +1,11 @@ +json.id pipeline_stage_step.id +json.step_name pipeline_stage_step.step_name +json.stage_id pipeline_stage_step.stage_id +json.show_index pipeline_stage_step.show_index +json.content pipeline_stage_step.content +json.template_id pipeline_stage_step.template_id +json.category stage_type == 'customize' ? Ci::Template.find(pipeline_stage_step.template_id).parent_category : Ci::Template.find(pipeline_stage_step.template_id).category +json.created_at pipeline_stage_step.created_at +json.updated_at pipeline_stage_step.updated_at + + diff --git a/app/views/ci/pipeline_stages/_list.json.jbuilder b/app/views/ci/pipeline_stages/_list.json.jbuilder new file mode 100644 index 00000000..a0c8b314 --- /dev/null +++ b/app/views/ci/pipeline_stages/_list.json.jbuilder @@ -0,0 +1,9 @@ +json.id pipeline_stage.id +json.stage_name pipeline_stage.stage_name +json.stage_type pipeline_stage.stage_type +json.pipeline_id pipeline_stage.pipeline_id +json.pipeline_name pipeline_name +json.show_index pipeline_stage.show_index +json.created_at pipeline_stage.created_at +json.updated_at pipeline_stage.updated_at + diff --git a/app/views/ci/pipelines/_list.json.jbuilder b/app/views/ci/pipelines/_list.json.jbuilder new file mode 100644 index 00000000..0113da58 --- /dev/null +++ b/app/views/ci/pipelines/_list.json.jbuilder @@ -0,0 +1,8 @@ +json.id pipeline.id +json.pipeline_name pipeline.pipeline_name +json.pipeline_status pipeline.pipeline_status +json.file_name pipeline.file_name +json.sync pipeline.sync +json.identifier pipeline.identifier +json.created_at pipeline.created_at.strftime("%Y-%m-%d %H:%M:%S") +json.updated_at pipeline.updated_at.strftime("%Y-%m-%d %H:%M:%S") diff --git a/app/views/ci/pipelines/content.json.jbuilder b/app/views/ci/pipelines/content.json.jbuilder new file mode 100644 index 00000000..60d211b9 --- /dev/null +++ b/app/views/ci/pipelines/content.json.jbuilder @@ -0,0 +1,3 @@ +json.content @yaml +json.sync @sync +json.sha @sha \ No newline at end of file diff --git a/app/views/ci/pipelines/list.json.jbuilder b/app/views/ci/pipelines/list.json.jbuilder new file mode 100644 index 00000000..7cfbeb1a --- /dev/null +++ b/app/views/ci/pipelines/list.json.jbuilder @@ -0,0 +1,3 @@ +json.pipelines @pipelines do |pipeline| + json.partial! "/ci/pipelines/list", pipeline: pipeline +end \ No newline at end of file diff --git a/app/views/ci/pipelines/stages.json.jbuilder b/app/views/ci/pipelines/stages.json.jbuilder new file mode 100644 index 00000000..74f27358 --- /dev/null +++ b/app/views/ci/pipelines/stages.json.jbuilder @@ -0,0 +1,3 @@ +json.stages @pipeline_stages do |pipeline_stage| + json.partial! "/ci/pipeline_stages/list", pipeline_stage: pipeline_stage, pipeline_name: @pipeline_name +end \ No newline at end of file diff --git a/app/views/ci/pipelines/steps.json.jbuilder b/app/views/ci/pipelines/steps.json.jbuilder new file mode 100644 index 00000000..e2421e2a --- /dev/null +++ b/app/views/ci/pipelines/steps.json.jbuilder @@ -0,0 +1,3 @@ +json.steps @pipeline_stage_steps do |pipeline_stage_step| + json.partial! "/ci/pipeline_stage_steps/list", pipeline_stage_step: pipeline_stage_step, stage_type: @stage_type +end \ No newline at end of file diff --git a/app/views/ci/templates/_list.json.jbuilder b/app/views/ci/templates/_list.json.jbuilder new file mode 100644 index 00000000..1aff25a4 --- /dev/null +++ b/app/views/ci/templates/_list.json.jbuilder @@ -0,0 +1,8 @@ +json.id template.id +json.template_name template.template_name +json.stage_type template.stage_type +json.category template.category +json.content template.content +json.created_at template.created_at +json.updated_at template.updated_at + diff --git a/app/views/ci/templates/_templates_by_stage.json.jbuilder b/app/views/ci/templates/_templates_by_stage.json.jbuilder new file mode 100644 index 00000000..141e8d5b --- /dev/null +++ b/app/views/ci/templates/_templates_by_stage.json.jbuilder @@ -0,0 +1,4 @@ +json.category category +json.templates templates do |template| + json.partial! "/ci/templates/list", template: template +end \ No newline at end of file diff --git a/app/views/ci/templates/list.json.jbuilder b/app/views/ci/templates/list.json.jbuilder new file mode 100644 index 00000000..3b463057 --- /dev/null +++ b/app/views/ci/templates/list.json.jbuilder @@ -0,0 +1,3 @@ +json.templates @templates do |template| + json.partial! "/ci/templates/list", template: template +end \ No newline at end of file diff --git a/app/views/ci/templates/templates_by_stage.json.jbuilder b/app/views/ci/templates/templates_by_stage.json.jbuilder new file mode 100644 index 00000000..89820794 --- /dev/null +++ b/app/views/ci/templates/templates_by_stage.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @category_templates do |category, templates| + json.partial! "/ci/templates/templates_by_stage", category: category, templates: templates +end \ No newline at end of file diff --git a/app/views/repositories/_last_commit.json.jbuilder b/app/views/repositories/_last_commit.json.jbuilder index 3c218463..0c312738 100644 --- a/app/views/repositories/_last_commit.json.jbuilder +++ b/app/views/repositories/_last_commit.json.jbuilder @@ -1,7 +1,9 @@ -json.commit do - json.message entry['latest_commit']['message'] - json.sha entry['latest_commit']['sha'] - json.created_at render_format_time_with_unix(entry['latest_commit']['created_at'].to_i) - json.time_from_now time_from_now(render_format_time_with_unix(entry['latest_commit']['created_at'].to_i)) - json.created_at_unix entry['latest_commit']['created_at'] +if latest_commit.blank? + json.nil! +else + json.message latest_commit['message'] + json.sha latest_commit['sha'] + json.created_at render_format_time_with_unix(latest_commit['created_at'].to_i) + json.time_from_now time_from_now(render_format_time_with_unix(latest_commit['created_at'].to_i)) + json.created_at_unix latest_commit['created_at'] end diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index ab9910dd..ec2a045b 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -8,18 +8,15 @@ if @project.forge? json.path entry['path'] json.type entry['type'] json.size entry['size'] - json.content entry['content'].present? && !direct_download ? render_decode64_content(entry['content']) : "" + + json.content decode64_content(entry, @owner, @repository, @ref) json.target entry['target'] json.download_url entry['download_url'] json.direct_download direct_download json.image_type image_type - json.is_readme_file is_readme_type?(file_name) - if entry['latest_commit'] - if entry['type'] != 'file' - json.partial! 'last_commit', entry: entry - else - json.commit nil - end + json.is_readme_file is_readme?(entry['type'], entry['name']) + json.commit do + json.partial! 'last_commit', latest_commit: entry['latest_commit'] end end @@ -37,8 +34,11 @@ if @project.educoder? json.direct_download false json.image_type false json.is_readme_file false - if entry['latest_commit'] - # json.partial! 'last_commit', entry: entry - json.partial! 'repositories/simple_entry', locals: { entry: entry } + json.commit do + json.message entry['title'] + json.time_from_now entry['time'] + json.sha nil + json.created_at_unix nil + json.created_at nil end end diff --git a/app/views/repositories/entries.json.jbuilder b/app/views/repositories/entries.json.jbuilder index 20fecec3..9efa0c11 100644 --- a/app/views/repositories/entries.json.jbuilder +++ b/app/views/repositories/entries.json.jbuilder @@ -51,20 +51,11 @@ if @project.forge? json.sha entry['sha'] json.type entry['type'] json.size entry['size'] - content = - if is_readme_type?(entry['name']) - is_readme_file = true - content = Gitea::Repository::Entries::GetService.call(@owner, @project.identifier, entry['name'], ref: @ref)['content'] - readme_render_decode64_content(content, @path) - else - is_readme_file = false - entry['content'] - end - json.is_readme_file is_readme_file - json.content content + json.is_readme_file is_readme?(entry['type'], entry['name']) + json.content decode64_content(entry, @owner, @repository, @ref, @path) json.target entry['target'] - if entry['latest_commit'] - json.partial! 'last_commit', entry: entry + json.commit do + json.partial! 'last_commit', latest_commit: entry['latest_commit'] end end end diff --git a/config/application.rb b/config/application.rb index a09b08de..62d95dbf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -42,7 +42,7 @@ module Educoderplus allow do origins '*' # location of your api - resource '/*', :headers => :any, :methods => [:get, :post, :delete, :options, :put] + resource '/*', :headers => :any, :methods => [:get, :post, :delete, :options, :put, :patch] end end end diff --git a/config/routes.rb b/config/routes.rb index be1b67f7..00efb4a2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,6 +32,32 @@ Rails.application.routes.draw do end end + resources :templates, only: [:list,:templates_by_stage,:create,:update,:destroy] do + collection do + get :list + get :templates_by_stage + end + end + + resources :pipelines do + collection do + get :list + end + member do + get :content + get :stages + post :create_stage + post :create_trustie_pipeline + delete :delete_stage, :path => ":stage_id/delete_stage", to: 'pipelines#delete_stage' + put :update_stage, :path => ":stage_id/update_stage", to: 'pipelines#update_stage' + get :stage_steps, :path => ":stage_id/steps", to: 'pipelines#steps' + post :create_stage_step, :path => ":stage_id/create_step", to: 'pipelines#create_stage_step' + post :stage_step, :path => ":stage_id/stage_step", to: 'pipelines#stage_step' + delete :delete_stage_step, :path => ":stage_id/:step_id/delete_step", to: 'pipelines#delete_stage_step' + put :update_stage_step, :path => ":stage_id/update_step", to: 'pipelines#update_stage_step' + end + end + # resources :repos, only: :index do # collection do # get 'get_trustie_pipeline', to: 'builds#get_trustie_pipeline', as: 'get_trustie_pipeline' @@ -332,6 +358,8 @@ Rails.application.routes.draw do post :sync_mirror get :top_counts get 'commits/:sha', to: 'repositories#commit', as: 'commit' + get 'readme' + get 'languages' end end diff --git a/db/migrate/20210108015318_create_ci_templates.rb b/db/migrate/20210108015318_create_ci_templates.rb new file mode 100644 index 00000000..80594513 --- /dev/null +++ b/db/migrate/20210108015318_create_ci_templates.rb @@ -0,0 +1,14 @@ +class CreateCiTemplates < ActiveRecord::Migration[5.2] + def change + create_table :ci_templates do |t| + t.string :template_name, null: false, comment: '模板名称' + t.string :stage_type, null: false, comment: '模板所属阶段类型:init/build/deploy/customize/confirm' + t.string :category, null: false, comment: '模板分类' + t.text :content, null: false, comment: '模板yml内容' + + t.timestamps + end + add_index :ci_templates, [:stage_type] + end +end + diff --git a/db/migrate/20210108055023_create_ci_pipelines.rb b/db/migrate/20210108055023_create_ci_pipelines.rb new file mode 100644 index 00000000..d1427820 --- /dev/null +++ b/db/migrate/20210108055023_create_ci_pipelines.rb @@ -0,0 +1,11 @@ +class CreateCiPipelines < ActiveRecord::Migration[5.2] + def change + create_table :ci_pipelines do |t| + t.string :pipeline_name, null: false, comment: '流水线名称' + t.string :pipeline_status, null: false, comment: 'successed/failed/running/errored/pending/killed/unknown' , default: 'unknown' + t.string :file_name, null: false, comment: '文件名称' + + t.timestamps + end + end +end diff --git a/db/migrate/20210108062016_create_ci_pipeline_stages.rb b/db/migrate/20210108062016_create_ci_pipeline_stages.rb new file mode 100644 index 00000000..8f7365a8 --- /dev/null +++ b/db/migrate/20210108062016_create_ci_pipeline_stages.rb @@ -0,0 +1,12 @@ +class CreateCiPipelineStages < ActiveRecord::Migration[5.2] + def change + create_table :ci_pipeline_stages do |t| + t.string :stage_name, null: false, comment: '阶段名称' + t.string :stage_type, null: false, comment: '阶段类型:init/build/deploy/customize/confirm' + t.integer :pipeline_id, null: false, comment: '阶段所属流水线id' + t.integer :show_index, null: false, comment: '阶段排序', default: 0 + + t.timestamps + end + end +end diff --git a/db/migrate/20210111013512_create_ci_pipeline_stage_steps.rb b/db/migrate/20210111013512_create_ci_pipeline_stage_steps.rb new file mode 100644 index 00000000..a4e38e13 --- /dev/null +++ b/db/migrate/20210111013512_create_ci_pipeline_stage_steps.rb @@ -0,0 +1,13 @@ +class CreateCiPipelineStageSteps < ActiveRecord::Migration[5.2] + def change + create_table :ci_pipeline_stage_steps do |t| + t.string :step_name, null: false, comment: '步骤名称' + t.integer :stage_id, null: false, comment: '阶段id' + t.integer :template_id, comment: '模板id' + t.text :content + t.integer :show_index, null: false, comment: '阶段排序', default: 0 + + t.timestamps + end + end +end diff --git a/db/migrate/20210112053516_add_parent_category_to_ci_templates.rb b/db/migrate/20210112053516_add_parent_category_to_ci_templates.rb new file mode 100644 index 00000000..16bcb463 --- /dev/null +++ b/db/migrate/20210112053516_add_parent_category_to_ci_templates.rb @@ -0,0 +1,5 @@ +class AddParentCategoryToCiTemplates < ActiveRecord::Migration[5.2] + def change + add_column :ci_templates, :parent_category, :string + end +end diff --git a/db/migrate/20210118011710_add_login_to_ci_pipelines.rb b/db/migrate/20210118011710_add_login_to_ci_pipelines.rb new file mode 100644 index 00000000..e1e914e5 --- /dev/null +++ b/db/migrate/20210118011710_add_login_to_ci_pipelines.rb @@ -0,0 +1,5 @@ +class AddLoginToCiPipelines < ActiveRecord::Migration[5.2] + def change + add_column :ci_pipelines, :login, :string + end +end diff --git a/db/migrate/20210119025745_add_sync_and_project_id_to_ci_pipelines.rb b/db/migrate/20210119025745_add_sync_and_project_id_to_ci_pipelines.rb new file mode 100644 index 00000000..e0d0d250 --- /dev/null +++ b/db/migrate/20210119025745_add_sync_and_project_id_to_ci_pipelines.rb @@ -0,0 +1,6 @@ +class AddSyncAndProjectIdToCiPipelines < ActiveRecord::Migration[5.2] + def change + add_column :ci_pipelines, :sync, :integer, null: false, comment: '0 未同步到gitea,1 已同步', default: 0 + add_column :ci_pipelines, :identifier, :string + end +end \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 353407a4..dd8544fd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -3445,4 +3445,12 @@ INSERT INTO `roles` (`id`, `name`, `position`, `assignable`, `builtin`, `permiss (15, 'Contestant', 13, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :select_contest_modules\n- :quote_project\n- :contest_attachments_download\n- :notificationcomment_contestnotifications\n- :add_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :browse_repository\n- :view_changesets\n', 'default'); COMMIT; +-- ---------------------------- +-- Records of ci_templates +-- ---------------------------- +BEGIN; +INSERT INTO `ci_templates` VALUES (2,'linux/amd64','init','初始化','kind: pipeline\r\ntype: docker\r\nname: default\r\nplatform:\r\n os: linux\r\n arch: amd64','2021-01-12 02:44:23','2021-01-12 02:44:23',NULL),(3,'linux/arm64','init','初始化','kind: pipeline\r\ntype: docker\r\nname: default\r\nplatform:\r\n os: linux\r\n arch: arm64','2021-01-12 02:45:17','2021-01-12 02:45:17',NULL),(4,'maven','build','Java','- name: maven\r\n image: maven:3-jdk-10\r\n commands:\r\n - mvn install -DskipTests=true','2021-01-12 02:53:29','2021-01-12 02:53:29','编译构建'),(5,'maven单元测试','customize','Java','- name: maven\r\n image: maven:3-jdk-10\r\n commands:\r\n - mvn test','2021-01-12 02:53:29','2021-01-12 02:53:29','单元测试'),(6,'golang单元测试','customize','Golang','- name: golang单元测试\r\n image: golang\r\n commands:\r\n - go test','2021-01-12 03:03:35','2021-01-12 03:03:35','单元测试'),(9,'gradle单元测试','customize','Java','- name: gradle\r\n image: gradle:jdk10\r\n commands:\r\n - gradle test','2021-01-12 03:05:33','2021-01-12 03:05:33','单元测试'),(7,'golang编译','build','Golang','- name: golang编译\r\n image: golang\r\n commands:\r\n - go build','2021-01-12 03:03:35','2021-01-12 03:03:35','编译构建'),(8,'gradle','build','Java','- name: gradle\r\n image: gradle:jdk10\r\n commands:\r\n - gradle build -x test','2021-01-12 03:05:33','2021-01-12 03:05:33','编译构建'),(10,'远程主机部署','deploy','部署','# 根据实际情况修改主机ip、账号、密码\r\n# 需要将软件包与部署脚本提前上传到远程主机(见文件上传模板)\r\n\r\n- name: 远程主机部署\r\n image: appleboy/drone-ssh\r\n settings:\r\n host: 192.168.1.1\r\n username: username\r\n password: \'pasword\'\r\n port: 22\r\n commands:\r\n - chmod +x /home/deploy.sh\r\n - ./home/deploy.sh','2021-01-12 03:32:46','2021-01-12 03:32:46','部署'),(11,'远程命令','customize','工具','- name: 远程命令\r\n image: appleboy/drone-ssh\r\n settings:\r\n host: 192.168.0.1\r\n username: username\r\n password: \'pwd\'\r\n port: 22\r\n script:\r\n - echo \'hello world!\'','2021-01-12 03:40:38','2021-01-12 03:40:38','其他'),(12,'上传文件','customize','工具','# 修改目标服务器的ip、账号密码以及上传路径\r\n# 本模板示例为上传软件包和部署脚本到home目录\r\n\r\n- name: 上传文件\r\n image: appleboy/drone-scp\r\n settings:\r\n host: 192.168.1.1\r\n username: username\r\n password: \'password\'\r\n port: 22\r\n target: /home\r\n source: \r\n - target/*.jar\r\n - deploy.sh','2021-01-12 03:40:55','2021-01-12 03:40:55','其他'),(17,'make-c','build','C语言','- name: 编译\r\n image: gcc\r\n commands:\r\n - ./configure\r\n - make','2021-01-15 01:19:38','2021-01-15 01:19:38','编译构建'),(19,'make-c++','build','C++','- name: 编译构建\r\n image: gcc\r\n commands:\r\n - ./configure\r\n - make','2021-01-15 01:21:05','2021-01-15 01:21:05','编译构建'),(20,'python','build','Python','- name: 编译构建\r\n image: python\r\n commands:\r\n - pip install -r requirements.txt','2021-01-15 01:22:36','2021-01-15 01:22:36','编译构建'),(21,'Docker镜像构建','build','Docker镜像构建','# 构建Docker镜像并推送到仓库\r\n# 定义镜像Hub路径以及账号密码\r\n- name: Docker镜像构建\r\n image: plugins/docker\r\n settings:\r\n username: username\r\n password: pwd\r\n repo: repoUrl\r\n tags: latest','2021-01-15 01:23:16','2021-01-15 01:23:16','编译构建'),(22,'空白模板','customize','customize','','2021-01-15 02:53:02','2021-01-15 02:53:02','其他'),(23,'空白模板','build','自定义','','2021-01-15 03:27:33','2021-01-15 03:27:33','编译构建'); +COMMIT; + + SET FOREIGN_KEY_CHECKS = 1;