xruben136x / SZZ_unisannio

Implementation of SZZ Algorithm
Mozilla Public License 2.0
4 stars 0 forks source link

build #9

Closed nik200122 closed 9 months ago

nik200122 commented 10 months ago

This issue has been opened to modify the build automation script to produce a package that can be used from command line, passing the name of the repository to analyze.

There is here the correlated chatGPT conversation:

Question: import git import re

%%

git diff between two commit

def get_diff(repo_path, commit_A, commit_B): repo = git.Repo(repo_path) diff = repo.git.diff(commit_A, commit_B, '-U0', '--histogram') return diff

%%

get the dictionary where the key is the file path and the value is a list of numbers of the changed lines

def generate_changes_dict(diff_output): file_path_pattern = re.compile(r'^+++ b/(.*)$') line_number_pattern = re.compile(r'^@@ -(\d+)(,(\d+))? +(\d+)(,(\d+))? @@')

result_dict = {}
current_file_path = None
numbers_list = []

diff_lines = diff_output.split('\n')

for line in diff_lines:
    file_path_match = file_path_pattern.match(line)
    line_number_match = line_number_pattern.match(line)

    if file_path_match:
        if current_file_path and numbers_list:
            result_dict[current_file_path] = numbers_list
            numbers_list = []

        current_file_path = file_path_match.group(1)
    elif line_number_match:
        start_line = int(line_number_match.group(1))
        num_lines = 1 if line_number_match.group(3) is None else int(line_number_match.group(3))

        # Aggiungi le linee modificate solo se non sono commenti
        if not match_comment(line):
            numbers_list.extend(range(start_line, start_line + num_lines))

if current_file_path and numbers_list:
    result_dict[current_file_path] = numbers_list

return result_dict

%% md

Funzione per non considerare le modifiche che riguardano commenti( cattura quelli su singola linea, le linee che iniziano per / o <!– e quelle che finiscono per –!> o /

%%

def match_comment(line): comment_pattern = re.compile(r'^\s(#|//|<!--|/*)|(?:.?--!>|.?*/)\s$')

return comment_pattern.match(line[1:])  # Ignora il primo carattere perchè le linee iniziano per '-'

%%

def get_candidate_commits(blame_result, file_path, changes_dict):

Definisci il pattern delle espressioni regolari

pattern = re.compile(r'([a-f0-9]+)\s+(\d+)\s+(\d+)?(?:\s+(\d+))?\nauthor\s+([^\n]+)')

# Inizializza il set di commit
commit_set = set()

# Trova tutte le corrispondenze nel testo di output
matches = pattern.findall(blame_result)

# Estrai le informazioni desiderate
for match in matches:
    commit_hash, first_number, second_number, third_number, author = match

    # Controlla se il secondo numero è nella lista associata al percorso del file
    if int(second_number) in changes_dict.get(file_path, []):
        # Aggiungi le informazioni richieste al set
        commit_set.add((commit_hash, author))

# Restituisci il set di commit
return commit_set

%%

def get_all_candidate_commits(repo, parent_commit, changes_dict): all_candidate_commits = set()

for file_path, line_numbers in changes_dict.items():
    blame_result = repo.git.blame(parent_commit, file_path, "--line-porcelain")
    candidate_commits = get_candidate_commits(blame_result, file_path, changes_dict)
    all_candidate_commits = all_candidate_commits.union(candidate_commits)

return all_candidate_commits

%%

retrieve commit from the repo

repository_url = "/Users/guido/Documents/Progetto/tensorflow" repo = git.Repo(repository_url) commits = repo.iter_commits()

%%

retrieve bug fix commit

bug_fix_commits = []

for commit in commits: commit_message = commit.message.lower() if 'bug' in commit_message and 'fix' in commit_message: bug_fix_commits.append(commit)

%%

retrieve only one commit and his parent

bug_fix_commit = bug_fix_commits[0] parent_commit = bug_fix_commit.parents[0]

%%

diff = get_diff(repository_url, bug_fix_commit, parent_commit) print(diff)

%%

changes_dict = generate_changes_dict(diff) print(changes_dict)

%%

all_candidate_commits = get_all_candidate_commits(repo, parent_commit, changes_dict)

%% md

%%

print(bug_fix_commit) print("Candidate commits: ") print(all_candidate_commits)

Questo è il file .ipynb su cui è scritto il codice. Sei in grado di modificarlo aggiungendo una variabile d'ambiente che rappresenta il nome del repository per poter far sì che durante l'esecuzione gli venga passata questa variabile che sarà usata per settare il nome del repository locale da cui prendere le informazioni?

Answer: Sì, puoi modificare il tuo notebook Jupyter aggiungendo una cella per impostare una variabile d'ambiente. Inoltre, potresti voler modificare la funzione get_diff per prendere in considerazione questa variabile d'ambiente quando si ottiene la differenza tra due commit.

Ecco un esempio di come potresti farlo:

python Copy code import os import git import re

Imposta la variabile d'ambiente con il nome del repository

os.environ['REPO_NAME'] = "nome_del_tuo_repository"

git diff tra due commit

def get_diff(commit_A, commit_B): repo = git.Repo(os.environ['REPO_NAME']) diff = repo.git.diff(commit_A, commit_B, '-U0', '--histogram') return diff

... Resto del tuo codice ...

In questo esempio, sto impostando la variabile d'ambiente REPO_NAME con il nome del tuo repository. Successivamente, la funzione get_diff utilizza questa variabile d'ambiente per ottenere la differenza tra due commit.

Ricorda di eseguire questa cella all'inizio del tuo notebook prima di chiamare altre funzioni che dipendono da questa variabile d'ambiente.

Quando eseguirai il tuo notebook, l'ambiente sarà configurato correttamente, e il nome del repository verrà utilizzato nelle funzioni che ne fanno uso.

Question:

This is a basic workflow to help you get started with Actions

name: build

Controls when the workflow will run

on:

Triggers the workflow on push or pull request events but only for the "main" branch

push: branches: [ "main" ] pull_request: branches: [ "main" ]

Allows you to run this workflow manually from the Actions tab

workflow_dispatch:

A workflow run is made up of one or more jobs that can run sequentially or in parallel

jobs:

This workflow contains a single job called "build"

build:

The type of runner that the job will run on

runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
  # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
  - name: Checkout repository
    uses: actions/checkout@v3

  # Runs a single command using the runners shell
  - name: Set up Python
    uses: actions/setup-python@v2
    with:
      python-version: 3.12

  - name: Install dependencies
    run: |
      pip install -r requirements.txt
  - name: Convert Jupyter Notebooks to Python
    run: |
      jupyter nbconvert --to script --output-dir=src notebooks/*.ipynb

Questo è il mio file build.yaml. Vorrei estenderlo per fare la build effettiva del progetto producendo un package che potrà essere poi eventualmente salvato su GitHub e scaricato dagli utenti. Come posso fare?

Answer: Per creare un package del tuo progetto Python, puoi seguire alcuni passaggi aggiuntivi nel tuo workflow. Assicurati di avere una struttura del progetto che sia adatta per la distribuzione, ad esempio con un file setup.py e un file MANIFEST.in. Ecco come potrebbe apparire una configurazione estesa del tuo file build.yaml:

yaml Copy code name: build

on: push: branches: [ "main" ] pull_request: branches: [ "main" ] workflow_dispatch:

jobs: build: runs-on: ubuntu-latest

steps:
  - name: Checkout repository
    uses: actions/checkout@v3

  - name: Set up Python
    uses: actions/setup-python@v2
    with:
      python-version: 3.12

  - name: Install dependencies
    run: |
      pip install -r requirements.txt

  - name: Convert Jupyter Notebooks to Python
    run: |
      jupyter nbconvert --to script --output-dir=src notebooks/*.ipynb

  - name: Build package
    run: |
      python setup.py sdist bdist_wheel

  - name: Upload package to GitHub Releases
    uses: softprops/action-gh-release@v1
    with:
      files: dist/*
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Spieghiamo i passaggi aggiuntivi:

Build Package: Dopo la conversione dei notebook, puoi eseguire il comando python setup.py sdist bdist_wheel per creare un pacchetto del tuo progetto. Assicurati che il tuo progetto abbia un file setup.py configurato correttamente. Upload to GitHub Releases: Puoi usare un'azione come softprops/action-gh-release per caricare il pacchetto creato nella sezione dei rilasci di GitHub. Assicurati di avere configurato correttamente il tuo file setup.py con le informazioni necessarie per il pacchetto. Nota: Assicurati di sostituire dist/* con il percorso corretto dei tuoi pacchetti. Il token di GitHub (${{ secrets.GITHUB_TOKEN }}) viene automaticamente generato da GitHub Actions e ti dà le autorizzazioni necessarie per caricare il pacchetto.

Questa configurazione assume che il tuo progetto sia configurato correttamente per la distribuzione Python. Ad esempio, dovresti avere un file setup.py nella radice del tuo progetto con le informazioni del tuo progetto. Verifica che il tuo progetto sia pronto per la distribuzione prima di implementare questa configurazione.

Question: Mi descrivi dettagliatamente cos'è un file setup.py e cosa dovrebbe contenere all'interno?

Answer: Il file setup.py è un file di configurazione utilizzato per definire le informazioni del tuo progetto Python, tra cui il nome, la versione, gli autori, le dipendenze e altre informazioni pertinenti. Questo file è essenziale per la distribuzione del tuo progetto, e viene utilizzato da strumenti come setuptools per gestire l'installazione, la distribuzione e altre operazioni correlate al pacchetto.

Ecco una breve panoramica delle sezioni principali che potresti includere in un file setup.py:

python Copy code from setuptools import setup, find_packages

setup( name='nome-del-tuo-progetto', version='1.0.0', packages=find_packages(), install_requires=[

Lista delle dipendenze del tuo progetto

],
entry_points={
    'console_scripts': [
        # Definisci eventuali script da eseguire dalla console
    ],
},
author='Tuo Nome',
author_email='tua@email.com',
description='Breve descrizione del tuo progetto',
url='https://github.com/tuo/nome-del-tuo-progetto',
classifiers=[
    'Development Status :: 3 - Alpha',
    'Intended Audience :: Developers',
    'License :: OSI Approved :: MIT License',
    'Programming Language :: Python :: 3',
    'Programming Language :: Python :: 3.6',
    'Programming Language :: Python :: 3.7',
    # Altri classifiers pertinenti al tuo progetto
],

) Ecco alcune delle sezioni chiave:

name: Il nome univoco del tuo pacchetto. version: La versione del tuo pacchetto. packages: Una lista di pacchetti inclusi nel tuo progetto. Puoi usare find_packages() per trovare automaticamente tutti i pacchetti nel tuo progetto. install_requires: Le dipendenze necessarie per eseguire il tuo progetto. entry_points: Definisce eventuali script o console entry points che dovrebbero essere installati quando il pacchetto è installato. author: Il nome dell'autore del progetto. author_email: L'indirizzo email dell'autore. description: Una breve descrizione del tuo progetto. url: URL del tuo repository su GitHub o in un altro sistema di controllo del codice sorgente. classifiers: Una lista di classificatori per informare su aspetti specifici del tuo progetto. Assicurati di personalizzare queste informazioni in base alle esigenze del tuo progetto. Una volta configurato correttamente, il file setup.py sarà utilizzato da strumenti come setuptools per creare distribuzioni del tuo progetto, rendendolo facilmente installabile da altri sviluppatori.

Question: from setuptools import setup, find_packages

setup( name='nome-del-tuo-progetto', version='1.0.0', packages=find_packages(), install_requires=[

Lista delle dipendenze del tuo progetto

],
entry_points={
    'console_scripts': [
        # Definisci eventuali script da eseguire dalla console
    ],
},
author='Tuo Nome',
author_email='tua@email.com',
description='Breve descrizione del tuo progetto',
url='https://github.com/tuo/nome-del-tuo-progetto',
classifiers=[
    'Development Status :: 3 - Alpha',
    'Intended Audience :: Developers',
    'License :: OSI Approved :: MIT License',
    'Programming Language :: Python :: 3',
    'Programming Language :: Python :: 3.6',
    'Programming Language :: Python :: 3.7',
    # Altri classifiers pertinenti al tuo progetto
],

) All'interno del file setup.py cosa rappresenta "entry_points"? E console_script all'interno?

Answer: Dentro il file setup.py, la sezione entry_points viene utilizzata per definire punti di ingresso nel tuo progetto. In particolare, la sotto-sezione 'console_scripts' viene utilizzata per definire script da eseguire dalla console (comandi da terminale). Questo è spesso utilizzato per dichiarare comandi associati al tuo progetto che saranno disponibili globalmente una volta che il tuo pacchetto è stato installato.

Ecco come funziona:

python Copy code entry_points={ 'console_scripts': [ 'comando-da-eseguire=modulo:funzione', ], }, 'console_scripts': Indica che stai definendo script della console. 'comando-da-eseguire': Questo è il nome del comando che sarà disponibile nella console una volta che il tuo pacchetto è stato installato. Puoi chiamarlo come preferisci. 'modulo:funzione': Questo specifica quale modulo e quale funzione deve essere eseguita quando il comando viene chiamato. Ad esempio, se hai un modulo chiamato mio_modulo e una funzione chiamata mio_comando, dovrebbe apparire come 'mio_modulo:mio_comando'. Quindi, se nel tuo progetto hai uno script Python chiamato mio_script.py con una funzione mio_comando, e nel tuo file setup.py hai:

python Copy code entry_points={ 'console_scripts': [ 'comando-da-eseguire=mio_script:mio_comando', ], }, Dopo aver installato il tuo pacchetto, potrai eseguire il comando comando-da-eseguire dalla console e questo chiamerà la funzione mio_comando dal modulo mio_script.