d!Pjjw#Tfcl$=Z+(`lPo6Q
zI*8yI3~E|Jo($c|qph4a@%3Ymrg`aB{=A!I%qI8pVwWp-t+{tJhCjeBKqDF>n=#aR
ziUJdRr*;tKF1KSf>b~p;zBv%XQkRlb^lsCX?)ee&k&px`6dsM|X;uXXXrcbZjU^xA
z=6dHE1H?GDd=`|Vz3yHWbnXVX36dt!I`*}l+S<)|J}>a{S-si}j
z6C~1VT+pBLM$B@uspk75Vpx$bqQx7I;YrK)ToW@Lvm_I<3T-5@Q(-H}W>4v8YEwQ1bu+fsD0l9@=Y89U$S
z;l_A}h5JY-D7j{J*b8y~gsifSXe;iA=Y02!r-q2ag>mLV|qx+v1
zE9!Ll$6-DCM^!?5zo8~0^(uQ=6wkWDd|u^k6xx78N~lDh9T+F^xXo=6hU{~K2wcu*
zd9P#s-bzm^%%c88(Dk1mYu+aO$Fmat>kMCk{_$@A_5J)`2Yd5S@2dlLymT^8i-uNv?zH}GId0VA>=JZLHv*nr%2IQ%dFIwsId2~p{|CH$Nj?m~<
zcH?Z~{zRgz!~<^}77}okch@W%Mc_0FOvPi^%5PwFnjGSTBfv?|3b?FCk+B8AhT}
z+54ul(7@aAg+fRG`dbw3v-3VB3pv}AZ1Kvn`Ar)jq{4?N`Mo`)!*KBG?u1Ff#lf@o
zWILb2p>74Ko);6Ml$$<2??D(?P!U7oI{W;!n=6%=N)
z?Pdg;3yoX5AWyvyyZ^d!GgZs1hh!l<&(kQ2&kkbq#8$Xu*!fskObKBeD7clMUH*z*
zf#s1L*g|l2onBuN;r;vm^K&(CRG?wBHd$DOg+=toweoZju5No@+lP140{3H>O!)%1
zG=6%VUpj%LYn(FnA3(LZ&kjC&wMJPOWp7fll$4GIc@Eic;6I6Uf
z($FuMy{Liq*NpP{MHncbjEjPHyo8rt>D6@4%8c}NbHT2XK+}kv%~tshUe8-B_=;6d
zSoRDkJo>|m=~U3A1$z@$g!x)oo40@=RbELTq)F=UI@OYX0(Eb`+u55K0Xe8@s1FaH9
z+h5~9B-9OA@L|lMD9Q|`p2*hwHRO%sP#qXoD@Y~F9;nuUqt!eyUF^li{sgc4{5+1j3
zN3{dZE(00#>?{a&7xN7UEMl9;5?*_AjUhieu8DE4_UBe@Vjm+{e*^
ze9kUcKX+3;-d`&eJ{UChmnZyByKr*WRmbeVFJ#l~KRO`!FDv+ep5$}|e>*vXcGpR-
z6X47}lqzI3dZy+7ysIP&Zni*jRhk)^?eU2{w5@7!xSI&9p|(rQ7+AfiHib&^x&btoyOUc`JeOf^>1K!UG?*{%EX?=s`jT9ycpkPE#;!9?6PI!hba*J^3VNH^5J?8wWN4@<0I+*Uas;*Gqkl=wIUMHVV
zw5BxdKj3R#G2T6hc8E&&LwXIx2;_BW5u|G}DY0!!>G@MxXBMfql9FZlDB*{hn=kaq
z!Ygqkec!UbecC}(DZ_LvrT?gG7GO2BY1MVcKPGikY71EyRCuD9Cy3U?M?_5IGtZ#=R93i>2aPF=p0baFBuBE>bP;wa03(g)5IfZF)W<}Fut
z2n`a>w0HvD0F9ZGtsNPTm81NwV+|wA{KIbXzMy7IQ(6aNqt;Wia=V3R&dZFtG2Jy703G4wD40Y{-hj!P1&)b;CIyI%d$KGk7ae5&N3%%ZxH3(ueY
zUE6)^zFoyPG2N@hkrLID&65`pQ+Y1WACrn#g@<%we=sm*y{nWP8_ZW#h{`O4Nggy)
z7eXY?rRjEnpD2oo#}YXzK8z8My?y>EIj(Z_{FL|FU~o4!%-3wIm(X<_#b+x^p<^vi
zpZE-C^PXaz1(|l_u$qvt3VY2saxe*aWo+;=@oq0??4%b!zv9nT~<8XJlxu;ZbEGRhY>=?FxhNF2Sabbqt6`A4M&Vjmyl-&qFZ>RAZ&G1zL
z77Nex-ni-H-6m(?Gr4eG^LG}sYI}`(!_(phIe|WIMizzDz)a!le(CDXc-JXIgHZvq
z(IKa9c|`odEcDfx2b71*6!k6jj=$#05IA%j?wKV0Y@
zVpulmPRVR#njC^h-O$O-&oUzM#8nm4QS!k>UOP^cjAzg2@s%*OBHfw)oyJ-nvHTVV
z6FztUa3`W|Z}Gaq^lAsJXrOvZsQGtZ=WfDz^d44wT@@FlA~!P
zH8t)?i$<%+WWlMszdmIUi+?5}k$>s)PUYp)yol&3sUZbmHhg`~&@g*}=De46CYr%F
zHV(AW&W11KDKpO#ox%2sv1*)&lJ*xvYRMDr!&~hGG@9eU%#=0i(}*CS_wbN#y2|s-{E<3O>JaJ!KJ^+-{M<>B9p8qgg2kO#77w4HrbX|eBE14s
zonp&g?Mvq8+mV=6)JMVP8_MiniYe4n#+y&*(zR0`(+J|O5D$7{6AWYT@Yg;NQBXjh
z+eNl0ssCW3w=Io&{(&@#+vSj??7EO8m@@_&T>T=!-(}@<^sMxqFp;Nf5mGgiq+c(yUtr8@eBW+iH|^o#WR6$9YEyVgDfU;`t9A8?cKBTyt~D$
z>IrdU;X2%<^R^k_92uEY=HtEHAi$H!DdEu23_mie3PcSg9vQe0X^;Wg^R*%s?lA>#
zTjB3BSgML_CAZCTN5f`2$Gn7ADy-=o0tZA^WLCU?zl)u*!VVrh-)B9}^hL>Q1O=zI
z*_7)tDXGI-!u1G=fk6T)P#A$Ar;`O>S-^SDx{KcT`c6${Mt@q^#@u|Mxz
zu(-;)9w|9HR&rwiABL9k;|FRTv53To@hDd|q>h4yjp-K1dg}DZz`?bvySSQS@!YMg
z#HDLvus5LJx)LdG`*>V3l+q&_G%_4deXlYnEI|_lcwPHqhCo~IA0{-09O)zJDnQ_y
zr|QEoz1YlJt%h3u&~D&M@Fq0bt$N&cZmux)=iGBy*+aIoLTen!udfm9r>WI?ncOlx
z4j&6}yY^Q>@x_tE_O}5|8qIq1kh*E`dE)5zo-4MOar0}l?)T@GKI)7QGOOiRelDwK
zHwd(zt8#jLw#yo3QWiUs#|~7|Sw3Bi^>V8y+-(IN{G`v?uvdzH!lWMQpMyJ-dMyE6
zv(fD1?zT%05Aq!G))QYy)ork;r~FY^UmpbQy^8;V`06qOVNaj!6FS@@*8q=c2K`i+
zVZ8IO^YAz^3Q#!Uu_l*oN24=}v_Qt=Vo(-g+woefS
z<*N#0vCLG{fbzL!-q;H$ECifTT;qYA+XLeI4IoSJsy?hw;v$=AsP=z)X`y6^lMR~4
zdyF}$N<2sT!M_D%27*6WX;f~x%GXeBGWkLC)$C|WE@$&(*Aj}HfQrIBV=gU=a0}rD
zJ?Oc!l5x(#)D)&OM13Ou!drjbt)lqQaR8F%uaW6U?qK|rnt7vXysu}$m-|jk^eOf!
zGbn~}fg;!E%v@a{_**CJxDAfIG)eU8;7zMg&TJtIGRAhBnT!c+-mSm=c-58{SERZ71$fl-W3l1OL*(iRJH;Y+O
zL@1;M5F{129AOt0+pcu??$ksaW4;^TCkkzC5YRgc*NS>;|5(XWgmlW5R$E5~Xh-f{
zrJgSUQLhkYuJa6vvSE2sAku=nnecjw4-ucr>7%hzUV_oG2i%cR6L{hV@PpA$$))9~
zwn3P~$G5so$VCVJ?u;HM;H@t6I@g=EWxU$*P35;*dY}QeZTskWtcY4X{jRZ_z=-rH
zIuD&_4G*h}J!ULNdefdiP0FLnHLpXc28@suy<_t?HVYS-$
zw%QwN5mz^cF5#vsvK^egA-3?Y@O@tWqd2td&IqkM?KVe{XBP5iIs9awQEuOt3)Zqd
z?f6&uW&o4;P#GiXM*p`5E6JQV`wgEX=m5Kd?jUx)4ODtWdxNV`Fv
zf)KC21#eHL$30cPsKZt-fE;SoEoe3b+zQfCTF5wj_Gy8KIB9aNgB^n7eXL+h9EfOc
zA>e)kfwCbY>#=@`k_torFERs6+An^wF#WNHbJH^8vdKY(w>}qSvVjfA5GpxYMt;ca)q;=A&MMHXIDv1rnJEf#*LT(tL6JErXt@5j+K(UULVF{Shu`>w4Iw^kSy%l#ENCKn&Te@qFRTAZVH=S+txP`c%d
zuaNpN*So?xRst98n?Fp|&MbKp>Cjm+`K_o?vQb<4XGJFU{TfI=w`K2wBL>(euO^^O
z3!a4@)%gmHs`kdQ4q*aK3r1IqAw@oFsvTzYff1FEy-)M1g_4@Mg03%olrbXm2WG>!
zwKTZqLkPeMZjRW2%HRiRG2t@k}s{?_2bEX@f6Awujx_b7&}p5zh~@19+#eqhh8)j|$7cE4dL*
zIM8EkhM~vE4s>H>*@?=Zj04qvnrfyDoQ1j+89y`Hy;);G_akIR@E&_MYl{UfYd%0L
zET@3-?vz8s7bQ5nCpWNdw&v?`9nW!n1nE8W8lIwQ`+FAz_0vlFc01zVDvCJk4=!9)
zyno|^13*M&L9Ml#V@TVVXlli+gjLfCV6>jYs{9ssaF)9fH#AoaF`lxQ$gE@1c0^}HZ=4H2kIgQD9^0jA!DPScal(hP4&9~DpByOF7%f1}K|LPBF@05)@^ugy
z#olV<_!+Nw#z1=Sx5w#zrtsvkjFC?aXFY?_R4A*_=2xZ#r7JZc(zyJ)LIj#F1)>R~
zoB1Sic=Sa1dD4#V}AfW}X@g+-r;NMgE(
zQ`1kTJFGp>i$ndYxM4R%*}KUqJP|7{trjgZ$Rt}0dy1a94*bFUxZ{d6t)dF~m;coq
z2;hD&{VvCfetbY#SCLZ#KkltBUYEJ2WtLueJ-0L0tGLys!XLS;XhmQa5!ar}!N>GD
z%?mzc`I&T1Gbky+ZZXFvsc1?C)MJP1A94f6Ft8)KXXr|Fdt-NOGat*ZVh4M=7&A{CG6yQJPJwxzpKjq>j#juvTrH4Sr%v
znl(9!XZHCNuJ5}@P_VM6dD}EPNFag3K)%1*TerzL^Sf+G9dYH#W5TT62{b=f$UsKV
zjs@;_4Tm_=0wpE$xcXAE2G7fU%)U3HEH$Zn4_kPvhQ~oK^aYo-t7omLcY;Y&q3`H6!_gk;XHr}l*eNM=h&nF+o4+TQr$PDqVVw3uP=1`|(uSQe
ziyw2h;4B-VuTo*Tk*rt1)p>AUviFd!!X$63O`c7xw^!&R4Uh29;y1+Qk?lX>Accben=Bok2>sXU<8%HS1@ijoKfxgX|0U~l
zSF?2%7?${TD?{c4mgS>;X!&{{>GoHOK0E2Ge%FteJ&|4^7u&sSO={B)E4Cysx?MDk
z%GNv%B1fy#i!)nhK{x(Y2X~jAGvJqfwa+Ex$jL_jSohbHQSy_}D}bt$CPj>}Kn8^V
zj2{xN$CG*cq{WR#Ng0Q#kqt#BmHYx=w#x#6p-Q5RQq1-|*jccZw`bvblCQkn@b-RC
zZ$IZ?pReW@LXq(P#TG3meP;mAE#t3*&zimcnL?k&2d`Q}arsI=&3VmKERzbk6L8}M
zj&;t)MVUiJQc{<=MgMBsYHN$7f}3B%O2(pN%3i-4UC`-A~Urch!dajYL)u*(b5%YNOE@A*3bEEDqup26h?Xa8MjX-W6eK2l(VGks5
z?Iyhy+m9Dt{fH0z(1YN(T;brqrA_#fjrAkiIk%ly?TZssVRP$ci5q+CuGzheX
z<%z3l6)A$kS;aPAg^2R2&V0eMBYN~#%&aaG!EKGcqiW8%*Yf;sx94o%TCC|wuG0|C
zp&`4Mx%@`_l$!%WJ?&t;vChNCFAX`gz8S)PMTOe^uFRldd|O!_?!3)0v_4#-{YMdB
z05pkrSwA+Z7`C&S=6^aJbzK1KWitAqSXzUK%IsatSg*DgXrkcTj5v)PFW_Ek->2he
z9O)X0QKuwxR|4VcBZLy;c$b)h`}ZSxeOm5QD4KA1CDVNC_U5iwEB5$k?mbdNYscXq
z$#I_~W|mx#mR#t1NPw+#XDE2-z_)JWaA?~P|FfL~x1&He3Y`0kJhw7!{#oSigo`&)Zj0kwl}q7;h(EYBFze2sG@o;iz4a=j;yA|+q)6kY
zaI6=p$_VoGXDMy^82Xs#05y46cToT6s!COvqgyNGPKefR8Nbn&B3u@qsU|kuU~T)CwdB2^zm&IC}BX#
z#p$-yUQ@+wjLPZC?Mvfa>=N5)Z#ZF>~wb|*D>@qT`sKwSt3OFL2
zV6^$=g=T)=(-u;38cVxUmIYCo^(nsys4DRy3d*0}whptUi8X(hc(iZE&xjrAW3m1@gEA9k032h(s>wsdv=yoek!4bZuz7T;
zS8MlUYtW>+oW*e7^A784GqT9BL?g|WZI*qz?agFHwOC41kxjB0#BrTU?k0mRqqvq`
z4}L|
zC|9gt1|S|nJ5)!bs1G)zmTGWvC&|wnJ=890IUGEbc9p4Tr=H}r7MRE-q`PRiK-13m
zx3GVpk=ls4ORLeTScHA=aBRHm_uCck#YevBkn3s=%>X0CtTp>roH~aO2JT`_L4r%P+5%EQe1de%=Q8If+B3RY{ob52vsp|j+SY50
zo{Y`ZUGPE<|AX4g+OsJxw44nhY?0n@sqY$WBlfNYx4dB-YTn7EPdGx>ciH)^gU*OOK-ZlyZ+OSfV1S6ND+l5
zZzbUk>ObPK_;8b*0C)Uv9Wq=Mr86L=r?gM!g21~nbB^Bg*zLLRxZlrszdc7$f`!K1
zz+QtJLGQ1YNhRVp@#C%eHsASQgj4MRF4^7sqhXettH4>c9P*#?GDmhr?a5=4k8W#T
z1+Xjp@-*4;wvdc~3#fu9NE#eT2Q9&|Dy=oy7v02$tb5PgznSxGIa}(n=N_wDZxu`k
z!V1dLQ%Cd4QhW2llIQBGj?FD2l}ifrpk~?q(j>*P--abI1xEOAF*O0WM`dT20i=Ec
z!f(EP{#Y=F=L+pPTqQ1NWCDHgojrHUV)(({!&=#C?uBG>a>6pPk!XX3v2dtVI-lNh
zin$JVGpUOg8j3wNwLTf95r!m+#5v@4s`ucb(IxAvffPY6O%=$SR&
zyee7d%zQlCxO`__3&ic393XF#oK=;GFkON%+Pv`yFvHe>RCMZnV!z+cF&YbqRnXXc
z5e(91C(JMVph`rlsncffhBqA=Wv`q(Jdtu^)CM1z2-GZ(_MgI~nJ|DUF>OG1jdSrqmO0$<5pE7*wtRc*OuoT^5b4IZ_n+s*19RaT
zX~It(i*1NO(LzsW>PzD2b?Q$ik+tXf?Q=aK9S$ywGFxNGH0h+>H8nk!;8@oxe85M*Y&eZcKU-zCj*U?
zXroLBNsRv(Ld&9%zC*M?*wFI}2Av5ZV)x>ybJVwWn6$zjy$y|KP4dqoL#yF<@q6kq
zkM|4T%GRFl%gybUFiK|?S4wZOKR#7Ia&dlEXb`m4)Br1@zoUJBZ?)kyC!zR3r=&<}
zoop&)Y(b5Gyiw37A6!n}W1!6JRvUIJ5-SCV2#aUCuvN;nWSw}IV7+WzIT2l?Sf5nv
zu&Z>+ITtBrV*RS@)&e62_^*RKD}xyPZ{ISFLVds9f8N=}82EED#YOx+siK&0^of1z
z+H$w@H!3Ffa$exFNyKT$#M9St#dtHIMl3qvCRKvlJqLwMBZdnpHcq+AHzbJ;A-Rzw
zc2Tx{f2_S$*l?z~W~SaO3$lJNPPJ-}>#V&f9?|5=(+@>z#zYuBf2;o{Mp028?h{#`
zgdMf!Sr#)QsDrIwDHl^%hVQc={Zmof+D*w{gg2O}UeA
zI>BgtJDF~0Ejqk3L7)9iC@uT}LigBguFyMr
z>ie!g!IX%?1iUiK(zN@LPPd6fVRl7%a#Rzt$aWa4B;TX7A_WHtVqW_GDYp5k{LFqm
zaDhN`)Xb5smJ+_=4#U-Eh}lu(nMRN`0t_<$u#&)1jFXC*;CE2>!tm%>Lbn(PRwNqM=wH@Hk@f!$5Ex9F&
zJgZA(iTACfHhqDPLLO+mjthKTKF#a18N%~k0CavW*s_0NJ{>nls|utOMh8QkzXq+W
zom*npugi-mn`V|YJu9r&QJG_09{c1%1avCFit1Q`lrnn`s~87qgc?Rgd*GejFR*stZKrm)aW(IN(|?tgKm1pkJY{vU}3
z=I&Qj`M}ljli`-*h20#Z6Q#|MXx?c!F>{vqfF-K(^4)0q`SSM^U41xrQkn{U3N~>E
z;$+7_kh|SZPyHO*K5aKrwS4}b6Xwr5YP&VGi{>o9%c(~twM#M>%U$qdMa*(Eegh)(4#W{tk@FC}?c?1gmopJL7sx61zmHtY*OaP4$mU0VFmArM6t#_A=n&+S-ZQ+`
zJh*d9ICsiYT|N6&q;!ybv*JlI9s}7mH`V%I05O*c9POIL?e4
zU&u}oW#I$2qu7N(`zLklR0BWOk9o!dje47B!b}(YR0>9MEx56*=WT^#{z#i!z<)|}
zTn#UW3?JrxyN~4fg23?oOYCS;4+l0sE9o3V8{f$+{Z
zN$)zaia0-@c-Y2hm`J$bZYeNqaI2fDwZAih{Cgp`^84hr_BP$akB1LZ+|D`jWNhyEOckE
zEbnP^sm88vZK|g>`w&pkc@C!R2_*i?E-+w$M^;O$SDLq%emD&L(6#d^2=PT(oEK@1
zNUbKb-w=*j^zB=X^)Qy{9r|D7y=7FJZ@2E-QlYd(Tijb(iWGM(?ocE^(c;BjgR~SW
z#T|;ZIKiPf@^>f*y;a$*EnmfvCkS~pAY-X`INz6kjL)(Va|D7^Ea8V
zz|qw-O-m^YWC8mb9=h#w&xM^{xm8Ajb7bK=5VIp|(?u5F-TwR`DMjm*uES`<#AohU
z`GVxz`usC_0-Z_+nmE~0)gt8RMn~%JHg0PVgn-#+v2x|t665^0#nephasp1i8+5$y
zw9}Px6vgxJ-*xVPkTADz_f*LZDNp^?9?-E2{)x>7DRnKbW$PPM*TdXZGQQT78@r;X
z`i=qOAn;Gvxtl513v3z$=jshql2)_7N4v&tR2qDz$rnHF3A-xKF8^p#(FyQrPJdIy
z#EABHw*Ug34gDeiS${oPnIAf&gir5;zrX(8>XTZ4R3=h%dF3za5Q8rOzovIeYafPp>y3#@-Sh5mJJ+c*`I&FyP3V-BykUJprM3zSvMaUepTM~TVb9dcrs`b)sczwZ^PWO4ONt5*g
zs!C|8i9~p5!_4A~vV&gS)zIk17rAqE6YwRf9jnL`ZY`oIDw_WQgZQB3yUjMjH5#dn
zDS4Yf*_nVBy*^Z$8n9RAwxauIyky2xca4@M#sCM8m%<%0D9=FoRMKhk3_HIhD&
zxWgBuV;iuzU5v_v0gb+S<6J}8s$Ve9DPJ%8f?@ta+uEjEN4x_{fmsXh
zUr0r-wnKL?x&@+bB-M$EJ%XyOKdb$=ImAiv1|(Omh7WCYbdG>cZpd+TxR~6h{+#^;
zdl*x4u1hM`zoX>GtftNSWq2ycJbHnC-mBho1ZgX8V;9f-TpK0wn06+*LGBx9dn!it
zUC|egLN9vl%hSY+|eauK7v9O&Q$FsaFNt293
zBnam#5jwv-M}MG0K6&Lz?Uq?8zy{Gjc~Z%QoIcCkSQ=)pD;hg)$YUT8%kRZ*{%q^1
zXR?i$Z(^^~Ru>dSV|5UGaYgsMv+UO`=8OL*pCnDHJ7!eW_e-h&fRV8jI&GCpmoZ%7
zvA^BK6K28O*9Rn@@LQL@^5~%Xu4lfLGJmDkZ}i~FI-MZaG531D_E=29&iH(^V-7Qe
zCVg4%nUMnI0{ZCj#o4YlxGJRt5?@{EqD1rz)A|(PI|*+g_2r2OfHp2^SxTX!0RXVc
zknd>YEnc?5k~>YF26A(S+8baqOT(Z5z31-T(RJAag*g?wOIE*}9n=HrX*U&{zVXpf
zbNLi|^xoYya&9j&!@54)Xr`ZS3Taz9Q0MpB6so+|q+%lRmN33{x{Vop4or=iul(MG
z^sFwMXEyB~u*{t=+D4M28%R*|yy+fuJ;e`Qx9wp!Sz$Fs7GPFw(jCb>K-@s(rJo=Yg|<-ds!^)SSpt4
z4V%py?QcDl(k~~=f2SLAp@f9Jyw&S_t`|+1*OmZFMP~NjA
zvr&s*A5b7((7I7zdz`h|IRG0}>2u|WMu-TmH}7f-8LzjU+IxFOe{0RenuT%>)?KL7
z(Lj``P?eC%UljUdkN-32%ViQ^IT2wW$G>;r^NXCeS7znPOqahDX0Aa>b!R
z%2~6?v43|8Dja~Y80P$&R3^Xhi!nO%6W)Icvzo$xCU)im3k&BuQkoV4`xkTctEU>-
z1gWC_&0X-2B4Ttj(q4dkfJg>-x003H$dwMXaodYLHk4O}
zKN>uavp-Y==Dqi;!tL}6CvrK|f1Ap4EkSe12u7h{z@?;giVqJ5=}7Hx$IhEsA=sz>~A
ztTtvexnUvusfJ_bEj?)DihmMc%wJ4ojdz+~Ovx?zmf!ko#n9C_KDC9SBc{ILEAW;B
zW8{rE<&?-|K_Dic(&02h5C{=UZP!~|g9$M6c1m&wv}Q2;2M
z#5Cjg?JTL&8)v`0zWM=|EROCkk_vmx2I_Qe6$d8j%-5xL$mw-5(X57Or{GbYn^a3k
z26$fvX>2f@g7*8N>;6kU)$8qCbu(XTtKXLa$Itt^J_-5q{6$pS1n*29Ofx9G{Gl|>_c*#r*aJ2gL
zj})xq_djXAI(rPVp0}`?7bB&mC0hN}@7s$@Oze$(cQp`@fVGzZ@~oS*Sni;8S5u=Lf%i*{!3GZj>p9cqRA9pSuo5Cc+1@j3RSMjE^)W7ZdY>6vMw2
z8=xVq{)RYwpEfziDMHj8cPopsVJx8hd~MM)`_9nWwm$?6xxcH-Sc!=R@(%BeRqaiG
z+T@6CJ+>>RSkG{t=2Q}tuVowlXhHOYKOv~iI22%ghq$0bwLP~q@Kr717;gp28yn^`BU
zz?uL-@r~wsbOwt;y}PNk^wv0Y2o0|Ngg#DR0$(f2-rs1FH4(>d+Kj-MdM_aXB|rGj
zz_W9@maEG*M)53&k0jEZ4SmUNCa*Eml@y}`1~CvYw4qzgZ)*XO4N;{wDXL-7m=Tq~
zi3_+iIxiTz+~?6hSBhC9_yF-V)#y&G2|{`k6^IlDUFs)mwkJGO$pL4)6!mDFnr)>?S9=3;h%E_Qfadnp~yB}Hr
zeZKatl2@*;ilNoe`<_wmZ)>qnI=3P7VC`6Rr8+|g8%ke`qVJ3xHq;)neSoA}yX_kC
z-wjHSD_(bD-%s&e%{tdld^sotC6GA8yYahaaA-z|2HnA&p3`>NB!X9HF?CIfi^|Y4h
z7fN6@?i7?GT`wl5?{dyz-R-(Y`D
z5k~*3VC5&GHx-u&n;q9*O2?!o3OYeY@xPPYxAh1;x^Eg_axpRqC#=&R@A0+6e-A^F
zei^!cMf+S#bSh?>5F|S7<6#iLs0u;uTngv?1X}2l(P!>~rKd3l>O*UaNk-|)(o;Qq
ze%4$|uyu3TAyKwHobJQiUL3y-FV(fj^TnWE@q$4W)C+||YE<=oX*o77-BBn$rbvX@
zJ-~=tgx*-!lWTA=`*SSe@7~X&rd{tvL;Dp^xT_*8uPe^7mn7*AB&ls$4H7QtE@`M=
zl{H>mNiFURWBu;oyfU+m${cQW=~x0!rv<#~q|WP;ZG$Td*gUEq|%N4L$st
zsh<*NjXz$B4*2Fhi2ygu{vsyFnoE5Ue%
zw>7)2gEEa;F{#bu(Y1kz0(Ka-)2kEH2!}j_nKemx^aq|rrEY#taKcbTy31U-6^EIjt7K}{q$qGNTIqRyN4{$1S=aHoh
zTy&&3i6q(-I%%(cVB9Ygac1o$*WS;@wTU#oC^cR*)uT;9q^!M0
zB{n&|^Q9h1`q}Len@(S2qLJ~)Gb?QVm#Gih;Z80O_0si8yM44o&&FlgJ=mLdJz
zj6-zbW+{nM{=s$gQ>*whGHp0*&XuT8i2UkhrA5gfaz|0LlcMb#0|7dy_-f|L`B{StFKPU?e?4ON@Y1X$
z$|{piI>(lk%8!P${`USTaxtbuKHHq$QF%Zfz@>FFF{wry(jXwuDM_0i`lNNw
zjcz}>dXRvj7U(2YoF-OP+Hfjeu6NE|8{rGe7IR?~V4!aB@e_%bNu5bJKMEuZIOJQH
zFxb=3!>E)H)Nv#Rl6-ColbeuNZH3kb0#?maHk7H8!%|yb!vKrx*8T;%X_m_IS|PSR
zTL)}adQMk@^9*L_kcvgEuvcHJWO-_fdn3-CR5SHHD*_X2!h<;LVa^u{bP|a>%Pn3k
zr}N`5>szlGHo~l&8}XW!y=P>r83|XPCWnQst=rElJj6@bcy3J>Q~pSxytf<=9l7gsgdI%qq
zh1*
z&i0TBJX?QTa0@Me@&-`GUwLZ&W7^zo)=G*+VS9uOsf~A7qw(4DN@-q`Z{LS#Feyf2
zH*i(E2dFfs3$o7bUl@#o7uE&Yo>Q+;%Q(`_OrRkBW(C3==`>Tk)EiKPJ&YhOoMM@|
z3O}zJJTAbmdLTN8ZXorfZC_YeIxc`E;OwR*cEGqac~bg8JB630!WttllB>Zv9fZ$U
zm(Dc^N7W3{!}fsd%|SO5Z6CJ8Q$03|s3eg?90bj#+(6rF3(~guodPvMXcI+Ui)+G4
z*x|6k{*Y$1<16#Cr$^(h?;Q;A5$u%7Y~3^FbyU_2sg%oQ^Uxfr+8ajmFnYNr~siPEnq7k21
zMg-4Zmc`ri86}(yjk<;|ILx%zu6kDWi@hD|b6p
zNY-th%D$%icAtDR@d>Gg&=ByY{IB7#
zBMMP2g&>dWwx#S~kS1XTiVgo}S;?xg=5`CdtC6bN$gY%}#+t1+d5+ggr(VheVq!8D
znIufv2N_D@W>q9%kSSloRuTxzRFtXs5FL4+B`T^)^^HPHXT#O}Z=&gJ=h=
zhfW?a44mV^xz6{7!G4+t77Uz#yu=@9$UI=zQ__^ON08pSh*+y
ziJvIUiFE_dy$UQ>w$cGeH&YBgs(Iqh*rr|Zd~^hI$9Ov4(vWVXdm{I0`{#W7EGSLC
zfSthDAn7smYOT&$$@jy?N{00AWY5Cu+S4G9x-R3Bu`MHhHBnQ38PY;j>C@fB3qfay
zdTL{|e`RJ^U>5JN|52gRmyw}Vhn_&Rc={gfi3K*TdFMS1D3D(pD=C+y%x0KON-9Ri
zsk6R|SM`h5c1#)YO%8T>pZM5wXg4QfjwGAU|K!}q-;aShVCu<&dGZ1#Pp}seFxV}4
zrUOeBnk0VZN^+l3;90Gdv`DHH=%hs8nQ5wJ-e7Hla06cY&|DKI!j3%Iw>c8brU4rn
z@0k>N&&sMgmfdxq(&Y857J%oO^}@IM`e79@@BlMtpoeQG))-(Bbyg4t{cstzy0
z`aK&}yl`%__{!!o{>xVX=l$G`J@Cy}*?;}*uL1a0fiQ^KOwc>Q^Js&1weLp+(!rpsa
zSv(*sO?}e;ba5^p#RfwC83uJCBjcTlxRnZPW%)lwSL_^|hTl(*dn@IN%Q-Ah&|}4I
zKg%TJj+)TKr+3zMBqf{Q@2qDma`=kEFS2lLaAcr#sLGOMXO&sn35xIXB~4+c)k^mBbIdt>A@TM*$K3X*0sv0dJl}Kw
zMWI;@P~!ITXK8st-G>+7^N>j)A-pf9>tDd3DU#S49VEl4I>XqJ*N?P!&%`cYV(HDa
zwe+8XOu#`bS*@T_TtY044W*&{uYP5{%8VqrV5+)I%l6^gf3@M1@zPs>W%L33^g3=quG3I9*!OZ4Y+s~~UdVWf&jNsj_g>aF^d$g~
z$i1@~{6)$JJH^hFhe5ma0bJ*yq=i@x=@QiuPP-HhB@V
z{y1XpwJc8$OS5wnJNk=L$H38|9nExx&9ePPulu!=%)MKW5x*Vpo;bZ-R|iy+M8!>B
zK9Hm&%2Xq@Wxj*&9Vs_8ZAJpg^v6ZhnxxYfxBLWx5@q35rk{5U)X|`xSgD)&M
z-sL`uB1SaB1YWFKf%go
zFR<@)midtP!s7=w@46oT#SCtQN<9iyqZlJiSE97~g
zSp6En^dIoQf3dtf8U8_f|NrEJ80ldPO`vU#+=ZUv2UrRBUh_=`M*a<9$?-EEJ`Kb(
zjoA;|W=X>Oud~8){3Rai{mJgwxRcVB5lbNG5naoknoCLn>~H;q-WPfLsr+H_*=c8A
zLPGK099I!-MSy--^V6(q91AcM8U&$=?VOC01hY}iRub1lYk0}rmc%0x3kiv_^fS=Y
zSBzh|fS!RvPwPc&VLPv$)&(&O5*#>VJ?v?BziH89rIMg*CIprZsiUb(8}c;3>Qnxy
zbh4ObgG{8Z@FuSfufG-UKW<={?P-yqe2OY|Q&gDM3eH8S*-BYnRcuRIV*TW!8DG;^
zlmK`k_83}Rvl`YjMLBD|s5I_Jzv3`-P<{WZSL9owll^TWP#ekkJr=7g>mjnqGC34%Q1$y+Ja)%?Xpq3>CY;_!s!#?
zSUk~-B$e)|`oEUS#58p`JAtrq-i|)&tD{q
zJfh^cf6z7jfw$RwXw!4H-j6Em^?-xnR_?pHl38=f{=!O^+do5AxI;NLg!i%^m^%$G
zv-5e%KPN?Yy!!A|3v`O@U=RsSYGblQwl?-H)pzzgkVmWhik3U%$*{B-$l^3Fi6)yr
z&xk#$7Pt=Xji(b1eURpGtJC_a>u$1WmDWp*;J;r1QnV_K@of~WG1&N6ru}32Q&!})
z-orUg4lME%!N*X}h3(64BAurBE$KzcCIUHvtI@YNM{C1>8%;lGoBI3jLARf4
zAsH;E28Pc$>Ce2zbmLRK0D^f=eYFP!5uE~jp6!&=GW;#AmT?3e^4ap-cN$Ip3
ztG8?0MK1~n0k+USS6JQnLw!I+W^-A+6?qti^54C0PAVzbceKx(Y@tNXl-q~nD8$foR=}I9T_?%>tF38c=)~>*!M$3Elmy4U
ze-k3&X}5DHAI1;OJ?lw*agyn-$(xs*g(Kv3!S&tS5psTJe|UtBta5UcINybbKVw#(
zW%&zN8cH^4o=<_}3AOm)-4Y(SdxYq=lUUw6t>-_AOIB?s2Etv$Sgb6GCflnXc
z#;#5D)^YuG6kj+^1UG_w-}03K?op%YyKf!DqoS_T8$reb%7H?kuQ{Qxg1`Szhyx(Y
zbw^*vF2{`6S&JO({ycH#IxY(51|C7Z6kP_4NO=CC!(y3(0*83(CAkmj#U)N9oB~})
zlr8=D6DM7kdg7793P(Q>jeFiFyMk5}g)MXUqI1I_UMrW5<7w)k-BpYwt8+EMOjh4Qp81?)$XuLvaAz7fKMb5NBfH8pB=^)HT#3BLI5lJ!Imof&
zkcwT9vdO>fJjS!o_pJk5iTc2I+GolIC!j69ynLTrIZ~V@%Ty$%ij=Y@T~u)rzAC6x
z9rWSR#BIy25>HirxJNqzieURO-Rq4TyYfD(cKo#{jN|Wkjcf@YzfBrdU!-fx`6f*Z
z?}_U=as_m1(gqvFB6i1Rq9rcYo}wBdR`aPx9;S*hMVs0|&Btvk&yMPCn&OW@0xMbM
zbWj+U(c0*Jd4qekP-GGlmAD~L+g%#WdXgR2l7-$iElAXC;INx14>u0i_^Fa;AXJ(%
zSLeqsv(0q3KW%X25B>*F52bHz^3gA1+kvzspMx>Rw@&Gek?y1eoON^NjN7Q!wLQZD9^Qglk>{j%CR3qn^VWhK_bKqm!4rsRHjB&>2KN7)>`x>tJFx
z(he3#McuvflFx{8)aUVQqsvqE|H$3OrlPA?g%jImOFq_+=zMu{2(yC4d9GlNz3Dyr
zE0|$d_f5QKPzy5p??2vF;DF?bc>G^HG~HDx*3zn_7Y@W>)?w3EX~oGuJana`qb!uC
zPeH^q7ruP)LRrsgl9zbkQ!F}@mGH8dfPxag_Omdp5+)fT2{
zAO4_G&8#~`^tZ&}1elNV*zH)r=nRoadd^-y%~&ydFY*2H9^G>rK3NrSAf!!gthnXz
zN;^Dqb+s-YNw$?&8u7TQS?FH$NZq7ez*yNYOzq=GZyrUQ#$0AN#Zi^Cvz8k-qn4K|
zf_BK`19UH5wk>+=2+9KNvdFps!}fWudy}WAZiq_N*)q5P=|Q(p>4CE=o;#2k#^j{K=zY+oen<0=K5P=Qp@wQ#fVto>C>
z2|t=2Q;*Z#%k~fq@%&3{ef4P$<9faDVfm15JIxGx?{O+)`l$mdD{}1SPBbXY!jpRo
zhYb%8&g{=J-ZzlacZZXFEStmR5y&Dl>AEK2)T_B+{GJ-=Z{;9WE<1#MOTg!}RM}y?
z=MFuf(e^48Y@ehuglXY+!hjDcOzk|J72R-VLv>S3U8vqQ)&}~8$|k$uAdhM
z{5;$)t9yeN+kWRVa>Dmb$|bH}YNbSCct)|s>MfY|$$33w`L=$I;~K17hsSa;r~YgPBf#O1X>ev-C7!_xg&7zfp@mNKj30
zHvmB=Cem;^Or-u0>kaHjdp!9*jw9S&b8kVXblItwYh+AQkUU$Qr#o=~H5A=Y<^Ciu_m#?olKcM|><0unyA
zG7tYE#FNXg_+EF2c|?VJx;0u>G|X&Z`MemrhuYN`CkCl8cvwg`EwIE%t{^WEUk^h8
z51y-Bo=sI5Cl(#6RZ?shKKtQrhX?`+lzwrli*(tX8TG{LlO*Ttohb(tJAD5eY-l>Q
z=Th$on$>e4!+&Ahvm-#xQ-<*Uoj9{C>ac$2^KTgG)Zm_#rQC=j*oN_G=`8H`i2T=~
zzQ{fu&a`01C5sKv^u=Fl}Msv?)(u|rQ!
zhsx~RDGx|#6EvzZy8l*U8x3Z}XyLrb5J_4)XxCg(vc^TnKN;Doh}-IYf__M~X%J`i
z(m>ch2*{o~)CHb{q~A~=uT;P0zdCm9qW<6MkUbmD!G1;$leD)e{?!A#RGmO_z5QLJ
zP11{|c|)7-Usr8}%XDWhK$npY|Xm*A9WM^`yAq8G=mPjT3gE4|NU)}NlDx9{#@=R$5QG3#}V|VOCxV5ui3?>
ze#hTj>)x~(geBwt>zwq~CN;W!ZKh>tJ+3!%D0k|HI}~j{Gld&i@JR`G5Yqo9`UN0BUj*7l{#P7}XY^
za~RljXZ}}5{7Z!@dz6#I!tV#BWgo)x6k_MOWp$DPibQUx4x>zydlyF#`n|Y|5uvrG
zG@^G^bK-5Vv`4uRX~wA%Z9`lJctsOgIcMAR^sUUuACzai&pWES!!=v%Me8d~BV(6>
z$aiKoEk+jugj}VaIAnFKl7U3IIB_C%cDddMi=W1RexapWy1}5igKqqx)BE6udqnBRS0x
z2?eta&g~`-i2-UGbRi2sJ&wTh+MHkRTO(eIZftrLf2f^XzNFq@YYx{}%ZPOEytT!i
zM#L8-FNz*0ZcwJHActn?{T}3VEJCCy=-7L9M439v7;&!O(t%XQGL`2r$MfX+U?Hh@%>^%aAY
z-IWf=>Gw)dm#Fhdrzq#s+I!#1+AI!#hzN@1L{JcGjSj|`rHMf$hX;MYm9pyy*S-;e%AxJ
zv$HMGX5IL2b$p}wMzV*$%fr#IX@$`g&%^!tm$SOf!W_F3`0!>EYi!U6(5%~`E4|GG3@+3lr=KD|TMk*-tcI0|fSF{4^W+~HXUufr3HK`4&q#lLc;*|+0O}fO
zHA5O>&>#Ea7(cPiUfwd(P+MpOf10Qmgg-I#10Y-ygqsG|zUiaP`S4U{!0XYAUI{2<)d8k3_>N
zzZW{dEhjS!J9;7ZK>&S?#(qkWq!qlfA1sLU9F6ycA*%47I3*Vao^2gQ@Mt^{=j)1E
zx|$kn|M?XT~b2RlR`l#hP&%%;I#Ds}5Lt0^uJP0~yrIvX~PZ}>+zyhaRmxkJ9N75r%OR9OwEX~E_J0!!W4xFv(+V+fPXV)l*Lwx{
zzUGKkCXw1N&ollg65BMgwOia9;JBamHM7#GVQ@RVy_`jU@X!Oq`Yuev_+`T<&qRSe
zBJ&3fh{1P|PCK|4+5;9&w!JYmE-5HYXv#o{5ow&5Y;-;@=fcu*3hC(ZND^w(TOU6<
zZ8=T{lL4oSn>3aME-NP9p#~1$8rEryOQqOLcr+RcmU(R3dN5cP#bYM!e&c)=^V(M2
z{w;y^Z%6=`xDfqDPSyV~z&ILezrX9|>|=M%$pjIec-;e^VVb!~oyB-NE9C8SDvXri
z%IEtNsQGuKkKCuX1G|jv<^|s;%_o~hOl>*apU+i;=2_Q89`fu*eswcxkCFrlu+;l8
z&$#N^tAKMvE;OY)cRjutiJx=^f$jk1j?QR~p>qA=EY3VvVX*W*$
z(5Th77pH1lo7kx<3H{cV-sgNV)JNBzpGur!?!h(wByycfojlKS5DP0+)o{9~?UnCU
zDQtTWxb+C4ktE!rNZ@tK=FkX_&oEv**dG_vIq)Er5jO_;16fpMVu=)D6Y0>#NaJW5?=2=qh@8c0Ma;Bo#vDJ~^AxP0-
zXwb(Af4)65&L<1_@)LPRLK~&pMU;WCQtZ8`SJmd|{3bSsnt<&)8uA=};^h*dE$kugKoc)_b@w1oE-KR%$t#})0J
zX-#Oh;*%Y8Nhwga%H>_!MzBgpc?LD>g%!qWenMA?Z%hz2;Ovg4w;(2dfEES;cbdqa
zbV((~H^_(w0=j{{}7h7XzuBSKhQPT4OI$ZH&)I$ac=Ir=WI@J36aQs-Qo_Jjmr{>pYA
zAxsOYfCF7zpzY&rnraxjxJnf5G`?oCK~boS=gIshEi@knb9hAJAnx;&9)so@0l8Dz
z(Q|ao;4~>z>4X#yfas+94xjw|VI!nbxq=ei7TJ61v1>(Le2yLOaeRzX~37_C6h
z=lJHCbU>6LmDIQs7`X@vn%*jU1g2djUktiMEBWtnBwcJ-Uyi{N4liJ+
z;%xy9bx$?B0!@~^E%%o$wIG4VrWQA-MjyxdmIt>c?PRS6Q(g6|78mbvqWGJGroJdT
z;z?&C7KzCD%Od{PcnY@D?x#pH1RZD^EgbS?R@<=Y2Hb=ilMn;su<=pxQm|&FxkmU8
zlVS4FZ%Ka=jpbN1
z09UVbx;!{GZMlUCbzbm;F<+Dk`#S(1({_!Sz#eZ#j@;*;ZQBl{Yey5oo%%MKP*qny
zEq@m5+ggpL-|s@xpJZyGa~7Ml$oeOI|2z>tOq4*Tl?k|a%HbftxWNZ
zbN3?_C^w2hNgA2|6Xamql0oXa7_ulo(q`mY!fsxn_GIa$IdvF~Z=4q3P4zsNs!=0Z
z4>F6fU2Dm~Q)=Pqt2rOwOV8jRAv&9zoDa6Ya?G8PNRTpbzWt$x?HMfK2E24)y%VL9
zZRdHfCrpi`?YA8w^fGjpBkQ^oL}^v(>3c6mQ9CAF|4xUy
z5H<77^Z&}i52NQUmNA5$86+=2-~n;01y#BzFiIVJg-EXq?B%JPu6Z_Tm~aTb)2pOt
zZz`?Q#3e%wruzDv0t~0nEog*4#p|7e{@%f89ZB)Q$+;-*#VF5G>$GIs7vIs-Nb5OhD+
zC{ds{N_k5heBXFvJd#qicp}Uk5Xw8={1!V+>p9jExXBlAcuITY)XrTj_#Kojm7T8z
zuL5q%_Z3D5gRBqBP17vu@l^s-O}vd7TE;73%vX}WDUz$cXiQU&*HX*cv{xxc!!JZz
zJY4G+^#^FOV|A->*+S84q+kuwz4uwZBG{PT*zq}Unv$7h)>#MwG-JMdITIKwg`wI{
zc%@SP@tjTQd_&9Q55cJe$%M0d>t?3G)l&Ab-A}{47kqE@qhn2SB7!(fqRI^cD6SWv
z0oL`R>%rK7oN%OisbB4%{#QfIuRixRKh_DGyeH`j-w`{wR1}fu1!CD~wI>9Rr$1>x
z(<|TQbrz0iSHIm9eY0@T_U+6mimTNKDoUYKH>O9v?1;&Z*+zxGnQM-cd8AXIc5wZ<|Z+CqV?lM|h
zd}+mYPz4WOYh{*w!1z^E7MCsCF;Zglhe$=XJKKnd@>HAO<Uhd+n5*4#
z2Nk22o4S$S+bYf@X6Ljtq&cX!HvCYw9D=piK0gpEQO*04t*+F9ZztO7epQGi;cGX(
z9@|MisMa%!{c2tm!5;wX78{(jY~lS9D%(#?=Bi^lv{L8lEmX{*Mn-I
zKL*=rUVs8Y3jd4|5<&bMlcsYi@OR{ghnxBTtw|j({*8JgpR1JhUZv%2m+&+%i3?34E
zF9$4T4Sb+~(gdr$j?5IJ&sMZFg?C)g50q-W}T4K(=
z>a>5->hDSDslm;Q*#n(A<9{dgfAuxNc1i4zxc`#UN4vap3u?YtcA+^7oEL>sbsO1KC0Ofi~UZx+!p*y$@<2qT6Ikl0JdMHAW
z{rbxzkLvw?TZ%%(4t^^iz^sgo;V$$sAbltIyW7pB;~3b2+`pyaVNCX&ilmM_g~)lK
z${WP&?HXRo?6aM2qkw_=nn2S5({g-TsUMdurA?iNhbAO}wsi(S7UAR;354S8cT_2`%Vq=69Q=$(Hj=o*{m>bXohajt?Q`^Hfj=pR&{GsyPjAf3;S?DZ1A
zq=>tJ`B0zo5lO{|yyUrgBNgWN?ixmp`vr&hHf~E*=J4B8N@Z|bX65cNIVl6SUT)IH
zvx@Wa3snWgQ3g%`)$2zd^Pplh$7ch`4MboEj!=y&Q0A_@d?`VAZlRLZ1bhZ;)4G9tvGgg~0
z^5paJ!4rCVcawJ`$q&@vlkzjv8my3ksY>|+cdQz_jcWE?d9u`=b#vsm#E8C{e&Ew=
z^Q1orK)No|apJ~Nyz=CSPfS73js8|WDwAvNUj+cM%(o%CS-F4AGUU-9^|j|u4^oMd}ld8q;&{KNJ;W|GRM
zM?L#_6sS1~qX6}{9t*9HQ46S_M$Or88ASNEf!;FgXA6Tv0fpl{$u?&*Qhz=Swn9t5
zu|`eaB2J0GuWdp6SAUXmK|nLTT}{;6D5V>WC0E-YET?
zK&13dblsX=P~Y1l6}`S8lGijBF~r2mp7O?NBvG9>zCUWjKolO;Fm9O3q=kFh7Acc3
zC!5mz(LPr=Y^B!$k0^6v2eBiskgBw87h_0nQF>h-%+y-uV*P?=eyy#EoMGt$R%aG^
zgQf!L6yD=6dOPe<7B(c<4UN-zyQB7DCP-xb)F8`Aef)>F;qKJ}Z1vZKK97as=DI^h
z7L+2)gmoJ))_oq8_OJ+1u7VwV=kM`x)x_kMgi8(K$_Fi?>)
zS}nctJ&$K*gmuIG-3@8SRD01)i7y2tG**`0y`yIl43rjvbIy2E*y`lmvG2tmdw&|!
zB*JP3AHS}Q_@dVRB-IMxk!V!_+p)stMq@lkgbztr$UVGE7;0V2^@|$4=~<;m)w{
zG8<+3#pnBmJtQAbsjS2G6UdN=4zn2;jAO1@+4(JxbT*L}_Y~%6PbFF6B}dYlc9n$uJ0ZdxraEE)QXWVk(g!Ii(zFf)kkFk#*`Kh0z(C9a%
zHb{%(4Ut`d1dhFS6AxZrJRT)Ivox36EAlnR%Ym7JzReVt`umk!@uzQYd6li=rG)sp
z&4=%`xp%D1VC(3NTa(hx%gz-gvl{BnUI(3>#gYekYk2{?f`p5r86t89tQ#W?kGoS2qAKUTOBZrxm;I29L-9}5
z+bumFOUZs8j2$#h;JFS~^b91`cADl!dgVJQU!7oQIVCgq`;nW{S8TqQTLdNL@1?Yd
ze@%XHoJ7v3u>yK@`IEImP)<=@W%bivJwIx$nb$WAS(qzRx0}t#emx2s_9}kiED9Er
zgJ;9oYw*Be0F4fn?7V460kEy97}V{(czt_%85HS$!(1k!+U6JfMTcct{h)Uf39*uf9e~YA=nG(0Ee@@%o
zRWA7vzL?k8xMC}0F{)d3b^WB7DOY=3?AmL2sTv%Qfag{=P)irCLrF88;WS;J4}GT9
z-)gObRQPHaS}hi|3SH}h(5O1K9Me_EeY
zBGF71*}5eQ9kf5GvTz0q9x56p%NhrW!;Cd`G&8QZoCa5KitQpiP#i3VHAFc|IZv4b
zcE={FU)?2T&$6(ZukBN4AZZu9PGB&e_z5cZhjjR>cF*$AN4iX#-F_FYUpeWY-u!ds
zs^=~|6+0f#a$$^NJ%<@gciM=?E+YVFZri=P>`Sh)g3_4ZhmWisguY3ZErcUX|2G8`
z4^YB8v(IRQNY|x>qx)B(5cB~vvJ)1?J6()yMq61$q*nf~=FT&yskTAGmRH3>5m1Uk
z04a(T6{HuH-fO4=QY>^ZgdR{pR5}O(QX(A!geIYbfDj-QA%uXG2zjLif|L*-5H{$y
zv+uj}ZTYdgv**v5d1lU>Ip@zg&-GmQbK6DYEmD#rCS!IM`>zpB?91s}YQbm#S70b1WY}F*y?7bAky~ohM^+T>
zop5=39^!sG$q;kt#+XB(3{t4krJPX=^<%Msbp7`zK1(u>-5@I1nK#PGFMJaesjV7EdcOCnB3Y
z8D@PZ`kTIu{!$zM0##^5zG*y8JE|92>$#(_%}=AUx88k_Msv})@=WertK+RMw8>|L
z{rHt8rW>AH1Phxo&M$$J2{Hakwi(P6AL1Z8MJY~RVH(^8vLmR6a;>qmlnh}7WT_WA
zdq=OuGMY%AJ&(N_vwT12&Z4rC-3M%05%VL>EqT;dnNh*9i=DUBLgTZPPAW{hri_NA
zH{*V!YZTt*T`syHKihnnhY85^BYw{cA@6{Quh@r{o#&P`H65(Ij+
zh851*u@K6yX&4)Us#tbszPuRu$&V5RrhH#wo1^_hwndhP#CWu?n0u|D3KrjREl%*~
z<1zyWWNWwD6f-dQ>b|MTfCvjm!pF|ST-SJGE$s3Nvk}-t|NfgMznI{_?6=}mYC|CC
zJ>^OFVds;HHB?`_45p8XuFbF^4<}qLQ^pNrt4dO)*A-^c(xp8)A`4aNg;DaGvR*8|Z+Mo$wU$5oc5ve+1IDu~#Z?z*uMW>?8eS~b
ztl(58f%ccNUYj%iENcgT+`i=&Z$PcqaSTmnk;)716jo%bKR{=`Esj>m;)s&Q&5f_)
zeb1`0W;9f&wnfD3@0d_&Q*_oh=YH>lo=g}1t7Z6)e|Az$4@643kBn2*XqRAmN^$gI0v8#5pr!vi28Ml0Pcuqe(
z?C8zpjyQmi)RT3l^jb|LqP
zx}y9kua%$ntZU!mRteZSJQ>&3Si3u3;|I{sl(8@3`3DP0qp21j^$26^q@L@n(u3XA
zEq5#3DE6$}N-q;)#1jp|C0st*4SVRxQp$1dRNTn?gQjcWgM5f`V&F8OKNNqfPv=_Me@C&
zY6{?Ac8AbVM&n+F?Gp((sGf<%TC!InBUCvzt;*zbmTw|)v$#_I9dmyglQ
zc-!5Fm}aRDVM+(07dJucAAIw$a;0YzEVfpXDu+DwqlX;Y7m_bi1d0d8%fK~UHXrzZTrG(^sC0p!3>+tif2c)O7h`>b|i&K10f&*?5O0eM@#eJ}gKHgO}wFJ4w@TOO!4kRh!S
zetBV8iYGM_5=oUT0ddA@-7^mUO*~%a3HiJ*ZhJ!iNx4)I{>q7hNK!u$TJE-3T+x`XDvyV2(ohI!4!?Kr%TSKgz?
z;t@F>uL-42;DGcXWbw0=)@bsP+sIeIN?TUQY+tSNGg7`lF9eU;2>e(;oisl4y5li|VMguOrmrU9Y3Ik|T>3t@Mc(C_ryfgNmB)pnNSTPJ55vDmC5H&h(oG
zZAZo6{($4M=9eYG23IUSAHsOC-`(6|ymb+QgNnC<`DTa{9q&Tr7R=I=x`|vqp01f7
zme8ahv`_BC1w)zGS)HJk{RF~P6huS;8=<6OWsf$s?Bja!ZSF4Gdva}lX_)F*4IXy*
z_>@sH$$76=65fKQk}CkJ$Vpl~RCHO6X!|ON7-YzHg(a(c3p~N3qe$D0T2b1nka)8N%;Y`xd<~*$
zQ(;cLi>(;E$qAWjoWZnI-rVjvSgB)w=d>$ju+_iDwN^m1v#BBrfT2mpgor*V6(_VG
z@beNAVt^in#6>Z3FAA&d3Dun1v$&0iBG
z;@3W=3cAo4JHgB&gTkV*v2kMWzGAM{iFKRpT0d3|v?Lz8mZwO}2s*xzMbfp24zI2-
zQhO|CShI;In#6&=>{bE5__D>ltXxN1;CE+lAPn+ei1bp5}
z7V?JG$5c@>AJ+*G!)Z-lF4vY`8f`@=(5ns}Cq?AEmzJHf)j$-;S5{WM8@n^`!6CgH
zxNN9~dl0v#*$)%8*b@=CWX6+$)NfMnSo7
zHm&C;~4;dBrgT*P4DyuZ`uTSM5rcudxRvt
zCHEN{pqTGQLhlLW1si7HS;Y+t+7}BuSK^Z2o)<7dra$v75Oy)G@ADZ
zTJs=r$E2eE+|DQp8WaV?MH3`lCZ?A-_?_kNAv#kM=MOwydDM+AKHOBSTS}+9cxRfao6>gsn-~|
z_S$23bpUg
zn2|%cv0140IhHxuKYRb~=GMGq@^DuVeMVZ0g9q<+&j14deY7!Pxkx+3x!E!*8EDX=
zA0bm8UWUoCOb%zCEV%XvVyqRlwHhNu;;*-ObSJ;ELB2|(RoZ1a5Vrj|M%&49i9PbF
z*4o^_O~w1_x=Bidw~Y(#7HB~)vVAC`F23;MR+<)>go0l#$PoG`^a@_>Oja=pz($u&nWxHXB!NJ4d+v#jH%A*Rn|BreM49NIXikA~I7RAyp5g87
z;L^fdNC$%X%srcoCgv*wQFKF3S0^r>{mPosnq#r#I+Ww@yE+pd-b9Vvj&+%<=>V*g
zTJsutVa9|{OmGj|D|#-_!Zv`CZCE)iq>>j3=(YswtE_gajaya50&lDC$)&vZBp*Cb
zX7^N~+O7BvS5xrt*DMkty^E}2Z&*_Sscrm^cTOcP?+X{6fH_7e2~=QUZ)!M34?ZtQ0SVLLix^&(A0rO#}E=O>0!
z)U$ZxOfp!`Ms5|Ii0(HPyneVNA;G8x$kNi;izqcTCB<`PH7Y$D`%1MGC|+ir5T6eD
zQsS|$$k8JkR8!7pT8_a5fN}|0l3IfU3$5>?TTVR>DQGQbj#pu3ALxX(C9ymVE4M=>
z|AI(DA4dglE_sEqX)K}r(DR}@yyag0%A;cnfNw*i8Y~T1FBoAe(>7h;GC$gBIX&V
z5d9WvX4-VryghQp2inc&4rB(CNN#>=Mr!UG(58czlsuTg$aGV_TknHlLCBrVttu5A
z=dJI<5CpH5CLe5D+E|{hy!W?|s2!g{6mM;{UOKO{chHQTE?m2x$
zPR0lbY4Nu7RnKSDPAyATTS6cC{>W&Y+E^l2eL0XT7_mv0ZCeX?H^%*WgG0W_RP0p6*22q+z7sBr#HBEE`(Ih1RlD^xz9s+6zyq(0
zivXJ`59l2fAGzA>4ZI-Q<63TMXkWD09dR7Fn18`3xfQ{#RV=i_>Uvu-6VGA;8P<5(
zQ&sAstx}NJmh;{QIg_%pVIAIQ4H??N`n
zPa50I42rfrSI2Ir?dLbq_Z}3|cg~Ez(hSJ6!y1%IQ|s!osn{W;tBC`Yds>><>gc6$
z#?Q|t2Wl(vb)5lA7x%Q5R94jAKvW7(0*Ju3jziLg^x$7cGu_*
zagt!)-`J;>wITJP$FqsG+Ai(7Giw`e&S7N#82=gP`W4rc#-|HCgY#uJu|tOs#*AVx
z=k+q8%PCIbK!X(PtQ$n6lbzr6l+Yn7(XL{tf66ReQ;uI=dQ)j4EWA2Sie6xRXY{O;
zu2)jx^tsrmy6-h!^1L7s2E;8S-^gh-gbsMZjQ;&}Y`x9bupj
z($?}OAg2PaWM7)iK@2?bBhuNA=}@H@-*qUm@{yeF@bA?oSNva>g-%E%YZk(b0(e`P
zBb#ybBhw1j+dc)88GiUO5xG^4b$m8@^%pHBf6+_bw??>nz`DyP|(!
zwFu|e3ziuOGm(ZW51F~e0SCl*&7so#g+dR_LaV)ESXxh*o)=#yq~Ol7#TLOePOW~>
z{C&`S5%Lq@7nh=FPv*$JeQN^vGh}wxamD^1=>zP5i;k70 UrZq-Gh*A1A14OVaxgG4
z;j&-Fb4GlB2ODm`wkCY}=(k9bt1G~(mm!G=rGW?|aJA#poP(P(j~O1oI`nDjHlt;j
zHg#Z`oklCp{Zo$f|9(*Qck}9sShXD*{YlG_!gkcaDkSfF`CD6PP4Op-|14GX
hPoqY^29F=3hPDudA6(7eH#|l=wAA!f;SU~#{R{HdMce=Y
literal 0
HcmV?d00001
diff --git a/docs/operator-manual/webhook.md b/docs/operator-manual/webhook.md
index 9a93d6ff0208c..1d5ad5ec79c96 100644
--- a/docs/operator-manual/webhook.md
+++ b/docs/operator-manual/webhook.md
@@ -4,7 +4,7 @@
Argo CD polls Git repositories every three minutes to detect changes to the manifests. To eliminate
this delay from polling, the API server can be configured to receive webhook events. Argo CD supports
-Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server and Gogs. The following explains how to configure
+Git webhook notifications from GitHub, GitLab, Bitbucket, Bitbucket Server, Azure DevOps and Gogs. The following explains how to configure
a Git webhook for GitHub, but the same process should be applicable to other providers.
!!! note
@@ -12,19 +12,28 @@ a Git webhook for GitHub, but the same process should be applicable to other pro
the same. A hook event for a push to branch `x` will trigger a refresh for an app pointing at the same repo with
`targetRevision: refs/tags/x`.
-### 1. Create The WebHook In The Git Provider
+## 1. Create The WebHook In The Git Provider
In your Git provider, navigate to the settings page where webhooks can be configured. The payload
URL configured in the Git provider should use the `/api/webhook` endpoint of your Argo CD instance
(e.g. `https://argocd.example.com/api/webhook`). If you wish to use a shared secret, input an
arbitrary value in the secret. This value will be used when configuring the webhook in the next step.
+## Github
+
![Add Webhook](../assets/webhook-config.png "Add Webhook")
!!! note
When creating the webhook in GitHub, the "Content type" needs to be set to "application/json". The default value "application/x-www-form-urlencoded" is not supported by the library used to handle the hooks
-### 2. Configure Argo CD With The WebHook Secret (Optional)
+## Azure DevOps
+
+![Add Webhook](../assets/azure-devops-webhook-config.png "Add Webhook")
+
+Azure DevOps optionally supports securing the webhook using basic authentication. To use it, specify the username and password in the webhook configuration and configure the same username/password in `argocd-secret` Kubernetes secret in
+`webhook.azuredevops.username` and `webhook.azuredevops.password` keys.
+
+## 2. Configure Argo CD With The WebHook Secret (Optional)
Configuring a webhook shared secret is optional, since Argo CD will still refresh applications
related to the Git repository, even with unauthenticated webhook events. This is safe to do since
@@ -36,12 +45,14 @@ In the `argocd-secret` kubernetes secret, configure one of the following keys wi
provider's webhook secret configured in step 1.
| Provider | K8s Secret Key |
-|-----------------| ---------------------------------|
+|-----------------|----------------------------------|
| GitHub | `webhook.github.secret` |
| GitLab | `webhook.gitlab.secret` |
| BitBucket | `webhook.bitbucket.uuid` |
| BitBucketServer | `webhook.bitbucketserver.secret` |
| Gogs | `webhook.gogs.secret` |
+| Azure DevOps | `webhook.azuredevops.username` |
+| | `webhook.azuredevops.password` |
Edit the Argo CD kubernetes secret:
@@ -79,6 +90,10 @@ stringData:
# gogs server webhook secret
webhook.gogs.secret: shhhh! it's a gogs server secret
+
+ # azuredevops username and password
+ webhook.azuredevops.username: admin
+ webhook.azuredevops.password: secret-password
```
After saving, the changes should take effect automatically.
diff --git a/go.mod b/go.mod
index e3eea7a804b99..3f3659a474ff8 100644
--- a/go.mod
+++ b/go.mod
@@ -29,9 +29,10 @@ require (
github.com/go-logr/logr v1.2.4
github.com/go-openapi/loads v0.21.2
github.com/go-openapi/runtime v0.26.0
+ github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8
github.com/go-redis/cache/v9 v9.0.0
github.com/gobwas/glob v0.2.3
- github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2
+ github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355
github.com/gogo/protobuf v1.3.2
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/protobuf v1.5.3
@@ -85,7 +86,6 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc
google.golang.org/grpc v1.56.2
google.golang.org/protobuf v1.31.0
- gopkg.in/go-playground/webhooks.v5 v5.17.0
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.24.2
diff --git a/go.sum b/go.sum
index 28695dba8b53d..8b9fad0e0e528 100644
--- a/go.sum
+++ b/go.sum
@@ -1048,6 +1048,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8 h1:QDFjrpOZagU8KEpSCF0WvBKOGq2GYuVZ4ZDg/gelrEE=
+github.com/go-playground/webhooks/v6 v6.2.1-0.20230808162451-10570b0a59e8/go.mod h1:GCocmfMtpJdkEOM1uG9p2nXzg1kY5X/LtvQgtPHUaaA=
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@@ -1094,8 +1096,8 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2 h1:BbwX8wsMRDZRdNYxAna+4ls3wvMKJyn4PT6Zk1CPxP4=
-github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0=
+github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355 h1:HTVNOdTWO/gHYeFnr/HwpYwY6tgMcYd+Rgf1XrHnORY=
+github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -2791,8 +2793,6 @@ gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
-gopkg.in/go-playground/webhooks.v5 v5.17.0 h1:truBced5ZmkiNKK47cM8bMe86wUSjNks7SFMuNKwzlc=
-gopkg.in/go-playground/webhooks.v5 v5.17.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
diff --git a/util/settings/settings.go b/util/settings/settings.go
index a9d49b78cd5df..06fd0488ad711 100644
--- a/util/settings/settings.go
+++ b/util/settings/settings.go
@@ -71,6 +71,10 @@ type ArgoCDSettings struct {
WebhookBitbucketServerSecret string `json:"webhookBitbucketServerSecret,omitempty"`
// WebhookGogsSecret holds the shared secret for authenticating Gogs webhook events
WebhookGogsSecret string `json:"webhookGogsSecret,omitempty"`
+ // WebhookAzureDevOpsUsername holds the username for authenticating Azure DevOps webhook events
+ WebhookAzureDevOpsUsername string `json:"webhookAzureDevOpsUsername,omitempty"`
+ // WebhookAzureDevOpsPassword holds the password for authenticating Azure DevOps webhook events
+ WebhookAzureDevOpsPassword string `json:"webhookAzureDevOpsPassword,omitempty"`
// Secrets holds all secrets in argocd-secret as a map[string]string
Secrets map[string]string `json:"secrets,omitempty"`
// KustomizeBuildOptions is a string of kustomize build parameters
@@ -411,6 +415,10 @@ const (
settingsWebhookBitbucketServerSecretKey = "webhook.bitbucketserver.secret"
// settingsWebhookGogsSecret is the key for Gogs webhook secret
settingsWebhookGogsSecretKey = "webhook.gogs.secret"
+ // settingsWebhookAzureDevOpsUsernameKey is the key for Azure DevOps webhook username
+ settingsWebhookAzureDevOpsUsernameKey = "webhook.azuredevops.username"
+ // settingsWebhookAzureDevOpsPasswordKey is the key for Azure DevOps webhook password
+ settingsWebhookAzureDevOpsPasswordKey = "webhook.azuredevops.password"
// settingsApplicationInstanceLabelKey is the key to configure injected app instance label key
settingsApplicationInstanceLabelKey = "application.instanceLabelKey"
// settingsResourceTrackingMethodKey is the key to configure tracking method for application resources
@@ -1457,6 +1465,12 @@ func (mgr *SettingsManager) updateSettingsFromSecret(settings *ArgoCDSettings, a
if gogsWebhookSecret := argoCDSecret.Data[settingsWebhookGogsSecretKey]; len(gogsWebhookSecret) > 0 {
settings.WebhookGogsSecret = string(gogsWebhookSecret)
}
+ if azureDevOpsUsername := argoCDSecret.Data[settingsWebhookAzureDevOpsUsernameKey]; len(azureDevOpsUsername) > 0 {
+ settings.WebhookAzureDevOpsUsername = string(azureDevOpsUsername)
+ }
+ if azureDevOpsPassword := argoCDSecret.Data[settingsWebhookAzureDevOpsPasswordKey]; len(azureDevOpsPassword) > 0 {
+ settings.WebhookAzureDevOpsPassword = string(azureDevOpsPassword)
+ }
// The TLS certificate may be externally managed. We try to load it from an
// external secret first. If the external secret doesn't exist, we either
@@ -1576,6 +1590,12 @@ func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
if settings.WebhookGogsSecret != "" {
argoCDSecret.Data[settingsWebhookGogsSecretKey] = []byte(settings.WebhookGogsSecret)
}
+ if settings.WebhookAzureDevOpsUsername != "" {
+ argoCDSecret.Data[settingsWebhookAzureDevOpsUsernameKey] = []byte(settings.WebhookAzureDevOpsUsername)
+ }
+ if settings.WebhookAzureDevOpsPassword != "" {
+ argoCDSecret.Data[settingsWebhookAzureDevOpsPasswordKey] = []byte(settings.WebhookAzureDevOpsPassword)
+ }
// we only write the certificate to the secret if it's not externally
// managed.
if settings.Certificate != nil && !settings.CertificateIsExternal {
diff --git a/util/webhook/testdata/azuredevops-git-push-event.json b/util/webhook/testdata/azuredevops-git-push-event.json
new file mode 100644
index 0000000000000..102e7f08aab3d
--- /dev/null
+++ b/util/webhook/testdata/azuredevops-git-push-event.json
@@ -0,0 +1,107 @@
+{
+ "subscriptionId": "8fd412f1-9873-4b45-8854-655b1b8a2eff",
+ "notificationId": 2,
+ "id": "09b0b950-47fa-4f45-8b65-5a22686314f8",
+ "eventType": "git.push",
+ "publisherId": "tfs",
+ "message": {
+ "text": "Alexander Matyushentsev pushed updates to alex-test:master\r\n(https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)",
+ "html": "Alexander Matyushentsev pushed updates to alex-test:master",
+ "markdown": "Alexander Matyushentsev pushed updates to [alex-test](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/):[master](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)"
+ },
+ "detailedMessage": {
+ "text": "Alexander Matyushentsev pushed a commit to alex-test:master\r\n - draft 298a79aa (https://dev.azure.com/alexander0053/alex-test/_git/alex-test/commit/298a79aa1552799a70718a0ee914d153d5a1a76b)",
+ "html": "Alexander Matyushentsev pushed a commit to alex-test:master\r\n",
+ "markdown": "Alexander Matyushentsev pushed a commit to [alex-test](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/):[master](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/#version=GBmaster)\r\n* draft [298a79aa](https://dev.azure.com/alexander0053/alex-test/_git/alex-test/commit/298a79aa1552799a70718a0ee914d153d5a1a76b)"
+ },
+ "resource": {
+ "commits": [
+ {
+ "commitId": "298a79aa1552799a70718a0ee914d153d5a1a76b",
+ "author": {
+ "name": "Alexander Matyushentsev",
+ "email": "AMatyushentsev@gmail.com",
+ "date": "2023-08-09T00:45:39Z"
+ },
+ "committer": {
+ "name": "Alexander Matyushentsev",
+ "email": "AMatyushentsev@gmail.com",
+ "date": "2023-08-09T00:45:39Z"
+ },
+ "comment": "draft\n\nSigned-off-by: Alexander Matyushentsev ",
+ "url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/commits/298a79aa1552799a70718a0ee914d153d5a1a76b"
+ }
+ ],
+ "refUpdates": [
+ {
+ "name": "refs/heads/master",
+ "oldObjectId": "fa51eeb1e50b98293ce281e6d5492b9decae613b",
+ "newObjectId": "298a79aa1552799a70718a0ee914d153d5a1a76b"
+ }
+ ],
+ "repository": {
+ "id": "ba2967cc-02c2-414c-8d10-1b99197cbaa6",
+ "name": "alex-test",
+ "url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6",
+ "project": {
+ "id": "ab1c194f-94fa-4d1a-87ff-e9458637d060",
+ "name": "alex-test",
+ "url": "https://dev.azure.com/alexander0053/_apis/projects/ab1c194f-94fa-4d1a-87ff-e9458637d060",
+ "state": "wellFormed",
+ "visibility": "unchanged",
+ "lastUpdateTime": "0001-01-01T00:00:00"
+ },
+ "defaultBranch": "refs/heads/master",
+ "remoteUrl": "https://dev.azure.com/alexander0053/alex-test/_git/alex-test"
+ },
+ "pushedBy": {
+ "displayName": "Alexander Matyushentsev",
+ "url": "https://spsprodcus4.vssps.visualstudio.com/A7a73fd0c-d080-434d-a8b4-0b4c0217e290/_apis/Identities/07220d5e-521c-683d-982c-726e80086d08",
+ "_links": {
+ "avatar": {
+ "href": "https://dev.azure.com/alexander0053/_apis/GraphProfile/MemberAvatars/aad.MDcyMjBkNWUtNTIxYy03ODNkLTk4MmMtNzI2ZTgwMDg2ZDA4"
+ }
+ },
+ "id": "07220d5e-521c-683d-982c-726e80086d08",
+ "uniqueName": "alexander@akuity.onmicrosoft.com",
+ "imageUrl": "https://dev.azure.com/alexander0053/_api/_common/identityImage?id=07220d5e-521c-683d-982c-726e80086d08",
+ "descriptor": "aad.MDcyMjBkNWUtNTIxYy03ODNkLTk4MmMtNzI2ZTgwMDg2ZDA4"
+ },
+ "pushId": 4,
+ "date": "2023-08-09T00:45:42.8315767Z",
+ "url": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4",
+ "_links": {
+ "self": {
+ "href": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4"
+ },
+ "repository": {
+ "href": "https://dev.azure.com/alexander0053/ab1c194f-94fa-4d1a-87ff-e9458637d060/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6"
+ },
+ "commits": {
+ "href": "https://dev.azure.com/alexander0053/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/pushes/4/commits"
+ },
+ "pusher": {
+ "href": "https://spsprodcus4.vssps.visualstudio.com/A7a73fd0c-d080-434d-a8b4-0b4c0217e290/_apis/Identities/07220d5e-521c-683d-982c-726e80086d08"
+ },
+ "refs": {
+ "href": "https://dev.azure.com/alexander0053/ab1c194f-94fa-4d1a-87ff-e9458637d060/_apis/git/repositories/ba2967cc-02c2-414c-8d10-1b99197cbaa6/refs/heads/master"
+ }
+ }
+ },
+ "resourceVersion": "1.0",
+ "resourceContainers": {
+ "collection": {
+ "id": "d54a3f95-82a0-47c4-8444-00da7391d976",
+ "baseUrl": "https://dev.azure.com/alexander0053/"
+ },
+ "account": {
+ "id": "7a73fd0c-d080-434d-a8b4-0b4c0217e290",
+ "baseUrl": "https://dev.azure.com/alexander0053/"
+ },
+ "project": {
+ "id": "ab1c194f-94fa-4d1a-87ff-e9458637d060",
+ "baseUrl": "https://dev.azure.com/alexander0053/"
+ }
+ },
+ "createdDate": "2023-08-09T00:45:49.3448928Z"
+}
\ No newline at end of file
diff --git a/util/webhook/webhook.go b/util/webhook/webhook.go
index ca4742e31a1f1..9955540ea04a9 100644
--- a/util/webhook/webhook.go
+++ b/util/webhook/webhook.go
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
- "github.com/argoproj/argo-cd/v2/util/glob"
"html"
"net/http"
"net/url"
@@ -12,13 +11,14 @@ import (
"regexp"
"strings"
+ "github.com/go-playground/webhooks/v6/azuredevops"
+ "github.com/go-playground/webhooks/v6/bitbucket"
+ bitbucketserver "github.com/go-playground/webhooks/v6/bitbucket-server"
+ "github.com/go-playground/webhooks/v6/github"
+ "github.com/go-playground/webhooks/v6/gitlab"
+ "github.com/go-playground/webhooks/v6/gogs"
gogsclient "github.com/gogits/go-gogs-client"
log "github.com/sirupsen/logrus"
- "gopkg.in/go-playground/webhooks.v5/bitbucket"
- bitbucketserver "gopkg.in/go-playground/webhooks.v5/bitbucket-server"
- "gopkg.in/go-playground/webhooks.v5/github"
- "gopkg.in/go-playground/webhooks.v5/gitlab"
- "gopkg.in/go-playground/webhooks.v5/gogs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/common"
@@ -28,6 +28,7 @@ import (
servercache "github.com/argoproj/argo-cd/v2/server/cache"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/db"
+ "github.com/argoproj/argo-cd/v2/util/glob"
"github.com/argoproj/argo-cd/v2/util/security"
"github.com/argoproj/argo-cd/v2/util/settings"
)
@@ -41,21 +42,26 @@ type settingsSource interface {
// https://github.com/shadow-maint/shadow/blob/master/libmisc/chkname.c#L36
const usernameRegex = `[a-zA-Z0-9_\.][a-zA-Z0-9_\.-]{0,30}[a-zA-Z0-9_\.\$-]?`
-var _ settingsSource = &settings.SettingsManager{}
+var (
+ _ settingsSource = &settings.SettingsManager{}
+ errBasicAuthVerificationFailed = errors.New("basic auth verification failed")
+)
type ArgoCDWebhookHandler struct {
- repoCache *cache.Cache
- serverCache *servercache.Cache
- db db.ArgoDB
- ns string
- appNs []string
- appClientset appclientset.Interface
- github *github.Webhook
- gitlab *gitlab.Webhook
- bitbucket *bitbucket.Webhook
- bitbucketserver *bitbucketserver.Webhook
- gogs *gogs.Webhook
- settingsSrc settingsSource
+ repoCache *cache.Cache
+ serverCache *servercache.Cache
+ db db.ArgoDB
+ ns string
+ appNs []string
+ appClientset appclientset.Interface
+ github *github.Webhook
+ gitlab *gitlab.Webhook
+ bitbucket *bitbucket.Webhook
+ bitbucketserver *bitbucketserver.Webhook
+ azuredevops *azuredevops.Webhook
+ azuredevopsAuthHandler func(r *http.Request) error
+ gogs *gogs.Webhook
+ settingsSrc settingsSource
}
func NewHandler(namespace string, applicationNamespaces []string, appClientset appclientset.Interface, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB) *ArgoCDWebhookHandler {
@@ -79,20 +85,35 @@ func NewHandler(namespace string, applicationNamespaces []string, appClientset a
if err != nil {
log.Warnf("Unable to init the Gogs webhook")
}
+ azuredevopsWebhook, err := azuredevops.New()
+ if err != nil {
+ log.Warnf("Unable to init the Azure DevOps webhook")
+ }
+ azuredevopsAuthHandler := func(r *http.Request) error {
+ if set.WebhookAzureDevOpsUsername != "" && set.WebhookAzureDevOpsPassword != "" {
+ username, password, ok := r.BasicAuth()
+ if !ok || username != set.WebhookAzureDevOpsUsername || password != set.WebhookAzureDevOpsPassword {
+ return errBasicAuthVerificationFailed
+ }
+ }
+ return nil
+ }
acdWebhook := ArgoCDWebhookHandler{
- ns: namespace,
- appNs: applicationNamespaces,
- appClientset: appClientset,
- github: githubWebhook,
- gitlab: gitlabWebhook,
- bitbucket: bitbucketWebhook,
- bitbucketserver: bitbucketserverWebhook,
- gogs: gogsWebhook,
- settingsSrc: settingsSrc,
- repoCache: repoCache,
- serverCache: serverCache,
- db: argoDB,
+ ns: namespace,
+ appNs: applicationNamespaces,
+ appClientset: appClientset,
+ github: githubWebhook,
+ gitlab: gitlabWebhook,
+ bitbucket: bitbucketWebhook,
+ bitbucketserver: bitbucketserverWebhook,
+ azuredevops: azuredevopsWebhook,
+ azuredevopsAuthHandler: azuredevopsAuthHandler,
+ gogs: gogsWebhook,
+ settingsSrc: settingsSrc,
+ repoCache: repoCache,
+ serverCache: serverCache,
+ db: argoDB,
}
return &acdWebhook
@@ -107,6 +128,14 @@ func parseRevision(ref string) string {
// the revision, and whether or not this affected origin/HEAD (the default branch of the repository)
func affectedRevisionInfo(payloadIf interface{}) (webURLs []string, revision string, change changeInfo, touchedHead bool, changedFiles []string) {
switch payload := payloadIf.(type) {
+ case azuredevops.GitPushEvent:
+ // See: https://learn.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.push
+ webURLs = append(webURLs, payload.Resource.Repository.RemoteURL)
+ revision = parseRevision(payload.Resource.RefUpdates[0].Name)
+ change.shaAfter = parseRevision(payload.Resource.RefUpdates[0].NewObjectID)
+ change.shaBefore = parseRevision(payload.Resource.RefUpdates[0].OldObjectID)
+ touchedHead = payload.Resource.RefUpdates[0].Name == payload.Resource.Repository.DefaultBranch
+ // unfortunately, Azure DevOps doesn't provide a list of changed files
case github.PushPayload:
// See: https://developer.github.com/v3/activity/events/types/#pushevent
webURLs = append(webURLs, payload.Repository.HTMLURL)
@@ -430,6 +459,14 @@ func (a *ArgoCDWebhookHandler) Handler(w http.ResponseWriter, r *http.Request) {
var err error
switch {
+ case r.Header.Get("X-Vss-Activityid") != "":
+ if err = a.azuredevopsAuthHandler(r); err != nil {
+ if errors.Is(err, errBasicAuthVerificationFailed) {
+ log.WithField(common.SecurityField, common.SecurityHigh).Infof("Azure DevOps webhook basic auth verification failed")
+ }
+ } else {
+ payload, err = a.azuredevops.Parse(r, azuredevops.GitPushEventType)
+ }
//Gogs needs to be checked before GitHub since it carries both Gogs and (incompatible) GitHub headers
case r.Header.Get("X-Gogs-Event") != "":
payload, err = a.gogs.Parse(r, gogs.PushEvent)
diff --git a/util/webhook/webhook_test.go b/util/webhook/webhook_test.go
index cf11162febc6c..b241d7c671841 100644
--- a/util/webhook/webhook_test.go
+++ b/util/webhook/webhook_test.go
@@ -5,18 +5,19 @@ import (
"encoding/json"
"fmt"
"io"
- "k8s.io/apimachinery/pkg/types"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
+ "k8s.io/apimachinery/pkg/types"
+
+ "github.com/go-playground/webhooks/v6/bitbucket"
+ bitbucketserver "github.com/go-playground/webhooks/v6/bitbucket-server"
+ "github.com/go-playground/webhooks/v6/github"
+ "github.com/go-playground/webhooks/v6/gitlab"
gogsclient "github.com/gogits/go-gogs-client"
- "gopkg.in/go-playground/webhooks.v5/bitbucket"
- bitbucketserver "gopkg.in/go-playground/webhooks.v5/bitbucket-server"
- "gopkg.in/go-playground/webhooks.v5/github"
- "gopkg.in/go-playground/webhooks.v5/gitlab"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"
@@ -89,6 +90,22 @@ func TestGitHubCommitEvent(t *testing.T) {
hook.Reset()
}
+func TestAzureDevOpsCommitEvent(t *testing.T) {
+ hook := test.NewGlobal()
+ h := NewMockHandler(nil, []string{})
+ req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
+ req.Header.Set("X-Vss-Activityid", "abc")
+ eventJSON, err := os.ReadFile("testdata/azuredevops-git-push-event.json")
+ assert.NoError(t, err)
+ req.Body = io.NopCloser(bytes.NewReader(eventJSON))
+ w := httptest.NewRecorder()
+ h.Handler(w, req)
+ assert.Equal(t, w.Code, http.StatusOK)
+ expectedLogResult := "Received push event repo: https://dev.azure.com/alexander0053/alex-test/_git/alex-test, revision: master, touchedHead: true"
+ assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
+ hook.Reset()
+}
+
// TestGitHubCommitEvent_MultiSource_Refresh makes sure that a webhook will refresh a multi-source app when at least
// one source matches.
func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
From a4eeb0039573d0621a2af43cda07d33b9413786e Mon Sep 17 00:00:00 2001
From: Alexander Matyushentsev
Date: Wed, 9 Aug 2023 16:52:28 -0700
Subject: [PATCH 3/5] fix: api server fails to call dex with istio (#14995)
Signed-off-by: Alexander Matyushentsev
---
manifests/base/dex/argocd-dex-server-service.yaml | 1 +
manifests/ha/install.yaml | 3 ++-
manifests/ha/namespace-install.yaml | 3 ++-
manifests/install.yaml | 3 ++-
manifests/namespace-install.yaml | 3 ++-
5 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/manifests/base/dex/argocd-dex-server-service.yaml b/manifests/base/dex/argocd-dex-server-service.yaml
index 7aeabca1e674e..cffbf006ae624 100644
--- a/manifests/base/dex/argocd-dex-server-service.yaml
+++ b/manifests/base/dex/argocd-dex-server-service.yaml
@@ -9,6 +9,7 @@ metadata:
spec:
ports:
- name: http
+ appProtocol: TCP
protocol: TCP
port: 5556
targetPort: 5556
diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml
index 26801daea28a2..7716ec7e68bc9 100644
--- a/manifests/ha/install.yaml
+++ b/manifests/ha/install.yaml
@@ -19769,7 +19769,8 @@ metadata:
name: argocd-dex-server
spec:
ports:
- - name: http
+ - appProtocol: TCP
+ name: http
port: 5556
protocol: TCP
targetPort: 5556
diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml
index 9c6be39785fec..03e2dd32f2395 100644
--- a/manifests/ha/namespace-install.yaml
+++ b/manifests/ha/namespace-install.yaml
@@ -1275,7 +1275,8 @@ metadata:
name: argocd-dex-server
spec:
ports:
- - name: http
+ - appProtocol: TCP
+ name: http
port: 5556
protocol: TCP
targetPort: 5556
diff --git a/manifests/install.yaml b/manifests/install.yaml
index 6a5afae6a87ae..370b3f22b35c8 100644
--- a/manifests/install.yaml
+++ b/manifests/install.yaml
@@ -18973,7 +18973,8 @@ metadata:
name: argocd-dex-server
spec:
ports:
- - name: http
+ - appProtocol: TCP
+ name: http
port: 5556
protocol: TCP
targetPort: 5556
diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml
index 415ea143c5b64..ac244c7ccfe1d 100644
--- a/manifests/namespace-install.yaml
+++ b/manifests/namespace-install.yaml
@@ -479,7 +479,8 @@ metadata:
name: argocd-dex-server
spec:
ports:
- - name: http
+ - appProtocol: TCP
+ name: http
port: 5556
protocol: TCP
targetPort: 5556
From 51164e82f4a34345a01515bc0c6b072a967922c0 Mon Sep 17 00:00:00 2001
From: Vladimir <31961982+zvlb@users.noreply.github.com>
Date: Thu, 10 Aug 2023 05:28:07 +0300
Subject: [PATCH 4/5] fix(ui): Update default and max count for maxCookieNumber
(#14979)
* Update default and max count for maxCookieNumber
Signed-off-by: zvlb |