validateAndSanitizeReleaseFiles validates and sanitizes the release files inputs. It ensures that each file has either a sheet or a statement, and that the sheet is valid. It also checks for duplicate versions in versioned releases and sorts the files by version. The input files are modified in pla
(ctx context.Context, s *store.Store, files []*v1pb.Release_File, releaseType v1pb.Release_Type)
| 368 | // If a sheet is provided, it populates the statement from the sheet. |
| 369 | // If a statement is provided, it computes the sheetSha256 from the statement. |
| 370 | func validateAndSanitizeReleaseFiles(ctx context.Context, s *store.Store, files []*v1pb.Release_File, releaseType v1pb.Release_Type) ([]*v1pb.Release_File, error) { |
| 371 | if len(files) == 0 { |
| 372 | return nil, errors.Errorf("release files cannot be empty") |
| 373 | } |
| 374 | |
| 375 | versionSet := map[string]struct{}{} |
| 376 | |
| 377 | for _, f := range files { |
| 378 | switch { |
| 379 | // Validate that either sheet or statement is provided |
| 380 | // For DECLARATIVE releases, empty content is allowed (represents dropping all objects) |
| 381 | case f.Sheet == "" && len(f.Statement) == 0: |
| 382 | if releaseType != v1pb.Release_DECLARATIVE { |
| 383 | return nil, errors.Errorf("either sheet or statement must be set for file %q", f.Path) |
| 384 | } |
| 385 | // For declarative releases with empty content, set empty statement and compute SHA256 |
| 386 | f.Statement = []byte{} |
| 387 | h := sha256.Sum256(f.Statement) |
| 388 | f.SheetSha256 = hex.EncodeToString(h[:]) |
| 389 | case f.Sheet != "" && len(f.Statement) > 0: |
| 390 | return nil, errors.Errorf("cannot set both sheet and statement for file %q", f.Path) |
| 391 | |
| 392 | // If sheet is provided but statement is not, populate statement from sheet |
| 393 | case f.Sheet != "": |
| 394 | _, sheetSha256, err := common.GetProjectResourceIDSheetSha256(f.Sheet) |
| 395 | if err != nil { |
| 396 | return nil, errors.Wrapf(err, "failed to get sheet SHA256 from %q", f.Sheet) |
| 397 | } |
| 398 | sheet, err := s.GetSheetFull(ctx, sheetSha256) |
| 399 | if err != nil { |
| 400 | return nil, errors.Wrapf(err, "failed to get sheet %q", f.Sheet) |
| 401 | } |
| 402 | if sheet == nil { |
| 403 | return nil, errors.Errorf("sheet %q not found", f.Sheet) |
| 404 | } |
| 405 | // Sheets are now project-agnostic, no need to check projectID |
| 406 | f.Statement = []byte(sheet.Statement) |
| 407 | f.SheetSha256 = sheet.Sha256 |
| 408 | case len(f.Statement) > 0: |
| 409 | // populate sheetSha256 from statement |
| 410 | h := sha256.Sum256(f.Statement) |
| 411 | f.SheetSha256 = hex.EncodeToString(h[:]) |
| 412 | default: |
| 413 | } |
| 414 | |
| 415 | // Version validation for versioned releases only. |
| 416 | // Declarative releases can have multiple files with the same version. |
| 417 | if releaseType == v1pb.Release_VERSIONED { |
| 418 | if _, ok := versionSet[f.Version]; ok { |
| 419 | return nil, errors.Errorf("found duplicate version %q", f.Version) |
| 420 | } |
| 421 | versionSet[f.Version] = struct{}{} |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | // Create files with additional parsed version data for sorting. |
| 426 | type fileWithVersion struct { |
| 427 | file *v1pb.Release_File |
no test coverage detected