This is a GitHub issue scanner for jQAssistant. It enables jQAssistant to scan and analyze GitHub issues.
Download the jQAssistant command line tool for your system: jQAssistant - Get Started.
Next download the latest version from the release tab. Put the jqa-githubissues-plugin-*.jar
into the plugins folder
of the jQAssistant commandline tool.
Create a file named githubissues.xml
.
The plugin can scan multiple repositories owned by different users. Please note that
the GitHub REST-API requires login credentials to
access any of its functions. Therefore, login credentials must be provided per
repository.
<github-issues-configuration>
<github-repository>
<user>github-user</user>
<name>github-repository-name</name>
<credentials>
<user>authentication-user</user>
<password>authentication-password</password>
</credentials>
</github-repository>
<github-repository>
...
</github-repository>
</github-issues-configuration>
Now scan your configuration and wait for the plugin to finish:
jqassistant.sh scan -f githubissues.xml
You can then start a local Neo4j server to start querying the database at http://localhost:7474:
jqassistant.sh server
The GitHub-Issues plugin uses the following labels in the resulting graph:
Label | Description | ID |
---|---|---|
GitHub-Issues-Configuration-File | A configuration file for the plugin. | - |
GitHub | Parent label for all nodes related to the GitHub-Issues plugin. | - |
Repository | Represents a GitHub Repository. | "repo-user/repo-name" |
Issue | Represents a GitHub Issue. | "repo-user/repo-name#issue-number" |
Milestone | Represents a GitHub Milestone which is a collection of Issues. | "repo-user/repo-name#milestone-id" |
Comment | Represents a Comment under a GitHub Issue. | - |
PullRequest | Every PullRequest is an Issue, but not every Issue is a PullRequest. | "repo-user/repo-name#issue-number" |
User | Represents a GitHub User. | "user-name" |
Commit | Represents a GitHub Commit. | "repo-user/repo-name#commit-sha" |
Here are the possible relations between those labels:
(GitHub-Issues-Configuration-File) -[:SPECIFIES_REPOSITORY] -> (Repository)
(Repository) -[:HAS_ISSUE] -> (Issue)
(Repository) -[:HAS_MILESTONE] -> (Milestone)
(Issue) -[:HAS_LABEL] -> (Label)
(Issue) -[:HAS_COMMENT] -> (Comment)
(Issue) -[:HAS_ASSIGNEE] -> (User)
(Issue) -[:CREATED_BY] -> (User)
(Issue) -[:IS_PART_OF] -> (Milestone)
(PullRequest) -[:HAS_LAST_COMMIT] -> (Commit)
(Milestone) -[:CREATED_BY] -> (User)
(Comment) -[:FOLLOWED_BY] -> (Comment)
(Comment) -[:CREATED_BY] -> (User)
(Issue|Comment) -[:REFERENCES_ISSUE] -> (Issue)
(Issue|Comment) -[:REFERENCES_COMMIT] -> (Commit)
(Issue|Comment) -[:REFERENCES_USER] -> (User)
List all your open Issues over multiple repositories:
MATCH
(r:Repository)-[:HAS_ISSUE]->(i:Issue {state:"open"})
RETURN
r.repositoryId, i.title, i.body
Count open Issues per repository:
MATCH
(r:Repository)-[:HAS_ISSUE]->(Issue {state:"open"})
RETURN
r.repositoryId, count(*) AS issueCount
ORDER BY
issueCount DESC
List open issues per user:
MATCH
(Issue {state:"open"})-[:HAS_ASSIGNEE]->(u:User)
RETURN
u.login, count(*)
Show issues without description:
MATCH
(i:Issue)
WHERE
i.body = ""
RETURN
i.issueId, i.title
Show issues without labels:
MATCH
(i:Issue)
WHERE
NOT (i:Issue)-[:HAS_LABEL]->()
RETURN
i.title, i.issueId
Show issues ordered descending by the amount of comments:
MATCH
path=((i:Issue)-[:HAS_COMMENT]->()-[:FOLLOWED_BY*]->())
RETURN
i.title, i.issueId, length(path) AS pathLength, i.state
ORDER BY
pathLength DESC, i.state DESC
Show durations it needed to resolve an issue:
WITH
issue, duration.inDays(date(issue.createdAt), date(issue.updatedAt)).days AS duration
RETURN
issue.issueId, issue.title, duration + " days" AS timToSolve
ORDER BY
duration DESC
Show issues older than 1 month that are still open:
MATCH
(issue:Issue {state:"open"})
WHERE
date(issue.createdAt) <= date('20180713')
RETURN
*
Let's have a look at a few indicators:
- Do these Issues have labels?
MATCH
(issue:Issue {state:"open"})
WHERE
date(issue.createdAt) <= date('20180713') AND NOT (issue:Issue)-[:HAS_LABEL]->()
RETURN
*
→ If not, then probably no one looked at these issues.
- Is anyone assigned to this issue?
MATCH
(issue:Issue {state:"open"})
WHERE
date(issue.createdAt) <= date('20180713') AND NOT (issue:Issue)-[:HAS_ASSIGNEE]->(:User)
RETURN
issue
→ If not, then probably no one feels responsible for this issue.
The performance of the plugin is limited by the GitHub REST API. We are not allowed to make more than one request per second. See this for more information. For each Issue and every Comment the body text gets parsed to HTML by this endpoint. We need to do this to resolve references to User, Commits and Commits in the markdown texts.
That is why a analysis of ~1800 issues can take a few hours.
At the moment only one configuration file is supported. When you scan more than one at a time nodes representing the same real world entity won't be identified. Furthermore, the plugin doesn't print a warning so be careful to avoid wrong analyses of your repositories!
Please have a look at the issue section in GitHub. If you can't find your bug open a ticket with an reproducible example and your error logs.
If you want to contribute here are a few tips to get you started:
Build the GitHub-Issues plugin:
cd plugin
# Build a fat-JAR
mvn clean package
# Copy the resulting JAR into the jQAssistant CLI plugins folder
cp target/jqa-githubissues-plugin-0.1-jar-with-dependencies.jar ../run/jqassistant-commandline-neo4jv3-1.4.0/plugins/
Run code coverage via Corbertura:
mvn cobertura:cobertura
The coverage reports can be found under target/site/cobertura
.