Skip to content

Commit 293b9ba

Browse files
authored
Merge branch 'master' into smaller-image
2 parents 8d35b49 + c5874ac commit 293b9ba

File tree

8 files changed

+167
-95
lines changed

8 files changed

+167
-95
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,23 @@ docker run -it -v $srcDir:/src <DOCKER_NAME>:<DOCKER_VERSION>
3131

3232
We use the [codacy-plugins-test](https://github.com/codacy/codacy-plugins-test) to test our external tools integration.
3333
You can follow the instructions there to make sure your tool is working as expected.
34+
35+
## What is Codacy?
36+
37+
[Codacy](https://www.codacy.com/) is an Automated Code Review Tool that monitors your technical debt, helps you improve your code quality, teaches best practices to your developers, and helps you save time in Code Reviews.
38+
39+
### Among Codacy’s features:
40+
41+
- Identify new Static Analysis issues
42+
- Commit and Pull Request Analysis with GitHub, BitBucket/Stash, GitLab (and also direct git repositories)
43+
- Auto-comments on Commits and Pull Requests
44+
- Integrations with Slack, HipChat, Jira, YouTrack
45+
- Track issues in Code Style, Security, Error Proneness, Performance, Unused Code and other categories
46+
47+
Codacy also helps keep track of Code Coverage, Code Duplication, and Code Complexity.
48+
49+
Codacy supports PHP, Python, Ruby, Java, JavaScript, and Scala, among others.
50+
51+
### Free for Open Source
52+
53+
Codacy is free for Open Source projects.

build.sbt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ resolvers ++= Seq(
1515

1616
libraryDependencies ++= Seq(
1717
"com.typesafe.play" %% "play-json" % "2.3.8",
18-
"com.codacy" %% "codacy-engine-scala-seed" % "2.6.31"
18+
"com.codacy" %% "codacy-engine-scala-seed" % "2.7.1"
1919
)
2020

2121
enablePlugins(JavaAppPackaging)
@@ -36,8 +36,12 @@ val installAll =
3636
|python3 -m pip install django==1.9.2 flask==0.10.1 pylint-flask==0.1 flask-wtf==0.12 --upgrade --ignore-installed --no-cache-dir &&
3737
|python -m pip install git+https://github.com/landscapeio/pylint-django@93fd04120d0690189c35b7b2eaace23117f388c5 --upgrade --ignore-installed --no-cache-dir &&
3838
|python3 -m pip install git+https://github.com/landscapeio/pylint-django@93fd04120d0690189c35b7b2eaace23117f388c5 --upgrade --ignore-installed --no-cache-dir &&
39-
|python -m pip install pylint==1.5.4 --upgrade --ignore-installed --no-cache-dir &&
40-
|python3 -m pip install pylint==1.5.4 --upgrade --ignore-installed --no-cache-dir &&
39+
|python -m pip install pylint-common==0.2.2 &&
40+
|python3 -m pip install pylint-common==0.2.2 &&
41+
|python -m pip install pylint-celery==0.3 &&
42+
|python3 -m pip install pylint-celery==0.3 &&
43+
|python -m pip install pylint==1.6.4 --upgrade --ignore-installed --no-cache-dir &&
44+
|python3 -m pip install pylint==1.6.4 --upgrade --ignore-installed --no-cache-dir &&
4145
|python -m pip uninstall -y pip &&
4246
|python3 -m pip uninstall -y pip &&
4347
|apk del wget ca-certificates git &&

circle.yml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@ machine:
33
- echo 'DOCKER_OPTS="-s btrfs -e lxc -D --userland-proxy=false"' | sudo tee -a /etc/default/docker
44
- sudo curl -L -o /usr/bin/docker 'https://s3-external-1.amazonaws.com/circle-downloads/docker-1.9.0-circleci-cp-workaround'
55
- sudo chmod 0755 /usr/bin/docker
6+
- mkdir -p $HOME/.sbt/.lib/0.13.13 && wget https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.13.13/sbt-launch.jar -O $HOME/.sbt/.lib/0.13.13/sbt-launch.jar
7+
- (cd $HOME; rm -f codacy-plugins-test; git clone https://github.com/codacy/codacy-plugins-test.git)
68
java:
79
version: oraclejdk8
810
services:
911
- docker
1012

1113
dependencies:
1214
override:
13-
- sbt "set version in Docker := \"latest\"" "set name := \"$CIRCLE_PROJECT_REPONAME\"" docker:publishLocal
15+
- (cd $HOME/codacy-plugins-test && sbt compile)
16+
- (cd $HOME/$CIRCLE_PROJECT_REPONAME && sbt "set version in Docker := \"latest\"" "set name := \"$CIRCLE_PROJECT_REPONAME\"" docker:publishLocal)
17+
cache_directories:
18+
- "~/.ivy2"
19+
- "~/.m2"
20+
- "~/.sbt"
21+
- "~/codacy-plugins-test/target"
22+
- "~/codacy-plugins-test/project/target"
23+
- "~/codacy-plugins-test/project/project"
24+
- "~/$CIRCLE_PROJECT_REPONAME/target"
25+
- "~/$CIRCLE_PROJECT_REPONAME/project/target"
26+
- "~/$CIRCLE_PROJECT_REPONAME/project/project"
1427

1528
test:
1629
pre:
17-
- git clone https://github.com/codacy/codacy-plugins-test.git
30+
- (cd $HOME/codacy-plugins-test; git fetch --all; git reset --hard origin/master)
1831
override:
19-
- (cd codacy-plugins-test; sbt "run-main codacy.plugins.DockerTest all $CIRCLE_PROJECT_REPONAME:latest")
32+
- (cd $HOME/codacy-plugins-test && sbt -Dcodacy.tests.noremove=true -Dcodacy.tests.threads=8 "run-main codacy.plugins.DockerTest pattern $CIRCLE_PROJECT_REPONAME:latest")
2033

2134
deployment:
2235
hub:
@@ -25,4 +38,4 @@ deployment:
2538
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
2639
- docker tag $CIRCLE_PROJECT_REPONAME codacy/$CIRCLE_PROJECT_REPONAME
2740
- docker tag $CIRCLE_PROJECT_REPONAME codacy/$CIRCLE_PROJECT_REPONAME:1.0.$CIRCLE_BUILD_NUM
28-
- docker push codacy/$CIRCLE_PROJECT_REPONAME
41+
- docker push codacy/$CIRCLE_PROJECT_REPONAME
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Emitted when a conditional statement (If or ternary if) uses a constant value for its test. This might not be what the user intended to do.
2+
3+
A simple example that will be reported:
4+
5+
if True: pass

src/main/resources/docs/description/description.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,5 +834,11 @@
834834
"title": "Prohibit inheriting non classes",
835835
"description": "Do not inherit from something which is not a class.",
836836
"timeToFix": 5
837+
},
838+
{
839+
"patternId": "W0125",
840+
"title": "Prohibit a conditional statement with a constant value",
841+
"description": "Prohibits when a conditional statement (If or ternary if) uses a constant value for its test.",
842+
"timeToFix": 5
837843
}
838844
]

src/main/resources/docs/patterns.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,11 @@
706706
"patternId": "W1501",
707707
"level": "Warning",
708708
"category": "ErrorProne"
709+
},
710+
{
711+
"patternId": "W0125",
712+
"level": "Warning",
713+
"category": "ErrorProne"
709714
}
710715
]
711716
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
##Patterns: W0125
2+
3+
print('ola')
4+
5+
this_is_an_intentionally_long_name_to_trigger_a_line_too_long_message_from_pylint = None
6+
7+
##Warn: W0125
8+
if True: pass
9+
10+
while True:
11+
break
12+
13+
while True:
14+
break # 4 space indent

src/main/scala/codacy/pylint/Pylint.scala

Lines changed: 93 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
package codacy.pylint
22

3-
import java.nio.file.{Files, Path, Paths}
3+
import java.nio.file.{Files, Path}
44

55
import codacy.dockerApi._
6-
import codacy.dockerApi.utils.{FileHelper, CommandRunner, ToolHelper}
6+
import codacy.dockerApi.utils.{CommandRunner, FileHelper, ToolHelper}
77
import play.api.libs.json._
88

9-
import scala.collection.immutable.Iterable
109
import scala.sys.process._
1110
import scala.util.{Properties, Success, Try}
1211

1312
object Pylint extends Tool {
1413

15-
override def apply(path: Path, conf: Option[List[PatternDef]], files: Option[Set[Path]])(implicit spec: Spec): Try[List[Result]] = {
14+
override def apply(path: Path, conf: Option[List[PatternDef]], files: Option[Set[Path]])(implicit spec: Spec): Try[List[Result]] = {
1615
val completeConf = ToolHelper.getPatternsToLint(conf)
1716

1817
def isEnabled(issue: Result) = {
19-
issue match {
20-
case Issue(_, _, patternId, _) => completeConf.map(item => item.exists(_.patternId == patternId)).getOrElse(true)
21-
case _ => true
22-
}
18+
issue match {
19+
case Issue(_, _, patternId, _) => completeConf.map(item => item.exists(_.patternId == patternId)).getOrElse(true)
20+
case _ => true
21+
}
2322
}
2423

2524
def buildFileCommands(files: Map[String, Array[String]]) = {
26-
files.map { case (key, values) => commandFor(key, path, completeConf, values)}
27-
.flatMap( item => item.toOption).toList
25+
files.map { case (key, values) => commandFor(key, path, completeConf, values) }
26+
.flatMap(item => item.toOption).toList
2827
}
2928

3029
def getStdout(command: List[String]): Try[List[String]] = {
3130
Try {
32-
CommandRunner.exec(command) match {
33-
case Right(resultFromTool) =>
34-
resultFromTool.stdout
35-
case Left(failure) => {
36-
throw failure}
31+
CommandRunner.exec(command, Some(path.toFile)) match {
32+
case Right(resultFromTool) =>
33+
resultFromTool.stdout
34+
case Left(failure) => {
35+
throw failure
3736
}
37+
}
3838
}
3939
}
4040

@@ -44,78 +44,83 @@ object Pylint extends Tool {
4444
val lines_iterable = commands.map { item => item.map(getStdout) }
4545
val lines = lines_iterable.map {
4646
case iterable => iterable.flatMap {
47-
case item => item.toOption
47+
case item => item.toOption
4848
}.flatten
4949
}
50-
lines.map { case line => line.flatMap(parseLine).flatten.filter(isEnabled)}
50+
lines.map { case line => line.flatMap(parseLine).flatten.filter(isEnabled) }
5151
}
5252

5353

5454
private implicit lazy val writer = Json.reads[Issue]
5555

5656
private def parseLine(line: String) = {
57-
val LineRegex = """(.*?)###(.*?)###(.*?)###(.*?)""".r
58-
line match {
59-
case LineRegex(filename, lineNumber, message, patternId) if message.contains("invalid syntax") =>
60-
val fileError = FileError(SourcePath(filename),
61-
Option(ErrorMessage(message)))
62-
val issue = Issue(SourcePath(filename),
63-
ResultMessage(message),
64-
PatternId(patternId),
65-
ResultLine(lineNumber.toInt))
66-
Option(List(fileError, issue))
67-
case LineRegex(filename, lineNumber, message, patternId) =>
68-
Option(List(Issue(SourcePath(filename),
69-
ResultMessage(message),
70-
PatternId(patternId),
71-
ResultLine(lineNumber.toInt))))
72-
case _ =>
73-
Option.empty
74-
}
57+
val LineRegex = """(.*?)###(\d*?)###(.*?)###(.*?)""".r
58+
59+
def createIssue(filename: String, lineNumber: String, message: String, patternId: String) = {
60+
// If the pylint returns no line put the issue in the first line
61+
val issueLine = if (lineNumber.nonEmpty) lineNumber.toInt else 1
62+
Issue(SourcePath(filename),
63+
ResultMessage(message),
64+
PatternId(patternId),
65+
ResultLine(issueLine))
66+
}
67+
68+
line match {
69+
case LineRegex(filename, lineNumber, message, patternId) if message.contains("invalid syntax") =>
70+
val fileError = FileError(SourcePath(filename),
71+
Option(ErrorMessage(message)))
72+
val issue = createIssue(filename, lineNumber, message, patternId)
73+
Option(List(fileError, issue))
74+
case LineRegex(filename, lineNumber, message, patternId) =>
75+
Option(List(createIssue(filename, lineNumber, message, patternId)))
76+
case _ =>
77+
Option.empty
78+
}
7579
}
7680

7781
private val msgTemplate = "{path}###{line}###{msg}###{msg_id}"
78-
private val classifyScript = s"""
79-
|import os
80-
|import sys
81-
|import ast
82-
|current = sys.version_info[0]
83-
|other = 2 if current == 3 else 3
84-
|def _classify_file(path):
85-
| try:
86-
| with open(path, 'r') as stream:
87-
| try:
88-
| ast.parse(stream.read())
89-
| except (ValueError, TypeError, UnicodeError):
90-
| # Assume it's the current interpreter.
91-
| return current
92-
| except SyntaxError:
93-
| # the other version or an actual syntax error on current interpreter
94-
| return other
95-
| else:
96-
| return current
97-
| except Exception:
98-
| # Shouldn't happen, but if it does, just assume there's
99-
| # something inherently wrong with the file.
100-
| return current
101-
|def classify_file(path):
102-
| interpreter = _classify_file(path)
103-
| return path + "###" + str(interpreter)
104-
|def flatten_files(folder):
105-
| for path, _, files in os.walk(folder):
106-
| for file in files:
107-
| if file.endswith(".py"):
108-
| yield os.path.join(path, file)
109-
|def walk_items(items):
110-
| for item in items:
111-
| if os.path.isfile(item): yield item
112-
| elif os.path.isdir(item):
113-
| for file in flatten_files(item): yield file
114-
|def classify(items):
115-
| for file in walk_items(items):
116-
| print(classify_file(file))
117-
|items = filter(None, sys.argv[1].split("###"))
118-
|classify(items)
82+
private val classifyScript =
83+
s"""
84+
|import os
85+
|import sys
86+
|import ast
87+
|current = sys.version_info[0]
88+
|other = 2 if current == 3 else 3
89+
|def _classify_file(path):
90+
| try:
91+
| with open(path, 'r') as stream:
92+
| try:
93+
| ast.parse(stream.read())
94+
| except (ValueError, TypeError, UnicodeError):
95+
| # Assume it's the current interpreter.
96+
| return current
97+
| except SyntaxError:
98+
| # the other version or an actual syntax error on current interpreter
99+
| return other
100+
| else:
101+
| return current
102+
| except Exception:
103+
| # Shouldn't happen, but if it does, just assume there's
104+
| # something inherently wrong with the file.
105+
| return current
106+
|def classify_file(path):
107+
| interpreter = _classify_file(path)
108+
| return path + "###" + str(interpreter)
109+
|def flatten_files(folder):
110+
| for path, _, files in os.walk(folder):
111+
| for file in files:
112+
| if file.endswith(".py"):
113+
| yield os.path.join(path, file)
114+
|def walk_items(items):
115+
| for item in items:
116+
| if os.path.isfile(item): yield item
117+
| elif os.path.isdir(item):
118+
| for file in flatten_files(item): yield file
119+
|def classify(items):
120+
| for file in walk_items(items):
121+
| print(classify_file(file))
122+
|items = filter(None, sys.argv[1].split("###"))
123+
|classify(items)
119124
""".stripMargin
120125

121126
private def collectFiles(files: Option[Set[Path]], path: Path) = {
@@ -132,16 +137,16 @@ object Pylint extends Tool {
132137
}
133138

134139
private def classifyFiles(files: List[String]) = {
135-
Try {
136-
val output = generateClassification(files)
137-
val lines = output.split(System.lineSeparator())
138-
val parsed = lines.map { case line =>
139-
val splitted = line.split("###")
140-
(splitted(0), splitted(1))
141-
}
142-
parsed.groupBy { case (path, version) => version}
143-
.map { case (key, pairs) => (key, pairs map { case (file, version) => file})}
140+
Try {
141+
val output = generateClassification(files)
142+
val lines = output.split(System.lineSeparator())
143+
val parsed = lines.map { case line =>
144+
val splitted = line.split("###")
145+
(splitted(0), splitted(1))
144146
}
147+
parsed.groupBy { case (path, version) => version }
148+
.map { case (key, pairs) => (key, pairs map { case (file, version) => file }) }
149+
}
145150
}
146151

147152
private def commandFor(interpreter: String, path: Path, conf: Option[List[PatternDef]], files: Array[String])(implicit spec: Spec): Try[List[String]] = {
@@ -160,14 +165,14 @@ object Pylint extends Tool {
160165

161166
//Additional plugins
162167
val django = Seq("--load-plugins=pylint_django",
163-
"--disable=django-installed-checker,django-model-checker")
168+
"--disable=django-installed-checker,django-model-checker")
164169
val flask = Seq("--load-plugins=pylint_flask")
165170
val additionalPlugins = django ++ flask
166171

167172
configPart.map { configPart =>
168173
List("python" + interpreter, "-m", "pylint") ++
169-
configPart ++ List(s"--msg-template=$msgTemplate") ++
170-
rulesPart ++ additionalPlugins ++ files
174+
configPart ++ List(s"--msg-template=$msgTemplate") ++
175+
rulesPart ++ additionalPlugins ++ files
171176
}
172177
}
173178

0 commit comments

Comments
 (0)