
    _i^                        d Z ddlZddlZddlZddlmZ ddlmZ ddlmZm	Z	 	 ddl
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ  ej*                  ej,                  d ej.                  d       ej0                  ej2                        g        ej4                  e      Z G d d      Zd Zedk(  r e        yy# e$ r! ddl
mZ ddlmZ ddlmZ dd	lmZ dd
lmZ Y w xY w)z?
Main orchestration script for Bitbucket SonarQube automation.
    N)Path)datetime)DictOptional)ConfigManager)BitbucketClient)
GitManager)SonarQubeScanner)ReportGenerator   z4%(asctime)s - %(name)s - %(levelname)s - %(message)szsonar_scan.log)levelformathandlersc            	           e Zd ZdZdefdZdefdZddZde	de
e   fd	Zde	defd
Zdde	dede
e	   ddfdZdefdZddZy)ScanOrchestratorz*Orchestrates the entire scanning workflow.configc                 *   || _         |j                  d      }|j                  d      }t        |j                  d      |j                  d      |s|nd|      | _        t	        |j                  d            | _        t        |j                  d	      |j                  d
      |j                  d            | _        t        |j                  d            | _	        t        |j                  d            | _        | j                         | _        y)zt
        Initialize orchestrator.
        
        Args:
            config: Configuration manager instance
        bitbucket.api_tokenbitbucket.app_passwordbitbucket.workspacebitbucket.usernameN)	workspaceusernameapp_password	api_tokenscanning.repos_dir)	repos_dirzsonarqube.urlsonarqube.tokenzscanning.sonar_scanner_path)	sonar_urlsonar_tokenscanner_pathscanning.reports_dir)reports_dirzscanning.history_file)r   getr   	bitbucketr	   git_managerr
   sonar_scannerr   report_generatorr   history_file_load_historyscan_history)selfr   r   r   s       ,/home/batmin/projects/sonar_scan/src/main.py__init__zScanOrchestrator.__init__*   s      JJ45	zz":;(jj!67ZZ 45-6D	
 &jj!56
 .jj1

#45$AB
 !0

#9:!

 !,C!DE ..0    returnc                    | j                   j                         r6	 t        | j                   d      5 }t        j                  |      cddd       S i S # 1 sw Y   i S xY w# t
        $ r#}t        j                  d|        Y d}~i S d}~ww xY w)z!Load scan history from JSON file.rNzFailed to load scan history: )r)   existsopenjsonload	Exceptionloggerwarningr,   fes      r-   r*   zScanOrchestrator._load_historyQ   s    ##%D$++S1 (Q99Q<( ( 		( 	  D!>qcBCC	Ds4   A  A	A  AA  A   	B)BBNc                     	 t        | j                  d      5 }t        j                  | j                  |d       ddd       y# 1 sw Y   yxY w# t
        $ r"}t        j                  d|        Y d}~yd}~ww xY w)zSave scan history to JSON file.w   )indentNzFailed to save scan history: )r4   r)   r5   dumpr+   r7   r8   errorr:   s      r-   _save_historyzScanOrchestrator._save_history[   sj    	>d''- :		$++Qq9: : : 	>LL8<==	>s3   A #AA A	A A 	A;A66A;	repo_slugc                     || j                   v r6| j                   |   j                  d      }|r	 t        j                  |      S y# t        $ r Y yw xY w)z
        Get last scan date for a repository.
        
        Args:
            repo_slug: Repository slug/name
            
        Returns:
            Last scan datetime or None
        	last_scanN)r+   r$   r   fromisoformatr7   )r,   rD   date_strs      r-   _get_last_scan_datez$ScanOrchestrator._get_last_scan_datec   sb     )))((377DH#11(;;  ! s   A 	AAc                    t        | j                  j                  d            }|j                         syt	        j
                         j                  d      }| d| d}t        |j                  |            }t        |      dkD  S )z
        Check if a report exists for today's date for the repository.
        
        Args:
            repo_slug: Repository slug/name
            
        Returns:
            True if a report exists for today, False otherwise
        r"   Fz%Y%m%d_z_*.pdfr   )
r   r   r$   r3   r   nowstrftimelistgloblen)r,   rD   r#   	today_strpatternmatching_filess         r-   _has_report_for_todayz&ScanOrchestrator._has_report_for_todayv   s~     4;;??+ABC!!# LLN++H5	 Kq62 k..w78>"Q&&r/   successreport_pathc                     t        j                         j                         ||rt        |      ndd| j                  |<   | j                          y)z
        Update scan history for a repository.
        
        Args:
            repo_slug: Repository slug/name
            success: Whether scan was successful
            report_path: Path to generated report
        N)rF   rU   rV   )r   rL   	isoformatstrr+   rC   )r,   rD   rU   rV   s       r-   _update_scan_historyz%ScanOrchestrator._update_scan_history   sE     "113/:3{+(
)$
 	r/   c                    | j                   j                  dg       }|sg S g }|D ]N  }t        |t              rH|j	                  d      rf|j                  dd      j                  dd      }|j                  d      }t        |      dk(  sh|\  }}|j                  ||d|idd	|d
gid       |j	                  d      rk|j                  dd      j                  dd      }|j                  d      }t        |      dk(  s|\  }}|j                  ||d|idd	|d
d|d
gid       d|v s|j                  d      }t        |      dk(  s/|\  }}d| d| d}	|j                  ||d|idd	|	d
gid       ]t        |t              so|j                  d      xs |j                  d      }|j                  d      }|j                  d      xs |j                  d      }	|s|	st        |t              rd|i}nJ|sHd|	v rD|	j                  dd      j                  dd      }d|v r|j                  d      d   nd}
|
rd|
ini }|j                  |||xs i dd	|	d
gid       Q |S )z
        Get additional repositories from configuration.
        These can be from different workspaces.
        
        Returns:
            List of repository info dictionaries
        z scanning.additional_repositorieszgit@bitbucket.org: .git/r?   slugclonessh)namehref)rb   r_   r   linkszhttps://github.com/httpsrb   r   ssh_url	clone_urlr   N)
r   r$   
isinstancerY   
startswithreplacesplitrP   appenddict)r,   additional_repos_configreposrepo_configpathpartsr   	repo_nameorgrf   ws_names              r-   _get_additional_repositoriesz-ScanOrchestrator._get_additional_repositories   s    #'++//2TVX"Y&I2 N	K+s+ ))*>?&../CRHPPQWY[\D JJsOE5zQ/4,	9$-$-*0))< '-2K$H*"&		& 	 !++,AB&../DbIQQRXZ\]D JJsOE5zQ).Y$-$-*0# '-2K$H-4k$J*"&	
& 
 K''--c2E5zQ/4,	9$6yk9+T"R$-$-*0))< '-2G$D*"&		& 	 K.'OOF3N{v7N	'OOK8	%//)4T8T!)S1%+Y$7	&/7:#*??3G#L#T#TU[]_#`D<?4Kdjjoa&8TG=D(9"ILL ) )%._"#). @&"		" 	KN	` r/   c           	         t         j                  d       | j                  j                         xs g }| j	                         }|r3t         j                  dt        |       d       |j                  |       |st         j                  d       yt         j                  dt        |       d       d}d}d}|D ]@  }|j                  d      xs |j                  d	      }|st         j                  d
|        Ct         j                  d|        	 t        | j                  j                  d            }||z  }	|	j                          xs |	dz  j                          }
|
rt         j                  d| d       | j                  j                  |d      }|s t         j                  d| d       |dz  }| j                  j                  ||      }|rt        |t               r6|d   s1t         j#                  d|        |dz  }| j%                  |d       kt        |t               r|\  }	}n%|}	d}n| j'                  |      }|st         j                  d| d       |j                  di       j                  d	      }|| j                  j                  d      k7  }|st         j                  d|        nt         j                  d|        n|j                  di       j                  d	      }|| j                  j                  d      k7  }|si| j)                  |      }| j                  j+                  ||      }|s t         j                  d| d        |dz  }t         j                  d!| d"       nt         j                  d|        | j                  j                  |d      }|s t         j                  d| d       |dz  }(| j                  j                  ||      }|rt        |t               r6|d   s1t         j#                  d#|        |dz  }| j%                  |d       t        |t               r|\  }	}n|}	d}| j                  j-                  |	      }|s| j                  j                  d      }| d$| j/                         j1                  d%d$      j1                  d&d$      }ddl}|j5                  d'd$|      }|j                  d      xs |}|j                  d(d)      xs d)}t         j                  d*| d+| d,       	 | j6                  j9                  |	||-      }| j6                  j=                  |      }|st         j                  d0|        |dz  }| j6                  j?                  |      st         j                  d1| d2       |j                  d      xs |}|j                  d(d)      xs d)}	 tA        |d3d45      5 }|D ]F  }|jC                         jE                  d6      s#|jG                  d7d      d   jC                         } n ddd       | j6                  jI                  |||8      rt         j                  d9| d:| d,       n3t         j                  d;| d<       nt         jK                  d1| d=       | j6                  jM                  |	|      }| j6                  j=                  |      }d}|rA|j                  d>      r0ddl'}|jQ                  d?       | j6                  jS                  |      }	 | jT                  jW                  ||||j                  d@      A      }t         j                  dB|        | j%                  ||j                  d>d      |       |j                  d>      r|dz  }t         j                  dD|        n/|dz  }t         j#                  dE| d/|j                  dF              C t         j                  dI       t         j                  dJ       t         j                  dKt        |              t         j                  dL|        t         j                  dM|        t         j                  dN|        t         j                  dI       y# t:        $ r+}t         j#                  d.| d/|        |dz  }Y d}~d}~ww xY w# 1 sw Y   xY w# t:        $ r Y w xY w# t:        $ r(}t         j#                  dC| d/|        d}Y d}~d}~ww xY w# t:        $ r?}t         j#                  dG| d/| dH       |dz  }| j%                  |d       Y d}~d}~ww xY w)Oz#Run the complete scanning workflow.z"Starting SonarQube scan automationzFound z$ additional repositories from configzHNo repositories found in workspace or additional repositories configuredNz repositories to checkr   rb   r_   z'Skipping repository with no name/slug: zProcessing repository: r   r]   z*Repository folder is empty or missing for z, cloning...ra   )protocolzNo SSH clone URL found for z
, skippingr   )rD   rg   zFailed to clone FTzNo report found for today for z, will scanr   r   zUpdating repository: z Updating additional repository: zNo changes detected for z$, skipping (report exists for today)zChanges detected for z, updating...zFailed to update rK   - z
[^a-z0-9_]descriptionr\   z(Generating sonar-project.properties for z (project key: ))	repo_pathproject_keyproject_namez0Failed to generate sonar-project.properties for z: z@Could not extract project key from sonar-project.properties for z	Project 'z-' does not exist in SonarQube, creating it...r2   zutf-8)encodingzsonar.projectName=)r~   r   r{   z(Successfully created SonarQube project: z (z$Failed to create SonarQube project 'z', but continuing with scanz' already exists in SonarQuberU      output)rs   scan_resultssonar_resultsscan_outputzReport generated: zFailed to generate report for zSuccessfully scanned zScan failed for rB   zUnexpected error processing exc_infoz<============================================================zScan Summary:z  Total repositories: z  Scanned: z  Skipped: z
  Failed: ),r8   infor%   get_all_repositoriesrv   rP   extendr9   r$   r   r   r3   get_clone_urlr&   clone_or_updaterh   tuplerB   rZ   rT   rI   has_changes_sinceget_sonar_properties_pathlowerrj   resubr'   generate_sonar_propertiesr7   get_project_keycheck_project_existsr4   stripri   rk   create_projectdebugrun_scantimesleepget_scan_resultsr(   generate_report) r,   ro   additional_reposscanned_countskipped_countfailed_count	repo_inforD   r   r}   is_empty_or_missingrg   clone_resultis_first_clonehas_today_reportworkspace_slugis_additional_repolast_scan_datehas_changessonar_props_pathr   r~   r   r   r{   r<   r;   liner   r   r   rV   s                                    r-   r   zScanOrchestrator.run_scan   s3	   89 335;  <<>KK&%5!6 77[\]LL)*NNeffSZL(>?@  S	<I!f-Fv1FI!HTUKK1)=>K< 1E!FG	%	1	*3*:*:*<&<&aYQWEWD_D_Da@a# 'KK"LYKWc de !% < <YQV < WI$)DYKz'Z[%*  $(#3#3#C#C"+"+ $D $L
 (J|U,KT`abTc'7	{%CD$)11)UC  ",64@1	>$0	)- (,'A'A)'L$+&DYK{$[\)2{B)G)K)KF)S-;t{{Od?e-e*1"KK*?	{(KL"KK*J9+(VW *3{B)G)K)KF)S-;t{{Od?e-e*1-1-E-Ei-PN*...*J*J9Vd*eK#. &.FykQu,v w - 2 ("KK*?	{-(XY"KK*J9+(VW !% < <YQV < WI$)DYKz'Z[%*  $(#3#3#C#C"+"+ $D $L
 (J|U,KT`abTc'8%DE$)11)UC  ",64@1	>$0	). $(#3#3#M#Mi#X ' $0E FI%.Kq"<"B"B"D"L"LSRU"V"^"^_bdg"hK"$&&["IK $-==#8#EIL"+--r"B"HbKKK"J9+Udepdqqr st
!+/+=+=+W+W&/(3)5 ,X ,( #00@@AQR"NN%efoep#qr!Q&M ))>>{KKK)K=8e fg $-==#8#EIL"+--r"B"HbK!"2C'J *a() *#'::<#:#:;N#O37::c13Ea3H3N3N3PL$)** ))88$/%1$/ 9 
 &N{m[]^j]kkl$mn)Mk]Zu'vwLL9[M9V!WX  $11::9FVW #00@@AQR $<#3#3I#>JJqM$($6$6$G$G$TM
'"&"7"7"G"G"+%1&3$0$4$4X$>	 #H #K KK"4[M BC )) $$Y6  ##I.!Q&MKK"7	{ CD A%LLL#3I;bAQAQRYAZ@[!\]]S	<l 	HO$,SZL9:k-12k-12j/0H{ % !'WXaWbbdefdg%hi%* !,* *
 % H ! 'LL#A)Bqc!RS"&K'$  <;I;bLW[\!)))U;;<s   B(d0A#d0&E
d02A/d0#A#d0C%d0.b(:d0Ad0(c,6%c&cc,
C*d05Ac<;Bd0(	c1 cd0cd0c)	$c,,	c95d08c99d0<	d-d("d0(d--d00	e894e33e8)r0   N)N)__name__
__module____qualname____doc__r   r.   r   r*   rC   rY   r   r   rI   boolrT   rZ   rN   rv   r    r/   r-   r   r   '   s    4%1} %1Nt >S Xh5G &'s 't '0c D xX[} hl  ]d ]~tr/   r   c                     	 t               } | j                  d      s*t        j                  d       t	        j
                  d       t        | j                  d            }t        | j                  d            }| j                  d      s*t        j                  d       t	        j
                  d       |s,|s*t        j                  d       t	        j
                  d       | j                  d	      s*t        j                  d
       t	        j
                  d       | j                          t        |       }|j                          y# t        $ r- t        j                  d       t	        j
                  d       Y yt        $ r9}t        j                  d| d       t	        j
                  d       Y d}~yd}~ww xY w)zMain entry point.r   z"Bitbucket workspace not configuredr   r   r   r   z'Bitbucket username/email not configuredzOBitbucket authentication not configured (need either API token or app password)r   zSonarQube token not configuredzScan interrupted by userzFatal error: Tr   N)r   r$   r8   rB   sysexitr   save_configr   r   KeyboardInterruptr   r7   )r   has_api_tokenhas_app_passwordorchestratorr<   s        r-   mainr     sB   % zz/0LL=>HHQK VZZ(=>?

+C DEzz./LLBCHHQK%5LLjkHHQKzz+,LL9:HHQK 	 (/ ./ }QC(48s   EE 3G G/F<<G__main__)r   r   r5   loggingpathlibr   r   typingr   r   config_managerr   bitbucket_clientr   r&   r	   sonarqube_scannerr
   r(   r   ImportErrorbasicConfigINFOFileHandlerStreamHandlerstdout	getLoggerr   r8   r   r   r   r/   r-   <module>r      s         !2,0&20   
,,A,-cjj) 
		8	$J JZ'T zF Y  2-1'312s   B: :#C C 