feat(ci): store CI runs, jobs, checks and runners, and keep job logs in files
Add the storage schema (proto/ermite/storage/ci.proto), the CI domain types, the ports (CIStore, CILogStore, WorkflowTrigger, CIGit and the inbound RunnerService and RunService), the ermite-kv store with its derived indexes (open runs, ready queue, active jobs, job tokens), run ids and per-workflow run numbers taken under version preconditions, repository deletion cleanup and the retention sweep, and the file log store under $ERMITE_DATA_DIR/ci/logs with per-line timestamps and the continued flag. ermite-actions joins go.mod and ermite-api moves to its main.
- commit
- 9cf140e4e72e50102fcfaaf9ad0513dd9613ab76
- author
- brice · · 2026-10-12 17:27 UTC
- parent
- fc4d564
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
17 files · +8292 -3
A gen/ermite/storage/ci.pb.go +1530 -0
| @@ -0,0 +1,1530 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | // Internal, unversioned storage schema of ermite: CI runs, jobs, checks and | |
| 4 | + | // runners (ADR 0025). Logs are not stored here: they are files under | |
| 5 | + | // $ERMITE_DATA_DIR/ci/logs. | |
| 6 | + | ||
| 7 | + | // Code generated by protoc-gen-go. DO NOT EDIT. | |
| 8 | + | // versions: | |
| 9 | + | // protoc-gen-go v1.36.12 | |
| 10 | + | // protoc (unknown) | |
| 11 | + | // source: ermite/storage/ci.proto | |
| 12 | + | ||
| 13 | + | package storagepb | |
| 14 | + | ||
| 15 | + | import ( | |
| 16 | + | protoreflect "google.golang.org/protobuf/reflect/protoreflect" | |
| 17 | + | protoimpl "google.golang.org/protobuf/runtime/protoimpl" | |
| 18 | + | timestamppb "google.golang.org/protobuf/types/known/timestamppb" | |
| 19 | + | reflect "reflect" | |
| 20 | + | sync "sync" | |
| 21 | + | unsafe "unsafe" | |
| 22 | + | ) | |
| 23 | + | ||
| 24 | + | const ( | |
| 25 | + | // Verify that this generated code is sufficiently up-to-date. | |
| 26 | + | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) | |
| 27 | + | // Verify that runtime/protoimpl is sufficiently up-to-date. | |
| 28 | + | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) | |
| 29 | + | ) | |
| 30 | + | ||
| 31 | + | // CIStatus is where a run, a job or a check is in its life. | |
| 32 | + | type CIStatus int32 | |
| 33 | + | ||
| 34 | + | const ( | |
| 35 | + | CIStatus_CI_STATUS_UNSPECIFIED CIStatus = 0 | |
| 36 | + | CIStatus_CI_STATUS_QUEUED CIStatus = 1 | |
| 37 | + | CIStatus_CI_STATUS_IN_PROGRESS CIStatus = 2 | |
| 38 | + | CIStatus_CI_STATUS_COMPLETED CIStatus = 3 | |
| 39 | + | ) | |
| 40 | + | ||
| 41 | + | // Enum value maps for CIStatus. | |
| 42 | + | var ( | |
| 43 | + | CIStatus_name = map[int32]string{ | |
| 44 | + | 0: "CI_STATUS_UNSPECIFIED", | |
| 45 | + | 1: "CI_STATUS_QUEUED", | |
| 46 | + | 2: "CI_STATUS_IN_PROGRESS", | |
| 47 | + | 3: "CI_STATUS_COMPLETED", | |
| 48 | + | } | |
| 49 | + | CIStatus_value = map[string]int32{ | |
| 50 | + | "CI_STATUS_UNSPECIFIED": 0, | |
| 51 | + | "CI_STATUS_QUEUED": 1, | |
| 52 | + | "CI_STATUS_IN_PROGRESS": 2, | |
| 53 | + | "CI_STATUS_COMPLETED": 3, | |
| 54 | + | } | |
| 55 | + | ) | |
| 56 | + | ||
| 57 | + | func (x CIStatus) Enum() *CIStatus { | |
| 58 | + | p := new(CIStatus) | |
| 59 | + | *p = x | |
| 60 | + | return p | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | func (x CIStatus) String() string { | |
| 64 | + | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | func (CIStatus) Descriptor() protoreflect.EnumDescriptor { | |
| 68 | + | return file_ermite_storage_ci_proto_enumTypes[0].Descriptor() | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | func (CIStatus) Type() protoreflect.EnumType { | |
| 72 | + | return &file_ermite_storage_ci_proto_enumTypes[0] | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | func (x CIStatus) Number() protoreflect.EnumNumber { | |
| 76 | + | return protoreflect.EnumNumber(x) | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | // Deprecated: Use CIStatus.Descriptor instead. | |
| 80 | + | func (CIStatus) EnumDescriptor() ([]byte, []int) { | |
| 81 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{0} | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // CIConclusion is how a run, a job, a step or a check ended. | |
| 85 | + | type CIConclusion int32 | |
| 86 | + | ||
| 87 | + | const ( | |
| 88 | + | CIConclusion_CI_CONCLUSION_UNSPECIFIED CIConclusion = 0 | |
| 89 | + | CIConclusion_CI_CONCLUSION_SUCCESS CIConclusion = 1 | |
| 90 | + | CIConclusion_CI_CONCLUSION_FAILURE CIConclusion = 2 | |
| 91 | + | CIConclusion_CI_CONCLUSION_CANCELLED CIConclusion = 3 | |
| 92 | + | CIConclusion_CI_CONCLUSION_SKIPPED CIConclusion = 4 | |
| 93 | + | CIConclusion_CI_CONCLUSION_TIMED_OUT CIConclusion = 5 | |
| 94 | + | CIConclusion_CI_CONCLUSION_NEUTRAL CIConclusion = 6 | |
| 95 | + | CIConclusion_CI_CONCLUSION_ACTION_REQUIRED CIConclusion = 7 | |
| 96 | + | ) | |
| 97 | + | ||
| 98 | + | // Enum value maps for CIConclusion. | |
| 99 | + | var ( | |
| 100 | + | CIConclusion_name = map[int32]string{ | |
| 101 | + | 0: "CI_CONCLUSION_UNSPECIFIED", | |
| 102 | + | 1: "CI_CONCLUSION_SUCCESS", | |
| 103 | + | 2: "CI_CONCLUSION_FAILURE", | |
| 104 | + | 3: "CI_CONCLUSION_CANCELLED", | |
| 105 | + | 4: "CI_CONCLUSION_SKIPPED", | |
| 106 | + | 5: "CI_CONCLUSION_TIMED_OUT", | |
| 107 | + | 6: "CI_CONCLUSION_NEUTRAL", | |
| 108 | + | 7: "CI_CONCLUSION_ACTION_REQUIRED", | |
| 109 | + | } | |
| 110 | + | CIConclusion_value = map[string]int32{ | |
| 111 | + | "CI_CONCLUSION_UNSPECIFIED": 0, | |
| 112 | + | "CI_CONCLUSION_SUCCESS": 1, | |
| 113 | + | "CI_CONCLUSION_FAILURE": 2, | |
| 114 | + | "CI_CONCLUSION_CANCELLED": 3, | |
| 115 | + | "CI_CONCLUSION_SKIPPED": 4, | |
| 116 | + | "CI_CONCLUSION_TIMED_OUT": 5, | |
| 117 | + | "CI_CONCLUSION_NEUTRAL": 6, | |
| 118 | + | "CI_CONCLUSION_ACTION_REQUIRED": 7, | |
| 119 | + | } | |
| 120 | + | ) | |
| 121 | + | ||
| 122 | + | func (x CIConclusion) Enum() *CIConclusion { | |
| 123 | + | p := new(CIConclusion) | |
| 124 | + | *p = x | |
| 125 | + | return p | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | func (x CIConclusion) String() string { | |
| 129 | + | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | func (CIConclusion) Descriptor() protoreflect.EnumDescriptor { | |
| 133 | + | return file_ermite_storage_ci_proto_enumTypes[1].Descriptor() | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | func (CIConclusion) Type() protoreflect.EnumType { | |
| 137 | + | return &file_ermite_storage_ci_proto_enumTypes[1] | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | func (x CIConclusion) Number() protoreflect.EnumNumber { | |
| 141 | + | return protoreflect.EnumNumber(x) | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | // Deprecated: Use CIConclusion.Descriptor instead. | |
| 145 | + | func (CIConclusion) EnumDescriptor() ([]byte, []int) { | |
| 146 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{1} | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // CIStepState is where a step is in its life. | |
| 150 | + | type CIStepState int32 | |
| 151 | + | ||
| 152 | + | const ( | |
| 153 | + | CIStepState_CI_STEP_STATE_UNSPECIFIED CIStepState = 0 | |
| 154 | + | CIStepState_CI_STEP_STATE_PENDING CIStepState = 1 | |
| 155 | + | CIStepState_CI_STEP_STATE_IN_PROGRESS CIStepState = 2 | |
| 156 | + | CIStepState_CI_STEP_STATE_COMPLETED CIStepState = 3 | |
| 157 | + | ) | |
| 158 | + | ||
| 159 | + | // Enum value maps for CIStepState. | |
| 160 | + | var ( | |
| 161 | + | CIStepState_name = map[int32]string{ | |
| 162 | + | 0: "CI_STEP_STATE_UNSPECIFIED", | |
| 163 | + | 1: "CI_STEP_STATE_PENDING", | |
| 164 | + | 2: "CI_STEP_STATE_IN_PROGRESS", | |
| 165 | + | 3: "CI_STEP_STATE_COMPLETED", | |
| 166 | + | } | |
| 167 | + | CIStepState_value = map[string]int32{ | |
| 168 | + | "CI_STEP_STATE_UNSPECIFIED": 0, | |
| 169 | + | "CI_STEP_STATE_PENDING": 1, | |
| 170 | + | "CI_STEP_STATE_IN_PROGRESS": 2, | |
| 171 | + | "CI_STEP_STATE_COMPLETED": 3, | |
| 172 | + | } | |
| 173 | + | ) | |
| 174 | + | ||
| 175 | + | func (x CIStepState) Enum() *CIStepState { | |
| 176 | + | p := new(CIStepState) | |
| 177 | + | *p = x | |
| 178 | + | return p | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | func (x CIStepState) String() string { | |
| 182 | + | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | func (CIStepState) Descriptor() protoreflect.EnumDescriptor { | |
| 186 | + | return file_ermite_storage_ci_proto_enumTypes[2].Descriptor() | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | func (CIStepState) Type() protoreflect.EnumType { | |
| 190 | + | return &file_ermite_storage_ci_proto_enumTypes[2] | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | func (x CIStepState) Number() protoreflect.EnumNumber { | |
| 194 | + | return protoreflect.EnumNumber(x) | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | // Deprecated: Use CIStepState.Descriptor instead. | |
| 198 | + | func (CIStepState) EnumDescriptor() ([]byte, []int) { | |
| 199 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{2} | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | // CIJobPhase is where a job is for the server. | |
| 203 | + | type CIJobPhase int32 | |
| 204 | + | ||
| 205 | + | const ( | |
| 206 | + | CIJobPhase_CI_JOB_PHASE_UNSPECIFIED CIJobPhase = 0 | |
| 207 | + | // Waits for the jobs it needs, or for a free max-parallel slot. | |
| 208 | + | CIJobPhase_CI_JOB_PHASE_WAITING CIJobPhase = 1 | |
| 209 | + | // Planned and waiting for a runner. | |
| 210 | + | CIJobPhase_CI_JOB_PHASE_READY CIJobPhase = 2 | |
| 211 | + | // Given to a runner that has not acknowledged it yet. | |
| 212 | + | CIJobPhase_CI_JOB_PHASE_ASSIGNED CIJobPhase = 3 | |
| 213 | + | // Acknowledged by its runner. | |
| 214 | + | CIJobPhase_CI_JOB_PHASE_RUNNING CIJobPhase = 4 | |
| 215 | + | // Over. | |
| 216 | + | CIJobPhase_CI_JOB_PHASE_ENDED CIJobPhase = 5 | |
| 217 | + | ) | |
| 218 | + | ||
| 219 | + | // Enum value maps for CIJobPhase. | |
| 220 | + | var ( | |
| 221 | + | CIJobPhase_name = map[int32]string{ | |
| 222 | + | 0: "CI_JOB_PHASE_UNSPECIFIED", | |
| 223 | + | 1: "CI_JOB_PHASE_WAITING", | |
| 224 | + | 2: "CI_JOB_PHASE_READY", | |
| 225 | + | 3: "CI_JOB_PHASE_ASSIGNED", | |
| 226 | + | 4: "CI_JOB_PHASE_RUNNING", | |
| 227 | + | 5: "CI_JOB_PHASE_ENDED", | |
| 228 | + | } | |
| 229 | + | CIJobPhase_value = map[string]int32{ | |
| 230 | + | "CI_JOB_PHASE_UNSPECIFIED": 0, | |
| 231 | + | "CI_JOB_PHASE_WAITING": 1, | |
| 232 | + | "CI_JOB_PHASE_READY": 2, | |
| 233 | + | "CI_JOB_PHASE_ASSIGNED": 3, | |
| 234 | + | "CI_JOB_PHASE_RUNNING": 4, | |
| 235 | + | "CI_JOB_PHASE_ENDED": 5, | |
| 236 | + | } | |
| 237 | + | ) | |
| 238 | + | ||
| 239 | + | func (x CIJobPhase) Enum() *CIJobPhase { | |
| 240 | + | p := new(CIJobPhase) | |
| 241 | + | *p = x | |
| 242 | + | return p | |
| 243 | + | } | |
| 244 | + | ||
| 245 | + | func (x CIJobPhase) String() string { | |
| 246 | + | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | func (CIJobPhase) Descriptor() protoreflect.EnumDescriptor { | |
| 250 | + | return file_ermite_storage_ci_proto_enumTypes[3].Descriptor() | |
| 251 | + | } | |
| 252 | + | ||
| 253 | + | func (CIJobPhase) Type() protoreflect.EnumType { | |
| 254 | + | return &file_ermite_storage_ci_proto_enumTypes[3] | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | func (x CIJobPhase) Number() protoreflect.EnumNumber { | |
| 258 | + | return protoreflect.EnumNumber(x) | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | // Deprecated: Use CIJobPhase.Descriptor instead. | |
| 262 | + | func (CIJobPhase) EnumDescriptor() ([]byte, []int) { | |
| 263 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{3} | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | // CIIsolation is how a runner isolates jobs. | |
| 267 | + | type CIIsolation int32 | |
| 268 | + | ||
| 269 | + | const ( | |
| 270 | + | CIIsolation_CI_ISOLATION_UNSPECIFIED CIIsolation = 0 | |
| 271 | + | CIIsolation_CI_ISOLATION_VM CIIsolation = 1 | |
| 272 | + | CIIsolation_CI_ISOLATION_CONTAINER_SYSBOX CIIsolation = 2 | |
| 273 | + | CIIsolation_CI_ISOLATION_CONTAINER_PRIVILEGED CIIsolation = 3 | |
| 274 | + | ) | |
| 275 | + | ||
| 276 | + | // Enum value maps for CIIsolation. | |
| 277 | + | var ( | |
| 278 | + | CIIsolation_name = map[int32]string{ | |
| 279 | + | 0: "CI_ISOLATION_UNSPECIFIED", | |
| 280 | + | 1: "CI_ISOLATION_VM", | |
| 281 | + | 2: "CI_ISOLATION_CONTAINER_SYSBOX", | |
| 282 | + | 3: "CI_ISOLATION_CONTAINER_PRIVILEGED", | |
| 283 | + | } | |
| 284 | + | CIIsolation_value = map[string]int32{ | |
| 285 | + | "CI_ISOLATION_UNSPECIFIED": 0, | |
| 286 | + | "CI_ISOLATION_VM": 1, | |
| 287 | + | "CI_ISOLATION_CONTAINER_SYSBOX": 2, | |
| 288 | + | "CI_ISOLATION_CONTAINER_PRIVILEGED": 3, | |
| 289 | + | } | |
| 290 | + | ) | |
| 291 | + | ||
| 292 | + | func (x CIIsolation) Enum() *CIIsolation { | |
| 293 | + | p := new(CIIsolation) | |
| 294 | + | *p = x | |
| 295 | + | return p | |
| 296 | + | } | |
| 297 | + | ||
| 298 | + | func (x CIIsolation) String() string { | |
| 299 | + | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |
| 300 | + | } | |
| 301 | + | ||
| 302 | + | func (CIIsolation) Descriptor() protoreflect.EnumDescriptor { | |
| 303 | + | return file_ermite_storage_ci_proto_enumTypes[4].Descriptor() | |
| 304 | + | } | |
| 305 | + | ||
| 306 | + | func (CIIsolation) Type() protoreflect.EnumType { | |
| 307 | + | return &file_ermite_storage_ci_proto_enumTypes[4] | |
| 308 | + | } | |
| 309 | + | ||
| 310 | + | func (x CIIsolation) Number() protoreflect.EnumNumber { | |
| 311 | + | return protoreflect.EnumNumber(x) | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | // Deprecated: Use CIIsolation.Descriptor instead. | |
| 315 | + | func (CIIsolation) EnumDescriptor() ([]byte, []int) { | |
| 316 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{4} | |
| 317 | + | } | |
| 318 | + | ||
| 319 | + | // CIRun is stored under "run/<repo>/<id:020d>". | |
| 320 | + | type CIRun struct { | |
| 321 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 322 | + | Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` | |
| 323 | + | Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` | |
| 324 | + | Number int64 `protobuf:"varint,3,opt,name=number,proto3" json:"number,omitempty"` | |
| 325 | + | Attempt int32 `protobuf:"varint,4,opt,name=attempt,proto3" json:"attempt,omitempty"` | |
| 326 | + | WorkflowPath string `protobuf:"bytes,5,opt,name=workflow_path,json=workflowPath,proto3" json:"workflow_path,omitempty"` | |
| 327 | + | WorkflowName string `protobuf:"bytes,6,opt,name=workflow_name,json=workflowName,proto3" json:"workflow_name,omitempty"` | |
| 328 | + | Title string `protobuf:"bytes,7,opt,name=title,proto3" json:"title,omitempty"` | |
| 329 | + | Event string `protobuf:"bytes,8,opt,name=event,proto3" json:"event,omitempty"` | |
| 330 | + | Ref string `protobuf:"bytes,9,opt,name=ref,proto3" json:"ref,omitempty"` | |
| 331 | + | Sha string `protobuf:"bytes,10,opt,name=sha,proto3" json:"sha,omitempty"` | |
| 332 | + | Actor string `protobuf:"bytes,11,opt,name=actor,proto3" json:"actor,omitempty"` | |
| 333 | + | Status CIStatus `protobuf:"varint,12,opt,name=status,proto3,enum=ermite.storage.CIStatus" json:"status,omitempty"` | |
| 334 | + | Conclusion CIConclusion `protobuf:"varint,13,opt,name=conclusion,proto3,enum=ermite.storage.CIConclusion" json:"conclusion,omitempty"` | |
| 335 | + | Error string `protobuf:"bytes,14,opt,name=error,proto3" json:"error,omitempty"` | |
| 336 | + | CreatedAt *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | |
| 337 | + | StartedAt *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` | |
| 338 | + | CompletedAt *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` | |
| 339 | + | CancelRequested bool `protobuf:"varint,20,opt,name=cancel_requested,json=cancelRequested,proto3" json:"cancel_requested,omitempty"` | |
| 340 | + | CancelRequestedAt *timestamppb.Timestamp `protobuf:"bytes,21,opt,name=cancel_requested_at,json=cancelRequestedAt,proto3" json:"cancel_requested_at,omitempty"` | |
| 341 | + | unknownFields protoimpl.UnknownFields | |
| 342 | + | sizeCache protoimpl.SizeCache | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | func (x *CIRun) Reset() { | |
| 346 | + | *x = CIRun{} | |
| 347 | + | mi := &file_ermite_storage_ci_proto_msgTypes[0] | |
| 348 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 349 | + | ms.StoreMessageInfo(mi) | |
| 350 | + | } | |
| 351 | + | ||
| 352 | + | func (x *CIRun) String() string { | |
| 353 | + | return protoimpl.X.MessageStringOf(x) | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | func (*CIRun) ProtoMessage() {} | |
| 357 | + | ||
| 358 | + | func (x *CIRun) ProtoReflect() protoreflect.Message { | |
| 359 | + | mi := &file_ermite_storage_ci_proto_msgTypes[0] | |
| 360 | + | if x != nil { | |
| 361 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 362 | + | if ms.LoadMessageInfo() == nil { | |
| 363 | + | ms.StoreMessageInfo(mi) | |
| 364 | + | } | |
| 365 | + | return ms | |
| 366 | + | } | |
| 367 | + | return mi.MessageOf(x) | |
| 368 | + | } | |
| 369 | + | ||
| 370 | + | // Deprecated: Use CIRun.ProtoReflect.Descriptor instead. | |
| 371 | + | func (*CIRun) Descriptor() ([]byte, []int) { | |
| 372 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{0} | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | func (x *CIRun) GetRepo() string { | |
| 376 | + | if x != nil { | |
| 377 | + | return x.Repo | |
| 378 | + | } | |
| 379 | + | return "" | |
| 380 | + | } | |
| 381 | + | ||
| 382 | + | func (x *CIRun) GetId() int64 { | |
| 383 | + | if x != nil { | |
| 384 | + | return x.Id | |
| 385 | + | } | |
| 386 | + | return 0 | |
| 387 | + | } | |
| 388 | + | ||
| 389 | + | func (x *CIRun) GetNumber() int64 { | |
| 390 | + | if x != nil { | |
| 391 | + | return x.Number | |
| 392 | + | } | |
| 393 | + | return 0 | |
| 394 | + | } | |
| 395 | + | ||
| 396 | + | func (x *CIRun) GetAttempt() int32 { | |
| 397 | + | if x != nil { | |
| 398 | + | return x.Attempt | |
| 399 | + | } | |
| 400 | + | return 0 | |
| 401 | + | } | |
| 402 | + | ||
| 403 | + | func (x *CIRun) GetWorkflowPath() string { | |
| 404 | + | if x != nil { | |
| 405 | + | return x.WorkflowPath | |
| 406 | + | } | |
| 407 | + | return "" | |
| 408 | + | } | |
| 409 | + | ||
| 410 | + | func (x *CIRun) GetWorkflowName() string { | |
| 411 | + | if x != nil { | |
| 412 | + | return x.WorkflowName | |
| 413 | + | } | |
| 414 | + | return "" | |
| 415 | + | } | |
| 416 | + | ||
| 417 | + | func (x *CIRun) GetTitle() string { | |
| 418 | + | if x != nil { | |
| 419 | + | return x.Title | |
| 420 | + | } | |
| 421 | + | return "" | |
| 422 | + | } | |
| 423 | + | ||
| 424 | + | func (x *CIRun) GetEvent() string { | |
| 425 | + | if x != nil { | |
| 426 | + | return x.Event | |
| 427 | + | } | |
| 428 | + | return "" | |
| 429 | + | } | |
| 430 | + | ||
| 431 | + | func (x *CIRun) GetRef() string { | |
| 432 | + | if x != nil { | |
| 433 | + | return x.Ref | |
| 434 | + | } | |
| 435 | + | return "" | |
| 436 | + | } | |
| 437 | + | ||
| 438 | + | func (x *CIRun) GetSha() string { | |
| 439 | + | if x != nil { | |
| 440 | + | return x.Sha | |
| 441 | + | } | |
| 442 | + | return "" | |
| 443 | + | } | |
| 444 | + | ||
| 445 | + | func (x *CIRun) GetActor() string { | |
| 446 | + | if x != nil { | |
| 447 | + | return x.Actor | |
| 448 | + | } | |
| 449 | + | return "" | |
| 450 | + | } | |
| 451 | + | ||
| 452 | + | func (x *CIRun) GetStatus() CIStatus { | |
| 453 | + | if x != nil { | |
| 454 | + | return x.Status | |
| 455 | + | } | |
| 456 | + | return CIStatus_CI_STATUS_UNSPECIFIED | |
| 457 | + | } | |
| 458 | + | ||
| 459 | + | func (x *CIRun) GetConclusion() CIConclusion { | |
| 460 | + | if x != nil { | |
| 461 | + | return x.Conclusion | |
| 462 | + | } | |
| 463 | + | return CIConclusion_CI_CONCLUSION_UNSPECIFIED | |
| 464 | + | } | |
| 465 | + | ||
| 466 | + | func (x *CIRun) GetError() string { | |
| 467 | + | if x != nil { | |
| 468 | + | return x.Error | |
| 469 | + | } | |
| 470 | + | return "" | |
| 471 | + | } | |
| 472 | + | ||
| 473 | + | func (x *CIRun) GetCreatedAt() *timestamppb.Timestamp { | |
| 474 | + | if x != nil { | |
| 475 | + | return x.CreatedAt | |
| 476 | + | } | |
| 477 | + | return nil | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | func (x *CIRun) GetStartedAt() *timestamppb.Timestamp { | |
| 481 | + | if x != nil { | |
| 482 | + | return x.StartedAt | |
| 483 | + | } | |
| 484 | + | return nil | |
| 485 | + | } | |
| 486 | + | ||
| 487 | + | func (x *CIRun) GetCompletedAt() *timestamppb.Timestamp { | |
| 488 | + | if x != nil { | |
| 489 | + | return x.CompletedAt | |
| 490 | + | } | |
| 491 | + | return nil | |
| 492 | + | } | |
| 493 | + | ||
| 494 | + | func (x *CIRun) GetCancelRequested() bool { | |
| 495 | + | if x != nil { | |
| 496 | + | return x.CancelRequested | |
| 497 | + | } | |
| 498 | + | return false | |
| 499 | + | } | |
| 500 | + | ||
| 501 | + | func (x *CIRun) GetCancelRequestedAt() *timestamppb.Timestamp { | |
| 502 | + | if x != nil { | |
| 503 | + | return x.CancelRequestedAt | |
| 504 | + | } | |
| 505 | + | return nil | |
| 506 | + | } | |
| 507 | + | ||
| 508 | + | // CIRunSource is stored under "runsrc/<run id:020d>": what a run was planned | |
| 509 | + | // from, kept apart so that listing runs does not decode it. | |
| 510 | + | type CIRunSource struct { | |
| 511 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 512 | + | RunId int64 `protobuf:"varint,4,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` | |
| 513 | + | // The workflow file at sha, as is. | |
| 514 | + | WorkflowYaml string `protobuf:"bytes,1,opt,name=workflow_yaml,json=workflowYaml,proto3" json:"workflow_yaml,omitempty"` | |
| 515 | + | // The "github" context of the run as JSON, without "job" and "run_attempt". | |
| 516 | + | GithubJson string `protobuf:"bytes,2,opt,name=github_json,json=githubJson,proto3" json:"github_json,omitempty"` | |
| 517 | + | // Repository of the run: the key of the run record is run/<repo>/<id>, and | |
| 518 | + | // the runner side knows a run by its id only. | |
| 519 | + | Repo string `protobuf:"bytes,3,opt,name=repo,proto3" json:"repo,omitempty"` | |
| 520 | + | unknownFields protoimpl.UnknownFields | |
| 521 | + | sizeCache protoimpl.SizeCache | |
| 522 | + | } | |
| 523 | + | ||
| 524 | + | func (x *CIRunSource) Reset() { | |
| 525 | + | *x = CIRunSource{} | |
| 526 | + | mi := &file_ermite_storage_ci_proto_msgTypes[1] | |
| 527 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 528 | + | ms.StoreMessageInfo(mi) | |
| 529 | + | } | |
| 530 | + | ||
| 531 | + | func (x *CIRunSource) String() string { | |
| 532 | + | return protoimpl.X.MessageStringOf(x) | |
| 533 | + | } | |
| 534 | + | ||
| 535 | + | func (*CIRunSource) ProtoMessage() {} | |
| 536 | + | ||
| 537 | + | func (x *CIRunSource) ProtoReflect() protoreflect.Message { | |
| 538 | + | mi := &file_ermite_storage_ci_proto_msgTypes[1] | |
| 539 | + | if x != nil { | |
| 540 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 541 | + | if ms.LoadMessageInfo() == nil { | |
| 542 | + | ms.StoreMessageInfo(mi) | |
| 543 | + | } | |
| 544 | + | return ms | |
| 545 | + | } | |
| 546 | + | return mi.MessageOf(x) | |
| 547 | + | } | |
| 548 | + | ||
| 549 | + | // Deprecated: Use CIRunSource.ProtoReflect.Descriptor instead. | |
| 550 | + | func (*CIRunSource) Descriptor() ([]byte, []int) { | |
| 551 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{1} | |
| 552 | + | } | |
| 553 | + | ||
| 554 | + | func (x *CIRunSource) GetRunId() int64 { | |
| 555 | + | if x != nil { | |
| 556 | + | return x.RunId | |
| 557 | + | } | |
| 558 | + | return 0 | |
| 559 | + | } | |
| 560 | + | ||
| 561 | + | func (x *CIRunSource) GetWorkflowYaml() string { | |
| 562 | + | if x != nil { | |
| 563 | + | return x.WorkflowYaml | |
| 564 | + | } | |
| 565 | + | return "" | |
| 566 | + | } | |
| 567 | + | ||
| 568 | + | func (x *CIRunSource) GetGithubJson() string { | |
| 569 | + | if x != nil { | |
| 570 | + | return x.GithubJson | |
| 571 | + | } | |
| 572 | + | return "" | |
| 573 | + | } | |
| 574 | + | ||
| 575 | + | func (x *CIRunSource) GetRepo() string { | |
| 576 | + | if x != nil { | |
| 577 | + | return x.Repo | |
| 578 | + | } | |
| 579 | + | return "" | |
| 580 | + | } | |
| 581 | + | ||
| 582 | + | // CIStep is a step of a job. | |
| 583 | + | type CIStep struct { | |
| 584 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 585 | + | Number int32 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` | |
| 586 | + | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | |
| 587 | + | State CIStepState `protobuf:"varint,3,opt,name=state,proto3,enum=ermite.storage.CIStepState" json:"state,omitempty"` | |
| 588 | + | Conclusion CIConclusion `protobuf:"varint,4,opt,name=conclusion,proto3,enum=ermite.storage.CIConclusion" json:"conclusion,omitempty"` | |
| 589 | + | StartedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` | |
| 590 | + | CompletedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` | |
| 591 | + | // Number of the first log line of the step; meaningful with has_lines. The | |
| 592 | + | // end of a step is the start of the next step that has lines, or the line | |
| 593 | + | // count of the job: it is never stored. | |
| 594 | + | LogStart int64 `protobuf:"varint,7,opt,name=log_start,json=logStart,proto3" json:"log_start,omitempty"` | |
| 595 | + | HasLines bool `protobuf:"varint,9,opt,name=has_lines,json=hasLines,proto3" json:"has_lines,omitempty"` | |
| 596 | + | unknownFields protoimpl.UnknownFields | |
| 597 | + | sizeCache protoimpl.SizeCache | |
| 598 | + | } | |
| 599 | + | ||
| 600 | + | func (x *CIStep) Reset() { | |
| 601 | + | *x = CIStep{} | |
| 602 | + | mi := &file_ermite_storage_ci_proto_msgTypes[2] | |
| 603 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 604 | + | ms.StoreMessageInfo(mi) | |
| 605 | + | } | |
| 606 | + | ||
| 607 | + | func (x *CIStep) String() string { | |
| 608 | + | return protoimpl.X.MessageStringOf(x) | |
| 609 | + | } | |
| 610 | + | ||
| 611 | + | func (*CIStep) ProtoMessage() {} | |
| 612 | + | ||
| 613 | + | func (x *CIStep) ProtoReflect() protoreflect.Message { | |
| 614 | + | mi := &file_ermite_storage_ci_proto_msgTypes[2] | |
| 615 | + | if x != nil { | |
| 616 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 617 | + | if ms.LoadMessageInfo() == nil { | |
| 618 | + | ms.StoreMessageInfo(mi) | |
| 619 | + | } | |
| 620 | + | return ms | |
| 621 | + | } | |
| 622 | + | return mi.MessageOf(x) | |
| 623 | + | } | |
| 624 | + | ||
| 625 | + | // Deprecated: Use CIStep.ProtoReflect.Descriptor instead. | |
| 626 | + | func (*CIStep) Descriptor() ([]byte, []int) { | |
| 627 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{2} | |
| 628 | + | } | |
| 629 | + | ||
| 630 | + | func (x *CIStep) GetNumber() int32 { | |
| 631 | + | if x != nil { | |
| 632 | + | return x.Number | |
| 633 | + | } | |
| 634 | + | return 0 | |
| 635 | + | } | |
| 636 | + | ||
| 637 | + | func (x *CIStep) GetName() string { | |
| 638 | + | if x != nil { | |
| 639 | + | return x.Name | |
| 640 | + | } | |
| 641 | + | return "" | |
| 642 | + | } | |
| 643 | + | ||
| 644 | + | func (x *CIStep) GetState() CIStepState { | |
| 645 | + | if x != nil { | |
| 646 | + | return x.State | |
| 647 | + | } | |
| 648 | + | return CIStepState_CI_STEP_STATE_UNSPECIFIED | |
| 649 | + | } | |
| 650 | + | ||
| 651 | + | func (x *CIStep) GetConclusion() CIConclusion { | |
| 652 | + | if x != nil { | |
| 653 | + | return x.Conclusion | |
| 654 | + | } | |
| 655 | + | return CIConclusion_CI_CONCLUSION_UNSPECIFIED | |
| 656 | + | } | |
| 657 | + | ||
| 658 | + | func (x *CIStep) GetStartedAt() *timestamppb.Timestamp { | |
| 659 | + | if x != nil { | |
| 660 | + | return x.StartedAt | |
| 661 | + | } | |
| 662 | + | return nil | |
| 663 | + | } | |
| 664 | + | ||
| 665 | + | func (x *CIStep) GetCompletedAt() *timestamppb.Timestamp { | |
| 666 | + | if x != nil { | |
| 667 | + | return x.CompletedAt | |
| 668 | + | } | |
| 669 | + | return nil | |
| 670 | + | } | |
| 671 | + | ||
| 672 | + | func (x *CIStep) GetLogStart() int64 { | |
| 673 | + | if x != nil { | |
| 674 | + | return x.LogStart | |
| 675 | + | } | |
| 676 | + | return 0 | |
| 677 | + | } | |
| 678 | + | ||
| 679 | + | func (x *CIStep) GetHasLines() bool { | |
| 680 | + | if x != nil { | |
| 681 | + | return x.HasLines | |
| 682 | + | } | |
| 683 | + | return false | |
| 684 | + | } | |
| 685 | + | ||
| 686 | + | // CIJob is stored under "job/<run_id:020d>/<run_attempt:010d>/<job id>". | |
| 687 | + | type CIJob struct { | |
| 688 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 689 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |
| 690 | + | RunId int64 `protobuf:"varint,2,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` | |
| 691 | + | // Attempt in which the job executed (the one of its log). | |
| 692 | + | Attempt int32 `protobuf:"varint,3,opt,name=attempt,proto3" json:"attempt,omitempty"` | |
| 693 | + | Repo string `protobuf:"bytes,4,opt,name=repo,proto3" json:"repo,omitempty"` | |
| 694 | + | Key string `protobuf:"bytes,5,opt,name=key,proto3" json:"key,omitempty"` | |
| 695 | + | Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` | |
| 696 | + | Phase CIJobPhase `protobuf:"varint,7,opt,name=phase,proto3,enum=ermite.storage.CIJobPhase" json:"phase,omitempty"` | |
| 697 | + | Conclusion CIConclusion `protobuf:"varint,8,opt,name=conclusion,proto3,enum=ermite.storage.CIConclusion" json:"conclusion,omitempty"` | |
| 698 | + | Error string `protobuf:"bytes,9,opt,name=error,proto3" json:"error,omitempty"` | |
| 699 | + | Labels []string `protobuf:"bytes,10,rep,name=labels,proto3" json:"labels,omitempty"` | |
| 700 | + | Needs []string `protobuf:"bytes,11,rep,name=needs,proto3" json:"needs,omitempty"` | |
| 701 | + | RunnerId string `protobuf:"bytes,12,opt,name=runner_id,json=runnerId,proto3" json:"runner_id,omitempty"` | |
| 702 | + | RunnerName string `protobuf:"bytes,13,opt,name=runner_name,json=runnerName,proto3" json:"runner_name,omitempty"` | |
| 703 | + | Isolation CIIsolation `protobuf:"varint,14,opt,name=isolation,proto3,enum=ermite.storage.CIIsolation" json:"isolation,omitempty"` | |
| 704 | + | Steps []*CIStep `protobuf:"bytes,15,rep,name=steps,proto3" json:"steps,omitempty"` | |
| 705 | + | CreatedAt *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | |
| 706 | + | StartedAt *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` | |
| 707 | + | CompletedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` | |
| 708 | + | // Position of the job in the workflow file, and of the instance in its | |
| 709 | + | // matrix. A combo of -1 is a job whose matrix is not expanded yet. | |
| 710 | + | Index int32 `protobuf:"varint,19,opt,name=index,proto3" json:"index,omitempty"` | |
| 711 | + | Combo int32 `protobuf:"varint,20,opt,name=combo,proto3" json:"combo,omitempty"` | |
| 712 | + | MatrixJson string `protobuf:"bytes,21,opt,name=matrix_json,json=matrixJson,proto3" json:"matrix_json,omitempty"` | |
| 713 | + | // Number of instances of the job's matrix, for the strategy context. | |
| 714 | + | ComboTotal int32 `protobuf:"varint,22,opt,name=combo_total,json=comboTotal,proto3" json:"combo_total,omitempty"` | |
| 715 | + | TimeoutMinutes int32 `protobuf:"varint,23,opt,name=timeout_minutes,json=timeoutMinutes,proto3" json:"timeout_minutes,omitempty"` | |
| 716 | + | FailFast bool `protobuf:"varint,24,opt,name=fail_fast,json=failFast,proto3" json:"fail_fast,omitempty"` | |
| 717 | + | MaxParallel int32 `protobuf:"varint,25,opt,name=max_parallel,json=maxParallel,proto3" json:"max_parallel,omitempty"` | |
| 718 | + | Outputs map[string]string `protobuf:"bytes,26,rep,name=outputs,proto3" json:"outputs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` | |
| 719 | + | TokenHash []byte `protobuf:"bytes,27,opt,name=token_hash,json=tokenHash,proto3" json:"token_hash,omitempty"` | |
| 720 | + | ReadyAt *timestamppb.Timestamp `protobuf:"bytes,28,opt,name=ready_at,json=readyAt,proto3" json:"ready_at,omitempty"` | |
| 721 | + | AssignedAt *timestamppb.Timestamp `protobuf:"bytes,29,opt,name=assigned_at,json=assignedAt,proto3" json:"assigned_at,omitempty"` | |
| 722 | + | AckedAt *timestamppb.Timestamp `protobuf:"bytes,30,opt,name=acked_at,json=ackedAt,proto3" json:"acked_at,omitempty"` | |
| 723 | + | AssignCancels int32 `protobuf:"varint,31,opt,name=assign_cancels,json=assignCancels,proto3" json:"assign_cancels,omitempty"` | |
| 724 | + | TokenValidUntil *timestamppb.Timestamp `protobuf:"bytes,32,opt,name=token_valid_until,json=tokenValidUntil,proto3" json:"token_valid_until,omitempty"` | |
| 725 | + | CancelReason int32 `protobuf:"varint,33,opt,name=cancel_reason,json=cancelReason,proto3" json:"cancel_reason,omitempty"` | |
| 726 | + | CancelMessage string `protobuf:"bytes,34,opt,name=cancel_message,json=cancelMessage,proto3" json:"cancel_message,omitempty"` | |
| 727 | + | CancelRequestedAt *timestamppb.Timestamp `protobuf:"bytes,35,opt,name=cancel_requested_at,json=cancelRequestedAt,proto3" json:"cancel_requested_at,omitempty"` | |
| 728 | + | // Set when the job is planned: runs-on, name and contexts are evaluated. | |
| 729 | + | Planned bool `protobuf:"varint,37,opt,name=planned,proto3" json:"planned,omitempty"` | |
| 730 | + | // Attempt of the run this record belongs to; it is the key. A job kept by a | |
| 731 | + | // re-run of the failed jobs is copied with the run attempt of the re-run and | |
| 732 | + | // keeps its own attempt. | |
| 733 | + | RunAttempt int32 `protobuf:"varint,38,opt,name=run_attempt,json=runAttempt,proto3" json:"run_attempt,omitempty"` | |
| 734 | + | unknownFields protoimpl.UnknownFields | |
| 735 | + | sizeCache protoimpl.SizeCache | |
| 736 | + | } | |
| 737 | + | ||
| 738 | + | func (x *CIJob) Reset() { | |
| 739 | + | *x = CIJob{} | |
| 740 | + | mi := &file_ermite_storage_ci_proto_msgTypes[3] | |
| 741 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 742 | + | ms.StoreMessageInfo(mi) | |
| 743 | + | } | |
| 744 | + | ||
| 745 | + | func (x *CIJob) String() string { | |
| 746 | + | return protoimpl.X.MessageStringOf(x) | |
| 747 | + | } | |
| 748 | + | ||
| 749 | + | func (*CIJob) ProtoMessage() {} | |
| 750 | + | ||
| 751 | + | func (x *CIJob) ProtoReflect() protoreflect.Message { | |
| 752 | + | mi := &file_ermite_storage_ci_proto_msgTypes[3] | |
| 753 | + | if x != nil { | |
| 754 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 755 | + | if ms.LoadMessageInfo() == nil { | |
| 756 | + | ms.StoreMessageInfo(mi) | |
| 757 | + | } | |
| 758 | + | return ms | |
| 759 | + | } | |
| 760 | + | return mi.MessageOf(x) | |
| 761 | + | } | |
| 762 | + | ||
| 763 | + | // Deprecated: Use CIJob.ProtoReflect.Descriptor instead. | |
| 764 | + | func (*CIJob) Descriptor() ([]byte, []int) { | |
| 765 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{3} | |
| 766 | + | } | |
| 767 | + | ||
| 768 | + | func (x *CIJob) GetId() string { | |
| 769 | + | if x != nil { | |
| 770 | + | return x.Id | |
| 771 | + | } | |
| 772 | + | return "" | |
| 773 | + | } | |
| 774 | + | ||
| 775 | + | func (x *CIJob) GetRunId() int64 { | |
| 776 | + | if x != nil { | |
| 777 | + | return x.RunId | |
| 778 | + | } | |
| 779 | + | return 0 | |
| 780 | + | } | |
| 781 | + | ||
| 782 | + | func (x *CIJob) GetAttempt() int32 { | |
| 783 | + | if x != nil { | |
| 784 | + | return x.Attempt | |
| 785 | + | } | |
| 786 | + | return 0 | |
| 787 | + | } | |
| 788 | + | ||
| 789 | + | func (x *CIJob) GetRepo() string { | |
| 790 | + | if x != nil { | |
| 791 | + | return x.Repo | |
| 792 | + | } | |
| 793 | + | return "" | |
| 794 | + | } | |
| 795 | + | ||
| 796 | + | func (x *CIJob) GetKey() string { | |
| 797 | + | if x != nil { | |
| 798 | + | return x.Key | |
| 799 | + | } | |
| 800 | + | return "" | |
| 801 | + | } | |
| 802 | + | ||
| 803 | + | func (x *CIJob) GetName() string { | |
| 804 | + | if x != nil { | |
| 805 | + | return x.Name | |
| 806 | + | } | |
| 807 | + | return "" | |
| 808 | + | } | |
| 809 | + | ||
| 810 | + | func (x *CIJob) GetPhase() CIJobPhase { | |
| 811 | + | if x != nil { | |
| 812 | + | return x.Phase | |
| 813 | + | } | |
| 814 | + | return CIJobPhase_CI_JOB_PHASE_UNSPECIFIED | |
| 815 | + | } | |
| 816 | + | ||
| 817 | + | func (x *CIJob) GetConclusion() CIConclusion { | |
| 818 | + | if x != nil { | |
| 819 | + | return x.Conclusion | |
| 820 | + | } | |
| 821 | + | return CIConclusion_CI_CONCLUSION_UNSPECIFIED | |
| 822 | + | } | |
| 823 | + | ||
| 824 | + | func (x *CIJob) GetError() string { | |
| 825 | + | if x != nil { | |
| 826 | + | return x.Error | |
| 827 | + | } | |
| 828 | + | return "" | |
| 829 | + | } | |
| 830 | + | ||
| 831 | + | func (x *CIJob) GetLabels() []string { | |
| 832 | + | if x != nil { | |
| 833 | + | return x.Labels | |
| 834 | + | } | |
| 835 | + | return nil | |
| 836 | + | } | |
| 837 | + | ||
| 838 | + | func (x *CIJob) GetNeeds() []string { | |
| 839 | + | if x != nil { | |
| 840 | + | return x.Needs | |
| 841 | + | } | |
| 842 | + | return nil | |
| 843 | + | } | |
| 844 | + | ||
| 845 | + | func (x *CIJob) GetRunnerId() string { | |
| 846 | + | if x != nil { | |
| 847 | + | return x.RunnerId | |
| 848 | + | } | |
| 849 | + | return "" | |
| 850 | + | } | |
| 851 | + | ||
| 852 | + | func (x *CIJob) GetRunnerName() string { | |
| 853 | + | if x != nil { | |
| 854 | + | return x.RunnerName | |
| 855 | + | } | |
| 856 | + | return "" | |
| 857 | + | } | |
| 858 | + | ||
| 859 | + | func (x *CIJob) GetIsolation() CIIsolation { | |
| 860 | + | if x != nil { | |
| 861 | + | return x.Isolation | |
| 862 | + | } | |
| 863 | + | return CIIsolation_CI_ISOLATION_UNSPECIFIED | |
| 864 | + | } | |
| 865 | + | ||
| 866 | + | func (x *CIJob) GetSteps() []*CIStep { | |
| 867 | + | if x != nil { | |
| 868 | + | return x.Steps | |
| 869 | + | } | |
| 870 | + | return nil | |
| 871 | + | } | |
| 872 | + | ||
| 873 | + | func (x *CIJob) GetCreatedAt() *timestamppb.Timestamp { | |
| 874 | + | if x != nil { | |
| 875 | + | return x.CreatedAt | |
| 876 | + | } | |
| 877 | + | return nil | |
| 878 | + | } | |
| 879 | + | ||
| 880 | + | func (x *CIJob) GetStartedAt() *timestamppb.Timestamp { | |
| 881 | + | if x != nil { | |
| 882 | + | return x.StartedAt | |
| 883 | + | } | |
| 884 | + | return nil | |
| 885 | + | } | |
| 886 | + | ||
| 887 | + | func (x *CIJob) GetCompletedAt() *timestamppb.Timestamp { | |
| 888 | + | if x != nil { | |
| 889 | + | return x.CompletedAt | |
| 890 | + | } | |
| 891 | + | return nil | |
| 892 | + | } | |
| 893 | + | ||
| 894 | + | func (x *CIJob) GetIndex() int32 { | |
| 895 | + | if x != nil { | |
| 896 | + | return x.Index | |
| 897 | + | } | |
| 898 | + | return 0 | |
| 899 | + | } | |
| 900 | + | ||
| 901 | + | func (x *CIJob) GetCombo() int32 { | |
| 902 | + | if x != nil { | |
| 903 | + | return x.Combo | |
| 904 | + | } | |
| 905 | + | return 0 | |
| 906 | + | } | |
| 907 | + | ||
| 908 | + | func (x *CIJob) GetMatrixJson() string { | |
| 909 | + | if x != nil { | |
| 910 | + | return x.MatrixJson | |
| 911 | + | } | |
| 912 | + | return "" | |
| 913 | + | } | |
| 914 | + | ||
| 915 | + | func (x *CIJob) GetComboTotal() int32 { | |
| 916 | + | if x != nil { | |
| 917 | + | return x.ComboTotal | |
| 918 | + | } | |
| 919 | + | return 0 | |
| 920 | + | } | |
| 921 | + | ||
| 922 | + | func (x *CIJob) GetTimeoutMinutes() int32 { | |
| 923 | + | if x != nil { | |
| 924 | + | return x.TimeoutMinutes | |
| 925 | + | } | |
| 926 | + | return 0 | |
| 927 | + | } | |
| 928 | + | ||
| 929 | + | func (x *CIJob) GetFailFast() bool { | |
| 930 | + | if x != nil { | |
| 931 | + | return x.FailFast | |
| 932 | + | } | |
| 933 | + | return false | |
| 934 | + | } | |
| 935 | + | ||
| 936 | + | func (x *CIJob) GetMaxParallel() int32 { | |
| 937 | + | if x != nil { | |
| 938 | + | return x.MaxParallel | |
| 939 | + | } | |
| 940 | + | return 0 | |
| 941 | + | } | |
| 942 | + | ||
| 943 | + | func (x *CIJob) GetOutputs() map[string]string { | |
| 944 | + | if x != nil { | |
| 945 | + | return x.Outputs | |
| 946 | + | } | |
| 947 | + | return nil | |
| 948 | + | } | |
| 949 | + | ||
| 950 | + | func (x *CIJob) GetTokenHash() []byte { | |
| 951 | + | if x != nil { | |
| 952 | + | return x.TokenHash | |
| 953 | + | } | |
| 954 | + | return nil | |
| 955 | + | } | |
| 956 | + | ||
| 957 | + | func (x *CIJob) GetReadyAt() *timestamppb.Timestamp { | |
| 958 | + | if x != nil { | |
| 959 | + | return x.ReadyAt | |
| 960 | + | } | |
| 961 | + | return nil | |
| 962 | + | } | |
| 963 | + | ||
| 964 | + | func (x *CIJob) GetAssignedAt() *timestamppb.Timestamp { | |
| 965 | + | if x != nil { | |
| 966 | + | return x.AssignedAt | |
| 967 | + | } | |
| 968 | + | return nil | |
| 969 | + | } | |
| 970 | + | ||
| 971 | + | func (x *CIJob) GetAckedAt() *timestamppb.Timestamp { | |
| 972 | + | if x != nil { | |
| 973 | + | return x.AckedAt | |
| 974 | + | } | |
| 975 | + | return nil | |
| 976 | + | } | |
| 977 | + | ||
| 978 | + | func (x *CIJob) GetAssignCancels() int32 { | |
| 979 | + | if x != nil { | |
| 980 | + | return x.AssignCancels | |
| 981 | + | } | |
| 982 | + | return 0 | |
| 983 | + | } | |
| 984 | + | ||
| 985 | + | func (x *CIJob) GetTokenValidUntil() *timestamppb.Timestamp { | |
| 986 | + | if x != nil { | |
| 987 | + | return x.TokenValidUntil | |
| 988 | + | } | |
| 989 | + | return nil | |
| 990 | + | } | |
| 991 | + | ||
| 992 | + | func (x *CIJob) GetCancelReason() int32 { | |
| 993 | + | if x != nil { | |
| 994 | + | return x.CancelReason | |
| 995 | + | } | |
| 996 | + | return 0 | |
| 997 | + | } | |
| 998 | + | ||
| 999 | + | func (x *CIJob) GetCancelMessage() string { | |
| 1000 | + | if x != nil { | |
| 1001 | + | return x.CancelMessage | |
| 1002 | + | } | |
| 1003 | + | return "" | |
| 1004 | + | } | |
| 1005 | + | ||
| 1006 | + | func (x *CIJob) GetCancelRequestedAt() *timestamppb.Timestamp { | |
| 1007 | + | if x != nil { | |
| 1008 | + | return x.CancelRequestedAt | |
| 1009 | + | } | |
| 1010 | + | return nil | |
| 1011 | + | } | |
| 1012 | + | ||
| 1013 | + | func (x *CIJob) GetPlanned() bool { | |
| 1014 | + | if x != nil { | |
| 1015 | + | return x.Planned | |
| 1016 | + | } | |
| 1017 | + | return false | |
| 1018 | + | } | |
| 1019 | + | ||
| 1020 | + | func (x *CIJob) GetRunAttempt() int32 { | |
| 1021 | + | if x != nil { | |
| 1022 | + | return x.RunAttempt | |
| 1023 | + | } | |
| 1024 | + | return 0 | |
| 1025 | + | } | |
| 1026 | + | ||
| 1027 | + | // CICheck is stored under "check/<repo>/<sha>/<job id>". | |
| 1028 | + | type CICheck struct { | |
| 1029 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 1030 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |
| 1031 | + | Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"` | |
| 1032 | + | Sha string `protobuf:"bytes,3,opt,name=sha,proto3" json:"sha,omitempty"` | |
| 1033 | + | Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` | |
| 1034 | + | Status CIStatus `protobuf:"varint,5,opt,name=status,proto3,enum=ermite.storage.CIStatus" json:"status,omitempty"` | |
| 1035 | + | Conclusion CIConclusion `protobuf:"varint,6,opt,name=conclusion,proto3,enum=ermite.storage.CIConclusion" json:"conclusion,omitempty"` | |
| 1036 | + | RunId int64 `protobuf:"varint,7,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` | |
| 1037 | + | JobId string `protobuf:"bytes,8,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` | |
| 1038 | + | Attempt int32 `protobuf:"varint,9,opt,name=attempt,proto3" json:"attempt,omitempty"` | |
| 1039 | + | WorkflowName string `protobuf:"bytes,10,opt,name=workflow_name,json=workflowName,proto3" json:"workflow_name,omitempty"` | |
| 1040 | + | CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | |
| 1041 | + | StartedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` | |
| 1042 | + | CompletedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` | |
| 1043 | + | unknownFields protoimpl.UnknownFields | |
| 1044 | + | sizeCache protoimpl.SizeCache | |
| 1045 | + | } | |
| 1046 | + | ||
| 1047 | + | func (x *CICheck) Reset() { | |
| 1048 | + | *x = CICheck{} | |
| 1049 | + | mi := &file_ermite_storage_ci_proto_msgTypes[4] | |
| 1050 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 1051 | + | ms.StoreMessageInfo(mi) | |
| 1052 | + | } | |
| 1053 | + | ||
| 1054 | + | func (x *CICheck) String() string { | |
| 1055 | + | return protoimpl.X.MessageStringOf(x) | |
| 1056 | + | } | |
| 1057 | + | ||
| 1058 | + | func (*CICheck) ProtoMessage() {} | |
| 1059 | + | ||
| 1060 | + | func (x *CICheck) ProtoReflect() protoreflect.Message { | |
| 1061 | + | mi := &file_ermite_storage_ci_proto_msgTypes[4] | |
| 1062 | + | if x != nil { | |
| 1063 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 1064 | + | if ms.LoadMessageInfo() == nil { | |
| 1065 | + | ms.StoreMessageInfo(mi) | |
| 1066 | + | } | |
| 1067 | + | return ms | |
| 1068 | + | } | |
| 1069 | + | return mi.MessageOf(x) | |
| 1070 | + | } | |
| 1071 | + | ||
| 1072 | + | // Deprecated: Use CICheck.ProtoReflect.Descriptor instead. | |
| 1073 | + | func (*CICheck) Descriptor() ([]byte, []int) { | |
| 1074 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{4} | |
| 1075 | + | } | |
| 1076 | + | ||
| 1077 | + | func (x *CICheck) GetId() string { | |
| 1078 | + | if x != nil { | |
| 1079 | + | return x.Id | |
| 1080 | + | } | |
| 1081 | + | return "" | |
| 1082 | + | } | |
| 1083 | + | ||
| 1084 | + | func (x *CICheck) GetRepo() string { | |
| 1085 | + | if x != nil { | |
| 1086 | + | return x.Repo | |
| 1087 | + | } | |
| 1088 | + | return "" | |
| 1089 | + | } | |
| 1090 | + | ||
| 1091 | + | func (x *CICheck) GetSha() string { | |
| 1092 | + | if x != nil { | |
| 1093 | + | return x.Sha | |
| 1094 | + | } | |
| 1095 | + | return "" | |
| 1096 | + | } | |
| 1097 | + | ||
| 1098 | + | func (x *CICheck) GetName() string { | |
| 1099 | + | if x != nil { | |
| 1100 | + | return x.Name | |
| 1101 | + | } | |
| 1102 | + | return "" | |
| 1103 | + | } | |
| 1104 | + | ||
| 1105 | + | func (x *CICheck) GetStatus() CIStatus { | |
| 1106 | + | if x != nil { | |
| 1107 | + | return x.Status | |
| 1108 | + | } | |
| 1109 | + | return CIStatus_CI_STATUS_UNSPECIFIED | |
| 1110 | + | } | |
| 1111 | + | ||
| 1112 | + | func (x *CICheck) GetConclusion() CIConclusion { | |
| 1113 | + | if x != nil { | |
| 1114 | + | return x.Conclusion | |
| 1115 | + | } | |
| 1116 | + | return CIConclusion_CI_CONCLUSION_UNSPECIFIED | |
| 1117 | + | } | |
| 1118 | + | ||
| 1119 | + | func (x *CICheck) GetRunId() int64 { | |
| 1120 | + | if x != nil { | |
| 1121 | + | return x.RunId | |
| 1122 | + | } | |
| 1123 | + | return 0 | |
| 1124 | + | } | |
| 1125 | + | ||
| 1126 | + | func (x *CICheck) GetJobId() string { | |
| 1127 | + | if x != nil { | |
| 1128 | + | return x.JobId | |
| 1129 | + | } | |
| 1130 | + | return "" | |
| 1131 | + | } | |
| 1132 | + | ||
| 1133 | + | func (x *CICheck) GetAttempt() int32 { | |
| 1134 | + | if x != nil { | |
| 1135 | + | return x.Attempt | |
| 1136 | + | } | |
| 1137 | + | return 0 | |
| 1138 | + | } | |
| 1139 | + | ||
| 1140 | + | func (x *CICheck) GetWorkflowName() string { | |
| 1141 | + | if x != nil { | |
| 1142 | + | return x.WorkflowName | |
| 1143 | + | } | |
| 1144 | + | return "" | |
| 1145 | + | } | |
| 1146 | + | ||
| 1147 | + | func (x *CICheck) GetCreatedAt() *timestamppb.Timestamp { | |
| 1148 | + | if x != nil { | |
| 1149 | + | return x.CreatedAt | |
| 1150 | + | } | |
| 1151 | + | return nil | |
| 1152 | + | } | |
| 1153 | + | ||
| 1154 | + | func (x *CICheck) GetStartedAt() *timestamppb.Timestamp { | |
| 1155 | + | if x != nil { | |
| 1156 | + | return x.StartedAt | |
| 1157 | + | } | |
| 1158 | + | return nil | |
| 1159 | + | } | |
| 1160 | + | ||
| 1161 | + | func (x *CICheck) GetCompletedAt() *timestamppb.Timestamp { | |
| 1162 | + | if x != nil { | |
| 1163 | + | return x.CompletedAt | |
| 1164 | + | } | |
| 1165 | + | return nil | |
| 1166 | + | } | |
| 1167 | + | ||
| 1168 | + | // CIRunner is stored under "runner/<id>". | |
| 1169 | + | type CIRunner struct { | |
| 1170 | + | state protoimpl.MessageState `protogen:"open.v1"` | |
| 1171 | + | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` | |
| 1172 | + | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` | |
| 1173 | + | Labels []string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty"` | |
| 1174 | + | Isolation CIIsolation `protobuf:"varint,4,opt,name=isolation,proto3,enum=ermite.storage.CIIsolation" json:"isolation,omitempty"` | |
| 1175 | + | Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` | |
| 1176 | + | Arch string `protobuf:"bytes,6,opt,name=arch,proto3" json:"arch,omitempty"` | |
| 1177 | + | Os string `protobuf:"bytes,7,opt,name=os,proto3" json:"os,omitempty"` | |
| 1178 | + | RegisteredAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=registered_at,json=registeredAt,proto3" json:"registered_at,omitempty"` | |
| 1179 | + | TokenHash []byte `protobuf:"bytes,9,opt,name=token_hash,json=tokenHash,proto3" json:"token_hash,omitempty"` | |
| 1180 | + | unknownFields protoimpl.UnknownFields | |
| 1181 | + | sizeCache protoimpl.SizeCache | |
| 1182 | + | } | |
| 1183 | + | ||
| 1184 | + | func (x *CIRunner) Reset() { | |
| 1185 | + | *x = CIRunner{} | |
| 1186 | + | mi := &file_ermite_storage_ci_proto_msgTypes[5] | |
| 1187 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 1188 | + | ms.StoreMessageInfo(mi) | |
| 1189 | + | } | |
| 1190 | + | ||
| 1191 | + | func (x *CIRunner) String() string { | |
| 1192 | + | return protoimpl.X.MessageStringOf(x) | |
| 1193 | + | } | |
| 1194 | + | ||
| 1195 | + | func (*CIRunner) ProtoMessage() {} | |
| 1196 | + | ||
| 1197 | + | func (x *CIRunner) ProtoReflect() protoreflect.Message { | |
| 1198 | + | mi := &file_ermite_storage_ci_proto_msgTypes[5] | |
| 1199 | + | if x != nil { | |
| 1200 | + | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |
| 1201 | + | if ms.LoadMessageInfo() == nil { | |
| 1202 | + | ms.StoreMessageInfo(mi) | |
| 1203 | + | } | |
| 1204 | + | return ms | |
| 1205 | + | } | |
| 1206 | + | return mi.MessageOf(x) | |
| 1207 | + | } | |
| 1208 | + | ||
| 1209 | + | // Deprecated: Use CIRunner.ProtoReflect.Descriptor instead. | |
| 1210 | + | func (*CIRunner) Descriptor() ([]byte, []int) { | |
| 1211 | + | return file_ermite_storage_ci_proto_rawDescGZIP(), []int{5} | |
| 1212 | + | } | |
| 1213 | + | ||
| 1214 | + | func (x *CIRunner) GetId() string { | |
| 1215 | + | if x != nil { | |
| 1216 | + | return x.Id | |
| 1217 | + | } | |
| 1218 | + | return "" | |
| 1219 | + | } | |
| 1220 | + | ||
| 1221 | + | func (x *CIRunner) GetName() string { | |
| 1222 | + | if x != nil { | |
| 1223 | + | return x.Name | |
| 1224 | + | } | |
| 1225 | + | return "" | |
| 1226 | + | } | |
| 1227 | + | ||
| 1228 | + | func (x *CIRunner) GetLabels() []string { | |
| 1229 | + | if x != nil { | |
| 1230 | + | return x.Labels | |
| 1231 | + | } | |
| 1232 | + | return nil | |
| 1233 | + | } | |
| 1234 | + | ||
| 1235 | + | func (x *CIRunner) GetIsolation() CIIsolation { | |
| 1236 | + | if x != nil { | |
| 1237 | + | return x.Isolation | |
| 1238 | + | } | |
| 1239 | + | return CIIsolation_CI_ISOLATION_UNSPECIFIED | |
| 1240 | + | } | |
| 1241 | + | ||
| 1242 | + | func (x *CIRunner) GetVersion() string { | |
| 1243 | + | if x != nil { | |
| 1244 | + | return x.Version | |
| 1245 | + | } | |
| 1246 | + | return "" | |
| 1247 | + | } | |
| 1248 | + | ||
| 1249 | + | func (x *CIRunner) GetArch() string { | |
| 1250 | + | if x != nil { | |
| 1251 | + | return x.Arch | |
| 1252 | + | } | |
| 1253 | + | return "" | |
| 1254 | + | } | |
| 1255 | + | ||
| 1256 | + | func (x *CIRunner) GetOs() string { | |
| 1257 | + | if x != nil { | |
| 1258 | + | return x.Os | |
| 1259 | + | } | |
| 1260 | + | return "" | |
| 1261 | + | } | |
| 1262 | + | ||
| 1263 | + | func (x *CIRunner) GetRegisteredAt() *timestamppb.Timestamp { | |
| 1264 | + | if x != nil { | |
| 1265 | + | return x.RegisteredAt | |
| 1266 | + | } | |
| 1267 | + | return nil | |
| 1268 | + | } | |
| 1269 | + | ||
| 1270 | + | func (x *CIRunner) GetTokenHash() []byte { | |
| 1271 | + | if x != nil { | |
| 1272 | + | return x.TokenHash | |
| 1273 | + | } | |
| 1274 | + | return nil | |
| 1275 | + | } | |
| 1276 | + | ||
| 1277 | + | var File_ermite_storage_ci_proto protoreflect.FileDescriptor | |
| 1278 | + | ||
| 1279 | + | const file_ermite_storage_ci_proto_rawDesc = "" + | |
| 1280 | + | "\n" + | |
| 1281 | + | "\x17ermite/storage/ci.proto\x12\x0eermite.storage\x1a\x1fgoogle/protobuf/timestamp.proto\"\xbf\x05\n" + | |
| 1282 | + | "\x05CIRun\x12\x12\n" + | |
| 1283 | + | "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" + | |
| 1284 | + | "\x02id\x18\x02 \x01(\x03R\x02id\x12\x16\n" + | |
| 1285 | + | "\x06number\x18\x03 \x01(\x03R\x06number\x12\x18\n" + | |
| 1286 | + | "\aattempt\x18\x04 \x01(\x05R\aattempt\x12#\n" + | |
| 1287 | + | "\rworkflow_path\x18\x05 \x01(\tR\fworkflowPath\x12#\n" + | |
| 1288 | + | "\rworkflow_name\x18\x06 \x01(\tR\fworkflowName\x12\x14\n" + | |
| 1289 | + | "\x05title\x18\a \x01(\tR\x05title\x12\x14\n" + | |
| 1290 | + | "\x05event\x18\b \x01(\tR\x05event\x12\x10\n" + | |
| 1291 | + | "\x03ref\x18\t \x01(\tR\x03ref\x12\x10\n" + | |
| 1292 | + | "\x03sha\x18\n" + | |
| 1293 | + | " \x01(\tR\x03sha\x12\x14\n" + | |
| 1294 | + | "\x05actor\x18\v \x01(\tR\x05actor\x120\n" + | |
| 1295 | + | "\x06status\x18\f \x01(\x0e2\x18.ermite.storage.CIStatusR\x06status\x12<\n" + | |
| 1296 | + | "\n" + | |
| 1297 | + | "conclusion\x18\r \x01(\x0e2\x1c.ermite.storage.CIConclusionR\n" + | |
| 1298 | + | "conclusion\x12\x14\n" + | |
| 1299 | + | "\x05error\x18\x0e \x01(\tR\x05error\x129\n" + | |
| 1300 | + | "\n" + | |
| 1301 | + | "created_at\x18\x0f \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + | |
| 1302 | + | "\n" + | |
| 1303 | + | "started_at\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x12=\n" + | |
| 1304 | + | "\fcompleted_at\x18\x11 \x01(\v2\x1a.google.protobuf.TimestampR\vcompletedAt\x12)\n" + | |
| 1305 | + | "\x10cancel_requested\x18\x14 \x01(\bR\x0fcancelRequested\x12J\n" + | |
| 1306 | + | "\x13cancel_requested_at\x18\x15 \x01(\v2\x1a.google.protobuf.TimestampR\x11cancelRequestedAt\"~\n" + | |
| 1307 | + | "\vCIRunSource\x12\x15\n" + | |
| 1308 | + | "\x06run_id\x18\x04 \x01(\x03R\x05runId\x12#\n" + | |
| 1309 | + | "\rworkflow_yaml\x18\x01 \x01(\tR\fworkflowYaml\x12\x1f\n" + | |
| 1310 | + | "\vgithub_json\x18\x02 \x01(\tR\n" + | |
| 1311 | + | "githubJson\x12\x12\n" + | |
| 1312 | + | "\x04repo\x18\x03 \x01(\tR\x04repo\"\xd9\x02\n" + | |
| 1313 | + | "\x06CIStep\x12\x16\n" + | |
| 1314 | + | "\x06number\x18\x01 \x01(\x05R\x06number\x12\x12\n" + | |
| 1315 | + | "\x04name\x18\x02 \x01(\tR\x04name\x121\n" + | |
| 1316 | + | "\x05state\x18\x03 \x01(\x0e2\x1b.ermite.storage.CIStepStateR\x05state\x12<\n" + | |
| 1317 | + | "\n" + | |
| 1318 | + | "conclusion\x18\x04 \x01(\x0e2\x1c.ermite.storage.CIConclusionR\n" + | |
| 1319 | + | "conclusion\x129\n" + | |
| 1320 | + | "\n" + | |
| 1321 | + | "started_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x12=\n" + | |
| 1322 | + | "\fcompleted_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\vcompletedAt\x12\x1b\n" + | |
| 1323 | + | "\tlog_start\x18\a \x01(\x03R\blogStart\x12\x1b\n" + | |
| 1324 | + | "\thas_lines\x18\t \x01(\bR\bhasLines\"\xef\v\n" + | |
| 1325 | + | "\x05CIJob\x12\x0e\n" + | |
| 1326 | + | "\x02id\x18\x01 \x01(\tR\x02id\x12\x15\n" + | |
| 1327 | + | "\x06run_id\x18\x02 \x01(\x03R\x05runId\x12\x18\n" + | |
| 1328 | + | "\aattempt\x18\x03 \x01(\x05R\aattempt\x12\x12\n" + | |
| 1329 | + | "\x04repo\x18\x04 \x01(\tR\x04repo\x12\x10\n" + | |
| 1330 | + | "\x03key\x18\x05 \x01(\tR\x03key\x12\x12\n" + | |
| 1331 | + | "\x04name\x18\x06 \x01(\tR\x04name\x120\n" + | |
| 1332 | + | "\x05phase\x18\a \x01(\x0e2\x1a.ermite.storage.CIJobPhaseR\x05phase\x12<\n" + | |
| 1333 | + | "\n" + | |
| 1334 | + | "conclusion\x18\b \x01(\x0e2\x1c.ermite.storage.CIConclusionR\n" + | |
| 1335 | + | "conclusion\x12\x14\n" + | |
| 1336 | + | "\x05error\x18\t \x01(\tR\x05error\x12\x16\n" + | |
| 1337 | + | "\x06labels\x18\n" + | |
| 1338 | + | " \x03(\tR\x06labels\x12\x14\n" + | |
| 1339 | + | "\x05needs\x18\v \x03(\tR\x05needs\x12\x1b\n" + | |
| 1340 | + | "\trunner_id\x18\f \x01(\tR\brunnerId\x12\x1f\n" + | |
| 1341 | + | "\vrunner_name\x18\r \x01(\tR\n" + | |
| 1342 | + | "runnerName\x129\n" + | |
| 1343 | + | "\tisolation\x18\x0e \x01(\x0e2\x1b.ermite.storage.CIIsolationR\tisolation\x12,\n" + | |
| 1344 | + | "\x05steps\x18\x0f \x03(\v2\x16.ermite.storage.CIStepR\x05steps\x129\n" + | |
| 1345 | + | "\n" + | |
| 1346 | + | "created_at\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + | |
| 1347 | + | "\n" + | |
| 1348 | + | "started_at\x18\x11 \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x12=\n" + | |
| 1349 | + | "\fcompleted_at\x18\x12 \x01(\v2\x1a.google.protobuf.TimestampR\vcompletedAt\x12\x14\n" + | |
| 1350 | + | "\x05index\x18\x13 \x01(\x05R\x05index\x12\x14\n" + | |
| 1351 | + | "\x05combo\x18\x14 \x01(\x05R\x05combo\x12\x1f\n" + | |
| 1352 | + | "\vmatrix_json\x18\x15 \x01(\tR\n" + | |
| 1353 | + | "matrixJson\x12\x1f\n" + | |
| 1354 | + | "\vcombo_total\x18\x16 \x01(\x05R\n" + | |
| 1355 | + | "comboTotal\x12'\n" + | |
| 1356 | + | "\x0ftimeout_minutes\x18\x17 \x01(\x05R\x0etimeoutMinutes\x12\x1b\n" + | |
| 1357 | + | "\tfail_fast\x18\x18 \x01(\bR\bfailFast\x12!\n" + | |
| 1358 | + | "\fmax_parallel\x18\x19 \x01(\x05R\vmaxParallel\x12<\n" + | |
| 1359 | + | "\aoutputs\x18\x1a \x03(\v2\".ermite.storage.CIJob.OutputsEntryR\aoutputs\x12\x1d\n" + | |
| 1360 | + | "\n" + | |
| 1361 | + | "token_hash\x18\x1b \x01(\fR\ttokenHash\x125\n" + | |
| 1362 | + | "\bready_at\x18\x1c \x01(\v2\x1a.google.protobuf.TimestampR\areadyAt\x12;\n" + | |
| 1363 | + | "\vassigned_at\x18\x1d \x01(\v2\x1a.google.protobuf.TimestampR\n" + | |
| 1364 | + | "assignedAt\x125\n" + | |
| 1365 | + | "\backed_at\x18\x1e \x01(\v2\x1a.google.protobuf.TimestampR\aackedAt\x12%\n" + | |
| 1366 | + | "\x0eassign_cancels\x18\x1f \x01(\x05R\rassignCancels\x12F\n" + | |
| 1367 | + | "\x11token_valid_until\x18 \x01(\v2\x1a.google.protobuf.TimestampR\x0ftokenValidUntil\x12#\n" + | |
| 1368 | + | "\rcancel_reason\x18! \x01(\x05R\fcancelReason\x12%\n" + | |
| 1369 | + | "\x0ecancel_message\x18\" \x01(\tR\rcancelMessage\x12J\n" + | |
| 1370 | + | "\x13cancel_requested_at\x18# \x01(\v2\x1a.google.protobuf.TimestampR\x11cancelRequestedAt\x12\x18\n" + | |
| 1371 | + | "\aplanned\x18% \x01(\bR\aplanned\x12\x1f\n" + | |
| 1372 | + | "\vrun_attempt\x18& \x01(\x05R\n" + | |
| 1373 | + | "runAttempt\x1a:\n" + | |
| 1374 | + | "\fOutputsEntry\x12\x10\n" + | |
| 1375 | + | "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + | |
| 1376 | + | "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xe5\x03\n" + | |
| 1377 | + | "\aCICheck\x12\x0e\n" + | |
| 1378 | + | "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + | |
| 1379 | + | "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x10\n" + | |
| 1380 | + | "\x03sha\x18\x03 \x01(\tR\x03sha\x12\x12\n" + | |
| 1381 | + | "\x04name\x18\x04 \x01(\tR\x04name\x120\n" + | |
| 1382 | + | "\x06status\x18\x05 \x01(\x0e2\x18.ermite.storage.CIStatusR\x06status\x12<\n" + | |
| 1383 | + | "\n" + | |
| 1384 | + | "conclusion\x18\x06 \x01(\x0e2\x1c.ermite.storage.CIConclusionR\n" + | |
| 1385 | + | "conclusion\x12\x15\n" + | |
| 1386 | + | "\x06run_id\x18\a \x01(\x03R\x05runId\x12\x15\n" + | |
| 1387 | + | "\x06job_id\x18\b \x01(\tR\x05jobId\x12\x18\n" + | |
| 1388 | + | "\aattempt\x18\t \x01(\x05R\aattempt\x12#\n" + | |
| 1389 | + | "\rworkflow_name\x18\n" + | |
| 1390 | + | " \x01(\tR\fworkflowName\x129\n" + | |
| 1391 | + | "\n" + | |
| 1392 | + | "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + | |
| 1393 | + | "\n" + | |
| 1394 | + | "started_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x12=\n" + | |
| 1395 | + | "\fcompleted_at\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\vcompletedAt\"\x9f\x02\n" + | |
| 1396 | + | "\bCIRunner\x12\x0e\n" + | |
| 1397 | + | "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + | |
| 1398 | + | "\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" + | |
| 1399 | + | "\x06labels\x18\x03 \x03(\tR\x06labels\x129\n" + | |
| 1400 | + | "\tisolation\x18\x04 \x01(\x0e2\x1b.ermite.storage.CIIsolationR\tisolation\x12\x18\n" + | |
| 1401 | + | "\aversion\x18\x05 \x01(\tR\aversion\x12\x12\n" + | |
| 1402 | + | "\x04arch\x18\x06 \x01(\tR\x04arch\x12\x0e\n" + | |
| 1403 | + | "\x02os\x18\a \x01(\tR\x02os\x12?\n" + | |
| 1404 | + | "\rregistered_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\fregisteredAt\x12\x1d\n" + | |
| 1405 | + | "\n" + | |
| 1406 | + | "token_hash\x18\t \x01(\fR\ttokenHash*o\n" + | |
| 1407 | + | "\bCIStatus\x12\x19\n" + | |
| 1408 | + | "\x15CI_STATUS_UNSPECIFIED\x10\x00\x12\x14\n" + | |
| 1409 | + | "\x10CI_STATUS_QUEUED\x10\x01\x12\x19\n" + | |
| 1410 | + | "\x15CI_STATUS_IN_PROGRESS\x10\x02\x12\x17\n" + | |
| 1411 | + | "\x13CI_STATUS_COMPLETED\x10\x03*\xf6\x01\n" + | |
| 1412 | + | "\fCIConclusion\x12\x1d\n" + | |
| 1413 | + | "\x19CI_CONCLUSION_UNSPECIFIED\x10\x00\x12\x19\n" + | |
| 1414 | + | "\x15CI_CONCLUSION_SUCCESS\x10\x01\x12\x19\n" + | |
| 1415 | + | "\x15CI_CONCLUSION_FAILURE\x10\x02\x12\x1b\n" + | |
| 1416 | + | "\x17CI_CONCLUSION_CANCELLED\x10\x03\x12\x19\n" + | |
| 1417 | + | "\x15CI_CONCLUSION_SKIPPED\x10\x04\x12\x1b\n" + | |
| 1418 | + | "\x17CI_CONCLUSION_TIMED_OUT\x10\x05\x12\x19\n" + | |
| 1419 | + | "\x15CI_CONCLUSION_NEUTRAL\x10\x06\x12!\n" + | |
| 1420 | + | "\x1dCI_CONCLUSION_ACTION_REQUIRED\x10\a*\x83\x01\n" + | |
| 1421 | + | "\vCIStepState\x12\x1d\n" + | |
| 1422 | + | "\x19CI_STEP_STATE_UNSPECIFIED\x10\x00\x12\x19\n" + | |
| 1423 | + | "\x15CI_STEP_STATE_PENDING\x10\x01\x12\x1d\n" + | |
| 1424 | + | "\x19CI_STEP_STATE_IN_PROGRESS\x10\x02\x12\x1b\n" + | |
| 1425 | + | "\x17CI_STEP_STATE_COMPLETED\x10\x03*\xa9\x01\n" + | |
| 1426 | + | "\n" + | |
| 1427 | + | "CIJobPhase\x12\x1c\n" + | |
| 1428 | + | "\x18CI_JOB_PHASE_UNSPECIFIED\x10\x00\x12\x18\n" + | |
| 1429 | + | "\x14CI_JOB_PHASE_WAITING\x10\x01\x12\x16\n" + | |
| 1430 | + | "\x12CI_JOB_PHASE_READY\x10\x02\x12\x19\n" + | |
| 1431 | + | "\x15CI_JOB_PHASE_ASSIGNED\x10\x03\x12\x18\n" + | |
| 1432 | + | "\x14CI_JOB_PHASE_RUNNING\x10\x04\x12\x16\n" + | |
| 1433 | + | "\x12CI_JOB_PHASE_ENDED\x10\x05*\x8a\x01\n" + | |
| 1434 | + | "\vCIIsolation\x12\x1c\n" + | |
| 1435 | + | "\x18CI_ISOLATION_UNSPECIFIED\x10\x00\x12\x13\n" + | |
| 1436 | + | "\x0fCI_ISOLATION_VM\x10\x01\x12!\n" + | |
| 1437 | + | "\x1dCI_ISOLATION_CONTAINER_SYSBOX\x10\x02\x12%\n" + | |
| 1438 | + | "!CI_ISOLATION_CONTAINER_PRIVILEGED\x10\x03B/Z-bnema.dev/ermite/gen/ermite/storage;storagepbb\x06proto3" | |
| 1439 | + | ||
| 1440 | + | var ( | |
| 1441 | + | file_ermite_storage_ci_proto_rawDescOnce sync.Once | |
| 1442 | + | file_ermite_storage_ci_proto_rawDescData []byte | |
| 1443 | + | ) | |
| 1444 | + | ||
| 1445 | + | func file_ermite_storage_ci_proto_rawDescGZIP() []byte { | |
| 1446 | + | file_ermite_storage_ci_proto_rawDescOnce.Do(func() { | |
| 1447 | + | file_ermite_storage_ci_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_storage_ci_proto_rawDesc), len(file_ermite_storage_ci_proto_rawDesc))) | |
| 1448 | + | }) | |
| 1449 | + | return file_ermite_storage_ci_proto_rawDescData | |
| 1450 | + | } | |
| 1451 | + | ||
| 1452 | + | var file_ermite_storage_ci_proto_enumTypes = make([]protoimpl.EnumInfo, 5) | |
| 1453 | + | var file_ermite_storage_ci_proto_msgTypes = make([]protoimpl.MessageInfo, 7) | |
| 1454 | + | var file_ermite_storage_ci_proto_goTypes = []any{ | |
| 1455 | + | (CIStatus)(0), // 0: ermite.storage.CIStatus | |
| 1456 | + | (CIConclusion)(0), // 1: ermite.storage.CIConclusion | |
| 1457 | + | (CIStepState)(0), // 2: ermite.storage.CIStepState | |
| 1458 | + | (CIJobPhase)(0), // 3: ermite.storage.CIJobPhase | |
| 1459 | + | (CIIsolation)(0), // 4: ermite.storage.CIIsolation | |
| 1460 | + | (*CIRun)(nil), // 5: ermite.storage.CIRun | |
| 1461 | + | (*CIRunSource)(nil), // 6: ermite.storage.CIRunSource | |
| 1462 | + | (*CIStep)(nil), // 7: ermite.storage.CIStep | |
| 1463 | + | (*CIJob)(nil), // 8: ermite.storage.CIJob | |
| 1464 | + | (*CICheck)(nil), // 9: ermite.storage.CICheck | |
| 1465 | + | (*CIRunner)(nil), // 10: ermite.storage.CIRunner | |
| 1466 | + | nil, // 11: ermite.storage.CIJob.OutputsEntry | |
| 1467 | + | (*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp | |
| 1468 | + | } | |
| 1469 | + | var file_ermite_storage_ci_proto_depIdxs = []int32{ | |
| 1470 | + | 0, // 0: ermite.storage.CIRun.status:type_name -> ermite.storage.CIStatus | |
| 1471 | + | 1, // 1: ermite.storage.CIRun.conclusion:type_name -> ermite.storage.CIConclusion | |
| 1472 | + | 12, // 2: ermite.storage.CIRun.created_at:type_name -> google.protobuf.Timestamp | |
| 1473 | + | 12, // 3: ermite.storage.CIRun.started_at:type_name -> google.protobuf.Timestamp | |
| 1474 | + | 12, // 4: ermite.storage.CIRun.completed_at:type_name -> google.protobuf.Timestamp | |
| 1475 | + | 12, // 5: ermite.storage.CIRun.cancel_requested_at:type_name -> google.protobuf.Timestamp | |
| 1476 | + | 2, // 6: ermite.storage.CIStep.state:type_name -> ermite.storage.CIStepState | |
| 1477 | + | 1, // 7: ermite.storage.CIStep.conclusion:type_name -> ermite.storage.CIConclusion | |
| 1478 | + | 12, // 8: ermite.storage.CIStep.started_at:type_name -> google.protobuf.Timestamp | |
| 1479 | + | 12, // 9: ermite.storage.CIStep.completed_at:type_name -> google.protobuf.Timestamp | |
| 1480 | + | 3, // 10: ermite.storage.CIJob.phase:type_name -> ermite.storage.CIJobPhase | |
| 1481 | + | 1, // 11: ermite.storage.CIJob.conclusion:type_name -> ermite.storage.CIConclusion | |
| 1482 | + | 4, // 12: ermite.storage.CIJob.isolation:type_name -> ermite.storage.CIIsolation | |
| 1483 | + | 7, // 13: ermite.storage.CIJob.steps:type_name -> ermite.storage.CIStep | |
| 1484 | + | 12, // 14: ermite.storage.CIJob.created_at:type_name -> google.protobuf.Timestamp | |
| 1485 | + | 12, // 15: ermite.storage.CIJob.started_at:type_name -> google.protobuf.Timestamp | |
| 1486 | + | 12, // 16: ermite.storage.CIJob.completed_at:type_name -> google.protobuf.Timestamp | |
| 1487 | + | 11, // 17: ermite.storage.CIJob.outputs:type_name -> ermite.storage.CIJob.OutputsEntry | |
| 1488 | + | 12, // 18: ermite.storage.CIJob.ready_at:type_name -> google.protobuf.Timestamp | |
| 1489 | + | 12, // 19: ermite.storage.CIJob.assigned_at:type_name -> google.protobuf.Timestamp | |
| 1490 | + | 12, // 20: ermite.storage.CIJob.acked_at:type_name -> google.protobuf.Timestamp | |
| 1491 | + | 12, // 21: ermite.storage.CIJob.token_valid_until:type_name -> google.protobuf.Timestamp | |
| 1492 | + | 12, // 22: ermite.storage.CIJob.cancel_requested_at:type_name -> google.protobuf.Timestamp | |
| 1493 | + | 0, // 23: ermite.storage.CICheck.status:type_name -> ermite.storage.CIStatus | |
| 1494 | + | 1, // 24: ermite.storage.CICheck.conclusion:type_name -> ermite.storage.CIConclusion | |
| 1495 | + | 12, // 25: ermite.storage.CICheck.created_at:type_name -> google.protobuf.Timestamp | |
| 1496 | + | 12, // 26: ermite.storage.CICheck.started_at:type_name -> google.protobuf.Timestamp | |
| 1497 | + | 12, // 27: ermite.storage.CICheck.completed_at:type_name -> google.protobuf.Timestamp | |
| 1498 | + | 4, // 28: ermite.storage.CIRunner.isolation:type_name -> ermite.storage.CIIsolation | |
| 1499 | + | 12, // 29: ermite.storage.CIRunner.registered_at:type_name -> google.protobuf.Timestamp | |
| 1500 | + | 30, // [30:30] is the sub-list for method output_type | |
| 1501 | + | 30, // [30:30] is the sub-list for method input_type | |
| 1502 | + | 30, // [30:30] is the sub-list for extension type_name | |
| 1503 | + | 30, // [30:30] is the sub-list for extension extendee | |
| 1504 | + | 0, // [0:30] is the sub-list for field type_name | |
| 1505 | + | } | |
| 1506 | + | ||
| 1507 | + | func init() { file_ermite_storage_ci_proto_init() } | |
| 1508 | + | func file_ermite_storage_ci_proto_init() { | |
| 1509 | + | if File_ermite_storage_ci_proto != nil { | |
| 1510 | + | return | |
| 1511 | + | } | |
| 1512 | + | type x struct{} | |
| 1513 | + | out := protoimpl.TypeBuilder{ | |
| 1514 | + | File: protoimpl.DescBuilder{ | |
| 1515 | + | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | |
| 1516 | + | RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_storage_ci_proto_rawDesc), len(file_ermite_storage_ci_proto_rawDesc)), | |
| 1517 | + | NumEnums: 5, | |
| 1518 | + | NumMessages: 7, | |
| 1519 | + | NumExtensions: 0, | |
| 1520 | + | NumServices: 0, | |
| 1521 | + | }, | |
| 1522 | + | GoTypes: file_ermite_storage_ci_proto_goTypes, | |
| 1523 | + | DependencyIndexes: file_ermite_storage_ci_proto_depIdxs, | |
| 1524 | + | EnumInfos: file_ermite_storage_ci_proto_enumTypes, | |
| 1525 | + | MessageInfos: file_ermite_storage_ci_proto_msgTypes, | |
| 1526 | + | }.Build() | |
| 1527 | + | File_ermite_storage_ci_proto = out.File | |
| 1528 | + | file_ermite_storage_ci_proto_goTypes = nil | |
| 1529 | + | file_ermite_storage_ci_proto_depIdxs = nil | |
| 1530 | + | } | |
M go.mod +2 -1
| @@ -12,7 +12,8 @@ tool ( | |||
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | require ( | |
| 15 | - | bnema.dev/ermite-api v0.0.0-20261011182719-cb5699451db6 | |
| 15 | + | bnema.dev/ermite-actions v0.0.0-20261012161738-a52e9b5cddd6 | |
| 16 | + | bnema.dev/ermite-api v0.0.0-20261012142818-97a93650f8b7 | |
| 16 | 17 | bnema.dev/ermite-git v0.1.0 | |
| 17 | 18 | bnema.dev/ermite-kv v0.0.0-20261010031554-5e09b48f81c3 | |
| 18 | 19 | connectrpc.com/connect v1.21.0 | |
A internal/adapters/cilogs/cilogs.go +371 -0
| @@ -0,0 +1,371 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | // Package cilogs implements ports.CILogStore with append-only files under | |
| 4 | + | // $ERMITE_DATA_DIR/ci/logs/<run id>/<job id>/<attempt>.log. | |
| 5 | + | // | |
| 6 | + | // A file holds one line per log line, in GitHub's raw log shape: an RFC 3339 | |
| 7 | + | // timestamp with nanoseconds, a space, then the text, behind a one-character | |
| 8 | + | // marker: "-" for a line, "+" for a piece that continues the line before it | |
| 9 | + | // (it keeps its own line number): | |
| 10 | + | // | |
| 11 | + | // - 2026-10-12T10:00:00.123456789Z text | |
| 12 | + | // - 2026-10-12T10:00:00.123456789Z more text of the same line | |
| 13 | + | // | |
| 14 | + | // The file is the truth for bytes and line counts: the metadata store is never | |
| 15 | + | // written per chunk. Counts are cached per open log and rebuilt from the file | |
| 16 | + | // after a restart. | |
| 17 | + | package cilogs | |
| 18 | + | ||
| 19 | + | import ( | |
| 20 | + | "bufio" | |
| 21 | + | "bytes" | |
| 22 | + | "context" | |
| 23 | + | "errors" | |
| 24 | + | "fmt" | |
| 25 | + | "io" | |
| 26 | + | "io/fs" | |
| 27 | + | "os" | |
| 28 | + | "path/filepath" | |
| 29 | + | "strconv" | |
| 30 | + | "sync" | |
| 31 | + | "time" | |
| 32 | + | ||
| 33 | + | "bnema.dev/ermite/internal/domain" | |
| 34 | + | "bnema.dev/ermite/internal/ports" | |
| 35 | + | ) | |
| 36 | + | ||
| 37 | + | // Dir is the directory of the logs under the data directory. | |
| 38 | + | func Dir(dataDir string) string { return filepath.Join(dataDir, "ci", "logs") } | |
| 39 | + | ||
| 40 | + | // timeLayout is RFC 3339 with nanoseconds, always in UTC and with all nine | |
| 41 | + | // digits, so that every prefix has the same length. | |
| 42 | + | const timeLayout = "2006-01-02T15:04:05.000000000Z" | |
| 43 | + | ||
| 44 | + | // prefixLen is the length of "- " + timestamp + " ". | |
| 45 | + | const prefixLen = 2 + len(timeLayout) + 1 | |
| 46 | + | ||
| 47 | + | // Store is the file-backed log store. | |
| 48 | + | type Store struct { | |
| 49 | + | root string | |
| 50 | + | ||
| 51 | + | mu sync.Mutex | |
| 52 | + | logs map[ports.CILogRef]*logState | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | var _ ports.CILogStore = (*Store)(nil) | |
| 56 | + | ||
| 57 | + | // logState is what is known of one log file. | |
| 58 | + | type logState struct { | |
| 59 | + | mu sync.Mutex | |
| 60 | + | loaded bool | |
| 61 | + | count int64 | |
| 62 | + | size int64 | |
| 63 | + | offsets []int64 // byte offset of every 1024th line: index i is line i*1024 | |
| 64 | + | changed chan struct{} | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | const offsetEvery = 1024 | |
| 68 | + | ||
| 69 | + | // New returns a store rooted at dir, which need not exist yet. | |
| 70 | + | func New(dir string) *Store { | |
| 71 | + | return &Store{root: dir, logs: make(map[ports.CILogRef]*logState)} | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | func (s *Store) path(ref ports.CILogRef) string { | |
| 75 | + | return filepath.Join(s.root, strconv.FormatInt(ref.RunID, 10), ref.JobID, strconv.Itoa(int(ref.Attempt))+".log") | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | func (s *Store) state(ref ports.CILogRef) *logState { | |
| 79 | + | s.mu.Lock() | |
| 80 | + | defer s.mu.Unlock() | |
| 81 | + | st, ok := s.logs[ref] | |
| 82 | + | if !ok { | |
| 83 | + | st = &logState{changed: make(chan struct{})} | |
| 84 | + | s.logs[ref] = st | |
| 85 | + | } | |
| 86 | + | return st | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // validRef rejects a reference that could escape the directory. | |
| 90 | + | func validRef(ref ports.CILogRef) bool { | |
| 91 | + | if ref.RunID < 1 || ref.Attempt < 1 { | |
| 92 | + | return false | |
| 93 | + | } | |
| 94 | + | _, _, _, ok := domain.ParseJobID(ref.JobID) | |
| 95 | + | return ok | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | // load reads the file to learn its line count and size, once. The caller | |
| 99 | + | // holds st.mu. | |
| 100 | + | func (s *Store) load(ref ports.CILogRef, st *logState) error { | |
| 101 | + | if st.loaded { | |
| 102 | + | return nil | |
| 103 | + | } | |
| 104 | + | f, err := os.Open(s.path(ref)) | |
| 105 | + | if errors.Is(err, fs.ErrNotExist) { | |
| 106 | + | st.loaded = true | |
| 107 | + | return nil | |
| 108 | + | } | |
| 109 | + | if err != nil { | |
| 110 | + | return fmt.Errorf("open log: %w", err) | |
| 111 | + | } | |
| 112 | + | defer func() { _ = f.Close() }() | |
| 113 | + | r := bufio.NewReaderSize(f, 1<<16) | |
| 114 | + | var count, pos int64 | |
| 115 | + | for { | |
| 116 | + | line, err := r.ReadBytes('\n') | |
| 117 | + | if len(line) > 0 && line[len(line)-1] == '\n' { | |
| 118 | + | if count%offsetEvery == 0 { | |
| 119 | + | st.offsets = append(st.offsets, pos) | |
| 120 | + | } | |
| 121 | + | count++ | |
| 122 | + | pos += int64(len(line)) | |
| 123 | + | } else if len(line) > 0 { | |
| 124 | + | // A torn last line from a crash: cut it, it was never acknowledged. | |
| 125 | + | if terr := os.Truncate(s.path(ref), pos); terr != nil { | |
| 126 | + | return fmt.Errorf("repair log: %w", terr) | |
| 127 | + | } | |
| 128 | + | } | |
| 129 | + | if err != nil { | |
| 130 | + | if errors.Is(err, io.EOF) { | |
| 131 | + | break | |
| 132 | + | } | |
| 133 | + | return fmt.Errorf("read log: %w", err) | |
| 134 | + | } | |
| 135 | + | } | |
| 136 | + | st.count, st.size, st.loaded = count, pos, true | |
| 137 | + | return nil | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | func encodeLine(l domain.CILogLine) []byte { | |
| 141 | + | b := make([]byte, 0, prefixLen+len(l.Text)+1) | |
| 142 | + | if l.Continued { | |
| 143 | + | b = append(b, '+', ' ') | |
| 144 | + | } else { | |
| 145 | + | b = append(b, '-', ' ') | |
| 146 | + | } | |
| 147 | + | b = l.Time.UTC().AppendFormat(b, timeLayout) | |
| 148 | + | b = append(b, ' ') | |
| 149 | + | b = append(b, l.Text...) | |
| 150 | + | return append(b, '\n') | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | func decodeLine(b []byte) (domain.CILogLine, error) { | |
| 154 | + | b = bytes.TrimSuffix(b, []byte{'\n'}) | |
| 155 | + | if len(b) < prefixLen || (b[0] != '-' && b[0] != '+') { | |
| 156 | + | return domain.CILogLine{}, errors.New("malformed log line") | |
| 157 | + | } | |
| 158 | + | t, err := time.Parse(timeLayout, string(b[2:2+len(timeLayout)])) | |
| 159 | + | if err != nil { | |
| 160 | + | return domain.CILogLine{}, fmt.Errorf("malformed log time: %w", err) | |
| 161 | + | } | |
| 162 | + | return domain.CILogLine{Time: t, Text: string(b[prefixLen:]), Continued: b[0] == '+'}, nil | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | // Append appends lines at offset. | |
| 166 | + | func (s *Store) Append(ctx context.Context, ref ports.CILogRef, offset int64, lines []domain.CILogLine, limit int64) (int64, int, error) { | |
| 167 | + | return s.append(ctx, ref, offset, lines, limit, false) | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | // AppendForced appends lines whatever the limit. | |
| 171 | + | func (s *Store) AppendForced(ctx context.Context, ref ports.CILogRef, lines []domain.CILogLine) (int64, error) { | |
| 172 | + | next, _, err := s.append(ctx, ref, -1, lines, 0, true) | |
| 173 | + | return next, err | |
| 174 | + | } | |
| 175 | + | ||
| 176 | + | func (s *Store) append(_ context.Context, ref ports.CILogRef, offset int64, lines []domain.CILogLine, limit int64, force bool) (int64, int, error) { | |
| 177 | + | if !validRef(ref) { | |
| 178 | + | return 0, 0, domain.ErrNotFound | |
| 179 | + | } | |
| 180 | + | st := s.state(ref) | |
| 181 | + | st.mu.Lock() | |
| 182 | + | defer st.mu.Unlock() | |
| 183 | + | if err := s.load(ref, st); err != nil { | |
| 184 | + | return 0, 0, err | |
| 185 | + | } | |
| 186 | + | if offset < 0 { | |
| 187 | + | offset = st.count | |
| 188 | + | } | |
| 189 | + | if offset > st.count { | |
| 190 | + | return 0, 0, &ports.ErrCILogGap{Count: st.count} | |
| 191 | + | } | |
| 192 | + | skip := st.count - offset | |
| 193 | + | if skip >= int64(len(lines)) { | |
| 194 | + | return st.count, 0, nil | |
| 195 | + | } | |
| 196 | + | fresh := lines[skip:] | |
| 197 | + | var buf bytes.Buffer | |
| 198 | + | for _, l := range fresh { | |
| 199 | + | buf.Write(encodeLine(l)) | |
| 200 | + | } | |
| 201 | + | if !force && st.size+int64(buf.Len()) > limit { | |
| 202 | + | return st.count, 0, ports.ErrCILogLimit | |
| 203 | + | } | |
| 204 | + | if err := os.MkdirAll(filepath.Dir(s.path(ref)), 0o700); err != nil { | |
| 205 | + | return 0, 0, fmt.Errorf("create log directory: %w", err) | |
| 206 | + | } | |
| 207 | + | f, err := os.OpenFile(s.path(ref), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o600) | |
| 208 | + | if err != nil { | |
| 209 | + | return 0, 0, fmt.Errorf("open log: %w", err) | |
| 210 | + | } | |
| 211 | + | n, werr := f.Write(buf.Bytes()) | |
| 212 | + | cerr := f.Close() | |
| 213 | + | if werr != nil || cerr != nil { | |
| 214 | + | // Cut what was written so that the file keeps whole lines. | |
| 215 | + | _ = os.Truncate(s.path(ref), st.size) | |
| 216 | + | return 0, 0, fmt.Errorf("write log: %w", errors.Join(werr, cerr)) | |
| 217 | + | } | |
| 218 | + | pos := st.size | |
| 219 | + | for i, l := range fresh { | |
| 220 | + | if (st.count+int64(i))%offsetEvery == 0 { | |
| 221 | + | st.offsets = append(st.offsets, pos) | |
| 222 | + | } | |
| 223 | + | pos += int64(len(encodeLine(l))) | |
| 224 | + | } | |
| 225 | + | st.count += int64(len(fresh)) | |
| 226 | + | st.size += int64(n) | |
| 227 | + | close(st.changed) | |
| 228 | + | st.changed = make(chan struct{}) | |
| 229 | + | return st.count, len(fresh), nil | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | // Count returns the number of lines held. | |
| 233 | + | func (s *Store) Count(_ context.Context, ref ports.CILogRef) (int64, error) { | |
| 234 | + | if !validRef(ref) { | |
| 235 | + | return 0, nil | |
| 236 | + | } | |
| 237 | + | st := s.state(ref) | |
| 238 | + | st.mu.Lock() | |
| 239 | + | defer st.mu.Unlock() | |
| 240 | + | if err := s.load(ref, st); err != nil { | |
| 241 | + | return 0, err | |
| 242 | + | } | |
| 243 | + | return st.count, nil | |
| 244 | + | } | |
| 245 | + | ||
| 246 | + | // Read returns lines from line number from. | |
| 247 | + | func (s *Store) Read(_ context.Context, ref ports.CILogRef, from int64, maxLines, maxBytes int) ([]domain.CILogLine, error) { | |
| 248 | + | if !validRef(ref) || from < 0 { | |
| 249 | + | return nil, domain.ErrNotFound | |
| 250 | + | } | |
| 251 | + | st := s.state(ref) | |
| 252 | + | st.mu.Lock() | |
| 253 | + | if err := s.load(ref, st); err != nil { | |
| 254 | + | st.mu.Unlock() | |
| 255 | + | return nil, err | |
| 256 | + | } | |
| 257 | + | count, size := st.count, st.size | |
| 258 | + | var start int64 | |
| 259 | + | var first int64 | |
| 260 | + | if idx := from / offsetEvery; idx < int64(len(st.offsets)) { | |
| 261 | + | start, first = st.offsets[idx], idx*offsetEvery | |
| 262 | + | } | |
| 263 | + | st.mu.Unlock() | |
| 264 | + | if from > count { | |
| 265 | + | return nil, domain.ErrOutOfRange | |
| 266 | + | } | |
| 267 | + | if _, err := os.Stat(s.path(ref)); errors.Is(err, fs.ErrNotExist) { | |
| 268 | + | if from == 0 && count == 0 { | |
| 269 | + | return nil, domain.ErrNotFound | |
| 270 | + | } | |
| 271 | + | return nil, domain.ErrNotFound | |
| 272 | + | } | |
| 273 | + | if from == count { | |
| 274 | + | return nil, nil | |
| 275 | + | } | |
| 276 | + | f, err := os.Open(s.path(ref)) | |
| 277 | + | if err != nil { | |
| 278 | + | return nil, fmt.Errorf("open log: %w", err) | |
| 279 | + | } | |
| 280 | + | defer func() { _ = f.Close() }() | |
| 281 | + | // Never read past the size seen under the lock: a concurrent append is | |
| 282 | + | // for the next call. | |
| 283 | + | r := bufio.NewReaderSize(io.NewSectionReader(f, start, size-start), 1<<16) | |
| 284 | + | var out []domain.CILogLine | |
| 285 | + | total := 0 | |
| 286 | + | for n := first; n < count; n++ { | |
| 287 | + | raw, err := r.ReadBytes('\n') | |
| 288 | + | if err != nil { | |
| 289 | + | return nil, fmt.Errorf("read log: %w", err) | |
| 290 | + | } | |
| 291 | + | if n < from { | |
| 292 | + | continue | |
| 293 | + | } | |
| 294 | + | l, err := decodeLine(raw) | |
| 295 | + | if err != nil { | |
| 296 | + | return nil, fmt.Errorf("log line %d: %w", n, err) | |
| 297 | + | } | |
| 298 | + | if len(out) > 0 && (len(out) >= maxLines || total+len(l.Text) > maxBytes) { | |
| 299 | + | break | |
| 300 | + | } | |
| 301 | + | total += len(l.Text) | |
| 302 | + | out = append(out, l) | |
| 303 | + | } | |
| 304 | + | return out, nil | |
| 305 | + | } | |
| 306 | + | ||
| 307 | + | // Changed returns a channel closed at the next append. | |
| 308 | + | func (s *Store) Changed(ref ports.CILogRef) <-chan struct{} { | |
| 309 | + | st := s.state(ref) | |
| 310 | + | st.mu.Lock() | |
| 311 | + | defer st.mu.Unlock() | |
| 312 | + | return st.changed | |
| 313 | + | } | |
| 314 | + | ||
| 315 | + | // Reset empties the log. | |
| 316 | + | func (s *Store) Reset(_ context.Context, ref ports.CILogRef) error { | |
| 317 | + | if !validRef(ref) { | |
| 318 | + | return nil | |
| 319 | + | } | |
| 320 | + | st := s.state(ref) | |
| 321 | + | st.mu.Lock() | |
| 322 | + | defer st.mu.Unlock() | |
| 323 | + | if err := os.Remove(s.path(ref)); err != nil && !errors.Is(err, fs.ErrNotExist) { | |
| 324 | + | return fmt.Errorf("remove log: %w", err) | |
| 325 | + | } | |
| 326 | + | st.loaded, st.count, st.size, st.offsets = true, 0, 0, nil | |
| 327 | + | close(st.changed) | |
| 328 | + | st.changed = make(chan struct{}) | |
| 329 | + | return nil | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | // RemoveRuns deletes the logs of the runs. | |
| 333 | + | func (s *Store) RemoveRuns(_ context.Context, runIDs []int64) error { | |
| 334 | + | var errs []error | |
| 335 | + | for _, id := range runIDs { | |
| 336 | + | if id < 1 { | |
| 337 | + | continue | |
| 338 | + | } | |
| 339 | + | if err := os.RemoveAll(filepath.Join(s.root, strconv.FormatInt(id, 10))); err != nil { | |
| 340 | + | errs = append(errs, err) | |
| 341 | + | } | |
| 342 | + | } | |
| 343 | + | s.mu.Lock() | |
| 344 | + | for ref := range s.logs { | |
| 345 | + | for _, id := range runIDs { | |
| 346 | + | if ref.RunID == id { | |
| 347 | + | delete(s.logs, ref) | |
| 348 | + | } | |
| 349 | + | } | |
| 350 | + | } | |
| 351 | + | s.mu.Unlock() | |
| 352 | + | return errors.Join(errs...) | |
| 353 | + | } | |
| 354 | + | ||
| 355 | + | // RunIDs lists the runs that have a log directory. | |
| 356 | + | func (s *Store) RunIDs(_ context.Context) ([]int64, error) { | |
| 357 | + | entries, err := os.ReadDir(s.root) | |
| 358 | + | if errors.Is(err, fs.ErrNotExist) { | |
| 359 | + | return nil, nil | |
| 360 | + | } | |
| 361 | + | if err != nil { | |
| 362 | + | return nil, fmt.Errorf("list logs: %w", err) | |
| 363 | + | } | |
| 364 | + | var ids []int64 | |
| 365 | + | for _, e := range entries { | |
| 366 | + | if id, err := strconv.ParseInt(e.Name(), 10, 64); err == nil && e.IsDir() { | |
| 367 | + | ids = append(ids, id) | |
| 368 | + | } | |
| 369 | + | } | |
| 370 | + | return ids, nil | |
| 371 | + | } | |
A internal/adapters/cilogs/cilogs_test.go +238 -0
| @@ -0,0 +1,238 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package cilogs_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "os" | |
| 8 | + | "path/filepath" | |
| 9 | + | "strings" | |
| 10 | + | "testing" | |
| 11 | + | "time" | |
| 12 | + | ||
| 13 | + | "github.com/stretchr/testify/assert" | |
| 14 | + | "github.com/stretchr/testify/require" | |
| 15 | + | ||
| 16 | + | "bnema.dev/ermite/internal/adapters/cilogs" | |
| 17 | + | "bnema.dev/ermite/internal/domain" | |
| 18 | + | "bnema.dev/ermite/internal/ports" | |
| 19 | + | ) | |
| 20 | + | ||
| 21 | + | var ( | |
| 22 | + | ref0 = ports.CILogRef{RunID: 7, JobID: "7-0-0", Attempt: 1} | |
| 23 | + | t0 = time.Date(2026, 10, 12, 10, 0, 0, 123456789, time.UTC) | |
| 24 | + | ) | |
| 25 | + | ||
| 26 | + | func lines(texts ...string) []domain.CILogLine { | |
| 27 | + | out := make([]domain.CILogLine, len(texts)) | |
| 28 | + | for i, s := range texts { | |
| 29 | + | out[i] = domain.CILogLine{Time: t0.Add(time.Duration(i) * time.Millisecond), Text: s} | |
| 30 | + | } | |
| 31 | + | return out | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | const big = 1 << 30 | |
| 35 | + | ||
| 36 | + | func TestAppendAndReadKeepTextTimeAndContinuation(t *testing.T) { | |
| 37 | + | ctx := context.Background() | |
| 38 | + | s := cilogs.New(t.TempDir()) | |
| 39 | + | in := lines("##[group]Run echo hi", "hi", "##[endgroup]") | |
| 40 | + | in = append(in, domain.CILogLine{Time: t0, Text: "second half", Continued: true}) | |
| 41 | + | next, added, err := s.Append(ctx, ref0, 0, in, big) | |
| 42 | + | require.NoError(t, err) | |
| 43 | + | assert.Equal(t, int64(4), next) | |
| 44 | + | assert.Equal(t, 4, added) | |
| 45 | + | ||
| 46 | + | got, err := s.Read(ctx, ref0, 0, 100, 1<<20) | |
| 47 | + | require.NoError(t, err) | |
| 48 | + | require.Len(t, got, 4) | |
| 49 | + | assert.Equal(t, "##[group]Run echo hi", got[0].Text) | |
| 50 | + | assert.True(t, got[0].Time.Equal(t0), "nanosecond precision is kept: %v", got[0].Time) | |
| 51 | + | assert.True(t, got[3].Continued) | |
| 52 | + | assert.False(t, got[2].Continued) | |
| 53 | + | ||
| 54 | + | tail, err := s.Read(ctx, ref0, 2, 100, 1<<20) | |
| 55 | + | require.NoError(t, err) | |
| 56 | + | assert.Equal(t, "##[endgroup]", tail[0].Text) | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | func TestAppendIsIdempotentByOffset(t *testing.T) { | |
| 60 | + | ctx := context.Background() | |
| 61 | + | s := cilogs.New(t.TempDir()) | |
| 62 | + | _, _, err := s.Append(ctx, ref0, 0, lines("a", "b", "c"), big) | |
| 63 | + | require.NoError(t, err) | |
| 64 | + | ||
| 65 | + | // A retry of the same request changes nothing. | |
| 66 | + | next, added, err := s.Append(ctx, ref0, 0, lines("a", "b", "c"), big) | |
| 67 | + | require.NoError(t, err) | |
| 68 | + | assert.Equal(t, int64(3), next) | |
| 69 | + | assert.Zero(t, added) | |
| 70 | + | ||
| 71 | + | // A request that overlaps appends only its new tail. | |
| 72 | + | next, added, err = s.Append(ctx, ref0, 1, lines("b", "c", "d"), big) | |
| 73 | + | require.NoError(t, err) | |
| 74 | + | assert.Equal(t, int64(4), next) | |
| 75 | + | assert.Equal(t, 1, added) | |
| 76 | + | ||
| 77 | + | // A retry whose lines differ is silently dropped, as the contract says. | |
| 78 | + | next, added, err = s.Append(ctx, ref0, 0, lines("X", "Y"), big) | |
| 79 | + | require.NoError(t, err) | |
| 80 | + | assert.Equal(t, int64(4), next) | |
| 81 | + | assert.Zero(t, added) | |
| 82 | + | got, _ := s.Read(ctx, ref0, 0, 10, 1<<20) | |
| 83 | + | assert.Equal(t, "a", got[0].Text) | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | func TestAppendAboveTheCountIsAGap(t *testing.T) { | |
| 87 | + | ctx := context.Background() | |
| 88 | + | s := cilogs.New(t.TempDir()) | |
| 89 | + | _, _, err := s.Append(ctx, ref0, 0, lines("a"), big) | |
| 90 | + | require.NoError(t, err) | |
| 91 | + | _, _, err = s.Append(ctx, ref0, 5, lines("z"), big) | |
| 92 | + | require.ErrorIs(t, err, domain.ErrOutOfRange) | |
| 93 | + | var gap *ports.ErrCILogGap | |
| 94 | + | require.ErrorAs(t, err, &gap) | |
| 95 | + | assert.Equal(t, int64(1), gap.Count) | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | func TestTheLimitRefusesTheWholeRequestUnlessForced(t *testing.T) { | |
| 99 | + | ctx := context.Background() | |
| 100 | + | s := cilogs.New(t.TempDir()) | |
| 101 | + | _, _, err := s.Append(ctx, ref0, 0, lines("aaaa"), 200) | |
| 102 | + | require.NoError(t, err) | |
| 103 | + | _, _, err = s.Append(ctx, ref0, 1, lines(strings.Repeat("b", 300)), 200) | |
| 104 | + | require.ErrorIs(t, err, ports.ErrCILogLimit) | |
| 105 | + | n, _ := s.Count(ctx, ref0) | |
| 106 | + | assert.Equal(t, int64(1), n, "nothing of the refused request was appended") | |
| 107 | + | ||
| 108 | + | next, err := s.AppendForced(ctx, ref0, lines("limit reached")) | |
| 109 | + | require.NoError(t, err) | |
| 110 | + | assert.Equal(t, int64(2), next) | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | func TestReadBoundsLinesAndBytesAndReportsMissingLogs(t *testing.T) { | |
| 114 | + | ctx := context.Background() | |
| 115 | + | s := cilogs.New(t.TempDir()) | |
| 116 | + | _, err := s.Read(ctx, ref0, 0, 10, 100) | |
| 117 | + | require.ErrorIs(t, err, domain.ErrNotFound) | |
| 118 | + | ||
| 119 | + | _, _, err = s.Append(ctx, ref0, 0, lines("12345", "12345", "12345", "12345"), big) | |
| 120 | + | require.NoError(t, err) | |
| 121 | + | got, err := s.Read(ctx, ref0, 0, 3, 1<<20) | |
| 122 | + | require.NoError(t, err) | |
| 123 | + | assert.Len(t, got, 3) | |
| 124 | + | got, err = s.Read(ctx, ref0, 0, 100, 12) | |
| 125 | + | require.NoError(t, err) | |
| 126 | + | assert.Len(t, got, 2, "the text of a batch is bounded") | |
| 127 | + | got, err = s.Read(ctx, ref0, 4, 10, 100) | |
| 128 | + | require.NoError(t, err) | |
| 129 | + | assert.Empty(t, got, "reading at the end is empty, not an error") | |
| 130 | + | _, err = s.Read(ctx, ref0, 5, 10, 100) | |
| 131 | + | assert.ErrorIs(t, err, domain.ErrOutOfRange) | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | func TestReadFindsLinesDeepInALargeLog(t *testing.T) { | |
| 135 | + | ctx := context.Background() | |
| 136 | + | s := cilogs.New(t.TempDir()) | |
| 137 | + | var all []domain.CILogLine | |
| 138 | + | for i := range 3000 { | |
| 139 | + | all = append(all, domain.CILogLine{Time: t0, Text: "line " + itoa(i)}) | |
| 140 | + | } | |
| 141 | + | for off := 0; off < len(all); off += 1000 { | |
| 142 | + | _, _, err := s.Append(ctx, ref0, int64(off), all[off:off+1000], big) | |
| 143 | + | require.NoError(t, err) | |
| 144 | + | } | |
| 145 | + | got, err := s.Read(ctx, ref0, 2500, 2, 1<<20) | |
| 146 | + | require.NoError(t, err) | |
| 147 | + | require.Len(t, got, 2) | |
| 148 | + | assert.Equal(t, "line 2500", got[0].Text) | |
| 149 | + | assert.Equal(t, "line 2501", got[1].Text) | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | func itoa(n int) string { | |
| 153 | + | if n == 0 { | |
| 154 | + | return "0" | |
| 155 | + | } | |
| 156 | + | var b []byte | |
| 157 | + | for n > 0 { | |
| 158 | + | b = append([]byte{byte('0' + n%10)}, b...) | |
| 159 | + | n /= 10 | |
| 160 | + | } | |
| 161 | + | return string(b) | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | func TestCountSurvivesARestartAndAChangedChannelFiresOnAppend(t *testing.T) { | |
| 165 | + | ctx := context.Background() | |
| 166 | + | dir := t.TempDir() | |
| 167 | + | s := cilogs.New(dir) | |
| 168 | + | ch := s.Changed(ref0) | |
| 169 | + | _, _, err := s.Append(ctx, ref0, 0, lines("a", "b"), big) | |
| 170 | + | require.NoError(t, err) | |
| 171 | + | select { | |
| 172 | + | case <-ch: | |
| 173 | + | default: | |
| 174 | + | t.Fatal("Changed did not fire on append") | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | s2 := cilogs.New(dir) | |
| 178 | + | n, err := s2.Count(ctx, ref0) | |
| 179 | + | require.NoError(t, err) | |
| 180 | + | assert.Equal(t, int64(2), n) | |
| 181 | + | _, _, err = s2.Append(ctx, ref0, 2, lines("c"), big) | |
| 182 | + | require.NoError(t, err) | |
| 183 | + | got, _ := s2.Read(ctx, ref0, 0, 10, 1<<20) | |
| 184 | + | assert.Len(t, got, 3) | |
| 185 | + | } | |
| 186 | + | ||
| 187 | + | func TestATornLastLineIsCutOnLoad(t *testing.T) { | |
| 188 | + | ctx := context.Background() | |
| 189 | + | dir := t.TempDir() | |
| 190 | + | s := cilogs.New(dir) | |
| 191 | + | _, _, err := s.Append(ctx, ref0, 0, lines("a", "b"), big) | |
| 192 | + | require.NoError(t, err) | |
| 193 | + | path := filepath.Join(dir, "7", "7-0-0", "1.log") | |
| 194 | + | f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0) | |
| 195 | + | require.NoError(t, err) | |
| 196 | + | _, err = f.WriteString("- 2026-10-12T10:00:00.1234") | |
| 197 | + | require.NoError(t, err) | |
| 198 | + | require.NoError(t, f.Close()) | |
| 199 | + | ||
| 200 | + | s2 := cilogs.New(dir) | |
| 201 | + | n, err := s2.Count(ctx, ref0) | |
| 202 | + | require.NoError(t, err) | |
| 203 | + | assert.Equal(t, int64(2), n) | |
| 204 | + | _, _, err = s2.Append(ctx, ref0, 2, lines("c"), big) | |
| 205 | + | require.NoError(t, err) | |
| 206 | + | got, err := s2.Read(ctx, ref0, 0, 10, 1<<20) | |
| 207 | + | require.NoError(t, err) | |
| 208 | + | assert.Len(t, got, 3) | |
| 209 | + | } | |
| 210 | + | ||
| 211 | + | func TestRemoveRunsAndResetAndRunIDs(t *testing.T) { | |
| 212 | + | ctx := context.Background() | |
| 213 | + | s := cilogs.New(t.TempDir()) | |
| 214 | + | other := ports.CILogRef{RunID: 9, JobID: "9-0-0", Attempt: 1} | |
| 215 | + | _, _, _ = s.Append(ctx, ref0, 0, lines("a"), big) | |
| 216 | + | _, _, _ = s.Append(ctx, other, 0, lines("b"), big) | |
| 217 | + | ids, err := s.RunIDs(ctx) | |
| 218 | + | require.NoError(t, err) | |
| 219 | + | assert.ElementsMatch(t, []int64{7, 9}, ids) | |
| 220 | + | ||
| 221 | + | require.NoError(t, s.Reset(ctx, ref0)) | |
| 222 | + | n, _ := s.Count(ctx, ref0) | |
| 223 | + | assert.Zero(t, n) | |
| 224 | + | ||
| 225 | + | require.NoError(t, s.RemoveRuns(ctx, []int64{9})) | |
| 226 | + | _, err = s.Read(ctx, other, 0, 10, 100) | |
| 227 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | func TestARefThatCouldEscapeTheDirectoryIsRefused(t *testing.T) { | |
| 231 | + | ctx := context.Background() | |
| 232 | + | dir := t.TempDir() | |
| 233 | + | s := cilogs.New(dir) | |
| 234 | + | _, _, err := s.Append(ctx, ports.CILogRef{RunID: 1, JobID: "../../etc", Attempt: 1}, 0, lines("x"), big) | |
| 235 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 236 | + | _, _, err = s.Append(ctx, ports.CILogRef{RunID: 1, JobID: "1-0-0", Attempt: 0}, 0, lines("x"), big) | |
| 237 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 238 | + | } | |
A internal/adapters/kvstore/ci.go +1013 -0
| @@ -0,0 +1,1013 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package kvstore | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "errors" | |
| 8 | + | "fmt" | |
| 9 | + | "slices" | |
| 10 | + | "strconv" | |
| 11 | + | "strings" | |
| 12 | + | "time" | |
| 13 | + | ||
| 14 | + | kv "bnema.dev/ermite-kv" | |
| 15 | + | "google.golang.org/protobuf/proto" | |
| 16 | + | ||
| 17 | + | storagepb "bnema.dev/ermite/gen/ermite/storage" | |
| 18 | + | "bnema.dev/ermite/internal/domain" | |
| 19 | + | "bnema.dev/ermite/internal/ports" | |
| 20 | + | ) | |
| 21 | + | ||
| 22 | + | // Key layout of CI. Repository names hold no '/', so a prefix ending in "/" | |
| 23 | + | // never matches another repository; ids are zero-padded so that keys sort | |
| 24 | + | // numerically. Job records are keyed by run, attempt of the run and job id. | |
| 25 | + | // | |
| 26 | + | // run/<repo>/<id:020d> CIRun | |
| 27 | + | // runsrc/<id:020d> CIRunSource | |
| 28 | + | // job/<run:020d>/<attempt:010d>/<id> CIJob | |
| 29 | + | // check/<repo>/<sha>/<job id> CICheck | |
| 30 | + | // runseq Sequence: last run id | |
| 31 | + | // runnum/<repo>/<workflow path> Sequence: last run number | |
| 32 | + | // runner/<id>, runnername/<name>, runnertoken/<hash> | |
| 33 | + | // | |
| 34 | + | // Indexes, derived from the job and run records by every write: | |
| 35 | + | // | |
| 36 | + | // ciopen/<id:020d> run not completed (value: repo) | |
| 37 | + | // ciready/<ready_at:020d>/<job key> job waiting for a runner | |
| 38 | + | // ciactive/<job key> job assigned or running | |
| 39 | + | // jobtoken/<hash> job token (value: job key) | |
| 40 | + | const ( | |
| 41 | + | runPrefix = "run/" | |
| 42 | + | runSrcPrefix = "runsrc/" | |
| 43 | + | jobPrefix = "job/" | |
| 44 | + | checkPrefix = "check/" | |
| 45 | + | runSeqKey = "runseq" | |
| 46 | + | runNumPrefix = "runnum/" | |
| 47 | + | runnerPrefix = "runner/" | |
| 48 | + | runnerName = "runnername/" | |
| 49 | + | runnerTokPref = "runnertoken/" | |
| 50 | + | openPrefix = "ciopen/" | |
| 51 | + | readyPrefix = "ciready/" | |
| 52 | + | activePrefix = "ciactive/" | |
| 53 | + | jobTokPrefix = "jobtoken/" | |
| 54 | + | idFormat = "%020d" | |
| 55 | + | ) | |
| 56 | + | ||
| 57 | + | // CI is the ermite-kv backed ports.CIStore. It shares the database of the | |
| 58 | + | // Store it comes from. | |
| 59 | + | type CI struct { | |
| 60 | + | db *kv.DB | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | var _ ports.CIStore = (*CI)(nil) | |
| 64 | + | ||
| 65 | + | // CI returns the CI store over the same database. It is valid until the Store | |
| 66 | + | // is closed. | |
| 67 | + | func (s *Store) CI() *CI { return &CI{db: s.db} } | |
| 68 | + | ||
| 69 | + | func runKey(repo domain.RepoName, id int64) []byte { | |
| 70 | + | return []byte(runPrefix + repo.String() + "/" + fmt.Sprintf(idFormat, id)) | |
| 71 | + | } | |
| 72 | + | func runsPrefix(repo domain.RepoName) string { return runPrefix + repo.String() + "/" } | |
| 73 | + | func runSrcKey(id int64) []byte { return []byte(runSrcPrefix + fmt.Sprintf(idFormat, id)) } | |
| 74 | + | func openKey(id int64) []byte { return []byte(openPrefix + fmt.Sprintf(idFormat, id)) } | |
| 75 | + | func jobsPrefix(runID int64) string { return jobPrefix + fmt.Sprintf(idFormat, runID) + "/" } | |
| 76 | + | func jobAttemptPrefix(runID int64, attempt int32) string { | |
| 77 | + | return jobsPrefix(runID) + fmt.Sprintf("%010d", attempt) + "/" | |
| 78 | + | } | |
| 79 | + | func jobRef(runID int64, attempt int32, id string) string { | |
| 80 | + | return jobAttemptPrefix(runID, attempt) + id | |
| 81 | + | } | |
| 82 | + | func jobKey(runID int64, attempt int32, id string) []byte { return []byte(jobRef(runID, attempt, id)) } | |
| 83 | + | func checksPrefix(repo domain.RepoName, sha string) string { | |
| 84 | + | return checkPrefix + repo.String() + "/" + sha + "/" | |
| 85 | + | } | |
| 86 | + | func checkKey(repo domain.RepoName, sha, jobID string) []byte { | |
| 87 | + | return []byte(checksPrefix(repo, sha) + jobID) | |
| 88 | + | } | |
| 89 | + | func runNumKey(repo domain.RepoName, workflow string) []byte { | |
| 90 | + | return []byte(runNumPrefix + repo.String() + "/" + workflow) | |
| 91 | + | } | |
| 92 | + | func readyKey(at time.Time, ref string) []byte { | |
| 93 | + | return []byte(readyPrefix + fmt.Sprintf(idFormat, at.UnixNano()) + "/" + ref) | |
| 94 | + | } | |
| 95 | + | func activeKey(ref string) []byte { return []byte(activePrefix + ref) } | |
| 96 | + | func jobTokenKey(h domain.TokenHash) []byte { | |
| 97 | + | return []byte(jobTokPrefix + h.String()) | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | // --- encoding --- | |
| 101 | + | ||
| 102 | + | func encodeStatus(s domain.CIStatus) storagepb.CIStatus { | |
| 103 | + | switch s { | |
| 104 | + | case domain.CIQueued: | |
| 105 | + | return storagepb.CIStatus_CI_STATUS_QUEUED | |
| 106 | + | case domain.CIInProgress: | |
| 107 | + | return storagepb.CIStatus_CI_STATUS_IN_PROGRESS | |
| 108 | + | case domain.CICompleted: | |
| 109 | + | return storagepb.CIStatus_CI_STATUS_COMPLETED | |
| 110 | + | } | |
| 111 | + | return storagepb.CIStatus_CI_STATUS_UNSPECIFIED | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | func decodeStatus(s storagepb.CIStatus) domain.CIStatus { | |
| 115 | + | switch s { | |
| 116 | + | case storagepb.CIStatus_CI_STATUS_IN_PROGRESS: | |
| 117 | + | return domain.CIInProgress | |
| 118 | + | case storagepb.CIStatus_CI_STATUS_COMPLETED: | |
| 119 | + | return domain.CICompleted | |
| 120 | + | } | |
| 121 | + | return domain.CIQueued | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | var conclusions = []struct { | |
| 125 | + | d domain.CIConclusion | |
| 126 | + | p storagepb.CIConclusion | |
| 127 | + | }{ | |
| 128 | + | {domain.CISuccess, storagepb.CIConclusion_CI_CONCLUSION_SUCCESS}, | |
| 129 | + | {domain.CIFailure, storagepb.CIConclusion_CI_CONCLUSION_FAILURE}, | |
| 130 | + | {domain.CICancelled, storagepb.CIConclusion_CI_CONCLUSION_CANCELLED}, | |
| 131 | + | {domain.CISkipped, storagepb.CIConclusion_CI_CONCLUSION_SKIPPED}, | |
| 132 | + | {domain.CITimedOut, storagepb.CIConclusion_CI_CONCLUSION_TIMED_OUT}, | |
| 133 | + | {domain.CINeutral, storagepb.CIConclusion_CI_CONCLUSION_NEUTRAL}, | |
| 134 | + | {domain.CIActionRequired, storagepb.CIConclusion_CI_CONCLUSION_ACTION_REQUIRED}, | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | func encodeConclusion(c domain.CIConclusion) storagepb.CIConclusion { | |
| 138 | + | for _, e := range conclusions { | |
| 139 | + | if e.d == c { | |
| 140 | + | return e.p | |
| 141 | + | } | |
| 142 | + | } | |
| 143 | + | return storagepb.CIConclusion_CI_CONCLUSION_UNSPECIFIED | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | func decodeConclusion(c storagepb.CIConclusion) domain.CIConclusion { | |
| 147 | + | for _, e := range conclusions { | |
| 148 | + | if e.p == c { | |
| 149 | + | return e.d | |
| 150 | + | } | |
| 151 | + | } | |
| 152 | + | return domain.CINoConclusion | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | var phases = []struct { | |
| 156 | + | d domain.CIJobPhase | |
| 157 | + | p storagepb.CIJobPhase | |
| 158 | + | }{ | |
| 159 | + | {domain.CIPhaseWaiting, storagepb.CIJobPhase_CI_JOB_PHASE_WAITING}, | |
| 160 | + | {domain.CIPhaseReady, storagepb.CIJobPhase_CI_JOB_PHASE_READY}, | |
| 161 | + | {domain.CIPhaseAssigned, storagepb.CIJobPhase_CI_JOB_PHASE_ASSIGNED}, | |
| 162 | + | {domain.CIPhaseRunning, storagepb.CIJobPhase_CI_JOB_PHASE_RUNNING}, | |
| 163 | + | {domain.CIPhaseEnded, storagepb.CIJobPhase_CI_JOB_PHASE_ENDED}, | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | func encodePhase(p domain.CIJobPhase) (storagepb.CIJobPhase, error) { | |
| 167 | + | for _, e := range phases { | |
| 168 | + | if e.d == p { | |
| 169 | + | return e.p, nil | |
| 170 | + | } | |
| 171 | + | } | |
| 172 | + | return 0, fmt.Errorf("invalid job phase %q", p) | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | func decodePhase(p storagepb.CIJobPhase) domain.CIJobPhase { | |
| 176 | + | for _, e := range phases { | |
| 177 | + | if e.p == p { | |
| 178 | + | return e.d | |
| 179 | + | } | |
| 180 | + | } | |
| 181 | + | return domain.CIPhaseWaiting | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | var isolations = []struct { | |
| 185 | + | d domain.CIIsolation | |
| 186 | + | p storagepb.CIIsolation | |
| 187 | + | }{ | |
| 188 | + | {domain.CIIsolationVM, storagepb.CIIsolation_CI_ISOLATION_VM}, | |
| 189 | + | {domain.CIIsolationContainerSysbox, storagepb.CIIsolation_CI_ISOLATION_CONTAINER_SYSBOX}, | |
| 190 | + | {domain.CIIsolationContainerPrivileged, storagepb.CIIsolation_CI_ISOLATION_CONTAINER_PRIVILEGED}, | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | func encodeIsolation(i domain.CIIsolation) storagepb.CIIsolation { | |
| 194 | + | for _, e := range isolations { | |
| 195 | + | if e.d == i { | |
| 196 | + | return e.p | |
| 197 | + | } | |
| 198 | + | } | |
| 199 | + | return storagepb.CIIsolation_CI_ISOLATION_UNSPECIFIED | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | func decodeIsolation(i storagepb.CIIsolation) domain.CIIsolation { | |
| 203 | + | for _, e := range isolations { | |
| 204 | + | if e.p == i { | |
| 205 | + | return e.d | |
| 206 | + | } | |
| 207 | + | } | |
| 208 | + | return domain.CIIsolationNone | |
| 209 | + | } | |
| 210 | + | ||
| 211 | + | var stepStates = []struct { | |
| 212 | + | d domain.CIStepState | |
| 213 | + | p storagepb.CIStepState | |
| 214 | + | }{ | |
| 215 | + | {domain.CIStepPending, storagepb.CIStepState_CI_STEP_STATE_PENDING}, | |
| 216 | + | {domain.CIStepInProgress, storagepb.CIStepState_CI_STEP_STATE_IN_PROGRESS}, | |
| 217 | + | {domain.CIStepCompleted, storagepb.CIStepState_CI_STEP_STATE_COMPLETED}, | |
| 218 | + | } | |
| 219 | + | ||
| 220 | + | func encodeStepState(s domain.CIStepState) storagepb.CIStepState { | |
| 221 | + | for _, e := range stepStates { | |
| 222 | + | if e.d == s { | |
| 223 | + | return e.p | |
| 224 | + | } | |
| 225 | + | } | |
| 226 | + | return storagepb.CIStepState_CI_STEP_STATE_UNSPECIFIED | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | func decodeStepState(s storagepb.CIStepState) domain.CIStepState { | |
| 230 | + | for _, e := range stepStates { | |
| 231 | + | if e.p == s { | |
| 232 | + | return e.d | |
| 233 | + | } | |
| 234 | + | } | |
| 235 | + | return domain.CIStepPending | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | var cancelReasons = []domain.CICancelReason{domain.CICancelNone, domain.CICancelUser, domain.CICancelFailFast, domain.CICancelTimeout} | |
| 239 | + | ||
| 240 | + | func encodeRun(r domain.CIRun) ([]byte, error) { | |
| 241 | + | if r.Repo.IsZero() { | |
| 242 | + | return nil, fmt.Errorf("%w: empty name", domain.ErrInvalidName) | |
| 243 | + | } | |
| 244 | + | return mustMarshal(&storagepb.CIRun{ | |
| 245 | + | Repo: r.Repo.String(), Id: r.ID, Number: r.Number, Attempt: r.Attempt, | |
| 246 | + | WorkflowPath: r.WorkflowPath, WorkflowName: r.WorkflowName, Title: r.Title, | |
| 247 | + | Event: r.Event, Ref: r.Ref, Sha: r.Sha, Actor: r.Actor, | |
| 248 | + | Status: encodeStatus(r.Status), Conclusion: encodeConclusion(r.Conclusion), Error: r.Error, | |
| 249 | + | CreatedAt: encodeTime(r.CreatedAt), StartedAt: encodeTime(r.StartedAt), CompletedAt: encodeTime(r.CompletedAt), | |
| 250 | + | CancelRequested: r.CancelRequested, CancelRequestedAt: encodeTime(r.CancelRequestedAt), | |
| 251 | + | }), nil | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | func decodeRun(item kv.Item) (domain.CIRun, error) { | |
| 255 | + | var pb storagepb.CIRun | |
| 256 | + | if err := proto.Unmarshal(item.Value, &pb); err != nil { | |
| 257 | + | return domain.CIRun{}, fmt.Errorf("decode run: %w", err) | |
| 258 | + | } | |
| 259 | + | repo, err := domain.ParseRepoName(pb.GetRepo()) | |
| 260 | + | if err != nil { | |
| 261 | + | return domain.CIRun{}, fmt.Errorf("decode run: %w", err) | |
| 262 | + | } | |
| 263 | + | return domain.CIRun{ | |
| 264 | + | Repo: repo, ID: pb.GetId(), Number: pb.GetNumber(), Attempt: pb.GetAttempt(), | |
| 265 | + | WorkflowPath: pb.GetWorkflowPath(), WorkflowName: pb.GetWorkflowName(), Title: pb.GetTitle(), | |
| 266 | + | Event: pb.GetEvent(), Ref: pb.GetRef(), Sha: pb.GetSha(), Actor: pb.GetActor(), | |
| 267 | + | Status: decodeStatus(pb.GetStatus()), Conclusion: decodeConclusion(pb.GetConclusion()), Error: pb.GetError(), | |
| 268 | + | CreatedAt: decodeTime(pb.GetCreatedAt()), StartedAt: decodeTime(pb.GetStartedAt()), CompletedAt: decodeTime(pb.GetCompletedAt()), | |
| 269 | + | CancelRequested: pb.GetCancelRequested(), CancelRequestedAt: decodeTime(pb.GetCancelRequestedAt()), | |
| 270 | + | Version: item.Version, | |
| 271 | + | }, nil | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | func encodeJob(j domain.CIJob) ([]byte, error) { | |
| 275 | + | if j.Repo.IsZero() { | |
| 276 | + | return nil, fmt.Errorf("%w: empty name", domain.ErrInvalidName) | |
| 277 | + | } | |
| 278 | + | phase, err := encodePhase(j.Phase) | |
| 279 | + | if err != nil { | |
| 280 | + | return nil, err | |
| 281 | + | } | |
| 282 | + | steps := make([]*storagepb.CIStep, 0, len(j.Steps)) | |
| 283 | + | for _, s := range j.Steps { | |
| 284 | + | steps = append(steps, &storagepb.CIStep{ | |
| 285 | + | Number: s.Number, Name: s.Name, State: encodeStepState(s.State), Conclusion: encodeConclusion(s.Conclusion), | |
| 286 | + | StartedAt: encodeTime(s.StartedAt), CompletedAt: encodeTime(s.CompletedAt), | |
| 287 | + | LogStart: s.LogStart, HasLines: s.HasLines, | |
| 288 | + | }) | |
| 289 | + | } | |
| 290 | + | reason := int32(slices.Index(cancelReasons, j.CancelReason)) | |
| 291 | + | pb := &storagepb.CIJob{ | |
| 292 | + | Id: j.ID, RunId: j.RunID, Attempt: j.Attempt, RunAttempt: j.RunAttempt, Repo: j.Repo.String(), | |
| 293 | + | Key: j.Key, Name: j.Name, Phase: phase, Conclusion: encodeConclusion(j.Conclusion), Error: j.Error, | |
| 294 | + | Labels: j.Labels, Needs: j.Needs, RunnerId: j.RunnerID, RunnerName: j.RunnerName, | |
| 295 | + | Isolation: encodeIsolation(j.Isolation), Steps: steps, | |
| 296 | + | CreatedAt: encodeTime(j.CreatedAt), StartedAt: encodeTime(j.StartedAt), CompletedAt: encodeTime(j.CompletedAt), | |
| 297 | + | Index: int32(j.Index), Combo: int32(j.Combo), MatrixJson: j.MatrixJSON, ComboTotal: int32(j.ComboTotal), //nolint:gosec // bounded by the 256 job limit | |
| 298 | + | TimeoutMinutes: j.TimeoutMinutes, FailFast: j.FailFast, MaxParallel: int32(j.MaxParallel), //nolint:gosec // validated at planning | |
| 299 | + | Outputs: j.Outputs, ReadyAt: encodeTime(j.ReadyAt), AssignedAt: encodeTime(j.AssignedAt), | |
| 300 | + | AckedAt: encodeTime(j.AckedAt), AssignCancels: int32(j.AssignCancels), //nolint:gosec // at most 3 | |
| 301 | + | TokenValidUntil: encodeTime(j.TokenValidUntil), CancelReason: max(reason, 0), CancelMessage: j.CancelMessage, | |
| 302 | + | CancelRequestedAt: encodeTime(j.CancelRequestedAt), Planned: j.Planned, | |
| 303 | + | } | |
| 304 | + | if j.HasToken() { | |
| 305 | + | pb.TokenHash = j.TokenHash[:] | |
| 306 | + | } | |
| 307 | + | return mustMarshal(pb), nil | |
| 308 | + | } | |
| 309 | + | ||
| 310 | + | //hexcheck:ignore suspicious-business-logic-in-adapter maps the stored record to the domain job field by field: no rule is decided here | |
| 311 | + | func decodeJob(item kv.Item) (domain.CIJob, error) { | |
| 312 | + | var pb storagepb.CIJob | |
| 313 | + | if err := proto.Unmarshal(item.Value, &pb); err != nil { | |
| 314 | + | return domain.CIJob{}, fmt.Errorf("decode job: %w", err) | |
| 315 | + | } | |
| 316 | + | repo, err := domain.ParseRepoName(pb.GetRepo()) | |
| 317 | + | if err != nil { | |
| 318 | + | return domain.CIJob{}, fmt.Errorf("decode job: %w", err) | |
| 319 | + | } | |
| 320 | + | j := domain.CIJob{ | |
| 321 | + | ID: pb.GetId(), RunID: pb.GetRunId(), Attempt: pb.GetAttempt(), RunAttempt: pb.GetRunAttempt(), Repo: repo, | |
| 322 | + | Key: pb.GetKey(), Name: pb.GetName(), Phase: decodePhase(pb.GetPhase()), Conclusion: decodeConclusion(pb.GetConclusion()), | |
| 323 | + | Error: pb.GetError(), Labels: pb.GetLabels(), Needs: pb.GetNeeds(), RunnerID: pb.GetRunnerId(), RunnerName: pb.GetRunnerName(), | |
| 324 | + | Isolation: decodeIsolation(pb.GetIsolation()), | |
| 325 | + | CreatedAt: decodeTime(pb.GetCreatedAt()), StartedAt: decodeTime(pb.GetStartedAt()), CompletedAt: decodeTime(pb.GetCompletedAt()), | |
| 326 | + | Index: int(pb.GetIndex()), Combo: int(pb.GetCombo()), Planned: pb.GetPlanned(), MatrixJSON: pb.GetMatrixJson(), | |
| 327 | + | ComboTotal: int(pb.GetComboTotal()), TimeoutMinutes: pb.GetTimeoutMinutes(), FailFast: pb.GetFailFast(), | |
| 328 | + | MaxParallel: int(pb.GetMaxParallel()), Outputs: pb.GetOutputs(), | |
| 329 | + | TokenValidUntil: decodeTime(pb.GetTokenValidUntil()), ReadyAt: decodeTime(pb.GetReadyAt()), | |
| 330 | + | AssignedAt: decodeTime(pb.GetAssignedAt()), AckedAt: decodeTime(pb.GetAckedAt()), AssignCancels: int(pb.GetAssignCancels()), | |
| 331 | + | CancelMessage: pb.GetCancelMessage(), CancelRequestedAt: decodeTime(pb.GetCancelRequestedAt()), | |
| 332 | + | Version: item.Version, | |
| 333 | + | } | |
| 334 | + | if r := int(pb.GetCancelReason()); r >= 0 && r < len(cancelReasons) { | |
| 335 | + | j.CancelReason = cancelReasons[r] | |
| 336 | + | } | |
| 337 | + | copy(j.TokenHash[:], pb.GetTokenHash()) | |
| 338 | + | for _, s := range pb.GetSteps() { | |
| 339 | + | j.Steps = append(j.Steps, domain.CIStep{ | |
| 340 | + | Number: s.GetNumber(), Name: s.GetName(), State: decodeStepState(s.GetState()), Conclusion: decodeConclusion(s.GetConclusion()), | |
| 341 | + | StartedAt: decodeTime(s.GetStartedAt()), CompletedAt: decodeTime(s.GetCompletedAt()), | |
| 342 | + | LogStart: s.GetLogStart(), HasLines: s.GetHasLines(), | |
| 343 | + | }) | |
| 344 | + | } | |
| 345 | + | return j, nil | |
| 346 | + | } | |
| 347 | + | ||
| 348 | + | func encodeCheck(c domain.CICheck) []byte { | |
| 349 | + | return mustMarshal(&storagepb.CICheck{ | |
| 350 | + | Id: c.ID, Repo: c.Repo.String(), Sha: c.Sha, Name: c.Name, Status: encodeStatus(c.Status), | |
| 351 | + | Conclusion: encodeConclusion(c.Conclusion), RunId: c.RunID, JobId: c.JobID, Attempt: c.Attempt, | |
| 352 | + | WorkflowName: c.WorkflowName, CreatedAt: encodeTime(c.CreatedAt), StartedAt: encodeTime(c.StartedAt), | |
| 353 | + | CompletedAt: encodeTime(c.CompletedAt), | |
| 354 | + | }) | |
| 355 | + | } | |
| 356 | + | ||
| 357 | + | func decodeCheck(item kv.Item) (domain.CICheck, error) { | |
| 358 | + | var pb storagepb.CICheck | |
| 359 | + | if err := proto.Unmarshal(item.Value, &pb); err != nil { | |
| 360 | + | return domain.CICheck{}, fmt.Errorf("decode check: %w", err) | |
| 361 | + | } | |
| 362 | + | repo, err := domain.ParseRepoName(pb.GetRepo()) | |
| 363 | + | if err != nil { | |
| 364 | + | return domain.CICheck{}, fmt.Errorf("decode check: %w", err) | |
| 365 | + | } | |
| 366 | + | return domain.CICheck{ | |
| 367 | + | ID: pb.GetId(), Repo: repo, Sha: pb.GetSha(), Name: pb.GetName(), Status: decodeStatus(pb.GetStatus()), | |
| 368 | + | Conclusion: decodeConclusion(pb.GetConclusion()), RunID: pb.GetRunId(), JobID: pb.GetJobId(), Attempt: pb.GetAttempt(), | |
| 369 | + | WorkflowName: pb.GetWorkflowName(), CreatedAt: decodeTime(pb.GetCreatedAt()), StartedAt: decodeTime(pb.GetStartedAt()), | |
| 370 | + | CompletedAt: decodeTime(pb.GetCompletedAt()), Version: item.Version, | |
| 371 | + | }, nil | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | func encodeRunner(r domain.CIRunner) []byte { | |
| 375 | + | return mustMarshal(&storagepb.CIRunner{ | |
| 376 | + | Id: r.ID, Name: r.Name, Labels: r.Labels, Isolation: encodeIsolation(r.Isolation), Version: r.Version, | |
| 377 | + | Arch: r.Arch, Os: r.OS, RegisteredAt: encodeTime(r.RegisteredAt), TokenHash: r.TokenHash[:], | |
| 378 | + | }) | |
| 379 | + | } | |
| 380 | + | ||
| 381 | + | func decodeRunner(val []byte) (domain.CIRunner, error) { | |
| 382 | + | var pb storagepb.CIRunner | |
| 383 | + | if err := proto.Unmarshal(val, &pb); err != nil { | |
| 384 | + | return domain.CIRunner{}, fmt.Errorf("decode runner: %w", err) | |
| 385 | + | } | |
| 386 | + | r := domain.CIRunner{ | |
| 387 | + | ID: pb.GetId(), Name: pb.GetName(), Labels: pb.GetLabels(), Isolation: decodeIsolation(pb.GetIsolation()), | |
| 388 | + | Version: pb.GetVersion(), Arch: pb.GetArch(), OS: pb.GetOs(), RegisteredAt: decodeTime(pb.GetRegisteredAt()), | |
| 389 | + | } | |
| 390 | + | copy(r.TokenHash[:], pb.GetTokenHash()) | |
| 391 | + | return r, nil | |
| 392 | + | } | |
| 393 | + | ||
| 394 | + | // --- writes --- | |
| 395 | + | ||
| 396 | + | // stage adds the preconditions and writes of one record to the batch. | |
| 397 | + | func stage(b *kv.Batch, key, val []byte, version uint64) { | |
| 398 | + | if version == 0 { | |
| 399 | + | b.RequireAbsent(key) | |
| 400 | + | } else { | |
| 401 | + | b.RequireVersion(key, version) | |
| 402 | + | } | |
| 403 | + | b.Put(key, val) | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | // runIndexes stages the open-run index of the run. | |
| 407 | + | func runIndexes(b *kv.Batch, r domain.CIRun) { | |
| 408 | + | if r.Status == domain.CICompleted { | |
| 409 | + | b.Delete(openKey(r.ID)) | |
| 410 | + | return | |
| 411 | + | } | |
| 412 | + | b.Put(openKey(r.ID), []byte(r.Repo.String())) | |
| 413 | + | } | |
| 414 | + | ||
| 415 | + | // jobIndexes stages the ready, active and token indexes of the job. old is | |
| 416 | + | // the job as stored before, if any: its token index goes when the token | |
| 417 | + | // changed. | |
| 418 | + | func (c *CI) jobIndexes(b *kv.Batch, j domain.CIJob, old *domain.CIJob) { | |
| 419 | + | ref := jobRef(j.RunID, j.RunAttempt, j.ID) | |
| 420 | + | if j.Phase == domain.CIPhaseReady { | |
| 421 | + | b.Put(readyKey(j.ReadyAt, ref), nil) | |
| 422 | + | } else { | |
| 423 | + | b.Delete(readyKey(j.ReadyAt, ref)) | |
| 424 | + | } | |
| 425 | + | if old != nil && old.Phase == domain.CIPhaseReady && (j.Phase != domain.CIPhaseReady || !old.ReadyAt.Equal(j.ReadyAt)) { | |
| 426 | + | b.Delete(readyKey(old.ReadyAt, ref)) | |
| 427 | + | } | |
| 428 | + | if j.Active() { | |
| 429 | + | b.Put(activeKey(ref), nil) | |
| 430 | + | } else { | |
| 431 | + | b.Delete(activeKey(ref)) | |
| 432 | + | } | |
| 433 | + | if old != nil && old.HasToken() && old.TokenHash != j.TokenHash { | |
| 434 | + | b.Delete(jobTokenKey(old.TokenHash)) | |
| 435 | + | } | |
| 436 | + | if j.HasToken() { | |
| 437 | + | b.Put(jobTokenKey(j.TokenHash), []byte(ref)) | |
| 438 | + | } | |
| 439 | + | } | |
| 440 | + | ||
| 441 | + | // guardRepo makes the batch depend on the repository, like | |
| 442 | + | // MergeRequests.guardRepo, for the writes that create keys under it. | |
| 443 | + | func (c *CI) guardRepo(ctx context.Context, b *kv.Batch, repo domain.RepoName) error { | |
| 444 | + | item, ok := c.db.Get(ctx, repoKey(repo)) | |
| 445 | + | if !ok { | |
| 446 | + | return domain.ErrNotFound | |
| 447 | + | } | |
| 448 | + | b.RequireVersion(repoKey(repo), item.Version) | |
| 449 | + | b.Put(repoKey(repo), item.Value) | |
| 450 | + | return nil | |
| 451 | + | } | |
| 452 | + | ||
| 453 | + | // stageRecords stages the writes of w. guarded collects the repositories that | |
| 454 | + | // need guarding. | |
| 455 | + | func (c *CI) stageRecords(ctx context.Context, b *kv.Batch, w ports.CIWrite) (guarded map[domain.RepoName]bool, err error) { | |
| 456 | + | guarded = map[domain.RepoName]bool{} | |
| 457 | + | for _, r := range w.Runs { | |
| 458 | + | val, err := encodeRun(r) | |
| 459 | + | if err != nil { | |
| 460 | + | return nil, err | |
| 461 | + | } | |
| 462 | + | stage(b, runKey(r.Repo, r.ID), val, r.Version) | |
| 463 | + | runIndexes(b, r) | |
| 464 | + | if r.Version == 0 { | |
| 465 | + | guarded[r.Repo] = true | |
| 466 | + | } | |
| 467 | + | } | |
| 468 | + | for _, j := range w.Jobs { | |
| 469 | + | val, err := encodeJob(j) | |
| 470 | + | if err != nil { | |
| 471 | + | return nil, err | |
| 472 | + | } | |
| 473 | + | key := jobKey(j.RunID, j.RunAttempt, j.ID) | |
| 474 | + | var old *domain.CIJob | |
| 475 | + | if j.Version != 0 { | |
| 476 | + | item, ok := c.db.Get(ctx, key) | |
| 477 | + | if !ok { | |
| 478 | + | return nil, domain.ErrConflict | |
| 479 | + | } | |
| 480 | + | if o, err := decodeJob(item); err == nil { | |
| 481 | + | old = &o | |
| 482 | + | } | |
| 483 | + | } | |
| 484 | + | stage(b, key, val, j.Version) | |
| 485 | + | c.jobIndexes(b, j, old) | |
| 486 | + | if j.Version == 0 { | |
| 487 | + | guarded[j.Repo] = true | |
| 488 | + | } | |
| 489 | + | } | |
| 490 | + | for _, ck := range w.Checks { | |
| 491 | + | stage(b, checkKey(ck.Repo, ck.Sha, ck.JobID), encodeCheck(ck), ck.Version) | |
| 492 | + | if ck.Version == 0 { | |
| 493 | + | guarded[ck.Repo] = true | |
| 494 | + | } | |
| 495 | + | } | |
| 496 | + | return guarded, nil | |
| 497 | + | } | |
| 498 | + | ||
| 499 | + | // Apply writes the records in one batch. | |
| 500 | + | // | |
| 501 | + | //hexcheck:ignore suspicious-business-logic-in-adapter stages records and their derived indexes under version preconditions: a storage concern, no policy | |
| 502 | + | func (c *CI) Apply(ctx context.Context, w ports.CIWrite) (uint64, error) { | |
| 503 | + | var b kv.Batch | |
| 504 | + | guarded, err := c.stageRecords(ctx, &b, w) | |
| 505 | + | if err != nil { | |
| 506 | + | return 0, err | |
| 507 | + | } | |
| 508 | + | for repo := range guarded { | |
| 509 | + | if err := c.guardRepo(ctx, &b, repo); err != nil { | |
| 510 | + | return 0, err | |
| 511 | + | } | |
| 512 | + | } | |
| 513 | + | v, err := c.db.Apply(ctx, &b) | |
| 514 | + | if errors.Is(err, kv.ErrConflict) { | |
| 515 | + | return 0, domain.ErrConflict | |
| 516 | + | } | |
| 517 | + | if err != nil { | |
| 518 | + | return 0, fmt.Errorf("write CI records: %w", err) | |
| 519 | + | } | |
| 520 | + | return v, nil | |
| 521 | + | } | |
| 522 | + | ||
| 523 | + | func (c *CI) readSeq(ctx context.Context, key []byte) (last int64, item kv.Item, ok bool, err error) { | |
| 524 | + | item, ok = c.db.Get(ctx, key) | |
| 525 | + | if !ok { | |
| 526 | + | return 0, kv.Item{}, false, nil | |
| 527 | + | } | |
| 528 | + | var seq storagepb.Sequence | |
| 529 | + | if err := proto.Unmarshal(item.Value, &seq); err != nil { | |
| 530 | + | return 0, kv.Item{}, false, fmt.Errorf("decode sequence %s: %w", key, err) | |
| 531 | + | } | |
| 532 | + | return seq.GetLast(), item, true, nil | |
| 533 | + | } | |
| 534 | + | ||
| 535 | + | func stageSeq(b *kv.Batch, key []byte, item kv.Item, ok bool, last int64) { | |
| 536 | + | if ok { | |
| 537 | + | b.RequireVersion(key, item.Version) | |
| 538 | + | } else { | |
| 539 | + | b.RequireAbsent(key) | |
| 540 | + | } | |
| 541 | + | b.Put(key, mustMarshal(&storagepb.Sequence{Last: last})) | |
| 542 | + | } | |
| 543 | + | ||
| 544 | + | const createRunTries = 20 | |
| 545 | + | ||
| 546 | + | // CreateRun takes the next run id and number and stores the draft. | |
| 547 | + | // | |
| 548 | + | //hexcheck:ignore suspicious-business-logic-in-adapter takes the next numbers under version preconditions: a storage concern, no policy | |
| 549 | + | func (c *CI) CreateRun(ctx context.Context, repo domain.RepoName, workflowPath string, build func(id, number int64) ports.CIRunDraft) (ports.CIRunDraft, error) { | |
| 550 | + | for range createRunTries { | |
| 551 | + | lastID, idItem, idOK, err := c.readSeq(ctx, []byte(runSeqKey)) | |
| 552 | + | if err != nil { | |
| 553 | + | return ports.CIRunDraft{}, err | |
| 554 | + | } | |
| 555 | + | numKey := runNumKey(repo, workflowPath) | |
| 556 | + | lastNum, numItem, numOK, err := c.readSeq(ctx, numKey) | |
| 557 | + | if err != nil { | |
| 558 | + | return ports.CIRunDraft{}, err | |
| 559 | + | } | |
| 560 | + | draft := build(lastID+1, lastNum+1) | |
| 561 | + | var b kv.Batch | |
| 562 | + | stageSeq(&b, []byte(runSeqKey), idItem, idOK, lastID+1) | |
| 563 | + | stageSeq(&b, numKey, numItem, numOK, lastNum+1) | |
| 564 | + | w := ports.CIWrite{Runs: []domain.CIRun{draft.Run}, Jobs: draft.Jobs, Checks: draft.Checks} | |
| 565 | + | guarded, err := c.stageRecords(ctx, &b, w) | |
| 566 | + | if err != nil { | |
| 567 | + | return ports.CIRunDraft{}, err | |
| 568 | + | } | |
| 569 | + | src := mustMarshal(&storagepb.CIRunSource{ | |
| 570 | + | RunId: draft.Run.ID, Repo: repo.String(), WorkflowYaml: draft.Source.WorkflowYAML, GithubJson: draft.Source.GithubJSON, | |
| 571 | + | }) | |
| 572 | + | b.RequireAbsent(runSrcKey(draft.Run.ID)) | |
| 573 | + | b.Put(runSrcKey(draft.Run.ID), src) | |
| 574 | + | for r := range guarded { | |
| 575 | + | if err := c.guardRepo(ctx, &b, r); err != nil { | |
| 576 | + | return ports.CIRunDraft{}, err | |
| 577 | + | } | |
| 578 | + | } | |
| 579 | + | v, err := c.db.Apply(ctx, &b) | |
| 580 | + | if errors.Is(err, kv.ErrConflict) { | |
| 581 | + | continue | |
| 582 | + | } | |
| 583 | + | if err != nil { | |
| 584 | + | return ports.CIRunDraft{}, fmt.Errorf("create run of %q: %w", repo, err) | |
| 585 | + | } | |
| 586 | + | draft.Run.Version = v | |
| 587 | + | for i := range draft.Jobs { | |
| 588 | + | draft.Jobs[i].Version = v | |
| 589 | + | } | |
| 590 | + | for i := range draft.Checks { | |
| 591 | + | draft.Checks[i].Version = v | |
| 592 | + | } | |
| 593 | + | return draft, nil | |
| 594 | + | } | |
| 595 | + | return ports.CIRunDraft{}, fmt.Errorf("create run of %q: %w", repo, domain.ErrConflict) | |
| 596 | + | } | |
| 597 | + | ||
| 598 | + | // --- reads --- | |
| 599 | + | ||
| 600 | + | // GetRun reads run/<repo>/<id>. | |
| 601 | + | func (c *CI) GetRun(ctx context.Context, repo domain.RepoName, id int64) (domain.CIRun, error) { | |
| 602 | + | item, ok := c.db.Get(ctx, runKey(repo, id)) | |
| 603 | + | if !ok || id < 1 { | |
| 604 | + | return domain.CIRun{}, domain.ErrNotFound | |
| 605 | + | } | |
| 606 | + | return decodeRun(item) | |
| 607 | + | } | |
| 608 | + | ||
| 609 | + | // GetRunSource reads runsrc/<id>. | |
| 610 | + | func (c *CI) GetRunSource(ctx context.Context, runID int64) (domain.CIRunSource, error) { | |
| 611 | + | item, ok := c.db.Get(ctx, runSrcKey(runID)) | |
| 612 | + | if !ok || runID < 1 { | |
| 613 | + | return domain.CIRunSource{}, domain.ErrNotFound | |
| 614 | + | } | |
| 615 | + | var pb storagepb.CIRunSource | |
| 616 | + | if err := proto.Unmarshal(item.Value, &pb); err != nil { | |
| 617 | + | return domain.CIRunSource{}, fmt.Errorf("decode run source: %w", err) | |
| 618 | + | } | |
| 619 | + | repo, err := domain.ParseRepoName(pb.GetRepo()) | |
| 620 | + | if err != nil { | |
| 621 | + | return domain.CIRunSource{}, fmt.Errorf("decode run source: %w", err) | |
| 622 | + | } | |
| 623 | + | return domain.CIRunSource{RunID: pb.GetRunId(), Repo: repo, WorkflowYAML: pb.GetWorkflowYaml(), GithubJSON: pb.GetGithubJson()}, nil | |
| 624 | + | } | |
| 625 | + | ||
| 626 | + | // ListRuns returns the runs of the repository, newest first. | |
| 627 | + | // | |
| 628 | + | //hexcheck:ignore suspicious-business-logic-in-adapter port method name only: reads a key range and reverses it | |
| 629 | + | func (c *CI) ListRuns(ctx context.Context, repo domain.RepoName, before int64, limit int) ([]domain.CIRun, error) { | |
| 630 | + | start := []byte(runsPrefix(repo)) | |
| 631 | + | end := []byte(runPrefix + repo.String() + "0") // '/' + 1 | |
| 632 | + | if before > 0 { | |
| 633 | + | end = runKey(repo, before) | |
| 634 | + | } | |
| 635 | + | // Collect then reverse: the snapshot iterates in key order only. | |
| 636 | + | var all []domain.CIRun | |
| 637 | + | for key, item := range c.db.Snapshot(ctx).Range(ctx, start, end) { | |
| 638 | + | r, err := decodeRun(item) | |
| 639 | + | if err != nil { | |
| 640 | + | return nil, fmt.Errorf("decode %s: %w", key, err) | |
| 641 | + | } | |
| 642 | + | all = append(all, r) | |
| 643 | + | } | |
| 644 | + | slices.Reverse(all) | |
| 645 | + | if limit > 0 && len(all) > limit { | |
| 646 | + | all = all[:limit] | |
| 647 | + | } | |
| 648 | + | return all, nil | |
| 649 | + | } | |
| 650 | + | ||
| 651 | + | // RunIDs returns the ids of the runs of the repository. | |
| 652 | + | func (c *CI) RunIDs(ctx context.Context, repo domain.RepoName) ([]int64, error) { | |
| 653 | + | var ids []int64 | |
| 654 | + | prefix := runsPrefix(repo) | |
| 655 | + | for key := range c.db.Snapshot(ctx).Prefix(ctx, []byte(prefix)) { | |
| 656 | + | id, err := strconv.ParseInt(strings.TrimPrefix(string(key), prefix), 10, 64) | |
| 657 | + | if err != nil { | |
| 658 | + | return nil, fmt.Errorf("decode %s: %w", key, err) | |
| 659 | + | } | |
| 660 | + | ids = append(ids, id) | |
| 661 | + | } | |
| 662 | + | return ids, nil | |
| 663 | + | } | |
| 664 | + | ||
| 665 | + | // ListOpenRuns returns the runs that have not completed. | |
| 666 | + | func (c *CI) ListOpenRuns(ctx context.Context) ([]domain.CIRun, error) { | |
| 667 | + | snap := c.db.Snapshot(ctx) | |
| 668 | + | var out []domain.CIRun | |
| 669 | + | for key, item := range snap.Prefix(ctx, []byte(openPrefix)) { | |
| 670 | + | id, err := strconv.ParseInt(strings.TrimPrefix(string(key), openPrefix), 10, 64) | |
| 671 | + | if err != nil { | |
| 672 | + | return nil, fmt.Errorf("decode %s: %w", key, err) | |
| 673 | + | } | |
| 674 | + | repo, err := domain.ParseRepoName(string(item.Value)) | |
| 675 | + | if err != nil { | |
| 676 | + | return nil, err | |
| 677 | + | } | |
| 678 | + | it, ok := snap.Get(ctx, runKey(repo, id)) | |
| 679 | + | if !ok { | |
| 680 | + | continue | |
| 681 | + | } | |
| 682 | + | r, err := decodeRun(it) | |
| 683 | + | if err != nil { | |
| 684 | + | return nil, err | |
| 685 | + | } | |
| 686 | + | out = append(out, r) | |
| 687 | + | } | |
| 688 | + | return out, nil | |
| 689 | + | } | |
| 690 | + | ||
| 691 | + | // GetJob reads one job record. | |
| 692 | + | func (c *CI) GetJob(ctx context.Context, runID int64, runAttempt int32, jobID string) (domain.CIJob, error) { | |
| 693 | + | item, ok := c.db.Get(ctx, jobKey(runID, runAttempt, jobID)) | |
| 694 | + | if !ok { | |
| 695 | + | return domain.CIJob{}, domain.ErrNotFound | |
| 696 | + | } | |
| 697 | + | return decodeJob(item) | |
| 698 | + | } | |
| 699 | + | ||
| 700 | + | // ListJobs returns the jobs of a run at an attempt, ordered by index then | |
| 701 | + | // combination. | |
| 702 | + | func (c *CI) ListJobs(ctx context.Context, runID int64, runAttempt int32) ([]domain.CIJob, error) { | |
| 703 | + | var out []domain.CIJob | |
| 704 | + | for key, item := range c.db.Snapshot(ctx).Prefix(ctx, []byte(jobAttemptPrefix(runID, runAttempt))) { | |
| 705 | + | j, err := decodeJob(item) | |
| 706 | + | if err != nil { | |
| 707 | + | return nil, fmt.Errorf("decode %s: %w", key, err) | |
| 708 | + | } | |
| 709 | + | out = append(out, j) | |
| 710 | + | } | |
| 711 | + | slices.SortFunc(out, func(a, b domain.CIJob) int { | |
| 712 | + | if a.Index != b.Index { | |
| 713 | + | return a.Index - b.Index | |
| 714 | + | } | |
| 715 | + | return a.Combo - b.Combo | |
| 716 | + | }) | |
| 717 | + | return out, nil | |
| 718 | + | } | |
| 719 | + | ||
| 720 | + | func (c *CI) jobByRef(snap *kv.Snapshot, ctx context.Context, ref string) (domain.CIJob, error) { | |
| 721 | + | item, ok := snap.Get(ctx, []byte(ref)) | |
| 722 | + | if !ok { | |
| 723 | + | return domain.CIJob{}, domain.ErrNotFound | |
| 724 | + | } | |
| 725 | + | return decodeJob(item) | |
| 726 | + | } | |
| 727 | + | ||
| 728 | + | // FindJobByToken resolves the token index, then reads the job. | |
| 729 | + | func (c *CI) FindJobByToken(ctx context.Context, hash domain.TokenHash, now time.Time) (domain.CIJob, error) { | |
| 730 | + | snap := c.db.Snapshot(ctx) | |
| 731 | + | idx, ok := snap.Get(ctx, jobTokenKey(hash)) | |
| 732 | + | if !ok { | |
| 733 | + | return domain.CIJob{}, domain.ErrNotFound | |
| 734 | + | } | |
| 735 | + | j, err := c.jobByRef(snap, ctx, string(idx.Value)) | |
| 736 | + | if err != nil { | |
| 737 | + | return domain.CIJob{}, err | |
| 738 | + | } | |
| 739 | + | if j.TokenHash != hash || (!j.TokenValidUntil.IsZero() && !now.Before(j.TokenValidUntil)) { | |
| 740 | + | return domain.CIJob{}, domain.ErrNotFound | |
| 741 | + | } | |
| 742 | + | return j, nil | |
| 743 | + | } | |
| 744 | + | ||
| 745 | + | // ListReadyJobs returns the jobs waiting for a runner, oldest first. | |
| 746 | + | func (c *CI) ListReadyJobs(ctx context.Context, limit int) ([]domain.CIJob, error) { | |
| 747 | + | snap := c.db.Snapshot(ctx) | |
| 748 | + | var out []domain.CIJob | |
| 749 | + | for key := range snap.Prefix(ctx, []byte(readyPrefix)) { | |
| 750 | + | ref := string(key)[len(readyPrefix)+len(fmt.Sprintf(idFormat, 0))+1:] | |
| 751 | + | j, err := c.jobByRef(snap, ctx, ref) | |
| 752 | + | if errors.Is(err, domain.ErrNotFound) { | |
| 753 | + | continue | |
| 754 | + | } | |
| 755 | + | if err != nil { | |
| 756 | + | return nil, err | |
| 757 | + | } | |
| 758 | + | out = append(out, j) | |
| 759 | + | if limit > 0 && len(out) >= limit { | |
| 760 | + | break | |
| 761 | + | } | |
| 762 | + | } | |
| 763 | + | return out, nil | |
| 764 | + | } | |
| 765 | + | ||
| 766 | + | // ListActiveJobs returns the jobs given to a runner and not over. | |
| 767 | + | func (c *CI) ListActiveJobs(ctx context.Context) ([]domain.CIJob, error) { | |
| 768 | + | snap := c.db.Snapshot(ctx) | |
| 769 | + | var out []domain.CIJob | |
| 770 | + | for key := range snap.Prefix(ctx, []byte(activePrefix)) { | |
| 771 | + | j, err := c.jobByRef(snap, ctx, strings.TrimPrefix(string(key), activePrefix)) | |
| 772 | + | if errors.Is(err, domain.ErrNotFound) { | |
| 773 | + | continue | |
| 774 | + | } | |
| 775 | + | if err != nil { | |
| 776 | + | return nil, err | |
| 777 | + | } | |
| 778 | + | out = append(out, j) | |
| 779 | + | } | |
| 780 | + | return out, nil | |
| 781 | + | } | |
| 782 | + | ||
| 783 | + | // ListChecks returns the checks of a commit ordered by creation time then | |
| 784 | + | // name. | |
| 785 | + | func (c *CI) ListChecks(ctx context.Context, repo domain.RepoName, sha string, limit int) ([]domain.CICheck, bool, error) { | |
| 786 | + | var out []domain.CICheck | |
| 787 | + | for key, item := range c.db.Snapshot(ctx).Prefix(ctx, []byte(checksPrefix(repo, sha))) { | |
| 788 | + | ck, err := decodeCheck(item) | |
| 789 | + | if err != nil { | |
| 790 | + | return nil, false, fmt.Errorf("decode %s: %w", key, err) | |
| 791 | + | } | |
| 792 | + | out = append(out, ck) | |
| 793 | + | } | |
| 794 | + | slices.SortStableFunc(out, func(a, b domain.CICheck) int { | |
| 795 | + | if d := a.CreatedAt.Compare(b.CreatedAt); d != 0 { | |
| 796 | + | return d | |
| 797 | + | } | |
| 798 | + | return strings.Compare(a.Name, b.Name) | |
| 799 | + | }) | |
| 800 | + | more := limit > 0 && len(out) > limit | |
| 801 | + | if more { | |
| 802 | + | out = out[:limit] | |
| 803 | + | } | |
| 804 | + | return out, more, nil | |
| 805 | + | } | |
| 806 | + | ||
| 807 | + | // --- retention and deletion --- | |
| 808 | + | ||
| 809 | + | // runKeys lists every key of a run except its record: source, jobs, indexes. | |
| 810 | + | func (c *CI) runKeys(ctx context.Context, snap *kv.Snapshot, repo domain.RepoName, runID int64) [][]byte { | |
| 811 | + | keys := [][]byte{runKey(repo, runID), runSrcKey(runID), openKey(runID)} | |
| 812 | + | for key, item := range snap.Prefix(ctx, []byte(jobsPrefix(runID))) { | |
| 813 | + | keys = append(keys, slices.Clone(key)) | |
| 814 | + | if j, err := decodeJob(item); err == nil { | |
| 815 | + | ref := jobRef(j.RunID, j.RunAttempt, j.ID) | |
| 816 | + | keys = append(keys, readyKey(j.ReadyAt, ref), activeKey(ref)) | |
| 817 | + | if j.HasToken() { | |
| 818 | + | keys = append(keys, jobTokenKey(j.TokenHash)) | |
| 819 | + | } | |
| 820 | + | } | |
| 821 | + | } | |
| 822 | + | return keys | |
| 823 | + | } | |
| 824 | + | ||
| 825 | + | // ciKeys lists every key of the snapshot that belongs to the repository's CI | |
| 826 | + | // runs, checks and numbers. | |
| 827 | + | func ciKeys(ctx context.Context, snap *kv.Snapshot, repo domain.RepoName) [][]byte { | |
| 828 | + | c := &CI{} | |
| 829 | + | var keys [][]byte | |
| 830 | + | for key, item := range snap.Prefix(ctx, []byte(runsPrefix(repo))) { | |
| 831 | + | if r, err := decodeRun(item); err == nil { | |
| 832 | + | keys = append(keys, c.runKeys(ctx, snap, repo, r.ID)...) | |
| 833 | + | } | |
| 834 | + | keys = append(keys, slices.Clone(key)) | |
| 835 | + | } | |
| 836 | + | for _, p := range []string{checkPrefix + repo.String() + "/", runNumPrefix + repo.String() + "/"} { | |
| 837 | + | for k := range snap.Prefix(ctx, []byte(p)) { | |
| 838 | + | keys = append(keys, slices.Clone(k)) | |
| 839 | + | } | |
| 840 | + | } | |
| 841 | + | return keys | |
| 842 | + | } | |
| 843 | + | ||
| 844 | + | const sweepChunk = 500 | |
| 845 | + | ||
| 846 | + | // Sweep removes the runs completed before the time. | |
| 847 | + | // | |
| 848 | + | //hexcheck:ignore suspicious-business-logic-in-adapter chunked delete of expired records: a storage concern, the retention is decided by the caller | |
| 849 | + | func (c *CI) Sweep(ctx context.Context, before time.Time) ([]int64, error) { | |
| 850 | + | snap := c.db.Snapshot(ctx) | |
| 851 | + | var ids []int64 | |
| 852 | + | var keys [][]byte | |
| 853 | + | for _, rp := range []string{runPrefix} { | |
| 854 | + | for _, item := range snap.Prefix(ctx, []byte(rp)) { | |
| 855 | + | r, err := decodeRun(item) | |
| 856 | + | if err != nil { | |
| 857 | + | return nil, err | |
| 858 | + | } | |
| 859 | + | if r.Status != domain.CICompleted || r.CompletedAt.IsZero() || !r.CompletedAt.Before(before) { | |
| 860 | + | continue | |
| 861 | + | } | |
| 862 | + | ids = append(ids, r.ID) | |
| 863 | + | keys = append(keys, c.runKeys(ctx, snap, r.Repo, r.ID)...) | |
| 864 | + | for ck := range snap.Prefix(ctx, []byte(checkPrefix+r.Repo.String()+"/"+r.Sha+"/")) { | |
| 865 | + | // A check belongs to the run only while it is the latest one | |
| 866 | + | // for its job: leave it when another run replaced it. | |
| 867 | + | if it, ok := snap.Get(ctx, ck); ok { | |
| 868 | + | if chk, err := decodeCheck(it); err == nil && chk.RunID == r.ID { | |
| 869 | + | keys = append(keys, slices.Clone(ck)) | |
| 870 | + | } | |
| 871 | + | } | |
| 872 | + | } | |
| 873 | + | } | |
| 874 | + | } | |
| 875 | + | for len(keys) > 0 { | |
| 876 | + | n := min(sweepChunk, len(keys)) | |
| 877 | + | var b kv.Batch | |
| 878 | + | for _, k := range keys[:n] { | |
| 879 | + | b.Delete(k) | |
| 880 | + | } | |
| 881 | + | if _, err := c.db.Apply(ctx, &b); err != nil { | |
| 882 | + | return nil, fmt.Errorf("sweep CI runs: %w", err) | |
| 883 | + | } | |
| 884 | + | keys = keys[n:] | |
| 885 | + | } | |
| 886 | + | return ids, nil | |
| 887 | + | } | |
| 888 | + | ||
| 889 | + | // --- runners --- | |
| 890 | + | ||
| 891 | + | func runnerKey(id string) []byte { return []byte(runnerPrefix + id) } | |
| 892 | + | func runnerNameKey(n string) []byte { return []byte(runnerName + strings.ToLower(n)) } | |
| 893 | + | func runnerTokenKey(h domain.TokenHash) []byte { return []byte(runnerTokPref + h.String()) } | |
| 894 | + | ||
| 895 | + | // CreateRunner stores the runner, its name index and its token index. | |
| 896 | + | // | |
| 897 | + | //hexcheck:ignore suspicious-business-logic-in-adapter replaces a runner of the same name in one batch: a storage concern, the rule is decided by the caller | |
| 898 | + | func (c *CI) CreateRunner(ctx context.Context, r domain.CIRunner, replace bool) ([]domain.CIRunner, error) { | |
| 899 | + | for range createRunTries { | |
| 900 | + | var b kv.Batch | |
| 901 | + | var removed []domain.CIRunner | |
| 902 | + | if idx, ok := c.db.Get(ctx, runnerNameKey(r.Name)); ok { | |
| 903 | + | if !replace { | |
| 904 | + | return nil, domain.ErrAlreadyExists | |
| 905 | + | } | |
| 906 | + | old, err := c.GetRunner(ctx, string(idx.Value)) | |
| 907 | + | if err != nil && !errors.Is(err, domain.ErrNotFound) { | |
| 908 | + | return nil, err | |
| 909 | + | } | |
| 910 | + | b.RequireVersion(runnerNameKey(r.Name), idx.Version) | |
| 911 | + | if err == nil { | |
| 912 | + | removed = append(removed, old) | |
| 913 | + | b.Delete(runnerKey(old.ID)) | |
| 914 | + | b.Delete(runnerTokenKey(old.TokenHash)) | |
| 915 | + | } | |
| 916 | + | } else { | |
| 917 | + | b.RequireAbsent(runnerNameKey(r.Name)) | |
| 918 | + | } | |
| 919 | + | b.RequireAbsent(runnerKey(r.ID)) | |
| 920 | + | b.RequireAbsent(runnerTokenKey(r.TokenHash)) | |
| 921 | + | b.Put(runnerKey(r.ID), encodeRunner(r)) | |
| 922 | + | b.Put(runnerNameKey(r.Name), []byte(r.ID)) | |
| 923 | + | b.Put(runnerTokenKey(r.TokenHash), []byte(r.ID)) | |
| 924 | + | _, err := c.db.Apply(ctx, &b) | |
| 925 | + | if errors.Is(err, kv.ErrConflict) { | |
| 926 | + | if !replace { | |
| 927 | + | if _, ok := c.db.Get(ctx, runnerNameKey(r.Name)); ok { | |
| 928 | + | return nil, domain.ErrAlreadyExists | |
| 929 | + | } | |
| 930 | + | } | |
| 931 | + | continue | |
| 932 | + | } | |
| 933 | + | if err != nil { | |
| 934 | + | return nil, fmt.Errorf("create runner %q: %w", r.Name, err) | |
| 935 | + | } | |
| 936 | + | return removed, nil | |
| 937 | + | } | |
| 938 | + | return nil, fmt.Errorf("create runner %q: %w", r.Name, domain.ErrConflict) | |
| 939 | + | } | |
| 940 | + | ||
| 941 | + | // GetRunner reads runner/<id>. | |
| 942 | + | func (c *CI) GetRunner(ctx context.Context, id string) (domain.CIRunner, error) { | |
| 943 | + | item, ok := c.db.Get(ctx, runnerKey(id)) | |
| 944 | + | if !ok || id == "" || strings.Contains(id, "/") { | |
| 945 | + | return domain.CIRunner{}, domain.ErrNotFound | |
| 946 | + | } | |
| 947 | + | return decodeRunner(item.Value) | |
| 948 | + | } | |
| 949 | + | ||
| 950 | + | // FindRunnerByToken resolves the token index, then reads the runner. | |
| 951 | + | func (c *CI) FindRunnerByToken(ctx context.Context, hash domain.TokenHash) (domain.CIRunner, error) { | |
| 952 | + | snap := c.db.Snapshot(ctx) | |
| 953 | + | idx, ok := snap.Get(ctx, runnerTokenKey(hash)) | |
| 954 | + | if !ok { | |
| 955 | + | return domain.CIRunner{}, domain.ErrNotFound | |
| 956 | + | } | |
| 957 | + | item, ok := snap.Get(ctx, runnerKey(string(idx.Value))) | |
| 958 | + | if !ok { | |
| 959 | + | return domain.CIRunner{}, domain.ErrNotFound | |
| 960 | + | } | |
| 961 | + | r, err := decodeRunner(item.Value) | |
| 962 | + | if err != nil { | |
| 963 | + | return domain.CIRunner{}, err | |
| 964 | + | } | |
| 965 | + | if r.TokenHash != hash { | |
| 966 | + | return domain.CIRunner{}, fmt.Errorf("runner %q: token index does not match the stored hash", r.ID) | |
| 967 | + | } | |
| 968 | + | return r, nil | |
| 969 | + | } | |
| 970 | + | ||
| 971 | + | // ListRunners returns every runner ordered by name. | |
| 972 | + | func (c *CI) ListRunners(ctx context.Context) ([]domain.CIRunner, error) { | |
| 973 | + | var out []domain.CIRunner | |
| 974 | + | for key, item := range c.db.Snapshot(ctx).Prefix(ctx, []byte(runnerPrefix)) { | |
| 975 | + | r, err := decodeRunner(item.Value) | |
| 976 | + | if err != nil { | |
| 977 | + | return nil, fmt.Errorf("decode %s: %w", key, err) | |
| 978 | + | } | |
| 979 | + | out = append(out, r) | |
| 980 | + | } | |
| 981 | + | slices.SortFunc(out, func(a, b domain.CIRunner) int { | |
| 982 | + | return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) | |
| 983 | + | }) | |
| 984 | + | return out, nil | |
| 985 | + | } | |
| 986 | + | ||
| 987 | + | // RemoveRunner removes the runner, its name and its token. | |
| 988 | + | func (c *CI) RemoveRunner(ctx context.Context, id string) (domain.CIRunner, error) { | |
| 989 | + | for range createRunTries { | |
| 990 | + | item, ok := c.db.Get(ctx, runnerKey(id)) | |
| 991 | + | if !ok || id == "" || strings.Contains(id, "/") { | |
| 992 | + | return domain.CIRunner{}, domain.ErrNotFound | |
| 993 | + | } | |
| 994 | + | r, err := decodeRunner(item.Value) | |
| 995 | + | if err != nil { | |
| 996 | + | return domain.CIRunner{}, err | |
| 997 | + | } | |
| 998 | + | var b kv.Batch | |
| 999 | + | b.RequireVersion(runnerKey(id), item.Version) | |
| 1000 | + | b.Delete(runnerKey(id)) | |
| 1001 | + | b.Delete(runnerNameKey(r.Name)) | |
| 1002 | + | b.Delete(runnerTokenKey(r.TokenHash)) | |
| 1003 | + | _, err = c.db.Apply(ctx, &b) | |
| 1004 | + | if errors.Is(err, kv.ErrConflict) { | |
| 1005 | + | continue | |
| 1006 | + | } | |
| 1007 | + | if err != nil { | |
| 1008 | + | return domain.CIRunner{}, fmt.Errorf("remove runner %q: %w", id, err) | |
| 1009 | + | } | |
| 1010 | + | return r, nil | |
| 1011 | + | } | |
| 1012 | + | return domain.CIRunner{}, fmt.Errorf("remove runner %q: %w", id, domain.ErrConflict) | |
| 1013 | + | } | |
A internal/adapters/kvstore/ci_test.go +358 -0
| @@ -0,0 +1,358 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package kvstore_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "sync" | |
| 8 | + | "testing" | |
| 9 | + | "time" | |
| 10 | + | ||
| 11 | + | "github.com/stretchr/testify/assert" | |
| 12 | + | "github.com/stretchr/testify/require" | |
| 13 | + | ||
| 14 | + | "bnema.dev/ermite/internal/adapters/kvstore" | |
| 15 | + | "bnema.dev/ermite/internal/domain" | |
| 16 | + | "bnema.dev/ermite/internal/ports" | |
| 17 | + | ) | |
| 18 | + | ||
| 19 | + | // ciRepo opens a store holding repository "proj". | |
| 20 | + | func ciRepo(t *testing.T) (*kvstore.Store, *kvstore.CI, domain.RepoName) { | |
| 21 | + | t.Helper() | |
| 22 | + | s := openStore(t, t.TempDir()) | |
| 23 | + | n := name(t, "proj") | |
| 24 | + | require.NoError(t, s.Create(context.Background(), domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: created})) | |
| 25 | + | return s, s.CI(), n | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | func draftOf(repo domain.RepoName, workflow string, jobs int) func(id, number int64) ports.CIRunDraft { | |
| 29 | + | return func(id, number int64) ports.CIRunDraft { | |
| 30 | + | d := ports.CIRunDraft{ | |
| 31 | + | Run: domain.CIRun{ | |
| 32 | + | Repo: repo, ID: id, Number: number, Attempt: 1, WorkflowPath: workflow, WorkflowName: "ci", Event: "push", | |
| 33 | + | Ref: "refs/heads/main", Sha: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Status: domain.CIQueued, CreatedAt: created, | |
| 34 | + | }, | |
| 35 | + | Source: domain.CIRunSource{WorkflowYAML: "on: push", GithubJSON: "{}"}, | |
| 36 | + | } | |
| 37 | + | for i := range jobs { | |
| 38 | + | j := domain.CIJob{ | |
| 39 | + | ID: domain.NewJobID(id, i, 0), RunID: id, Attempt: 1, RunAttempt: 1, Repo: repo, Key: "build", Name: "build", | |
| 40 | + | Phase: domain.CIPhaseReady, ReadyAt: created.Add(time.Duration(i) * time.Second), CreatedAt: created, | |
| 41 | + | Labels: []string{"self-hosted"}, Index: i, TimeoutMinutes: 360, Planned: true, | |
| 42 | + | } | |
| 43 | + | d.Jobs = append(d.Jobs, j) | |
| 44 | + | d.Checks = append(d.Checks, domain.CICheck{ | |
| 45 | + | ID: j.ID, Repo: repo, Sha: d.Run.Sha, Name: j.Name, Status: domain.CIQueued, RunID: id, JobID: j.ID, Attempt: 1, | |
| 46 | + | WorkflowName: "ci", CreatedAt: created, | |
| 47 | + | }) | |
| 48 | + | } | |
| 49 | + | return d | |
| 50 | + | } | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | func TestCICreateRunTakesIncreasingIdsAndPerWorkflowNumbers(t *testing.T) { | |
| 54 | + | ctx := context.Background() | |
| 55 | + | _, ci, n := ciRepo(t) | |
| 56 | + | ||
| 57 | + | a, err := ci.CreateRun(ctx, n, ".github/workflows/ci.yml", draftOf(n, ".github/workflows/ci.yml", 1)) | |
| 58 | + | require.NoError(t, err) | |
| 59 | + | b, err := ci.CreateRun(ctx, n, ".github/workflows/ci.yml", draftOf(n, ".github/workflows/ci.yml", 1)) | |
| 60 | + | require.NoError(t, err) | |
| 61 | + | c, err := ci.CreateRun(ctx, n, ".github/workflows/lint.yml", draftOf(n, ".github/workflows/lint.yml", 0)) | |
| 62 | + | require.NoError(t, err) | |
| 63 | + | ||
| 64 | + | assert.Equal(t, []int64{1, 2, 3}, []int64{a.Run.ID, b.Run.ID, c.Run.ID}) | |
| 65 | + | assert.Equal(t, []int64{1, 2, 1}, []int64{a.Run.Number, b.Run.Number, c.Run.Number}) | |
| 66 | + | assert.NotZero(t, a.Run.Version) | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | func TestCIConcurrentCreateRunNeverSharesAnId(t *testing.T) { | |
| 70 | + | ctx := context.Background() | |
| 71 | + | _, ci, n := ciRepo(t) | |
| 72 | + | var mu sync.Mutex | |
| 73 | + | ids := map[int64]bool{} | |
| 74 | + | var wg sync.WaitGroup | |
| 75 | + | for range 12 { | |
| 76 | + | wg.Add(1) | |
| 77 | + | go func() { | |
| 78 | + | defer wg.Done() | |
| 79 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 80 | + | if assert.NoError(t, err) { | |
| 81 | + | mu.Lock() | |
| 82 | + | defer mu.Unlock() | |
| 83 | + | assert.False(t, ids[d.Run.ID], "id %d handed out twice", d.Run.ID) | |
| 84 | + | ids[d.Run.ID] = true | |
| 85 | + | } | |
| 86 | + | }() | |
| 87 | + | } | |
| 88 | + | wg.Wait() | |
| 89 | + | assert.Len(t, ids, 12) | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | func TestCICreateRunFailsOnAMissingRepository(t *testing.T) { | |
| 93 | + | _, ci, _ := ciRepo(t) | |
| 94 | + | ghost := name(t, "ghost") | |
| 95 | + | _, err := ci.CreateRun(context.Background(), ghost, "w.yml", draftOf(ghost, "w.yml", 1)) | |
| 96 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func TestCIJobIndexesFollowThePhase(t *testing.T) { | |
| 100 | + | ctx := context.Background() | |
| 101 | + | _, ci, n := ciRepo(t) | |
| 102 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 2)) | |
| 103 | + | require.NoError(t, err) | |
| 104 | + | ||
| 105 | + | ready, err := ci.ListReadyJobs(ctx, 10) | |
| 106 | + | require.NoError(t, err) | |
| 107 | + | require.Len(t, ready, 2) | |
| 108 | + | assert.Equal(t, d.Jobs[0].ID, ready[0].ID, "oldest first") | |
| 109 | + | ||
| 110 | + | open, err := ci.ListOpenRuns(ctx) | |
| 111 | + | require.NoError(t, err) | |
| 112 | + | assert.Len(t, open, 1) | |
| 113 | + | ||
| 114 | + | // Assign the first job: it leaves the queue, enters the active set and its | |
| 115 | + | // token becomes findable. | |
| 116 | + | j := ready[0] | |
| 117 | + | hash := domain.HashTokenSecret("ermitejob_x") | |
| 118 | + | j.Phase, j.TokenHash, j.AssignedAt = domain.CIPhaseAssigned, hash, created.Add(time.Minute) | |
| 119 | + | j.TokenValidUntil = created.Add(48 * time.Hour) | |
| 120 | + | _, err = ci.Apply(ctx, ports.CIWrite{Jobs: []domain.CIJob{j}}) | |
| 121 | + | require.NoError(t, err) | |
| 122 | + | ||
| 123 | + | ready, _ = ci.ListReadyJobs(ctx, 10) | |
| 124 | + | assert.Len(t, ready, 1) | |
| 125 | + | active, err := ci.ListActiveJobs(ctx) | |
| 126 | + | require.NoError(t, err) | |
| 127 | + | require.Len(t, active, 1) | |
| 128 | + | assert.Equal(t, j.ID, active[0].ID) | |
| 129 | + | found, err := ci.FindJobByToken(ctx, hash, created.Add(2*time.Minute)) | |
| 130 | + | require.NoError(t, err) | |
| 131 | + | assert.Equal(t, j.ID, found.ID) | |
| 132 | + | _, err = ci.FindJobByToken(ctx, hash, created.Add(72*time.Hour)) | |
| 133 | + | assert.ErrorIs(t, err, domain.ErrNotFound, "an expired token is not found") | |
| 134 | + | ||
| 135 | + | // A new token replaces the old one. | |
| 136 | + | j2, err := ci.GetJob(ctx, j.RunID, j.RunAttempt, j.ID) | |
| 137 | + | require.NoError(t, err) | |
| 138 | + | j2.TokenHash = domain.HashTokenSecret("ermitejob_y") | |
| 139 | + | _, err = ci.Apply(ctx, ports.CIWrite{Jobs: []domain.CIJob{j2}}) | |
| 140 | + | require.NoError(t, err) | |
| 141 | + | _, err = ci.FindJobByToken(ctx, hash, created.Add(2*time.Minute)) | |
| 142 | + | assert.ErrorIs(t, err, domain.ErrNotFound, "the replaced token is gone") | |
| 143 | + | ||
| 144 | + | // End it. | |
| 145 | + | j3, _ := ci.GetJob(ctx, j.RunID, j.RunAttempt, j.ID) | |
| 146 | + | j3.Phase, j3.Conclusion = domain.CIPhaseEnded, domain.CISuccess | |
| 147 | + | _, err = ci.Apply(ctx, ports.CIWrite{Jobs: []domain.CIJob{j3}}) | |
| 148 | + | require.NoError(t, err) | |
| 149 | + | active, _ = ci.ListActiveJobs(ctx) | |
| 150 | + | assert.Empty(t, active) | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | func TestCIApplyChecksVersions(t *testing.T) { | |
| 154 | + | ctx := context.Background() | |
| 155 | + | _, ci, n := ciRepo(t) | |
| 156 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 157 | + | require.NoError(t, err) | |
| 158 | + | ||
| 159 | + | a, err := ci.GetRun(ctx, n, d.Run.ID) | |
| 160 | + | require.NoError(t, err) | |
| 161 | + | b := a | |
| 162 | + | a.Status = domain.CIInProgress | |
| 163 | + | _, err = ci.Apply(ctx, ports.CIWrite{Runs: []domain.CIRun{a}}) | |
| 164 | + | require.NoError(t, err) | |
| 165 | + | b.Status = domain.CICompleted | |
| 166 | + | _, err = ci.Apply(ctx, ports.CIWrite{Runs: []domain.CIRun{b}}) | |
| 167 | + | assert.ErrorIs(t, err, domain.ErrConflict, "a write on a stale copy loses") | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | func TestCIChecksAreOrderedAndLimited(t *testing.T) { | |
| 171 | + | ctx := context.Background() | |
| 172 | + | _, ci, n := ciRepo(t) | |
| 173 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 3)) | |
| 174 | + | require.NoError(t, err) | |
| 175 | + | ||
| 176 | + | checks, more, err := ci.ListChecks(ctx, n, d.Run.Sha, 10) | |
| 177 | + | require.NoError(t, err) | |
| 178 | + | assert.Len(t, checks, 3) | |
| 179 | + | assert.False(t, more) | |
| 180 | + | checks, more, err = ci.ListChecks(ctx, n, d.Run.Sha, 2) | |
| 181 | + | require.NoError(t, err) | |
| 182 | + | assert.Len(t, checks, 2) | |
| 183 | + | assert.True(t, more) | |
| 184 | + | none, _, err := ci.ListChecks(ctx, n, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 10) | |
| 185 | + | require.NoError(t, err) | |
| 186 | + | assert.Empty(t, none) | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | func TestCIListRunsNewestFirstAndBounded(t *testing.T) { | |
| 190 | + | ctx := context.Background() | |
| 191 | + | s, ci, n := ciRepo(t) | |
| 192 | + | other := name(t, "other") | |
| 193 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: other, Visibility: domain.VisibilityPublic, CreatedAt: created})) | |
| 194 | + | _, err := ci.CreateRun(ctx, other, "w.yml", draftOf(other, "w.yml", 0)) | |
| 195 | + | require.NoError(t, err) | |
| 196 | + | for range 5 { | |
| 197 | + | _, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 0)) | |
| 198 | + | require.NoError(t, err) | |
| 199 | + | } | |
| 200 | + | runs, err := ci.ListRuns(ctx, n, 0, 3) | |
| 201 | + | require.NoError(t, err) | |
| 202 | + | require.Len(t, runs, 3) | |
| 203 | + | assert.Equal(t, []int64{6, 5, 4}, []int64{runs[0].ID, runs[1].ID, runs[2].ID}) | |
| 204 | + | runs, err = ci.ListRuns(ctx, n, 4, 10) | |
| 205 | + | require.NoError(t, err) | |
| 206 | + | assert.Equal(t, []int64{3, 2}, []int64{runs[0].ID, runs[1].ID}) | |
| 207 | + | ids, err := ci.RunIDs(ctx, n) | |
| 208 | + | require.NoError(t, err) | |
| 209 | + | assert.Len(t, ids, 5) | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | func TestCIRunnersCreateReplaceFindRemove(t *testing.T) { | |
| 213 | + | ctx := context.Background() | |
| 214 | + | _, ci, _ := ciRepo(t) | |
| 215 | + | h1, h2 := domain.HashTokenSecret("ermiterun_1"), domain.HashTokenSecret("ermiterun_2") | |
| 216 | + | r1 := domain.CIRunner{ID: "r1", Name: "Alpha", Labels: []string{"linux"}, Isolation: domain.CIIsolationVM, TokenHash: h1, RegisteredAt: created} | |
| 217 | + | _, err := ci.CreateRunner(ctx, r1, false) | |
| 218 | + | require.NoError(t, err) | |
| 219 | + | ||
| 220 | + | r2 := domain.CIRunner{ID: "r2", Name: "alpha", TokenHash: h2, RegisteredAt: created} | |
| 221 | + | _, err = ci.CreateRunner(ctx, r2, false) | |
| 222 | + | assert.ErrorIs(t, err, domain.ErrAlreadyExists, "names are case-insensitive") | |
| 223 | + | ||
| 224 | + | removed, err := ci.CreateRunner(ctx, r2, true) | |
| 225 | + | require.NoError(t, err) | |
| 226 | + | require.Len(t, removed, 1) | |
| 227 | + | assert.Equal(t, "r1", removed[0].ID) | |
| 228 | + | _, err = ci.FindRunnerByToken(ctx, h1) | |
| 229 | + | assert.ErrorIs(t, err, domain.ErrNotFound, "the replaced runner's token stops working") | |
| 230 | + | got, err := ci.FindRunnerByToken(ctx, h2) | |
| 231 | + | require.NoError(t, err) | |
| 232 | + | assert.Equal(t, "r2", got.ID) | |
| 233 | + | ||
| 234 | + | list, err := ci.ListRunners(ctx) | |
| 235 | + | require.NoError(t, err) | |
| 236 | + | assert.Len(t, list, 1) | |
| 237 | + | ||
| 238 | + | _, err = ci.RemoveRunner(ctx, "r2") | |
| 239 | + | require.NoError(t, err) | |
| 240 | + | _, err = ci.RemoveRunner(ctx, "r2") | |
| 241 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 242 | + | _, err = ci.FindRunnerByToken(ctx, h2) | |
| 243 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 244 | + | // The name is free again. | |
| 245 | + | _, err = ci.CreateRunner(ctx, domain.CIRunner{ID: "r3", Name: "alpha", TokenHash: domain.HashTokenSecret("ermiterun_3")}, false) | |
| 246 | + | assert.NoError(t, err) | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | func TestCISweepRemovesOldCompletedRunsOnly(t *testing.T) { | |
| 250 | + | ctx := context.Background() | |
| 251 | + | _, ci, n := ciRepo(t) | |
| 252 | + | oldRun, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 253 | + | require.NoError(t, err) | |
| 254 | + | newRun, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 255 | + | require.NoError(t, err) | |
| 256 | + | openRun, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 257 | + | require.NoError(t, err) | |
| 258 | + | ||
| 259 | + | for _, tc := range []struct { | |
| 260 | + | d ports.CIRunDraft | |
| 261 | + | at time.Time | |
| 262 | + | }{{oldRun, created}, {newRun, created.Add(100 * 24 * time.Hour)}} { | |
| 263 | + | run, _ := ci.GetRun(ctx, n, tc.d.Run.ID) | |
| 264 | + | run.Status, run.Conclusion, run.CompletedAt = domain.CICompleted, domain.CISuccess, tc.at | |
| 265 | + | job, _ := ci.GetJob(ctx, run.ID, 1, tc.d.Jobs[0].ID) | |
| 266 | + | job.Phase, job.Conclusion = domain.CIPhaseEnded, domain.CISuccess | |
| 267 | + | _, err := ci.Apply(ctx, ports.CIWrite{Runs: []domain.CIRun{run}, Jobs: []domain.CIJob{job}}) | |
| 268 | + | require.NoError(t, err) | |
| 269 | + | } | |
| 270 | + | ||
| 271 | + | ids, err := ci.Sweep(ctx, created.Add(90*24*time.Hour)) | |
| 272 | + | require.NoError(t, err) | |
| 273 | + | assert.Equal(t, []int64{oldRun.Run.ID}, ids) | |
| 274 | + | ||
| 275 | + | _, err = ci.GetRun(ctx, n, oldRun.Run.ID) | |
| 276 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 277 | + | _, err = ci.GetRunSource(ctx, oldRun.Run.ID) | |
| 278 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 279 | + | jobs, _ := ci.ListJobs(ctx, oldRun.Run.ID, 1) | |
| 280 | + | assert.Empty(t, jobs) | |
| 281 | + | _, err = ci.GetRun(ctx, n, newRun.Run.ID) | |
| 282 | + | assert.NoError(t, err) | |
| 283 | + | _, err = ci.GetRun(ctx, n, openRun.Run.ID) | |
| 284 | + | assert.NoError(t, err, "an open run is never swept") | |
| 285 | + | ||
| 286 | + | // Numbers are never reused. | |
| 287 | + | next, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 0)) | |
| 288 | + | require.NoError(t, err) | |
| 289 | + | assert.Equal(t, int64(4), next.Run.Number) | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | func TestCIRepositoryDeletionRemovesEverythingAndRestartsNumbers(t *testing.T) { | |
| 293 | + | ctx := context.Background() | |
| 294 | + | s, ci, n := ciRepo(t) | |
| 295 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 2)) | |
| 296 | + | require.NoError(t, err) | |
| 297 | + | _, err = ci.CreateRunner(ctx, domain.CIRunner{ID: "r1", Name: "a", TokenHash: domain.HashTokenSecret("x")}, false) | |
| 298 | + | require.NoError(t, err) | |
| 299 | + | ||
| 300 | + | require.NoError(t, s.Delete(ctx, n)) | |
| 301 | + | ||
| 302 | + | _, err = ci.GetRun(ctx, n, d.Run.ID) | |
| 303 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 304 | + | _, err = ci.GetRunSource(ctx, d.Run.ID) | |
| 305 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 306 | + | jobs, _ := ci.ListJobs(ctx, d.Run.ID, 1) | |
| 307 | + | assert.Empty(t, jobs) | |
| 308 | + | checks, _, _ := ci.ListChecks(ctx, n, d.Run.Sha, 10) | |
| 309 | + | assert.Empty(t, checks) | |
| 310 | + | ready, _ := ci.ListReadyJobs(ctx, 10) | |
| 311 | + | assert.Empty(t, ready) | |
| 312 | + | open, _ := ci.ListOpenRuns(ctx) | |
| 313 | + | assert.Empty(t, open) | |
| 314 | + | runners, _ := ci.ListRunners(ctx) | |
| 315 | + | assert.Len(t, runners, 1, "runners do not belong to a repository") | |
| 316 | + | ||
| 317 | + | // A repository created again under the name starts its run numbers at 1, | |
| 318 | + | // while ids are never reused. | |
| 319 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: created})) | |
| 320 | + | again, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 0)) | |
| 321 | + | require.NoError(t, err) | |
| 322 | + | assert.Equal(t, int64(1), again.Run.Number) | |
| 323 | + | assert.Equal(t, int64(2), again.Run.ID) | |
| 324 | + | } | |
| 325 | + | ||
| 326 | + | func TestCIStateSurvivesARestart(t *testing.T) { | |
| 327 | + | ctx := context.Background() | |
| 328 | + | dir := t.TempDir() | |
| 329 | + | s, err := kvstore.Open(ctx, dir) | |
| 330 | + | require.NoError(t, err) | |
| 331 | + | n := name(t, "proj") | |
| 332 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: n, Visibility: domain.VisibilityPrivate, CreatedAt: created})) | |
| 333 | + | ci := s.CI() | |
| 334 | + | d, err := ci.CreateRun(ctx, n, "w.yml", draftOf(n, "w.yml", 1)) | |
| 335 | + | require.NoError(t, err) | |
| 336 | + | j := d.Jobs[0] | |
| 337 | + | hash := domain.HashTokenSecret("ermitejob_z") | |
| 338 | + | j.Phase, j.TokenHash, j.RunnerID, j.RunnerName = domain.CIPhaseRunning, hash, "r1", "alpha" | |
| 339 | + | j.Steps = []domain.CIStep{{Number: 1, Name: "Set up job", State: domain.CIStepCompleted, Conclusion: domain.CISuccess, HasLines: true, LogStart: 0}} | |
| 340 | + | j.Outputs = map[string]string{"a": "b"} | |
| 341 | + | _, err = ci.Apply(ctx, ports.CIWrite{Jobs: []domain.CIJob{j}}) | |
| 342 | + | require.NoError(t, err) | |
| 343 | + | require.NoError(t, s.Close(ctx)) | |
| 344 | + | ||
| 345 | + | s2, err := kvstore.Open(ctx, dir) | |
| 346 | + | require.NoError(t, err) | |
| 347 | + | defer func() { _ = s2.Close(ctx) }() | |
| 348 | + | ci2 := s2.CI() | |
| 349 | + | got, err := ci2.FindJobByToken(ctx, hash, time.Time{}) | |
| 350 | + | require.NoError(t, err) | |
| 351 | + | assert.Equal(t, domain.CIPhaseRunning, got.Phase) | |
| 352 | + | assert.Equal(t, "alpha", got.RunnerName) | |
| 353 | + | assert.Equal(t, map[string]string{"a": "b"}, got.Outputs) | |
| 354 | + | require.Len(t, got.Steps, 1) | |
| 355 | + | assert.True(t, got.Steps[0].HasLines) | |
| 356 | + | active, _ := ci2.ListActiveJobs(ctx) | |
| 357 | + | assert.Len(t, active, 1) | |
| 358 | + | } | |
M internal/adapters/kvstore/repos.go +3 -2
| @@ -99,7 +99,8 @@ const deleteChunk = 1000 | |||
| 99 | 99 | const deleteTries = 32 | |
| 100 | 100 | ||
| 101 | 101 | // Delete removes the repository and everything keyed to it: its merge | |
| 102 | - | // requests, their comments and read positions, and its number sequence. The | |
| 102 | + | // requests, their comments and read positions, and its number sequence, and | |
| 103 | + | // its CI runs, jobs, checks and run numbers. The | |
| 103 | 104 | // Git data is moved to the trash by the caller; the merge requests are not | |
| 104 | 105 | // kept anywhere, like the repository metadata, so a repository created again | |
| 105 | 106 | // under the same name starts clean, with its numbers starting at 1. A missing | |
| @@ -119,7 +120,7 @@ func (s *Store) Delete(ctx context.Context, name domain.RepoName) error { | |||
| 119 | 120 | for range deleteTries { | |
| 120 | 121 | snap := s.db.Snapshot(ctx) | |
| 121 | 122 | repoItem, repoOK := snap.Get(ctx, repoKey(name)) | |
| 122 | - | keys := mergeRequestKeys(ctx, snap, name) | |
| 123 | + | keys := append(mergeRequestKeys(ctx, snap, name), ciKeys(ctx, snap, name)...) | |
| 123 | 124 | for len(keys) > deleteChunk { | |
| 124 | 125 | var b kv.Batch | |
| 125 | 126 | for _, k := range keys[:deleteChunk] { | |
A internal/domain/ci.go +548 -0
| @@ -0,0 +1,548 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "errors" | |
| 7 | + | "fmt" | |
| 8 | + | "regexp" | |
| 9 | + | "slices" | |
| 10 | + | "strconv" | |
| 11 | + | "strings" | |
| 12 | + | "time" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // CIStatus is where a run, a job or a check is in its life (GitHub's status). | |
| 16 | + | type CIStatus string | |
| 17 | + | ||
| 18 | + | // Statuses. | |
| 19 | + | const ( | |
| 20 | + | CIQueued CIStatus = "queued" | |
| 21 | + | CIInProgress CIStatus = "in_progress" | |
| 22 | + | CICompleted CIStatus = "completed" | |
| 23 | + | ) | |
| 24 | + | ||
| 25 | + | // CIConclusion is how a run, a job, a step or a check ended, with GitHub's | |
| 26 | + | // values. It is empty until the status is completed. | |
| 27 | + | type CIConclusion string | |
| 28 | + | ||
| 29 | + | // Conclusions. | |
| 30 | + | const ( | |
| 31 | + | CINoConclusion CIConclusion = "" | |
| 32 | + | CISuccess CIConclusion = "success" | |
| 33 | + | CIFailure CIConclusion = "failure" | |
| 34 | + | CICancelled CIConclusion = "cancelled" | |
| 35 | + | CISkipped CIConclusion = "skipped" | |
| 36 | + | CITimedOut CIConclusion = "timed_out" | |
| 37 | + | CINeutral CIConclusion = "neutral" | |
| 38 | + | CIActionRequired CIConclusion = "action_required" | |
| 39 | + | ) | |
| 40 | + | ||
| 41 | + | // PassesMerge reports whether a check that ended this way lets a merge go | |
| 42 | + | // through under FailingChecksBlock: success, skipped and neutral do. | |
| 43 | + | func (c CIConclusion) PassesMerge() bool { | |
| 44 | + | return c == CISuccess || c == CISkipped || c == CINeutral | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | // IsFailure reports whether the conclusion is a failure or a timeout. | |
| 48 | + | func (c CIConclusion) IsFailure() bool { return c == CIFailure || c == CITimedOut } | |
| 49 | + | ||
| 50 | + | // CIStepState is where a step is in its life. | |
| 51 | + | type CIStepState string | |
| 52 | + | ||
| 53 | + | // Step states. | |
| 54 | + | const ( | |
| 55 | + | CIStepPending CIStepState = "pending" | |
| 56 | + | CIStepInProgress CIStepState = "in_progress" | |
| 57 | + | CIStepCompleted CIStepState = "completed" | |
| 58 | + | ) | |
| 59 | + | ||
| 60 | + | // CIJobPhase is where a job is for the server, finer than the status owners | |
| 61 | + | // see. | |
| 62 | + | type CIJobPhase string | |
| 63 | + | ||
| 64 | + | // Job phases. | |
| 65 | + | const ( | |
| 66 | + | // CIPhaseWaiting: waits for the jobs it needs or for a max-parallel slot. | |
| 67 | + | CIPhaseWaiting CIJobPhase = "waiting" | |
| 68 | + | // CIPhaseReady: planned, waits for a runner. | |
| 69 | + | CIPhaseReady CIJobPhase = "ready" | |
| 70 | + | // CIPhaseAssigned: given to a runner that has not acknowledged it. | |
| 71 | + | CIPhaseAssigned CIJobPhase = "assigned" | |
| 72 | + | // CIPhaseRunning: acknowledged by its runner. | |
| 73 | + | CIPhaseRunning CIJobPhase = "running" | |
| 74 | + | // CIPhaseEnded: over. | |
| 75 | + | CIPhaseEnded CIJobPhase = "ended" | |
| 76 | + | ) | |
| 77 | + | ||
| 78 | + | // CIIsolation is how a runner isolates jobs. | |
| 79 | + | type CIIsolation string | |
| 80 | + | ||
| 81 | + | // Isolations. | |
| 82 | + | const ( | |
| 83 | + | CIIsolationNone CIIsolation = "" | |
| 84 | + | CIIsolationVM CIIsolation = "vm" | |
| 85 | + | CIIsolationContainerSysbox CIIsolation = "container-sysbox" | |
| 86 | + | CIIsolationContainerPrivileged CIIsolation = "container-privileged" | |
| 87 | + | ) | |
| 88 | + | ||
| 89 | + | // CICancelReason says why a job is asked to stop. | |
| 90 | + | type CICancelReason string | |
| 91 | + | ||
| 92 | + | // Cancel reasons. | |
| 93 | + | const ( | |
| 94 | + | CICancelNone CICancelReason = "" | |
| 95 | + | CICancelUser CICancelReason = "user" | |
| 96 | + | CICancelFailFast CICancelReason = "fail-fast" | |
| 97 | + | CICancelTimeout CICancelReason = "timeout" | |
| 98 | + | ) | |
| 99 | + | ||
| 100 | + | // Limits and durations of the CI protocol (runner.proto). | |
| 101 | + | const ( | |
| 102 | + | // CIMaxLogBytes is the size of a job log, as stored (512 MiB). | |
| 103 | + | CIMaxLogBytes = 512 << 20 | |
| 104 | + | // CIMaxOutputBytes bounds one output and all outputs together (1 MiB). | |
| 105 | + | CIMaxOutputBytes = 1 << 20 | |
| 106 | + | // CIMaxLogLines and CIMaxLogChunkBytes bound one AppendLog request. | |
| 107 | + | CIMaxLogLines = 1000 | |
| 108 | + | CIMaxLogChunkBytes = 64 << 10 | |
| 109 | + | // CIMaxSteps bounds the steps of a job. | |
| 110 | + | CIMaxSteps = 1000 | |
| 111 | + | // CIMaxStepName bounds a step name, in characters. | |
| 112 | + | CIMaxStepName = 1024 | |
| 113 | + | // CIMaxLabels and CIMaxLabelLen bound the labels of a runner. | |
| 114 | + | CIMaxLabels = 64 | |
| 115 | + | CIMaxLabelLen = 64 | |
| 116 | + | // CIDefaultTimeoutMinutes is the timeout of a job without timeout-minutes. | |
| 117 | + | CIDefaultTimeoutMinutes = 360 | |
| 118 | + | // CIMaxAssignCancels is how many times in a row an assignment may be | |
| 119 | + | // cancelled before the job fails. | |
| 120 | + | CIMaxAssignCancels = 3 | |
| 121 | + | // CIRetentionDays is the retention of runs, jobs, checks and logs. | |
| 122 | + | CIRetentionDays = 90 | |
| 123 | + | ) | |
| 124 | + | ||
| 125 | + | // Durations of the CI protocol. | |
| 126 | + | const ( | |
| 127 | + | // CIAckTimeout is how long a runner has to send its first heartbeat. | |
| 128 | + | CIAckTimeout = 30 * time.Second | |
| 129 | + | // CIHeartbeatDeadline is how long a running job may go without heartbeat. | |
| 130 | + | CIHeartbeatDeadline = 2 * time.Minute | |
| 131 | + | // CICancelGrace is how long a runner has to conclude after a cancel request. | |
| 132 | + | CICancelGrace = 60 * time.Second | |
| 133 | + | // CITimeoutSlack is added to timeout-minutes by the server. | |
| 134 | + | CITimeoutSlack = 60 * time.Second | |
| 135 | + | // CIQueueTimeout is how long a job waits for a runner. | |
| 136 | + | CIQueueTimeout = 24 * time.Hour | |
| 137 | + | // CITokenGrace is how long a job token is recognised after the job ended. | |
| 138 | + | CITokenGrace = 24 * time.Hour | |
| 139 | + | ) | |
| 140 | + | ||
| 141 | + | // Messages of jobs ended by the server, GitHub's where it has them. | |
| 142 | + | const ( | |
| 143 | + | // CIQueueTimeoutMessage is GitHub's message for a job that no runner took. | |
| 144 | + | CIQueueTimeoutMessage = "The job was not acquired by Runner of type self-hosted even after multiple attempts" | |
| 145 | + | // CIShutdownMessage is GitHub's message for a runner that stops. | |
| 146 | + | CIShutdownMessage = "The runner has received a shutdown signal. This can happen when the runner service is stopped, or a manually started runner is canceled." | |
| 147 | + | // CILogLimitMessage ends a log that reached its limit. | |
| 148 | + | CILogLimitMessage = "The log of this job reached the limit of 512 MiB; the job is stopped." | |
| 149 | + | ) | |
| 150 | + | ||
| 151 | + | // CILostMessage is GitHub's message for a runner that stopped sending heartbeats. | |
| 152 | + | func CILostMessage(runner string) string { | |
| 153 | + | return "The self-hosted runner: " + runner + " lost communication with the server. Verify the machine is running and has a healthy network connection. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error." | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | // CIRun is one execution of one workflow file for one event. | |
| 157 | + | type CIRun struct { | |
| 158 | + | Repo RepoName | |
| 159 | + | ID int64 | |
| 160 | + | Number int64 | |
| 161 | + | Attempt int32 | |
| 162 | + | WorkflowPath string | |
| 163 | + | WorkflowName string | |
| 164 | + | Title string | |
| 165 | + | Event string | |
| 166 | + | Ref string | |
| 167 | + | Sha string | |
| 168 | + | Actor string | |
| 169 | + | Status CIStatus | |
| 170 | + | Conclusion CIConclusion | |
| 171 | + | Error string | |
| 172 | + | CreatedAt time.Time | |
| 173 | + | StartedAt time.Time | |
| 174 | + | CompletedAt time.Time | |
| 175 | + | CancelRequested bool | |
| 176 | + | CancelRequestedAt time.Time | |
| 177 | + | // Version is the version of the stored record, which grows at every | |
| 178 | + | // change and never goes back. | |
| 179 | + | Version uint64 | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | // CIRunSource is what a run was planned from: the workflow file at the sha of | |
| 183 | + | // the run, and the "github" context without the parts that depend on the job | |
| 184 | + | // and the attempt. | |
| 185 | + | type CIRunSource struct { | |
| 186 | + | RunID int64 | |
| 187 | + | Repo RepoName | |
| 188 | + | WorkflowYAML string | |
| 189 | + | GithubJSON string | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | // CIStep is a step of a job with where its lines are in the job log. | |
| 193 | + | type CIStep struct { | |
| 194 | + | Number int32 | |
| 195 | + | Name string | |
| 196 | + | State CIStepState | |
| 197 | + | Conclusion CIConclusion | |
| 198 | + | StartedAt time.Time | |
| 199 | + | CompletedAt time.Time | |
| 200 | + | LogStart int64 | |
| 201 | + | // HasLines is set once a line of the step reached the log; LogStart is | |
| 202 | + | // then the number of its first line. LogEnd is not stored: see StepRanges. | |
| 203 | + | HasLines bool | |
| 204 | + | } | |
| 205 | + | ||
| 206 | + | // CIJob is one job instance (one matrix combination) in one attempt. | |
| 207 | + | type CIJob struct { | |
| 208 | + | ID string | |
| 209 | + | RunID int64 | |
| 210 | + | // Attempt is the attempt in which the job executed, RunAttempt the attempt | |
| 211 | + | // of the run the record belongs to: they differ for a job kept by a re-run | |
| 212 | + | // of the failed jobs. | |
| 213 | + | Attempt int32 | |
| 214 | + | RunAttempt int32 | |
| 215 | + | Repo RepoName | |
| 216 | + | Key string | |
| 217 | + | Name string | |
| 218 | + | Phase CIJobPhase | |
| 219 | + | Conclusion CIConclusion | |
| 220 | + | Error string | |
| 221 | + | Labels []string | |
| 222 | + | Needs []string | |
| 223 | + | RunnerID string | |
| 224 | + | RunnerName string | |
| 225 | + | Isolation CIIsolation | |
| 226 | + | Steps []CIStep | |
| 227 | + | ||
| 228 | + | CreatedAt time.Time | |
| 229 | + | StartedAt time.Time | |
| 230 | + | CompletedAt time.Time | |
| 231 | + | ||
| 232 | + | // Index is the position of the job in the workflow file, Combo the | |
| 233 | + | // position of the instance in its matrix. | |
| 234 | + | Index int | |
| 235 | + | Combo int | |
| 236 | + | // Planned is set once runs-on, the name and the contexts are evaluated. | |
| 237 | + | // A job that is not planned has not been expanded yet. | |
| 238 | + | Planned bool | |
| 239 | + | MatrixJSON string | |
| 240 | + | // ComboTotal is the number of instances of the job's matrix. | |
| 241 | + | ComboTotal int | |
| 242 | + | // TimeoutMinutes is the timeout of the job, at least 1. | |
| 243 | + | TimeoutMinutes int32 | |
| 244 | + | FailFast bool | |
| 245 | + | // MaxParallel is the max-parallel of the strategy, 0 for no limit. | |
| 246 | + | MaxParallel int | |
| 247 | + | Outputs map[string]string | |
| 248 | + | ||
| 249 | + | // TokenHash is the hash of the job token of the current assignment; zero | |
| 250 | + | // when there is none. | |
| 251 | + | TokenHash TokenHash | |
| 252 | + | TokenValidUntil time.Time | |
| 253 | + | ReadyAt time.Time | |
| 254 | + | AssignedAt time.Time | |
| 255 | + | AckedAt time.Time | |
| 256 | + | // AssignCancels counts the assignments cancelled in a row. | |
| 257 | + | AssignCancels int | |
| 258 | + | ||
| 259 | + | CancelReason CICancelReason | |
| 260 | + | CancelMessage string | |
| 261 | + | CancelRequestedAt time.Time | |
| 262 | + | ||
| 263 | + | Version uint64 | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | // Status is the status owners see: a job is queued until its runner | |
| 267 | + | // acknowledged it. | |
| 268 | + | func (j CIJob) Status() CIStatus { | |
| 269 | + | switch j.Phase { | |
| 270 | + | case CIPhaseRunning: | |
| 271 | + | return CIInProgress | |
| 272 | + | case CIPhaseEnded: | |
| 273 | + | return CICompleted | |
| 274 | + | } | |
| 275 | + | return CIQueued | |
| 276 | + | } | |
| 277 | + | ||
| 278 | + | // Active reports whether the job is given to a runner and not over. | |
| 279 | + | func (j CIJob) Active() bool { return j.Phase == CIPhaseAssigned || j.Phase == CIPhaseRunning } | |
| 280 | + | ||
| 281 | + | // Ended reports whether the job is over. | |
| 282 | + | func (j CIJob) Ended() bool { return j.Phase == CIPhaseEnded } | |
| 283 | + | ||
| 284 | + | // HasToken reports whether a job token is set. | |
| 285 | + | func (j CIJob) HasToken() bool { return j.TokenHash != TokenHash{} } | |
| 286 | + | ||
| 287 | + | // CICheck is the result of one job on one commit. | |
| 288 | + | type CICheck struct { | |
| 289 | + | ID string | |
| 290 | + | Repo RepoName | |
| 291 | + | Sha string | |
| 292 | + | Name string | |
| 293 | + | Status CIStatus | |
| 294 | + | Conclusion CIConclusion | |
| 295 | + | RunID int64 | |
| 296 | + | JobID string | |
| 297 | + | Attempt int32 | |
| 298 | + | WorkflowName string | |
| 299 | + | CreatedAt time.Time | |
| 300 | + | StartedAt time.Time | |
| 301 | + | CompletedAt time.Time | |
| 302 | + | Version uint64 | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | // CIRunner is a registered CI runner. TokenHash is never sent to clients. | |
| 306 | + | type CIRunner struct { | |
| 307 | + | ID string | |
| 308 | + | Name string | |
| 309 | + | Labels []string | |
| 310 | + | Isolation CIIsolation | |
| 311 | + | Version string | |
| 312 | + | Arch string | |
| 313 | + | OS string | |
| 314 | + | RegisteredAt time.Time | |
| 315 | + | TokenHash TokenHash | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | // RunnerTokenPrefix and JobTokenPrefix start the secrets of runners and jobs. | |
| 319 | + | const ( | |
| 320 | + | RunnerTokenPrefix = "ermiterun_" | |
| 321 | + | JobTokenPrefix = "ermitejob_" | |
| 322 | + | // CITokenRandomLen is the length of the base64url part of both. | |
| 323 | + | CITokenRandomLen = 43 | |
| 324 | + | ) | |
| 325 | + | ||
| 326 | + | // IsRunnerToken and IsJobToken report whether s has the shape of the secret. | |
| 327 | + | func IsRunnerToken(s string) bool { return hasTokenShape(s, RunnerTokenPrefix) } | |
| 328 | + | func IsJobToken(s string) bool { return hasTokenShape(s, JobTokenPrefix) } | |
| 329 | + | ||
| 330 | + | func hasTokenShape(s, prefix string) bool { | |
| 331 | + | rest, ok := strings.CutPrefix(s, prefix) | |
| 332 | + | if !ok || len(rest) != CITokenRandomLen { | |
| 333 | + | return false | |
| 334 | + | } | |
| 335 | + | for _, r := range rest { | |
| 336 | + | switch { | |
| 337 | + | case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '-', r == '_': | |
| 338 | + | default: | |
| 339 | + | return false | |
| 340 | + | } | |
| 341 | + | } | |
| 342 | + | return true | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | // NewJobID names a job instance: "<run>-<index>-<combo>". It is the same in | |
| 346 | + | // every attempt of the run. | |
| 347 | + | func NewJobID(runID int64, index, combo int) string { | |
| 348 | + | return fmt.Sprintf("%d-%d-%d", runID, index, combo) | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | // ParseJobID returns the run of a job id, or ok false when s is not one. | |
| 352 | + | func ParseJobID(s string) (runID int64, index, combo int, ok bool) { | |
| 353 | + | parts := strings.Split(s, "-") | |
| 354 | + | if len(parts) != 3 { | |
| 355 | + | return 0, 0, 0, false | |
| 356 | + | } | |
| 357 | + | r, err1 := strconv.ParseInt(parts[0], 10, 64) | |
| 358 | + | i, err2 := strconv.Atoi(parts[1]) | |
| 359 | + | c, err3 := strconv.Atoi(parts[2]) | |
| 360 | + | if err1 != nil || err2 != nil || err3 != nil || r < 1 || i < 0 || c < 0 || NewJobID(r, i, c) != s { | |
| 361 | + | return 0, 0, 0, false | |
| 362 | + | } | |
| 363 | + | return r, i, c, true | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | var labelRE = regexp.MustCompile(`^[A-Za-z0-9._-]{1,64}$`) | |
| 367 | + | ||
| 368 | + | // ParseRunnerName validates a runner name. | |
| 369 | + | func ParseRunnerName(s string) error { | |
| 370 | + | if !labelRE.MatchString(s) { | |
| 371 | + | return fmt.Errorf("%w: a runner name has 1 to 64 letters, digits, '.', '_' or '-'", ErrInvalid) | |
| 372 | + | } | |
| 373 | + | return nil | |
| 374 | + | } | |
| 375 | + | ||
| 376 | + | // ParseRunnerLabels validates labels and returns them lowercased, without | |
| 377 | + | // duplicates, in order of first appearance. | |
| 378 | + | func ParseRunnerLabels(labels []string) ([]string, error) { | |
| 379 | + | if len(labels) > CIMaxLabels { | |
| 380 | + | return nil, fmt.Errorf("%w: at most %d labels", ErrInvalid, CIMaxLabels) | |
| 381 | + | } | |
| 382 | + | var out []string | |
| 383 | + | for _, l := range labels { | |
| 384 | + | if !labelRE.MatchString(l) { | |
| 385 | + | return nil, fmt.Errorf("%w: label %q: 1 to %d letters, digits, '.', '_' or '-'", ErrInvalid, l, CIMaxLabelLen) | |
| 386 | + | } | |
| 387 | + | l = strings.ToLower(l) | |
| 388 | + | if !slices.Contains(out, l) { | |
| 389 | + | out = append(out, l) | |
| 390 | + | } | |
| 391 | + | } | |
| 392 | + | return out, nil | |
| 393 | + | } | |
| 394 | + | ||
| 395 | + | // RunnerLabels returns the labels a runner matches: the ones it chose plus | |
| 396 | + | // the implicit "self-hosted", its os and its arch, lowercase and sorted. | |
| 397 | + | func RunnerLabels(chosen []string, os, arch string) []string { | |
| 398 | + | out := slices.Clone(chosen) | |
| 399 | + | for _, l := range []string{"self-hosted", strings.ToLower(os), strings.ToLower(arch)} { | |
| 400 | + | if !slices.Contains(out, l) { | |
| 401 | + | out = append(out, l) | |
| 402 | + | } | |
| 403 | + | } | |
| 404 | + | slices.Sort(out) | |
| 405 | + | return out | |
| 406 | + | } | |
| 407 | + | ||
| 408 | + | // LabelsMatch reports whether every wanted label is among have, ignoring case. | |
| 409 | + | func LabelsMatch(want, have []string) bool { | |
| 410 | + | for _, w := range want { | |
| 411 | + | if !slices.ContainsFunc(have, func(h string) bool { return strings.EqualFold(h, w) }) { | |
| 412 | + | return false | |
| 413 | + | } | |
| 414 | + | } | |
| 415 | + | return true | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | var outputNameRE = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_-]{0,254}$`) | |
| 419 | + | ||
| 420 | + | // CheckOutputs validates the outputs of a job: names, case-insensitive | |
| 421 | + | // uniqueness and the 1 MiB limits. | |
| 422 | + | func CheckOutputs(outputs map[string]string) error { | |
| 423 | + | seen := make(map[string]bool, len(outputs)) | |
| 424 | + | total := 0 | |
| 425 | + | for k, v := range outputs { | |
| 426 | + | if !outputNameRE.MatchString(k) { | |
| 427 | + | return fmt.Errorf("%w: output name %q: 1 to 255 letters, digits, '_' or '-', starting with a letter or '_'", ErrInvalid, k) | |
| 428 | + | } | |
| 429 | + | lk := strings.ToLower(k) | |
| 430 | + | if seen[lk] { | |
| 431 | + | return fmt.Errorf("%w: output names differ only by case: %q", ErrInvalid, k) | |
| 432 | + | } | |
| 433 | + | seen[lk] = true | |
| 434 | + | if len(v) > CIMaxOutputBytes { | |
| 435 | + | return fmt.Errorf("%w: output %q is larger than 1 MiB", ErrInvalid, k) | |
| 436 | + | } | |
| 437 | + | total += len(k) + len(v) | |
| 438 | + | } | |
| 439 | + | if total > CIMaxOutputBytes { | |
| 440 | + | return fmt.Errorf("%w: the outputs are larger than 1 MiB together", ErrInvalid) | |
| 441 | + | } | |
| 442 | + | return nil | |
| 443 | + | } | |
| 444 | + | ||
| 445 | + | // JobEndedError is returned for a call that needs a running job when the job | |
| 446 | + | // has ended. It matches ErrPreconditionFailed; the API adds the JobEnded | |
| 447 | + | // detail. | |
| 448 | + | type JobEndedError struct { | |
| 449 | + | Conclusion CIConclusion | |
| 450 | + | Message string | |
| 451 | + | } | |
| 452 | + | ||
| 453 | + | // Error implements error. | |
| 454 | + | func (e *JobEndedError) Error() string { | |
| 455 | + | return fmt.Sprintf("%v: the job has ended (%s)", ErrPreconditionFailed, e.Conclusion) | |
| 456 | + | } | |
| 457 | + | ||
| 458 | + | // Is makes errors.Is(err, ErrPreconditionFailed) true. | |
| 459 | + | func (e *JobEndedError) Is(target error) bool { return target == ErrPreconditionFailed } | |
| 460 | + | ||
| 461 | + | // IsJobEnded returns the JobEndedError in err. | |
| 462 | + | func IsJobEnded(err error) (*JobEndedError, bool) { | |
| 463 | + | var e *JobEndedError | |
| 464 | + | if errors.As(err, &e) { | |
| 465 | + | return e, true | |
| 466 | + | } | |
| 467 | + | return nil, false | |
| 468 | + | } | |
| 469 | + | ||
| 470 | + | // ErrOutOfRange means an offset is beyond what the server holds. | |
| 471 | + | var ErrOutOfRange = errors.New("out of range") | |
| 472 | + | ||
| 473 | + | // CILogLine is one line of a job log. | |
| 474 | + | type CILogLine struct { | |
| 475 | + | Time time.Time | |
| 476 | + | // Text has no line feed. | |
| 477 | + | Text string | |
| 478 | + | // Continued marks a piece of a line that was too long to send whole. | |
| 479 | + | Continued bool | |
| 480 | + | } | |
| 481 | + | ||
| 482 | + | // StepRange is where the lines of a step are in the job log. | |
| 483 | + | type StepRange struct { | |
| 484 | + | Start, End int64 | |
| 485 | + | } | |
| 486 | + | ||
| 487 | + | // StepRanges returns the range of every step of the job for a log of lines | |
| 488 | + | // lines: a step with lines ends where the next step with lines begins (the | |
| 489 | + | // last ends at the end of the log); a step without lines is empty, placed | |
| 490 | + | // where the previous step ends. | |
| 491 | + | func StepRanges(steps []CIStep, lines int64) []StepRange { | |
| 492 | + | out := make([]StepRange, len(steps)) | |
| 493 | + | var pos int64 | |
| 494 | + | for i, s := range steps { | |
| 495 | + | if !s.HasLines { | |
| 496 | + | out[i] = StepRange{Start: pos, End: pos} | |
| 497 | + | continue | |
| 498 | + | } | |
| 499 | + | end := lines | |
| 500 | + | for _, n := range steps[i+1:] { | |
| 501 | + | if n.HasLines { | |
| 502 | + | end = n.LogStart | |
| 503 | + | break | |
| 504 | + | } | |
| 505 | + | } | |
| 506 | + | out[i] = StepRange{Start: s.LogStart, End: max(end, s.LogStart)} | |
| 507 | + | pos = out[i].End | |
| 508 | + | } | |
| 509 | + | return out | |
| 510 | + | } | |
| 511 | + | ||
| 512 | + | // StepOfLine returns the number of the step a log line belongs to, 0 when no | |
| 513 | + | // step has that line. | |
| 514 | + | func StepOfLine(steps []CIStep, line int64) int32 { | |
| 515 | + | var found int32 | |
| 516 | + | for _, s := range steps { | |
| 517 | + | if s.HasLines && s.LogStart <= line { | |
| 518 | + | found = s.Number | |
| 519 | + | } | |
| 520 | + | } | |
| 521 | + | return found | |
| 522 | + | } | |
| 523 | + | ||
| 524 | + | // RunUpdated is published when a CI run is created or changes. It carries the | |
| 525 | + | // visibility and the creation time of the repository, like PushEvent. | |
| 526 | + | type RunUpdated struct { | |
| 527 | + | Run CIRun | |
| 528 | + | At time.Time | |
| 529 | + | Visibility Visibility | |
| 530 | + | CreatedAt time.Time | |
| 531 | + | } | |
| 532 | + | ||
| 533 | + | // EventKind implements Event. | |
| 534 | + | func (RunUpdated) EventKind() string { return "ci.run_updated" } | |
| 535 | + | ||
| 536 | + | // JobUpdated is published when a CI job is created or changes (status, | |
| 537 | + | // conclusion, runner or steps; never per log line). Lines is the number of | |
| 538 | + | // lines of the job log at that time, for the end of the step in progress. | |
| 539 | + | type JobUpdated struct { | |
| 540 | + | Job CIJob | |
| 541 | + | Lines int64 | |
| 542 | + | At time.Time | |
| 543 | + | Visibility Visibility | |
| 544 | + | CreatedAt time.Time | |
| 545 | + | } | |
| 546 | + | ||
| 547 | + | // EventKind implements Event. | |
| 548 | + | func (JobUpdated) EventKind() string { return "ci.job_updated" } | |
A internal/ports/ci.go +354 -0
| @@ -0,0 +1,354 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package ports | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "errors" | |
| 8 | + | "time" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // CIRunDraft is what CreateRun stores: a run with its source, its jobs and | |
| 14 | + | // their checks, built once the store has chosen the ids. | |
| 15 | + | type CIRunDraft struct { | |
| 16 | + | Run domain.CIRun | |
| 17 | + | Source domain.CIRunSource | |
| 18 | + | Jobs []domain.CIJob | |
| 19 | + | Checks []domain.CICheck | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | // CIWrite is a set of records written in one atomic batch. A record with | |
| 23 | + | // Version 0 must not exist yet; any other must be unchanged since it was read. | |
| 24 | + | // Apply returns domain.ErrConflict otherwise and domain.ErrNotFound when the | |
| 25 | + | // repository no longer exists. | |
| 26 | + | type CIWrite struct { | |
| 27 | + | Runs []domain.CIRun | |
| 28 | + | Jobs []domain.CIJob | |
| 29 | + | Checks []domain.CICheck | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | // CIStore keeps CI runs, jobs and checks. The store derives its own indexes | |
| 33 | + | // (the queue of ready jobs, the active jobs, the open runs, the job tokens) | |
| 34 | + | // from the records it writes. | |
| 35 | + | type CIStore interface { | |
| 36 | + | // CreateRun takes the next run id and the next number of the workflow in | |
| 37 | + | // the repository (both never reused), calls build with them and stores | |
| 38 | + | // the draft it returns, in one batch that fails with domain.ErrNotFound | |
| 39 | + | // once the repository is deleted. The records come back with their | |
| 40 | + | // versions. | |
| 41 | + | CreateRun(ctx context.Context, repo domain.RepoName, workflowPath string, build func(id, number int64) CIRunDraft) (CIRunDraft, error) | |
| 42 | + | // Apply writes the records in one batch and returns the version they all | |
| 43 | + | // have afterwards. | |
| 44 | + | Apply(ctx context.Context, w CIWrite) (version uint64, err error) | |
| 45 | + | // GetRun returns the run, or domain.ErrNotFound. | |
| 46 | + | GetRun(ctx context.Context, repo domain.RepoName, id int64) (domain.CIRun, error) | |
| 47 | + | // GetRunSource returns what the run was planned from, or | |
| 48 | + | // domain.ErrNotFound. It is how a run is found from its id alone. | |
| 49 | + | GetRunSource(ctx context.Context, runID int64) (domain.CIRunSource, error) | |
| 50 | + | // ListRuns returns the runs of the repository with an id below before (0 | |
| 51 | + | // for no bound), newest first, at most limit. | |
| 52 | + | ListRuns(ctx context.Context, repo domain.RepoName, before int64, limit int) ([]domain.CIRun, error) | |
| 53 | + | // RunIDs returns the ids of the runs of the repository. | |
| 54 | + | RunIDs(ctx context.Context, repo domain.RepoName) ([]int64, error) | |
| 55 | + | // ListOpenRuns returns the runs that have not completed. | |
| 56 | + | ListOpenRuns(ctx context.Context) ([]domain.CIRun, error) | |
| 57 | + | // GetJob returns a job of a run at an attempt of the run, or | |
| 58 | + | // domain.ErrNotFound. | |
| 59 | + | GetJob(ctx context.Context, runID int64, runAttempt int32, jobID string) (domain.CIJob, error) | |
| 60 | + | // ListJobs returns the jobs of a run at an attempt, ordered by index then | |
| 61 | + | // combination. | |
| 62 | + | ListJobs(ctx context.Context, runID int64, runAttempt int32) ([]domain.CIJob, error) | |
| 63 | + | // FindJobByToken returns the job whose current token has that hash and | |
| 64 | + | // whose token is still valid at now, or domain.ErrNotFound. | |
| 65 | + | FindJobByToken(ctx context.Context, hash domain.TokenHash, now time.Time) (domain.CIJob, error) | |
| 66 | + | // ListReadyJobs returns the jobs that wait for a runner, oldest first, at | |
| 67 | + | // most limit. | |
| 68 | + | ListReadyJobs(ctx context.Context, limit int) ([]domain.CIJob, error) | |
| 69 | + | // ListActiveJobs returns the jobs given to a runner and not over. | |
| 70 | + | ListActiveJobs(ctx context.Context) ([]domain.CIJob, error) | |
| 71 | + | // ListChecks returns the checks of a commit, at most limit; more is true | |
| 72 | + | // when there are more. | |
| 73 | + | ListChecks(ctx context.Context, repo domain.RepoName, sha string, limit int) (checks []domain.CICheck, more bool, err error) | |
| 74 | + | // Sweep removes the runs completed before the time, with their jobs, | |
| 75 | + | // checks and sources (not their numbers), and returns their ids so that | |
| 76 | + | // the logs can go too. | |
| 77 | + | Sweep(ctx context.Context, before time.Time) ([]int64, error) | |
| 78 | + | // CreateRunner stores the runner and the index of its token. A name in use | |
| 79 | + | // is domain.ErrAlreadyExists, or with replace the old runner is removed in | |
| 80 | + | // the same batch and returned. | |
| 81 | + | CreateRunner(ctx context.Context, r domain.CIRunner, replace bool) (removed []domain.CIRunner, err error) | |
| 82 | + | // GetRunner returns the runner, or domain.ErrNotFound. | |
| 83 | + | GetRunner(ctx context.Context, id string) (domain.CIRunner, error) | |
| 84 | + | // FindRunnerByToken returns the runner whose token has that hash, or | |
| 85 | + | // domain.ErrNotFound. | |
| 86 | + | FindRunnerByToken(ctx context.Context, hash domain.TokenHash) (domain.CIRunner, error) | |
| 87 | + | // ListRunners returns every runner ordered by name. | |
| 88 | + | ListRunners(ctx context.Context) ([]domain.CIRunner, error) | |
| 89 | + | // RemoveRunner removes the runner and its token, or returns | |
| 90 | + | // domain.ErrNotFound. | |
| 91 | + | RemoveRunner(ctx context.Context, id string) (domain.CIRunner, error) | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | // CIChecksReader is the read side of the checks, for the merge rule. | |
| 95 | + | type CIChecksReader interface { | |
| 96 | + | ListChecks(ctx context.Context, repo domain.RepoName, sha string, limit int) (checks []domain.CICheck, more bool, err error) | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // CILogRef names the log of one execution of a job. | |
| 100 | + | type CILogRef struct { | |
| 101 | + | RunID int64 | |
| 102 | + | JobID string | |
| 103 | + | Attempt int32 | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | // ErrCILogLimit is returned by CILogStore.Append when the lines do not fit in | |
| 107 | + | // the log limit; nothing was appended. | |
| 108 | + | var ErrCILogLimit = errors.New("log limit reached") | |
| 109 | + | ||
| 110 | + | // ErrCILogGap wraps domain.ErrOutOfRange when an offset is above the count. | |
| 111 | + | type ErrCILogGap struct{ Count int64 } | |
| 112 | + | ||
| 113 | + | // Error implements error. | |
| 114 | + | func (e *ErrCILogGap) Error() string { | |
| 115 | + | return "out of range: the log holds lines up to the offset of " + itoa(e.Count) | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // Is makes errors.Is(err, domain.ErrOutOfRange) true. | |
| 119 | + | func (e *ErrCILogGap) Is(target error) bool { return target == domain.ErrOutOfRange } | |
| 120 | + | ||
| 121 | + | func itoa(n int64) string { | |
| 122 | + | if n == 0 { | |
| 123 | + | return "0" | |
| 124 | + | } | |
| 125 | + | var b [20]byte | |
| 126 | + | i := len(b) | |
| 127 | + | for n > 0 { | |
| 128 | + | i-- | |
| 129 | + | b[i] = byte('0' + n%10) | |
| 130 | + | n /= 10 | |
| 131 | + | } | |
| 132 | + | return string(b[i:]) | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | // CILogStore keeps the logs of the jobs: append-only files, one line per log | |
| 136 | + | // line, never in the metadata store. Lines are numbered from 0. | |
| 137 | + | type CILogStore interface { | |
| 138 | + | // Append appends the lines at offset (the number of the first line), with | |
| 139 | + | // the idempotence of AppendLog: lines already stored are skipped, a gap is | |
| 140 | + | // an *ErrCILogGap. It returns the number of lines after the call and how | |
| 141 | + | // many of the request were new. A log that would exceed limit bytes gets | |
| 142 | + | // nothing and ErrCILogLimit. | |
| 143 | + | Append(ctx context.Context, ref CILogRef, offset int64, lines []domain.CILogLine, limit int64) (next int64, added int, err error) | |
| 144 | + | // AppendForced appends lines whatever the limit (the last line of a log | |
| 145 | + | // that reached it). | |
| 146 | + | AppendForced(ctx context.Context, ref CILogRef, lines []domain.CILogLine) (next int64, err error) | |
| 147 | + | // Count returns the number of lines held, 0 for a log that does not exist. | |
| 148 | + | Count(ctx context.Context, ref CILogRef) (int64, error) | |
| 149 | + | // Read returns lines from the one numbered from, at most maxLines lines and | |
| 150 | + | // about maxBytes of text. It returns domain.ErrNotFound when the log does | |
| 151 | + | // not exist and domain.ErrOutOfRange when from is above the count. | |
| 152 | + | Read(ctx context.Context, ref CILogRef, from int64, maxLines, maxBytes int) ([]domain.CILogLine, error) | |
| 153 | + | // Changed returns a channel that is closed at the next append to the log. | |
| 154 | + | Changed(ref CILogRef) <-chan struct{} | |
| 155 | + | // Reset empties the log. | |
| 156 | + | Reset(ctx context.Context, ref CILogRef) error | |
| 157 | + | // RemoveRuns deletes the logs of the runs. | |
| 158 | + | RemoveRuns(ctx context.Context, runIDs []int64) error | |
| 159 | + | // RunIDs lists the runs that have a log directory, for the orphan sweep. | |
| 160 | + | RunIDs(ctx context.Context) ([]int64, error) | |
| 161 | + | } | |
| 162 | + | ||
| 163 | + | // WorkflowTrigger starts the workflows that match pushed refs. OnPush only | |
| 164 | + | // queues the work and returns: planning happens in the background and its | |
| 165 | + | // errors never reach the push. | |
| 166 | + | type WorkflowTrigger interface { | |
| 167 | + | OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate, pusher domain.Principal) | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | // CIGit is what planning needs from Git beyond RepoReader and MergeGit. | |
| 171 | + | type CIGit interface { | |
| 172 | + | // ChangedPaths returns the paths changed from base to head (a zero base | |
| 173 | + | // means the empty tree), in the order of the diff and without renames | |
| 174 | + | // detection, at most max; truncated is true when there were more. | |
| 175 | + | ChangedPaths(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, max int) (paths []string, truncated bool, err error) | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | // --- inbound --- | |
| 179 | + | ||
| 180 | + | // RegisterRunnerParams describe a runner to register. | |
| 181 | + | type RegisterRunnerParams struct { | |
| 182 | + | Name string | |
| 183 | + | Labels []string | |
| 184 | + | Version string | |
| 185 | + | Isolation domain.CIIsolation | |
| 186 | + | Arch, OS string | |
| 187 | + | Replace bool | |
| 188 | + | } | |
| 189 | + | ||
| 190 | + | // RegisteredRunner is a new runner with its secret, shown once. | |
| 191 | + | type RegisteredRunner struct { | |
| 192 | + | Runner domain.CIRunner | |
| 193 | + | Token string | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | // RunnerJob is a job given to a runner. | |
| 197 | + | type RunnerJob struct { | |
| 198 | + | Job domain.CIJob | |
| 199 | + | Token string | |
| 200 | + | Repo domain.RepoName | |
| 201 | + | Sha, Ref string | |
| 202 | + | WorkflowPath string | |
| 203 | + | WorkflowYAML string | |
| 204 | + | } | |
| 205 | + | ||
| 206 | + | // FetchJobParams selects the job a runner asks for. | |
| 207 | + | type FetchJobParams struct { | |
| 208 | + | Labels []string | |
| 209 | + | PollID string | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | // FetchJobResult is the answer of a poll. | |
| 213 | + | type FetchJobResult struct { | |
| 214 | + | Job *RunnerJob | |
| 215 | + | // RetryAfter is how long the runner waits before polling again. | |
| 216 | + | RetryAfter time.Duration | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | // StepUpdate is the state of one step as a runner reports it. | |
| 220 | + | type StepUpdate struct { | |
| 221 | + | Number int32 | |
| 222 | + | Name string | |
| 223 | + | State domain.CIStepState | |
| 224 | + | Conclusion domain.CIConclusion | |
| 225 | + | StartedAt time.Time | |
| 226 | + | CompletedAt time.Time | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | // CompleteJobParams end a job. | |
| 230 | + | type CompleteJobParams struct { | |
| 231 | + | JobID string | |
| 232 | + | Conclusion domain.CIConclusion | |
| 233 | + | Outputs map[string]string | |
| 234 | + | RunnerShutdown bool | |
| 235 | + | Steps []StepUpdate | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | // CompleteJobResult is the stored result of a job. | |
| 239 | + | type CompleteJobResult struct { | |
| 240 | + | Conclusion domain.CIConclusion | |
| 241 | + | Error string | |
| 242 | + | Applied bool | |
| 243 | + | } | |
| 244 | + | ||
| 245 | + | // JobSignal is one message of WatchJob. | |
| 246 | + | type JobSignal struct { | |
| 247 | + | Keepalive bool | |
| 248 | + | // Cancel is set for a cancel request. | |
| 249 | + | Cancel *JobCancel | |
| 250 | + | // Ended is set when the job has ended. | |
| 251 | + | Ended *domain.JobEndedError | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | // JobCancel is a cancel request for a running job. | |
| 255 | + | type JobCancel struct { | |
| 256 | + | Reason domain.CICancelReason | |
| 257 | + | Message string | |
| 258 | + | RequestedAt time.Time | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | // RunnerService is what runners call. The secret is the bearer secret of the | |
| 262 | + | // call: the runner token for FetchJob, the job token for the other calls. | |
| 263 | + | // A bad secret is domain.ErrUnauthenticated; a job token of another job than | |
| 264 | + | // the one named is domain.ErrForbidden. *usecase.CIRunner implements it. | |
| 265 | + | type RunnerService interface { | |
| 266 | + | Register(ctx context.Context, p domain.Principal, params RegisterRunnerParams) (RegisteredRunner, error) | |
| 267 | + | FetchJob(ctx context.Context, secret string, params FetchJobParams) (FetchJobResult, error) | |
| 268 | + | UpdateJob(ctx context.Context, secret, jobID string, steps []StepUpdate) error | |
| 269 | + | AppendLog(ctx context.Context, secret, jobID string, step int32, offset int64, lines []domain.CILogLine) (next int64, err error) | |
| 270 | + | Heartbeat(ctx context.Context, secret, jobID string) error | |
| 271 | + | WatchJob(ctx context.Context, secret, jobID string, send func(JobSignal) error) error | |
| 272 | + | CompleteJob(ctx context.Context, secret string, params CompleteJobParams) (CompleteJobResult, error) | |
| 273 | + | } | |
| 274 | + | ||
| 275 | + | // RunStep is a step with its place in the log. | |
| 276 | + | type RunStep struct { | |
| 277 | + | domain.CIStep | |
| 278 | + | LogEnd int64 | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | // JobView is a job as owners see it. | |
| 282 | + | type JobView struct { | |
| 283 | + | domain.CIJob | |
| 284 | + | // Lines is the number of lines of its log now. | |
| 285 | + | Lines int64 | |
| 286 | + | // RunnerGone is set when the runner of the job was removed since. | |
| 287 | + | RunnerGone bool | |
| 288 | + | } | |
| 289 | + | ||
| 290 | + | // Steps returns the steps with their log ranges. | |
| 291 | + | func (v JobView) StepRanges() []domain.StepRange { return domain.StepRanges(v.Steps, v.Lines) } | |
| 292 | + | ||
| 293 | + | // ListRunsParams select a page of runs. | |
| 294 | + | type ListRunsParams struct { | |
| 295 | + | PageSize int | |
| 296 | + | PageToken string | |
| 297 | + | } | |
| 298 | + | ||
| 299 | + | // RunPage is a page of runs, newest first. | |
| 300 | + | type RunPage struct { | |
| 301 | + | Runs []domain.CIRun | |
| 302 | + | NextPageToken string | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | // RunDetail is a run with its jobs. | |
| 306 | + | type RunDetail struct { | |
| 307 | + | Run domain.CIRun | |
| 308 | + | Jobs []JobView | |
| 309 | + | LatestAttempt int32 | |
| 310 | + | } | |
| 311 | + | ||
| 312 | + | // LogBatch is a message of a log stream: consecutive lines from Offset. | |
| 313 | + | type LogBatch struct { | |
| 314 | + | Offset int64 | |
| 315 | + | Lines []JobLogLine | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | // JobLogLine is a log line with its step. | |
| 319 | + | type JobLogLine struct { | |
| 320 | + | Step int32 | |
| 321 | + | domain.CILogLine | |
| 322 | + | } | |
| 323 | + | ||
| 324 | + | // ChecksResult are the checks of a commit. | |
| 325 | + | type ChecksResult struct { | |
| 326 | + | Checks []domain.CICheck | |
| 327 | + | Truncated bool | |
| 328 | + | } | |
| 329 | + | ||
| 330 | + | // RunnerPage is a page of runners. | |
| 331 | + | type RunnerPage struct { | |
| 332 | + | Runners []RunnerView | |
| 333 | + | NextPageToken string | |
| 334 | + | } | |
| 335 | + | ||
| 336 | + | // RunnerView is a runner with what is known of it now. | |
| 337 | + | type RunnerView struct { | |
| 338 | + | domain.CIRunner | |
| 339 | + | LastSeen time.Time | |
| 340 | + | Busy bool | |
| 341 | + | } | |
| 342 | + | ||
| 343 | + | // RunService is the owner side of CI. *usecase.CIRuns implements it. | |
| 344 | + | type RunService interface { | |
| 345 | + | ListRuns(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListRunsParams) (RunPage, error) | |
| 346 | + | GetRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, attempt int32) (RunDetail, error) | |
| 347 | + | GetJob(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32) (JobView, error) | |
| 348 | + | GetJobLog(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32, offset int64, follow bool, send func(LogBatch) error) error | |
| 349 | + | CancelRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64) (domain.CIRun, error) | |
| 350 | + | RerunRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, failedOnly bool) (domain.CIRun, error) | |
| 351 | + | ListChecks(ctx context.Context, p domain.Principal, repo domain.RepoName, sha string) (ChecksResult, error) | |
| 352 | + | ListRunners(ctx context.Context, p domain.Principal, size int, token string) (RunnerPage, error) | |
| 353 | + | RemoveRunner(ctx context.Context, p domain.Principal, id string) error | |
| 354 | + | } | |
A internal/ports/mocks/CIChecksReader.go +134 -0
| @@ -0,0 +1,134 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewCIChecksReader creates a new instance of CIChecksReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewCIChecksReader(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *CIChecksReader { | |
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 21 | + | helper.Helper() | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | mock := &CIChecksReader{} | |
| 25 | + | mock.Mock.Test(t) | |
| 26 | + | ||
| 27 | + | t.Cleanup(func() { | |
| 28 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 29 | + | helper.Helper() | |
| 30 | + | } | |
| 31 | + | mock.AssertExpectations(t) | |
| 32 | + | }) | |
| 33 | + | ||
| 34 | + | return mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | // CIChecksReader is an autogenerated mock type for the CIChecksReader type | |
| 38 | + | type CIChecksReader struct { | |
| 39 | + | mock.Mock | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | type CIChecksReader_Expecter struct { | |
| 43 | + | mock *mock.Mock | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | func (_m *CIChecksReader) EXPECT() *CIChecksReader_Expecter { | |
| 47 | + | return &CIChecksReader_Expecter{mock: &_m.Mock} | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // ListChecks provides a mock function for the type CIChecksReader | |
| 51 | + | func (_mock *CIChecksReader) ListChecks(ctx context.Context, repo domain.RepoName, sha string, limit int) ([]domain.CICheck, bool, error) { | |
| 52 | + | ret := _mock.Called(ctx, repo, sha, limit) | |
| 53 | + | ||
| 54 | + | if len(ret) == 0 { | |
| 55 | + | panic("no return value specified for ListChecks") | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | var r0 []domain.CICheck | |
| 59 | + | var r1 bool | |
| 60 | + | var r2 error | |
| 61 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, int) ([]domain.CICheck, bool, error)); ok { | |
| 62 | + | return returnFunc(ctx, repo, sha, limit) | |
| 63 | + | } | |
| 64 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, int) []domain.CICheck); ok { | |
| 65 | + | r0 = returnFunc(ctx, repo, sha, limit) | |
| 66 | + | } else { | |
| 67 | + | if ret.Get(0) != nil { | |
| 68 | + | r0 = ret.Get(0).([]domain.CICheck) | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, string, int) bool); ok { | |
| 72 | + | r1 = returnFunc(ctx, repo, sha, limit) | |
| 73 | + | } else { | |
| 74 | + | r1 = ret.Get(1).(bool) | |
| 75 | + | } | |
| 76 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, string, int) error); ok { | |
| 77 | + | r2 = returnFunc(ctx, repo, sha, limit) | |
| 78 | + | } else { | |
| 79 | + | r2 = ret.Error(2) | |
| 80 | + | } | |
| 81 | + | return r0, r1, r2 | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // CIChecksReader_ListChecks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListChecks' | |
| 85 | + | type CIChecksReader_ListChecks_Call struct { | |
| 86 | + | *mock.Call | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // ListChecks is a helper method to define mock.On call | |
| 90 | + | // - ctx context.Context | |
| 91 | + | // - repo domain.RepoName | |
| 92 | + | // - sha string | |
| 93 | + | // - limit int | |
| 94 | + | func (_e *CIChecksReader_Expecter) ListChecks(ctx any, repo any, sha any, limit any) *CIChecksReader_ListChecks_Call { | |
| 95 | + | return &CIChecksReader_ListChecks_Call{Call: _e.mock.On("ListChecks", ctx, repo, sha, limit)} | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | func (_c *CIChecksReader_ListChecks_Call) Run(run func(ctx context.Context, repo domain.RepoName, sha string, limit int)) *CIChecksReader_ListChecks_Call { | |
| 99 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 100 | + | var arg0 context.Context | |
| 101 | + | if args[0] != nil { | |
| 102 | + | arg0 = args[0].(context.Context) | |
| 103 | + | } | |
| 104 | + | var arg1 domain.RepoName | |
| 105 | + | if args[1] != nil { | |
| 106 | + | arg1 = args[1].(domain.RepoName) | |
| 107 | + | } | |
| 108 | + | var arg2 string | |
| 109 | + | if args[2] != nil { | |
| 110 | + | arg2 = args[2].(string) | |
| 111 | + | } | |
| 112 | + | var arg3 int | |
| 113 | + | if args[3] != nil { | |
| 114 | + | arg3 = args[3].(int) | |
| 115 | + | } | |
| 116 | + | run( | |
| 117 | + | arg0, | |
| 118 | + | arg1, | |
| 119 | + | arg2, | |
| 120 | + | arg3, | |
| 121 | + | ) | |
| 122 | + | }) | |
| 123 | + | return _c | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | func (_c *CIChecksReader_ListChecks_Call) Return(checks []domain.CICheck, more bool, err error) *CIChecksReader_ListChecks_Call { | |
| 127 | + | _c.Call.Return(checks, more, err) | |
| 128 | + | return _c | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | func (_c *CIChecksReader_ListChecks_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, sha string, limit int) ([]domain.CICheck, bool, error)) *CIChecksReader_ListChecks_Call { | |
| 132 | + | _c.Call.Return(run) | |
| 133 | + | return _c | |
| 134 | + | } | |
A internal/ports/mocks/CIGit.go +140 -0
| @@ -0,0 +1,140 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewCIGit creates a new instance of CIGit. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewCIGit(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *CIGit { | |
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 21 | + | helper.Helper() | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | mock := &CIGit{} | |
| 25 | + | mock.Mock.Test(t) | |
| 26 | + | ||
| 27 | + | t.Cleanup(func() { | |
| 28 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 29 | + | helper.Helper() | |
| 30 | + | } | |
| 31 | + | mock.AssertExpectations(t) | |
| 32 | + | }) | |
| 33 | + | ||
| 34 | + | return mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | // CIGit is an autogenerated mock type for the CIGit type | |
| 38 | + | type CIGit struct { | |
| 39 | + | mock.Mock | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | type CIGit_Expecter struct { | |
| 43 | + | mock *mock.Mock | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | func (_m *CIGit) EXPECT() *CIGit_Expecter { | |
| 47 | + | return &CIGit_Expecter{mock: &_m.Mock} | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // ChangedPaths provides a mock function for the type CIGit | |
| 51 | + | func (_mock *CIGit) ChangedPaths(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, max int) ([]string, bool, error) { | |
| 52 | + | ret := _mock.Called(ctx, repo, base, head, max) | |
| 53 | + | ||
| 54 | + | if len(ret) == 0 { | |
| 55 | + | panic("no return value specified for ChangedPaths") | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | var r0 []string | |
| 59 | + | var r1 bool | |
| 60 | + | var r2 error | |
| 61 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) ([]string, bool, error)); ok { | |
| 62 | + | return returnFunc(ctx, repo, base, head, max) | |
| 63 | + | } | |
| 64 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) []string); ok { | |
| 65 | + | r0 = returnFunc(ctx, repo, base, head, max) | |
| 66 | + | } else { | |
| 67 | + | if ret.Get(0) != nil { | |
| 68 | + | r0 = ret.Get(0).([]string) | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) bool); ok { | |
| 72 | + | r1 = returnFunc(ctx, repo, base, head, max) | |
| 73 | + | } else { | |
| 74 | + | r1 = ret.Get(1).(bool) | |
| 75 | + | } | |
| 76 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) error); ok { | |
| 77 | + | r2 = returnFunc(ctx, repo, base, head, max) | |
| 78 | + | } else { | |
| 79 | + | r2 = ret.Error(2) | |
| 80 | + | } | |
| 81 | + | return r0, r1, r2 | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // CIGit_ChangedPaths_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChangedPaths' | |
| 85 | + | type CIGit_ChangedPaths_Call struct { | |
| 86 | + | *mock.Call | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // ChangedPaths is a helper method to define mock.On call | |
| 90 | + | // - ctx context.Context | |
| 91 | + | // - repo domain.RepoName | |
| 92 | + | // - base domain.ObjectID | |
| 93 | + | // - head domain.ObjectID | |
| 94 | + | // - max int | |
| 95 | + | func (_e *CIGit_Expecter) ChangedPaths(ctx any, repo any, base any, head any, max any) *CIGit_ChangedPaths_Call { | |
| 96 | + | return &CIGit_ChangedPaths_Call{Call: _e.mock.On("ChangedPaths", ctx, repo, base, head, max)} | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func (_c *CIGit_ChangedPaths_Call) Run(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, max int)) *CIGit_ChangedPaths_Call { | |
| 100 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 101 | + | var arg0 context.Context | |
| 102 | + | if args[0] != nil { | |
| 103 | + | arg0 = args[0].(context.Context) | |
| 104 | + | } | |
| 105 | + | var arg1 domain.RepoName | |
| 106 | + | if args[1] != nil { | |
| 107 | + | arg1 = args[1].(domain.RepoName) | |
| 108 | + | } | |
| 109 | + | var arg2 domain.ObjectID | |
| 110 | + | if args[2] != nil { | |
| 111 | + | arg2 = args[2].(domain.ObjectID) | |
| 112 | + | } | |
| 113 | + | var arg3 domain.ObjectID | |
| 114 | + | if args[3] != nil { | |
| 115 | + | arg3 = args[3].(domain.ObjectID) | |
| 116 | + | } | |
| 117 | + | var arg4 int | |
| 118 | + | if args[4] != nil { | |
| 119 | + | arg4 = args[4].(int) | |
| 120 | + | } | |
| 121 | + | run( | |
| 122 | + | arg0, | |
| 123 | + | arg1, | |
| 124 | + | arg2, | |
| 125 | + | arg3, | |
| 126 | + | arg4, | |
| 127 | + | ) | |
| 128 | + | }) | |
| 129 | + | return _c | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | func (_c *CIGit_ChangedPaths_Call) Return(paths []string, truncated bool, err error) *CIGit_ChangedPaths_Call { | |
| 133 | + | _c.Call.Return(paths, truncated, err) | |
| 134 | + | return _c | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | func (_c *CIGit_ChangedPaths_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, max int) ([]string, bool, error)) *CIGit_ChangedPaths_Call { | |
| 138 | + | _c.Call.Return(run) | |
| 139 | + | return _c | |
| 140 | + | } | |
A internal/ports/mocks/CILogStore.go +592 -0
| @@ -0,0 +1,592 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | "bnema.dev/ermite/internal/ports" | |
| 12 | + | mock "github.com/stretchr/testify/mock" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // NewCILogStore creates a new instance of CILogStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 16 | + | // The first argument is typically a *testing.T value. | |
| 17 | + | func NewCILogStore(t interface { | |
| 18 | + | mock.TestingT | |
| 19 | + | Cleanup(func()) | |
| 20 | + | }) *CILogStore { | |
| 21 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 22 | + | helper.Helper() | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | mock := &CILogStore{} | |
| 26 | + | mock.Mock.Test(t) | |
| 27 | + | ||
| 28 | + | t.Cleanup(func() { | |
| 29 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 30 | + | helper.Helper() | |
| 31 | + | } | |
| 32 | + | mock.AssertExpectations(t) | |
| 33 | + | }) | |
| 34 | + | ||
| 35 | + | return mock | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // CILogStore is an autogenerated mock type for the CILogStore type | |
| 39 | + | type CILogStore struct { | |
| 40 | + | mock.Mock | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | type CILogStore_Expecter struct { | |
| 44 | + | mock *mock.Mock | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | func (_m *CILogStore) EXPECT() *CILogStore_Expecter { | |
| 48 | + | return &CILogStore_Expecter{mock: &_m.Mock} | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // Append provides a mock function for the type CILogStore | |
| 52 | + | func (_mock *CILogStore) Append(ctx context.Context, ref ports.CILogRef, offset int64, lines []domain.CILogLine, limit int64) (int64, int, error) { | |
| 53 | + | ret := _mock.Called(ctx, ref, offset, lines, limit) | |
| 54 | + | ||
| 55 | + | if len(ret) == 0 { | |
| 56 | + | panic("no return value specified for Append") | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | var r0 int64 | |
| 60 | + | var r1 int | |
| 61 | + | var r2 error | |
| 62 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, int64, []domain.CILogLine, int64) (int64, int, error)); ok { | |
| 63 | + | return returnFunc(ctx, ref, offset, lines, limit) | |
| 64 | + | } | |
| 65 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, int64, []domain.CILogLine, int64) int64); ok { | |
| 66 | + | r0 = returnFunc(ctx, ref, offset, lines, limit) | |
| 67 | + | } else { | |
| 68 | + | r0 = ret.Get(0).(int64) | |
| 69 | + | } | |
| 70 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, ports.CILogRef, int64, []domain.CILogLine, int64) int); ok { | |
| 71 | + | r1 = returnFunc(ctx, ref, offset, lines, limit) | |
| 72 | + | } else { | |
| 73 | + | r1 = ret.Get(1).(int) | |
| 74 | + | } | |
| 75 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, ports.CILogRef, int64, []domain.CILogLine, int64) error); ok { | |
| 76 | + | r2 = returnFunc(ctx, ref, offset, lines, limit) | |
| 77 | + | } else { | |
| 78 | + | r2 = ret.Error(2) | |
| 79 | + | } | |
| 80 | + | return r0, r1, r2 | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // CILogStore_Append_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Append' | |
| 84 | + | type CILogStore_Append_Call struct { | |
| 85 | + | *mock.Call | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | // Append is a helper method to define mock.On call | |
| 89 | + | // - ctx context.Context | |
| 90 | + | // - ref ports.CILogRef | |
| 91 | + | // - offset int64 | |
| 92 | + | // - lines []domain.CILogLine | |
| 93 | + | // - limit int64 | |
| 94 | + | func (_e *CILogStore_Expecter) Append(ctx any, ref any, offset any, lines any, limit any) *CILogStore_Append_Call { | |
| 95 | + | return &CILogStore_Append_Call{Call: _e.mock.On("Append", ctx, ref, offset, lines, limit)} | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | func (_c *CILogStore_Append_Call) Run(run func(ctx context.Context, ref ports.CILogRef, offset int64, lines []domain.CILogLine, limit int64)) *CILogStore_Append_Call { | |
| 99 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 100 | + | var arg0 context.Context | |
| 101 | + | if args[0] != nil { | |
| 102 | + | arg0 = args[0].(context.Context) | |
| 103 | + | } | |
| 104 | + | var arg1 ports.CILogRef | |
| 105 | + | if args[1] != nil { | |
| 106 | + | arg1 = args[1].(ports.CILogRef) | |
| 107 | + | } | |
| 108 | + | var arg2 int64 | |
| 109 | + | if args[2] != nil { | |
| 110 | + | arg2 = args[2].(int64) | |
| 111 | + | } | |
| 112 | + | var arg3 []domain.CILogLine | |
| 113 | + | if args[3] != nil { | |
| 114 | + | arg3 = args[3].([]domain.CILogLine) | |
| 115 | + | } | |
| 116 | + | var arg4 int64 | |
| 117 | + | if args[4] != nil { | |
| 118 | + | arg4 = args[4].(int64) | |
| 119 | + | } | |
| 120 | + | run( | |
| 121 | + | arg0, | |
| 122 | + | arg1, | |
| 123 | + | arg2, | |
| 124 | + | arg3, | |
| 125 | + | arg4, | |
| 126 | + | ) | |
| 127 | + | }) | |
| 128 | + | return _c | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | func (_c *CILogStore_Append_Call) Return(next int64, added int, err error) *CILogStore_Append_Call { | |
| 132 | + | _c.Call.Return(next, added, err) | |
| 133 | + | return _c | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | func (_c *CILogStore_Append_Call) RunAndReturn(run func(ctx context.Context, ref ports.CILogRef, offset int64, lines []domain.CILogLine, limit int64) (int64, int, error)) *CILogStore_Append_Call { | |
| 137 | + | _c.Call.Return(run) | |
| 138 | + | return _c | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | // AppendForced provides a mock function for the type CILogStore | |
| 142 | + | func (_mock *CILogStore) AppendForced(ctx context.Context, ref ports.CILogRef, lines []domain.CILogLine) (int64, error) { | |
| 143 | + | ret := _mock.Called(ctx, ref, lines) | |
| 144 | + | ||
| 145 | + | if len(ret) == 0 { | |
| 146 | + | panic("no return value specified for AppendForced") | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | var r0 int64 | |
| 150 | + | var r1 error | |
| 151 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, []domain.CILogLine) (int64, error)); ok { | |
| 152 | + | return returnFunc(ctx, ref, lines) | |
| 153 | + | } | |
| 154 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, []domain.CILogLine) int64); ok { | |
| 155 | + | r0 = returnFunc(ctx, ref, lines) | |
| 156 | + | } else { | |
| 157 | + | r0 = ret.Get(0).(int64) | |
| 158 | + | } | |
| 159 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, ports.CILogRef, []domain.CILogLine) error); ok { | |
| 160 | + | r1 = returnFunc(ctx, ref, lines) | |
| 161 | + | } else { | |
| 162 | + | r1 = ret.Error(1) | |
| 163 | + | } | |
| 164 | + | return r0, r1 | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | // CILogStore_AppendForced_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AppendForced' | |
| 168 | + | type CILogStore_AppendForced_Call struct { | |
| 169 | + | *mock.Call | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | // AppendForced is a helper method to define mock.On call | |
| 173 | + | // - ctx context.Context | |
| 174 | + | // - ref ports.CILogRef | |
| 175 | + | // - lines []domain.CILogLine | |
| 176 | + | func (_e *CILogStore_Expecter) AppendForced(ctx any, ref any, lines any) *CILogStore_AppendForced_Call { | |
| 177 | + | return &CILogStore_AppendForced_Call{Call: _e.mock.On("AppendForced", ctx, ref, lines)} | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | func (_c *CILogStore_AppendForced_Call) Run(run func(ctx context.Context, ref ports.CILogRef, lines []domain.CILogLine)) *CILogStore_AppendForced_Call { | |
| 181 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 182 | + | var arg0 context.Context | |
| 183 | + | if args[0] != nil { | |
| 184 | + | arg0 = args[0].(context.Context) | |
| 185 | + | } | |
| 186 | + | var arg1 ports.CILogRef | |
| 187 | + | if args[1] != nil { | |
| 188 | + | arg1 = args[1].(ports.CILogRef) | |
| 189 | + | } | |
| 190 | + | var arg2 []domain.CILogLine | |
| 191 | + | if args[2] != nil { | |
| 192 | + | arg2 = args[2].([]domain.CILogLine) | |
| 193 | + | } | |
| 194 | + | run( | |
| 195 | + | arg0, | |
| 196 | + | arg1, | |
| 197 | + | arg2, | |
| 198 | + | ) | |
| 199 | + | }) | |
| 200 | + | return _c | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | func (_c *CILogStore_AppendForced_Call) Return(next int64, err error) *CILogStore_AppendForced_Call { | |
| 204 | + | _c.Call.Return(next, err) | |
| 205 | + | return _c | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | func (_c *CILogStore_AppendForced_Call) RunAndReturn(run func(ctx context.Context, ref ports.CILogRef, lines []domain.CILogLine) (int64, error)) *CILogStore_AppendForced_Call { | |
| 209 | + | _c.Call.Return(run) | |
| 210 | + | return _c | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | // Changed provides a mock function for the type CILogStore | |
| 214 | + | func (_mock *CILogStore) Changed(ref ports.CILogRef) <-chan struct{} { | |
| 215 | + | ret := _mock.Called(ref) | |
| 216 | + | ||
| 217 | + | if len(ret) == 0 { | |
| 218 | + | panic("no return value specified for Changed") | |
| 219 | + | } | |
| 220 | + | ||
| 221 | + | var r0 <-chan struct{} | |
| 222 | + | if returnFunc, ok := ret.Get(0).(func(ports.CILogRef) <-chan struct{}); ok { | |
| 223 | + | r0 = returnFunc(ref) | |
| 224 | + | } else { | |
| 225 | + | if ret.Get(0) != nil { | |
| 226 | + | r0 = ret.Get(0).(<-chan struct{}) | |
| 227 | + | } | |
| 228 | + | } | |
| 229 | + | return r0 | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | // CILogStore_Changed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Changed' | |
| 233 | + | type CILogStore_Changed_Call struct { | |
| 234 | + | *mock.Call | |
| 235 | + | } | |
| 236 | + | ||
| 237 | + | // Changed is a helper method to define mock.On call | |
| 238 | + | // - ref ports.CILogRef | |
| 239 | + | func (_e *CILogStore_Expecter) Changed(ref any) *CILogStore_Changed_Call { | |
| 240 | + | return &CILogStore_Changed_Call{Call: _e.mock.On("Changed", ref)} | |
| 241 | + | } | |
| 242 | + | ||
| 243 | + | func (_c *CILogStore_Changed_Call) Run(run func(ref ports.CILogRef)) *CILogStore_Changed_Call { | |
| 244 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 245 | + | var arg0 ports.CILogRef | |
| 246 | + | if args[0] != nil { | |
| 247 | + | arg0 = args[0].(ports.CILogRef) | |
| 248 | + | } | |
| 249 | + | run( | |
| 250 | + | arg0, | |
| 251 | + | ) | |
| 252 | + | }) | |
| 253 | + | return _c | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | func (_c *CILogStore_Changed_Call) Return(valCh <-chan struct{}) *CILogStore_Changed_Call { | |
| 257 | + | _c.Call.Return(valCh) | |
| 258 | + | return _c | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | func (_c *CILogStore_Changed_Call) RunAndReturn(run func(ref ports.CILogRef) <-chan struct{}) *CILogStore_Changed_Call { | |
| 262 | + | _c.Call.Return(run) | |
| 263 | + | return _c | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | // Count provides a mock function for the type CILogStore | |
| 267 | + | func (_mock *CILogStore) Count(ctx context.Context, ref ports.CILogRef) (int64, error) { | |
| 268 | + | ret := _mock.Called(ctx, ref) | |
| 269 | + | ||
| 270 | + | if len(ret) == 0 { | |
| 271 | + | panic("no return value specified for Count") | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | var r0 int64 | |
| 275 | + | var r1 error | |
| 276 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef) (int64, error)); ok { | |
| 277 | + | return returnFunc(ctx, ref) | |
| 278 | + | } | |
| 279 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef) int64); ok { | |
| 280 | + | r0 = returnFunc(ctx, ref) | |
| 281 | + | } else { | |
| 282 | + | r0 = ret.Get(0).(int64) | |
| 283 | + | } | |
| 284 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, ports.CILogRef) error); ok { | |
| 285 | + | r1 = returnFunc(ctx, ref) | |
| 286 | + | } else { | |
| 287 | + | r1 = ret.Error(1) | |
| 288 | + | } | |
| 289 | + | return r0, r1 | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | // CILogStore_Count_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Count' | |
| 293 | + | type CILogStore_Count_Call struct { | |
| 294 | + | *mock.Call | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | // Count is a helper method to define mock.On call | |
| 298 | + | // - ctx context.Context | |
| 299 | + | // - ref ports.CILogRef | |
| 300 | + | func (_e *CILogStore_Expecter) Count(ctx any, ref any) *CILogStore_Count_Call { | |
| 301 | + | return &CILogStore_Count_Call{Call: _e.mock.On("Count", ctx, ref)} | |
| 302 | + | } | |
| 303 | + | ||
| 304 | + | func (_c *CILogStore_Count_Call) Run(run func(ctx context.Context, ref ports.CILogRef)) *CILogStore_Count_Call { | |
| 305 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 306 | + | var arg0 context.Context | |
| 307 | + | if args[0] != nil { | |
| 308 | + | arg0 = args[0].(context.Context) | |
| 309 | + | } | |
| 310 | + | var arg1 ports.CILogRef | |
| 311 | + | if args[1] != nil { | |
| 312 | + | arg1 = args[1].(ports.CILogRef) | |
| 313 | + | } | |
| 314 | + | run( | |
| 315 | + | arg0, | |
| 316 | + | arg1, | |
| 317 | + | ) | |
| 318 | + | }) | |
| 319 | + | return _c | |
| 320 | + | } | |
| 321 | + | ||
| 322 | + | func (_c *CILogStore_Count_Call) Return(n int64, err error) *CILogStore_Count_Call { | |
| 323 | + | _c.Call.Return(n, err) | |
| 324 | + | return _c | |
| 325 | + | } | |
| 326 | + | ||
| 327 | + | func (_c *CILogStore_Count_Call) RunAndReturn(run func(ctx context.Context, ref ports.CILogRef) (int64, error)) *CILogStore_Count_Call { | |
| 328 | + | _c.Call.Return(run) | |
| 329 | + | return _c | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | // Read provides a mock function for the type CILogStore | |
| 333 | + | func (_mock *CILogStore) Read(ctx context.Context, ref ports.CILogRef, from int64, maxLines int, maxBytes int) ([]domain.CILogLine, error) { | |
| 334 | + | ret := _mock.Called(ctx, ref, from, maxLines, maxBytes) | |
| 335 | + | ||
| 336 | + | if len(ret) == 0 { | |
| 337 | + | panic("no return value specified for Read") | |
| 338 | + | } | |
| 339 | + | ||
| 340 | + | var r0 []domain.CILogLine | |
| 341 | + | var r1 error | |
| 342 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, int64, int, int) ([]domain.CILogLine, error)); ok { | |
| 343 | + | return returnFunc(ctx, ref, from, maxLines, maxBytes) | |
| 344 | + | } | |
| 345 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef, int64, int, int) []domain.CILogLine); ok { | |
| 346 | + | r0 = returnFunc(ctx, ref, from, maxLines, maxBytes) | |
| 347 | + | } else { | |
| 348 | + | if ret.Get(0) != nil { | |
| 349 | + | r0 = ret.Get(0).([]domain.CILogLine) | |
| 350 | + | } | |
| 351 | + | } | |
| 352 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, ports.CILogRef, int64, int, int) error); ok { | |
| 353 | + | r1 = returnFunc(ctx, ref, from, maxLines, maxBytes) | |
| 354 | + | } else { | |
| 355 | + | r1 = ret.Error(1) | |
| 356 | + | } | |
| 357 | + | return r0, r1 | |
| 358 | + | } | |
| 359 | + | ||
| 360 | + | // CILogStore_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' | |
| 361 | + | type CILogStore_Read_Call struct { | |
| 362 | + | *mock.Call | |
| 363 | + | } | |
| 364 | + | ||
| 365 | + | // Read is a helper method to define mock.On call | |
| 366 | + | // - ctx context.Context | |
| 367 | + | // - ref ports.CILogRef | |
| 368 | + | // - from int64 | |
| 369 | + | // - maxLines int | |
| 370 | + | // - maxBytes int | |
| 371 | + | func (_e *CILogStore_Expecter) Read(ctx any, ref any, from any, maxLines any, maxBytes any) *CILogStore_Read_Call { | |
| 372 | + | return &CILogStore_Read_Call{Call: _e.mock.On("Read", ctx, ref, from, maxLines, maxBytes)} | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | func (_c *CILogStore_Read_Call) Run(run func(ctx context.Context, ref ports.CILogRef, from int64, maxLines int, maxBytes int)) *CILogStore_Read_Call { | |
| 376 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 377 | + | var arg0 context.Context | |
| 378 | + | if args[0] != nil { | |
| 379 | + | arg0 = args[0].(context.Context) | |
| 380 | + | } | |
| 381 | + | var arg1 ports.CILogRef | |
| 382 | + | if args[1] != nil { | |
| 383 | + | arg1 = args[1].(ports.CILogRef) | |
| 384 | + | } | |
| 385 | + | var arg2 int64 | |
| 386 | + | if args[2] != nil { | |
| 387 | + | arg2 = args[2].(int64) | |
| 388 | + | } | |
| 389 | + | var arg3 int | |
| 390 | + | if args[3] != nil { | |
| 391 | + | arg3 = args[3].(int) | |
| 392 | + | } | |
| 393 | + | var arg4 int | |
| 394 | + | if args[4] != nil { | |
| 395 | + | arg4 = args[4].(int) | |
| 396 | + | } | |
| 397 | + | run( | |
| 398 | + | arg0, | |
| 399 | + | arg1, | |
| 400 | + | arg2, | |
| 401 | + | arg3, | |
| 402 | + | arg4, | |
| 403 | + | ) | |
| 404 | + | }) | |
| 405 | + | return _c | |
| 406 | + | } | |
| 407 | + | ||
| 408 | + | func (_c *CILogStore_Read_Call) Return(cILogLines []domain.CILogLine, err error) *CILogStore_Read_Call { | |
| 409 | + | _c.Call.Return(cILogLines, err) | |
| 410 | + | return _c | |
| 411 | + | } | |
| 412 | + | ||
| 413 | + | func (_c *CILogStore_Read_Call) RunAndReturn(run func(ctx context.Context, ref ports.CILogRef, from int64, maxLines int, maxBytes int) ([]domain.CILogLine, error)) *CILogStore_Read_Call { | |
| 414 | + | _c.Call.Return(run) | |
| 415 | + | return _c | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | // RemoveRuns provides a mock function for the type CILogStore | |
| 419 | + | func (_mock *CILogStore) RemoveRuns(ctx context.Context, runIDs []int64) error { | |
| 420 | + | ret := _mock.Called(ctx, runIDs) | |
| 421 | + | ||
| 422 | + | if len(ret) == 0 { | |
| 423 | + | panic("no return value specified for RemoveRuns") | |
| 424 | + | } | |
| 425 | + | ||
| 426 | + | var r0 error | |
| 427 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []int64) error); ok { | |
| 428 | + | r0 = returnFunc(ctx, runIDs) | |
| 429 | + | } else { | |
| 430 | + | r0 = ret.Error(0) | |
| 431 | + | } | |
| 432 | + | return r0 | |
| 433 | + | } | |
| 434 | + | ||
| 435 | + | // CILogStore_RemoveRuns_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveRuns' | |
| 436 | + | type CILogStore_RemoveRuns_Call struct { | |
| 437 | + | *mock.Call | |
| 438 | + | } | |
| 439 | + | ||
| 440 | + | // RemoveRuns is a helper method to define mock.On call | |
| 441 | + | // - ctx context.Context | |
| 442 | + | // - runIDs []int64 | |
| 443 | + | func (_e *CILogStore_Expecter) RemoveRuns(ctx any, runIDs any) *CILogStore_RemoveRuns_Call { | |
| 444 | + | return &CILogStore_RemoveRuns_Call{Call: _e.mock.On("RemoveRuns", ctx, runIDs)} | |
| 445 | + | } | |
| 446 | + | ||
| 447 | + | func (_c *CILogStore_RemoveRuns_Call) Run(run func(ctx context.Context, runIDs []int64)) *CILogStore_RemoveRuns_Call { | |
| 448 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 449 | + | var arg0 context.Context | |
| 450 | + | if args[0] != nil { | |
| 451 | + | arg0 = args[0].(context.Context) | |
| 452 | + | } | |
| 453 | + | var arg1 []int64 | |
| 454 | + | if args[1] != nil { | |
| 455 | + | arg1 = args[1].([]int64) | |
| 456 | + | } | |
| 457 | + | run( | |
| 458 | + | arg0, | |
| 459 | + | arg1, | |
| 460 | + | ) | |
| 461 | + | }) | |
| 462 | + | return _c | |
| 463 | + | } | |
| 464 | + | ||
| 465 | + | func (_c *CILogStore_RemoveRuns_Call) Return(err error) *CILogStore_RemoveRuns_Call { | |
| 466 | + | _c.Call.Return(err) | |
| 467 | + | return _c | |
| 468 | + | } | |
| 469 | + | ||
| 470 | + | func (_c *CILogStore_RemoveRuns_Call) RunAndReturn(run func(ctx context.Context, runIDs []int64) error) *CILogStore_RemoveRuns_Call { | |
| 471 | + | _c.Call.Return(run) | |
| 472 | + | return _c | |
| 473 | + | } | |
| 474 | + | ||
| 475 | + | // Reset provides a mock function for the type CILogStore | |
| 476 | + | func (_mock *CILogStore) Reset(ctx context.Context, ref ports.CILogRef) error { | |
| 477 | + | ret := _mock.Called(ctx, ref) | |
| 478 | + | ||
| 479 | + | if len(ret) == 0 { | |
| 480 | + | panic("no return value specified for Reset") | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | var r0 error | |
| 484 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CILogRef) error); ok { | |
| 485 | + | r0 = returnFunc(ctx, ref) | |
| 486 | + | } else { | |
| 487 | + | r0 = ret.Error(0) | |
| 488 | + | } | |
| 489 | + | return r0 | |
| 490 | + | } | |
| 491 | + | ||
| 492 | + | // CILogStore_Reset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reset' | |
| 493 | + | type CILogStore_Reset_Call struct { | |
| 494 | + | *mock.Call | |
| 495 | + | } | |
| 496 | + | ||
| 497 | + | // Reset is a helper method to define mock.On call | |
| 498 | + | // - ctx context.Context | |
| 499 | + | // - ref ports.CILogRef | |
| 500 | + | func (_e *CILogStore_Expecter) Reset(ctx any, ref any) *CILogStore_Reset_Call { | |
| 501 | + | return &CILogStore_Reset_Call{Call: _e.mock.On("Reset", ctx, ref)} | |
| 502 | + | } | |
| 503 | + | ||
| 504 | + | func (_c *CILogStore_Reset_Call) Run(run func(ctx context.Context, ref ports.CILogRef)) *CILogStore_Reset_Call { | |
| 505 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 506 | + | var arg0 context.Context | |
| 507 | + | if args[0] != nil { | |
| 508 | + | arg0 = args[0].(context.Context) | |
| 509 | + | } | |
| 510 | + | var arg1 ports.CILogRef | |
| 511 | + | if args[1] != nil { | |
| 512 | + | arg1 = args[1].(ports.CILogRef) | |
| 513 | + | } | |
| 514 | + | run( | |
| 515 | + | arg0, | |
| 516 | + | arg1, | |
| 517 | + | ) | |
| 518 | + | }) | |
| 519 | + | return _c | |
| 520 | + | } | |
| 521 | + | ||
| 522 | + | func (_c *CILogStore_Reset_Call) Return(err error) *CILogStore_Reset_Call { | |
| 523 | + | _c.Call.Return(err) | |
| 524 | + | return _c | |
| 525 | + | } | |
| 526 | + | ||
| 527 | + | func (_c *CILogStore_Reset_Call) RunAndReturn(run func(ctx context.Context, ref ports.CILogRef) error) *CILogStore_Reset_Call { | |
| 528 | + | _c.Call.Return(run) | |
| 529 | + | return _c | |
| 530 | + | } | |
| 531 | + | ||
| 532 | + | // RunIDs provides a mock function for the type CILogStore | |
| 533 | + | func (_mock *CILogStore) RunIDs(ctx context.Context) ([]int64, error) { | |
| 534 | + | ret := _mock.Called(ctx) | |
| 535 | + | ||
| 536 | + | if len(ret) == 0 { | |
| 537 | + | panic("no return value specified for RunIDs") | |
| 538 | + | } | |
| 539 | + | ||
| 540 | + | var r0 []int64 | |
| 541 | + | var r1 error | |
| 542 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]int64, error)); ok { | |
| 543 | + | return returnFunc(ctx) | |
| 544 | + | } | |
| 545 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []int64); ok { | |
| 546 | + | r0 = returnFunc(ctx) | |
| 547 | + | } else { | |
| 548 | + | if ret.Get(0) != nil { | |
| 549 | + | r0 = ret.Get(0).([]int64) | |
| 550 | + | } | |
| 551 | + | } | |
| 552 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 553 | + | r1 = returnFunc(ctx) | |
| 554 | + | } else { | |
| 555 | + | r1 = ret.Error(1) | |
| 556 | + | } | |
| 557 | + | return r0, r1 | |
| 558 | + | } | |
| 559 | + | ||
| 560 | + | // CILogStore_RunIDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RunIDs' | |
| 561 | + | type CILogStore_RunIDs_Call struct { | |
| 562 | + | *mock.Call | |
| 563 | + | } | |
| 564 | + | ||
| 565 | + | // RunIDs is a helper method to define mock.On call | |
| 566 | + | // - ctx context.Context | |
| 567 | + | func (_e *CILogStore_Expecter) RunIDs(ctx any) *CILogStore_RunIDs_Call { | |
| 568 | + | return &CILogStore_RunIDs_Call{Call: _e.mock.On("RunIDs", ctx)} | |
| 569 | + | } | |
| 570 | + | ||
| 571 | + | func (_c *CILogStore_RunIDs_Call) Run(run func(ctx context.Context)) *CILogStore_RunIDs_Call { | |
| 572 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 573 | + | var arg0 context.Context | |
| 574 | + | if args[0] != nil { | |
| 575 | + | arg0 = args[0].(context.Context) | |
| 576 | + | } | |
| 577 | + | run( | |
| 578 | + | arg0, | |
| 579 | + | ) | |
| 580 | + | }) | |
| 581 | + | return _c | |
| 582 | + | } | |
| 583 | + | ||
| 584 | + | func (_c *CILogStore_RunIDs_Call) Return(int64s []int64, err error) *CILogStore_RunIDs_Call { | |
| 585 | + | _c.Call.Return(int64s, err) | |
| 586 | + | return _c | |
| 587 | + | } | |
| 588 | + | ||
| 589 | + | func (_c *CILogStore_RunIDs_Call) RunAndReturn(run func(ctx context.Context) ([]int64, error)) *CILogStore_RunIDs_Call { | |
| 590 | + | _c.Call.Return(run) | |
| 591 | + | return _c | |
| 592 | + | } | |
A internal/ports/mocks/CIStore.go +1384 -0
| @@ -0,0 +1,1384 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | "time" | |
| 10 | + | ||
| 11 | + | "bnema.dev/ermite/internal/domain" | |
| 12 | + | "bnema.dev/ermite/internal/ports" | |
| 13 | + | mock "github.com/stretchr/testify/mock" | |
| 14 | + | ) | |
| 15 | + | ||
| 16 | + | // NewCIStore creates a new instance of CIStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 17 | + | // The first argument is typically a *testing.T value. | |
| 18 | + | func NewCIStore(t interface { | |
| 19 | + | mock.TestingT | |
| 20 | + | Cleanup(func()) | |
| 21 | + | }) *CIStore { | |
| 22 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 23 | + | helper.Helper() | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | mock := &CIStore{} | |
| 27 | + | mock.Mock.Test(t) | |
| 28 | + | ||
| 29 | + | t.Cleanup(func() { | |
| 30 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 31 | + | helper.Helper() | |
| 32 | + | } | |
| 33 | + | mock.AssertExpectations(t) | |
| 34 | + | }) | |
| 35 | + | ||
| 36 | + | return mock | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // CIStore is an autogenerated mock type for the CIStore type | |
| 40 | + | type CIStore struct { | |
| 41 | + | mock.Mock | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | type CIStore_Expecter struct { | |
| 45 | + | mock *mock.Mock | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | func (_m *CIStore) EXPECT() *CIStore_Expecter { | |
| 49 | + | return &CIStore_Expecter{mock: &_m.Mock} | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // Apply provides a mock function for the type CIStore | |
| 53 | + | func (_mock *CIStore) Apply(ctx context.Context, w ports.CIWrite) (uint64, error) { | |
| 54 | + | ret := _mock.Called(ctx, w) | |
| 55 | + | ||
| 56 | + | if len(ret) == 0 { | |
| 57 | + | panic("no return value specified for Apply") | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | var r0 uint64 | |
| 61 | + | var r1 error | |
| 62 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CIWrite) (uint64, error)); ok { | |
| 63 | + | return returnFunc(ctx, w) | |
| 64 | + | } | |
| 65 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, ports.CIWrite) uint64); ok { | |
| 66 | + | r0 = returnFunc(ctx, w) | |
| 67 | + | } else { | |
| 68 | + | r0 = ret.Get(0).(uint64) | |
| 69 | + | } | |
| 70 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, ports.CIWrite) error); ok { | |
| 71 | + | r1 = returnFunc(ctx, w) | |
| 72 | + | } else { | |
| 73 | + | r1 = ret.Error(1) | |
| 74 | + | } | |
| 75 | + | return r0, r1 | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // CIStore_Apply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Apply' | |
| 79 | + | type CIStore_Apply_Call struct { | |
| 80 | + | *mock.Call | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // Apply is a helper method to define mock.On call | |
| 84 | + | // - ctx context.Context | |
| 85 | + | // - w ports.CIWrite | |
| 86 | + | func (_e *CIStore_Expecter) Apply(ctx any, w any) *CIStore_Apply_Call { | |
| 87 | + | return &CIStore_Apply_Call{Call: _e.mock.On("Apply", ctx, w)} | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | func (_c *CIStore_Apply_Call) Run(run func(ctx context.Context, w ports.CIWrite)) *CIStore_Apply_Call { | |
| 91 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 92 | + | var arg0 context.Context | |
| 93 | + | if args[0] != nil { | |
| 94 | + | arg0 = args[0].(context.Context) | |
| 95 | + | } | |
| 96 | + | var arg1 ports.CIWrite | |
| 97 | + | if args[1] != nil { | |
| 98 | + | arg1 = args[1].(ports.CIWrite) | |
| 99 | + | } | |
| 100 | + | run( | |
| 101 | + | arg0, | |
| 102 | + | arg1, | |
| 103 | + | ) | |
| 104 | + | }) | |
| 105 | + | return _c | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | func (_c *CIStore_Apply_Call) Return(version uint64, err error) *CIStore_Apply_Call { | |
| 109 | + | _c.Call.Return(version, err) | |
| 110 | + | return _c | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | func (_c *CIStore_Apply_Call) RunAndReturn(run func(ctx context.Context, w ports.CIWrite) (uint64, error)) *CIStore_Apply_Call { | |
| 114 | + | _c.Call.Return(run) | |
| 115 | + | return _c | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // CreateRun provides a mock function for the type CIStore | |
| 119 | + | func (_mock *CIStore) CreateRun(ctx context.Context, repo domain.RepoName, workflowPath string, build func(id int64, number int64) ports.CIRunDraft) (ports.CIRunDraft, error) { | |
| 120 | + | ret := _mock.Called(ctx, repo, workflowPath, build) | |
| 121 | + | ||
| 122 | + | if len(ret) == 0 { | |
| 123 | + | panic("no return value specified for CreateRun") | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | var r0 ports.CIRunDraft | |
| 127 | + | var r1 error | |
| 128 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, func(id int64, number int64) ports.CIRunDraft) (ports.CIRunDraft, error)); ok { | |
| 129 | + | return returnFunc(ctx, repo, workflowPath, build) | |
| 130 | + | } | |
| 131 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, func(id int64, number int64) ports.CIRunDraft) ports.CIRunDraft); ok { | |
| 132 | + | r0 = returnFunc(ctx, repo, workflowPath, build) | |
| 133 | + | } else { | |
| 134 | + | r0 = ret.Get(0).(ports.CIRunDraft) | |
| 135 | + | } | |
| 136 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, string, func(id int64, number int64) ports.CIRunDraft) error); ok { | |
| 137 | + | r1 = returnFunc(ctx, repo, workflowPath, build) | |
| 138 | + | } else { | |
| 139 | + | r1 = ret.Error(1) | |
| 140 | + | } | |
| 141 | + | return r0, r1 | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | // CIStore_CreateRun_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateRun' | |
| 145 | + | type CIStore_CreateRun_Call struct { | |
| 146 | + | *mock.Call | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // CreateRun is a helper method to define mock.On call | |
| 150 | + | // - ctx context.Context | |
| 151 | + | // - repo domain.RepoName | |
| 152 | + | // - workflowPath string | |
| 153 | + | // - build func(id int64, number int64) ports.CIRunDraft | |
| 154 | + | func (_e *CIStore_Expecter) CreateRun(ctx any, repo any, workflowPath any, build any) *CIStore_CreateRun_Call { | |
| 155 | + | return &CIStore_CreateRun_Call{Call: _e.mock.On("CreateRun", ctx, repo, workflowPath, build)} | |
| 156 | + | } | |
| 157 | + | ||
| 158 | + | func (_c *CIStore_CreateRun_Call) Run(run func(ctx context.Context, repo domain.RepoName, workflowPath string, build func(id int64, number int64) ports.CIRunDraft)) *CIStore_CreateRun_Call { | |
| 159 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 160 | + | var arg0 context.Context | |
| 161 | + | if args[0] != nil { | |
| 162 | + | arg0 = args[0].(context.Context) | |
| 163 | + | } | |
| 164 | + | var arg1 domain.RepoName | |
| 165 | + | if args[1] != nil { | |
| 166 | + | arg1 = args[1].(domain.RepoName) | |
| 167 | + | } | |
| 168 | + | var arg2 string | |
| 169 | + | if args[2] != nil { | |
| 170 | + | arg2 = args[2].(string) | |
| 171 | + | } | |
| 172 | + | var arg3 func(id int64, number int64) ports.CIRunDraft | |
| 173 | + | if args[3] != nil { | |
| 174 | + | arg3 = args[3].(func(id int64, number int64) ports.CIRunDraft) | |
| 175 | + | } | |
| 176 | + | run( | |
| 177 | + | arg0, | |
| 178 | + | arg1, | |
| 179 | + | arg2, | |
| 180 | + | arg3, | |
| 181 | + | ) | |
| 182 | + | }) | |
| 183 | + | return _c | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | func (_c *CIStore_CreateRun_Call) Return(cIRunDraft ports.CIRunDraft, err error) *CIStore_CreateRun_Call { | |
| 187 | + | _c.Call.Return(cIRunDraft, err) | |
| 188 | + | return _c | |
| 189 | + | } | |
| 190 | + | ||
| 191 | + | func (_c *CIStore_CreateRun_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, workflowPath string, build func(id int64, number int64) ports.CIRunDraft) (ports.CIRunDraft, error)) *CIStore_CreateRun_Call { | |
| 192 | + | _c.Call.Return(run) | |
| 193 | + | return _c | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | // CreateRunner provides a mock function for the type CIStore | |
| 197 | + | func (_mock *CIStore) CreateRunner(ctx context.Context, r domain.CIRunner, replace bool) ([]domain.CIRunner, error) { | |
| 198 | + | ret := _mock.Called(ctx, r, replace) | |
| 199 | + | ||
| 200 | + | if len(ret) == 0 { | |
| 201 | + | panic("no return value specified for CreateRunner") | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | var r0 []domain.CIRunner | |
| 205 | + | var r1 error | |
| 206 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.CIRunner, bool) ([]domain.CIRunner, error)); ok { | |
| 207 | + | return returnFunc(ctx, r, replace) | |
| 208 | + | } | |
| 209 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.CIRunner, bool) []domain.CIRunner); ok { | |
| 210 | + | r0 = returnFunc(ctx, r, replace) | |
| 211 | + | } else { | |
| 212 | + | if ret.Get(0) != nil { | |
| 213 | + | r0 = ret.Get(0).([]domain.CIRunner) | |
| 214 | + | } | |
| 215 | + | } | |
| 216 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.CIRunner, bool) error); ok { | |
| 217 | + | r1 = returnFunc(ctx, r, replace) | |
| 218 | + | } else { | |
| 219 | + | r1 = ret.Error(1) | |
| 220 | + | } | |
| 221 | + | return r0, r1 | |
| 222 | + | } | |
| 223 | + | ||
| 224 | + | // CIStore_CreateRunner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateRunner' | |
| 225 | + | type CIStore_CreateRunner_Call struct { | |
| 226 | + | *mock.Call | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | // CreateRunner is a helper method to define mock.On call | |
| 230 | + | // - ctx context.Context | |
| 231 | + | // - r domain.CIRunner | |
| 232 | + | // - replace bool | |
| 233 | + | func (_e *CIStore_Expecter) CreateRunner(ctx any, r any, replace any) *CIStore_CreateRunner_Call { | |
| 234 | + | return &CIStore_CreateRunner_Call{Call: _e.mock.On("CreateRunner", ctx, r, replace)} | |
| 235 | + | } | |
| 236 | + | ||
| 237 | + | func (_c *CIStore_CreateRunner_Call) Run(run func(ctx context.Context, r domain.CIRunner, replace bool)) *CIStore_CreateRunner_Call { | |
| 238 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 239 | + | var arg0 context.Context | |
| 240 | + | if args[0] != nil { | |
| 241 | + | arg0 = args[0].(context.Context) | |
| 242 | + | } | |
| 243 | + | var arg1 domain.CIRunner | |
| 244 | + | if args[1] != nil { | |
| 245 | + | arg1 = args[1].(domain.CIRunner) | |
| 246 | + | } | |
| 247 | + | var arg2 bool | |
| 248 | + | if args[2] != nil { | |
| 249 | + | arg2 = args[2].(bool) | |
| 250 | + | } | |
| 251 | + | run( | |
| 252 | + | arg0, | |
| 253 | + | arg1, | |
| 254 | + | arg2, | |
| 255 | + | ) | |
| 256 | + | }) | |
| 257 | + | return _c | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | func (_c *CIStore_CreateRunner_Call) Return(removed []domain.CIRunner, err error) *CIStore_CreateRunner_Call { | |
| 261 | + | _c.Call.Return(removed, err) | |
| 262 | + | return _c | |
| 263 | + | } | |
| 264 | + | ||
| 265 | + | func (_c *CIStore_CreateRunner_Call) RunAndReturn(run func(ctx context.Context, r domain.CIRunner, replace bool) ([]domain.CIRunner, error)) *CIStore_CreateRunner_Call { | |
| 266 | + | _c.Call.Return(run) | |
| 267 | + | return _c | |
| 268 | + | } | |
| 269 | + | ||
| 270 | + | // FindJobByToken provides a mock function for the type CIStore | |
| 271 | + | func (_mock *CIStore) FindJobByToken(ctx context.Context, hash domain.TokenHash, now time.Time) (domain.CIJob, error) { | |
| 272 | + | ret := _mock.Called(ctx, hash, now) | |
| 273 | + | ||
| 274 | + | if len(ret) == 0 { | |
| 275 | + | panic("no return value specified for FindJobByToken") | |
| 276 | + | } | |
| 277 | + | ||
| 278 | + | var r0 domain.CIJob | |
| 279 | + | var r1 error | |
| 280 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.TokenHash, time.Time) (domain.CIJob, error)); ok { | |
| 281 | + | return returnFunc(ctx, hash, now) | |
| 282 | + | } | |
| 283 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.TokenHash, time.Time) domain.CIJob); ok { | |
| 284 | + | r0 = returnFunc(ctx, hash, now) | |
| 285 | + | } else { | |
| 286 | + | r0 = ret.Get(0).(domain.CIJob) | |
| 287 | + | } | |
| 288 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.TokenHash, time.Time) error); ok { | |
| 289 | + | r1 = returnFunc(ctx, hash, now) | |
| 290 | + | } else { | |
| 291 | + | r1 = ret.Error(1) | |
| 292 | + | } | |
| 293 | + | return r0, r1 | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | // CIStore_FindJobByToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindJobByToken' | |
| 297 | + | type CIStore_FindJobByToken_Call struct { | |
| 298 | + | *mock.Call | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | // FindJobByToken is a helper method to define mock.On call | |
| 302 | + | // - ctx context.Context | |
| 303 | + | // - hash domain.TokenHash | |
| 304 | + | // - now time.Time | |
| 305 | + | func (_e *CIStore_Expecter) FindJobByToken(ctx any, hash any, now any) *CIStore_FindJobByToken_Call { | |
| 306 | + | return &CIStore_FindJobByToken_Call{Call: _e.mock.On("FindJobByToken", ctx, hash, now)} | |
| 307 | + | } | |
| 308 | + | ||
| 309 | + | func (_c *CIStore_FindJobByToken_Call) Run(run func(ctx context.Context, hash domain.TokenHash, now time.Time)) *CIStore_FindJobByToken_Call { | |
| 310 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 311 | + | var arg0 context.Context | |
| 312 | + | if args[0] != nil { | |
| 313 | + | arg0 = args[0].(context.Context) | |
| 314 | + | } | |
| 315 | + | var arg1 domain.TokenHash | |
| 316 | + | if args[1] != nil { | |
| 317 | + | arg1 = args[1].(domain.TokenHash) | |
| 318 | + | } | |
| 319 | + | var arg2 time.Time | |
| 320 | + | if args[2] != nil { | |
| 321 | + | arg2 = args[2].(time.Time) | |
| 322 | + | } | |
| 323 | + | run( | |
| 324 | + | arg0, | |
| 325 | + | arg1, | |
| 326 | + | arg2, | |
| 327 | + | ) | |
| 328 | + | }) | |
| 329 | + | return _c | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | func (_c *CIStore_FindJobByToken_Call) Return(cIJob domain.CIJob, err error) *CIStore_FindJobByToken_Call { | |
| 333 | + | _c.Call.Return(cIJob, err) | |
| 334 | + | return _c | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | func (_c *CIStore_FindJobByToken_Call) RunAndReturn(run func(ctx context.Context, hash domain.TokenHash, now time.Time) (domain.CIJob, error)) *CIStore_FindJobByToken_Call { | |
| 338 | + | _c.Call.Return(run) | |
| 339 | + | return _c | |
| 340 | + | } | |
| 341 | + | ||
| 342 | + | // FindRunnerByToken provides a mock function for the type CIStore | |
| 343 | + | func (_mock *CIStore) FindRunnerByToken(ctx context.Context, hash domain.TokenHash) (domain.CIRunner, error) { | |
| 344 | + | ret := _mock.Called(ctx, hash) | |
| 345 | + | ||
| 346 | + | if len(ret) == 0 { | |
| 347 | + | panic("no return value specified for FindRunnerByToken") | |
| 348 | + | } | |
| 349 | + | ||
| 350 | + | var r0 domain.CIRunner | |
| 351 | + | var r1 error | |
| 352 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.TokenHash) (domain.CIRunner, error)); ok { | |
| 353 | + | return returnFunc(ctx, hash) | |
| 354 | + | } | |
| 355 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.TokenHash) domain.CIRunner); ok { | |
| 356 | + | r0 = returnFunc(ctx, hash) | |
| 357 | + | } else { | |
| 358 | + | r0 = ret.Get(0).(domain.CIRunner) | |
| 359 | + | } | |
| 360 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.TokenHash) error); ok { | |
| 361 | + | r1 = returnFunc(ctx, hash) | |
| 362 | + | } else { | |
| 363 | + | r1 = ret.Error(1) | |
| 364 | + | } | |
| 365 | + | return r0, r1 | |
| 366 | + | } | |
| 367 | + | ||
| 368 | + | // CIStore_FindRunnerByToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindRunnerByToken' | |
| 369 | + | type CIStore_FindRunnerByToken_Call struct { | |
| 370 | + | *mock.Call | |
| 371 | + | } | |
| 372 | + | ||
| 373 | + | // FindRunnerByToken is a helper method to define mock.On call | |
| 374 | + | // - ctx context.Context | |
| 375 | + | // - hash domain.TokenHash | |
| 376 | + | func (_e *CIStore_Expecter) FindRunnerByToken(ctx any, hash any) *CIStore_FindRunnerByToken_Call { | |
| 377 | + | return &CIStore_FindRunnerByToken_Call{Call: _e.mock.On("FindRunnerByToken", ctx, hash)} | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | func (_c *CIStore_FindRunnerByToken_Call) Run(run func(ctx context.Context, hash domain.TokenHash)) *CIStore_FindRunnerByToken_Call { | |
| 381 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 382 | + | var arg0 context.Context | |
| 383 | + | if args[0] != nil { | |
| 384 | + | arg0 = args[0].(context.Context) | |
| 385 | + | } | |
| 386 | + | var arg1 domain.TokenHash | |
| 387 | + | if args[1] != nil { | |
| 388 | + | arg1 = args[1].(domain.TokenHash) | |
| 389 | + | } | |
| 390 | + | run( | |
| 391 | + | arg0, | |
| 392 | + | arg1, | |
| 393 | + | ) | |
| 394 | + | }) | |
| 395 | + | return _c | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | func (_c *CIStore_FindRunnerByToken_Call) Return(cIRunner domain.CIRunner, err error) *CIStore_FindRunnerByToken_Call { | |
| 399 | + | _c.Call.Return(cIRunner, err) | |
| 400 | + | return _c | |
| 401 | + | } | |
| 402 | + | ||
| 403 | + | func (_c *CIStore_FindRunnerByToken_Call) RunAndReturn(run func(ctx context.Context, hash domain.TokenHash) (domain.CIRunner, error)) *CIStore_FindRunnerByToken_Call { | |
| 404 | + | _c.Call.Return(run) | |
| 405 | + | return _c | |
| 406 | + | } | |
| 407 | + | ||
| 408 | + | // GetJob provides a mock function for the type CIStore | |
| 409 | + | func (_mock *CIStore) GetJob(ctx context.Context, runID int64, runAttempt int32, jobID string) (domain.CIJob, error) { | |
| 410 | + | ret := _mock.Called(ctx, runID, runAttempt, jobID) | |
| 411 | + | ||
| 412 | + | if len(ret) == 0 { | |
| 413 | + | panic("no return value specified for GetJob") | |
| 414 | + | } | |
| 415 | + | ||
| 416 | + | var r0 domain.CIJob | |
| 417 | + | var r1 error | |
| 418 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64, int32, string) (domain.CIJob, error)); ok { | |
| 419 | + | return returnFunc(ctx, runID, runAttempt, jobID) | |
| 420 | + | } | |
| 421 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64, int32, string) domain.CIJob); ok { | |
| 422 | + | r0 = returnFunc(ctx, runID, runAttempt, jobID) | |
| 423 | + | } else { | |
| 424 | + | r0 = ret.Get(0).(domain.CIJob) | |
| 425 | + | } | |
| 426 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, int64, int32, string) error); ok { | |
| 427 | + | r1 = returnFunc(ctx, runID, runAttempt, jobID) | |
| 428 | + | } else { | |
| 429 | + | r1 = ret.Error(1) | |
| 430 | + | } | |
| 431 | + | return r0, r1 | |
| 432 | + | } | |
| 433 | + | ||
| 434 | + | // CIStore_GetJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJob' | |
| 435 | + | type CIStore_GetJob_Call struct { | |
| 436 | + | *mock.Call | |
| 437 | + | } | |
| 438 | + | ||
| 439 | + | // GetJob is a helper method to define mock.On call | |
| 440 | + | // - ctx context.Context | |
| 441 | + | // - runID int64 | |
| 442 | + | // - runAttempt int32 | |
| 443 | + | // - jobID string | |
| 444 | + | func (_e *CIStore_Expecter) GetJob(ctx any, runID any, runAttempt any, jobID any) *CIStore_GetJob_Call { | |
| 445 | + | return &CIStore_GetJob_Call{Call: _e.mock.On("GetJob", ctx, runID, runAttempt, jobID)} | |
| 446 | + | } | |
| 447 | + | ||
| 448 | + | func (_c *CIStore_GetJob_Call) Run(run func(ctx context.Context, runID int64, runAttempt int32, jobID string)) *CIStore_GetJob_Call { | |
| 449 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 450 | + | var arg0 context.Context | |
| 451 | + | if args[0] != nil { | |
| 452 | + | arg0 = args[0].(context.Context) | |
| 453 | + | } | |
| 454 | + | var arg1 int64 | |
| 455 | + | if args[1] != nil { | |
| 456 | + | arg1 = args[1].(int64) | |
| 457 | + | } | |
| 458 | + | var arg2 int32 | |
| 459 | + | if args[2] != nil { | |
| 460 | + | arg2 = args[2].(int32) | |
| 461 | + | } | |
| 462 | + | var arg3 string | |
| 463 | + | if args[3] != nil { | |
| 464 | + | arg3 = args[3].(string) | |
| 465 | + | } | |
| 466 | + | run( | |
| 467 | + | arg0, | |
| 468 | + | arg1, | |
| 469 | + | arg2, | |
| 470 | + | arg3, | |
| 471 | + | ) | |
| 472 | + | }) | |
| 473 | + | return _c | |
| 474 | + | } | |
| 475 | + | ||
| 476 | + | func (_c *CIStore_GetJob_Call) Return(cIJob domain.CIJob, err error) *CIStore_GetJob_Call { | |
| 477 | + | _c.Call.Return(cIJob, err) | |
| 478 | + | return _c | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | func (_c *CIStore_GetJob_Call) RunAndReturn(run func(ctx context.Context, runID int64, runAttempt int32, jobID string) (domain.CIJob, error)) *CIStore_GetJob_Call { | |
| 482 | + | _c.Call.Return(run) | |
| 483 | + | return _c | |
| 484 | + | } | |
| 485 | + | ||
| 486 | + | // GetRun provides a mock function for the type CIStore | |
| 487 | + | func (_mock *CIStore) GetRun(ctx context.Context, repo domain.RepoName, id int64) (domain.CIRun, error) { | |
| 488 | + | ret := _mock.Called(ctx, repo, id) | |
| 489 | + | ||
| 490 | + | if len(ret) == 0 { | |
| 491 | + | panic("no return value specified for GetRun") | |
| 492 | + | } | |
| 493 | + | ||
| 494 | + | var r0 domain.CIRun | |
| 495 | + | var r1 error | |
| 496 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) (domain.CIRun, error)); ok { | |
| 497 | + | return returnFunc(ctx, repo, id) | |
| 498 | + | } | |
| 499 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) domain.CIRun); ok { | |
| 500 | + | r0 = returnFunc(ctx, repo, id) | |
| 501 | + | } else { | |
| 502 | + | r0 = ret.Get(0).(domain.CIRun) | |
| 503 | + | } | |
| 504 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |
| 505 | + | r1 = returnFunc(ctx, repo, id) | |
| 506 | + | } else { | |
| 507 | + | r1 = ret.Error(1) | |
| 508 | + | } | |
| 509 | + | return r0, r1 | |
| 510 | + | } | |
| 511 | + | ||
| 512 | + | // CIStore_GetRun_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRun' | |
| 513 | + | type CIStore_GetRun_Call struct { | |
| 514 | + | *mock.Call | |
| 515 | + | } | |
| 516 | + | ||
| 517 | + | // GetRun is a helper method to define mock.On call | |
| 518 | + | // - ctx context.Context | |
| 519 | + | // - repo domain.RepoName | |
| 520 | + | // - id int64 | |
| 521 | + | func (_e *CIStore_Expecter) GetRun(ctx any, repo any, id any) *CIStore_GetRun_Call { | |
| 522 | + | return &CIStore_GetRun_Call{Call: _e.mock.On("GetRun", ctx, repo, id)} | |
| 523 | + | } | |
| 524 | + | ||
| 525 | + | func (_c *CIStore_GetRun_Call) Run(run func(ctx context.Context, repo domain.RepoName, id int64)) *CIStore_GetRun_Call { | |
| 526 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 527 | + | var arg0 context.Context | |
| 528 | + | if args[0] != nil { | |
| 529 | + | arg0 = args[0].(context.Context) | |
| 530 | + | } | |
| 531 | + | var arg1 domain.RepoName | |
| 532 | + | if args[1] != nil { | |
| 533 | + | arg1 = args[1].(domain.RepoName) | |
| 534 | + | } | |
| 535 | + | var arg2 int64 | |
| 536 | + | if args[2] != nil { | |
| 537 | + | arg2 = args[2].(int64) | |
| 538 | + | } | |
| 539 | + | run( | |
| 540 | + | arg0, | |
| 541 | + | arg1, | |
| 542 | + | arg2, | |
| 543 | + | ) | |
| 544 | + | }) | |
| 545 | + | return _c | |
| 546 | + | } | |
| 547 | + | ||
| 548 | + | func (_c *CIStore_GetRun_Call) Return(cIRun domain.CIRun, err error) *CIStore_GetRun_Call { | |
| 549 | + | _c.Call.Return(cIRun, err) | |
| 550 | + | return _c | |
| 551 | + | } | |
| 552 | + | ||
| 553 | + | func (_c *CIStore_GetRun_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, id int64) (domain.CIRun, error)) *CIStore_GetRun_Call { | |
| 554 | + | _c.Call.Return(run) | |
| 555 | + | return _c | |
| 556 | + | } | |
| 557 | + | ||
| 558 | + | // GetRunSource provides a mock function for the type CIStore | |
| 559 | + | func (_mock *CIStore) GetRunSource(ctx context.Context, runID int64) (domain.CIRunSource, error) { | |
| 560 | + | ret := _mock.Called(ctx, runID) | |
| 561 | + | ||
| 562 | + | if len(ret) == 0 { | |
| 563 | + | panic("no return value specified for GetRunSource") | |
| 564 | + | } | |
| 565 | + | ||
| 566 | + | var r0 domain.CIRunSource | |
| 567 | + | var r1 error | |
| 568 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64) (domain.CIRunSource, error)); ok { | |
| 569 | + | return returnFunc(ctx, runID) | |
| 570 | + | } | |
| 571 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64) domain.CIRunSource); ok { | |
| 572 | + | r0 = returnFunc(ctx, runID) | |
| 573 | + | } else { | |
| 574 | + | r0 = ret.Get(0).(domain.CIRunSource) | |
| 575 | + | } | |
| 576 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, int64) error); ok { | |
| 577 | + | r1 = returnFunc(ctx, runID) | |
| 578 | + | } else { | |
| 579 | + | r1 = ret.Error(1) | |
| 580 | + | } | |
| 581 | + | return r0, r1 | |
| 582 | + | } | |
| 583 | + | ||
| 584 | + | // CIStore_GetRunSource_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRunSource' | |
| 585 | + | type CIStore_GetRunSource_Call struct { | |
| 586 | + | *mock.Call | |
| 587 | + | } | |
| 588 | + | ||
| 589 | + | // GetRunSource is a helper method to define mock.On call | |
| 590 | + | // - ctx context.Context | |
| 591 | + | // - runID int64 | |
| 592 | + | func (_e *CIStore_Expecter) GetRunSource(ctx any, runID any) *CIStore_GetRunSource_Call { | |
| 593 | + | return &CIStore_GetRunSource_Call{Call: _e.mock.On("GetRunSource", ctx, runID)} | |
| 594 | + | } | |
| 595 | + | ||
| 596 | + | func (_c *CIStore_GetRunSource_Call) Run(run func(ctx context.Context, runID int64)) *CIStore_GetRunSource_Call { | |
| 597 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 598 | + | var arg0 context.Context | |
| 599 | + | if args[0] != nil { | |
| 600 | + | arg0 = args[0].(context.Context) | |
| 601 | + | } | |
| 602 | + | var arg1 int64 | |
| 603 | + | if args[1] != nil { | |
| 604 | + | arg1 = args[1].(int64) | |
| 605 | + | } | |
| 606 | + | run( | |
| 607 | + | arg0, | |
| 608 | + | arg1, | |
| 609 | + | ) | |
| 610 | + | }) | |
| 611 | + | return _c | |
| 612 | + | } | |
| 613 | + | ||
| 614 | + | func (_c *CIStore_GetRunSource_Call) Return(cIRunSource domain.CIRunSource, err error) *CIStore_GetRunSource_Call { | |
| 615 | + | _c.Call.Return(cIRunSource, err) | |
| 616 | + | return _c | |
| 617 | + | } | |
| 618 | + | ||
| 619 | + | func (_c *CIStore_GetRunSource_Call) RunAndReturn(run func(ctx context.Context, runID int64) (domain.CIRunSource, error)) *CIStore_GetRunSource_Call { | |
| 620 | + | _c.Call.Return(run) | |
| 621 | + | return _c | |
| 622 | + | } | |
| 623 | + | ||
| 624 | + | // GetRunner provides a mock function for the type CIStore | |
| 625 | + | func (_mock *CIStore) GetRunner(ctx context.Context, id string) (domain.CIRunner, error) { | |
| 626 | + | ret := _mock.Called(ctx, id) | |
| 627 | + | ||
| 628 | + | if len(ret) == 0 { | |
| 629 | + | panic("no return value specified for GetRunner") | |
| 630 | + | } | |
| 631 | + | ||
| 632 | + | var r0 domain.CIRunner | |
| 633 | + | var r1 error | |
| 634 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string) (domain.CIRunner, error)); ok { | |
| 635 | + | return returnFunc(ctx, id) | |
| 636 | + | } | |
| 637 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string) domain.CIRunner); ok { | |
| 638 | + | r0 = returnFunc(ctx, id) | |
| 639 | + | } else { | |
| 640 | + | r0 = ret.Get(0).(domain.CIRunner) | |
| 641 | + | } | |
| 642 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { | |
| 643 | + | r1 = returnFunc(ctx, id) | |
| 644 | + | } else { | |
| 645 | + | r1 = ret.Error(1) | |
| 646 | + | } | |
| 647 | + | return r0, r1 | |
| 648 | + | } | |
| 649 | + | ||
| 650 | + | // CIStore_GetRunner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRunner' | |
| 651 | + | type CIStore_GetRunner_Call struct { | |
| 652 | + | *mock.Call | |
| 653 | + | } | |
| 654 | + | ||
| 655 | + | // GetRunner is a helper method to define mock.On call | |
| 656 | + | // - ctx context.Context | |
| 657 | + | // - id string | |
| 658 | + | func (_e *CIStore_Expecter) GetRunner(ctx any, id any) *CIStore_GetRunner_Call { | |
| 659 | + | return &CIStore_GetRunner_Call{Call: _e.mock.On("GetRunner", ctx, id)} | |
| 660 | + | } | |
| 661 | + | ||
| 662 | + | func (_c *CIStore_GetRunner_Call) Run(run func(ctx context.Context, id string)) *CIStore_GetRunner_Call { | |
| 663 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 664 | + | var arg0 context.Context | |
| 665 | + | if args[0] != nil { | |
| 666 | + | arg0 = args[0].(context.Context) | |
| 667 | + | } | |
| 668 | + | var arg1 string | |
| 669 | + | if args[1] != nil { | |
| 670 | + | arg1 = args[1].(string) | |
| 671 | + | } | |
| 672 | + | run( | |
| 673 | + | arg0, | |
| 674 | + | arg1, | |
| 675 | + | ) | |
| 676 | + | }) | |
| 677 | + | return _c | |
| 678 | + | } | |
| 679 | + | ||
| 680 | + | func (_c *CIStore_GetRunner_Call) Return(cIRunner domain.CIRunner, err error) *CIStore_GetRunner_Call { | |
| 681 | + | _c.Call.Return(cIRunner, err) | |
| 682 | + | return _c | |
| 683 | + | } | |
| 684 | + | ||
| 685 | + | func (_c *CIStore_GetRunner_Call) RunAndReturn(run func(ctx context.Context, id string) (domain.CIRunner, error)) *CIStore_GetRunner_Call { | |
| 686 | + | _c.Call.Return(run) | |
| 687 | + | return _c | |
| 688 | + | } | |
| 689 | + | ||
| 690 | + | // ListActiveJobs provides a mock function for the type CIStore | |
| 691 | + | func (_mock *CIStore) ListActiveJobs(ctx context.Context) ([]domain.CIJob, error) { | |
| 692 | + | ret := _mock.Called(ctx) | |
| 693 | + | ||
| 694 | + | if len(ret) == 0 { | |
| 695 | + | panic("no return value specified for ListActiveJobs") | |
| 696 | + | } | |
| 697 | + | ||
| 698 | + | var r0 []domain.CIJob | |
| 699 | + | var r1 error | |
| 700 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.CIJob, error)); ok { | |
| 701 | + | return returnFunc(ctx) | |
| 702 | + | } | |
| 703 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.CIJob); ok { | |
| 704 | + | r0 = returnFunc(ctx) | |
| 705 | + | } else { | |
| 706 | + | if ret.Get(0) != nil { | |
| 707 | + | r0 = ret.Get(0).([]domain.CIJob) | |
| 708 | + | } | |
| 709 | + | } | |
| 710 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 711 | + | r1 = returnFunc(ctx) | |
| 712 | + | } else { | |
| 713 | + | r1 = ret.Error(1) | |
| 714 | + | } | |
| 715 | + | return r0, r1 | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | // CIStore_ListActiveJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListActiveJobs' | |
| 719 | + | type CIStore_ListActiveJobs_Call struct { | |
| 720 | + | *mock.Call | |
| 721 | + | } | |
| 722 | + | ||
| 723 | + | // ListActiveJobs is a helper method to define mock.On call | |
| 724 | + | // - ctx context.Context | |
| 725 | + | func (_e *CIStore_Expecter) ListActiveJobs(ctx any) *CIStore_ListActiveJobs_Call { | |
| 726 | + | return &CIStore_ListActiveJobs_Call{Call: _e.mock.On("ListActiveJobs", ctx)} | |
| 727 | + | } | |
| 728 | + | ||
| 729 | + | func (_c *CIStore_ListActiveJobs_Call) Run(run func(ctx context.Context)) *CIStore_ListActiveJobs_Call { | |
| 730 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 731 | + | var arg0 context.Context | |
| 732 | + | if args[0] != nil { | |
| 733 | + | arg0 = args[0].(context.Context) | |
| 734 | + | } | |
| 735 | + | run( | |
| 736 | + | arg0, | |
| 737 | + | ) | |
| 738 | + | }) | |
| 739 | + | return _c | |
| 740 | + | } | |
| 741 | + | ||
| 742 | + | func (_c *CIStore_ListActiveJobs_Call) Return(cIJobs []domain.CIJob, err error) *CIStore_ListActiveJobs_Call { | |
| 743 | + | _c.Call.Return(cIJobs, err) | |
| 744 | + | return _c | |
| 745 | + | } | |
| 746 | + | ||
| 747 | + | func (_c *CIStore_ListActiveJobs_Call) RunAndReturn(run func(ctx context.Context) ([]domain.CIJob, error)) *CIStore_ListActiveJobs_Call { | |
| 748 | + | _c.Call.Return(run) | |
| 749 | + | return _c | |
| 750 | + | } | |
| 751 | + | ||
| 752 | + | // ListChecks provides a mock function for the type CIStore | |
| 753 | + | func (_mock *CIStore) ListChecks(ctx context.Context, repo domain.RepoName, sha string, limit int) ([]domain.CICheck, bool, error) { | |
| 754 | + | ret := _mock.Called(ctx, repo, sha, limit) | |
| 755 | + | ||
| 756 | + | if len(ret) == 0 { | |
| 757 | + | panic("no return value specified for ListChecks") | |
| 758 | + | } | |
| 759 | + | ||
| 760 | + | var r0 []domain.CICheck | |
| 761 | + | var r1 bool | |
| 762 | + | var r2 error | |
| 763 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, int) ([]domain.CICheck, bool, error)); ok { | |
| 764 | + | return returnFunc(ctx, repo, sha, limit) | |
| 765 | + | } | |
| 766 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, int) []domain.CICheck); ok { | |
| 767 | + | r0 = returnFunc(ctx, repo, sha, limit) | |
| 768 | + | } else { | |
| 769 | + | if ret.Get(0) != nil { | |
| 770 | + | r0 = ret.Get(0).([]domain.CICheck) | |
| 771 | + | } | |
| 772 | + | } | |
| 773 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, string, int) bool); ok { | |
| 774 | + | r1 = returnFunc(ctx, repo, sha, limit) | |
| 775 | + | } else { | |
| 776 | + | r1 = ret.Get(1).(bool) | |
| 777 | + | } | |
| 778 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, string, int) error); ok { | |
| 779 | + | r2 = returnFunc(ctx, repo, sha, limit) | |
| 780 | + | } else { | |
| 781 | + | r2 = ret.Error(2) | |
| 782 | + | } | |
| 783 | + | return r0, r1, r2 | |
| 784 | + | } | |
| 785 | + | ||
| 786 | + | // CIStore_ListChecks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListChecks' | |
| 787 | + | type CIStore_ListChecks_Call struct { | |
| 788 | + | *mock.Call | |
| 789 | + | } | |
| 790 | + | ||
| 791 | + | // ListChecks is a helper method to define mock.On call | |
| 792 | + | // - ctx context.Context | |
| 793 | + | // - repo domain.RepoName | |
| 794 | + | // - sha string | |
| 795 | + | // - limit int | |
| 796 | + | func (_e *CIStore_Expecter) ListChecks(ctx any, repo any, sha any, limit any) *CIStore_ListChecks_Call { | |
| 797 | + | return &CIStore_ListChecks_Call{Call: _e.mock.On("ListChecks", ctx, repo, sha, limit)} | |
| 798 | + | } | |
| 799 | + | ||
| 800 | + | func (_c *CIStore_ListChecks_Call) Run(run func(ctx context.Context, repo domain.RepoName, sha string, limit int)) *CIStore_ListChecks_Call { | |
| 801 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 802 | + | var arg0 context.Context | |
| 803 | + | if args[0] != nil { | |
| 804 | + | arg0 = args[0].(context.Context) | |
| 805 | + | } | |
| 806 | + | var arg1 domain.RepoName | |
| 807 | + | if args[1] != nil { | |
| 808 | + | arg1 = args[1].(domain.RepoName) | |
| 809 | + | } | |
| 810 | + | var arg2 string | |
| 811 | + | if args[2] != nil { | |
| 812 | + | arg2 = args[2].(string) | |
| 813 | + | } | |
| 814 | + | var arg3 int | |
| 815 | + | if args[3] != nil { | |
| 816 | + | arg3 = args[3].(int) | |
| 817 | + | } | |
| 818 | + | run( | |
| 819 | + | arg0, | |
| 820 | + | arg1, | |
| 821 | + | arg2, | |
| 822 | + | arg3, | |
| 823 | + | ) | |
| 824 | + | }) | |
| 825 | + | return _c | |
| 826 | + | } | |
| 827 | + | ||
| 828 | + | func (_c *CIStore_ListChecks_Call) Return(checks []domain.CICheck, more bool, err error) *CIStore_ListChecks_Call { | |
| 829 | + | _c.Call.Return(checks, more, err) | |
| 830 | + | return _c | |
| 831 | + | } | |
| 832 | + | ||
| 833 | + | func (_c *CIStore_ListChecks_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, sha string, limit int) ([]domain.CICheck, bool, error)) *CIStore_ListChecks_Call { | |
| 834 | + | _c.Call.Return(run) | |
| 835 | + | return _c | |
| 836 | + | } | |
| 837 | + | ||
| 838 | + | // ListJobs provides a mock function for the type CIStore | |
| 839 | + | func (_mock *CIStore) ListJobs(ctx context.Context, runID int64, runAttempt int32) ([]domain.CIJob, error) { | |
| 840 | + | ret := _mock.Called(ctx, runID, runAttempt) | |
| 841 | + | ||
| 842 | + | if len(ret) == 0 { | |
| 843 | + | panic("no return value specified for ListJobs") | |
| 844 | + | } | |
| 845 | + | ||
| 846 | + | var r0 []domain.CIJob | |
| 847 | + | var r1 error | |
| 848 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64, int32) ([]domain.CIJob, error)); ok { | |
| 849 | + | return returnFunc(ctx, runID, runAttempt) | |
| 850 | + | } | |
| 851 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int64, int32) []domain.CIJob); ok { | |
| 852 | + | r0 = returnFunc(ctx, runID, runAttempt) | |
| 853 | + | } else { | |
| 854 | + | if ret.Get(0) != nil { | |
| 855 | + | r0 = ret.Get(0).([]domain.CIJob) | |
| 856 | + | } | |
| 857 | + | } | |
| 858 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, int64, int32) error); ok { | |
| 859 | + | r1 = returnFunc(ctx, runID, runAttempt) | |
| 860 | + | } else { | |
| 861 | + | r1 = ret.Error(1) | |
| 862 | + | } | |
| 863 | + | return r0, r1 | |
| 864 | + | } | |
| 865 | + | ||
| 866 | + | // CIStore_ListJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListJobs' | |
| 867 | + | type CIStore_ListJobs_Call struct { | |
| 868 | + | *mock.Call | |
| 869 | + | } | |
| 870 | + | ||
| 871 | + | // ListJobs is a helper method to define mock.On call | |
| 872 | + | // - ctx context.Context | |
| 873 | + | // - runID int64 | |
| 874 | + | // - runAttempt int32 | |
| 875 | + | func (_e *CIStore_Expecter) ListJobs(ctx any, runID any, runAttempt any) *CIStore_ListJobs_Call { | |
| 876 | + | return &CIStore_ListJobs_Call{Call: _e.mock.On("ListJobs", ctx, runID, runAttempt)} | |
| 877 | + | } | |
| 878 | + | ||
| 879 | + | func (_c *CIStore_ListJobs_Call) Run(run func(ctx context.Context, runID int64, runAttempt int32)) *CIStore_ListJobs_Call { | |
| 880 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 881 | + | var arg0 context.Context | |
| 882 | + | if args[0] != nil { | |
| 883 | + | arg0 = args[0].(context.Context) | |
| 884 | + | } | |
| 885 | + | var arg1 int64 | |
| 886 | + | if args[1] != nil { | |
| 887 | + | arg1 = args[1].(int64) | |
| 888 | + | } | |
| 889 | + | var arg2 int32 | |
| 890 | + | if args[2] != nil { | |
| 891 | + | arg2 = args[2].(int32) | |
| 892 | + | } | |
| 893 | + | run( | |
| 894 | + | arg0, | |
| 895 | + | arg1, | |
| 896 | + | arg2, | |
| 897 | + | ) | |
| 898 | + | }) | |
| 899 | + | return _c | |
| 900 | + | } | |
| 901 | + | ||
| 902 | + | func (_c *CIStore_ListJobs_Call) Return(cIJobs []domain.CIJob, err error) *CIStore_ListJobs_Call { | |
| 903 | + | _c.Call.Return(cIJobs, err) | |
| 904 | + | return _c | |
| 905 | + | } | |
| 906 | + | ||
| 907 | + | func (_c *CIStore_ListJobs_Call) RunAndReturn(run func(ctx context.Context, runID int64, runAttempt int32) ([]domain.CIJob, error)) *CIStore_ListJobs_Call { | |
| 908 | + | _c.Call.Return(run) | |
| 909 | + | return _c | |
| 910 | + | } | |
| 911 | + | ||
| 912 | + | // ListOpenRuns provides a mock function for the type CIStore | |
| 913 | + | func (_mock *CIStore) ListOpenRuns(ctx context.Context) ([]domain.CIRun, error) { | |
| 914 | + | ret := _mock.Called(ctx) | |
| 915 | + | ||
| 916 | + | if len(ret) == 0 { | |
| 917 | + | panic("no return value specified for ListOpenRuns") | |
| 918 | + | } | |
| 919 | + | ||
| 920 | + | var r0 []domain.CIRun | |
| 921 | + | var r1 error | |
| 922 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.CIRun, error)); ok { | |
| 923 | + | return returnFunc(ctx) | |
| 924 | + | } | |
| 925 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.CIRun); ok { | |
| 926 | + | r0 = returnFunc(ctx) | |
| 927 | + | } else { | |
| 928 | + | if ret.Get(0) != nil { | |
| 929 | + | r0 = ret.Get(0).([]domain.CIRun) | |
| 930 | + | } | |
| 931 | + | } | |
| 932 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 933 | + | r1 = returnFunc(ctx) | |
| 934 | + | } else { | |
| 935 | + | r1 = ret.Error(1) | |
| 936 | + | } | |
| 937 | + | return r0, r1 | |
| 938 | + | } | |
| 939 | + | ||
| 940 | + | // CIStore_ListOpenRuns_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListOpenRuns' | |
| 941 | + | type CIStore_ListOpenRuns_Call struct { | |
| 942 | + | *mock.Call | |
| 943 | + | } | |
| 944 | + | ||
| 945 | + | // ListOpenRuns is a helper method to define mock.On call | |
| 946 | + | // - ctx context.Context | |
| 947 | + | func (_e *CIStore_Expecter) ListOpenRuns(ctx any) *CIStore_ListOpenRuns_Call { | |
| 948 | + | return &CIStore_ListOpenRuns_Call{Call: _e.mock.On("ListOpenRuns", ctx)} | |
| 949 | + | } | |
| 950 | + | ||
| 951 | + | func (_c *CIStore_ListOpenRuns_Call) Run(run func(ctx context.Context)) *CIStore_ListOpenRuns_Call { | |
| 952 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 953 | + | var arg0 context.Context | |
| 954 | + | if args[0] != nil { | |
| 955 | + | arg0 = args[0].(context.Context) | |
| 956 | + | } | |
| 957 | + | run( | |
| 958 | + | arg0, | |
| 959 | + | ) | |
| 960 | + | }) | |
| 961 | + | return _c | |
| 962 | + | } | |
| 963 | + | ||
| 964 | + | func (_c *CIStore_ListOpenRuns_Call) Return(cIRuns []domain.CIRun, err error) *CIStore_ListOpenRuns_Call { | |
| 965 | + | _c.Call.Return(cIRuns, err) | |
| 966 | + | return _c | |
| 967 | + | } | |
| 968 | + | ||
| 969 | + | func (_c *CIStore_ListOpenRuns_Call) RunAndReturn(run func(ctx context.Context) ([]domain.CIRun, error)) *CIStore_ListOpenRuns_Call { | |
| 970 | + | _c.Call.Return(run) | |
| 971 | + | return _c | |
| 972 | + | } | |
| 973 | + | ||
| 974 | + | // ListReadyJobs provides a mock function for the type CIStore | |
| 975 | + | func (_mock *CIStore) ListReadyJobs(ctx context.Context, limit int) ([]domain.CIJob, error) { | |
| 976 | + | ret := _mock.Called(ctx, limit) | |
| 977 | + | ||
| 978 | + | if len(ret) == 0 { | |
| 979 | + | panic("no return value specified for ListReadyJobs") | |
| 980 | + | } | |
| 981 | + | ||
| 982 | + | var r0 []domain.CIJob | |
| 983 | + | var r1 error | |
| 984 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int) ([]domain.CIJob, error)); ok { | |
| 985 | + | return returnFunc(ctx, limit) | |
| 986 | + | } | |
| 987 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int) []domain.CIJob); ok { | |
| 988 | + | r0 = returnFunc(ctx, limit) | |
| 989 | + | } else { | |
| 990 | + | if ret.Get(0) != nil { | |
| 991 | + | r0 = ret.Get(0).([]domain.CIJob) | |
| 992 | + | } | |
| 993 | + | } | |
| 994 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, int) error); ok { | |
| 995 | + | r1 = returnFunc(ctx, limit) | |
| 996 | + | } else { | |
| 997 | + | r1 = ret.Error(1) | |
| 998 | + | } | |
| 999 | + | return r0, r1 | |
| 1000 | + | } | |
| 1001 | + | ||
| 1002 | + | // CIStore_ListReadyJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReadyJobs' | |
| 1003 | + | type CIStore_ListReadyJobs_Call struct { | |
| 1004 | + | *mock.Call | |
| 1005 | + | } | |
| 1006 | + | ||
| 1007 | + | // ListReadyJobs is a helper method to define mock.On call | |
| 1008 | + | // - ctx context.Context | |
| 1009 | + | // - limit int | |
| 1010 | + | func (_e *CIStore_Expecter) ListReadyJobs(ctx any, limit any) *CIStore_ListReadyJobs_Call { | |
| 1011 | + | return &CIStore_ListReadyJobs_Call{Call: _e.mock.On("ListReadyJobs", ctx, limit)} | |
| 1012 | + | } | |
| 1013 | + | ||
| 1014 | + | func (_c *CIStore_ListReadyJobs_Call) Run(run func(ctx context.Context, limit int)) *CIStore_ListReadyJobs_Call { | |
| 1015 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1016 | + | var arg0 context.Context | |
| 1017 | + | if args[0] != nil { | |
| 1018 | + | arg0 = args[0].(context.Context) | |
| 1019 | + | } | |
| 1020 | + | var arg1 int | |
| 1021 | + | if args[1] != nil { | |
| 1022 | + | arg1 = args[1].(int) | |
| 1023 | + | } | |
| 1024 | + | run( | |
| 1025 | + | arg0, | |
| 1026 | + | arg1, | |
| 1027 | + | ) | |
| 1028 | + | }) | |
| 1029 | + | return _c | |
| 1030 | + | } | |
| 1031 | + | ||
| 1032 | + | func (_c *CIStore_ListReadyJobs_Call) Return(cIJobs []domain.CIJob, err error) *CIStore_ListReadyJobs_Call { | |
| 1033 | + | _c.Call.Return(cIJobs, err) | |
| 1034 | + | return _c | |
| 1035 | + | } | |
| 1036 | + | ||
| 1037 | + | func (_c *CIStore_ListReadyJobs_Call) RunAndReturn(run func(ctx context.Context, limit int) ([]domain.CIJob, error)) *CIStore_ListReadyJobs_Call { | |
| 1038 | + | _c.Call.Return(run) | |
| 1039 | + | return _c | |
| 1040 | + | } | |
| 1041 | + | ||
| 1042 | + | // ListRunners provides a mock function for the type CIStore | |
| 1043 | + | func (_mock *CIStore) ListRunners(ctx context.Context) ([]domain.CIRunner, error) { | |
| 1044 | + | ret := _mock.Called(ctx) | |
| 1045 | + | ||
| 1046 | + | if len(ret) == 0 { | |
| 1047 | + | panic("no return value specified for ListRunners") | |
| 1048 | + | } | |
| 1049 | + | ||
| 1050 | + | var r0 []domain.CIRunner | |
| 1051 | + | var r1 error | |
| 1052 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.CIRunner, error)); ok { | |
| 1053 | + | return returnFunc(ctx) | |
| 1054 | + | } | |
| 1055 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.CIRunner); ok { | |
| 1056 | + | r0 = returnFunc(ctx) | |
| 1057 | + | } else { | |
| 1058 | + | if ret.Get(0) != nil { | |
| 1059 | + | r0 = ret.Get(0).([]domain.CIRunner) | |
| 1060 | + | } | |
| 1061 | + | } | |
| 1062 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 1063 | + | r1 = returnFunc(ctx) | |
| 1064 | + | } else { | |
| 1065 | + | r1 = ret.Error(1) | |
| 1066 | + | } | |
| 1067 | + | return r0, r1 | |
| 1068 | + | } | |
| 1069 | + | ||
| 1070 | + | // CIStore_ListRunners_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRunners' | |
| 1071 | + | type CIStore_ListRunners_Call struct { | |
| 1072 | + | *mock.Call | |
| 1073 | + | } | |
| 1074 | + | ||
| 1075 | + | // ListRunners is a helper method to define mock.On call | |
| 1076 | + | // - ctx context.Context | |
| 1077 | + | func (_e *CIStore_Expecter) ListRunners(ctx any) *CIStore_ListRunners_Call { | |
| 1078 | + | return &CIStore_ListRunners_Call{Call: _e.mock.On("ListRunners", ctx)} | |
| 1079 | + | } | |
| 1080 | + | ||
| 1081 | + | func (_c *CIStore_ListRunners_Call) Run(run func(ctx context.Context)) *CIStore_ListRunners_Call { | |
| 1082 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1083 | + | var arg0 context.Context | |
| 1084 | + | if args[0] != nil { | |
| 1085 | + | arg0 = args[0].(context.Context) | |
| 1086 | + | } | |
| 1087 | + | run( | |
| 1088 | + | arg0, | |
| 1089 | + | ) | |
| 1090 | + | }) | |
| 1091 | + | return _c | |
| 1092 | + | } | |
| 1093 | + | ||
| 1094 | + | func (_c *CIStore_ListRunners_Call) Return(cIRunners []domain.CIRunner, err error) *CIStore_ListRunners_Call { | |
| 1095 | + | _c.Call.Return(cIRunners, err) | |
| 1096 | + | return _c | |
| 1097 | + | } | |
| 1098 | + | ||
| 1099 | + | func (_c *CIStore_ListRunners_Call) RunAndReturn(run func(ctx context.Context) ([]domain.CIRunner, error)) *CIStore_ListRunners_Call { | |
| 1100 | + | _c.Call.Return(run) | |
| 1101 | + | return _c | |
| 1102 | + | } | |
| 1103 | + | ||
| 1104 | + | // ListRuns provides a mock function for the type CIStore | |
| 1105 | + | func (_mock *CIStore) ListRuns(ctx context.Context, repo domain.RepoName, before int64, limit int) ([]domain.CIRun, error) { | |
| 1106 | + | ret := _mock.Called(ctx, repo, before, limit) | |
| 1107 | + | ||
| 1108 | + | if len(ret) == 0 { | |
| 1109 | + | panic("no return value specified for ListRuns") | |
| 1110 | + | } | |
| 1111 | + | ||
| 1112 | + | var r0 []domain.CIRun | |
| 1113 | + | var r1 error | |
| 1114 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int) ([]domain.CIRun, error)); ok { | |
| 1115 | + | return returnFunc(ctx, repo, before, limit) | |
| 1116 | + | } | |
| 1117 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int) []domain.CIRun); ok { | |
| 1118 | + | r0 = returnFunc(ctx, repo, before, limit) | |
| 1119 | + | } else { | |
| 1120 | + | if ret.Get(0) != nil { | |
| 1121 | + | r0 = ret.Get(0).([]domain.CIRun) | |
| 1122 | + | } | |
| 1123 | + | } | |
| 1124 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64, int) error); ok { | |
| 1125 | + | r1 = returnFunc(ctx, repo, before, limit) | |
| 1126 | + | } else { | |
| 1127 | + | r1 = ret.Error(1) | |
| 1128 | + | } | |
| 1129 | + | return r0, r1 | |
| 1130 | + | } | |
| 1131 | + | ||
| 1132 | + | // CIStore_ListRuns_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRuns' | |
| 1133 | + | type CIStore_ListRuns_Call struct { | |
| 1134 | + | *mock.Call | |
| 1135 | + | } | |
| 1136 | + | ||
| 1137 | + | // ListRuns is a helper method to define mock.On call | |
| 1138 | + | // - ctx context.Context | |
| 1139 | + | // - repo domain.RepoName | |
| 1140 | + | // - before int64 | |
| 1141 | + | // - limit int | |
| 1142 | + | func (_e *CIStore_Expecter) ListRuns(ctx any, repo any, before any, limit any) *CIStore_ListRuns_Call { | |
| 1143 | + | return &CIStore_ListRuns_Call{Call: _e.mock.On("ListRuns", ctx, repo, before, limit)} | |
| 1144 | + | } | |
| 1145 | + | ||
| 1146 | + | func (_c *CIStore_ListRuns_Call) Run(run func(ctx context.Context, repo domain.RepoName, before int64, limit int)) *CIStore_ListRuns_Call { | |
| 1147 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1148 | + | var arg0 context.Context | |
| 1149 | + | if args[0] != nil { | |
| 1150 | + | arg0 = args[0].(context.Context) | |
| 1151 | + | } | |
| 1152 | + | var arg1 domain.RepoName | |
| 1153 | + | if args[1] != nil { | |
| 1154 | + | arg1 = args[1].(domain.RepoName) | |
| 1155 | + | } | |
| 1156 | + | var arg2 int64 | |
| 1157 | + | if args[2] != nil { | |
| 1158 | + | arg2 = args[2].(int64) | |
| 1159 | + | } | |
| 1160 | + | var arg3 int | |
| 1161 | + | if args[3] != nil { | |
| 1162 | + | arg3 = args[3].(int) | |
| 1163 | + | } | |
| 1164 | + | run( | |
| 1165 | + | arg0, | |
| 1166 | + | arg1, | |
| 1167 | + | arg2, | |
| 1168 | + | arg3, | |
| 1169 | + | ) | |
| 1170 | + | }) | |
| 1171 | + | return _c | |
| 1172 | + | } | |
| 1173 | + | ||
| 1174 | + | func (_c *CIStore_ListRuns_Call) Return(cIRuns []domain.CIRun, err error) *CIStore_ListRuns_Call { | |
| 1175 | + | _c.Call.Return(cIRuns, err) | |
| 1176 | + | return _c | |
| 1177 | + | } | |
| 1178 | + | ||
| 1179 | + | func (_c *CIStore_ListRuns_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, before int64, limit int) ([]domain.CIRun, error)) *CIStore_ListRuns_Call { | |
| 1180 | + | _c.Call.Return(run) | |
| 1181 | + | return _c | |
| 1182 | + | } | |
| 1183 | + | ||
| 1184 | + | // RemoveRunner provides a mock function for the type CIStore | |
| 1185 | + | func (_mock *CIStore) RemoveRunner(ctx context.Context, id string) (domain.CIRunner, error) { | |
| 1186 | + | ret := _mock.Called(ctx, id) | |
| 1187 | + | ||
| 1188 | + | if len(ret) == 0 { | |
| 1189 | + | panic("no return value specified for RemoveRunner") | |
| 1190 | + | } | |
| 1191 | + | ||
| 1192 | + | var r0 domain.CIRunner | |
| 1193 | + | var r1 error | |
| 1194 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string) (domain.CIRunner, error)); ok { | |
| 1195 | + | return returnFunc(ctx, id) | |
| 1196 | + | } | |
| 1197 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string) domain.CIRunner); ok { | |
| 1198 | + | r0 = returnFunc(ctx, id) | |
| 1199 | + | } else { | |
| 1200 | + | r0 = ret.Get(0).(domain.CIRunner) | |
| 1201 | + | } | |
| 1202 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { | |
| 1203 | + | r1 = returnFunc(ctx, id) | |
| 1204 | + | } else { | |
| 1205 | + | r1 = ret.Error(1) | |
| 1206 | + | } | |
| 1207 | + | return r0, r1 | |
| 1208 | + | } | |
| 1209 | + | ||
| 1210 | + | // CIStore_RemoveRunner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveRunner' | |
| 1211 | + | type CIStore_RemoveRunner_Call struct { | |
| 1212 | + | *mock.Call | |
| 1213 | + | } | |
| 1214 | + | ||
| 1215 | + | // RemoveRunner is a helper method to define mock.On call | |
| 1216 | + | // - ctx context.Context | |
| 1217 | + | // - id string | |
| 1218 | + | func (_e *CIStore_Expecter) RemoveRunner(ctx any, id any) *CIStore_RemoveRunner_Call { | |
| 1219 | + | return &CIStore_RemoveRunner_Call{Call: _e.mock.On("RemoveRunner", ctx, id)} | |
| 1220 | + | } | |
| 1221 | + | ||
| 1222 | + | func (_c *CIStore_RemoveRunner_Call) Run(run func(ctx context.Context, id string)) *CIStore_RemoveRunner_Call { | |
| 1223 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1224 | + | var arg0 context.Context | |
| 1225 | + | if args[0] != nil { | |
| 1226 | + | arg0 = args[0].(context.Context) | |
| 1227 | + | } | |
| 1228 | + | var arg1 string | |
| 1229 | + | if args[1] != nil { | |
| 1230 | + | arg1 = args[1].(string) | |
| 1231 | + | } | |
| 1232 | + | run( | |
| 1233 | + | arg0, | |
| 1234 | + | arg1, | |
| 1235 | + | ) | |
| 1236 | + | }) | |
| 1237 | + | return _c | |
| 1238 | + | } | |
| 1239 | + | ||
| 1240 | + | func (_c *CIStore_RemoveRunner_Call) Return(cIRunner domain.CIRunner, err error) *CIStore_RemoveRunner_Call { | |
| 1241 | + | _c.Call.Return(cIRunner, err) | |
| 1242 | + | return _c | |
| 1243 | + | } | |
| 1244 | + | ||
| 1245 | + | func (_c *CIStore_RemoveRunner_Call) RunAndReturn(run func(ctx context.Context, id string) (domain.CIRunner, error)) *CIStore_RemoveRunner_Call { | |
| 1246 | + | _c.Call.Return(run) | |
| 1247 | + | return _c | |
| 1248 | + | } | |
| 1249 | + | ||
| 1250 | + | // RunIDs provides a mock function for the type CIStore | |
| 1251 | + | func (_mock *CIStore) RunIDs(ctx context.Context, repo domain.RepoName) ([]int64, error) { | |
| 1252 | + | ret := _mock.Called(ctx, repo) | |
| 1253 | + | ||
| 1254 | + | if len(ret) == 0 { | |
| 1255 | + | panic("no return value specified for RunIDs") | |
| 1256 | + | } | |
| 1257 | + | ||
| 1258 | + | var r0 []int64 | |
| 1259 | + | var r1 error | |
| 1260 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) ([]int64, error)); ok { | |
| 1261 | + | return returnFunc(ctx, repo) | |
| 1262 | + | } | |
| 1263 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) []int64); ok { | |
| 1264 | + | r0 = returnFunc(ctx, repo) | |
| 1265 | + | } else { | |
| 1266 | + | if ret.Get(0) != nil { | |
| 1267 | + | r0 = ret.Get(0).([]int64) | |
| 1268 | + | } | |
| 1269 | + | } | |
| 1270 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |
| 1271 | + | r1 = returnFunc(ctx, repo) | |
| 1272 | + | } else { | |
| 1273 | + | r1 = ret.Error(1) | |
| 1274 | + | } | |
| 1275 | + | return r0, r1 | |
| 1276 | + | } | |
| 1277 | + | ||
| 1278 | + | // CIStore_RunIDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RunIDs' | |
| 1279 | + | type CIStore_RunIDs_Call struct { | |
| 1280 | + | *mock.Call | |
| 1281 | + | } | |
| 1282 | + | ||
| 1283 | + | // RunIDs is a helper method to define mock.On call | |
| 1284 | + | // - ctx context.Context | |
| 1285 | + | // - repo domain.RepoName | |
| 1286 | + | func (_e *CIStore_Expecter) RunIDs(ctx any, repo any) *CIStore_RunIDs_Call { | |
| 1287 | + | return &CIStore_RunIDs_Call{Call: _e.mock.On("RunIDs", ctx, repo)} | |
| 1288 | + | } | |
| 1289 | + | ||
| 1290 | + | func (_c *CIStore_RunIDs_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *CIStore_RunIDs_Call { | |
| 1291 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1292 | + | var arg0 context.Context | |
| 1293 | + | if args[0] != nil { | |
| 1294 | + | arg0 = args[0].(context.Context) | |
| 1295 | + | } | |
| 1296 | + | var arg1 domain.RepoName | |
| 1297 | + | if args[1] != nil { | |
| 1298 | + | arg1 = args[1].(domain.RepoName) | |
| 1299 | + | } | |
| 1300 | + | run( | |
| 1301 | + | arg0, | |
| 1302 | + | arg1, | |
| 1303 | + | ) | |
| 1304 | + | }) | |
| 1305 | + | return _c | |
| 1306 | + | } | |
| 1307 | + | ||
| 1308 | + | func (_c *CIStore_RunIDs_Call) Return(int64s []int64, err error) *CIStore_RunIDs_Call { | |
| 1309 | + | _c.Call.Return(int64s, err) | |
| 1310 | + | return _c | |
| 1311 | + | } | |
| 1312 | + | ||
| 1313 | + | func (_c *CIStore_RunIDs_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) ([]int64, error)) *CIStore_RunIDs_Call { | |
| 1314 | + | _c.Call.Return(run) | |
| 1315 | + | return _c | |
| 1316 | + | } | |
| 1317 | + | ||
| 1318 | + | // Sweep provides a mock function for the type CIStore | |
| 1319 | + | func (_mock *CIStore) Sweep(ctx context.Context, before time.Time) ([]int64, error) { | |
| 1320 | + | ret := _mock.Called(ctx, before) | |
| 1321 | + | ||
| 1322 | + | if len(ret) == 0 { | |
| 1323 | + | panic("no return value specified for Sweep") | |
| 1324 | + | } | |
| 1325 | + | ||
| 1326 | + | var r0 []int64 | |
| 1327 | + | var r1 error | |
| 1328 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, time.Time) ([]int64, error)); ok { | |
| 1329 | + | return returnFunc(ctx, before) | |
| 1330 | + | } | |
| 1331 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, time.Time) []int64); ok { | |
| 1332 | + | r0 = returnFunc(ctx, before) | |
| 1333 | + | } else { | |
| 1334 | + | if ret.Get(0) != nil { | |
| 1335 | + | r0 = ret.Get(0).([]int64) | |
| 1336 | + | } | |
| 1337 | + | } | |
| 1338 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, time.Time) error); ok { | |
| 1339 | + | r1 = returnFunc(ctx, before) | |
| 1340 | + | } else { | |
| 1341 | + | r1 = ret.Error(1) | |
| 1342 | + | } | |
| 1343 | + | return r0, r1 | |
| 1344 | + | } | |
| 1345 | + | ||
| 1346 | + | // CIStore_Sweep_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sweep' | |
| 1347 | + | type CIStore_Sweep_Call struct { | |
| 1348 | + | *mock.Call | |
| 1349 | + | } | |
| 1350 | + | ||
| 1351 | + | // Sweep is a helper method to define mock.On call | |
| 1352 | + | // - ctx context.Context | |
| 1353 | + | // - before time.Time | |
| 1354 | + | func (_e *CIStore_Expecter) Sweep(ctx any, before any) *CIStore_Sweep_Call { | |
| 1355 | + | return &CIStore_Sweep_Call{Call: _e.mock.On("Sweep", ctx, before)} | |
| 1356 | + | } | |
| 1357 | + | ||
| 1358 | + | func (_c *CIStore_Sweep_Call) Run(run func(ctx context.Context, before time.Time)) *CIStore_Sweep_Call { | |
| 1359 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1360 | + | var arg0 context.Context | |
| 1361 | + | if args[0] != nil { | |
| 1362 | + | arg0 = args[0].(context.Context) | |
| 1363 | + | } | |
| 1364 | + | var arg1 time.Time | |
| 1365 | + | if args[1] != nil { | |
| 1366 | + | arg1 = args[1].(time.Time) | |
| 1367 | + | } | |
| 1368 | + | run( | |
| 1369 | + | arg0, | |
| 1370 | + | arg1, | |
| 1371 | + | ) | |
| 1372 | + | }) | |
| 1373 | + | return _c | |
| 1374 | + | } | |
| 1375 | + | ||
| 1376 | + | func (_c *CIStore_Sweep_Call) Return(int64s []int64, err error) *CIStore_Sweep_Call { | |
| 1377 | + | _c.Call.Return(int64s, err) | |
| 1378 | + | return _c | |
| 1379 | + | } | |
| 1380 | + | ||
| 1381 | + | func (_c *CIStore_Sweep_Call) RunAndReturn(run func(ctx context.Context, before time.Time) ([]int64, error)) *CIStore_Sweep_Call { | |
| 1382 | + | _c.Call.Return(run) | |
| 1383 | + | return _c | |
| 1384 | + | } | |
A internal/ports/mocks/RunService.go +769 -0
| @@ -0,0 +1,769 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | "bnema.dev/ermite/internal/ports" | |
| 12 | + | mock "github.com/stretchr/testify/mock" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // NewRunService creates a new instance of RunService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 16 | + | // The first argument is typically a *testing.T value. | |
| 17 | + | func NewRunService(t interface { | |
| 18 | + | mock.TestingT | |
| 19 | + | Cleanup(func()) | |
| 20 | + | }) *RunService { | |
| 21 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 22 | + | helper.Helper() | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | mock := &RunService{} | |
| 26 | + | mock.Mock.Test(t) | |
| 27 | + | ||
| 28 | + | t.Cleanup(func() { | |
| 29 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 30 | + | helper.Helper() | |
| 31 | + | } | |
| 32 | + | mock.AssertExpectations(t) | |
| 33 | + | }) | |
| 34 | + | ||
| 35 | + | return mock | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // RunService is an autogenerated mock type for the RunService type | |
| 39 | + | type RunService struct { | |
| 40 | + | mock.Mock | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | type RunService_Expecter struct { | |
| 44 | + | mock *mock.Mock | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | func (_m *RunService) EXPECT() *RunService_Expecter { | |
| 48 | + | return &RunService_Expecter{mock: &_m.Mock} | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // CancelRun provides a mock function for the type RunService | |
| 52 | + | func (_mock *RunService) CancelRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64) (domain.CIRun, error) { | |
| 53 | + | ret := _mock.Called(ctx, p, repo, runID) | |
| 54 | + | ||
| 55 | + | if len(ret) == 0 { | |
| 56 | + | panic("no return value specified for CancelRun") | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | var r0 domain.CIRun | |
| 60 | + | var r1 error | |
| 61 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) (domain.CIRun, error)); ok { | |
| 62 | + | return returnFunc(ctx, p, repo, runID) | |
| 63 | + | } | |
| 64 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) domain.CIRun); ok { | |
| 65 | + | r0 = returnFunc(ctx, p, repo, runID) | |
| 66 | + | } else { | |
| 67 | + | r0 = ret.Get(0).(domain.CIRun) | |
| 68 | + | } | |
| 69 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64) error); ok { | |
| 70 | + | r1 = returnFunc(ctx, p, repo, runID) | |
| 71 | + | } else { | |
| 72 | + | r1 = ret.Error(1) | |
| 73 | + | } | |
| 74 | + | return r0, r1 | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | // RunService_CancelRun_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelRun' | |
| 78 | + | type RunService_CancelRun_Call struct { | |
| 79 | + | *mock.Call | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | // CancelRun is a helper method to define mock.On call | |
| 83 | + | // - ctx context.Context | |
| 84 | + | // - p domain.Principal | |
| 85 | + | // - repo domain.RepoName | |
| 86 | + | // - runID int64 | |
| 87 | + | func (_e *RunService_Expecter) CancelRun(ctx any, p any, repo any, runID any) *RunService_CancelRun_Call { | |
| 88 | + | return &RunService_CancelRun_Call{Call: _e.mock.On("CancelRun", ctx, p, repo, runID)} | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | func (_c *RunService_CancelRun_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64)) *RunService_CancelRun_Call { | |
| 92 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 93 | + | var arg0 context.Context | |
| 94 | + | if args[0] != nil { | |
| 95 | + | arg0 = args[0].(context.Context) | |
| 96 | + | } | |
| 97 | + | var arg1 domain.Principal | |
| 98 | + | if args[1] != nil { | |
| 99 | + | arg1 = args[1].(domain.Principal) | |
| 100 | + | } | |
| 101 | + | var arg2 domain.RepoName | |
| 102 | + | if args[2] != nil { | |
| 103 | + | arg2 = args[2].(domain.RepoName) | |
| 104 | + | } | |
| 105 | + | var arg3 int64 | |
| 106 | + | if args[3] != nil { | |
| 107 | + | arg3 = args[3].(int64) | |
| 108 | + | } | |
| 109 | + | run( | |
| 110 | + | arg0, | |
| 111 | + | arg1, | |
| 112 | + | arg2, | |
| 113 | + | arg3, | |
| 114 | + | ) | |
| 115 | + | }) | |
| 116 | + | return _c | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | func (_c *RunService_CancelRun_Call) Return(cIRun domain.CIRun, err error) *RunService_CancelRun_Call { | |
| 120 | + | _c.Call.Return(cIRun, err) | |
| 121 | + | return _c | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | func (_c *RunService_CancelRun_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64) (domain.CIRun, error)) *RunService_CancelRun_Call { | |
| 125 | + | _c.Call.Return(run) | |
| 126 | + | return _c | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | // GetJob provides a mock function for the type RunService | |
| 130 | + | func (_mock *RunService) GetJob(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32) (ports.JobView, error) { | |
| 131 | + | ret := _mock.Called(ctx, p, repo, jobID, attempt) | |
| 132 | + | ||
| 133 | + | if len(ret) == 0 { | |
| 134 | + | panic("no return value specified for GetJob") | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | var r0 ports.JobView | |
| 138 | + | var r1 error | |
| 139 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, string, int32) (ports.JobView, error)); ok { | |
| 140 | + | return returnFunc(ctx, p, repo, jobID, attempt) | |
| 141 | + | } | |
| 142 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, string, int32) ports.JobView); ok { | |
| 143 | + | r0 = returnFunc(ctx, p, repo, jobID, attempt) | |
| 144 | + | } else { | |
| 145 | + | r0 = ret.Get(0).(ports.JobView) | |
| 146 | + | } | |
| 147 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, string, int32) error); ok { | |
| 148 | + | r1 = returnFunc(ctx, p, repo, jobID, attempt) | |
| 149 | + | } else { | |
| 150 | + | r1 = ret.Error(1) | |
| 151 | + | } | |
| 152 | + | return r0, r1 | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | // RunService_GetJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJob' | |
| 156 | + | type RunService_GetJob_Call struct { | |
| 157 | + | *mock.Call | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | // GetJob is a helper method to define mock.On call | |
| 161 | + | // - ctx context.Context | |
| 162 | + | // - p domain.Principal | |
| 163 | + | // - repo domain.RepoName | |
| 164 | + | // - jobID string | |
| 165 | + | // - attempt int32 | |
| 166 | + | func (_e *RunService_Expecter) GetJob(ctx any, p any, repo any, jobID any, attempt any) *RunService_GetJob_Call { | |
| 167 | + | return &RunService_GetJob_Call{Call: _e.mock.On("GetJob", ctx, p, repo, jobID, attempt)} | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | func (_c *RunService_GetJob_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32)) *RunService_GetJob_Call { | |
| 171 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 172 | + | var arg0 context.Context | |
| 173 | + | if args[0] != nil { | |
| 174 | + | arg0 = args[0].(context.Context) | |
| 175 | + | } | |
| 176 | + | var arg1 domain.Principal | |
| 177 | + | if args[1] != nil { | |
| 178 | + | arg1 = args[1].(domain.Principal) | |
| 179 | + | } | |
| 180 | + | var arg2 domain.RepoName | |
| 181 | + | if args[2] != nil { | |
| 182 | + | arg2 = args[2].(domain.RepoName) | |
| 183 | + | } | |
| 184 | + | var arg3 string | |
| 185 | + | if args[3] != nil { | |
| 186 | + | arg3 = args[3].(string) | |
| 187 | + | } | |
| 188 | + | var arg4 int32 | |
| 189 | + | if args[4] != nil { | |
| 190 | + | arg4 = args[4].(int32) | |
| 191 | + | } | |
| 192 | + | run( | |
| 193 | + | arg0, | |
| 194 | + | arg1, | |
| 195 | + | arg2, | |
| 196 | + | arg3, | |
| 197 | + | arg4, | |
| 198 | + | ) | |
| 199 | + | }) | |
| 200 | + | return _c | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | func (_c *RunService_GetJob_Call) Return(jobView ports.JobView, err error) *RunService_GetJob_Call { | |
| 204 | + | _c.Call.Return(jobView, err) | |
| 205 | + | return _c | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | func (_c *RunService_GetJob_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32) (ports.JobView, error)) *RunService_GetJob_Call { | |
| 209 | + | _c.Call.Return(run) | |
| 210 | + | return _c | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | // GetJobLog provides a mock function for the type RunService | |
| 214 | + | func (_mock *RunService) GetJobLog(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32, offset int64, follow bool, send func(ports.LogBatch) error) error { | |
| 215 | + | ret := _mock.Called(ctx, p, repo, jobID, attempt, offset, follow, send) | |
| 216 | + | ||
| 217 | + | if len(ret) == 0 { | |
| 218 | + | panic("no return value specified for GetJobLog") | |
| 219 | + | } | |
| 220 | + | ||
| 221 | + | var r0 error | |
| 222 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, string, int32, int64, bool, func(ports.LogBatch) error) error); ok { | |
| 223 | + | r0 = returnFunc(ctx, p, repo, jobID, attempt, offset, follow, send) | |
| 224 | + | } else { | |
| 225 | + | r0 = ret.Error(0) | |
| 226 | + | } | |
| 227 | + | return r0 | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | // RunService_GetJobLog_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJobLog' | |
| 231 | + | type RunService_GetJobLog_Call struct { | |
| 232 | + | *mock.Call | |
| 233 | + | } | |
| 234 | + | ||
| 235 | + | // GetJobLog is a helper method to define mock.On call | |
| 236 | + | // - ctx context.Context | |
| 237 | + | // - p domain.Principal | |
| 238 | + | // - repo domain.RepoName | |
| 239 | + | // - jobID string | |
| 240 | + | // - attempt int32 | |
| 241 | + | // - offset int64 | |
| 242 | + | // - follow bool | |
| 243 | + | // - send func(ports.LogBatch) error | |
| 244 | + | func (_e *RunService_Expecter) GetJobLog(ctx any, p any, repo any, jobID any, attempt any, offset any, follow any, send any) *RunService_GetJobLog_Call { | |
| 245 | + | return &RunService_GetJobLog_Call{Call: _e.mock.On("GetJobLog", ctx, p, repo, jobID, attempt, offset, follow, send)} | |
| 246 | + | } | |
| 247 | + | ||
| 248 | + | func (_c *RunService_GetJobLog_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32, offset int64, follow bool, send func(ports.LogBatch) error)) *RunService_GetJobLog_Call { | |
| 249 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 250 | + | var arg0 context.Context | |
| 251 | + | if args[0] != nil { | |
| 252 | + | arg0 = args[0].(context.Context) | |
| 253 | + | } | |
| 254 | + | var arg1 domain.Principal | |
| 255 | + | if args[1] != nil { | |
| 256 | + | arg1 = args[1].(domain.Principal) | |
| 257 | + | } | |
| 258 | + | var arg2 domain.RepoName | |
| 259 | + | if args[2] != nil { | |
| 260 | + | arg2 = args[2].(domain.RepoName) | |
| 261 | + | } | |
| 262 | + | var arg3 string | |
| 263 | + | if args[3] != nil { | |
| 264 | + | arg3 = args[3].(string) | |
| 265 | + | } | |
| 266 | + | var arg4 int32 | |
| 267 | + | if args[4] != nil { | |
| 268 | + | arg4 = args[4].(int32) | |
| 269 | + | } | |
| 270 | + | var arg5 int64 | |
| 271 | + | if args[5] != nil { | |
| 272 | + | arg5 = args[5].(int64) | |
| 273 | + | } | |
| 274 | + | var arg6 bool | |
| 275 | + | if args[6] != nil { | |
| 276 | + | arg6 = args[6].(bool) | |
| 277 | + | } | |
| 278 | + | var arg7 func(ports.LogBatch) error | |
| 279 | + | if args[7] != nil { | |
| 280 | + | arg7 = args[7].(func(ports.LogBatch) error) | |
| 281 | + | } | |
| 282 | + | run( | |
| 283 | + | arg0, | |
| 284 | + | arg1, | |
| 285 | + | arg2, | |
| 286 | + | arg3, | |
| 287 | + | arg4, | |
| 288 | + | arg5, | |
| 289 | + | arg6, | |
| 290 | + | arg7, | |
| 291 | + | ) | |
| 292 | + | }) | |
| 293 | + | return _c | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | func (_c *RunService_GetJobLog_Call) Return(err error) *RunService_GetJobLog_Call { | |
| 297 | + | _c.Call.Return(err) | |
| 298 | + | return _c | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | func (_c *RunService_GetJobLog_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, jobID string, attempt int32, offset int64, follow bool, send func(ports.LogBatch) error) error) *RunService_GetJobLog_Call { | |
| 302 | + | _c.Call.Return(run) | |
| 303 | + | return _c | |
| 304 | + | } | |
| 305 | + | ||
| 306 | + | // GetRun provides a mock function for the type RunService | |
| 307 | + | func (_mock *RunService) GetRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, attempt int32) (ports.RunDetail, error) { | |
| 308 | + | ret := _mock.Called(ctx, p, repo, runID, attempt) | |
| 309 | + | ||
| 310 | + | if len(ret) == 0 { | |
| 311 | + | panic("no return value specified for GetRun") | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | var r0 ports.RunDetail | |
| 315 | + | var r1 error | |
| 316 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, int32) (ports.RunDetail, error)); ok { | |
| 317 | + | return returnFunc(ctx, p, repo, runID, attempt) | |
| 318 | + | } | |
| 319 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, int32) ports.RunDetail); ok { | |
| 320 | + | r0 = returnFunc(ctx, p, repo, runID, attempt) | |
| 321 | + | } else { | |
| 322 | + | r0 = ret.Get(0).(ports.RunDetail) | |
| 323 | + | } | |
| 324 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, int32) error); ok { | |
| 325 | + | r1 = returnFunc(ctx, p, repo, runID, attempt) | |
| 326 | + | } else { | |
| 327 | + | r1 = ret.Error(1) | |
| 328 | + | } | |
| 329 | + | return r0, r1 | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | // RunService_GetRun_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRun' | |
| 333 | + | type RunService_GetRun_Call struct { | |
| 334 | + | *mock.Call | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | // GetRun is a helper method to define mock.On call | |
| 338 | + | // - ctx context.Context | |
| 339 | + | // - p domain.Principal | |
| 340 | + | // - repo domain.RepoName | |
| 341 | + | // - runID int64 | |
| 342 | + | // - attempt int32 | |
| 343 | + | func (_e *RunService_Expecter) GetRun(ctx any, p any, repo any, runID any, attempt any) *RunService_GetRun_Call { | |
| 344 | + | return &RunService_GetRun_Call{Call: _e.mock.On("GetRun", ctx, p, repo, runID, attempt)} | |
| 345 | + | } | |
| 346 | + | ||
| 347 | + | func (_c *RunService_GetRun_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, attempt int32)) *RunService_GetRun_Call { | |
| 348 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 349 | + | var arg0 context.Context | |
| 350 | + | if args[0] != nil { | |
| 351 | + | arg0 = args[0].(context.Context) | |
| 352 | + | } | |
| 353 | + | var arg1 domain.Principal | |
| 354 | + | if args[1] != nil { | |
| 355 | + | arg1 = args[1].(domain.Principal) | |
| 356 | + | } | |
| 357 | + | var arg2 domain.RepoName | |
| 358 | + | if args[2] != nil { | |
| 359 | + | arg2 = args[2].(domain.RepoName) | |
| 360 | + | } | |
| 361 | + | var arg3 int64 | |
| 362 | + | if args[3] != nil { | |
| 363 | + | arg3 = args[3].(int64) | |
| 364 | + | } | |
| 365 | + | var arg4 int32 | |
| 366 | + | if args[4] != nil { | |
| 367 | + | arg4 = args[4].(int32) | |
| 368 | + | } | |
| 369 | + | run( | |
| 370 | + | arg0, | |
| 371 | + | arg1, | |
| 372 | + | arg2, | |
| 373 | + | arg3, | |
| 374 | + | arg4, | |
| 375 | + | ) | |
| 376 | + | }) | |
| 377 | + | return _c | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | func (_c *RunService_GetRun_Call) Return(runDetail ports.RunDetail, err error) *RunService_GetRun_Call { | |
| 381 | + | _c.Call.Return(runDetail, err) | |
| 382 | + | return _c | |
| 383 | + | } | |
| 384 | + | ||
| 385 | + | func (_c *RunService_GetRun_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, attempt int32) (ports.RunDetail, error)) *RunService_GetRun_Call { | |
| 386 | + | _c.Call.Return(run) | |
| 387 | + | return _c | |
| 388 | + | } | |
| 389 | + | ||
| 390 | + | // ListChecks provides a mock function for the type RunService | |
| 391 | + | func (_mock *RunService) ListChecks(ctx context.Context, p domain.Principal, repo domain.RepoName, sha string) (ports.ChecksResult, error) { | |
| 392 | + | ret := _mock.Called(ctx, p, repo, sha) | |
| 393 | + | ||
| 394 | + | if len(ret) == 0 { | |
| 395 | + | panic("no return value specified for ListChecks") | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | var r0 ports.ChecksResult | |
| 399 | + | var r1 error | |
| 400 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, string) (ports.ChecksResult, error)); ok { | |
| 401 | + | return returnFunc(ctx, p, repo, sha) | |
| 402 | + | } | |
| 403 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, string) ports.ChecksResult); ok { | |
| 404 | + | r0 = returnFunc(ctx, p, repo, sha) | |
| 405 | + | } else { | |
| 406 | + | r0 = ret.Get(0).(ports.ChecksResult) | |
| 407 | + | } | |
| 408 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, string) error); ok { | |
| 409 | + | r1 = returnFunc(ctx, p, repo, sha) | |
| 410 | + | } else { | |
| 411 | + | r1 = ret.Error(1) | |
| 412 | + | } | |
| 413 | + | return r0, r1 | |
| 414 | + | } | |
| 415 | + | ||
| 416 | + | // RunService_ListChecks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListChecks' | |
| 417 | + | type RunService_ListChecks_Call struct { | |
| 418 | + | *mock.Call | |
| 419 | + | } | |
| 420 | + | ||
| 421 | + | // ListChecks is a helper method to define mock.On call | |
| 422 | + | // - ctx context.Context | |
| 423 | + | // - p domain.Principal | |
| 424 | + | // - repo domain.RepoName | |
| 425 | + | // - sha string | |
| 426 | + | func (_e *RunService_Expecter) ListChecks(ctx any, p any, repo any, sha any) *RunService_ListChecks_Call { | |
| 427 | + | return &RunService_ListChecks_Call{Call: _e.mock.On("ListChecks", ctx, p, repo, sha)} | |
| 428 | + | } | |
| 429 | + | ||
| 430 | + | func (_c *RunService_ListChecks_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, sha string)) *RunService_ListChecks_Call { | |
| 431 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 432 | + | var arg0 context.Context | |
| 433 | + | if args[0] != nil { | |
| 434 | + | arg0 = args[0].(context.Context) | |
| 435 | + | } | |
| 436 | + | var arg1 domain.Principal | |
| 437 | + | if args[1] != nil { | |
| 438 | + | arg1 = args[1].(domain.Principal) | |
| 439 | + | } | |
| 440 | + | var arg2 domain.RepoName | |
| 441 | + | if args[2] != nil { | |
| 442 | + | arg2 = args[2].(domain.RepoName) | |
| 443 | + | } | |
| 444 | + | var arg3 string | |
| 445 | + | if args[3] != nil { | |
| 446 | + | arg3 = args[3].(string) | |
| 447 | + | } | |
| 448 | + | run( | |
| 449 | + | arg0, | |
| 450 | + | arg1, | |
| 451 | + | arg2, | |
| 452 | + | arg3, | |
| 453 | + | ) | |
| 454 | + | }) | |
| 455 | + | return _c | |
| 456 | + | } | |
| 457 | + | ||
| 458 | + | func (_c *RunService_ListChecks_Call) Return(checksResult ports.ChecksResult, err error) *RunService_ListChecks_Call { | |
| 459 | + | _c.Call.Return(checksResult, err) | |
| 460 | + | return _c | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | func (_c *RunService_ListChecks_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, sha string) (ports.ChecksResult, error)) *RunService_ListChecks_Call { | |
| 464 | + | _c.Call.Return(run) | |
| 465 | + | return _c | |
| 466 | + | } | |
| 467 | + | ||
| 468 | + | // ListRunners provides a mock function for the type RunService | |
| 469 | + | func (_mock *RunService) ListRunners(ctx context.Context, p domain.Principal, size int, token string) (ports.RunnerPage, error) { | |
| 470 | + | ret := _mock.Called(ctx, p, size, token) | |
| 471 | + | ||
| 472 | + | if len(ret) == 0 { | |
| 473 | + | panic("no return value specified for ListRunners") | |
| 474 | + | } | |
| 475 | + | ||
| 476 | + | var r0 ports.RunnerPage | |
| 477 | + | var r1 error | |
| 478 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) (ports.RunnerPage, error)); ok { | |
| 479 | + | return returnFunc(ctx, p, size, token) | |
| 480 | + | } | |
| 481 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) ports.RunnerPage); ok { | |
| 482 | + | r0 = returnFunc(ctx, p, size, token) | |
| 483 | + | } else { | |
| 484 | + | r0 = ret.Get(0).(ports.RunnerPage) | |
| 485 | + | } | |
| 486 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, int, string) error); ok { | |
| 487 | + | r1 = returnFunc(ctx, p, size, token) | |
| 488 | + | } else { | |
| 489 | + | r1 = ret.Error(1) | |
| 490 | + | } | |
| 491 | + | return r0, r1 | |
| 492 | + | } | |
| 493 | + | ||
| 494 | + | // RunService_ListRunners_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRunners' | |
| 495 | + | type RunService_ListRunners_Call struct { | |
| 496 | + | *mock.Call | |
| 497 | + | } | |
| 498 | + | ||
| 499 | + | // ListRunners is a helper method to define mock.On call | |
| 500 | + | // - ctx context.Context | |
| 501 | + | // - p domain.Principal | |
| 502 | + | // - size int | |
| 503 | + | // - token string | |
| 504 | + | func (_e *RunService_Expecter) ListRunners(ctx any, p any, size any, token any) *RunService_ListRunners_Call { | |
| 505 | + | return &RunService_ListRunners_Call{Call: _e.mock.On("ListRunners", ctx, p, size, token)} | |
| 506 | + | } | |
| 507 | + | ||
| 508 | + | func (_c *RunService_ListRunners_Call) Run(run func(ctx context.Context, p domain.Principal, size int, token string)) *RunService_ListRunners_Call { | |
| 509 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 510 | + | var arg0 context.Context | |
| 511 | + | if args[0] != nil { | |
| 512 | + | arg0 = args[0].(context.Context) | |
| 513 | + | } | |
| 514 | + | var arg1 domain.Principal | |
| 515 | + | if args[1] != nil { | |
| 516 | + | arg1 = args[1].(domain.Principal) | |
| 517 | + | } | |
| 518 | + | var arg2 int | |
| 519 | + | if args[2] != nil { | |
| 520 | + | arg2 = args[2].(int) | |
| 521 | + | } | |
| 522 | + | var arg3 string | |
| 523 | + | if args[3] != nil { | |
| 524 | + | arg3 = args[3].(string) | |
| 525 | + | } | |
| 526 | + | run( | |
| 527 | + | arg0, | |
| 528 | + | arg1, | |
| 529 | + | arg2, | |
| 530 | + | arg3, | |
| 531 | + | ) | |
| 532 | + | }) | |
| 533 | + | return _c | |
| 534 | + | } | |
| 535 | + | ||
| 536 | + | func (_c *RunService_ListRunners_Call) Return(runnerPage ports.RunnerPage, err error) *RunService_ListRunners_Call { | |
| 537 | + | _c.Call.Return(runnerPage, err) | |
| 538 | + | return _c | |
| 539 | + | } | |
| 540 | + | ||
| 541 | + | func (_c *RunService_ListRunners_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, size int, token string) (ports.RunnerPage, error)) *RunService_ListRunners_Call { | |
| 542 | + | _c.Call.Return(run) | |
| 543 | + | return _c | |
| 544 | + | } | |
| 545 | + | ||
| 546 | + | // ListRuns provides a mock function for the type RunService | |
| 547 | + | func (_mock *RunService) ListRuns(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRunsParams) (ports.RunPage, error) { | |
| 548 | + | ret := _mock.Called(ctx, p, repo, params) | |
| 549 | + | ||
| 550 | + | if len(ret) == 0 { | |
| 551 | + | panic("no return value specified for ListRuns") | |
| 552 | + | } | |
| 553 | + | ||
| 554 | + | var r0 ports.RunPage | |
| 555 | + | var r1 error | |
| 556 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRunsParams) (ports.RunPage, error)); ok { | |
| 557 | + | return returnFunc(ctx, p, repo, params) | |
| 558 | + | } | |
| 559 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRunsParams) ports.RunPage); ok { | |
| 560 | + | r0 = returnFunc(ctx, p, repo, params) | |
| 561 | + | } else { | |
| 562 | + | r0 = ret.Get(0).(ports.RunPage) | |
| 563 | + | } | |
| 564 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRunsParams) error); ok { | |
| 565 | + | r1 = returnFunc(ctx, p, repo, params) | |
| 566 | + | } else { | |
| 567 | + | r1 = ret.Error(1) | |
| 568 | + | } | |
| 569 | + | return r0, r1 | |
| 570 | + | } | |
| 571 | + | ||
| 572 | + | // RunService_ListRuns_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRuns' | |
| 573 | + | type RunService_ListRuns_Call struct { | |
| 574 | + | *mock.Call | |
| 575 | + | } | |
| 576 | + | ||
| 577 | + | // ListRuns is a helper method to define mock.On call | |
| 578 | + | // - ctx context.Context | |
| 579 | + | // - p domain.Principal | |
| 580 | + | // - repo domain.RepoName | |
| 581 | + | // - params ports.ListRunsParams | |
| 582 | + | func (_e *RunService_Expecter) ListRuns(ctx any, p any, repo any, params any) *RunService_ListRuns_Call { | |
| 583 | + | return &RunService_ListRuns_Call{Call: _e.mock.On("ListRuns", ctx, p, repo, params)} | |
| 584 | + | } | |
| 585 | + | ||
| 586 | + | func (_c *RunService_ListRuns_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRunsParams)) *RunService_ListRuns_Call { | |
| 587 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 588 | + | var arg0 context.Context | |
| 589 | + | if args[0] != nil { | |
| 590 | + | arg0 = args[0].(context.Context) | |
| 591 | + | } | |
| 592 | + | var arg1 domain.Principal | |
| 593 | + | if args[1] != nil { | |
| 594 | + | arg1 = args[1].(domain.Principal) | |
| 595 | + | } | |
| 596 | + | var arg2 domain.RepoName | |
| 597 | + | if args[2] != nil { | |
| 598 | + | arg2 = args[2].(domain.RepoName) | |
| 599 | + | } | |
| 600 | + | var arg3 ports.ListRunsParams | |
| 601 | + | if args[3] != nil { | |
| 602 | + | arg3 = args[3].(ports.ListRunsParams) | |
| 603 | + | } | |
| 604 | + | run( | |
| 605 | + | arg0, | |
| 606 | + | arg1, | |
| 607 | + | arg2, | |
| 608 | + | arg3, | |
| 609 | + | ) | |
| 610 | + | }) | |
| 611 | + | return _c | |
| 612 | + | } | |
| 613 | + | ||
| 614 | + | func (_c *RunService_ListRuns_Call) Return(runPage ports.RunPage, err error) *RunService_ListRuns_Call { | |
| 615 | + | _c.Call.Return(runPage, err) | |
| 616 | + | return _c | |
| 617 | + | } | |
| 618 | + | ||
| 619 | + | func (_c *RunService_ListRuns_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRunsParams) (ports.RunPage, error)) *RunService_ListRuns_Call { | |
| 620 | + | _c.Call.Return(run) | |
| 621 | + | return _c | |
| 622 | + | } | |
| 623 | + | ||
| 624 | + | // RemoveRunner provides a mock function for the type RunService | |
| 625 | + | func (_mock *RunService) RemoveRunner(ctx context.Context, p domain.Principal, id string) error { | |
| 626 | + | ret := _mock.Called(ctx, p, id) | |
| 627 | + | ||
| 628 | + | if len(ret) == 0 { | |
| 629 | + | panic("no return value specified for RemoveRunner") | |
| 630 | + | } | |
| 631 | + | ||
| 632 | + | var r0 error | |
| 633 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, string) error); ok { | |
| 634 | + | r0 = returnFunc(ctx, p, id) | |
| 635 | + | } else { | |
| 636 | + | r0 = ret.Error(0) | |
| 637 | + | } | |
| 638 | + | return r0 | |
| 639 | + | } | |
| 640 | + | ||
| 641 | + | // RunService_RemoveRunner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveRunner' | |
| 642 | + | type RunService_RemoveRunner_Call struct { | |
| 643 | + | *mock.Call | |
| 644 | + | } | |
| 645 | + | ||
| 646 | + | // RemoveRunner is a helper method to define mock.On call | |
| 647 | + | // - ctx context.Context | |
| 648 | + | // - p domain.Principal | |
| 649 | + | // - id string | |
| 650 | + | func (_e *RunService_Expecter) RemoveRunner(ctx any, p any, id any) *RunService_RemoveRunner_Call { | |
| 651 | + | return &RunService_RemoveRunner_Call{Call: _e.mock.On("RemoveRunner", ctx, p, id)} | |
| 652 | + | } | |
| 653 | + | ||
| 654 | + | func (_c *RunService_RemoveRunner_Call) Run(run func(ctx context.Context, p domain.Principal, id string)) *RunService_RemoveRunner_Call { | |
| 655 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 656 | + | var arg0 context.Context | |
| 657 | + | if args[0] != nil { | |
| 658 | + | arg0 = args[0].(context.Context) | |
| 659 | + | } | |
| 660 | + | var arg1 domain.Principal | |
| 661 | + | if args[1] != nil { | |
| 662 | + | arg1 = args[1].(domain.Principal) | |
| 663 | + | } | |
| 664 | + | var arg2 string | |
| 665 | + | if args[2] != nil { | |
| 666 | + | arg2 = args[2].(string) | |
| 667 | + | } | |
| 668 | + | run( | |
| 669 | + | arg0, | |
| 670 | + | arg1, | |
| 671 | + | arg2, | |
| 672 | + | ) | |
| 673 | + | }) | |
| 674 | + | return _c | |
| 675 | + | } | |
| 676 | + | ||
| 677 | + | func (_c *RunService_RemoveRunner_Call) Return(err error) *RunService_RemoveRunner_Call { | |
| 678 | + | _c.Call.Return(err) | |
| 679 | + | return _c | |
| 680 | + | } | |
| 681 | + | ||
| 682 | + | func (_c *RunService_RemoveRunner_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, id string) error) *RunService_RemoveRunner_Call { | |
| 683 | + | _c.Call.Return(run) | |
| 684 | + | return _c | |
| 685 | + | } | |
| 686 | + | ||
| 687 | + | // RerunRun provides a mock function for the type RunService | |
| 688 | + | func (_mock *RunService) RerunRun(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, failedOnly bool) (domain.CIRun, error) { | |
| 689 | + | ret := _mock.Called(ctx, p, repo, runID, failedOnly) | |
| 690 | + | ||
| 691 | + | if len(ret) == 0 { | |
| 692 | + | panic("no return value specified for RerunRun") | |
| 693 | + | } | |
| 694 | + | ||
| 695 | + | var r0 domain.CIRun | |
| 696 | + | var r1 error | |
| 697 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) (domain.CIRun, error)); ok { | |
| 698 | + | return returnFunc(ctx, p, repo, runID, failedOnly) | |
| 699 | + | } | |
| 700 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) domain.CIRun); ok { | |
| 701 | + | r0 = returnFunc(ctx, p, repo, runID, failedOnly) | |
| 702 | + | } else { | |
| 703 | + | r0 = ret.Get(0).(domain.CIRun) | |
| 704 | + | } | |
| 705 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) error); ok { | |
| 706 | + | r1 = returnFunc(ctx, p, repo, runID, failedOnly) | |
| 707 | + | } else { | |
| 708 | + | r1 = ret.Error(1) | |
| 709 | + | } | |
| 710 | + | return r0, r1 | |
| 711 | + | } | |
| 712 | + | ||
| 713 | + | // RunService_RerunRun_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RerunRun' | |
| 714 | + | type RunService_RerunRun_Call struct { | |
| 715 | + | *mock.Call | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | // RerunRun is a helper method to define mock.On call | |
| 719 | + | // - ctx context.Context | |
| 720 | + | // - p domain.Principal | |
| 721 | + | // - repo domain.RepoName | |
| 722 | + | // - runID int64 | |
| 723 | + | // - failedOnly bool | |
| 724 | + | func (_e *RunService_Expecter) RerunRun(ctx any, p any, repo any, runID any, failedOnly any) *RunService_RerunRun_Call { | |
| 725 | + | return &RunService_RerunRun_Call{Call: _e.mock.On("RerunRun", ctx, p, repo, runID, failedOnly)} | |
| 726 | + | } | |
| 727 | + | ||
| 728 | + | func (_c *RunService_RerunRun_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, failedOnly bool)) *RunService_RerunRun_Call { | |
| 729 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 730 | + | var arg0 context.Context | |
| 731 | + | if args[0] != nil { | |
| 732 | + | arg0 = args[0].(context.Context) | |
| 733 | + | } | |
| 734 | + | var arg1 domain.Principal | |
| 735 | + | if args[1] != nil { | |
| 736 | + | arg1 = args[1].(domain.Principal) | |
| 737 | + | } | |
| 738 | + | var arg2 domain.RepoName | |
| 739 | + | if args[2] != nil { | |
| 740 | + | arg2 = args[2].(domain.RepoName) | |
| 741 | + | } | |
| 742 | + | var arg3 int64 | |
| 743 | + | if args[3] != nil { | |
| 744 | + | arg3 = args[3].(int64) | |
| 745 | + | } | |
| 746 | + | var arg4 bool | |
| 747 | + | if args[4] != nil { | |
| 748 | + | arg4 = args[4].(bool) | |
| 749 | + | } | |
| 750 | + | run( | |
| 751 | + | arg0, | |
| 752 | + | arg1, | |
| 753 | + | arg2, | |
| 754 | + | arg3, | |
| 755 | + | arg4, | |
| 756 | + | ) | |
| 757 | + | }) | |
| 758 | + | return _c | |
| 759 | + | } | |
| 760 | + | ||
| 761 | + | func (_c *RunService_RerunRun_Call) Return(cIRun domain.CIRun, err error) *RunService_RerunRun_Call { | |
| 762 | + | _c.Call.Return(cIRun, err) | |
| 763 | + | return _c | |
| 764 | + | } | |
| 765 | + | ||
| 766 | + | func (_c *RunService_RerunRun_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, runID int64, failedOnly bool) (domain.CIRun, error)) *RunService_RerunRun_Call { | |
| 767 | + | _c.Call.Return(run) | |
| 768 | + | return _c | |
| 769 | + | } | |
A internal/ports/mocks/RunnerService.go +556 -0
| @@ -0,0 +1,556 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | "bnema.dev/ermite/internal/ports" | |
| 12 | + | mock "github.com/stretchr/testify/mock" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // NewRunnerService creates a new instance of RunnerService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 16 | + | // The first argument is typically a *testing.T value. | |
| 17 | + | func NewRunnerService(t interface { | |
| 18 | + | mock.TestingT | |
| 19 | + | Cleanup(func()) | |
| 20 | + | }) *RunnerService { | |
| 21 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 22 | + | helper.Helper() | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | mock := &RunnerService{} | |
| 26 | + | mock.Mock.Test(t) | |
| 27 | + | ||
| 28 | + | t.Cleanup(func() { | |
| 29 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 30 | + | helper.Helper() | |
| 31 | + | } | |
| 32 | + | mock.AssertExpectations(t) | |
| 33 | + | }) | |
| 34 | + | ||
| 35 | + | return mock | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // RunnerService is an autogenerated mock type for the RunnerService type | |
| 39 | + | type RunnerService struct { | |
| 40 | + | mock.Mock | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | type RunnerService_Expecter struct { | |
| 44 | + | mock *mock.Mock | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | func (_m *RunnerService) EXPECT() *RunnerService_Expecter { | |
| 48 | + | return &RunnerService_Expecter{mock: &_m.Mock} | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // AppendLog provides a mock function for the type RunnerService | |
| 52 | + | func (_mock *RunnerService) AppendLog(ctx context.Context, secret string, jobID string, step int32, offset int64, lines []domain.CILogLine) (int64, error) { | |
| 53 | + | ret := _mock.Called(ctx, secret, jobID, step, offset, lines) | |
| 54 | + | ||
| 55 | + | if len(ret) == 0 { | |
| 56 | + | panic("no return value specified for AppendLog") | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | var r0 int64 | |
| 60 | + | var r1 error | |
| 61 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, int32, int64, []domain.CILogLine) (int64, error)); ok { | |
| 62 | + | return returnFunc(ctx, secret, jobID, step, offset, lines) | |
| 63 | + | } | |
| 64 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, int32, int64, []domain.CILogLine) int64); ok { | |
| 65 | + | r0 = returnFunc(ctx, secret, jobID, step, offset, lines) | |
| 66 | + | } else { | |
| 67 | + | r0 = ret.Get(0).(int64) | |
| 68 | + | } | |
| 69 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, int32, int64, []domain.CILogLine) error); ok { | |
| 70 | + | r1 = returnFunc(ctx, secret, jobID, step, offset, lines) | |
| 71 | + | } else { | |
| 72 | + | r1 = ret.Error(1) | |
| 73 | + | } | |
| 74 | + | return r0, r1 | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | // RunnerService_AppendLog_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AppendLog' | |
| 78 | + | type RunnerService_AppendLog_Call struct { | |
| 79 | + | *mock.Call | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | // AppendLog is a helper method to define mock.On call | |
| 83 | + | // - ctx context.Context | |
| 84 | + | // - secret string | |
| 85 | + | // - jobID string | |
| 86 | + | // - step int32 | |
| 87 | + | // - offset int64 | |
| 88 | + | // - lines []domain.CILogLine | |
| 89 | + | func (_e *RunnerService_Expecter) AppendLog(ctx any, secret any, jobID any, step any, offset any, lines any) *RunnerService_AppendLog_Call { | |
| 90 | + | return &RunnerService_AppendLog_Call{Call: _e.mock.On("AppendLog", ctx, secret, jobID, step, offset, lines)} | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | func (_c *RunnerService_AppendLog_Call) Run(run func(ctx context.Context, secret string, jobID string, step int32, offset int64, lines []domain.CILogLine)) *RunnerService_AppendLog_Call { | |
| 94 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 95 | + | var arg0 context.Context | |
| 96 | + | if args[0] != nil { | |
| 97 | + | arg0 = args[0].(context.Context) | |
| 98 | + | } | |
| 99 | + | var arg1 string | |
| 100 | + | if args[1] != nil { | |
| 101 | + | arg1 = args[1].(string) | |
| 102 | + | } | |
| 103 | + | var arg2 string | |
| 104 | + | if args[2] != nil { | |
| 105 | + | arg2 = args[2].(string) | |
| 106 | + | } | |
| 107 | + | var arg3 int32 | |
| 108 | + | if args[3] != nil { | |
| 109 | + | arg3 = args[3].(int32) | |
| 110 | + | } | |
| 111 | + | var arg4 int64 | |
| 112 | + | if args[4] != nil { | |
| 113 | + | arg4 = args[4].(int64) | |
| 114 | + | } | |
| 115 | + | var arg5 []domain.CILogLine | |
| 116 | + | if args[5] != nil { | |
| 117 | + | arg5 = args[5].([]domain.CILogLine) | |
| 118 | + | } | |
| 119 | + | run( | |
| 120 | + | arg0, | |
| 121 | + | arg1, | |
| 122 | + | arg2, | |
| 123 | + | arg3, | |
| 124 | + | arg4, | |
| 125 | + | arg5, | |
| 126 | + | ) | |
| 127 | + | }) | |
| 128 | + | return _c | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | func (_c *RunnerService_AppendLog_Call) Return(next int64, err error) *RunnerService_AppendLog_Call { | |
| 132 | + | _c.Call.Return(next, err) | |
| 133 | + | return _c | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | func (_c *RunnerService_AppendLog_Call) RunAndReturn(run func(ctx context.Context, secret string, jobID string, step int32, offset int64, lines []domain.CILogLine) (int64, error)) *RunnerService_AppendLog_Call { | |
| 137 | + | _c.Call.Return(run) | |
| 138 | + | return _c | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | // CompleteJob provides a mock function for the type RunnerService | |
| 142 | + | func (_mock *RunnerService) CompleteJob(ctx context.Context, secret string, params ports.CompleteJobParams) (ports.CompleteJobResult, error) { | |
| 143 | + | ret := _mock.Called(ctx, secret, params) | |
| 144 | + | ||
| 145 | + | if len(ret) == 0 { | |
| 146 | + | panic("no return value specified for CompleteJob") | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | var r0 ports.CompleteJobResult | |
| 150 | + | var r1 error | |
| 151 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, ports.CompleteJobParams) (ports.CompleteJobResult, error)); ok { | |
| 152 | + | return returnFunc(ctx, secret, params) | |
| 153 | + | } | |
| 154 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, ports.CompleteJobParams) ports.CompleteJobResult); ok { | |
| 155 | + | r0 = returnFunc(ctx, secret, params) | |
| 156 | + | } else { | |
| 157 | + | r0 = ret.Get(0).(ports.CompleteJobResult) | |
| 158 | + | } | |
| 159 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string, ports.CompleteJobParams) error); ok { | |
| 160 | + | r1 = returnFunc(ctx, secret, params) | |
| 161 | + | } else { | |
| 162 | + | r1 = ret.Error(1) | |
| 163 | + | } | |
| 164 | + | return r0, r1 | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | // RunnerService_CompleteJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CompleteJob' | |
| 168 | + | type RunnerService_CompleteJob_Call struct { | |
| 169 | + | *mock.Call | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | // CompleteJob is a helper method to define mock.On call | |
| 173 | + | // - ctx context.Context | |
| 174 | + | // - secret string | |
| 175 | + | // - params ports.CompleteJobParams | |
| 176 | + | func (_e *RunnerService_Expecter) CompleteJob(ctx any, secret any, params any) *RunnerService_CompleteJob_Call { | |
| 177 | + | return &RunnerService_CompleteJob_Call{Call: _e.mock.On("CompleteJob", ctx, secret, params)} | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | func (_c *RunnerService_CompleteJob_Call) Run(run func(ctx context.Context, secret string, params ports.CompleteJobParams)) *RunnerService_CompleteJob_Call { | |
| 181 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 182 | + | var arg0 context.Context | |
| 183 | + | if args[0] != nil { | |
| 184 | + | arg0 = args[0].(context.Context) | |
| 185 | + | } | |
| 186 | + | var arg1 string | |
| 187 | + | if args[1] != nil { | |
| 188 | + | arg1 = args[1].(string) | |
| 189 | + | } | |
| 190 | + | var arg2 ports.CompleteJobParams | |
| 191 | + | if args[2] != nil { | |
| 192 | + | arg2 = args[2].(ports.CompleteJobParams) | |
| 193 | + | } | |
| 194 | + | run( | |
| 195 | + | arg0, | |
| 196 | + | arg1, | |
| 197 | + | arg2, | |
| 198 | + | ) | |
| 199 | + | }) | |
| 200 | + | return _c | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | func (_c *RunnerService_CompleteJob_Call) Return(completeJobResult ports.CompleteJobResult, err error) *RunnerService_CompleteJob_Call { | |
| 204 | + | _c.Call.Return(completeJobResult, err) | |
| 205 | + | return _c | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | func (_c *RunnerService_CompleteJob_Call) RunAndReturn(run func(ctx context.Context, secret string, params ports.CompleteJobParams) (ports.CompleteJobResult, error)) *RunnerService_CompleteJob_Call { | |
| 209 | + | _c.Call.Return(run) | |
| 210 | + | return _c | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | // FetchJob provides a mock function for the type RunnerService | |
| 214 | + | func (_mock *RunnerService) FetchJob(ctx context.Context, secret string, params ports.FetchJobParams) (ports.FetchJobResult, error) { | |
| 215 | + | ret := _mock.Called(ctx, secret, params) | |
| 216 | + | ||
| 217 | + | if len(ret) == 0 { | |
| 218 | + | panic("no return value specified for FetchJob") | |
| 219 | + | } | |
| 220 | + | ||
| 221 | + | var r0 ports.FetchJobResult | |
| 222 | + | var r1 error | |
| 223 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, ports.FetchJobParams) (ports.FetchJobResult, error)); ok { | |
| 224 | + | return returnFunc(ctx, secret, params) | |
| 225 | + | } | |
| 226 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, ports.FetchJobParams) ports.FetchJobResult); ok { | |
| 227 | + | r0 = returnFunc(ctx, secret, params) | |
| 228 | + | } else { | |
| 229 | + | r0 = ret.Get(0).(ports.FetchJobResult) | |
| 230 | + | } | |
| 231 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string, ports.FetchJobParams) error); ok { | |
| 232 | + | r1 = returnFunc(ctx, secret, params) | |
| 233 | + | } else { | |
| 234 | + | r1 = ret.Error(1) | |
| 235 | + | } | |
| 236 | + | return r0, r1 | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | // RunnerService_FetchJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FetchJob' | |
| 240 | + | type RunnerService_FetchJob_Call struct { | |
| 241 | + | *mock.Call | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | // FetchJob is a helper method to define mock.On call | |
| 245 | + | // - ctx context.Context | |
| 246 | + | // - secret string | |
| 247 | + | // - params ports.FetchJobParams | |
| 248 | + | func (_e *RunnerService_Expecter) FetchJob(ctx any, secret any, params any) *RunnerService_FetchJob_Call { | |
| 249 | + | return &RunnerService_FetchJob_Call{Call: _e.mock.On("FetchJob", ctx, secret, params)} | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | func (_c *RunnerService_FetchJob_Call) Run(run func(ctx context.Context, secret string, params ports.FetchJobParams)) *RunnerService_FetchJob_Call { | |
| 253 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 254 | + | var arg0 context.Context | |
| 255 | + | if args[0] != nil { | |
| 256 | + | arg0 = args[0].(context.Context) | |
| 257 | + | } | |
| 258 | + | var arg1 string | |
| 259 | + | if args[1] != nil { | |
| 260 | + | arg1 = args[1].(string) | |
| 261 | + | } | |
| 262 | + | var arg2 ports.FetchJobParams | |
| 263 | + | if args[2] != nil { | |
| 264 | + | arg2 = args[2].(ports.FetchJobParams) | |
| 265 | + | } | |
| 266 | + | run( | |
| 267 | + | arg0, | |
| 268 | + | arg1, | |
| 269 | + | arg2, | |
| 270 | + | ) | |
| 271 | + | }) | |
| 272 | + | return _c | |
| 273 | + | } | |
| 274 | + | ||
| 275 | + | func (_c *RunnerService_FetchJob_Call) Return(fetchJobResult ports.FetchJobResult, err error) *RunnerService_FetchJob_Call { | |
| 276 | + | _c.Call.Return(fetchJobResult, err) | |
| 277 | + | return _c | |
| 278 | + | } | |
| 279 | + | ||
| 280 | + | func (_c *RunnerService_FetchJob_Call) RunAndReturn(run func(ctx context.Context, secret string, params ports.FetchJobParams) (ports.FetchJobResult, error)) *RunnerService_FetchJob_Call { | |
| 281 | + | _c.Call.Return(run) | |
| 282 | + | return _c | |
| 283 | + | } | |
| 284 | + | ||
| 285 | + | // Heartbeat provides a mock function for the type RunnerService | |
| 286 | + | func (_mock *RunnerService) Heartbeat(ctx context.Context, secret string, jobID string) error { | |
| 287 | + | ret := _mock.Called(ctx, secret, jobID) | |
| 288 | + | ||
| 289 | + | if len(ret) == 0 { | |
| 290 | + | panic("no return value specified for Heartbeat") | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | var r0 error | |
| 294 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) error); ok { | |
| 295 | + | r0 = returnFunc(ctx, secret, jobID) | |
| 296 | + | } else { | |
| 297 | + | r0 = ret.Error(0) | |
| 298 | + | } | |
| 299 | + | return r0 | |
| 300 | + | } | |
| 301 | + | ||
| 302 | + | // RunnerService_Heartbeat_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Heartbeat' | |
| 303 | + | type RunnerService_Heartbeat_Call struct { | |
| 304 | + | *mock.Call | |
| 305 | + | } | |
| 306 | + | ||
| 307 | + | // Heartbeat is a helper method to define mock.On call | |
| 308 | + | // - ctx context.Context | |
| 309 | + | // - secret string | |
| 310 | + | // - jobID string | |
| 311 | + | func (_e *RunnerService_Expecter) Heartbeat(ctx any, secret any, jobID any) *RunnerService_Heartbeat_Call { | |
| 312 | + | return &RunnerService_Heartbeat_Call{Call: _e.mock.On("Heartbeat", ctx, secret, jobID)} | |
| 313 | + | } | |
| 314 | + | ||
| 315 | + | func (_c *RunnerService_Heartbeat_Call) Run(run func(ctx context.Context, secret string, jobID string)) *RunnerService_Heartbeat_Call { | |
| 316 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 317 | + | var arg0 context.Context | |
| 318 | + | if args[0] != nil { | |
| 319 | + | arg0 = args[0].(context.Context) | |
| 320 | + | } | |
| 321 | + | var arg1 string | |
| 322 | + | if args[1] != nil { | |
| 323 | + | arg1 = args[1].(string) | |
| 324 | + | } | |
| 325 | + | var arg2 string | |
| 326 | + | if args[2] != nil { | |
| 327 | + | arg2 = args[2].(string) | |
| 328 | + | } | |
| 329 | + | run( | |
| 330 | + | arg0, | |
| 331 | + | arg1, | |
| 332 | + | arg2, | |
| 333 | + | ) | |
| 334 | + | }) | |
| 335 | + | return _c | |
| 336 | + | } | |
| 337 | + | ||
| 338 | + | func (_c *RunnerService_Heartbeat_Call) Return(err error) *RunnerService_Heartbeat_Call { | |
| 339 | + | _c.Call.Return(err) | |
| 340 | + | return _c | |
| 341 | + | } | |
| 342 | + | ||
| 343 | + | func (_c *RunnerService_Heartbeat_Call) RunAndReturn(run func(ctx context.Context, secret string, jobID string) error) *RunnerService_Heartbeat_Call { | |
| 344 | + | _c.Call.Return(run) | |
| 345 | + | return _c | |
| 346 | + | } | |
| 347 | + | ||
| 348 | + | // Register provides a mock function for the type RunnerService | |
| 349 | + | func (_mock *RunnerService) Register(ctx context.Context, p domain.Principal, params ports.RegisterRunnerParams) (ports.RegisteredRunner, error) { | |
| 350 | + | ret := _mock.Called(ctx, p, params) | |
| 351 | + | ||
| 352 | + | if len(ret) == 0 { | |
| 353 | + | panic("no return value specified for Register") | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | var r0 ports.RegisteredRunner | |
| 357 | + | var r1 error | |
| 358 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.RegisterRunnerParams) (ports.RegisteredRunner, error)); ok { | |
| 359 | + | return returnFunc(ctx, p, params) | |
| 360 | + | } | |
| 361 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.RegisterRunnerParams) ports.RegisteredRunner); ok { | |
| 362 | + | r0 = returnFunc(ctx, p, params) | |
| 363 | + | } else { | |
| 364 | + | r0 = ret.Get(0).(ports.RegisteredRunner) | |
| 365 | + | } | |
| 366 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, ports.RegisterRunnerParams) error); ok { | |
| 367 | + | r1 = returnFunc(ctx, p, params) | |
| 368 | + | } else { | |
| 369 | + | r1 = ret.Error(1) | |
| 370 | + | } | |
| 371 | + | return r0, r1 | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | // RunnerService_Register_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Register' | |
| 375 | + | type RunnerService_Register_Call struct { | |
| 376 | + | *mock.Call | |
| 377 | + | } | |
| 378 | + | ||
| 379 | + | // Register is a helper method to define mock.On call | |
| 380 | + | // - ctx context.Context | |
| 381 | + | // - p domain.Principal | |
| 382 | + | // - params ports.RegisterRunnerParams | |
| 383 | + | func (_e *RunnerService_Expecter) Register(ctx any, p any, params any) *RunnerService_Register_Call { | |
| 384 | + | return &RunnerService_Register_Call{Call: _e.mock.On("Register", ctx, p, params)} | |
| 385 | + | } | |
| 386 | + | ||
| 387 | + | func (_c *RunnerService_Register_Call) Run(run func(ctx context.Context, p domain.Principal, params ports.RegisterRunnerParams)) *RunnerService_Register_Call { | |
| 388 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 389 | + | var arg0 context.Context | |
| 390 | + | if args[0] != nil { | |
| 391 | + | arg0 = args[0].(context.Context) | |
| 392 | + | } | |
| 393 | + | var arg1 domain.Principal | |
| 394 | + | if args[1] != nil { | |
| 395 | + | arg1 = args[1].(domain.Principal) | |
| 396 | + | } | |
| 397 | + | var arg2 ports.RegisterRunnerParams | |
| 398 | + | if args[2] != nil { | |
| 399 | + | arg2 = args[2].(ports.RegisterRunnerParams) | |
| 400 | + | } | |
| 401 | + | run( | |
| 402 | + | arg0, | |
| 403 | + | arg1, | |
| 404 | + | arg2, | |
| 405 | + | ) | |
| 406 | + | }) | |
| 407 | + | return _c | |
| 408 | + | } | |
| 409 | + | ||
| 410 | + | func (_c *RunnerService_Register_Call) Return(registeredRunner ports.RegisteredRunner, err error) *RunnerService_Register_Call { | |
| 411 | + | _c.Call.Return(registeredRunner, err) | |
| 412 | + | return _c | |
| 413 | + | } | |
| 414 | + | ||
| 415 | + | func (_c *RunnerService_Register_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params ports.RegisterRunnerParams) (ports.RegisteredRunner, error)) *RunnerService_Register_Call { | |
| 416 | + | _c.Call.Return(run) | |
| 417 | + | return _c | |
| 418 | + | } | |
| 419 | + | ||
| 420 | + | // UpdateJob provides a mock function for the type RunnerService | |
| 421 | + | func (_mock *RunnerService) UpdateJob(ctx context.Context, secret string, jobID string, steps []ports.StepUpdate) error { | |
| 422 | + | ret := _mock.Called(ctx, secret, jobID, steps) | |
| 423 | + | ||
| 424 | + | if len(ret) == 0 { | |
| 425 | + | panic("no return value specified for UpdateJob") | |
| 426 | + | } | |
| 427 | + | ||
| 428 | + | var r0 error | |
| 429 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, []ports.StepUpdate) error); ok { | |
| 430 | + | r0 = returnFunc(ctx, secret, jobID, steps) | |
| 431 | + | } else { | |
| 432 | + | r0 = ret.Error(0) | |
| 433 | + | } | |
| 434 | + | return r0 | |
| 435 | + | } | |
| 436 | + | ||
| 437 | + | // RunnerService_UpdateJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateJob' | |
| 438 | + | type RunnerService_UpdateJob_Call struct { | |
| 439 | + | *mock.Call | |
| 440 | + | } | |
| 441 | + | ||
| 442 | + | // UpdateJob is a helper method to define mock.On call | |
| 443 | + | // - ctx context.Context | |
| 444 | + | // - secret string | |
| 445 | + | // - jobID string | |
| 446 | + | // - steps []ports.StepUpdate | |
| 447 | + | func (_e *RunnerService_Expecter) UpdateJob(ctx any, secret any, jobID any, steps any) *RunnerService_UpdateJob_Call { | |
| 448 | + | return &RunnerService_UpdateJob_Call{Call: _e.mock.On("UpdateJob", ctx, secret, jobID, steps)} | |
| 449 | + | } | |
| 450 | + | ||
| 451 | + | func (_c *RunnerService_UpdateJob_Call) Run(run func(ctx context.Context, secret string, jobID string, steps []ports.StepUpdate)) *RunnerService_UpdateJob_Call { | |
| 452 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 453 | + | var arg0 context.Context | |
| 454 | + | if args[0] != nil { | |
| 455 | + | arg0 = args[0].(context.Context) | |
| 456 | + | } | |
| 457 | + | var arg1 string | |
| 458 | + | if args[1] != nil { | |
| 459 | + | arg1 = args[1].(string) | |
| 460 | + | } | |
| 461 | + | var arg2 string | |
| 462 | + | if args[2] != nil { | |
| 463 | + | arg2 = args[2].(string) | |
| 464 | + | } | |
| 465 | + | var arg3 []ports.StepUpdate | |
| 466 | + | if args[3] != nil { | |
| 467 | + | arg3 = args[3].([]ports.StepUpdate) | |
| 468 | + | } | |
| 469 | + | run( | |
| 470 | + | arg0, | |
| 471 | + | arg1, | |
| 472 | + | arg2, | |
| 473 | + | arg3, | |
| 474 | + | ) | |
| 475 | + | }) | |
| 476 | + | return _c | |
| 477 | + | } | |
| 478 | + | ||
| 479 | + | func (_c *RunnerService_UpdateJob_Call) Return(err error) *RunnerService_UpdateJob_Call { | |
| 480 | + | _c.Call.Return(err) | |
| 481 | + | return _c | |
| 482 | + | } | |
| 483 | + | ||
| 484 | + | func (_c *RunnerService_UpdateJob_Call) RunAndReturn(run func(ctx context.Context, secret string, jobID string, steps []ports.StepUpdate) error) *RunnerService_UpdateJob_Call { | |
| 485 | + | _c.Call.Return(run) | |
| 486 | + | return _c | |
| 487 | + | } | |
| 488 | + | ||
| 489 | + | // WatchJob provides a mock function for the type RunnerService | |
| 490 | + | func (_mock *RunnerService) WatchJob(ctx context.Context, secret string, jobID string, send func(ports.JobSignal) error) error { | |
| 491 | + | ret := _mock.Called(ctx, secret, jobID, send) | |
| 492 | + | ||
| 493 | + | if len(ret) == 0 { | |
| 494 | + | panic("no return value specified for WatchJob") | |
| 495 | + | } | |
| 496 | + | ||
| 497 | + | var r0 error | |
| 498 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, func(ports.JobSignal) error) error); ok { | |
| 499 | + | r0 = returnFunc(ctx, secret, jobID, send) | |
| 500 | + | } else { | |
| 501 | + | r0 = ret.Error(0) | |
| 502 | + | } | |
| 503 | + | return r0 | |
| 504 | + | } | |
| 505 | + | ||
| 506 | + | // RunnerService_WatchJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchJob' | |
| 507 | + | type RunnerService_WatchJob_Call struct { | |
| 508 | + | *mock.Call | |
| 509 | + | } | |
| 510 | + | ||
| 511 | + | // WatchJob is a helper method to define mock.On call | |
| 512 | + | // - ctx context.Context | |
| 513 | + | // - secret string | |
| 514 | + | // - jobID string | |
| 515 | + | // - send func(ports.JobSignal) error | |
| 516 | + | func (_e *RunnerService_Expecter) WatchJob(ctx any, secret any, jobID any, send any) *RunnerService_WatchJob_Call { | |
| 517 | + | return &RunnerService_WatchJob_Call{Call: _e.mock.On("WatchJob", ctx, secret, jobID, send)} | |
| 518 | + | } | |
| 519 | + | ||
| 520 | + | func (_c *RunnerService_WatchJob_Call) Run(run func(ctx context.Context, secret string, jobID string, send func(ports.JobSignal) error)) *RunnerService_WatchJob_Call { | |
| 521 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 522 | + | var arg0 context.Context | |
| 523 | + | if args[0] != nil { | |
| 524 | + | arg0 = args[0].(context.Context) | |
| 525 | + | } | |
| 526 | + | var arg1 string | |
| 527 | + | if args[1] != nil { | |
| 528 | + | arg1 = args[1].(string) | |
| 529 | + | } | |
| 530 | + | var arg2 string | |
| 531 | + | if args[2] != nil { | |
| 532 | + | arg2 = args[2].(string) | |
| 533 | + | } | |
| 534 | + | var arg3 func(ports.JobSignal) error | |
| 535 | + | if args[3] != nil { | |
| 536 | + | arg3 = args[3].(func(ports.JobSignal) error) | |
| 537 | + | } | |
| 538 | + | run( | |
| 539 | + | arg0, | |
| 540 | + | arg1, | |
| 541 | + | arg2, | |
| 542 | + | arg3, | |
| 543 | + | ) | |
| 544 | + | }) | |
| 545 | + | return _c | |
| 546 | + | } | |
| 547 | + | ||
| 548 | + | func (_c *RunnerService_WatchJob_Call) Return(err error) *RunnerService_WatchJob_Call { | |
| 549 | + | _c.Call.Return(err) | |
| 550 | + | return _c | |
| 551 | + | } | |
| 552 | + | ||
| 553 | + | func (_c *RunnerService_WatchJob_Call) RunAndReturn(run func(ctx context.Context, secret string, jobID string, send func(ports.JobSignal) error) error) *RunnerService_WatchJob_Call { | |
| 554 | + | _c.Call.Return(run) | |
| 555 | + | return _c | |
| 556 | + | } | |
A internal/ports/mocks/WorkflowTrigger.go +106 -0
| @@ -0,0 +1,106 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewWorkflowTrigger creates a new instance of WorkflowTrigger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewWorkflowTrigger(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *WorkflowTrigger { | |
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 21 | + | helper.Helper() | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | mock := &WorkflowTrigger{} | |
| 25 | + | mock.Mock.Test(t) | |
| 26 | + | ||
| 27 | + | t.Cleanup(func() { | |
| 28 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 29 | + | helper.Helper() | |
| 30 | + | } | |
| 31 | + | mock.AssertExpectations(t) | |
| 32 | + | }) | |
| 33 | + | ||
| 34 | + | return mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | // WorkflowTrigger is an autogenerated mock type for the WorkflowTrigger type | |
| 38 | + | type WorkflowTrigger struct { | |
| 39 | + | mock.Mock | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | type WorkflowTrigger_Expecter struct { | |
| 43 | + | mock *mock.Mock | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | func (_m *WorkflowTrigger) EXPECT() *WorkflowTrigger_Expecter { | |
| 47 | + | return &WorkflowTrigger_Expecter{mock: &_m.Mock} | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // OnPush provides a mock function for the type WorkflowTrigger | |
| 51 | + | func (_mock *WorkflowTrigger) OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate, pusher domain.Principal) { | |
| 52 | + | _mock.Called(ctx, repo, updates, pusher) | |
| 53 | + | return | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // WorkflowTrigger_OnPush_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnPush' | |
| 57 | + | type WorkflowTrigger_OnPush_Call struct { | |
| 58 | + | *mock.Call | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // OnPush is a helper method to define mock.On call | |
| 62 | + | // - ctx context.Context | |
| 63 | + | // - repo domain.RepoName | |
| 64 | + | // - updates []domain.RefUpdate | |
| 65 | + | // - pusher domain.Principal | |
| 66 | + | func (_e *WorkflowTrigger_Expecter) OnPush(ctx any, repo any, updates any, pusher any) *WorkflowTrigger_OnPush_Call { | |
| 67 | + | return &WorkflowTrigger_OnPush_Call{Call: _e.mock.On("OnPush", ctx, repo, updates, pusher)} | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func (_c *WorkflowTrigger_OnPush_Call) Run(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate, pusher domain.Principal)) *WorkflowTrigger_OnPush_Call { | |
| 71 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 72 | + | var arg0 context.Context | |
| 73 | + | if args[0] != nil { | |
| 74 | + | arg0 = args[0].(context.Context) | |
| 75 | + | } | |
| 76 | + | var arg1 domain.RepoName | |
| 77 | + | if args[1] != nil { | |
| 78 | + | arg1 = args[1].(domain.RepoName) | |
| 79 | + | } | |
| 80 | + | var arg2 []domain.RefUpdate | |
| 81 | + | if args[2] != nil { | |
| 82 | + | arg2 = args[2].([]domain.RefUpdate) | |
| 83 | + | } | |
| 84 | + | var arg3 domain.Principal | |
| 85 | + | if args[3] != nil { | |
| 86 | + | arg3 = args[3].(domain.Principal) | |
| 87 | + | } | |
| 88 | + | run( | |
| 89 | + | arg0, | |
| 90 | + | arg1, | |
| 91 | + | arg2, | |
| 92 | + | arg3, | |
| 93 | + | ) | |
| 94 | + | }) | |
| 95 | + | return _c | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | func (_c *WorkflowTrigger_OnPush_Call) Return() *WorkflowTrigger_OnPush_Call { | |
| 99 | + | _c.Call.Return() | |
| 100 | + | return _c | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | func (_c *WorkflowTrigger_OnPush_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate, pusher domain.Principal)) *WorkflowTrigger_OnPush_Call { | |
| 104 | + | _c.Run(run) | |
| 105 | + | return _c | |
| 106 | + | } | |
A proto/ermite/storage/ci.proto +194 -0
| @@ -0,0 +1,194 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | // Internal, unversioned storage schema of ermite: CI runs, jobs, checks and | |
| 4 | + | // runners (ADR 0025). Logs are not stored here: they are files under | |
| 5 | + | // $ERMITE_DATA_DIR/ci/logs. | |
| 6 | + | ||
| 7 | + | syntax = "proto3"; | |
| 8 | + | ||
| 9 | + | package ermite.storage; | |
| 10 | + | ||
| 11 | + | import "google/protobuf/timestamp.proto"; | |
| 12 | + | ||
| 13 | + | option go_package = "bnema.dev/ermite/gen/ermite/storage;storagepb"; | |
| 14 | + | ||
| 15 | + | // CIStatus is where a run, a job or a check is in its life. | |
| 16 | + | enum CIStatus { | |
| 17 | + | CI_STATUS_UNSPECIFIED = 0; | |
| 18 | + | CI_STATUS_QUEUED = 1; | |
| 19 | + | CI_STATUS_IN_PROGRESS = 2; | |
| 20 | + | CI_STATUS_COMPLETED = 3; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | // CIConclusion is how a run, a job, a step or a check ended. | |
| 24 | + | enum CIConclusion { | |
| 25 | + | CI_CONCLUSION_UNSPECIFIED = 0; | |
| 26 | + | CI_CONCLUSION_SUCCESS = 1; | |
| 27 | + | CI_CONCLUSION_FAILURE = 2; | |
| 28 | + | CI_CONCLUSION_CANCELLED = 3; | |
| 29 | + | CI_CONCLUSION_SKIPPED = 4; | |
| 30 | + | CI_CONCLUSION_TIMED_OUT = 5; | |
| 31 | + | CI_CONCLUSION_NEUTRAL = 6; | |
| 32 | + | CI_CONCLUSION_ACTION_REQUIRED = 7; | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | // CIStepState is where a step is in its life. | |
| 36 | + | enum CIStepState { | |
| 37 | + | CI_STEP_STATE_UNSPECIFIED = 0; | |
| 38 | + | CI_STEP_STATE_PENDING = 1; | |
| 39 | + | CI_STEP_STATE_IN_PROGRESS = 2; | |
| 40 | + | CI_STEP_STATE_COMPLETED = 3; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | // CIJobPhase is where a job is for the server. | |
| 44 | + | enum CIJobPhase { | |
| 45 | + | CI_JOB_PHASE_UNSPECIFIED = 0; | |
| 46 | + | // Waits for the jobs it needs, or for a free max-parallel slot. | |
| 47 | + | CI_JOB_PHASE_WAITING = 1; | |
| 48 | + | // Planned and waiting for a runner. | |
| 49 | + | CI_JOB_PHASE_READY = 2; | |
| 50 | + | // Given to a runner that has not acknowledged it yet. | |
| 51 | + | CI_JOB_PHASE_ASSIGNED = 3; | |
| 52 | + | // Acknowledged by its runner. | |
| 53 | + | CI_JOB_PHASE_RUNNING = 4; | |
| 54 | + | // Over. | |
| 55 | + | CI_JOB_PHASE_ENDED = 5; | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | // CIIsolation is how a runner isolates jobs. | |
| 59 | + | enum CIIsolation { | |
| 60 | + | CI_ISOLATION_UNSPECIFIED = 0; | |
| 61 | + | CI_ISOLATION_VM = 1; | |
| 62 | + | CI_ISOLATION_CONTAINER_SYSBOX = 2; | |
| 63 | + | CI_ISOLATION_CONTAINER_PRIVILEGED = 3; | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | // CIRun is stored under "run/<repo>/<id:020d>". | |
| 67 | + | message CIRun { | |
| 68 | + | string repo = 1; | |
| 69 | + | int64 id = 2; | |
| 70 | + | int64 number = 3; | |
| 71 | + | int32 attempt = 4; | |
| 72 | + | string workflow_path = 5; | |
| 73 | + | string workflow_name = 6; | |
| 74 | + | string title = 7; | |
| 75 | + | string event = 8; | |
| 76 | + | string ref = 9; | |
| 77 | + | string sha = 10; | |
| 78 | + | string actor = 11; | |
| 79 | + | CIStatus status = 12; | |
| 80 | + | CIConclusion conclusion = 13; | |
| 81 | + | string error = 14; | |
| 82 | + | google.protobuf.Timestamp created_at = 15; | |
| 83 | + | google.protobuf.Timestamp started_at = 16; | |
| 84 | + | google.protobuf.Timestamp completed_at = 17; | |
| 85 | + | bool cancel_requested = 20; | |
| 86 | + | google.protobuf.Timestamp cancel_requested_at = 21; | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // CIRunSource is stored under "runsrc/<run id:020d>": what a run was planned | |
| 90 | + | // from, kept apart so that listing runs does not decode it. | |
| 91 | + | message CIRunSource { | |
| 92 | + | int64 run_id = 4; | |
| 93 | + | // The workflow file at sha, as is. | |
| 94 | + | string workflow_yaml = 1; | |
| 95 | + | // The "github" context of the run as JSON, without "job" and "run_attempt". | |
| 96 | + | string github_json = 2; | |
| 97 | + | // Repository of the run: the key of the run record is run/<repo>/<id>, and | |
| 98 | + | // the runner side knows a run by its id only. | |
| 99 | + | string repo = 3; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // CIStep is a step of a job. | |
| 103 | + | message CIStep { | |
| 104 | + | int32 number = 1; | |
| 105 | + | string name = 2; | |
| 106 | + | CIStepState state = 3; | |
| 107 | + | CIConclusion conclusion = 4; | |
| 108 | + | google.protobuf.Timestamp started_at = 5; | |
| 109 | + | google.protobuf.Timestamp completed_at = 6; | |
| 110 | + | // Number of the first log line of the step; meaningful with has_lines. The | |
| 111 | + | // end of a step is the start of the next step that has lines, or the line | |
| 112 | + | // count of the job: it is never stored. | |
| 113 | + | int64 log_start = 7; | |
| 114 | + | bool has_lines = 9; | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | // CIJob is stored under "job/<run_id:020d>/<run_attempt:010d>/<job id>". | |
| 118 | + | message CIJob { | |
| 119 | + | string id = 1; | |
| 120 | + | int64 run_id = 2; | |
| 121 | + | // Attempt in which the job executed (the one of its log). | |
| 122 | + | int32 attempt = 3; | |
| 123 | + | string repo = 4; | |
| 124 | + | string key = 5; | |
| 125 | + | string name = 6; | |
| 126 | + | CIJobPhase phase = 7; | |
| 127 | + | CIConclusion conclusion = 8; | |
| 128 | + | string error = 9; | |
| 129 | + | repeated string labels = 10; | |
| 130 | + | repeated string needs = 11; | |
| 131 | + | string runner_id = 12; | |
| 132 | + | string runner_name = 13; | |
| 133 | + | CIIsolation isolation = 14; | |
| 134 | + | repeated CIStep steps = 15; | |
| 135 | + | google.protobuf.Timestamp created_at = 16; | |
| 136 | + | google.protobuf.Timestamp started_at = 17; | |
| 137 | + | google.protobuf.Timestamp completed_at = 18; | |
| 138 | + | // Position of the job in the workflow file, and of the instance in its | |
| 139 | + | // matrix. A combo of -1 is a job whose matrix is not expanded yet. | |
| 140 | + | int32 index = 19; | |
| 141 | + | int32 combo = 20; | |
| 142 | + | string matrix_json = 21; | |
| 143 | + | // Number of instances of the job's matrix, for the strategy context. | |
| 144 | + | int32 combo_total = 22; | |
| 145 | + | int32 timeout_minutes = 23; | |
| 146 | + | bool fail_fast = 24; | |
| 147 | + | int32 max_parallel = 25; | |
| 148 | + | map<string, string> outputs = 26; | |
| 149 | + | bytes token_hash = 27; | |
| 150 | + | google.protobuf.Timestamp ready_at = 28; | |
| 151 | + | google.protobuf.Timestamp assigned_at = 29; | |
| 152 | + | google.protobuf.Timestamp acked_at = 30; | |
| 153 | + | int32 assign_cancels = 31; | |
| 154 | + | google.protobuf.Timestamp token_valid_until = 32; | |
| 155 | + | int32 cancel_reason = 33; | |
| 156 | + | string cancel_message = 34; | |
| 157 | + | google.protobuf.Timestamp cancel_requested_at = 35; | |
| 158 | + | // Set when the job is planned: runs-on, name and contexts are evaluated. | |
| 159 | + | bool planned = 37; | |
| 160 | + | // Attempt of the run this record belongs to; it is the key. A job kept by a | |
| 161 | + | // re-run of the failed jobs is copied with the run attempt of the re-run and | |
| 162 | + | // keeps its own attempt. | |
| 163 | + | int32 run_attempt = 38; | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | // CICheck is stored under "check/<repo>/<sha>/<job id>". | |
| 167 | + | message CICheck { | |
| 168 | + | string id = 1; | |
| 169 | + | string repo = 2; | |
| 170 | + | string sha = 3; | |
| 171 | + | string name = 4; | |
| 172 | + | CIStatus status = 5; | |
| 173 | + | CIConclusion conclusion = 6; | |
| 174 | + | int64 run_id = 7; | |
| 175 | + | string job_id = 8; | |
| 176 | + | int32 attempt = 9; | |
| 177 | + | string workflow_name = 10; | |
| 178 | + | google.protobuf.Timestamp created_at = 11; | |
| 179 | + | google.protobuf.Timestamp started_at = 12; | |
| 180 | + | google.protobuf.Timestamp completed_at = 13; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | // CIRunner is stored under "runner/<id>". | |
| 184 | + | message CIRunner { | |
| 185 | + | string id = 1; | |
| 186 | + | string name = 2; | |
| 187 | + | repeated string labels = 3; | |
| 188 | + | CIIsolation isolation = 4; | |
| 189 | + | string version = 5; | |
| 190 | + | string arch = 6; | |
| 191 | + | string os = 7; | |
| 192 | + | google.protobuf.Timestamp registered_at = 8; | |
| 193 | + | bytes token_hash = 9; | |
| 194 | + | } | |