Skip to content

Commit a1c18c2

Browse files
authored
Merge pull request #32 from bakhtos/backend-pydriller
Use `PyDriller` as git backend
2 parents ab858d7 + ef859b8 commit a1c18c2

File tree

19 files changed

+235
-310
lines changed

19 files changed

+235
-310
lines changed

README.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,29 @@ The corresponding path only needs to be un-commented for analysis (all others ha
3232

3333
##### 2. Running the tool
3434
To start the tool via the terminal using the config file, simply enter `python3 code2DFD.py --config_path PATH_TO_CONFIG` in a command line opened in the root directory.
35-
For example, `python3 code2DFD.py --config_path config/config.ini` for the example config in this repository.
36-
The extraction will start and some status messages appear on the screen.
37-
If you want to analyse in application on GitHub, simply put in the GitHub handle, using the `--github_path` option.
38-
For example, for the repository `https://github.com/sqshq/piggymetrics`, run the command `python3 code2DFD.py --github_path sqshq/piggymetrics`
35+
For example, `python3 code2DFD.py --config_path config/config.ini` for the [example config](config/config.ini) in this repository.
3936

37+
The config file needs to specify the following sections and parameters:
38+
- Repository
39+
- `path`: `organization/repository` part of GitHub URL
40+
- `url`: the full URL of the repository to clone from (may be local path)
41+
- `local_path`: local directory to clone the repository to (without the repository name itself)
42+
- Technology profiles: same as in [example config](config/config.ini)
43+
- DFD: empty section
44+
- Analysis Settings (optional)
45+
- `development_mode`: boolean, turns on development mode
46+
- `commit`: hash of the commit to checkout and analyze; repository will be returned to the same commit it was in before analysis; if commit not provided, attempts to checkout `HEAD`
47+
48+
It is possible to provide these parameters also by command line, see `python3 code2DFD.py --help` for exact usage
49+
50+
If both config file and CLI arguments provided, CLI arguments take precedence
51+
52+
###### 2.1 RESTful service
4053
To run the tool as a RESTful API service, run `python3 flask_code2DFD.py`.
41-
This will spawn up a Flask server and you can trigger DFD-extractions by sending a request to or opening your browser at `localhost:5000/dfd?path=*repository/path*`
54+
55+
This will spawn up a Flask server and you can trigger DFD-extractions by sending a request to `localhost:5001/dfd` with parameters `url` and optionally `commit`.
56+
57+
Currently only GitHub URLs are supported this way.
4258

4359

4460
##### 3. Output

code2DFD.py

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
#
33
# Author: Simon Schneider, 2023
44
# Contact: simon.schneider@tuhh.de
5-
5+
import os
66
from configparser import ConfigParser
77
from datetime import datetime
88
import argparse
99

10-
import core.dfd_extraction as dfd_extraction
10+
from core.dfd_extraction import perform_analysis
1111
from output_generators.logger import logger
1212
import tmp.tmp as tmp
13-
from core.file_interaction import get_output_path, get_local_path, clone_repo
1413

1514
CONFIG_SECTIONS = ["Analysis Settings", "Repository", "Technology Profiles", "DFD"]
1615
COMMUNICATIONS_TECH_LIST = '[("RabbitMQ", "rmq"), ("Kafka", "kfk"), ("RestTemplate", "rst"),\
@@ -24,32 +23,34 @@
2423
DEFAULT_CONFIG.set("Technology Profiles", "communication_techs_list", COMMUNICATIONS_TECH_LIST)
2524

2625

27-
def api_invocation(path: str) -> dict:
26+
def api_invocation(url: str, commit: str) -> dict:
2827
"""Entry function for when tool is called via API call.
2928
"""
3029

31-
print("New call for " + path)
32-
response = dict()
30+
print("New call for " + url)
3331

3432
start_time = datetime.now()
3533

3634
logger.info("*** New execution ***")
37-
logger.debug("Copying config file to tmp file")
35+
logger.debug("Initializing config to tmp file")
36+
for section in CONFIG_SECTIONS: # Copying what is needed from default to temp
37+
tmp.tmp_config.add_section(section)
38+
for entry in DEFAULT_CONFIG[section]:
39+
tmp.tmp_config.set(section, entry, DEFAULT_CONFIG[section][entry])
3840

3941
# Overwrite repo_path from config file with the one from the API call
40-
repo_path = str(path)
41-
tmp.tmp_config.set("Repository", "path", repo_path)
42-
43-
local_path = get_local_path(repo_path)
44-
tmp.tmp_config.set("Repository", "local_path", local_path)
45-
46-
clone_repo(repo_path, local_path)
42+
tmp.tmp_config.set("Repository", "url", url)
43+
tmp.tmp_config.set("Repository", "local_path",
44+
os.path.join(os.getcwd(), "analysed_repositories"))
45+
if commit is not None:
46+
tmp.tmp_config.set("Analysis Settings", "commit", commit)
4747

4848
# Call extraction
49-
codeable_models, traceability = dfd_extraction.perform_analysis()
49+
codeable_models, traceability = perform_analysis()
5050

51+
response = dict()
5152
response["codeable_models_file"] = codeable_models
52-
response["traceability"] = traceability
53+
response["traceability_file"] = traceability
5354

5455
# Execution time
5556
end_time = datetime.now()
@@ -61,47 +62,54 @@ def api_invocation(path: str) -> dict:
6162
return response
6263

6364

64-
def main():
65+
def cli_invocation():
6566
parser = argparse.ArgumentParser()
66-
source = parser.add_mutually_exclusive_group(required=True)
67-
source.add_argument("--config_path", type=str, help="Path to the config file to use")
68-
source.add_argument("--github_path", type=str, help="Path to the repository on GitHub as 'repository/path'")
69-
now = datetime.now()
70-
start_time = now.strftime("%H:%M:%S")
67+
parser.add_argument("--config_path", type=str, help="Path to the config file to use (can be replaced with following CLI arguments")
68+
repository = parser.add_argument_group("Repository", "Repository information")
69+
repository.add_argument("--repo_url", type=str, help="URL to clone the repository from (might be local path)")
70+
repository.add_argument("--repo_local_path", type=str, help="Location to clone repository to (default: 'analysed_repositories' in CWD)")
71+
settings = parser.add_argument_group("Analysis Settings", "Parameters for additional analysis settings")
72+
settings.add_argument("--commit", type=str, help="Analyze repository at this commit")
73+
settings.add_argument("--development_mode", action='store_true', help="Switch on development mode")
7174

7275
args = parser.parse_args()
7376

7477
logger.info("*** New execution ***")
75-
logger.debug("Copying config file to tmp file")
7678

77-
if args.config_path is not None:
79+
if args.config_path:
7880
# Copy config to tmp file
81+
logger.debug("Copying config file to tmp file")
7982
tmp.tmp_config.read(args.config_path)
80-
repo_path = tmp.tmp_config.get("Repository", "path")
81-
82-
elif args.github_path is not None:
83+
else:
8384
# global ini_config
85+
logger.debug("Initializing tmp file with default config")
8486
for section in CONFIG_SECTIONS: # Copying what is needed from default to temp
8587
tmp.tmp_config.add_section(section)
8688
for entry in DEFAULT_CONFIG[section]:
8789
tmp.tmp_config.set(section, entry, DEFAULT_CONFIG[section][entry])
88-
repo_path = args.github_path.strip()
89-
tmp.tmp_config.set("Repository", "path", repo_path) # overwrite with user-provided path
9090

91-
local_path = get_local_path(repo_path)
92-
clone_repo(repo_path, local_path)
93-
tmp.tmp_config.set("Repository", "local_path", local_path)
94-
tmp.tmp_config.set("Analysis Settings", "output_path", get_output_path(repo_path))
91+
if args.repo_url:
92+
tmp.tmp_config.set("Repository", "url", args.repo_url)
93+
elif not tmp.tmp_config.has_option("Repository", "url"):
94+
raise AttributeError("Parameter [Repository][url] must be provided either in config file or by --repo_url")
95+
96+
if args.repo_local_path:
97+
tmp.tmp_config.set("Repository", "local_path", args.local_path)
98+
elif not tmp.tmp_config.has_option("Repository", "local_path"):
99+
tmp.tmp_config.set("Repository", "local_path", os.path.join(os.getcwd(), "analysed_repositories"))
95100

96-
# calling the actual extraction
97-
dfd_extraction.perform_analysis()
101+
if args.development_mode:
102+
tmp.tmp_config.set("Analysis Settings", "development_mode", "True")
98103

99-
now = datetime.now()
100-
end_time = now.strftime("%H:%M:%S")
104+
if args.commit is not None:
105+
commit = args.commit[:7]
106+
tmp.tmp_config.set("Analysis Settings", "commit", commit)
107+
elif tmp.tmp_config.has_option("Analysis Settings", "commit"):
108+
commit = tmp.tmp_config.get("Analysis Settings", "commit")[:7]
109+
tmp.tmp_config.set("Analysis Settings", "commit", commit)
101110

102-
print("\nStarted", start_time)
103-
print("Finished", end_time)
111+
perform_analysis()
104112

105113

106114
if __name__ == '__main__':
107-
main()
115+
cli_invocation()

config/config.ini

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
[Repository]
2-
path = apssouza22/java-microservice
3-
;path = callistaenterprise/blog-microservices
4-
;path = fernandoabcampos/spring-netflix-oss-microservices
5-
;path = georgwittberger/apache-spring-boot-microservice-example
6-
;path = mudigal-technologies/microservices-sample
7-
;path = spring-petclinic/spring-petclinic-microservices
8-
;path = sqshq/piggymetrics
9-
10-
;path = anilallewar/microservices-basics-spring-boot
11-
;path = ewolff/microservice
12-
;path = ewolff/microservice-kafka
13-
;path = jferrater/tap-and-eat-microservices
14-
;path = koushikkothagal/spring-boot-microservices-workshop
15-
;path = mdeket/spring-cloud-movie-recommendation
16-
;path = blaugmail/clone-sample-spring-oauth2-microservices
17-
;path = shabbirdwd53/springboot-microservice
18-
;path = rohitghatol/spring-boot-microservices
19-
;path = yidongnan/spring-cloud-netflix-example
2+
url = https://github.com/apssouza22/java-microservice
3+
;url = https://github.com/callistaenterprise/blog-microservices
4+
;url = https://github.com/fernandoabcampos/spring-netflix-oss-microservices
5+
;url = https://github.com/georgwittberger/apache-spring-boot-microservice-example
6+
;url = https://github.com/mudigal-technologies/microservices-sample
7+
;url = https://github.com/spring-petclinic/spring-petclinic-microservices
8+
;url = https://github.com/sqshq/piggymetrics
9+
;url = https://github.com/anilallewar/microservices-basics-spring-boot
10+
;url = https://github.com/ewolff/microservice
11+
;url = https://github.com/ewolff/microservice-kafka
12+
;url = https://github.com/jferrater/tap-and-eat-microservices
13+
;url = https://github.com/koushikkothagal/spring-boot-microservices-workshop
14+
;url = https://github.com/mdeket/spring-cloud-movie-recommendation
15+
;url = https://github.com/blaugmail/clone-sample-spring-oauth2-microservices
16+
;url = https://github.com/shabbirdwd53/springboot-microservice
17+
;url = https://github.com/rohitghatol/spring-boot-microservices
18+
;url = https://github.com/yidongnan/spring-cloud-netflix-example
2019

2120
[Technology Profiles]
2221
communication_techs_list = [("RabbitMQ", "rmq"), ("Kafka", "kfk"), ("RestTemplate", "rst"), ("FeignClient", "fgn"), ("Implicit Connections", "imp"), ("Database Connections", "dbc"), ("HTML", "html"), ("Docker-Compose", "dcm")]
2322

2423
[Analysis Settings]
2524
development_mode = False
25+
; commit for apssouza22/java-microservice
26+
;commit = 056414c4c938e536f467a3f37532194b860d96a3
2627

2728
[DFD]

core/dfd_extraction.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import ast
22
from datetime import datetime
33
from itertools import combinations
4+
import os
5+
6+
from pydriller import Repository
47

58
import output_generators.codeable_model as codeable_model
69
import core.technology_switch as tech_sw
@@ -49,14 +52,37 @@
4952

5053

5154
def perform_analysis():
52-
"""Main function for the extraction, calling all technology-specific extractors, managing output etc.
5355
"""
56+
Entrypoint for the DFD extraction that initializes the repository
57+
"""
58+
local_path = tmp.tmp_config.get("Repository", "local_path")
59+
url_path = tmp.tmp_config.get("Repository", "url")
60+
61+
os.makedirs(local_path, exist_ok=True)
62+
repository = Repository(path_to_repo=url_path, clone_repo_to=local_path)
63+
with repository._prep_repo(url_path) as git_repo:
64+
tmp.tmp_config.set("Repository", "local_path", str(git_repo.path))
65+
head = git_repo.get_head().hash[:7]
66+
if tmp.tmp_config.has_option("Analysis Settings", "commit"):
67+
commit = tmp.tmp_config.get("Analysis Settings", "commit")
68+
else:
69+
commit = head
70+
repo_name = git_repo.project_name
71+
tmp.tmp_config.set("Analysis Settings", "output_path", os.path.join(os.getcwd(), "code2DFD_output", repo_name.replace("/", "--"), commit))
72+
git_repo.checkout(commit)
73+
print(f"\nStart extraction of DFD for {repo_name} on commit {commit} at {datetime.now().strftime("%H:%M:%S")}")
74+
codeable_models, traceability_content = DFD_extraction()
75+
print(f"Finished: {datetime.now().strftime("%H:%M:%S")}")
76+
77+
git_repo.checkout(head)
78+
79+
return codeable_models, traceability_content
80+
5481

82+
def DFD_extraction():
83+
"""Main function for the extraction, calling all technology-specific extractors, managing output etc.
84+
"""
5585
dfd = CDFD("TestDFD")
56-
repo_path = tmp.tmp_config["Repository"]["path"]
57-
now = datetime.now()
58-
start_time = now.strftime("%H:%M:%S")
59-
print("\n\tStart extraction of DFD for " + repo_path + " at " + str(start_time))
6086

6187
microservices, information_flows, external_components = dict(), dict(), dict()
6288

0 commit comments

Comments
 (0)