diff --git a/generator/__init__.py b/generator/__init__.py
index 05c141b..ba32392 100644
--- a/generator/__init__.py
+++ b/generator/__init__.py
@@ -2,5 +2,6 @@ from .author import Author
from .event import Event
from .config import config
from .issue import Issue
+from .repo import Repo
from .api import GithubAPI
from .generator import generate_changelog
\ No newline at end of file
diff --git a/generator/api.py b/generator/api.py
index f6d2f84..cf90ded 100644
--- a/generator/api.py
+++ b/generator/api.py
@@ -17,6 +17,7 @@ class GithubAPI:
self.s.headers.update({'Authorization': 'token {}'.format(token)})
else:
warn("use Token!", stacklevel=2) # TODO
+ self.s.headers.update({'User-Agent': 'github-changelog-generator'})
def call(self, url, parameters=None):
if "//" not in url:
diff --git a/generator/cli.py b/generator/cli.py
index 7306211..60888df 100644
--- a/generator/cli.py
+++ b/generator/cli.py
@@ -4,6 +4,6 @@ from generator import generate_changelog
def main():
- since = datetime.today() - timedelta(10)
+ since = datetime.today() - timedelta(5)
generate_changelog(since)
diff --git a/generator/config.py b/generator/config.py
index 553125b..c08da80 100644
--- a/generator/config.py
+++ b/generator/config.py
@@ -1,4 +1,5 @@
import os.path
+import sys
import pkg_resources
import yaml
@@ -9,13 +10,17 @@ class Config:
def __init__(self):
with open(self.get_config_path(), 'r') as stream:
- config = yaml.safe_load(stream)
- self.api_token = config["api_token"] # type:str
- self.labels_to_ignore = set(config["labels_to_ignore"])
- self.sort_by_labels = config["sort_by_labels"]
- self.is_matomo = config["is_matomo"] # type:bool
- if self.is_matomo:
- self.compare_config()
+ try:
+ config = yaml.safe_load(stream)
+ self.api_token = config["api_token"] # type:str
+ self.labels_to_ignore = set(config["labels_to_ignore"])
+ self.sort_by_labels = config["sort_by_labels"] # type:list
+ self.repositories = config["repositories"] # type:list
+ self.is_matomo = config["is_matomo"] # type:bool
+ if self.is_matomo:
+ self.compare_config()
+ except KeyError as e:
+ sys.exit("required option '{}' is missing from the config".format(e.args[0]))
def get_config_path(self) -> str:
for path in self.config_paths:
diff --git a/generator/defaultconfig.yaml b/generator/defaultconfig.yaml
index 2a13087..249d9d5 100644
--- a/generator/defaultconfig.yaml
+++ b/generator/defaultconfig.yaml
@@ -1,4 +1,19 @@
api_token: null
+repositories:
+ - matomo-org/matomo
+ - matomo-org/tag-manager
+ - matomo-org/matomo-log-analytics
+ - matomo-org/matomo-php-tracker
+ - matomo-org/referrer-spam-blacklist
+ - matomo-org/tracker-proxy
+ - matomo-org/device-detector
+ - matomo-org/searchengine-and-social-list
+ - matomo-org/component-network
+ - matomo-org/component-ini
+ - matomo-org/component-decompress
+ - matomo-org/component-cache
+ - matomo-org/matomo-package
+ - matomo-org/matomo-icons
labels_to_ignore:
- wontfix
- not-in-changelog
diff --git a/generator/formatters/base.py b/generator/formatters/base.py
index 7b330a1..6148ea3 100644
--- a/generator/formatters/base.py
+++ b/generator/formatters/base.py
@@ -1,11 +1,11 @@
from typing import List
-from generator import Issue
+from generator import Repo
class BaseFormatter:
- def __init__(self, issues: List[Issue]):
- self.issues = issues
+ def __init__(self, repos: List[Repo]):
+ self.repos = repos
def __str__(self) -> str:
return ""
diff --git a/generator/formatters/html.py b/generator/formatters/html.py
index 2f0585a..6b1e000 100644
--- a/generator/formatters/html.py
+++ b/generator/formatters/html.py
@@ -7,21 +7,26 @@ class HTMLFormatter(BaseFormatter):
def __str__(self) -> str:
text = "
\n"
- for issue in self.issues:
- text += "\t
#{id} {title}".format(
- url=issue.url,
- id=issue.number,
- title=html.escape(issue.title)
- )
- if issue.authors:
- text += " [by {}]".format(
- ", ".join(
- "
@{name}".format(
- url=author.profile_url,
- name=html.escape(author.username)
- ) for author in issue.authors
+ for repo in self.repos:
+ if repo.issues:
+ text += "
\n".format(repo.absolute_url, repo.path)
+ text += "
\n"
+ for issue in repo.issues:
+ text += "\t- #{id} {title}".format(
+ url=issue.url,
+ id=issue.number,
+ title=html.escape(issue.title)
)
- )
- text += "\n"
+ if issue.authors:
+ text += " [by {}]".format(
+ ", ".join(
+ "@{name}".format(
+ url=author.profile_url,
+ name=html.escape(author.username)
+ ) for author in issue.authors
+ )
+ )
+ text += "
\n"
+ text += "
"
text += "
"
return text
diff --git a/generator/formatters/markdown.py b/generator/formatters/markdown.py
index d03b271..1c33c0b 100644
--- a/generator/formatters/markdown.py
+++ b/generator/formatters/markdown.py
@@ -7,20 +7,23 @@ class MarkdownFormatter(BaseFormatter):
def __str__(self) -> str:
text = ""
- for issue in self.issues:
- text += "\t- [#{id}]({url}) {title}".format(
- url=issue.url,
- id=issue.number,
- title=html.escape(issue.title)
- )
- if issue.authors:
- text += " [by {}]".format(
- ", ".join(
- "[@{name}]({url})".format(
- url=author.profile_url,
- name=html.escape(author.username)
- ) for author in issue.authors
+ for repo in self.repos:
+ if repo.issues:
+ text += "### [{}]({})\n".format(repo.path, repo.absolute_url)
+ for issue in repo.issues:
+ text += " - [#{id}]({url}) {title}".format(
+ url=issue.url,
+ id=issue.number,
+ title=html.escape(issue.title)
)
- )
- text += "\n"
+ if issue.authors:
+ text += " [by {}]".format(
+ ", ".join(
+ "[@{name}]({url})".format(
+ url=author.profile_url,
+ name=html.escape(author.username)
+ ) for author in issue.authors
+ )
+ )
+ text += "\n"
return text
diff --git a/generator/generator.py b/generator/generator.py
index c91549a..bcd2651 100644
--- a/generator/generator.py
+++ b/generator/generator.py
@@ -1,7 +1,8 @@
from datetime import datetime
+from typing import List
-from generator import GithubAPI, config, Issue
-from generator.formatters import MarkdownFormatter
+from generator import GithubAPI, config, Issue, Repo
+from generator.formatters import HTMLFormatter, MarkdownFormatter
api = GithubAPI(token=config.api_token)
@@ -14,18 +15,35 @@ def getissueorder(issue: Issue):
return order, issue.number
-def generate_changelog(since: datetime):
- issues = api.fetch_issues_since("matomo-org/matomo", since)
- issues = list(issues) # enumerate iterable
- for issue in issues:
- if issue.pull_request:
- issue.add_pr_data(api.fetch_pr_details(issue))
- issue.compare_close_date(since)
- issues = [i for i in issues if i.should_be_included] # remove all filtered issues
- for issue in issues:
- for event in api.fetch_events(issue):
- if event.author_should_be_listed:
- issue.authors.add(event.author)
+def generate_statistics(repos: List[Repo]):
+ unique_authors = set()
+ num_issues = 0
+ for repo in repos:
+ for issue in repo.issues:
+ num_issues += 1
+ unique_authors.update(issue.authors)
+ print("{num} Tickets closed by {contr} contributors".format(num=num_issues, contr=len(unique_authors)))
- issues.sort(key=getissueorder)
- print(MarkdownFormatter(issues))
+
+def generate_changelog(since: datetime):
+ repos = []
+ for repo_url in config.repositories:
+ repo = Repo(repo_url)
+ issues = api.fetch_issues_since(repo_url, since)
+ issues = list(issues) # enumerate iterable
+ for issue in issues:
+ if issue.pull_request:
+ issue.add_pr_data(api.fetch_pr_details(issue))
+ issue.compare_close_date(since)
+ issues = [i for i in issues if i.should_be_included] # remove all filtered issues
+ for issue in issues:
+ for event in api.fetch_events(issue):
+ if event.author_should_be_listed:
+ issue.authors.add(event.author)
+
+ issues.sort(key=getissueorder)
+ repo.issues = issues
+ repos.append(repo)
+
+ print(HTMLFormatter(repos))
+ generate_statistics(repos)
diff --git a/generator/issue.py b/generator/issue.py
index ba091a9..42d8bf0 100644
--- a/generator/issue.py
+++ b/generator/issue.py
@@ -8,7 +8,7 @@ class Issue():
def __init__(self, api):
self.number = api["number"] # type:int
self.title = api["title"] # type:str
- self.url = api["url"] # type:str
+ self.url = api["html_url"] # type:str
self.labels = [l["name"] for l in api["labels"]] # type:List[str]
self.closed_at = datetime.strptime(api["closed_at"], "%Y-%m-%dT%H:%M:%SZ")
self.pull_request = "pull_request" in api
@@ -20,7 +20,8 @@ class Issue():
self.merged = None # type:bool
self.authors = set() # type:set
self.creator = Author(api["user"])
- self.authors.add(self.creator)
+ if self.pull_request:
+ self.authors.add(self.creator)
def __repr__(self):
return "".format(self.number)
diff --git a/generator/repo.py b/generator/repo.py
new file mode 100644
index 0000000..94e1c8f
--- /dev/null
+++ b/generator/repo.py
@@ -0,0 +1,10 @@
+class Repo(object):
+ BASE_URL = "https://github.com/"
+
+ def __init__(self, path):
+ self.path = path
+ self.issues = []
+
+ @property
+ def absolute_url(self):
+ return self.BASE_URL + self.path