| 1 | <% content_for :title, "CI/CD · #{@repository.full_name}" %> |
| 2 | <%= render "shared/repository_social", repository: @repository %> |
| 3 | |
| 4 | <div class="border-b border-surface-600 bg-surface-800"> |
| 5 | <div class="max-w-6xl mx-auto px-4 sm:px-6 pt-6 pb-0"> |
| 6 | <div class="flex items-center gap-2 text-sm mb-4"> |
| 7 | <svg class="w-4 h-4 text-gray-500" viewBox="0 0 16 16" fill="currentColor"> |
| 8 | <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8Z"/> |
| 9 | </svg> |
| 10 | <%= link_to "@#{@owner.username}", user_profile_path(@owner.username), |
| 11 | class: "text-brand-500 hover:underline font-medium" %> |
| 12 | <span class="text-gray-500">/</span> |
| 13 | <%= link_to @repository.name, repository_path(@owner.username, @repository.name), |
| 14 | class: "text-brand-500 hover:underline font-semibold" %> |
| 15 | <% if @repository.is_private %> |
| 16 | <span class="badge-gray ml-1">Private</span> |
| 17 | <% end %> |
| 18 | </div> |
| 19 | |
| 20 | <% if @repository.description.present? %> |
| 21 | <p class="text-sm text-gray-400 mb-4"><%= @repository.description %></p> |
| 22 | <% end %> |
| 23 | |
| 24 | <div class="flex items-center gap-0 -mb-px"> |
| 25 | <%= link_to repository_path(@owner.username, @repository.name), |
| 26 | class: "tab-item flex items-center gap-1.5" do %> |
| 27 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 28 | <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8Z"/> |
| 29 | </svg> |
| 30 | Code |
| 31 | <% end %> |
| 32 | <% if @repository.initialized? %> |
| 33 | <%= link_to repository_commits_path(@owner.username, @repository.name, @repository.default_branch), |
| 34 | class: "tab-item flex items-center gap-1.5" do %> |
| 35 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 36 | <path d="M11.93 8.5a4.002 4.002 0 0 1-7.86 0H.75a.75.75 0 0 1 0-1.5h3.32a4.002 4.002 0 0 1 7.86 0h3.32a.75.75 0 0 1 0 1.5Zm-1.43-.75a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"/> |
| 37 | </svg> |
| 38 | Commits |
| 39 | <% if @commit_count&.> 0 %> |
| 40 | <span class="badge-gray text-xs"><%= number_with_delimiter(@commit_count) %></span> |
| 41 | <% end %> |
| 42 | <% end %> |
| 43 | <% end %> |
| 44 | <%= link_to repository_pull_requests_path(@owner.username, @repository.name), |
| 45 | class: "tab-item flex items-center gap-1.5" do %> |
| 46 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 47 | <path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM12 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/> |
| 48 | </svg> |
| 49 | Pulls |
| 50 | <% if @repository.open_pull_requests_count > 0 %> |
| 51 | <span class="badge-gray text-xs"><%= number_with_delimiter(@repository.open_pull_requests_count) %></span> |
| 52 | <% end %> |
| 53 | <% end %> |
| 54 | <div class="tab-item active flex items-center gap-1.5"> |
| 55 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 56 | <path d="M4.75 4.5C2.679 4.5 1 5.958 1 7.75S2.679 11 4.75 11c1.028 0 1.948-.36 2.75-1.242l.652-.716.696.672C10.023 10.849 10.956 11 11.25 11 13.321 11 15 9.542 15 7.75S13.321 4.5 11.25 4.5c-1.028 0-1.948.36-2.75 1.242l-.652.716-.696-.672C5.977 4.651 5.044 4.5 4.75 4.5Zm0 1.5c.483 0 .943.152 1.36.556l.562.541-.562.618C5.693 8.119 5.233 8.5 4.75 8.5c-1.241 0-2.25-.672-2.25-1.5S3.509 6 4.75 6Zm6.5 0c1.241 0 2.25.672 2.25 1.5S12.491 9 11.25 9c-.483 0-.943-.152-1.36-.556l-.562-.541.562-.618c.417-.404.877-.785 1.36-.785Z"/> |
| 57 | </svg> |
| 58 | CI/CD |
| 59 | <% if @commit_count&.> 0 %> |
| 60 | <span class="badge-gray text-xs"><%= number_with_delimiter(@commit_count) %></span> |
| 61 | <% end %> |
| 62 | </div> |
| 63 | </div> |
| 64 | </div> |
| 65 | </div> |
| 66 | |
| 67 | <div class="max-w-6xl mx-auto px-4 sm:px-6 py-6"> |
| 68 | <div class="grid gap-6 lg:grid-cols-[1.35fr_0.95fr]"> |
| 69 | <div class="space-y-6"> |
| 70 | <div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4"> |
| 71 | <div class="card p-5"> |
| 72 | <p class="text-xs uppercase tracking-[0.18em] text-gray-500">Success Rate</p> |
| 73 | <p class="mt-3 text-2xl font-semibold text-gray-100"><%= @pipeline_summary[:success_rate] %></p> |
| 74 | <p class="mt-1 text-xs text-emerald-400">+1.2% in the last 7 days</p> |
| 75 | </div> |
| 76 | <div class="card p-5"> |
| 77 | <p class="text-xs uppercase tracking-[0.18em] text-gray-500">Avg Duration</p> |
| 78 | <p class="mt-3 text-2xl font-semibold text-gray-100"><%= @pipeline_summary[:avg_duration] %></p> |
| 79 | <p class="mt-1 text-xs text-gray-400">From build to deploy</p> |
| 80 | </div> |
| 81 | <div class="card p-5"> |
| 82 | <p class="text-xs uppercase tracking-[0.18em] text-gray-500">Runs This Week</p> |
| 83 | <p class="mt-3 text-2xl font-semibold text-gray-100"><%= @pipeline_summary[:weekly_runs] %></p> |
| 84 | <p class="mt-1 text-xs text-gray-400">Across all branches</p> |
| 85 | </div> |
| 86 | <div class="card p-5"> |
| 87 | <p class="text-xs uppercase tracking-[0.18em] text-gray-500">Last Deploy</p> |
| 88 | <p class="mt-3 text-2xl font-semibold text-gray-100"><%= @pipeline_summary[:last_deploy] %></p> |
| 89 | <p class="mt-1 text-xs text-brand-400">Production is live</p> |
| 90 | </div> |
| 91 | </div> |
| 92 | |
| 93 | <div class="card overflow-hidden"> |
| 94 | <div class="flex items-center justify-between gap-4 border-b border-surface-600 px-5 py-4 bg-surface-600"> |
| 95 | <div> |
| 96 | <h2 class="text-sm font-semibold text-gray-100">Recent Pipeline Runs</h2> |
| 97 | <p class="mt-1 text-xs text-gray-400">Latest activity for <span class="text-gray-200"><%= @repository.full_name %></span></p> |
| 98 | </div> |
| 99 | <span class="badge-gray text-xs"><%= @branch %></span> |
| 100 | </div> |
| 101 | |
| 102 | <div> |
| 103 | <% @pipeline_runs.each do |run| %> |
| 104 | <% status_classes = case run[:status] |
| 105 | when "passed" then "bg-emerald-500/12 text-emerald-300 ring-1 ring-inset ring-emerald-500/20" |
| 106 | when "running" then "bg-sky-500/12 text-sky-300 ring-1 ring-inset ring-sky-500/20" |
| 107 | else "bg-rose-500/12 text-rose-300 ring-1 ring-inset ring-rose-500/20" |
| 108 | end %> |
| 109 | <div class="flex items-center justify-between gap-4 border-b border-surface-600/80 px-5 py-4 last:border-b-0"> |
| 110 | <div class="min-w-0"> |
| 111 | <div class="flex items-center gap-3"> |
| 112 | <p class="text-sm font-medium text-gray-100"><%= run[:name] %></p> |
| 113 | <span class="rounded-full px-2 py-0.5 text-[11px] font-medium <%= status_classes %>"><%= run[:status].capitalize %></span> |
| 114 | </div> |
| 115 | <p class="mt-1 text-xs text-gray-400"> |
| 116 | <span class="font-mono text-brand-400"><%= run[:sha] %></span> |
| 117 | on <span class="text-gray-200"><%= run[:ref] %></span> |
| 118 | by <span class="text-gray-200"><%= run[:actor] %></span> |
| 119 | </p> |
| 120 | </div> |
| 121 | <div class="shrink-0 text-right text-xs text-gray-400"> |
| 122 | <p><%= run[:duration] %></p> |
| 123 | <p class="mt-1 text-gray-500"><%= run[:started_at] %></p> |
| 124 | </div> |
| 125 | </div> |
| 126 | <% end %> |
| 127 | </div> |
| 128 | </div> |
| 129 | </div> |
| 130 | |
| 131 | <div class="space-y-6"> |
| 132 | <div class="card overflow-hidden"> |
| 133 | <div class="border-b border-surface-600 px-5 py-4 bg-surface-600"> |
| 134 | <h2 class="text-sm font-semibold text-gray-100">Environments</h2> |
| 135 | </div> |
| 136 | <div class="space-y-0"> |
| 137 | <% @environments.each do |environment| %> |
| 138 | <% env_classes = case environment[:status] |
| 139 | when "healthy" then "bg-emerald-400" |
| 140 | when "building" then "bg-sky-400" |
| 141 | else "bg-amber-400" |
| 142 | end %> |
| 143 | <div class="border-b border-surface-600/80 px-5 py-4 last:border-b-0"> |
| 144 | <div class="flex items-center justify-between gap-3"> |
| 145 | <div class="flex items-center gap-3"> |
| 146 | <span class="h-2.5 w-2.5 rounded-full <%= env_classes %>"></span> |
| 147 | <div> |
| 148 | <p class="text-sm font-medium text-gray-100"><%= environment[:name] %></p> |
| 149 | <p class="text-xs text-gray-400"><%= environment[:url] %></p> |
| 150 | </div> |
| 151 | </div> |
| 152 | <span class="badge-gray text-xs"><%= environment[:version] %></span> |
| 153 | </div> |
| 154 | <p class="mt-3 text-xs text-gray-500">Updated <%= environment[:updated_at] %></p> |
| 155 | </div> |
| 156 | <% end %> |
| 157 | </div> |
| 158 | </div> |
| 159 | |
| 160 | <div class="card overflow-hidden"> |
| 161 | <div class="border-b border-surface-600 px-5 py-4 bg-surface-600"> |
| 162 | <h2 class="text-sm font-semibold text-gray-100">Checks</h2> |
| 163 | </div> |
| 164 | <div class="space-y-0"> |
| 165 | <% @checks.each do |check| %> |
| 166 | <% icon = case check[:status] |
| 167 | when "passed" then "text-emerald-400" |
| 168 | when "running" then "text-sky-400" |
| 169 | else "text-rose-400" |
| 170 | end %> |
| 171 | <div class="flex items-start gap-3 border-b border-surface-600/80 px-5 py-4 last:border-b-0"> |
| 172 | <svg class="w-4 h-4 mt-0.5 shrink-0 <%= icon %>" viewBox="0 0 16 16" fill="currentColor"> |
| 173 | <path d="M8 1.5a6.5 6.5 0 1 0 6.5 6.5A6.507 6.507 0 0 0 8 1.5Zm3.03 5.28-3.5 3.75a.75.75 0 0 1-1.08.02L4.72 8.81a.75.75 0 0 1 1.06-1.06l1.18 1.18 2.95-3.17a.75.75 0 1 1 1.12 1Z"/> |
| 174 | </svg> |
| 175 | <div class="min-w-0"> |
| 176 | <div class="flex items-center gap-2"> |
| 177 | <p class="text-sm font-medium text-gray-100"><%= check[:name] %></p> |
| 178 | <span class="text-xs text-gray-500"><%= check[:status] %></span> |
| 179 | </div> |
| 180 | <p class="mt-1 text-xs text-gray-400"><%= check[:detail] %></p> |
| 181 | </div> |
| 182 | </div> |
| 183 | <% end %> |
| 184 | </div> |
| 185 | </div> |
| 186 | |
| 187 | <div class="card p-5"> |
| 188 | <h2 class="text-sm font-semibold text-gray-100">Deployment Policy</h2> |
| 189 | <p class="mt-2 text-sm text-gray-400"> |
| 190 | Pushes to <span class="font-mono text-gray-200">main</span> deploy automatically after all required checks pass. |
| 191 | Release branches require one approval before staging promotion. |
| 192 | </p> |
| 193 | </div> |
| 194 | </div> |
| 195 | </div> |
| 196 | </div> |