SBOM: various fixes.
- be a bit stricter with SBOM handling with the test default formula flow in CI by making it raise errors if SBOM's aren't generated and validated as expected - fix handling of HEAD installations of formulae so SBOM generation is both correct and doesn't raise errors - make `Formula#bottle_hash` more accepting of edge cases e.g. HEAD-only formulae without a stable spec Fixes #17333
This commit is contained in:
		
							parent
							
								
									ea0582871c
								
							
						
					
					
						commit
						b066ac414c
					
				
							
								
								
									
										1
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							@ -405,6 +405,7 @@ jobs:
 | 
				
			|||||||
            runs-on: macos-14
 | 
					            runs-on: macos-14
 | 
				
			||||||
    env:
 | 
					    env:
 | 
				
			||||||
      HOMEBREW_TEST_BOT_ANALYTICS: 1
 | 
					      HOMEBREW_TEST_BOT_ANALYTICS: 1
 | 
				
			||||||
 | 
					      HOMEBREW_ENFORCE_SBOM: 1
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Set up Homebrew
 | 
					      - name: Set up Homebrew
 | 
				
			||||||
        id: set-up-homebrew
 | 
					        id: set-up-homebrew
 | 
				
			||||||
 | 
				
			|||||||
@ -2579,9 +2579,13 @@ class Formula
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  # Returns the bottle information for a formula.
 | 
					  # Returns the bottle information for a formula.
 | 
				
			||||||
  def bottle_hash(compact_for_api: false)
 | 
					  def bottle_hash(compact_for_api: false)
 | 
				
			||||||
    bottle_spec = T.must(stable).bottle_specification
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    hash = {}
 | 
					    hash = {}
 | 
				
			||||||
 | 
					    stable_spec = stable
 | 
				
			||||||
 | 
					    return hash unless stable_spec
 | 
				
			||||||
 | 
					    return hash unless bottle_defined?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bottle_spec = stable_spec.bottle_specification
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hash["rebuild"] = bottle_spec.rebuild if !compact_for_api || !bottle_spec.rebuild.zero?
 | 
					    hash["rebuild"] = bottle_spec.rebuild if !compact_for_api || !bottle_spec.rebuild.zero?
 | 
				
			||||||
    hash["root_url"] = bottle_spec.root_url unless compact_for_api
 | 
					    hash["root_url"] = bottle_spec.root_url unless compact_for_api
 | 
				
			||||||
    hash["files"] = {}
 | 
					    hash["files"] = {}
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,13 @@ class SBOM
 | 
				
			|||||||
  # Instantiates a {SBOM} for a new installation of a formula.
 | 
					  # Instantiates a {SBOM} for a new installation of a formula.
 | 
				
			||||||
  sig { params(formula: Formula, tab: Tab).returns(T.attached_class) }
 | 
					  sig { params(formula: Formula, tab: Tab).returns(T.attached_class) }
 | 
				
			||||||
  def self.create(formula, tab)
 | 
					  def self.create(formula, tab)
 | 
				
			||||||
 | 
					    active_spec = if formula.stable?
 | 
				
			||||||
 | 
					      T.must(formula.stable)
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      T.must(formula.head)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    active_spec_sym = formula.active_spec_sym
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    attributes = {
 | 
					    attributes = {
 | 
				
			||||||
      name:                 formula.name,
 | 
					      name:                 formula.name,
 | 
				
			||||||
      homebrew_version:     HOMEBREW_VERSION,
 | 
					      homebrew_version:     HOMEBREW_VERSION,
 | 
				
			||||||
@ -32,13 +39,13 @@ class SBOM
 | 
				
			|||||||
        path:         formula.specified_path.to_s,
 | 
					        path:         formula.specified_path.to_s,
 | 
				
			||||||
        tap:          formula.tap&.name,
 | 
					        tap:          formula.tap&.name,
 | 
				
			||||||
        tap_git_head: nil, # Filled in later if possible
 | 
					        tap_git_head: nil, # Filled in later if possible
 | 
				
			||||||
        spec:         formula.active_spec_sym.to_s,
 | 
					        spec:         active_spec_sym.to_s,
 | 
				
			||||||
        patches:      formula.stable&.patches,
 | 
					        patches:      active_spec.patches,
 | 
				
			||||||
        bottle:       formula.bottle_hash,
 | 
					        bottle:       formula.bottle_hash,
 | 
				
			||||||
        stable:       {
 | 
					        active_spec_sym =>       {
 | 
				
			||||||
          version:  formula.stable&.version,
 | 
					          version:  active_spec.version,
 | 
				
			||||||
          url:      formula.stable&.url,
 | 
					          url:      active_spec.url,
 | 
				
			||||||
          checksum: formula.stable&.checksum,
 | 
					          checksum: active_spec.checksum,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -230,7 +237,8 @@ class SBOM
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  def generate_packages_json(runtime_dependency_declaration, compiler_declaration, bottling:)
 | 
					  def generate_packages_json(runtime_dependency_declaration, compiler_declaration, bottling:)
 | 
				
			||||||
    bottle = []
 | 
					    bottle = []
 | 
				
			||||||
    if !bottling && (bottle_info = get_bottle_info(source[:bottle]))
 | 
					    if !bottling && (bottle_info = get_bottle_info(source[:bottle])) &&
 | 
				
			||||||
 | 
					       (stable_version = source.dig(:stable, :version))
 | 
				
			||||||
      bottle << {
 | 
					      bottle << {
 | 
				
			||||||
        SPDXID:           "SPDXRef-Bottle-#{name}",
 | 
					        SPDXID:           "SPDXRef-Bottle-#{name}",
 | 
				
			||||||
        name:             name.to_s,
 | 
					        name:             name.to_s,
 | 
				
			||||||
@ -267,18 +275,18 @@ class SBOM
 | 
				
			|||||||
      {
 | 
					      {
 | 
				
			||||||
        SPDXID:           "SPDXRef-Archive-#{name}-src",
 | 
					        SPDXID:           "SPDXRef-Archive-#{name}-src",
 | 
				
			||||||
        name:             name.to_s,
 | 
					        name:             name.to_s,
 | 
				
			||||||
        versionInfo:      stable_version.to_s,
 | 
					        versionInfo:      spec_version.to_s,
 | 
				
			||||||
        filesAnalyzed:    false,
 | 
					        filesAnalyzed:    false,
 | 
				
			||||||
        licenseDeclared:  assert_value(nil),
 | 
					        licenseDeclared:  assert_value(nil),
 | 
				
			||||||
        builtDate:        source_modified_time.to_s,
 | 
					        builtDate:        source_modified_time.to_s,
 | 
				
			||||||
        licenseConcluded: assert_value(license),
 | 
					        licenseConcluded: assert_value(license),
 | 
				
			||||||
        downloadLocation: source[:stable][:url],
 | 
					        downloadLocation: source[spec_symbol][:url],
 | 
				
			||||||
        copyrightText:    assert_value(nil),
 | 
					        copyrightText:    assert_value(nil),
 | 
				
			||||||
        externalRefs:     [],
 | 
					        externalRefs:     [],
 | 
				
			||||||
        checksums:        [
 | 
					        checksums:        [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            algorithm:     "SHA256",
 | 
					            algorithm:     "SHA256",
 | 
				
			||||||
            checksumValue: source[:stable][:checksum].to_s,
 | 
					            checksumValue: source[spec_symbol][:checksum].to_s,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -362,13 +370,13 @@ class SBOM
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
      SPDXID:            "SPDXRef-DOCUMENT",
 | 
					      SPDXID:            "SPDXRef-DOCUMENT",
 | 
				
			||||||
      spdxVersion:       "SPDX-2.3",
 | 
					      spdxVersion:       "SPDX-2.3",
 | 
				
			||||||
      name:              "SBOM-SPDX-#{name}-#{stable_version}",
 | 
					      name:              "SBOM-SPDX-#{name}-#{spec_version}",
 | 
				
			||||||
      creationInfo:      {
 | 
					      creationInfo:      {
 | 
				
			||||||
        created:  (Time.at(time).utc if time.present? && !bottling),
 | 
					        created:  (Time.at(time).utc if time.present? && !bottling),
 | 
				
			||||||
        creators: ["Tool: https://github.com/homebrew/brew@#{homebrew_version}"],
 | 
					        creators: ["Tool: https://github.com/homebrew/brew@#{homebrew_version}"],
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      dataLicense:       "CC0-1.0",
 | 
					      dataLicense:       "CC0-1.0",
 | 
				
			||||||
      documentNamespace: "https://formulae.brew.sh/spdx/#{name}-#{stable_version}.json",
 | 
					      documentNamespace: "https://formulae.brew.sh/spdx/#{name}-#{spec_version}.json",
 | 
				
			||||||
      documentDescribes: packages.map { |dependency| dependency[:SPDXID] },
 | 
					      documentDescribes: packages.map { |dependency| dependency[:SPDXID] },
 | 
				
			||||||
      files:             [],
 | 
					      files:             [],
 | 
				
			||||||
      packages:,
 | 
					      packages:,
 | 
				
			||||||
@ -397,9 +405,14 @@ class SBOM
 | 
				
			|||||||
    Tap.fetch(tap_name) if tap_name
 | 
					    Tap.fetch(tap_name) if tap_name
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sig { returns(Symbol) }
 | 
				
			||||||
 | 
					  def spec_symbol
 | 
				
			||||||
 | 
					    source.fetch(:spec).to_sym
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sig { returns(T.nilable(Version)) }
 | 
					  sig { returns(T.nilable(Version)) }
 | 
				
			||||||
  def stable_version
 | 
					  def spec_version
 | 
				
			||||||
    source[:stable][:version]
 | 
					    source.fetch(spec_symbol)[:version]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sig { returns(Time) }
 | 
					  sig { returns(Time) }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user