Skip to content

Commit dc572c6

Browse files
authored
Add NOTICE files to source units (#1466)
1 parent 9d8190f commit dc572c6

File tree

14 files changed

+287
-4
lines changed

14 files changed

+287
-4
lines changed

Changelog.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# FOSSA CLI Changelog
22

3+
## 3.9.37
4+
5+
- License Scanning: Update Themis to include NOTICE files, and parse the additional NOTICE file fields in Themis's output. ([#1466](https://github.com/fossas/fossa-cli/pull/1466))
6+
37
## 3.9.36
48

5-
- fossa-deps: Fixed an issue where Rocky Linux deps were not supported in the fossa-deps file ([#1473](https://github.com/fossas/fossa-cli/pull/1473))
6-
- `fossa report`: Remove subscription type check in preflight checks ([#1474](https://github.com/fossas/fossa-cli/pull/1474))
9+
- fossa-deps: Fixed an issue where Rocky Linux deps were not supported in the fossa-deps file ([#1473](https://github.com/fossas/fossa-cli/pull/1473))
10+
- `fossa report`: Remove subscription type check in preflight checks ([#1474](https://github.com/fossas/fossa-cli/pull/1474))
711

812
## 3.9.35
913

src/App/Fossa/Lernie/Analyze.hs

+1
Original file line numberDiff line numberDiff line change
@@ -304,5 +304,6 @@ createLicenseUnitSingles ((path, title), licenseUnitData) =
304304
, licenseUnitDir = ""
305305
, licenseUnitFiles = NE.singleton (unCustomLicensePath path)
306306
, licenseUnitData = NE.singleton licenseUnitData
307+
, licenseUnitNoticeFiles = []
307308
, licenseUnitInfo = LicenseUnitInfo{licenseUnitInfoDescription = Just $ "custom license search " <> unCustomLicenseTitle title}
308309
}

src/App/Fossa/ManualDeps.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ toSourceUnit root depsFile manualDeps@ManualDependencies{..} maybeApiOpts vendor
206206
, sourceUnitType = "user-specific-yaml"
207207
, sourceUnitBuild = build
208208
, sourceUnitGraphBreadth = Complete
209+
, sourceUnitNoticeFiles = []
209210
, sourceUnitOriginPaths = [someBaseToOriginPath originPath]
210211
, additionalData = additional
211212
}
@@ -508,7 +509,8 @@ instance FromJSON CustomDependency where
508509
<$> (obj `neText` "name")
509510
<*> (unTextLike <$> obj `neText` "version")
510511
<*> (obj `neText` "license")
511-
<*> obj .:? "metadata"
512+
<*> obj
513+
.:? "metadata"
512514
<* forbidMembers "custom dependencies" ["type", "path", "url"] obj
513515

514516
instance FromJSON RemoteDependency where

src/Srclib/Converter.hs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ toSourceUnit leaveUnfiltered path dependencies projectType graphBreadth originPa
7171
, buildDependencies = deps
7272
}
7373
, sourceUnitGraphBreadth = graphBreadth
74+
, sourceUnitNoticeFiles = []
7475
, sourceUnitOriginPaths = originPaths
7576
, additionalData = Nothing
7677
}

src/Srclib/Types.hs

+34
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Srclib.Types (
55
SourceUnit (..),
66
SourceUnitBuild (..),
77
SourceUnitDependency (..),
8+
SourceUnitNoticeFile (..),
89
AdditionalDepData (..),
910
SourceUserDefDep (..),
1011
SourceRemoteDep (..),
@@ -112,13 +113,36 @@ textToOriginPath = OriginPath . toString
112113
-- Manifest?: string;
113114
-- }
114115

116+
data SourceUnitNoticeFile = SourceUnitNoticeFile
117+
{ sourceUnitNoticeFilePath :: Text
118+
, sourceUnitNoticeFileContents :: Text
119+
, sourceUnitNoticeFileCopyrights :: [Text]
120+
}
121+
deriving (Eq, Ord, Show)
122+
123+
instance ToJSON SourceUnitNoticeFile where
124+
toJSON SourceUnitNoticeFile{..} =
125+
object
126+
[ "path" .= sourceUnitNoticeFilePath
127+
, "contents" .= sourceUnitNoticeFileContents
128+
, "copyrights" .= sourceUnitNoticeFileCopyrights
129+
]
130+
131+
instance FromJSON SourceUnitNoticeFile where
132+
parseJSON = withObject "SourceUnitNoticeFile" $ \obj ->
133+
SourceUnitNoticeFile
134+
<$> obj .: "path"
135+
<*> obj .: "contents"
136+
<*> obj .:? "copyrights" .!= []
137+
115138
data FullSourceUnit = FullSourceUnit
116139
{ fullSourceUnitName :: Text
117140
, fullSourceUnitType :: Text
118141
, fullSourceUnitTitle :: Maybe Text
119142
, fullSourceUnitManifest :: Maybe Text
120143
, fullSourceUnitBuild :: Maybe SourceUnitBuild
121144
, fullSourceUnitGraphBreadth :: GraphBreadth
145+
, fullSourceUnitNoticeFiles :: [SourceUnitNoticeFile]
122146
, fullSourceUnitOriginPaths :: [OriginPath]
123147
, fullSourceUnitAdditionalData :: Maybe AdditionalDepData
124148
, fullSourceUnitFiles :: Maybe (NonEmpty Text)
@@ -136,6 +160,7 @@ licenseUnitToFullSourceUnit LicenseUnit{..} =
136160
, fullSourceUnitManifest = Nothing
137161
, fullSourceUnitBuild = Nothing
138162
, fullSourceUnitGraphBreadth = Complete
163+
, fullSourceUnitNoticeFiles = licenseUnitNoticeFiles
139164
, fullSourceUnitOriginPaths = []
140165
, fullSourceUnitAdditionalData = Nothing
141166
, fullSourceUnitFiles = Just licenseUnitFiles
@@ -153,6 +178,7 @@ sourceUnitToFullSourceUnit SourceUnit{..} =
153178
, fullSourceUnitBuild = sourceUnitBuild
154179
, fullSourceUnitGraphBreadth = sourceUnitGraphBreadth
155180
, fullSourceUnitOriginPaths = sourceUnitOriginPaths
181+
, fullSourceUnitNoticeFiles = sourceUnitNoticeFiles
156182
, fullSourceUnitAdditionalData = additionalData
157183
, fullSourceUnitFiles = Nothing
158184
, fullSourceUnitData = Nothing
@@ -169,6 +195,7 @@ instance ToJSON FullSourceUnit where
169195
, "Build" .= fullSourceUnitBuild
170196
, "GraphBreadth" .= fullSourceUnitGraphBreadth
171197
, "OriginPaths" .= fullSourceUnitOriginPaths
198+
, "NoticeFiles" .= fullSourceUnitNoticeFiles
172199
, "AdditionalDependencyData" .= fullSourceUnitAdditionalData
173200
, "Files" .= fullSourceUnitFiles
174201
, "Data" .= fullSourceUnitData
@@ -204,6 +231,7 @@ data LicenseUnit = LicenseUnit
204231
, licenseUnitDir :: Text
205232
, licenseUnitFiles :: (NonEmpty Text)
206233
, licenseUnitData :: (NonEmpty LicenseUnitData)
234+
, licenseUnitNoticeFiles :: [SourceUnitNoticeFile]
207235
, licenseUnitInfo :: LicenseUnitInfo
208236
}
209237
deriving (Eq, Ord, Show)
@@ -217,6 +245,7 @@ emptyLicenseUnit =
217245
, licenseUnitDir = ""
218246
, licenseUnitFiles = "" :| []
219247
, licenseUnitData = emptyLicenseUnitData :| []
248+
, licenseUnitNoticeFiles = []
220249
, licenseUnitInfo = LicenseUnitInfo{licenseUnitInfoDescription = Nothing}
221250
}
222251

@@ -235,6 +264,7 @@ instance ToJSON LicenseUnit where
235264
, "Dir" .= licenseUnitDir
236265
, "Files" .= licenseUnitFiles
237266
, "Data" .= licenseUnitData
267+
, "NoticeFiles" .= licenseUnitNoticeFiles
238268
, "Info" .= licenseUnitInfo
239269
]
240270

@@ -247,6 +277,7 @@ instance FromJSON LicenseUnit where
247277
<*> obj .: "Dir"
248278
<*> obj .: "Files"
249279
<*> obj .: "Data"
280+
<*> obj .:? "NoticeFiles" .!= []
250281
<*> obj .: "Info"
251282

252283
newtype LicenseUnitInfo = LicenseUnitInfo
@@ -350,6 +381,7 @@ data SourceUnit = SourceUnit
350381
-- ^ path to manifest file
351382
, sourceUnitBuild :: Maybe SourceUnitBuild
352383
, sourceUnitGraphBreadth :: GraphBreadth
384+
, sourceUnitNoticeFiles :: [SourceUnitNoticeFile]
353385
, sourceUnitOriginPaths :: [OriginPath]
354386
, additionalData :: Maybe AdditionalDepData
355387
}
@@ -431,6 +463,7 @@ instance ToJSON SourceUnit where
431463
, "Manifest" .= sourceUnitManifest
432464
, "Build" .= sourceUnitBuild
433465
, "GraphBreadth" .= sourceUnitGraphBreadth
466+
, "NoticeFiles" .= sourceUnitNoticeFiles
434467
, "OriginPaths" .= sourceUnitOriginPaths
435468
, "AdditionalDependencyData" .= additionalData
436469
]
@@ -443,6 +476,7 @@ instance FromJSON SourceUnit where
443476
<*> obj .: "Manifest"
444477
<*> obj .:? "Build"
445478
<*> obj .: "GraphBreadth"
479+
<*> obj .:? "NoticeFiles" .!= []
446480
<*> obj .: "OriginPaths"
447481
<*> obj .:? "AdditionalDependencyData"
448482

test/App/Fossa/Analyze/UploadSpec.hs

+2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ expectedMergedFullSourceUnits = NE.fromList [fullSourceUnit, fullLicenseUnit]
8888
, fullSourceUnitFiles = Nothing
8989
, fullSourceUnitData = Nothing
9090
, fullSourceUnitInfo = Nothing
91+
, fullSourceUnitNoticeFiles = []
9192
}
9293
fullLicenseUnit =
9394
FullSourceUnit
@@ -102,6 +103,7 @@ expectedMergedFullSourceUnits = NE.fromList [fullSourceUnit, fullLicenseUnit]
102103
, fullSourceUnitFiles = Just $ "" NE.:| []
103104
, fullSourceUnitData = Just $ emptyLicenseUnitData NE.:| []
104105
, fullSourceUnitInfo = Just LicenseUnitInfo{licenseUnitInfoDescription = Nothing}
106+
, fullSourceUnitNoticeFiles = []
105107
}
106108

107109
expectGetSuccessWithReachability :: Has MockApi sig m => m ()

test/App/Fossa/Container/AnalyzeNativeSpec.hs

+1
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ jarsInContainerSpec = describe "Jars in Containers" $ do
236236
}
237237
, sourceUnitGraphBreadth = Complete
238238
, sourceUnitOriginPaths = [textToOriginPath "package-lock.json"]
239+
, sourceUnitNoticeFiles = []
239240
, additionalData = Nothing
240241
}
241242

test/App/Fossa/FirstPartyScanSpec.hs

+26-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import Control.Effect.FossaApiClient (FossaApiClientF (..))
1010
import Data.List qualified as List
1111
import Data.List.NonEmpty qualified as NE
1212
import Data.Maybe (isJust)
13+
import Data.Text (strip)
1314
import Fossa.API.Types (Organization (..))
1415
import Path (Dir, Path, Rel, mkRelDir, (</>))
1516
import Path.IO (getCurrentDir)
16-
import Srclib.Types (LicenseSourceUnit (..), LicenseUnit (licenseUnitData, licenseUnitName), LicenseUnitData (licenseUnitDataContents))
17+
import Srclib.Types (LicenseSourceUnit (..), LicenseUnit (..), LicenseUnitData (licenseUnitDataContents), SourceUnitNoticeFile (..))
1718
import Test.Effect (expectFatal', expectationFailure', it', shouldBe')
1819
import Test.Fixtures qualified as Fixtures
1920
import Test.Hspec (Spec, describe, runIO)
@@ -25,12 +26,16 @@ fixtureDir = $(mkRelDir "test/App/Fossa/VendoredDependency/testdata/repo")
2526
fixtureDirWithVendoredDeps :: Path Rel Dir
2627
fixtureDirWithVendoredDeps = $(mkRelDir "test/App/Fossa/VendoredDependency/testdata/firstparty")
2728

29+
fixtureDirWithNoticeFiles :: Path Rel Dir
30+
fixtureDirWithNoticeFiles = $(mkRelDir "test/App/Fossa/VendoredDependency/testdata/firstparty-with-notice")
31+
2832
spec :: Spec
2933
spec = do
3034
describe "runFirstPartyScan" $ do
3135
currDir <- runIO getCurrentDir
3236
let scanDir = currDir </> fixtureDir
3337
let scanDirWithVendoredDeps = currDir </> fixtureDirWithVendoredDeps
38+
let scanDirWithNoticeFiles = currDir </> fixtureDirWithNoticeFiles
3439

3540
it' "should fail if the organization does not support first party scans and you force it on" $ do
3641
expectGetOrganizationThatDoesNotSupportFirstPartyScans
@@ -99,6 +104,26 @@ spec = do
99104
licenseUnitName secondUnit `shouldBe'` "apache-2.0"
100105
licenseUnitDataContents unitData `shouldBe'` Nothing
101106

107+
it' "should find notice files" $ do
108+
expectGetOrganizationThatDefaultsToFirstPartyScans
109+
licenseSourceUnit <- firstPartyScanWithOrgInfo scanDirWithNoticeFiles Fixtures.standardAnalyzeConfig
110+
case licenseSourceUnit of
111+
Nothing -> expectationFailure' "first party scan should have run"
112+
Just LicenseSourceUnit{licenseSourceUnitLicenseUnits = units} -> do
113+
length units `shouldBe'` 3
114+
let noticeUnit = NE.head units
115+
licenseUnitName noticeUnit `shouldBe'` ""
116+
licenseUnitType noticeUnit `shouldBe'` "NoticeFileMatches"
117+
let noticeFiles = licenseUnitNoticeFiles noticeUnit
118+
length noticeFiles `shouldBe'` 1
119+
let noticeFile = head noticeFiles
120+
let copyrights = sourceUnitNoticeFileCopyrights noticeFile
121+
length copyrights `shouldBe'` 1
122+
let copyright = head copyrights
123+
copyright `shouldBe'` "2024 Frank Frankson"
124+
strip (sourceUnitNoticeFileContents noticeFile) `shouldBe'` "This is a notice file that is copyright 2024 Frank Frankson"
125+
sourceUnitNoticeFilePath noticeFile `shouldBe'` "NOTICE.txt"
126+
102127
-- The default org defaults to not running first party scans but has first-party scans enabled
103128
expectGetOrganizationThatDefaultsToNoFirstPartyScans :: Has MockApi sig m => m ()
104129
expectGetOrganizationThatDefaultsToNoFirstPartyScans = GetOrganization `alwaysReturns` Fixtures.organization

test/App/Fossa/LernieSpec.hs

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ expectedLicenseUnit =
172172
, licenseUnitFiles = NE.singleton $ toText . toFilePath $ absDir </> $(mkRelDir "two.txt")
173173
, licenseUnitData = NE.singleton expectedUnitData
174174
, licenseUnitInfo = LicenseUnitInfo{licenseUnitInfoDescription = Just "custom license search Proprietary License"}
175+
, licenseUnitNoticeFiles = []
175176
}
176177

177178
expectedDoubleSourceUnit :: LicenseSourceUnit

test/App/Fossa/LicenseScannerSpec.hs

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ unitOne =
4343
, licenseUnitFiles =
4444
NE.fromList ["foo/bar/LICENSE", "foo/bar/one.txt"]
4545
, licenseUnitInfo = info
46+
, licenseUnitNoticeFiles = []
4647
}
4748

4849
unitTwo :: LicenseUnit
@@ -55,6 +56,7 @@ unitTwo =
5556
, licenseUnitData = NE.fromList [emptyLicenseUnitData{licenseUnitDataPath = "foo/bar/baz/ANOTHER_LICENSE"}, emptyLicenseUnitData{licenseUnitDataPath = "foo/bar/baz/two.txt"}]
5657
, licenseUnitFiles = NE.fromList ["foo/bar/baz/ANOTHER_LICENSE", "foo/bar/baz/two.txt"]
5758
, licenseUnitInfo = info
59+
, licenseUnitNoticeFiles = []
5860
}
5961
expectedCombinedUnit :: LicenseUnit
6062
expectedCombinedUnit =
@@ -78,6 +80,7 @@ expectedCombinedUnit =
7880
, "foo/bar/one.txt"
7981
]
8082
, licenseUnitInfo = info
83+
, licenseUnitNoticeFiles = []
8184
}
8285

8386
fixtureDir :: Path Rel Dir

0 commit comments

Comments
 (0)