Skip to content
This repository has been archived by the owner on Apr 1, 2022. It is now read-only.

Commit

Permalink
Parses entry in pod and dependency section correctly when it is enclo…
Browse files Browse the repository at this point in the history
…sed with quotation (#337)
  • Loading branch information
meghfossa authored Aug 20, 2021
1 parent f18cfaf commit a15e018
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 47 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Spectrometer Changelog

## v2.15.6

- CocoaPods: Fixes `Podfile.lock` parsing. It safely parses when Pod and Dependencies entries are enclosed with quotations. ([#337](https://github.com/fossas/spectrometer/pull/337))

## v2.15.5

- Fixes an issue where `--json` would output the raw project ID, instead of a normalized ID ([#339](https://github.com/fossas/spectrometer/pull/339))
Expand Down
6 changes: 4 additions & 2 deletions docs/strategies/ios/cocoapods.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ We scan the `Podfile.lock` for two particular sections: `PODS` and
dependencies, and `DEPENDENCIES` tells us which of the dependencies the project
depends on directly.

In the following example, we have four dependencies, `one`, `two`, `three`, and
`four`. `one`, and `three` are direct dependencies, `one` depends on both
In the following example, we have five dependencies, `one`, `two`, `three`,
`four` and `five/+zlib`. `one`, `three` and `five/+zlib` are direct dependencies, `one` depends on both
`two` and `three`, and `three` depends on `four`.

```
Expand All @@ -33,8 +33,10 @@ PODS:
- three (3.0.0)
- four (= 2.3.3)
- four (4.0.0):
- "five/+zlib (7.0.0)"
DEPENDENCIES:
- one (> 4.4)
- three (from `Submodules/subproject/.podspec`)
- "five/+zlib (7.0.0)"
```
21 changes: 16 additions & 5 deletions src/Strategy/Cocoapods/PodfileLock.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module Strategy.Cocoapods.PodfileLock (
Dep (..),
Pod (..),
Section (..),

-- * for testing,
podParser,
depParser,
) where

import Control.Effect.Diagnostics
Expand Down Expand Up @@ -158,21 +162,22 @@ remoteParser = indentBlock $ do

podParser :: Parser Pod
podParser = indentBlock $ do
_ <- chunk "- "
name <- findDep
name <- symbol "-" *> ignoreDoubleQuote *> findDep
version <- findVersion
_ <- restOfLine
pure (L.IndentMany Nothing (pure . Pod name version) depParser)

depParser :: Parser Dep
depParser = do
_ <- chunk "- "
name <- findDep
name <- symbol "-" *> ignoreDoubleQuote *> findDep
_ <- restOfLine
pure $ Dep name

findDep :: Parser Text
findDep = lexeme (takeWhile1P (Just "dep") (not . C.isSpace))
findDep = lexeme (takeWhile1P (Just "dep") isNotDoubleQuoteAndSpace)
where
isNotDoubleQuoteAndSpace :: Char -> Bool
isNotDoubleQuoteAndSpace c = c /= '"' && (not . C.isSpace) c

findVersion :: Parser Text
findVersion = between (char '(') (char ')') (lexeme (takeWhileP (Just "version") (/= ')')))
Expand All @@ -199,3 +204,9 @@ lexeme = L.lexeme sc

sc :: Parser ()
sc = L.space (void $ some (char ' ')) empty empty

symbol :: Text -> Parser Text
symbol = L.symbol scn

ignoreDoubleQuote :: Parser (Maybe Text)
ignoreDoubleQuote = optional $ symbol "\""
134 changes: 94 additions & 40 deletions test/Cocoapods/PodfileLockSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,135 @@ module Cocoapods.PodfileLockSpec (
) where

import Data.Map.Strict qualified as Map
import Data.Text (Text)
import Data.Text.IO qualified as TIO
import Data.Void (Void)
import DepTypes
import GraphUtil
import Strategy.Cocoapods.PodfileLock
import Test.Hspec qualified as T
import Text.Megaparsec
import Test.Hspec.Megaparsec (shouldParse)
import Text.Megaparsec (Parsec, errorBundlePretty, parse, runParser)

dependencyOne :: Dependency
dependencyOne =
parseMatch :: (Show a, Eq a) => Parsec Void Text a -> Text -> a -> T.Expectation
parseMatch parser input expected = parse parser "" input `shouldParse` expected

depOf :: Text -> Maybe Text -> Dependency
depOf name version =
Dependency
{ dependencyType = PodType
, dependencyName = "one"
, dependencyVersion = Just (CEq "1.0.0")
, dependencyName = name
, dependencyVersion = CEq <$> version
, dependencyLocations = []
, dependencyEnvironments = []
, dependencyTags = Map.empty
}

dependencyOne :: Dependency
dependencyOne = depOf "one" (Just "1.0.0")

dependencyTwo :: Dependency
dependencyTwo =
Dependency
{ dependencyType = PodType
, dependencyName = "two"
, dependencyVersion = Just (CEq "2.0.0")
, dependencyLocations = []
, dependencyEnvironments = []
, dependencyTags = Map.empty
}
dependencyTwo = depOf "two" (Just "2.0.0")

dependencyThree :: Dependency
dependencyThree =
Dependency
{ dependencyType = PodType
, dependencyName = "three"
, dependencyVersion = Just (CEq "3.0.0")
, dependencyLocations = []
, dependencyEnvironments = []
, dependencyTags = Map.empty
}
dependencyThree = depOf "three" (Just "3.0.0")

dependencyFour :: Dependency
dependencyFour =
Dependency
{ dependencyType = PodType
, dependencyName = "four"
, dependencyVersion = Just (CEq "4.0.0")
, dependencyLocations = []
, dependencyEnvironments = []
, dependencyTags = Map.empty
}
dependencyFour = depOf "four" (Just "4.0.0")

dependencyAbnormalName :: Dependency
dependencyAbnormalName = depOf "ab-normal/+name" (Just "2.0.0")

dependencyNotSoSafeName :: Dependency
dependencyNotSoSafeName = depOf "not-so-safe-name" (Just "2.0.0")

dependencyGitSourced :: Dependency
dependencyGitSourced = depOf "some-dep-sourced-from-git" (Just "2.0.0")

dependencyGitTagged :: Dependency
dependencyGitTagged = depOf "depWithTag" (Just "2.0.0")

dependencyTwoDepA :: Dependency
dependencyTwoDepA = depOf "two_dep_A" Nothing

dependencyTwoDepB :: Dependency
dependencyTwoDepB = depOf "two-dep-B" Nothing

podSection :: Section
podSection = PodSection [Pod "one" "1.0.0" [Dep "two", Dep "three"], Pod "two" "2.0.0" [], Pod "three" "3.0.0" [Dep "four"], Pod "four" "4.0.0" []]
podSection =
PodSection
[ Pod "one" "1.0.0" [Dep "two", Dep "three", Dep "ab-normal/+name"]
, Pod "two" "2.0.0" [Dep "two_dep_A", Dep "two-dep-B"]
, Pod "three" "3.0.0" [Dep "four"]
, Pod "ab-normal/+name" "2.0.0" []
, Pod "four" "4.0.0" []
, Pod "not-so-safe-name" "2.0.0" []
, Pod "some-dep-sourced-from-git" "2.0.0" []
, Pod "depWithTag" "2.0.0" []
]

dependencySection :: Section
dependencySection = DependencySection [Dep "one", Dep "three"]
dependencySection =
DependencySection
[ Dep "one"
, Dep "three"
, Dep "not-so-safe-name"
, Dep "some-dep-sourced-from-git"
, Dep "depWithTag"
]

spec :: T.Spec
spec = do
T.describe "podfile lock analyzer" $
T.it "produces the expected output" $ do
let graph = buildGraph [podSection, dependencySection]

expectDeps [dependencyOne, dependencyTwo, dependencyThree, dependencyFour] graph
expectDirect [dependencyOne, dependencyThree] graph
expectDeps
[ dependencyOne
, dependencyTwo
, dependencyThree
, dependencyAbnormalName
, dependencyFour
, dependencyNotSoSafeName
, dependencyGitSourced
, dependencyGitTagged
, dependencyTwoDepA
, dependencyTwoDepB
]
graph
expectDirect
[ dependencyOne
, dependencyThree
, dependencyNotSoSafeName
, dependencyGitSourced
, dependencyGitTagged
]
graph
expectEdges
[ (dependencyOne, dependencyTwo)
[ (dependencyOne, dependencyAbnormalName)
, (dependencyOne, dependencyTwo)
, (dependencyOne, dependencyThree)
, (dependencyTwo, dependencyTwoDepA)
, (dependencyTwo, dependencyTwoDepB)
, (dependencyThree, dependencyFour)
]
graph

podLockFile <- T.runIO (TIO.readFile "test/Cocoapods/testdata/Podfile.lock")
T.describe "podfile lock parser" $
T.it "parses error messages into an empty list" $
T.describe "podfile lock parser" $ do
T.describe "dep parser" $ do
let shouldParseInto input = parseMatch depParser input
T.it "parses names" $ do
"- atomFire (1.0.0)" `shouldParseInto` Dep "atomFire"
"- \"at/+om (1.0.0)\"" `shouldParseInto` Dep "at/+om"
"- \"not-so-safe-name (2.0.0)\"" `shouldParseInto` Dep "not-so-safe-name"

T.describe "pod parser" $ do
let shouldParseInto input = parseMatch podParser input
T.it "parses names" $ do
"- atomFire (1.0.0)" `shouldParseInto` Pod "atomFire" "1.0.0" ([])
"- \"atomFire (1.0.0)\"" `shouldParseInto` Pod "atomFire" "1.0.0" ([])

T.it "parses pod and dependency sections" $
case runParser findSections "" podLockFile of
Left err -> T.expectationFailure ("failed to parse: " <> errorBundlePretty err)
Right result -> do
Expand Down
10 changes: 10 additions & 0 deletions test/Cocoapods/testdata/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ PODS:
- one (1.0.0):
- two (= 3.2.1)
- three (= 3.2.1)
- "ab-normal/+name (2.0.0)"
- two (2.0.0)
- two_dep_A
- "two-dep-B"
- three (3.0.0)
- four (= 2.3.3)
- "ab-normal/+name (2.0.0)"
- four (4.0.0):
- "not-so-safe-name (2.0.0)"
- some-dep-sourced-from-git (2.0.0)
- depWithTag (2.0.0)

DEPENDENCIES:
- one (> 4.4)
- three (from `Submodules/subproject/.podspec`)
- "not-so-safe-name (2.0.0)"
- "some-dep-sourced-from-git (from `[email protected]:ab/ios.git`, commit `559e6a`)"
- depWithTag (from `[email protected]:ab/cap.git`, tag `v1.2.3`)

SPEC REPOS:
https://github.com/cocoapods/specs.git:
Expand Down

0 comments on commit a15e018

Please sign in to comment.