| 1 | module main |
| 2 | |
| 3 | import x.json2 as json |
| 4 | import net.http |
| 5 | import os |
| 6 | import git |
| 7 | |
| 8 | struct CiTriggerPayload { |
| 9 | repo_id int |
| 10 | commit_hash string |
| 11 | branch string |
| 12 | repo_path string |
| 13 | yaml_config string |
| 14 | callback_url string |
| 15 | } |
| 16 | |
| 17 | struct CiTriggerResponse { |
| 18 | success bool |
| 19 | result CiTriggerResult |
| 20 | } |
| 21 | |
| 22 | struct CiTriggerResult { |
| 23 | id int |
| 24 | status string |
| 25 | } |
| 26 | |
| 27 | // trigger_ci_if_configured checks if the repo has a .gitly-ci.yml and triggers a CI run |
| 28 | fn (mut app App) trigger_ci_if_configured(repo_id int, branch_name string) { |
| 29 | repo := app.find_repo_by_id(repo_id) or { return } |
| 30 | |
| 31 | if app.config.ci_service_url == '' { |
| 32 | return |
| 33 | } |
| 34 | |
| 35 | // Read .gitly-ci.yml from the repo using git show (works with bare repos) |
| 36 | show_result := git.Git.exec_in_dir(repo.git_dir, ['show', '${branch_name}:.gitly-ci.yml']) |
| 37 | if show_result.exit_code != 0 || show_result.output.trim_space() == '' { |
| 38 | app.info('No .gitly-ci.yml found in ${repo.name}/${branch_name}') |
| 39 | return |
| 40 | } |
| 41 | yaml_config := show_result.output |
| 42 | |
| 43 | app.info('Found .gitly-ci.yml in ${repo.name}/${branch_name}, triggering CI') |
| 44 | app.send_ci_trigger(repo, branch_name, yaml_config) |
| 45 | } |
| 46 | |
| 47 | // trigger_ci_with_config triggers CI with a known YAML config (e.g. when the file was just created via web UI) |
| 48 | fn (mut app App) trigger_ci_with_config(repo_id int, branch_name string, yaml_config string) { |
| 49 | repo := app.find_repo_by_id(repo_id) or { return } |
| 50 | |
| 51 | if app.config.ci_service_url == '' { |
| 52 | return |
| 53 | } |
| 54 | |
| 55 | app.info('Triggering CI for ${repo.name}/${branch_name} with provided config') |
| 56 | app.send_ci_trigger(repo, branch_name, yaml_config) |
| 57 | } |
| 58 | |
| 59 | fn (mut app App) send_ci_trigger(repo Repo, branch_name string, yaml_config string) { |
| 60 | // Get the latest commit hash for this branch |
| 61 | commit_hash := repo.get_last_branch_commit_hash(branch_name) |
| 62 | |
| 63 | // Build callback URL |
| 64 | callback_url := 'http://localhost:${app.port}/api/v1/ci/status' |
| 65 | |
| 66 | // Get the absolute path to the git directory |
| 67 | repo_path := os.real_path(repo.git_dir) |
| 68 | |
| 69 | payload := json.encode(CiTriggerPayload{ |
| 70 | repo_id: repo.id |
| 71 | commit_hash: commit_hash |
| 72 | branch: branch_name |
| 73 | repo_path: repo_path |
| 74 | yaml_config: yaml_config |
| 75 | callback_url: callback_url |
| 76 | }) |
| 77 | |
| 78 | // Record pending status |
| 79 | app.upsert_ci_status(repo.id, commit_hash, branch_name, .pending, 0) or { |
| 80 | app.warn('Failed to create CI status: ${err}') |
| 81 | } |
| 82 | |
| 83 | // Trigger CI service |
| 84 | ci_url := '${app.config.ci_service_url}/api/v1/trigger' |
| 85 | app.info('Posting CI trigger to ${ci_url}') |
| 86 | |
| 87 | response := http.post_json(ci_url, payload) or { |
| 88 | app.warn('Failed to trigger CI: ${err}') |
| 89 | return |
| 90 | } |
| 91 | |
| 92 | if response.status_code == 200 { |
| 93 | result := json.decode[CiTriggerResponse](response.body) or { |
| 94 | app.warn('Failed to parse CI trigger response') |
| 95 | return |
| 96 | } |
| 97 | if result.success { |
| 98 | app.upsert_ci_status(repo.id, commit_hash, branch_name, .pending, result.result.id) or { |
| 99 | app.warn('Failed to update CI status with run id') |
| 100 | } |
| 101 | app.info('CI run ${result.result.id} triggered for ${repo.name}') |
| 102 | } |
| 103 | } else { |
| 104 | app.warn('CI trigger returned status ${response.status_code}: ${response.body}') |
| 105 | } |
| 106 | } |
| 107 | |