
    ~i                       U d Z ddlmZ ddlZddlZddlmZ dZded<    eej                  j                  dd	            Zed
z  dz  dz  Zed
z  dz  dz  Z G d de      ZddZddZddZddddZddddZddZddd dZd!dZy)"u   bot author 정규화/검증 헬퍼.

회장 명시 (task-2481): PR author는 정확히 'jeon-jonghyuk-taskctl-bot[bot]' 1개만 허용.
"[bot]" suffix만으로 통과하지 않음 — canonical slug가 BOT_AUTHOR_ALLOWLIST에 있어야 함 (fail-closed).
    )annotationsN)Path)zjeon-jonghyuk-taskctl-bottuple[str, ...]BOT_AUTHOR_ALLOWLISTWORKSPACE_ROOTz/home/jay/workspacememoryspecszallowed_bot_accounts.jsonzallowed_approvers.jsonc                      e Zd ZdZy)BotAuthorErroru)   bot/human author 검증 실패 시 raise.N)__name__
__module____qualname____doc__     D/home/jay/workspace/.worktrees/task-2481-dev4/utils/bot_pr_author.pyr   r      s    3r   r   c                    	 t         j                         rRt        j                  t         j	                  d            } | j                  d      xs g }|rt        d |D              S t        d t        D              S # t        $ r Y !w xY w)uc   allowed_bot_accounts.json의 'exact' 목록 로드. 파일 없으면 BOT_AUTHOR_ALLOWLIST fallback.utf-8encodingexactc              3  X   K   | ]"  }|j                         j                          $ y wN)striplower.0ss     r   	<genexpr>z)load_allowed_bot_exact.<locals>.<genexpr>   s     >1QWWY__.>s   (*c              3  &   K   | ]	  }| d   yw)[bot]Nr   )r   slugs     r   r   z)load_allowed_bot_exact.<locals>.<genexpr>!   s     ADD6As   )	_ALLOWED_BOT_FILEexistsjsonloads	read_textgettuple	Exceptionr   )datar   s     r   load_allowed_bot_exactr,      s    ##%::/9979KLDHHW%+E>>>> A,@AAA  s   A%A> >	B
	B
c                     	 t         j                         rPt        j                  t         j	                  d            } | j                  d      xs g }t        d |D              S 	 y# t        $ r Y yw xY w)u8   allowed_approvers.json의 'manual_logins' 목록 로드.r   r   manual_loginsc              3  B   K   | ]  }|s|j                           y wr   )r   r   s     r   r   z/load_allowed_human_approvers.<locals>.<genexpr>*   s     8qa8s   r   )_ALLOWED_APPROVERS_FILEr$   r%   r&   r'   r(   r)   r*   )r+   manuals     r   load_allowed_human_approversr2   $   su    "))+::5???QRDXXo.4"F8F888 ,   s   A#A( (	A43A4c                    | sy| j                         j                         }|j                  d      r|dd }|j                  d      r|dd }|S )u   gh CLI('app/<slug>') / REST('<slug>[bot]') / 사람 login 모두 canonical slug로 정규화.

    - 'app/foo' → 'foo'
    - 'foo[bot]' → 'foo'
    - 'foo' → 'foo' (소문자)
     app/   Nr!   )r   r   
startswithendswith)loginr"   s     r   normalize_actorr;   0   sQ     ;;= DvABx}}WCRyKr   strictc                  | sy|"t         j                  j                  d      dk(  }t        |       }|r|t        v S | j                         j                         }|j                  d      s|j                  d      ry|t        v S )u  PR author가 허용된 bot인지 검증.

    strict=True (default for task-2481 commands): canonical slug가 BOT_AUTHOR_ALLOWLIST에 있어야 통과.
        단순 [bot] suffix는 통과 아님 (fail-closed).
    strict=False: 기존 호환 — [bot] suffix / app/ prefix / allowlist canonical 중 하나라도 매칭.

    strict 미지정 시 환경변수 TASKCTL_BOT_AUTHOR_STRICT=1로 활성화 (default 비활성, backwards-compat).
    FTASKCTL_BOT_AUTHOR_STRICT1r!   r5   T)	osenvironr(   r;   r   r   r   r9   r8   )r:   r=   r"   raws       r   is_bot_authorrD   A   sz     ~ ;<C5!D+++
++-


C
||Gv 6'''r   c               H    t        | |      st        d|  dt               y)u&   bot이 아니면 BotAuthorError raise.r<   zPR author 'u.   '은 허용된 bot이 아닙니다. allowlist=N)rD   r   r   )r:   r=   s     r   assert_bot_authorrF   W   s5    v.% !-.0
 	
 /r   c                <    t        | d      rt        d|  d      y)u5   bot이면 BotAuthorError raise (사람 actor 강제).Fr<   zreviewer/actor 'u3   '은 bot입니다. 사람 login이 필요합니다.N)rD   r   )r:   s    r   assert_human_actorrH   `   s-     U5)ug%XY
 	
 *r   F)allow_missing_allowlistc                   t               }|s8|s"t        j                  j                  d      dk(  ryt	        dt
         d      | |vrt	        d|  dt        |       d      y)	uw  reviewer가 allowed_approvers.json의 manual_logins에 있는지 검증.

    회장 명시: 회장 또는 아누 사람 계정만 approver 허용 (fail-closed).

    allow_missing_allowlist=False (default): allowlist 파일이 없거나 빈 경우 즉시 fail.
    allow_missing_allowlist=True: 환경변수 TASKCTL_ALLOW_MISSING_APPROVER_ALLOWLIST=1 일 때만 사용.
    (TASKCTL_ALLOW_MISSING_APPROVER_ALLOWLISTr@   NuT   allowed_approvers.json의 manual_logins가 비어 있거나 파일이 없습니다 (u   ) — fail-closed.z
reviewer 'u-   '은 allowed_approvers.json의 manual_logins(u   )에 없습니다.)r2   rA   rB   r(   r   r0   list)r:   rI   alloweds      r   assert_allowed_human_approverrN   i   s     +,G"bjjnn6'
' '((:<
 	
 GLTRY]O[mn
 	
 r   c           	     d    t        |       }t        |      }||k(  rt        d|  d| d| d      y)uH   정규화된 author == reviewer면 BotAuthorError raise (self-approval).u   self-approval 차단: author 'z' == reviewer 'z' (canonical: 'z')N)r;   r   )authorreviewernorm_authornorm_reviewers       r   assert_distinct_actorsrT      sO    !&)K#H-Mm#,VHOH: N'=,
 	
 $r   )returnr   )r:   strrU   rV   )r:   rV   r=   bool | NonerU   bool)r:   rV   r=   rW   rU   None)r:   rV   rU   rY   )r:   rV   rI   rX   rU   rY   )rP   rV   rQ   rV   rU   rY   )r   
__future__r   r%   rA   pathlibr   r   __annotations__rB   r(   
_WORKSPACEr#   r0   r*   r   r,   r2   r;   rD   rF   rH   rN   rT   r   r   r   <module>r^      s   
 #  	 (F o F"**..!13HIJ
)G36QQ $x/'9<TT 4Y 4B	" 8< (, <@ 

 RW 
0
r   