From 1955bef8c89e9abea4c0264882ad4686e4778359 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Sun, 30 Aug 2026 21:44:07 +0900 Subject: [PATCH 1/3] feat: apply DevsLab OSS identity to devslab-examples --- .github/assets/collection-lockup.svg | 1 + .github/assets/collection-mark.svg | 1 + .github/assets/readme-header.png | Bin 0 -> 13602 bytes .github/assets/social-preview.png | Bin 0 -> 19577 bytes README.ko.md | 6 +++ README.md | 6 +++ scripts/check-brand-assets.mjs | 59 +++++++++++++++++++++++++++ 7 files changed, 73 insertions(+) create mode 100644 .github/assets/collection-lockup.svg create mode 100644 .github/assets/collection-mark.svg create mode 100644 .github/assets/readme-header.png create mode 100644 .github/assets/social-preview.png create mode 100644 scripts/check-brand-assets.mjs diff --git a/.github/assets/collection-lockup.svg b/.github/assets/collection-lockup.svg new file mode 100644 index 0000000..c71a3a0 --- /dev/null +++ b/.github/assets/collection-lockup.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.github/assets/collection-mark.svg b/.github/assets/collection-mark.svg new file mode 100644 index 0000000..770ff15 --- /dev/null +++ b/.github/assets/collection-mark.svg @@ -0,0 +1 @@ +devslab-examples \ No newline at end of file diff --git a/.github/assets/readme-header.png b/.github/assets/readme-header.png new file mode 100644 index 0000000000000000000000000000000000000000..aaacb64e05d26868e1009cf0f3ea6ab77d42b5ab GIT binary patch literal 13602 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

(9XRPnsHiRYe6r9+(3aj&n@DmXEkQP z6{Y8JvxZ`Ba@J-nm`q5d2U}z|c(cHnZ42_Vx`i8HNU5iTyqW@NPU{MQNxhe|90(h8;wKLP#dXs%*3W)+j&MpO9pvE6xU^?eAfsq+men{h zq}>hn)Us<-@~Dr?po1r@ZeG}2t0?u1Cw?)s<%|K%QLQ_P_JVzq*?Esv(b5DcGbw?s z8BTun-xFO$Uc+COeJ&H!{+7SO1@m4%^HX+oAb>u=!s*V}5=BzehsQGmPFRQ0x(EgQ zniMM4GmcOIcF-gE-S#B=yv#7WwN0>jBZ7eN1O>x>Uj)o^gj&YJ7=)q|#r6q?z9W_2 zNt1CA`Lov2O60|f`xbV>a;B&6_T1msm?yz~Y3*%aLk)LF*TYd;UObnyZC^uP{mg4r z;?i~Ma78y`7l3H^v686Uwz9r9=*;weXj(Tefp~hV+Si?}$3AQ28nIF2#T(;+Q;#eN zAUBfS!h+XMjw08`e2hN%5vLOI1sf~6wZ}Z}Ox%?>-j&RB!?4EauFU$Ps4#oiU{BUO z%Aikhe;UN2nFuc-)VZKmz> zX(lo(Zlo~|>xLL{wZDZie2`6$hl!{hL20D4wnfXa?r9L3DjvCkS%3D?E6LTmIwOoe zOYgmG;LzOMV3A^+fz-_EPOxt*fZ9jE0N}u`qN8rLlV++zWkuzmHgB zx?6v#W2r3*Ma5*TIMBjXT0|=(RJ%{SE97-Swg7_tDggg5J!VrBHd6=#z0N63&*$(? zb=pYM^6f$}ZE>%ZU)d)k_}t|w7rr!|QpTpgKAt=O&^X&VBGI^n-!~XYp3Fdv=SbT6 zIO3HcR^jGa@d8C*Xrou)04PO_NAS*@SlS~@WRkP29S0==sgR)tf%nsm&_j&268<1) zG#yv5jsWSI)hU`y&r~<`+|ttbye!vyH9GrEWKzm-+nQ|7%T>bdesK%xO_)>X1O3OU zP`&wVoXMGZdL1rKQgqMg*59)>^XhcADQK1f-@2>1{0L`s*bAacb*{W&X$_g5xs%r9)BCpy)9A1 z;OMDX-FYXy-3lg6Pp^uTzI9%+>&Nb_E^`GUO6VF_^?PL#| zUIpO;g9!z_4;Z3Kvue_VCvW}2H!X%NGJ#9jfpG`SM!{{@I!}?jpq8nl!a|Z^GiUUO zY4lQl$4-Gpeq-!N!L}w{=ACRCUoT_7Q>4HY^RzkWAO5vp2G`DYM%iht-mdD@(5-y` z!6rqP-`G$yT^-OI@|YC5=4E`#C_CyLI4ZEx-zDLk3@0)F<@y@XtCol+12}M{mAj%S zrzF|I^L0rtpO;Cc*<2d*jNe}=cpRC9ePOCqKb1HdVyZh|v%^DSMW|dd@IzV7h!QCX z)aX%!hWX9w(LPt`^48l&=#eUHRG0RlV3!EKS}!?h$Hl#Xs-&ujCw9?z$^`}T*h66$O?Z=R z(04b+X)w_Mc*AbQo`DZ)OVYomVtdfbz58Ao9v2C~9{1a!G3t*;DhA=RZBSEA4ET3J zpkjxM0!JuT_ECF%7<8X7Kmylt#dL7)SEYB>C1{iUM0I9rmh3V(9ySVcpJp3NydNdB z5u%cyABBFqS^oK;B>a@zgt135tQrhC`FfNpQ6N`E^~h91U_)+HB27jDL zA(J+^2-i12z&FUmRFb=W?gzPvwwGhx7e}%I<*Q%{qW)UR=1GzdIIID@U%HHWY z);t|sk#Z@0;xvSuAL99l;zV*Attsg2JASm)%`>Lz5J((Vs}~F`hE{8CYx3Ui^zBY( z@Jy@&^)1egBY%0W6^@d4;htZHtAIO$N z_gkmf@AP6-eg{|DM75}Y4&UN9T3UQ{Oo_OjDm0>QU~*%yFuz09^m)<)1*nI0v;)G# z)ql@BFKd+KIhGCW0T}dm(=5~U+DrHmrK@D<*_?`j_Kp%YCr5CJ8PT_iyTq$Q^E18|OE{&uLVc7MPJd__Cr5q0+)`ayLvP zQ?ivut?Lq7_OpV21v*0eGc*|cCGs$ZJc4USA=bg+>(hYjw!u9Se#I%GJr z?21fE&k6F#9$jSTK7;i}l^6|?$1K{gCi{5W!cQn{!zYL%0$@J>SU z-7I9MgdHEzw5}5)4-^)B5qjiRXvG@TqtrRiE4E%ap1Aal<^rdzX)+FY>#VGdb~_iG zp_y#I)}q-b^EUHnM%i!K$+X`~F4i80cw|N>>|Rm`{GmZZPSkOhF!Heiz@693U=%43z!G-^)Ip@sY(Rd?Q8gAu8wIVko)A!m#GGyg}Z$hWX;qb zgsnjLS)WLTfBHek)s3Cc^v(3DXg#WpBs4kduX}i@8wHCG^?{%3rW9sKhc)qL9 zuJSo14w{uevOOb*#P8{a;45z!WWf*>dz8G9ZT@dfM!3aKv1HtEd&>ynMKO0 zi5Y}1KBu`11e_3oy^{QWU+1!s^Q@=Fg2^TPkr1BYPPdzjy&7KSY8vge1zaIkoZ`9ejcxq5^V(6ydNy!Ak;!8zkv1decdlA%pG4;Th zj|N=>HTIqwBd-q=zD$$yq47K&vO#E3D7*AH)fI6cw)_%1zI7}Lk`11j(rJQi*Aix8 zaH)%nCAcdV7;Nf#3e(QoE$oe0#K`s*pTlyPZ;G;7u49v?wVW*1D0M z23ur@JDc5^cte`mtKo_x8_X}nJ9Te7eY#9}9CxQd4Atz%1kC~_=w+J9{l3mwyxgxy za$)&+vUT#KB0br<&4)Kcwly6$0Jr)vI?Tg2camn*2M+>#f12^27l;0U^wK`u zK4$nDKDs)FxKI!4z%{}`nWln zh0SwG-;_3M!Ccvwm4Ti!;t0okg@Ko|+|0t~(WGzPM5h?tMvEH*I_6zxzE_mk#1q%! zVa*xLqDw_}^Y?E0xTrA?yTfkc85q(`FzsQYXN!Hj{z?LDz=?5c*T8tFv--;O$?0g9 zqUW$N(L?sa{^*9(+uaWjx-8AMw-LU%2s76_T5W~`TRU)an8lp!ql1)eFnI|3{MbU@ z-fXkY!uYsd?t=ForP@ZZh{36h$*|#0y#hsDFq^;jEW^QUbGdzLq`iAm&TdkPn3KBR z48ORU-hGQFoV6OH?%hh!ZPfj~Evux(uWGWP3_ru^3d}ID14)0+>T#@uu4g9tl=TO3 z$iynL1ShA@c^t~#%(P(NNdgBSoPmfJWjiIE%{XHPg!@@@s8iOz3rodNXr(H8>X4Bc zvzp=-$$%jBDF)9WMsIZ}^hnzOMRHZj!l#=FwCTf{}+_~X1rKhvt4KSmsf*ufOUpJ&rlWmP^mP!ibBIanMTE`?CPt*u<+y^S0#k;B=61UCdhWeLzw!N^ya&+iq zA8saXos^Zxl_CslkfNNWZz32XfcYl9+li{3D@ItWF2YE|w?Cwwe&y1b1@Fpg-<*`U zk0-7@g>?wx_bLJ>$-@F`x0JlH3g+|WFstoT!`Gh~uMP*^RHZ2~XHRD@;{5 zV%>^zU#qbqB$b|K49AFR`kuS7GHW86?KBhSWE)+VX+=t$EF@l04`FZ6FtkZ(VT&Zn z`|{G0&(zH%A=qAdAbp|fvQAe*m0+u6;Nrb*Cy_vGSVwRowrHtf{)t_7g0dRKy<4DZ z?TAaS*qf+p*cL^RJ4H`EmEq?!4IIwq+FVKOxm64CP|QzX-t6wywQGKVGsJQK;#{C1 zRKcW&6H92%s&LnCwi8Hbn6t6ldJ~Gj=6>}JHy6b}L1bJ^< zBV$*m{=%Kh9z%lZP9pb&sY@xL{g0gcZB+Ma+9IbUpd_aRgd`Z;hj3yN=758JL)Eq> zyW`s4ErNPXy?%eBg(SN_RY5O=`X0R_Qu5rjrTS@$i(M~g7HM{*YsFpzmT(}%9?)O- zqOx6^oeXQ^q_!o~8lA7+$h5{bcO0NVmlXFpd%B|zcP)>_24+ybZs&t(yyWzNrDL*{ zW;vcMvLX?4>Ok5#UwydQAt@AYhsw`RrYMJvEDQOuaDFD132hZZd^vojBFEJg$t{@y zcK9%5Ycs}^^5}aXz-(?2?0u9Y6yG8h>;xqX=_erHmKPSll<(R>8=S2D@)|hYSoL+Q z3@v4#qmr^lie@d3ol)I)S|k$sq1^jR{#6agXU@Y6GD$3surDQoH`*zVw~M}tbr=gO zxVkeEY#nml&g*KZT@k%4octnqQ^i#qE^QvhDIm16K3TV`eTCMXRSDYe!65v$kZM@s zMpkaUxg!#KTz7sWr6kFR#s)caFcO_SCO5uz9U7HtcDnqu8+JLBN~tb{4B^z;4-Edk zM1G)X?e1)Ln$Duhjk9gg9ACcta&I@qWbrFNGQF}d`&xL9*8GQx(Jup0D2n@FaY3Gt ze3Y?>OK}drh0-R58+N&b5bpRUz!Gp6WT6*4=X}d1&6BMMKP&E4?ZW4#F26f80w0c{ zgq8?>Akmy|>}|4}vBfR45v1LXD!ggqSEM(Lv72eD?+z7QMM31>dbv?I!GT-!C~qhG4(`AtwY)5W*$w5ME-Ne0tfV zU>gyixpoPQgBPnOnG@GeCPZMZ+QCyX5D&iY*(4a@ibX_G)MKdr9?ezd2*z#e#-)9l7l|SF{DFS5HrjL7tArvo|^l1F};)Cn$uW{JF zP=(v6s^NH9x&N8!c#1bAP_IZ?iF#AR@iSYG?$dWUvt11WqaEPV4d&k4$tO+Z%NQobmjTP36Z(p4e=0Jo((*lZsfNR7Aon z)o(CL5&k1sv;4+}OMS@Qs}XG|gd94WO3P;+ z$$(Xy>UVW!K&sik>c_4mSM}6cy&9@dGTm;zzrN>(6P$mgA$FQ@eIyX>A~fJ77KD^+ z_8xKow>b7~th46u(d3xw<848sYw+b$mF@-C&P>L`8r-o~3;>;OY!dsYZ=0Kb!=}H?T~2!r2&Wxc=?x*?{6sYZx8JM{*?xZ%vKxzqq@J0!DqN7{r}7|KO6Mls{7fce`Dys^=}ujmw&6VKPw}rI(a)g X&&f%zcZ-E>RbRPiexcaN)3sNj&p(`Pw7r}s(P^A+<5phI8rPruP0!bhg z=~WcbM2M6SdIW?>0z^s(5CZQ5I(s|snf>nXJLPx&;7Q2c*IM@~*ILORS4}Sq3Wy7E zad8P6T>0Ici;K5}i;E{{Cm-;M+1YSA;Dz7!igf@N*RK7Wf81P|*$26}4s#j&e$FCf zV0xH^kaP>x{LE6if4k4Bq2{n?jV18-a%ZTnRFU;y2sRYXXgK7ujc@l|@v!si!_P&# zFBt4h-zRiUsEw@Fdq&EZBYw12bDX6{u2Yibx_1E^;7Tu8 zN1WSyb2fVW<_ph>%_pzb=JRkf&*lra$ma8V!#*c&;Qe13b^@p3;`-hY&9yb@_YvP4 zzR&xk;rqNlH~h_lXQQF}{@bVg>2QB@hMy1jH)q)AB*weBqo2w07kd3?rradtza-0F z8orbJFAMTo@%_Y`zhS|Dr1Za^a`T9PVeQXk`Afs!EckDD{k| zkzI*?#mZ#)?=2#jYta?G?&Je>uS$-%YDZ14UHO(8;kCjxbJ0_bl;j;Mh@CD^@6AAo zm_k)@{hYUbK(VgNsb0U24O=%an#(9!W~Uzo*vHxE*u%Um!L7~iJU^uAvZ-x(PMq60 zdkA>5vt-}h&yB#5ff4(D{XPQN`d>!;7<^~nkR~upl(ZcNUi6&fwFU}d? zBQ~4$$+()jr-&>Pq z@`K#x-WwJ-dVnFE_LX2}LHs!j za$#;Q!CJM?%ufADnD@MbC1^fITz1)jMu_O9x!B}R1s|^!G=|lUQ*>x>ILE%v7|r;fpE*TWi2r3MpA`la6TGEhc9!s3d3r=wWUBo%_q`of`x{=@{U9PB zdcAkOf7-tguN6*v4C`D3!bu_c7;O$$v1fc5|G}a0@J6~R!oJ?)gI`kdKWD)j2swcf zw&8;pTWtv5xoUzV+c%9Ozy&9hGA;#`?WaK8$1eP|t)x@U;-~Vg!aLVnZHgwRF#$if zX*7#}qnT97JNONpRu6Fu^DCWQ&H9J9s5DaRv^9S^5kM^vWSoz&rGNoKmGnO2@(lF) z=}>~ZN|0FBpEgzWgu8cZhA+B*p7@ZQAFv(K5|8Z?<9a+JiF?Y~YgOc=u;c zbJBxARQrMNJle(GyPV4i(Flo}ctWs&m%45#B;Y!6d1e!`OQrX$Je7uw zen=g!mBM61Ti$E7LYl+zAJ-XB3n7iFlc^l+B}wO;@IPgHX9;9zB_TAnCE$lNpO1zL zJm>P1sAs?#N|EzPh}6jCH`3xs$|18g!Fv;+#)nYCT9-W;4J_Ch1VO2(1@3HK%Nk)1 zCy+i>tg)4f$Pm*B0IiPe8wko?tzQa!2n+{#8I*ayZa5V)us;P z+O@e`o)f%QHFDw%3eCBLTLSY5Q< zKd%R?9=+7M&X%^22p-~}fNOt_j(M=MplPkxga~c)Ph5b1&>ab9z~h;{gT6zFNp8NZ zVb+NRt;y=H-0)daHz!9o+@UmNZe+qO;q+L6bDF(KivA(S0)g?z!wb1h4=;51ynEP> zInj&{)}wyArzv5Z+dUd5tyO%z?_)-JyzY|K&;aAw#CpJt*kETw6E{F=9;`uVqo+^Z z%{B1~X@5gy=sP{3oBga*Lztt(X zC5cmlqE0f&?;AD%$)y}=3!BPMdw|LfBit=;Hc0oIt)YrKuFqR)=XyrWcseREpt##E z?`qbwhXTpE@SJWAOaIf-id$}|rx{`}lu8yc(_5Jwt7_uF+UUy!$90>C(Or(mFBCZ- zawdb_=tVo$6JvFblI;TQ=YZC-aSr_R?H#taKd&8O58li(Auu|PlsM1FS)(&PQI&lj z7+AP=)lB8!^O>PNddr_)QzTJ{$V+ofT+=6Jcp8Pfj6nly)b|B?Lg_;ue@W$#f7aLnV&lA7+XF`q%@Ugb+Lt61T3B3ZcC{1`UlyFB zwc#Nh`in`cDGC-m;7%U1rK|=KsfaWJaJAuY@sB%stwJ-*-m|Fs8#UC06})BR04UZL z#iek>j3?V6At|7Cx{P4iubSgg;uFO_qnk+B8~H6J_k5yGQ_}{AF_>FrF|_D!TKK+j zhr3wb#rPoUQUMyEM3lCn9dI&77T+CGIye{MKf)wJKbp5MtSt}|LmnbH({8OxgN)PF zxZ$9|JkRKX-Oc8lUIGUbHTxyWOz}dwLzJ1!t9I2a#`^~XB`OepLRf=R)Y`aoJe1He zwIXH;9;R@H*0?)o~bNq?-T%r!b?$_KIh_E;Qm?=YGi2{1oR_$j|}COMUO z3u9^=+k53mfNHZg9Sd!B(_83oY&!z>1XK#1Ma`nDO%}|yD^?BlcHeqC$++tvQ*HkX z$ng73<_~JbnB@g=_x8!~t}M6dcg|g0FYIDFH9#0A>`{F&e9usZs0HBjm89erkekbl}Q_<(~ei zme;)TS{>KpB(E*uYnI21Y^a>VBZGhtNz)XXJ@|t8ugw1%IpDsHc!lSkaU9`5@nosL znPsnPj?+trBj90_1u{EoJr*i#(V+?2McG*2yE~&L)2%ksP%}HYemn<;V1J6!)0tjs zH_Xv7Ittz~9I;R&iOQ2XU@T)6n7F{1Ixzc1g3Vtd74~)X_0;{n`fE4EewDdmz;u$w z`bTA5hump5mcyb2Rbx*Lx@N+1N2kXDO)o9v+Q4dvB3ni?%{CUzU{R5Ct)lUCT+{Ql z#)(MRFfZik;7r#vMsofb#-n`{QHx4=GHJ!mSUD`L(-I>#ICF+Y}? ztDh78ZMQU_|Ij`gJ{wCu(3|RZ`#K|(jiBdHQl|Von?6A$ENk*z!^78;Sw8(D&TI6h zbXT9r0s&wnk9pC+W% zQ)MKL8G19igqR8~)WJrbBv}Peiz!T1{$J=I!TsWjcGX5iZb;*s(Bed$`LD$Y_s9)Z z*eTAOf3bT4WhTHUtULcgg8IXhsR*a&Drzx64A*@4A_I|D43&k?v z>|ve{78IG!*eKcOkScUTMAiC)<2v-p)jrt9N~r3m4wjnBSBl>jG1aGdu%yy!bzGj) zG-2?Q=9@2(Q`f*Ai{ef)#%N2YmxeFWCMOFAR_79(yY{tDYv*f6t`te*z{_D}rTwo09? zUzinM*EzPYNLuy!jm+CD^NN(S73=+KlTuRw*6ui|gHR8<@-T&NdS=6OmF<2n;chfr zj_Q;V$!OcUD+wY$A0}85wlZ=>m42$DTV4>sU?z>ec+s}qq z`+YsuZ0Ku^(~(@xxpk?NaEHhG)r+*Lib6FQAFE+;K8#?S>&+f=Nz|SFlO8}2N~+nU zhZk@&nHM0+bmm5>*?ZD7RNXE*MYPaW{Tz;(kv6pI*3H#uJ8=;n210v;^+!RTV;D`g zC8q`HEc(E20wW)H~vZn8A?B%I@&cCkgdEnyAA|wLD`t z;a`qQ!_G`jTkSD_j^GuGRsb#U8wV=<^(f^otO)2`46yK4V8m#zlQ60yPZ%l=#5#kunrW+O*}KZ+_*|5u^cz-M6$jyPH%afnFqLgF$t$JR`eb}kDigt zuyncmvxBswN6Z5RZ1fR6jn#Waok$qJGJAc@4Vm2qmTfm`9p%flVT0Ij2fZKIz0^iY zg*7aXCDwXprHInmjbC4AGKZ3;GChku8b`a=gWkP>rB7g9CVK7d!9_B=7Xp zlgU3tM2-jRS1NF5({>{JAJ?b3n*{P74dX8f8uFuFTA-OR;l+UIqp&On_mRs zT0R&?zuA6{={jC`NxXReSEa}8$N~Owb3yed+pvJ`MS6YCeMh$rR1$nkZ!>7_Gm1{Z zCq*XakoM8p?|aH!^9?es6EDdVX>2?q>-vo*V*fF{xet}_6ZT;(1*lpL%VB!OvB8@! z84z?*nf#6O&nMBr!!3!f(SdISH51TJ(YBOoBoM4nmVof8-!nRehL3EMA;RI(*7?4` zK_eX~CwK8JvLC94PX%U~XHG>iXH;agSs@qA-faIKr~yXgmn|mU%u_7S0j_tlZIi4IN3*W1jpCASa0{61?i#|GicDky4hM3tS*44+k|@j zb+(Q%v+Qi01{Ue2%PM6QM%z5$h9L9vALLnElQ6*2*oZ#H!jn@t>(a6pfQ-1z&3%I5 z`;)JWXcm|q!Xo#~R*##ytTxJ?Heg;=x=y+wSR%A%>5O;AK)+p40Kg*T$wZcFDstlu znEB|yd6bW>yup4OH|F^b?`@kw7tlkzLaCwJDwtz3q{$6w%RQ%{+AQuZQ>7+@(}WW% z0@3#kNfvNs1$LyD9!TB3UPB$})`%_3GL|72-U`i-B64}=J<^cuq8DT>elj2y8&lTi z)0$MS^I$i<=@43B)k8cG`c<@S!y12}Q^QmzO1dMvnxoVxxb3?t52_TEX6dhu?u8zF zDy+Z1$GDQqQ%yKJ%cl`MsOJze8p|*0^r(kRpLq5iOS5Uk#EeZ)UmCQM`mRWIxO;hF zJtv!ObW!_ce>cc)wt7h=`chL!?X)(pdy?$wh1JR?vu3qSy4lwhQlt4qiu`?7N?vW6 z!|DYx_|vLjNtwX--l*j_32iG(TolN(%CP*ah3_;FQCGrc)~u~TJG0ma+to@OhNCvN z!P;t9CT-i+vYFkd&s;aqJzZop&hQ@BvGeVm23h(@^i^p945@@~y0Qw>xhQ;bBo@E{ zI0`d$$s{(d&Ggx0w{KG@ggZMF?G`pOY_a#ZbwEs)NR$G-ir_L{D}DmC!@dc}ek%8@ z0bAAw=sIDpjYX7xg-N?)`yD}r^CG@C?@cOlA}FSvwh zO^Y~9T-56xpqa#}>UtO=+S92-25!R!X-Og~asHTJu((2sS_iO&N7XosC;1c$i2TR- zB@Wg!7>J2j2b=+?FyF@O6$qm;p)5FgdB4-eFfL{owtm_TZOd86sI7UCOU1rReTHhZ z8N^z1wtpM+Du-KNBop0)PLNRJNOLsW)fmJI!&Tfh2bAR3`)r+=SE>W=6u|)vxkT42 zSf|FGB$?(1M-z2wap9X%gqm5kXdQ-oUf3HlyB-AJ1&G} zo?6?{=;~r+t{B^(%2CLSyY3Cc9BKFWj zkrc4-zTegi52lJXKhaAYF=NWvfe{#2@W-*%qn4E~(=6CM&y|GpVAGt%ZXZcdbm01m zXeBHS{7blQngXsqtOv929l3FdnQM7jhqI`9Z*kJ5lO#Xo*E6ESxpS>yjav6@EM8w; zls8?a1s&7waKkOz4>G7>lqzvmuZl$305~k6{Lm!d29D4i3F$6~nfP$PDZi|U^`QRf zpk9{j*I^`iX}^>GtjXnB0sYck>^gH`nWO16A}y)JU9cHdIPoHR|6CSaR>= z-IZx~I9nL~l_^eFn1u?MLuN6W1Dj!7_<)(bN2ac8SkWcJ<5xF^fM8w|$%W~+M3X0J zn^sYSQQ-GHa*30%A`V6im8;AoRRnRHez>#2Wcjth#lDqq~Fl z>Mr^Y21x^e7Mn&T8KpQ)H?SHff<4Rlto-Ibpb|o|jmu5AtT%?WOL|EV) z0Fnn$dd~t#)8u2(x5i0?2Oh&b`I}~LJbvPef|#qkF`Nwq{K0Qb`3(y`?cOQ$D^n;U z5R-bvWu_BSdE)_8RiZy?Bjz5*7`aUy)d>cPUCzu*-dB+3BnJ=i&mQeK+AW(!y=HLq zYa|br_X*iP7*39cv1fuMR00*?%^D*iuMPR3S?79`0PnAuY_(lz6Vw>gOG2jsPLgY% zOAY{t!Vm^Z72Y5hQ0T%iBN7K=mv`9o2JA~?7*;WE$lRs@fYM+B>jdYCEZ{|jC&{J0 zG1+Fak1|b3b$>ssmsC=QC@i)<{A{I;0icF7o6!`8;eu+ZSHG4nX9OQSJ5RvH+m%@Z z*@mrJfP+D|?=9}|TvCsiE^XudP>8bL`^skPSD@-(;-i7RI_?#lwo4o#y6)<0zI{)| zQd-)gvAVJ9yeF=qaeBjoQY>Zh@PLy{bAcJ6@CLKeNT~svJ)h(&uSA-1B-zO*cU55! zbV5&-sjwW$=(-X~iz#0ghRc(`%6@-Y&&DodPG({SC2gMhN7LLRx3t^#Vx3>(>17Lb4`>Me9}!IT3zThZeP7<;#2B_FVyJZ-0tVJ(%ROLUgrK_07(D zkP|cz*vEwtGlVZ>QRIm;fZuT(@k9+pl#k)xmFbd$E=^$?Gk{Moh*PN!$4(f$f1G=< z(8AEMoIP6?_1>cIc3~Vfu`fySxw%sN3jMiRgCrZqg#}_h4oiHm{q`+b~!Ge?{BUbyH#uo_-5hRl#7;je&A5S&~=qP8B#-2 zD@Mzz`H7Lyo7aLMEWHpxJ)@TYMj+a8wJR-7;uz(RhYQy(H#}dnPo?mSb+6Uez`|&O zyI#(p89S!GGWD1k0l-SfS%?yqBZJ&KBrLMSXj9Nw$U^WP@J{Z{M5$-R&hrix6u+aE z_Bn8)(vG0I8XwP)XyGicGV7I=Ub!z~9je6o3U!&u%G_;O4{=*m^D|K&IDIf|w#gz* zwAGZ&o_5nGvl@kj)KNl9@}Prpp34zAV#^3vjVm{1cu9*DC{wS>Cm+ zfBGI{JC1DS)#l$rhYwXqho7@9oB(nev?CDl-&pg`$%cuSrQ!8hUPCZMpBtjyiUKiP ze3j^hchj};>t2Nqr(RL+Ud!uulvi0595vSk;a<=xC}iJ910Z;p;@huk4do88Paot& z0@$U<)RA87XIkXPrqtW-d%!)GO5A7lUwg^t)|yPAji9ES0DS=B3v;MBMafu{p{buT z+Ih`~OTUZt(!&O!YD?Qj`b?A|WAKxZId)K(+@Qq8QOD(OfKrYM*8#p4Fzs9N% zomF?=GHp@X*U`~sEe=&@$6Tb@A+{8haSFjA?aKIQWI|w<0V??sp|0T>sy{5O`X#E% zs#t*&r@|@HCSF-sTGSJt(OdX6vR$2=0*axvPi3jJ%b5_n5lFJ1!Q;i^U|mbo8BB*} z8%?C^%l0}gaOu-$ilB4lPhHpt3bdfh>ESu9^r@Vl-I^-x#d@wJ_?1tov}dk)Q(@q4pOh5`4jdIrU2wb;A0qor#0Ft=_um zUqPKhUO`y`S%og{^CQzwQXc;Xy1F1SA%(hZxnKt>gfyi-L7Miy$zP?0&B&x}|CNRy zBsT%-R-X4`a?fOPZ`gL{^ZAis6DDGIHQx%#y67uZgtSguoOI-^w?wGCBa$Zj`}8f- zLcWhIHA-P5QI1lz=3p9y@g|;SN}vXmcbP6zRJXi#p}W;So(~r>hU1JyO9R|9tT)Ls z<-^UEXXrTdOjVTp4Ir6RREE!q%qK$bygZt48m+UjIQC%|407H!jnq3PKSfs?Yeg zyuGcWfc?J#>C(+&g}3l+o0UDAX?UP&V6#Z)`=2IKMvyP!z z+~xHh#RuWDH9l7%OR6Wg@{>nqvy^{SDg3ki!S{Pbgv`!g05bi^_5W0wko6-6{{JkF z=%q>DtsfJl3VSmAKBceCc<1H51)cza?ECQ&5&DyNL)NB1O6o~zE zCx27x^rIN;=b|lOiw`@B>t-E*jPF$I;#4^VKNr+@J#9W@GnZ_L3tyTJod`W)S4}U_ zi&~pQi2!+#kRhVy>SD%3=u8VsFVL5S1~1ZMaZ6g|ywh?v?jQWcPX~5G^TeL{=BZW$dZC*-3v8 zvsbSy+dS15X&bE`NPFnqCK)WZNBir2X9|3;#5_xDsd>(Ik5)&v);4PGNZ6rD$#Akg z>!u|8zVnBtF%L{M`mt0l&jzLgXu5`~H&Jur!NhbwJy5X3e}v@c|A@nP+t#ZzuykWR zE?EWnX1kSG5}c*`Ob7Q6oWSw_rd}lx1JJ2}u=olV4?r3o?0z8iVV@8SD`o>@%>mUPI|4QJyV^`(Ap$-^OxNvT?pD_ z7DGNaWDEu+qG?n&1pJy`yL#2DmOsh~#NTz*&_-4O=Dl~1w2KDCuKeph6i-AX{JV4*w9%>ym*?!HF9k}3&i;0QK;T5OCGGfj6@IPxe20LaabVTLcU-8be~Sp8mAR0R zRzgE&%}T5VoR7V$rL|#UHqn>n5C*Ghve*;2t7QHKBA{_HGt2gRp9U0aS*(1dSu%Kt zB9eNe&d%uk;|TBBAwhfqe6bT1A3huU5_=zAS#;;A#ajz~BVll4&_s%6YKrbkL?%*y zeR=R6uT^3F2cn9sSk~QpKpbwD(3WC3a;kc`3@Vy@9bX?h^W3>u8G*a=F#Ga)_G3Ag ztV`CnW8`F)-J(ttHvTyC(qtz&m+?VJB#Wzitgq3uSQyNWBS8R2DkZm|`ZM zlIVu_li8^*xq-baF|V(}2eYFRtiiEBB}@nOX76q!U+$b+Jh^GT*?$@|TIJOrJE0d$ zf#WK<6rO(~SyJrog@^K~EcMR|0oP2vu#yHyB1|!oRHTcbKH| zkKWr=B62bUrj;O8rIRGq_RzjDu4*KV0aSP&fzCQs5-O-QX8FAr_UeLx3XeDT3YA+{ z1RDf^(jEJ`zQpQtg98S6t@|N2#*nmffbjPUe{yEGD8@aJUyH`w z3ux?$A+vHD*+EydBeQ&_ic*7>z*uVLYb&PMx63(PmaMpSB|4B^#7Ys?Y%e)=`$}bz zd?VVAm|VRYq)CY|6RWI`AexE!>pppW?E1ATE(Iz=0B3BL)m8aoJPg`=3$Pj$+pV^- zCcYjSl>K6qZ>~h7-q~(@QaTY7XS_iOl&SZEcmlb|yovSwAEDW*K^d9WxKs6Co?>iC z-fep_Jkn`(76&yj>`F=`>e9_sr_gpS2hPH_BlIjlRzica$f?iN1}&+*a?OUHQUyvT z)p}`KgjcO=S_;mS#C8UZr}1t!rj{zP=`uz8?J7Z|A5R{@hDE1v6*~OUV}PuK>KC5y zShrujMB0YctRKBQ0al79IGhU%4K-sLfF)DYm|j^|pEWk!HDLN4e{Ol_e*H&sOuQUi z!;g5Y#`p~3K(klfLPz`n4_32b3hjJTznF=KfsXXRnhKxcH2_4(JFeLk1IUiB`$_;h zCUYDVd$78?Gl&Q?ZOK8Uc_1F?nU+CB$+dwly+DL6h)~l&nx;G4?a>x`F zEl=%I{0;j~%Oz>5q$aIjg9ZB>T66tMG|3Kocoh1ssD@&!K6AinLOW{^0tSJrNxMr_ zVgp}#nw41ND#N}!sY=!-9z+VAU{9;H0~OJL(en^MQ8jR29E_O3-Q;wQ?`ZJ^9D9CpyiT~aPar+6fLgxR7s=YK`y>H%e3U7zbf zN=P{<1z`(Z3EK815QtHKpKN(9yR5!5tkUf04DS*C<21qU{hxn(JhfCK|7_}j6V%2^ zyi!vE(sjQPfE}V=ig{T)w`pni9#49FBt^x#9cTUA;*mX|&j)Y2!^nI4&Cuwsg2w(= zx`H!4)uRdiXzQgESsJrRf2nW(AS49a^3=G&_%une8J*driFzY*mTqyI^@iPGu4eaw*1zMe;L3_wom@p}B=Z5<8QszOnZJDHxGcp^zgFE#kkl zu6{`(PRljpldBiUw3a^~i>hlr56(fx6v1@vKtL*WDr)U*;-o zitg~=5IPIZbgGoBMN`h;g=c~7oJWL}BonDpkSJ5L(%X?1J1W81Q0RLJrG`u(Nm`5& zutxS|_8j}2KC$v~y*$xa*yI9>M&+lPpGy%wMju4YD|T~X1X|G25RM)yQ-OL_7f13# zpOu5Ze0QeV@RSvUKaF8EdrmhU+d|+C0P%_LJB?oJ@4T!e8PPFs->&960faSn_UWze9y=K1RRM*CMRwDQLkginK8Ex9lv8rlq{A3AP#(*7=b2lYrfE|RmX8qBF6GSMok_aFUndmwh06bEeuY#L~R7h`-evo$Z5Fynkrj(&UA>M9tx88*0n;L|6E8_|cguru=Zegq5=j zI|quei}-`C#wY8)&8wWiBLbqjmvvm8mg0D@Sibayj)PRa8>`os(hz~3#S{baR%%$l zkc7f`h9_~I4sSvi#V%DE#M<2%UX|SHiKgLQ}GVRf3gGOK@`79x!mjd#GlOgTs zXZ7zCp+JoXVb+?uD&GVP$53Mb_pDs{L>*GHp^iEA7Md(t#P(h?g#?v%3 zF5)D&2{=Fb@kz23c)yI(m3(9I`^OHyUve+7FFH=f-p9MqNjJgyHpqFI<3?Yf%n{)4 ziJ5{V)trUsZF>|jF91t`q_=ZO`L)Mgk`41sqYxfAyYPgplA|sud`s;P*R))hKwveQ z5Mz?{EJ(iCzk@%|+%MJ7SooUya7k@kO|BP${kZgNkYilZxa&t%NWYowH;bF}wwGp>k$2hV*Y$wU+&kDAEB;u1~1cf&}%s5biOvJ`B_*VHX@fGbBruEdzH+jH>+rX; z$_&pYxk)&cvZEcoAsRo$mU51OJg-4;{FE1-W?8;(L`nPG4RTI0RzxGsKSqvmS{Jt^ zI^>geXDZ?e3mKKm(WQ=5`d&qrdSF(L4rmKrfDq6avGVwS#_BC)R#uaOt9^ivGK|QH zcRzDd9w`m?P^NBkJQ=0O`8@uR^xK{i*X$^m^7z|;2Vy9Di;wk-z4MJ!sw%F*Z1YP|y1t3KQA9Kyj;M3S zV{6_Zh_9@RW-1jGTEZIdi3Q+^#>2iz*Dur-X2C$l@OGp^kd9l2A}*jWAY(*3ZM$t% z2lU(qcQ4G+FH9l*5MEtzAEz7-O#pO&~uOFW3)?H-7ikwUb zY^qdnSP)=a=hJPAmfZm20$drl8eQm&lAo;S>>s|6(6C2hr%Hkz^ziYU%y*fre8&Ub zd+lvLhEf({K*Y(2ub9?X_M%mB0t=c7uKKGZ&LdS0;_10dB*(pXm5ZKvoH|=K-{IVw z$B=W)j&M+B5t=0WDCCX&^w4*$NBKm`y(3>@_hCA&FWKrt_;g_BjCpF6*4HK$B4*2> zvDUg*@yw4_tM4i~#M9TUR~F>0K3L#u5F38VaKr_?vciV<5g96Bt8Wx*S7D;HM>_QS zm7U3vuA~N_>efatC_ryH#kr$SpDc2!qjoWMUN4pWNgFXl8KTl6iJTU*c%T;8{r$S@ z@Z1gS{uEBCi{Fs-L}d&}RG&HQyc!wDl60C_Z?B~lJCEpdf_$f`I_wX(uVR03YClc; z;@lC}v^2lmG!ANgrN|5e5Ldv)m!kH1m#s9%bF9<$*b1vN2pe`EYni3Dot_Khley2* z-O7F7_39J8P0p*o91{u9TQ5>Mj+VC-I`P}*Om>EJWy=HAc=zxVa;^@yT*OYd*MC8# z#XN#AifRxm*TiIvDR7F))$~C6e&}fx*r@lgLfgse;&k?--bT}=g0-Quhv$?0wZDAU zpxRmIdh+B>g-cuHx{2vc=xB~iPjD%?d=mIH(1HXh9gosG=rj}j_UzYAf$OdgewVc- ziUr18hnLmQ$t7#oZMyFm}92GoqcE;CqPd}G7?Ij#}nO)jb} z0>Y|>Z-eu-NNHaSv3DOM7j_Vy$zHb!HA|I%%!aS(?Wjt;M%U>kd(nmjSNE38uk^w* zSw)n+k$P-}HXU+*Euscj(-bhWu=2VzowIoPfmRf2=#aTkBJ`3$U;LT1x=c%=11`oLcIJU=bW;eFccg$YHl$|2Z zBwH`6=>bX&;L0_V!Ii*muP>(w)yF&KFDMe{-FbIy+y=53{g7y)5QGw1g z?^LIcR8_kMAk*rY0LAEo3Kjw3*q4 zz7Q!CxT0sRuFUXe=rYMCS{)W=KUj4pGW30S_Jh1=cjuxW&WlUAw!G4S{mbBZyVG{YIf8ht*dmjzx9((dILfIgNn*In8_{^ zndcPXtErnYEbTxGWZR5N^67=A*OB`=POy)Il~w>plGB_QcTc{^0g|C%wa#LXIPe`> z@jBMc(w_$|4ks(~^?8LJaiI9(;#DnDiF%4M*qHx{&y8dXLE7w-oN>~ z01$Ks-W^3?J5D&bX0OiPCeZ>@qJ3r;^bhq}VsH=&h$Ct0+=z_5lBppTTxY#Mu$uMJsmvUUU_g#)YB5z{O>K zr*Zy+kiA$HJDZW;>nZH+3Lb?=1Y6;oV9?_2xeYH&;cDFuraqn@9bii|Ow7VWi+kK#9vytONxjjh*Z{|J}hjHKht}98$s!Wpv$a%yGZXlroOd zEs+Wh4p09i;EU&@_|*E9NQ1@BPFDtp-|V7QrF=r8CBy-^BvUp`04+v!9F&sHxix#% z_i4&bH$}#6(#|OJ=P8B3Of=Gtg2gbT%=1Y`(cnjy%N@y1E==B=&}WNzW%5Ncb~Y&{ z(235cZ|(tu1grAIOvJu8}qclj*^P3Z1irgU2LbOGa6NWZQdpkukdjUah(S-*Hjg%ZHMVJkM4g z&TQ-hqRO6@CEuW?;_mB!0|2PLCm+*Q_nB9qZW z>bj?%o1wkB+~mQh_#g5qz-&x<2;iml(Qs&b3Oto`y2$+&^RU}1VJrTNUAy(3gOT?p z>Z$ePNzjAfSW{?IQSYnTX}5tF4S4w%#$pP@a&@NHHwhbRlH5&qTFwITR*OXdQ^+X= z;iLY5s8(Z4H*#6ym3K$$Q7DEiFIM0wQbv! z+RB9?>Xzmo)6*1=SLP?!Nqb=tcu*ptYxZeFn<0Cm=e7ZXj$fpUlVV zL?;*)bWNv$mmZ@#s?;M3dBoiXMn47=pP1BPj;6IKx6hgB)GZ``T-;9JxW^hd@L9oF zI=U}y+VB)Xv4vh*+*DD@W3O-8l$FHR9$2uH_M;NgbR#W8{iVK@e7!hJrr{&4p%2Z4 z$-4W9WcUYDxm~SS;>>Nw{w9ntR?zf_*Y@-Pea;wRuyVB`P#XwQHr;lo3}h-Td9lG` z>6QndRX6KQ#Aj7oFa3SA-Q6c5Xlv~c02f=_JYV&s4QQ}SvoKquTE~la;FL_yO+Ld5 z9p!fIB}7aH>z#AH+J8{^G+^oh^={Rg$t|+~LRfkVNXV9S1$s2t z1>hRsxT{whdcHBiPY$_1gXzd#E$KsN65s~X7_~S}*?$B+-@};i5z&8F;Io-~{l9Xn z|FeE&m&m{<{o*0yKmXF{Uv>S_>F4==?)o?L{oM6GF8$v<-+%Qemo_PMwt2Ibgp2$8 z?<#FQdAF|UzRhA6uJ6z9o&M7Gd#8WZ^>60;x$ED|$4%I|x!%tN{!YQaobo$?r?)|F zsBiX;klEtww=+&!7dDHNn)m*Tt^X(X^Is#y!_Dpe&DQ_b`To-Nd#8WZ^* + devslab-examples - DevsLab 오픈소스 +

+ +**[DevsLab](https://devslab.kr/) 오픈소스** · [OSS 브랜드 가이드](https://devslab.kr/brand/open-source/) · 컬렉션 O12 + [English](README.md) · **한국어** [devslab-kr](https://github.com/devslab-kr) Spring Boot 스타터와 라이브러리들의 실행 가능한 예제 모음. diff --git a/README.md b/README.md index e68b120..0cda474 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # devslab-examples +

+ devslab-examples - Open source by DevsLab +

+ +**Open source by [DevsLab](https://devslab.kr/)** · [OSS brand guide](https://devslab.kr/brand/open-source/) · Collection O12 + **English** · [한국어](README.ko.md) Runnable examples for [devslab-kr](https://github.com/devslab-kr) Spring Boot starters and libraries. diff --git a/scripts/check-brand-assets.mjs b/scripts/check-brand-assets.mjs new file mode 100644 index 0000000..9d0e3fb --- /dev/null +++ b/scripts/check-brand-assets.mjs @@ -0,0 +1,59 @@ +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const root = resolve(import.meta.dirname, '..'); +const expectedHashes = new Map([ + ['.github/assets/readme-header.png', '47a942ffe3633979b805495dc3f27eef10d243ff278c72f1061152eac8a7f078'], + ['.github/assets/social-preview.png', 'e9bc6856c557ee4ede2db139b6b40cd754d1e90ebf63b25dc6f02f546cfbd151'], + ['.github/assets/collection-mark.svg', 'f2370535bb544213ecc0648d82298cd83767388a1c0ac991b907e4a778a03287'], + ['.github/assets/collection-lockup.svg', 'eaac691c43dd97c4033b966e27272a02d1dc06a5c0b01b95ec8483f6c2ff8bad'], +]); + +const read = (file) => readFileSync(resolve(root, file), 'utf8'); +const assert = (condition, message) => { + if (!condition) throw new Error(message); +}; +const changedFiles = () => execFileSync('git', ['diff', '--name-only', 'origin/main'], { cwd: root, encoding: 'utf8' }) + .trim() + .split(/\r?\n/) + .filter(Boolean); + +for (const [file, expected] of expectedHashes) { + const absolute = resolve(root, file); + assert(existsSync(absolute), `missing required O12 collection asset: ${file}`); + const actual = createHash('sha256').update(readFileSync(absolute)).digest('hex'); + assert(actual === expected, `${file} has ${actual}; expected oss-brand v0.1.1 hash ${expected}`); +} + +const mark = read('.github/assets/collection-mark.svg'); +const lockup = read('.github/assets/collection-lockup.svg'); +assert(mark.includes('data-oss-project="O12"'), 'collection mark must identify the O12 asset'); +assert(mark.includes('M10 6H6V26H10') && mark.includes('M14 11L21 16L14 21Z'), 'collection mark must retain O12 bracket-and-play geometry'); +assert(lockup.includes('data-oss-lockup="O12"') && lockup.includes('Open source by DevsLab'), 'collection lockup must provide the O12 endorsement'); + +for (const [file, endorsement] of [ + ['README.md', 'Open source by [DevsLab](https://devslab.kr/)'], + ['README.ko.md', '[DevsLab](https://devslab.kr/) 오픈소스'], +]) { + const content = read(file); + assert(content.includes('.github/assets/readme-header.png'), `${file} must use the local O12 README header`); + assert(content.includes('https://devslab.kr/brand/open-source/'), `${file} must link to the canonical OSS brand guide`); + assert(content.includes(endorsement), `${file} must include its localized collection endorsement`); +} + +const rootOnly = new Set([ + '.github/assets/readme-header.png', + '.github/assets/social-preview.png', + '.github/assets/collection-mark.svg', + '.github/assets/collection-lockup.svg', + 'README.md', + 'README.ko.md', + 'scripts/check-brand-assets.mjs', +]); +for (const file of changedFiles()) { + assert(rootOnly.has(file), `O12 is collection-level only; unexpected changed path: ${file}`); +} + +console.log(`O12 collection brand contract passed (${expectedHashes.size} exact assets, no per-demo changes).`); From fcf7169de5902679ba2f73c63f35682b8bca6580 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Mon, 31 Aug 2026 02:54:16 +0900 Subject: [PATCH 2/3] ci: gate O12 collection brand contract --- .github/workflows/ci.yml | 13 +++++++++++++ scripts/check-brand-assets.mjs | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2636a1..40e2dfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,19 @@ permissions: contents: read jobs: + brand: + name: Verify O12 brand assets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Verify O12 collection brand contract + run: node scripts/check-brand-assets.mjs + detect: name: Detect demos to build runs-on: ubuntu-latest diff --git a/scripts/check-brand-assets.mjs b/scripts/check-brand-assets.mjs index 9d0e3fb..4577246 100644 --- a/scripts/check-brand-assets.mjs +++ b/scripts/check-brand-assets.mjs @@ -44,6 +44,7 @@ for (const [file, endorsement] of [ } const rootOnly = new Set([ + '.github/workflows/ci.yml', '.github/assets/readme-header.png', '.github/assets/social-preview.png', '.github/assets/collection-mark.svg', @@ -56,4 +57,15 @@ for (const file of changedFiles()) { assert(rootOnly.has(file), `O12 is collection-level only; unexpected changed path: ${file}`); } +const ci = read('.github/workflows/ci.yml'); +const brandStart = ci.indexOf(' brand:\n'); +assert(brandStart >= 0, 'CI must include a dedicated O12 brand job'); +const nextJobPattern = /\n [A-Za-z][A-Za-z0-9_-]*:\n/g; +nextJobPattern.lastIndex = brandStart + ' brand:\n'.length; +const nextJob = nextJobPattern.exec(ci); +const brandJob = ci.slice(brandStart, nextJob?.index ?? ci.length); +assert(brandJob.includes("node-version: '22'"), 'dedicated O12 brand job must use Node 22'); +assert(brandJob.includes('run: node scripts/check-brand-assets.mjs'), 'dedicated O12 brand job must run the collection checker'); +assert(!brandJob.includes('needs:'), 'dedicated O12 brand job must not depend on the demo matrix'); + console.log(`O12 collection brand contract passed (${expectedHashes.size} exact assets, no per-demo changes).`); From 06c3f1027d02fbdf1940ebe31f64c0b242b89e04 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Mon, 31 Aug 2026 02:56:38 +0900 Subject: [PATCH 3/3] fix: fetch O12 comparison history in CI --- .github/workflows/ci.yml | 2 ++ scripts/check-brand-assets.mjs | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40e2dfa..52d839e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + with: + fetch-depth: 0 - uses: actions/setup-node@v4 with: diff --git a/scripts/check-brand-assets.mjs b/scripts/check-brand-assets.mjs index 4577246..fd37a68 100644 --- a/scripts/check-brand-assets.mjs +++ b/scripts/check-brand-assets.mjs @@ -65,6 +65,7 @@ nextJobPattern.lastIndex = brandStart + ' brand:\n'.length; const nextJob = nextJobPattern.exec(ci); const brandJob = ci.slice(brandStart, nextJob?.index ?? ci.length); assert(brandJob.includes("node-version: '22'"), 'dedicated O12 brand job must use Node 22'); +assert(brandJob.includes('fetch-depth: 0'), 'dedicated O12 brand job must fetch origin/main for collection-only diff validation'); assert(brandJob.includes('run: node scripts/check-brand-assets.mjs'), 'dedicated O12 brand job must run the collection checker'); assert(!brandJob.includes('needs:'), 'dedicated O12 brand job must not depend on the demo matrix');