
    i#                        d Z ddlZddlZddlZddlZddlZddlZddlmZmZm	Z	 ddl
mZ  e e	d            Z eej                  j                  dd            Zed	z  d
z  dz  Zed	z  dz  Z ej$                  ej&                  d        ej(                  d      ZdgdgdgdgdgdgdgdZdgZdeddfdZdedeeef   fdZdededee   fdZd ee   deee   ef   fd!Z ded"ee   deeef   fd#Z!d5d%ededed&e"def
d'Z#e$d(k(  r ejJ                         Z&e&jO                  d)d*+       e&jO                  d,d-.       e&jO                  d/d-.       e&jO                  d0e"d$1       e&jQ                         Z)e)jT                  r ee)jT                        neZ* e#e)jV                  e)jX                  e*e)jZ                        Z. e/ ej`                  e.d2d34             yy)6ux  
post_merge_probe.py - 머지 후 5분 health probe (task-2367 P1)

사용법:
    python3 scripts/post_merge_probe.py --task-id task-XXXX --merge-sha <sha> [--project-path <path>] [--delay 300]

동작:
    1. delay 초 대기 (기본 300초 = 5분)
    2. project_path에서 build/test 재실행
    3. 결과를 audit log에 기록
    4. FAIL 시 auto_revert.py 트리거
    N)datetimetimezone	timedelta)Path	   )hoursWORKSPACE_ROOTz/home/jay/workspacememoryauditzauto-merge.logeventsz'%(asctime)s [%(levelname)s] %(message)s)levelformatpost_merge_probezextension/__tests__/zserver/tests/ztests/skills/satori/ztests/skills/hybrid/ztests/scripts/zdashboard/tests/zsrc/__tests__/)z
extension/zserver/zskills/satori-cardnews/zskills/hybrid-image/zscripts/z
dashboard/zsrc/ztests/smoke/recordreturnc                    ddl }t        j                  j                  dd       t        j	                  d       t        t        dd      5 }t        d	 |D              d
z   }ddd       | j                  dt        j                  t              j                                | d<   t        t        dd      5 }|j                  |j                         |j                         	 |j                  t!        j"                  | d      dz          |j                  |j                         |j$                         	 ddd       y# 1 sw Y   xY w# |j                  |j                         |j$                         w xY w# 1 sw Y   yxY w)u>   audit log에 append-only JSONL로 기록 (fcntl.flock 사용).r   NTparentsexist_ok)r   rzutf-8)encodingc              3       K   | ]  }d   yw)   N ).0_s     I/home/jay/workspace/.worktrees/task-2464-dev6/scripts/post_merge_probe.py	<genexpr>z _append_audit.<locals>.<genexpr>7   s     !s   r   	timestampsequenceaF)ensure_ascii
)fcntl	AUDIT_LOGparentmkdirtouchopensum
setdefaultr   nowKST	isoformatflockfilenoLOCK_EXwritejsondumpsLOCK_UN)r   r$   fseqs       r   _append_auditr8   0   s"   4$7OOTO"	iw	/ $1Q!#$
k8<<#4#>#>#@AF:	iw	/ 31AHHJ.	3GGDJJvE:TABKK
EMM23 3	$ $ KK
EMM23 3s0   E:+E>&)E*E>E,E;;E>>Fproject_pathc           	      <   | dz  }|j                         rH	 t        j                  g d| ddd      }|j                  dk(  |j                  |j
                  z   dd fS 	 t        | j                  d            dd }|syt        j                  g d|D cg c]  }t        |       c}z   ddd      }|j                  dk(  |j                  |j
                  z   dd fS # t        $ r}d	d
| fcY d}~S d}~ww xY wc c}w # t        $ r}d	d| fcY d}~S d}~ww xY w)uR   프로젝트 빌드 실행. 성공 시 (True, output), 실패 시 (False, output).zpackage.json)npmrunbuildT,  cwdcapture_outputtexttimeoutr   0NFzbuild error: z*.py   )Tu   no build target — skipped)python3z-m
py_compile<   )rA   rB   rC   zcompile error: )
exists
subprocessr<   
returncodestdoutstderr	Exceptionlistglobstr)r9   package_jsonr   epy_filesr6   s         r   
_run_buildrU   B   s0   .0L
	0' #A LLA%188(;UV'DEE.))&122A68NN+x.H!s1v.HH	
 !AHHqxx$7#@AA  	0]1#.//	0 /I  .,--.sT   AC D ?D C<
'7D 	C9(C4.C94C9<D 	D
DDD	merge_shac                 .   |sg S 	 t        j                  dd| d| dg| ddd      }|j                  dk7  rg S |j                  j	                         D cg c]#  }|j                         s|j                         % c}S c c}w # t        $ r g cY S w xY w)	uF   git diff <merge_sha>~1..<merge_sha> --name-only — failures return []gitdiffz~1..z--name-onlyT   r?   r   )rJ   r<   rK   rL   
splitlinesstriprN   )r9   rV   r   lines       r   _changed_pathsr^   a   s    	NNFyki[9=I
 <<1I)*)<)<)>O$**,

OOO 	s.   3B B B,B>B B BBchanged_pathsc                     t               }| D ]H  }t        j                         D ]/  \  }}|j                  |      s|D ]  }|j	                  |        1 J |st
        dfS t        |      dfS )u   changed_paths에서 테스트 스코프를 결정한다.

    Returns:
        (test_paths, mode) where mode is "scoped" or "smoke" (full sweep 금지)
    smokescoped)set	SCOPE_MAPitems
startswithaddSMOKE_TEST_PATHSsorted)r_   matchedpathprefix	test_dirsds         r   _resolve_test_scopero   t   sz     G #!*!2 	#FIv&" #AKKN#	##
  '**7OX&&    
test_pathsc           	         |syg }|D ]A  }| |z  }|j                         r|j                  |       *t        j                  d|        C |sy	 t	        j
                  ddddt        |       dd	g|z   | d
d
d      }|j                  dk(  |j                  |j                  z   dd fS # t        $ r}dd| fcY d}~S d}~ww xY w)u  스코프 기반 테스트 실행.

    test_paths가 비어 있으면 부작용 없이 skip — full sweep 절대 금지.
    test_paths가 명시되면 존재하는 디렉토리만 pytest 인자로 전달하며,
    `--rootdir`로 conftest 탐색 범위를 project_path로 한정하고
    `-p no:cacheprovider`로 캐시 부작용을 차단한다.
    모든 test_paths가 존재하지 않으면 (True, "no scoped test target — skipped").
    )Tskippedu"   scoped test path 없음 — skip: )Tu!   no scoped test target — skippedpytestz
--tb=shortz-qz	--rootdirz-pzno:cacheproviderTiX  r?   r   rD   NFzpytest error: )is_dirappendloggerinforJ   r<   rQ   rK   rL   rM   rN   )r9   rq   existing_pathstp	full_pathr   rS   s          r   _run_tests_scopedr|      s       "N C 2%	!!"%KK<RDABC :-NNL!" 	 
  !AHHqxx$7#@AA -s+,,-s   AB( (	C1B=7C=Cr>   task_iddelayc                    t         j                  dd       t         |  dz  }|j                         rt        j	                  d|         d| dS |dkD  r0t        j	                  d| d	|         t        j                  |       t        ||      }|s	t        d
}}nt        |      \  }}t        |      \  }}	t        ||      \  }
}|xr |
}| |d|rdndd|rdnddg d||
|	dd |dd d|||dd}t        |       |j                  t        j                  |dd             |s|t        j!                  d|  d       	 t#        j$                  dt'        t(        dz  dz        d| d |d!t'        |      d"d#| d$|
 g
t"        j*                  t"        j*                  %       |S |S # t,        $ r#}t        j!                  d&|        Y d}~|S d}~ww xY w)'u   probe 실행 후 결과 반환.Tr   z.probe-doneu   probe 이미 실행됨: already_run)statusr}   r   zprobe delay u
   s 시작: ra   z
post-probe
probe_pass
probe_failzpost_merge_probe.pypassfailrs   iN)build_oktest_okbuild_out_tailtest_out_tail)r_   rq   mode)r}   rV   tieroutcomemerger	qc_resultscope_guard
diff_filesdiff_locprobescopeF   r"   indentzprobe FAIL: u    — auto_revert 트리거rF   scriptszauto_revert.py	--task-id--merge-sha--project-pathz--reasonzpost_merge_probe FAIL: build=z test=)rL   rM   u   auto_revert 호출 실패: )PROBE_MARK_DIRr'   rI   rw   rx   timesleepr^   rh   ro   rU   r|   r8   
write_textr3   r4   errorrJ   PopenrQ   	WORKSPACEDEVNULLrN   )r}   rV   r9   r~   	mark_filechangedscope_test_paths
scope_moder   	build_outr   test_outoverallr   rS   s                  r   	run_prober      s   5G9K!88I.wi89'G<<qyl5'G9=>

5 \95G(8'*':7'C$*$\2Hi),8HIGX"7G#*<'&VF  '.%de_	
 %*
!F. &FqIJ 	|G9,FGH	<C	I 58H HIg	!3|#4<XJfWIV	X
 "))!)) M6M  	<LL6qc:;;M	<s   A F% %	G.GG__main__r   T)requiredr    )defaultr   z--delay)typer   Fr   r   )r>   )1__doc__argparser3   loggingosrJ   r   r   r   r   pathlibr   r-   environgetr   r%   r   basicConfigINFO	getLoggerrw   rd   rh   dictr8   tupleboolrQ   rU   rO   r^   ro   r|   intr   __name__ArgumentParserparseradd_argument
parse_argsargsr9   r}   rV   r~   resultprintr4   r   rp   r   <module>r      su      	   2 2 yq!" 02GHI	 7*-==	X%0   
,,4 
		-	. **  6734!"%&	 ## 3$ 34 3$.T .eD#I&6 .> # $s) &'tCy 'U49c>5J '"+-D +-d3i +-E$PS)DT +-\Cs Cs C$ Cs CUY CL z$X$$&F
d3
r2
("5
	S9D.2.?.?4))*YLt||T^^\4::NF	*$**V%
:; rp   