
    _in                        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	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j0                  d ej2                  d       ej4                  ej6                        g        ej8                  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
Quick scan orchestration script for Bitbucket SonarQube automation.
Scans only the top 20 repositories with the most recent commits.
    N)Path)datetime)DictOptionalListTuple)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ee   dedee   fdZddZy)ScanOrchestratorzIOrchestrates the entire scanning workflow for quick scans (top 20 repos).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       2/home/batmin/projects/sonar_scan/src/main_quick.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_historyR   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: )r6   r+   r7   dumpr-   r9   r:   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   fromisoformatr9   )r.   rF   date_strs      r/   _get_last_scan_datez$ScanOrchestrator._get_last_scan_dated   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&   r5   r   nowstrftimelistgloblen)r.   rF   r%   	today_strpatternmatching_filess         r/   _has_report_for_todayz&ScanOrchestrator._has_report_for_todayw   s~     4;;??+ABC!!# LLN++H5	 Kq62 k..w78>"Q&&r1   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)rH   rW   rX   )r   rN   	isoformatstrr-   rE   )r.   rF   rW   rX   s       r/   _update_scan_historyz%ScanOrchestrator._update_scan_history   sE     "113/:3{+(
)$
 	r1   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/rA   slugclonessh)namehref)rd   ra   r   linkszhttps://github.com/httpsrd   r   ssh_url	clone_urlr   N)
r   r&   
isinstancer[   
startswithreplacesplitrR   appenddict)r.   additional_repos_configreposrepo_configpathpartsr   	repo_nameorgrh   ws_names              r/   _get_additional_repositoriesz-ScanOrchestrator._get_additional_repositories   s    #'++//2TVX"Y&I2 M	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 ) )%._"#). @&"		" 	IM	^ r1   rq   limitc                    t         j                  dt        |       d| d       g }g }|D ]h  }|j                  d      xs |j                  d      }|s+|j                  di       j                  d      }|| j                  j                  d      k7  r|j                  |       |	 | j                  j                  |      }|r|j                  d      r	 |j                  d      }	t        j                  |	j                  d	d
            }
|
j                  r|
j                  d      }
|j                  |
|f       t         j                  d| d|
        n:t         j                  d| d       |j                  t        j                  |f       k |j!                  d d       |d| D cg c]  \  }}|	 }}}t         j                  dt        |       d       |rt#        |dd d      D ]D  \  }}|j                  d      xs |j                  d      }t         j                  d| d|        F t        |      dkD  r%t         j                  dt        |      dz
   d       |r3t         j                  dt        |       d       |j%                  |       |S # t        $ rG}t         j                  d| d|        |j                  t        j                  |f       Y d}~]d}~ww xY w# t        $ rG}t         j                  d| d|        |j                  t        j                  |f       Y d}~d}~ww xY wc c}}w ) a8  
        Get repositories sorted by most recent commit date.
        
        Args:
            repos: List of repository info dictionaries
            limit: Maximum number of repos to return
            
        Returns:
            List of top N repositories sorted by commit date (most recent first)
        zFetching commit dates for z repositories to find the z most recent...rd   ra   r   r   dateZz+00:00N)tzinfozFound commit date for : z Failed to parse commit date for z Could not fetch commit date for z, using fallbackzError fetching commit date for c                     | d   S )Nr    )xs    r/   <lambda>zAScanOrchestrator._get_repos_with_recent_commits.<locals>.<lambda>6  s
    AaD r1   T)keyreversez	Selected z& repositories with most recent commits   r   z  z. z
  ... and z morezAdding z$ additional repositories from config)r:   inforR   r&   r   rn   r'   get_latest_commitr   rI   rl   r}   debugr9   r;   minsort	enumerateextend)r.   rq   ry   repos_with_datesrepos_without_dates	repo_inforF   workspace_sluglatest_commitcommit_date_strcommit_dater>   rM   selected_reposis                  r/   _get_repos_with_recent_commitsz/ScanOrchestrator._get_repos_with_recent_commits   s/    	0U<VW\V]]lmn8:*, &	CI!f-Fv1FI ']];;??GN1F!GG#**95C $ @ @ K ]%6%6v%>K*7*;*;F*C&.&<&<_=T=TUXZb=c&d&--*5*=*=T*=*JK(//i0HI'=i[;-%XY NN%Ei[P`#ab$++X\\9,EFE&	CR 	.$? 9I%8PQ9)QQiN 344Z[\ ).!*<a @ 39%MM&1JY]]65J	b2i[123 >"Q&j^)<q)@(AGH KK'#&9":!;;_`a!!"56C % K)I)TVWXVY'Z[(//y0IJJK  C!@2aSQR ''y(ABBC RsI   ).K:BJ';K:4M'	K70<K2,K:2K77K::	M
<MM
c           	         t         j                  d       | j                  j                         xs g }|st         j	                  d       yt         j                  dt        |       d       | j                  |d      }|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-                         j/                  d)d(      j/                  d*d(      }ddl}|j3                  d+d(|      }|j                  d      xs |}|j                  d,d-      xs d-}t         j                  d.| d/| d0       	 | j4                  j7                  |	||1      }| j4                  j;                  |      }|st         j	                  d4|        |dz  }| j4                  j=                  |      st         j                  d5| d6       |j                  d      xs |}|j                  d,d-      xs d-}	 t?        |d7d89      5 }|D ]F  }|jA                         jC                  d:      s#|jE                  d;d      d   jA                         } n ddd       | j4                  jG                  |||<      rt         j                  d=| d>| d0       n3t         j	                  d?| d@       nt         jI                  d5| dA       | j4                  jK                  |	|      }| j4                  j;                  |      }d}|rA|j                  dB      r0ddl&}|jO                  dC       | j4                  jQ                  |      }	 | jR                  jU                  ||||j                  dD      E      }t         j                  dF|        | j#                  ||j                  dBd      |       |j                  dB      r|dz  }t         j                  dH|        n/|dz  }t         j!                  dI| d3|j                  dJ              C t         j                  dM       t         j                  dN       t         j                  dOt        |              t         j                  dP|        t         j                  dQ|        t         j                  dR|        t         j                  dM       y# t8        $ r+}t         j!                  d2| d3|        |dz  }Y d}~d}~ww xY w# 1 sw Y   xY w# t8        $ r Y w xY w# t8        $ r(}t         j!                  dG| d3|        d}Y d}~d}~ww xY w# t8        $ r?}t         j!                  dK| d3| dL       |dz  }| j#                  |d       Y d}~d}~ww xY w)Sz4Run the complete scanning workflow for top 20 repos.zKStarting SonarQube quick scan automation (top 20 repos with recent commits)z"No repositories found in workspaceNzFound z repositories in workspace   )ry   z%No repositories selected for scanningzProcessing z) repositories (top 20 + additional repos)r   rd   ra   z'Skipping repository with no name/slug: zProcessing repository: r   r_   z*Repository folder is empty or missing for z, cloning...rc   )protocolzNo SSH clone URL found for z
, skippingr   )rF   ri   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 rM   - 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 r~   z@Could not extract project key from sonar-project.properties for z	Project 'z-' does not exist in SonarQube, creating it...r4   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 SonarQuberW   r   output)ru   scan_resultssonar_resultsscan_outputzReport generated: zFailed to generate report for zSuccessfully scanned zScan failed for rD   zUnexpected error processing exc_infoz<============================================================zQuick Scan Summary:z   Total repositories processed: z  Scanned: z  Skipped: z
  Failed: )+r:   r   r'   get_all_repositoriesr;   rR   r   r&   r   r   r5   get_clone_urlr(   clone_or_updaterj   tuplerD   r\   rV   rK   has_changes_sinceget_sonar_properties_pathlowerrl   resubr)   generate_sonar_propertiesr9   get_project_keycheck_project_existsr6   striprk   rm   create_projectr   run_scantimesleepget_scan_resultsr*   generate_report) r.   	all_reposrq   scanned_countskipped_countfailed_countr   rF   r   r   is_empty_or_missingri   clone_resultis_first_clonehas_today_reportr   is_additional_repolast_scan_datehas_changessonar_props_pathr   r   r   r   r   r>   r=   liner   r   r   rX   s                                    r/   r   zScanOrchestrator.run_scanK  s;	   ab NN779?R	NN?@fS^,,FGH 33IR3HNNBCk#e*-VWX  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)*6s5zlCD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(d8	A#d8.E
d8:A/d8+A#d8C%d86b0:d8Ad80c4>%c'$&c'
c4C*d8=AdBd80	c$9 cd8c$$d8'c1	,c44	d=d8 dd8	d5d0*d80d55d88	f 4e;;f )r2   N)N)r   )__name__
__module____qualname____doc__r	   r0   r   r,   rE   r[   r   r   rK   boolrV   r\   rP   rx   r   intr   r   r   r1   r/   r   r   (   s    S%1} %1Nt >S Xh5G &'s 't '0c D xX[} hl  \d \|LDJ Ls LTXY]T^ L\wr1   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&   r:   rD   sysexitr   save_configr   r   KeyboardInterruptr   r9   )r   has_api_tokenhas_app_passwordorchestratorr>   s        r/   mainr   E  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   r7   loggingpathlibr   r   typingr   r   r   r   config_managerr	   bitbucket_clientr
   r(   r   sonarqube_scannerr   r*   r   ImportErrorbasicConfigINFOFileHandlerStreamHandlerstdout	getLoggerr   r:   r   r   r   r1   r/   <module>r      s         . .2,0&20   
,,A,-cjj) 
		8	$Z Zz'T zF y  2-1'312s   B> >#C$#C$