From 78acd79aff3da584cc782b47dc48dfe967035a07 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 17:24:02 +0530 Subject: [PATCH] feat: Implement automatic publishing of releases to Maven Central This commit introduces a GitHub Actions workflow to automatically publish releases to Maven Central when a new tag is pushed. Key changes: - Added a new `publish` job to the `ci.yml` workflow. - This job triggers on pushes to tags (e.g., `v1.0.0`, `v1.0.0-alpha1`). - It extracts the version from the Git tag. - It detects the release type (alpha, beta, stable) based on the tag name. - It restores `gradle.properties` from secrets. - It imports a GPG key for signing artifacts. - It publishes artifacts to Maven Central using the `publishAllPublicationsToMavenCentral` Gradle task. - It creates a GitHub Release with the published artifacts and release notes. Gradle changes: - Added `com.vanniktech.maven.publish` and `signing` plugins to `libs.versions.toml` and the root `build.gradle.kts`. - Configured the `maven-publish` plugin in `formz/build.gradle.kts` to: - Publish to Maven Central. - Sign all publications. - Set coordinates, POM details (name, description, license, developers, SCM). - Removed the previous manual `maven-publish` configuration from `formz/build.gradle.kts`. - The version name is now derived from the `VERSION_NAME` project property, falling back to a default if not set. A `secring.gpg` file has been added, presumably containing the GPG secret key for signing, though its content is binary and not displayed in the diff. --- .github/workflows/ci.yml | 68 ++++++++++++++++++++++++++++++++++++++ build.gradle.kts | 1 + formz/build.gradle.kts | 51 +++++++++++++++++----------- gradle/libs.versions.toml | 5 +++ secring.gpg | Bin 0 -> 9929 bytes 5 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 secring.gpg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9f5052..4fa906c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,3 +94,71 @@ jobs: path: | **/build/outputs/aar/*.aar **/build/libs/*.jar + publish: + name: Publish Release + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Extract version from Git tag + id: version + run: | + TAG=${GITHUB_REF#refs/tags/} + VERSION_NAME=${TAG#v} + echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV + + - name: Detect release type + id: release_type + run: | + if [[ "${GITHUB_REF}" == *alpha* ]]; then + echo "release_stage=alpha" >> $GITHUB_OUTPUT + elif [[ "${GITHUB_REF}" == *beta* ]]; then + echo "release_stage=beta" >> $GITHUB_OUTPUT + else + echo "release_stage=stable" >> $GITHUB_OUTPUT + fi + - name: Restore gradle.properties + env: + GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} + shell: bash + run: | + mkdir -p ~/.gradle/ + echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV + echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.PASSPHRASE }} + + - name: Publish to Maven Central + run: ./gradlew formz:publishAllPublicationsToMavenCentral --no-configuration-cache -PVERSION_NAME=${{ env.VERSION_NAME }} + + - name: Upload GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref_name }} + name: Release ${{ github.ref_name }} + body: | + 🚀 **Release:** `${{ github.ref_name }}` + 🏷 **Type:** `${{ steps.release_type.outputs.release_stage }}` + + This release was automatically published from CI. + draft: false + prerelease: ${{ steps.release_type.outputs.release_stage != 'stable' }} + files: | + **/build/outputs/aar/*.aar + **/build/libs/*.jar \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e7c69b4..a9ed3c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ plugins { alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.arturbosch.detekt) apply false alias(libs.plugins.spotless) apply false + alias(libs.plugins.maven.publish) apply false } val detektVersion = libs.versions.detekt.get() diff --git a/formz/build.gradle.kts b/formz/build.gradle.kts index ad77bfc..b27c2b5 100644 --- a/formz/build.gradle.kts +++ b/formz/build.gradle.kts @@ -3,9 +3,10 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) - id("maven-publish") + alias(libs.plugins.maven.publish) + alias(libs.plugins.signing) } - +val versionName = project.findProperty("VERSION_NAME") as String? ?: "0.0.6-alpha" android { namespace = "com.nareshchocha.formz" compileSdk = @@ -39,12 +40,6 @@ android { jvmTarget.set(JvmTarget.JVM_21) } } - publishing { - singleVariant("release") { - withSourcesJar() - withJavadocJar() - } - } } dependencies { @@ -52,19 +47,37 @@ dependencies { testImplementation(libs.junit) } -publishing { - publications { - create("release") { - groupId = "com.github.ChochaNaresh" - artifactId = "formz" - version = "0.0.5" +val automaticRelease: Boolean = true +mavenPublishing { + publishToMavenCentral(automaticRelease) + signAllPublications() + coordinates("io.github.chochanaresh", "formz", versionName) - afterEvaluate { - from(components["release"]) - /* from { - components.release - }*/ + pom { + name.set("formz") + description.set( + "Formz is a lightweight validation framework for Android forms written in Kotlin. It provides a simple, yet powerful way to define, validate, and manage form inputs in your Android applications. The library is designed with immutability and performance in mind, ensuring that expensive validation logic is computed only once per input." + ) + inceptionYear.set("2025") + url.set("https://github.com/ChochaNaresh/Formz") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt") } } + developers { + developer { + id.set("ChochaNaresh") + name.set("Naresh Chocha") + url.set("https://github.com/ChochaNaresh") + } + } + scm { + url.set("https://github.com/ChochaNaresh/Formz") + connection.set("scm:git:git://github.com/ChochaNaresh/Formz.git") + developerConnection.set("scm:git:ssh://git@github.com/ChochaNaresh/Formz.git") + } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0ce522e..a20fdd5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ agp = "8.11.0" kotlin = "2.2.0" detekt = "1.23.8" +mavenPublish = "0.33.0" compileSdk = "36" targetSdk = "36" minSdk = "21" @@ -55,4 +56,8 @@ compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = " # code style review arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } spotless = { id = "com.diffplug.spotless", version = "7.0.4" } +# maven publish +maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" } +# signing +signing = { id = "signing" } diff --git a/secring.gpg b/secring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..5c7edf14200b4756eb5674c08910d59bb1121af9 GIT binary patch literal 9929 zcma*rV^}7Ry6^G$2-j;uFb#`wo1xMPV_uG%OW&rg&=j=$!1%J%qrpM zkm!)&1d#Reg;zBBg+qjg#?f8fdhcpa(`n^@JLB0VO3g1cl6eUr(2^Yxr-E|`Ri)m#+aQeo~StnuTK5Osl@bgX=vV-nh)qoX%3k zyHD5m!`^0AkH$&G2E5w~DNwJ`GEP&u9dQy%bXoZ;?|mOoUaoHl+kV7`SR2#T-&`Y- z6V=L(xK=_FlVKnT=fl4i)y6wnWoy$P#y+WRLNP!2BHusn0R-B1yuaVfL+k&jZhia5S_P&+rSS&|K!hCioz)#ziyNi zEYx&i>{K?Pd)i0}>B8{rFu$uV&s2e5#W6FXKXh(QvypAbMd_KN|62kPC&nFIe|~kj5)>eQ<(La6 z&x!5r2!;v{2L1^H36W5lGMeIWjsE`1@Lu5Lqu7Vnamj6mz|A+_m}HZqBU`BOQ7ES3 zo2Z#4G{0C^e5d}Y%I5XHDtn*7&dyBsSDNNR+X__arA(UM}OtqCh>3i+|ThNc&mY^lwmCzp;@P%e5|fLIFvZ zq-2%6ZFt&eXax^qei9psk)UZ>w}!KvyuOb9OVyn4mltnp1_h&`-_mle1{t&D?_aSi z_-FEA{t*B;`))~gtD}DKVMKV&{Z8h3W3jJ4_6csu;yJ*FSQt3E&b&3Sx3&1W_1DNF z3nMdwhllCmY0Y^bQq1m6j-7~J@!-D;nMDGxSAH;GEi)3)Ky%U@V7Yuz@r>myZL zj5fOOTnvl)lAm64+Wcx&4^L_Ne`HHAPhZyF1+B;09~O<`3^b#-DH(!2F^}{lu)D^y zN!8aQL9HLTo=mEETuZB{kqcIji{25FB?|l7U{pQ~3!ep?YxYrT`S45YwE~&H$fN}N zjs!lbYI3+X<+T#-?^L3QWcv%uVI?zE;b&@rZ08L_0IAEhPBFX5Y(4|56ee_a;>z4; z8w-k(Rb9u$UV7!AOjNlI^x&_w-YW+A-pz<2&Rb|j8oSa}7^UWQ{gyBss6Y(!7|Fox zF&^9+#3HwJT6D*;ZbJ=n7W<9q+R1_1KuhBQ$?F8?Qc*L#m+ZRxJ2SlU6XW<=V*rFH}iMeyH%M|X9D6no#+n^qta?^Y0hn$ zRD>-kwS&KCu69$QY1E(a|1c25Ask6Tik7~*ez=|uqIK>#uzR6Q0Lt8`AXEf_{S3j! z#aGBQ#5-i?RUGa@8oG$rE(~*!_@&J+TGe8)q9e1VD>yWep{{i7XI1!WF`&r{4$s5* zUukvq8`rB}o+QCjk#NFi5u-(lO{L}&a&{|$oBG7%E~7nPP@pegM1ejP zNYFcXu!a>Eci%X%rpby@+b_^;eAp{l;MvU440u`<(dN6YpXo|3d?5ZZl_@a>32!7GaCb!Uodo7%N*WA zO_si`v``cm;M`B#2&)OzLCTx4j*^<1XK%S>1hvL+Bo8(f)lRoUUkrHihMXgkFb1%j zo7T;fJBcNviXVJLNpy}PY&&X}mYWGCS7AOx83_$fb@ma>K(nE!C^$>IM%wn|rsXeO zRB-#dH@Q#Ek6$#c>y?UXW^eSo^-v1UR+UuhlObKRQD3R2;)|9gSn?u4lfHktAV_Y( zm{ZAXX5?z)6Q0RwYAFr9{db1>)5RpP?*)bdRbT%1w!yRfLH%R(C(Xs(*pY(U;!Xdz z>05$hX>BWQ@Pe`_7V>0ocixef*InJY+sI)Z3oJLyLNF*6(*_`f!ri>Lx4eC;kdm>B zxvLe4xRs-+l`#p8sf)R>o3(=_iG#T(iKUYziLJReEs22Xzs?T-+OmjP+8bNjF_}8r z3q?WvfI?seg+i%n6UI*f&2CT7vaL&x6(`ELi$O#D*PDjGhlECegN1>BAcFv4 z!o$HLKtN$ZK!QUM!9ZXKfx*L}{Coe{i~OZd|4@nESbfElR(VM2mf+Wc2ZA|SJmJ>1 z5zkVB2w6{f@>?K)z)oy3iT%An5lX|w`7{JZ$G36apNUWQ^2~T>6y%!jfUXZ#pH;s8+rQM7u3CO zrw!c_$t4ZvPeD{ZQZwN{4^B{JAqTxm)*)|udmFnu#^oDe&11?i3l|+woZdOi{?PuE zQW4fY{2oFN%wRic&HpKoyi93~p{m?EP@&Om<%!;Jr>MVwERxRY*}Yl66HW$m^19D- z(Eo<@cadoqF1)y1=JBK3^>7$GlJ8A_Cr#})`Ixgmx3ifwzV}z`su>ha{CAL5swYmO z8iS9+!E5JUOij+HD^c>%N8^-`h8#5q<1`?R{eWkWT~*b(vUm;glYX13{|aL_7Q%hd zs}B^GvT$;$B_(Hx<64>*PbRkRJ5>3^6mOxi`DMf_uCSWRIAT=}Zy8nzDBEk(r#swL zvmMSNap|N@=(qX=)2TeXHrO^cempD>VZWLxuUkAECw7( zb)puUGgiF+1O2?07N(Bu*{ez3|HIvk_@HyVL8t(54zhbgAArQ>W)^oNjkX_*sKHPB zr}kOD5d1bU4FOkRYsB-&i%PJV232z%{jk7uqA zVS0fT8=8sjV7kzh?b&tLM5JNM-Qtq$I9WY!y!aYbkgC}jbB4(EN`eGlU<_LEQ#UyJ zi*iAv(mUXld&^niPy;uvjtecPL{g&{CujzxFxjcOz^H4X=Dmp4j&J}<9zKSJ4l(tu zH!;}c3Yk?@=x!U#$EVZCWlRqn#cr3@HFMgZv)&qrwZ~0>u+BRD=wpL`13|6#t27+` zuar%1k~HtN=`UYwVx3gNPs1;ZK_$VP@Tp6&@JaC;YUG*EfLN9Tf|Tr?VViqODeY`X zvpt}`oqqY450#J8_N?y?%dP?)wxH!Gpd|`IHZv$9TxL*F$QWVm+x5lZccX$fm9;=6 zSVL&6)r8h|{7yhR^z4B-zd_oj6dTXVoOX*yKKGmuqHc#GSt#eca}1FmJf@?M_%Q!> zpX;u5a=B&AONby%rCKqwvplks3MO>GO(`M+!GJ480|%Br-S+bWr4I%h8XtFgdHbXB zN(W#)r-~o!m)gbs_W|1f;BL3~&%u?AHphF-M&?^s_$;9uBxiZPz^(5EKA`dkzo$jW z7w5NJ&7_R<%-l3jh%C&jiG4r*S+b2hdc1EpwCQKQ%jS0Mv(L0|AuX8n0pDA+V7b7B z9z4OpERSAmkH?s+ubp&;*jKds$^_-sDf;Y1?Tn-2QM#fU-(Ve4vZI&3=z))Q2Lg?p z3fw*dID_p&4Y*Rp!)RV=2J}dWFJ~x-y=(pa^6Ha}P9xUt7YXv=yjZoKw7J zg*TLuZNnJ-$*)v4f)T)NzHMD5Z}wqpXos<-tTKTE_ekoJympxLx4owqYRoDxTT+nW zo~zx848{3-fNOo>Fj`#rpPR4(M{UyKQ)n2OZyDzet77H4@GSbv#R-u5<=W{n&jZyA z{jNcuzjm#M#eQ*fYy``&40Ys3!C8j*tBMFWdE!O`Hb0&P466H=;B;y$X}^!b%n^3m(-rOPgW3%j)09Wb3X?vSctE&>SdQ*qA0mNHnCO}dPaI;yc?EqxD%!p zV_}cJz}*TO8)HJ}y;l5@=bJl?44l#C^N$B0gj085Mt?+PxP^4aUsEX92pWsY4a@pxa`r#9zB-BDv(Mzr%R*;*>|;qF@>zRtQlH>B4`oU z<~-czcDvq)fk)AriZ|MAd*%23k6V65`Q~RXMQm+SMq}g`X2X+Ep;zQh<=E%_$~n$z zd391-jlED#u{XN{L_WIi{>aOz*cb zmm>qNg%wGwKU73;3BTXZM!(Wytkta*F6xu$u+{-9baoJ|O-e@za{$35Xv`W?EY_SS zk^=Mo+0EyS4%ym-5@O;ZGs-=)o2Y4|KW-G|6_%e(Ntuok68P@>gNY&K@Rr}Jfl~VC zr<7Qv7`uL&2Psv%M)4u{Ic*de0E^^IACc}5h0CR^x!;YsUKyeYBx>Td*=W%XRPrj2 zIZ9j#VbD;d8!oEF|0~m{HWOQQPhkj~OTeN(xX&N!t=d-{O!zRCpOLt9HX-=extCM_ zT0_|}?(J&Y=8p!LgOl?R4daCg-yB$iFppf^MI()%5KaJAnrVPO-SwsSkvM#Q*Ee=g zW4sCtDDt1KEujzmbsBMY^osU{cDvfVgI;V8dQH3zIg=dSi71qF;LUFeSBVQl8}cMk z5L{3=|KN<|f592ze-~J-I`SubHV!@9-otX?J(x#@q6?BNb3{T9MUM2GcGJAkBmf3M zxc9qDhpWw_%3kzz-?(*SVZ5*sE<}}ISN8P?I9ozT@Ln2rIRcYibu|$$Ebd%qgnth; z%fs2YQg%MlFdW2KISjCKSHuViVq)(fx@CJQ7$ilCp3Z=rI|6S+k-V1hlaluGj(n-5 z$Gn?Z6|bipv=&O0Wf05^rvG%T#(KX@CVu+@w9zPx<@75hR@Aeb{V9u6JVF{H74M&o zY6}(2gs%^4UJ+Sx$wpBBJ3Bt8_=LUA54=(Fm>F%Q^*o+=0je2o`0nBCA|j zfmS=Ud=7V2s^HW9OJGcHQUBz%sxJs>mY`63RRefvU-pg;?Q=0mr$hwhoScX6zs@Er z#^W=xHx#JD{16sbC6~Fa~qIpl5z@Y6b4z1*TFM*_o{41rs&?%(8U3yx@ z`!OEg4{TI28h9lLNwNtP4^W|p+KxbxNYfTjt&j@QwlzWHU;Y++)4l?C(|!H1$nz>` z*YzXHLm`2|bI|-T4DPUC1XAcP{UzH6D5aA=7+WplL7yFg5@*xpT@qD<#tA&(KFR7i z(FCDSTVEzpu7HqS{h7xu{q;%EIk){sKsM5nq>3Rv zqpXs9D|Vs6O7@FQn~quQYP%wCUE7vi{ ziZBw1cq&t!`TH_Q`Rya1-D=Il5E-yv-0R;KfOzKcf0wA_CmW%im27JSh9lInN&xQ+ zV1&mGQ-y}zA0g92G5-#eUNf&7(P5gdslI@+SimPw|CYI!(qKuzjt`YL$WX1Q`>!U+|LQXzzW6ZM}wg zlrdsqbW>$&O5CKNcl$AFQUfPx${KIx#mE8qT9XwSkAEWGk|zxapkr>SQ6zq)3ov-t zqWhVvfxX0Yx;@_6x56bL7vvSBlVNaH=lSXm$9c*>nu5ozMis5)u(Gyz7f?lq*-)&# z_sZPyH^qDlQ&lof>J3$pFE*EMP5PIn!_fN!cN@9D(y+OA z^9FWxoKKQKK3Q-B7VNbBRz>@aQPDrd8mG%2$EA))X74>86?)(AL$mo%D^f^DrZ-@a z9y=>qstXYEy{LYW+T)$xaPCqv*{~WmG)|=GiiFBNnzl0(OR=yP7x(2GnkzyVg{2)= z!Dra$s&$TpfMJ2m1me(4=-2GV%e_-WU;?MPsfp#tV?XM)L(m8gP_Uvepti2Z!DtmG zRQe-q;QK(2;PJIbZu#LOR?0VE(<`<%MnRe{C6kp_jcY+nF&KC`Un&M;uA$;W#heZ_ z?AnJ^H)vvpzvb3EfW;hidMxb+a?fCftz^eVP#<)qaDxENV2!MDiY2(0O(pLjEo#wuA`>5PaJA6L`zbyOPLj@Fo z(aIZgGg@fn85)DHg3spZKx9v|AUu|*%x5}V#X+IYcMM{&v=$@5Sf8Q7N_ulcOvwGY z*mefEe_^Cl^>^c2WxdE!hgA6)7KCI8SDa1!NsNyW8dOc&FM6gPfXFcEKebYBC-O_= zM*VjaxDA{?rZxi<>@=l16^4t)$JNpJ^!21#QJ&EqL;tbNbs?)^;KKEb=ILL!WCe-$ z&&59F;}8IrOBg|D$gk_EW%<1k_SY5tRg}NZYFKLUfi#KjgmoD&Ey?f=B;=3_Ja@F5 zqhnyOS;4;tC7%5Q#u164R#A>)283&Txs(_R<_UGyO7pxb+Q`xq2_FWc1Ohsw)Zg@P zV77PFoqj9D!IcX-{Bj^m2&BU z4B8~5ZQ-3$%XtR&C$y~K{D-6SV)}l@QxWcKf=<{~@r}3JsWKp-x$0Rrq?`9*%|e0g zW$gz zwyy_-;ulU5PT26e&(1=^89wFHZHkA8Tm8GkNc=}NK6ZWEQpt+gc>1IDynZH7_F5L$ ztp{As>6_9LGXed0zYwyuHAj`e+KRuqwBL6OQqeg6gr9(E!aMy#;(=F`9gigUjsHYe|EIM5^RfT; z()RDYlL!h}TB7GS32FEXeM_-UQI?pzDfrMW3%w_!kO99QabP8EUCv8E+X#wL4kHnZ zmhr_2vRVbXEV-tnQ0#n}#&q2{Ct}ZtpW8A^F9A@L^6jL9B}vE-fZ%@=`}xVAGVye{ zZ8`e~a2OsQ8jXCD6YYVy@{D#H5s`bT-C4+2cH=)ykGED*Mtf$i!#2yPOk?zrUCJXH?S4T4<8H;ERbLHH5(AH`6eNm52J<)6X4#UHM#bSG`hz>4}W}*}* z+;Q*USP;Y@VNtQGweos}sCHiPb%f6)X9rV2YAVF$Njp$8kOJKiisgiLJw@Qhrc#}* zoG1{=yux(Kt7(MG4La3$Uddx?%FHAGp#mkfb(t%cSi0B)IowDXitay?n!fhq6JAJ- z=}_d3!%xf-6Kl;#LAND>en)H_MG`-lz%^8v3pSLZJb43pF6@U^=@WV5)P}{^L&|W7 z6^~asnfKI?<@OQXS+`3+;mI>A1!~`PY6cW12?;iwY(lH4n>gGh{g#7XoIP z)U^nx&XBp_Q~%LLLe@;&A$9WXJ}OtQ6satsKr;Cvh0_T-c!dKO({3-#xY=^A{eTi- zfVaS3#xGdlDip7E_f~7+-H$yXq0JES_BfW-O-0Esrn4~*R`Fk*^W+Fl2&_cZq{<5d zM$&SSY-{jjLBFq$}I2&qtX zCHnY03>W=61N^QSH|b_78U-^^^KD?iyUrvb17R59`Em3>-sHIZu>&kK)GvuJ+_9=w zRCWAM%b%g{l*e;DzjP>C|9%k^TQa#U;^o`Pw3r)RHT5XT;J3tu#L3nB*}j=4QT6+6 z)T@z-Bg?)hZNH$eK9qY9Mr`EqFf^V+&IfyMv2DUlPWk5woQVJiTW3$EP#vgu?v5rU z0wFXR46dkDn7qG7vp_r5>)ZRKRrY!?wwIz8(yUz#6TJ!ev-} z;psdY7F>pxiit-#`)uLte?z;bP*R3WR7Ua%_`CaL&FPFF=)XQ{ZUA4Mmn7uvwPAY8 zhr*7q7pL?b0-XNfDoQlj)a|a)irdp;F!J1jNn;HDX~M*?%azG7)#T;;UZbo26O8*m z&MUhb7vy05Q}%9KJ(wwXGemu4bJ_b+JYRFtd-|qaa42KEUHF%M{N7&e8EXH*#=n~c zTYSz!f>o9Tt72WnOmzQeE!9L8q>U)DB|J9OZyq+uZ_r*wk6E6m1a@Lk#U+k1eT;ZS z(PPSxLT(34u(R6LD8*xyo#wv_7uB2&JHbU-WGZ+jA9yJr(~NB`gSi3|Hha2O@0!i^ z!G{dSh|*X_r&9}#MO6<>VUr8FW*$~}_&(idH;TmPjJ#u*&xegvsjd1+L)-WS$qt)c z>k@la`ifZ3XIjNfVJ6>FTJ5JROC>;k8orP{z~bY`&9NI}Zsbz1_(I4h<~+-&uArxT z?Cn#fmjJ#qT6;u^j)AtMkuLpi+m?OqJ9m4o&>u(* z5{`W1j=|;pln;ABYN>Bt_$|(XHcLL<{A3U@x(sG0j;Xh~HG)?LV0qJdmO73mMl>=Y zkJ1u@t5gtY?wpi)^(OqBC}`zq|7-TdrAG3uAWWGbYWS3`8trj`dA0XL>-*X~10`O& zkeGsuH1rea7#kS(7%BLeK{ItPvb$kK&d+5(`|briyP}CZ_)e#x8h-qOOz-!P#L0#^ zxT7@p+2^t+V#3U;V$#E7Iic^O$Y#%b?1M477y6Rrnu9%02~H5?j%MIueTgn?!RHp} zJ4yD$1rDS?Chasn_L^+HcG2GaXlTweQ$305ocVK3vb9>Xm4(IbTF6;6l=M)3_YtpT%bGT6 z9uDE}k%P1y=*#I}cz$1+HwUlSBBj z9UbF;O66zbED_TYpK8Ujc=FN9T_jeRZ$<+!C{Ke*&Egl7ZIkh3yfT*d!hAN2win3e zKd(uHEukF!I2P&C@%;w2hruP2k@Go6I<9x_$w7ZZEy9D0f(-zPZz>EbyrG1&>dB0I z-x>JI?;cod1mq}vZnS<9)u{W7Q<7KOyxYFNxZX5F$7C4Gevy}$y~KJ3Cptd-wgf$n z*1W<-fn;o|g`pSb&TX9#9O++pK>(B_YTxW*xhfz*Z$O zeFcCxrr_iTeT_ebnFA_VIyn+FkC#X(^bPs#z` z=%0~vVN!DdOb2XS;?rIM`+ge@hc+Y{cbL4H4h-h+d`vifNLA?tmyei!**oa?q3@{% zw$+h$ahOq)y{eGB#s6vH;zi+2A~p}iZXGZ2-bJ9Wi_ZV=rR{&h*}qELzXeuVg#4vu z6M~vzQJeV4(YhR*e0Y_C0A!I9k~5l4Se@AjiySd=tBwfC)uSBp!L+C0nsj%y!DbBs z^U&X(b}dZ`(T>Gr5$n>#)7Qk!J%r!Udd=C_;$e_wkb6x-aFRtp#fyKHTTBtPwhE#K zdJrZ-#%b1MM50{=trILTP-iG$i6Q@-P1u#`GDYBAgY}~;$73`18&PpeQRh^P9T6;* zfA0i|PQ(A~RcrnDN-1r#aPa$d1*;!n);7o8NY?ht%_MZ6tqok=<5i5|eE)yebm7g^8S375`c&8B-2=Hp~sPY%}eM ztEnK5PLJjFt1SqLBJL4!sm`5}8kan+QE9xcuk0uHSzxPceADl(ttKa-x$TRVsmRJB z9{3en#$(0w3$_NUhKv?oq(4(=y0LNQZ^V;+R6gb1{Ek$0Jc+us<$ERwK!vVE(*Z-^ zs&cT8`a%I$vy~tBG3cIdDexGsdO1#&%s&`oZZ{|(c7f>T>{A9YEcbQ|t`N6|05r)xzesVsG!*1c-TOhhg;P^*S)uP5<;0Kd=iGynhq literal 0 HcmV?d00001