From eaba6c52a9c5f3413c22ad251e4ead9f64adff53 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 20:29:40 -0400 Subject: [PATCH 1/9] Add yml indent config --- .editorconfig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 215de47..bf41e3b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,12 @@ file_header_template = Copyright (c) Christopher Whitley. All rights reserved.\n [*.xml] indent_size = 2 +################################################################################ +### Yaml files +################################################################################ +[*.yml] +indent_size = 2 + ################################################################################ ### C# files ################################################################################ @@ -466,4 +472,4 @@ dotnet_diagnostic.CA1027.severity = none ### CA1008: Enums should have zero value ### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1008 ################################################################################ -dotnet_diagnostic.CA1008.severity = none \ No newline at end of file +dotnet_diagnostic.CA1008.severity = none From 5fb32f218291abe2fa109ea98309bc6368a6be3c Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:05:29 -0400 Subject: [PATCH 2/9] Updated README.md --- README.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..93611eb --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +

+AsepriteDotNet Logo +
+A Cross Platform C# Library for Reading Aseprite Files + +[![main](https://github.com/AristurtleDev/AsepriteDotNet/actions/workflows/main.yml/badge.svg)](https://github.com/AristurtleDev/AsepriteDotNet/actions/workflows/main.yml) +[![Nuget 0.2.3](https://img.shields.io/nuget/v/AsepriteDotNet?color=blue&style=flat-square)](https://www.nuget.org/packages/AsepriteDotNet/0.2.3) +[![License: MIT](https://img.shields.io/badge/📃%20license-MIT-blue?style=flat)](LICENSE) +[![Twitter](https://img.shields.io/badge/%20-Share%20On%20Twitter-555?style=flat&logo=twitter)](https://twitter.com/intent/tweet?text=AsepriteDotNet%20by%20%40aristurtledev%0A%0AA%20new%20cross-platform%20library%20in%20C%23%20for%20reading%20Aseprite%20.ase%2F.aseprite%20files.%20https%3A%2F%2Fgithub.com%2FAristurtleDev%2FAsepriteDotNet%0A%0A%23aseprite%20%23dotnet%20%23csharp%20%23oss%0A) +

+ +**AsepriteDotNet** is a cross-platform C# library for reading Aseprite (.aseprite/.ase) files. Once file has been read, the library presents an easy to navigate `AsepriteFile` class containing the data read from the file. + +# Features +* Simple one line import method (see [Usage](#usage) section below) +* Supports Aseprite files using **RGBA**, **Grayscale** and **Indexed** color modes. +* Supports all Aseprite layer blend modes. +* Support Aseprite Tileset, Tilemap Layer, and Tilemap Cel. +* Provides processors to convert the Aseprite data loaded into common formats, including: + * Sprite + * SpriteSheet + * TextureAtlas + * Tileset + * Tilemap + * AnimatedTilemap + +# Installation +Install via NuGet +``` +dotnet add package AsepriteDotNet --version 0.2.3 +``` + +# Usage +## Load Aseprite file +The following example demonstrates how to load an Aseprite file from disk. + +```csharp +// Import namespaces +using AsepriteDotNet.Aseprite; +using AsepriteDotNet.IO; + +// Load the Aseprite file from disk. +AsepriteFile aseFile = AsepriteFileLoader.FromFile("file.aseprite"); +``` + +## Get Frame Color Data +In Aseprite, each frame is a collection of cels, with each cel on a different layer and each layer having its own blending mode. To get the full image of a single frame, the frame needs to be flattened. Flatting a frame blends all cel elements, starting with the bottom most layer and blending the layer above it until all layers have blended producing a single iamge. + +Doing this in `AsepriteDotNet` will produce a `Rgba32[]` containing the pixel data from flattening the frame. You can specify if only cel elements that are on a visible layer should be included, if cels on the background layer should be included, and if tilemap cels should be included. + +```csharp +// Import namespaces +using AsepriteDotNet.Aseprite; +using AsepriteDotNet.Common; +using AsepriteDotNet.IO; + +// Load the Aseprite file from disk. +AsepriteFile file = AsepriteFileLoader.FromFile("file.aseprite"); + +// Flatten the frame to get the pixel data +Rgba32[] framePixels = file.Frames[0].FlattenFrame(onlyVisibleLayers: true, includeBackgroundLayer: false, includeTilemapCels: false); +``` + +## Processor +`AsepriteDotNet` provides several out-of-the-box processors that can be used to transform the data in the `AsepriteFile` that is loaded into common formats. These are out-of-the-box general solutions and may not fit all use cases, but should give an idea if you want to create your own. + +Some processors take a `ProcessorOptions` argument. The following table defines the values that can be set for these options + +| Property | Type | Description | +| --- | --- | --- | +| **OnlyVisibleLayers** | `bool` | Indicates whether only visible layers should be processed. | +| **IncludeBackgroundLayer** | `bool` | Indicates whether the layer assigned as the background layer should be processed. | +| **IncludeTilemapLayers** | `bool` | Indicates whether tilemap layers should be processed. | +| **MergeDuplicateFrames** | `bool` | Indicates whether duplicates frames should be merged. | +| **BorderPadding** | `int` | The amount of transparent pixels to add to the edge of the generated texture. | +| **Spacing** | `int` | The amount of transparent pixels to add between each texture region in the generated texture. | +| **InnerPadding** | `int` | The amount of transparent pixels to add around the edge of each texture region in the generated texture. | + +The examples below demonstrate how to transform the `AsepriteFile` using one of the processors: + +```cs +// Import namespaces +using AsepriteDotNet.Aseprite; +using AsepriteDotNet.Common; +using AsepriteDotNet.IO; +using AsepriteDotNet.Processors; + +// Load the Aseprite file from disk. +AsepriteFile aseFile = AsepriteFileLoader.FromFile("file.aseprite"); + +// Create new processor options or use the provided defaults +ProcessorOptions options = ProcessorOptions.Default; + +// Use the Sprite processor to create a sprite from a single frame +Sprite sprite = SpriteProcessor.Process(aseFile, frameIndex: 0, options); + +// Use the TextureAtlas processor to generate a texture atlas from the Aseprite file. A TextureAtlas generates a packed texture with all frames and TextureRegion data describing the bounds of each frame in the source texture +TextureAtlas atlas = TextureAtlasProcessor.Process(aseFile, options); + +// Use the SpriteSheet processor to generate a sprite sheet from the Aseprite file. A SpriteSheet contains a TextureAtlas as well as AnimationTags which define the animations created from tags in Aseprite. +SpriteSheet spriteSheet = SpriteSheetProcessor.Process(aseFile, options); + +// Use the TileSetProcessor to generate a texture from a tileset in the Aseprite file +Tileset tileset = TilesetProcessor.Process(aseFile, tilesetIndex: 0, options); + +// Use the TilemapProcessor to generate a tilemap from a specified frame in the Aseprite file +Tilemap tilemap = TilemapProcessor.Process(aseFile frameIndex: 0, options); + +// Use the AnimatedTilemapProcess to generate an animated tilemap from the Aseprite file +AnimatedTilemap animatedTilemap = AnimatedTilemapProcessor.Process(aseFile, options); +``` + +# License +**AsepriteDotNet** is licensed under the **MIT License**. Please refer to the [LICENSE](LICENSE) file for full license text. + + + + + +Made with [contrib.rocks](https://contrib.rocks). + From 64e1ec9cef13a336380d1ae502493820600acf4f Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:05:46 -0400 Subject: [PATCH 3/9] Added repository images --- .images/banner.png | Bin 0 -> 34729 bytes .images/banner.svg | 743 +++++++++++++++++++++++++++++++++++++++++ .images/nuget-icon.png | Bin 0 -> 10341 bytes .images/nuget-icon.svg | 734 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1477 insertions(+) create mode 100644 .images/banner.png create mode 100644 .images/banner.svg create mode 100644 .images/nuget-icon.png create mode 100644 .images/nuget-icon.svg diff --git a/.images/banner.png b/.images/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a5a70c953e2120ffdabcd6dab59d2ebe4d6c41 GIT binary patch literal 34729 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.images/nuget-icon.png b/.images/nuget-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..787a2bf155f9c17f0c810ae56169f61dd2cebc83 GIT binary patch literal 10341 zcmV-rD4N%aP)}Ge)>c<7<1FQ^E5 zik=8(0kPa6Nbf~dsz9g#LPAJFdL?bM**5dMe*{v{|wLWmE5;Q;yq2nXN> z0u>=d4FHtV3Q$x4CB%6x#5P&t5(%)P3#aftF zEdYd&zJ!n^6c_?ZWinS@@QC(hH4nCjZ)_lVFBiMYGnVL4a4ZwKhtg0SpKmsm8aNPQ zCcqI&>06Z2qfUC2R!gfC07A$j!jSn?r0()IN+D=m2R3wSG#G_rXDrbuAmxWk$k>`F z=<_OA!jN(*($4_AMJW{>T3IWl)d&C~gafdY5b_cTT};?h>_l==b{8gx>J zfh8>R428IyaTReN|Aw3cDHIeYD$@S|m`5o!JEZ}wgjOK{gpj8QL%s$fE`g)NNay+e z9R6#+lQrFzK_hQd{aK_UE^Dvh_xzJsxo8CvH(n4SQUev~3zX7zPHjvJ(xL)D2nhf~ z_CTc3KK%m3UdtY2oZa!a8QEqK=7dZG0jc+0xw!(Hv-aYR=U$^(2d@*tkVGodk(AOK z&Cb` z;WS|jVi?~ZZK3wFJoH+EiWJ$ql2NcT9fb+G=s&PO2K5_=pr9al zcz7T`KOcE{c}TpTg#G*X19AeNq3sa-m(Jil?H4i`;lL6WdeKnR2D6BiA1)#1$0X=0 z^%yW2`*MVm-J$@%k-W+3g0i)f?r##^s1!syYX;pXOMHN7Z`IDY&% zR(-V^TefUL@Y9_U^mHWHCM+;>e@(-URq+UH6M#1tyn#`pMnSE%zC{%T0Y{G>#lQdk zZ^N!#yF^hG-!mGGA5lsTRx`B#P8R?|h=MRAo%eC^4xQhd;glTwd$j|@HmrxDBE|L3 z&*J96Y^?a`6FmL&(_k3;^j8-zUc~IVFCj568Bre$gu<`>Ja4lNW)Vpr$D!h65kC9Z zrG6<=^bFjskGiUI^3oleABqSthwc3X%rMY$$;6$7%0E7^RFeH(4<-9r#W>)fG7COHdVR#lQ zU$vyp2L-Oa`v*Fy!tlqDKbq9}0KhN|K3e`EK6>v%#Q)=W=<_NXZh}aW@b+=^_V0?L zzaD8)=K}!8arokkFJw!WEb&q*m4^r+(&s=Y;xqvuggi$@j0m06lQ0Sngk|6vB5h@! zvi2hQ42sPgHo?WkK824hX3d<1=bxR0ORpV)ptrq?yT0l?+{=|%|Kob_JnyhJ%$qll zdFGjC+|_FJUP6f6VFfo5r%VBakT4LE7%;g5>(;wU(A|SP;Thu9U|wx%3F2n%!=>}_ z2n`Ljt6qb_fQ*a`==FN|`}@PotA2f=C}QaFp~wr>B6Maqi{+Fi6e51^Z%DY5fPjDi zyV_tfnUIy0g|f0TXfzs&BZt{+M!$ajbQdmMSZp$xmf2N-gK^3TKp66yYbP(}cIOj@ zz%9UCQr@&x7cu{pd3JUFkt0VieE4v5>Cy%L`}fCy0RzyrYgfd?#9;B_#W0yn)oB@q z!RpUfBmJkVFcj*nv>_e~Uw^}{&X13e$Cxo=5ET`Ln3xz095@i&yLU&A9z8H`-aJ%P z)J!`Z$6?i~RZ5m+-y($AcVOXQ96kaNLgo;bd>XN+AJ|(be69g*@D6YIsJAqs5SQm4 zL3+v!o7=qGh@yyDvu0t-mMyq>^QOf#9LJ$+*RI&Sc{758gR7rUojw(Rm@XpX@4f1z zzj-znDND}aMsk|u>F3Qi-^ABne~r96%f?0w!=O{APWb-&@6oeo&+6yn$B(bvvuDo= zgTb&+QbkU{p(6kxL`@j-QQL_T)twIjZUG*W^5-4AfhkW?J&RGJMxn58GDs?R=n0P_JCM zg2x_v42g+})z4pk`DGWL=ch}m$VoU_0KSuXDmeGvn(AkAHw9!a($4xzj^<#}_=%Fz z{`~XL*tTsOj7FoR^a%+Gm^yW8_2-zF81O8Q(u5kJ00oMUWn;p)36jb@fBrna`Q{sF zwOUE(Q&UqhZQ3;49(4x|8U#@ky$B(Zk_vDF4if-Eh!Ow{8Uh$Ye4MA3j__m2qo};K&hRwai0KRN=LTdqSY{ktP7zv{Hn142MkC z@Mw{dk%1dG>{rbf6%}Fi>ebbsdv@yqQ+mxpu;NB3I(F_P>9kR=*CRPO+3q&zbUJ+h z{rAT~iQiLxkA~GbBbBS=V0l(j8OGvDX#Av|ja;~J0r~m) z_BRx$(`g!w20}^A6k@!ohkx6)lFCU2Z|*oLyQvN~nigI=$% z(*^*_Mit}$95VtiWR7RwwggOFC5gga`Y9gg!a-XhZ7~%Z8VZhUoMum{todJ;mzRTe zsVRq5$WSh^4Ss%pHnoAaP^oGZQd+GRlv4W^GY+K50ze4y01)opyZ-PkcauH>D122& zPfwSWHYO$p?b@}ITy|{+3>Z-TcUoE+INzFIyuw$BG>L1A;NW0rG@6DJTA)LR4%L6B zrlyJ&6%|R1D#!siVgz`Jb>*pCQ&-K(@+=rmdhzNoPh_TLqPV!Y!L&-H5|NSi@0>zH zLh#&k&sBdudg>Tl!fMVOTqC@YoR9>a&hiOiZ3rRg+qbXXZSeE+!`!)Zt3Mw(aztk` znaTNke9rLTgAZcMmM!Ssy*uP` z%R2~PUtdg~JQ;@$9lE{hhad=8ym+xrtJQuksUj!QBxMjGWCo{}eI4;u|9Vq^XNVWv zgX)hA4UxR+93qMXuxsm1$$1P016HnFi9LJvpt!ggX0sV?Zf@w@xic0lSWr*1xN+k~ z%$olK`u=YW7+L+_K+?yj(VL6H`XBx$xebCKVBNZP*sx&(ii(P0GMS)KsSq9>j(PLu zpO$)A7!Zf%;3Lut0w^7;XZJcr~=)-qie5j@ShzC&4cYNrZE=lapH;X*tGs*`@LnJ|9x z1i19{wA%Rqz*we_9 zIsowO?uXF7b;Ia!V{qZZg+`ZC8=X#v$DVi$X}U~wdM&2G^gjIp5HvLsBS()!LPCN= z+E89zjz=GTRG*TPa;mbj@>Pcv+C-W*0TN6 zQdwD9S?E9L0h}p_N7S-`AQI0fg8$M59+TRkPyZO~*s-HYZAeK;L606ibSF-nIH=WX z$F$_UUz?`w0sx+|qU@nU)8i`yj_-gj?+?H;GpA$Rq;W_}vhTo5r_*7{yGziqODE`~ z38Ft73{GvIfoISYk%)Y!Kc0N%DNKIqaipfE+Lgb&yc};XdJ|o`bU{{D*1sw$D#lVu z8#YUBCYrPeC zA8^|%X!F+1`0j`AA@fzBAR$+vqS)$!ay3-|%z@8zW|g7!SNjTg1vK3qtL4<^S0HC! zDoT&$q3}u$+DC*SBs8R2i>DznRR{;l6dDpF~>@n)n+0w8mf8`@6p zz^gmeH@{|i4(<8}Tgc@YjKzACUoVC}R|~-)fUpE|Zx^V7J)jDbW>eTy3^yyGO)f@d zeg#B>0Kzhm`?x^e-W|%onl{|na0LmUoHB`m=r{)h2hp@4iGf)(2o_&TFq$C>q9h-{ z#=v_j;N4rA@rJXId$~X!)3D0hRB8YKQ$y)mf~NM4Q8Zd#%o@vEl_PA6VJtHO1k$Wm zo0%r=0!XR3%=&v(mQ*&S_`3#ut`-1FTe=;VXp#VkCgF;)z`E{Se_sFq0NS(?kqWfM zj6Q79Bmod~<`afI>jGdbH$XJs6Y&;pqA#qZ0336q>0p|=1~_aisI2E*a~mpB=n7lS znMaEuRGJw8CtA84mT2n1#1Sxr>T@fIqOIk~8x?uwP-~>e0&+H`iAAU^r~s#u!9}Bn z(pL?_*4JlIvxv%EEp)kBn6yTygWcg8V!wL3@MJbnwbhoc-)=^lBmk6BQQk&XqDwCE zv?c)hQauEN8LYhJ55aU<1|T$kYm$D~vFf8DK0kfZ+%L z0?{mjVF{w=Jpj(mkan3nuYs_nNHmMBau?9#UJ*^^Un{PcO!OLP`P7e6in8od$-yC< ziN2@;%X%#bp#)coFXLeLFG$zkfT_#?Q<(ukd7bp4sn*=aFff$p?G}KW=W+!qwB9J> zO+KYDml{9SC6~a~c|dtqS*utKiY5w;qAhxO_r#R&saQMcTfE!*eFUhag`lB!zJs~K z2t%Qc1@Nu?t!*BfEC7_!!>n3nF28KuO-d9*lxMe???W|&(0c3D{=WV3&A@N*p#MV> z^4nM6^)(kk2r*Dfx7nAo`EbZNEyLnq=^5KzvZa~D5KS#R3J{isLD1KGE@Na^*li(3 zwHs|aza4%K0Fb%+ngELZc4ca293lXQ0{u(6)KZvAtRMY~CJ{GNTQ*oG>%yVJYxI80SXb>g&= zQ@JpgTi@+aN>P+l04lajhNBL+rGHHj)00HDt^ev($Ej! z&Q}?2P)hMf;xQb&uovf2;!s#tfMTunz*DURBY=DWsi^WT+(0R{KEGMP;bG!bp&mDg zj0p;Q&hldFHVnt0ZI1xRl+AA>W7eCY`za5zH7`LdF=5%J#fZPrDs?sXUo0>{LZQY5T6g^UeX8y#(=m68l#ETvSE zGlNq?#i~1af2+?fpB6f|CpdT8Ay;Hd9&Nh^SoG0zCfR$gpbIm9z%u&B{(*9d!6I!DO09k`SRsWj!fPX96EFeFTVI6EsbGq%Ckz5doc%Qy>wH0 zZE6Yrcz!qhM}?v5q5)QY09EbX(eLZg2$>QEmgi7XQX;vIKLZmc)QNM<0I(T_)VWv2 z5aLE;?7zz53!YR2sm(bX5>aw8M^avWNhPvRXQ1Rpaf3(4Wr;;N`NAHAPK`qNU%NNl zE`}gtRu6Q0Z!jL6Ft*9w$E^tqL91-8YMr+1CWJf%8M~5+*2sycQc-y-UGNC;LBYN> zn9EJ@85RT!*Y?&y6e&v5ZbF+^2G0<0DBGy(WhsfzN8G%F=s3GK+KumEu^emo4GMy6 zIghC`o`EQec=ru}tVlVbq<8FBP|0iz&eudqYI`^Z0?pk#X7vJ(_2KtVc$hU2djr{Ub`b8u0( zAobI;(4|Osm@!wHP?S`F%#$~ex+MW;|M3f=7Y&5}Nc-*`o8DW7+JPfP)LGF-26aHrnRLXC8-mR5E+c1SlH}0<07@?w z;M!+r(0$2Z_zjk>rL%=kj6WjZc>oh8PsE86)=l_DQAA~>^F5xLs~$(h3g#i_HW6Rf z7v=vN>%#O3te1ug0>BWOUsMwhktGa%{dO;Y-m@93iQxK&aVS2K-Js#6F0B;V>l4v) z=}@$ZZCt!yYxoXmhlqK-FmBR#i{WC^)6)?i9u7A*H+=NbM-HvKHp$7!2o4U0ySqD9 zu3YJ~dVr<}ic~?t?PuC~j*!1MN=baIsCNYla16biQ18qd;|D#8{Ii*O_O+QPjmtyw zveUSEB+H`xpdn9(tnU&Ky)YI&{hSkG-+xpXWNj7LwynM$=Z`=Bh}6_nn9XJ^UAojM z^#j1S-+qhC%uJX}raIA0ok61ofDqyW1@{}<*{?7E&Tu7c*~^O*;5nL_lnk@E{)LSU zVer*kD^Zx7hbLxFf%Z}nlHWgxyzSSa&#bc57mWh4en>$0vt7}4c*94_O+gXrW-%5} z?IGEj!*g@h12j?q2q7#K+|NvVuPE8P!3xM4hWiaCt@}tQc^CZf-Z!{; zEgvtxI}g0TA!E%Yr2H!m>0e%iYlsIzrr5SKa0JGTibjW{|FhA!5rAjc5I@%;{K8=k zcRo<%8^-_@-F>ir(*_B}%katE50G;_4GGt-qW?p&Fc#?1?VUjlo*gumiu5uRUdqMj z(Y8&*?-uM90K&2_5H&xUUo>0^sp?yi0t}&~Z#%5tu)(gYR>C!*@G`j}?8zwbc3eTz z7^p~*{81cUe(5EP*UsLJ*d+jjkZ7>@L^f}T3vspM-eZ9R3ZuV(}tfUXDux) z#g47p(e8;(_B6Foq`g)Lmh0HQX`9us?{_28M*tbOn2BiP!#*4-Jr4zh zqp1)tluy6NX z#Kzh*oVjaha0DQPJWS+V7j9e^+rpwV!MrXGhQDtEC?)dMlfXUG8+UFKnII4FpNxeu z$_H;QTGU_zgMxw(-Z27sze~GPPAW;O&LHJK=W+D+qxWp*15%~{h3s8!QnZWJ{k}jU z>`#Jz_9lo4d5|w31KIq+AS&C&o$QPKvHQ?|Nzu0#zWPsOthr({2!&LrNO9@?V@Uho zWjqk`0HULvf3Vb=u_*wAkU@k&7j|fb#hG7>&w*jyHkh}@Q-01s@E?o@@s}8VTx z+)J}cVO(+m#*cre?9gz?Kc7gdb^rD-A_AMYZb5doZT|&^VX)!9KcVb+J~B2X+DvN= zq0)@FIr~s>EE9shzCPk!#i{@hLcCx!4Pys|Ri~ll2AIA&Ne!><0`XEp@x>&3=hLQ9y+3^+G?FFF%~$bi-pvaU5? zYXo=_fWfr#7^N8{FmH~hh8H)3=9UrpXX7E89Sbz#a>zRoMn+)G+O?9bbGvu%jkrP z03nonvKwI{q&arji|LVYeEiu8$x0%HLV<<iISN;t!Y>c~7e`0`gcIX8 z;@4q6;QZhApl4wvwk;llnWG~B5O{|+&Gl@|m@#m1sq$KyJb7}X%V~(QW5!dm=eG85tQFh>2bb@e8ykz{}8cUUDc zECbJ=Ht=e%0V~CBK?45%{>|b++^$`_W-&^!{Ilk{VN7S=n*SIDU?HHgLmSc~0GBRZ zLQak}&!OAQ`rB-5J6=%dnZjKOzfOS&8x(~$5djYC{JW8Q0ze2M(3{(nz*|?r-F6;r z>{d@IECJIq5Zkuf7gV!PpFXHG5v1m5tDkwd*T6H#7p$!115~$mER6sG09bNc1$)~s z&(0{Bjls~P4gR~uraQYuCQh1+L+5H7V9Rn_eSoJ{gQZ^WNPJY4)s7su1)v%B&!nR- z&Yd~~dtKH?jv9?qNi`8bD~ihPF5_Os;s`+6)UxkyP61HyU|i)$OG~pWUu7QKfWqF1k;hzNAY&f^&vJZB%KEZBo}Kb=Qj zfwtky_b`_BNwm%iN~Beuz0EWNuodP;+2nhB`yfc;hWU?oKvAgyhvJLy{l*LU+dp=o zQ-lwO4-P|YOc0EwrW=*qO;{2DE!COK0IJpjBCYbqX(jUgZC{USJfXK*5 zyR)(^i`t=tm|Fs?Qt}u*yaPrLuQEP;VD#5G5treR=H3lhn%K)xQOo#FqyU;{3ZR8r zxO;lS)zyBZjndLmsB8IiFr4M%-@70jphK}F0O>SeTk~&f#wkEd$j1W@G|o0ZKR*xd zs+!l~mG_Q!PgAfY0Ex7y!UeY?X*TB~P!(T<$Hz`=RNnY=rxE5~GjhmX?;Ctdp`HLx zN^`*w11+rDKG8CL)jGi0cm0?I7%paE#E21%%6{(r1#}Fm(f2F88g^p2k2QM$IhP`) z-i#9eca92YGC_C_hPU^E9T-Ao(_a;5GtC!{DT_iY%$X;$J_==i*%sLg?RMQ zNA1r`DZc*C7a0G520)b+kVl|(0q7WB^WD{}SMAn2w08FH-FxRFn>S=x8_SPStU!l8wH&hNVr2Y#qX<#)F#K~DHGT5CfF?Rxa0jkq)?a5rp|C#&z>Xa|;H3gOw5yu` zy+ZtK8iI@&-m&^`N-35vUv7VcTPa$t7N39qdF@A6DW%D_@>moAN~xA8xHIBu%LgV* zdoS?skLF}EVu%=@OAIgkESh)L4*@3yTA&3zn2f)_^s@bVDaE3N3-IEjRly=zo`YAA zugyHYy0t}*=$g9=R|Yf=cn4he&N z)g*>}xRYpJf1Vm%++?E3g;ulF!7ReG<|It7Y{$rvBM=Z^ziZjXjT@mL73kNc%3Qro zn7`Enpz3B5f@O<_SAP~o5syFqI6nLAGYG<;Q%{hWmxqZHCu05j^|c?T0r;Y!yjCxa zAcUyEF-7u^$H^td1;{9Y>8n3XVp=iBMEVN++(Dc*&mr^f1eiBmpp37Ys7x$=-M-iE zOB>8)Gom6Ru;{5k^y=UZxk`cd4@647`D4N2{rGU@u{zH>cI=49AAcNOyLN?#hlgG5 zX>J5TKyGd>4j(>@4I4H zL!bsT;l@WX+_bZfY+OC6CTWwK#WNTDX|neD&2=cx}O}m>BDe ziGzYMcuaSw?6CV61QE}_x&z<*w`m^7t&j=;Pg6>_OG#{700`+0o;@jF{TSbnzkeMJ zW|+6ci{_mPgt*Gh+|*t&Ve2I-=9Uv~ayQD2>O{aXkS{rmA3s=WcT7ILUXKpp?J;{y z0OAshu=iv(B0KtG;^;_B9Nifm!mRHC?&OQLr|{+nhfrMN{J_l3z*zt@D5a8b!EEr( zAmSqXjhhfXkQ*Om_vsJKE3cXsuH?&@NM9jq$OOl`@*IVmoLNd z-Ctwrb6o%cKG7lgEiMO}cU{5mU#>yptH#jS5Db1G7z6tTBcQGAk>O@ZC04FEg)O@i za4!B&r!KApa1g+c05(!e?YafCVF4f{7A~@bidB;ojr9iBKm7yc#zhhCiJI$ND$8InAhdlT*39pYV2uh&Pc?%2)WjJwngkp>m5$$zrQx^ZsW^2u3rZIm zqC5McQ@9WOHLmd2xWe7FYRjomaL`qnP*SQ#W_B5_UMs-)OSwqPEJ4RG9}JFdkB0_? zA}G+kx{kF!ofE(L;cT|vV15i$SGKkU6hIz;JW6S!+t8rr1f}#CagiM{|8!vpH@$b` zB!Gg{RwmJ`QgG>Ic>3w5?JjEl`v0M8s2hScRhG-{0d->*@-h}fV?!`BHUz*>n9U-t zr`<$+LO!mg6d@*bTR}6^_!HY9u5Yr<7DhrOJr_N>J z%kR#ZO(tO?rPTIa^nU`Rxz6a#&zTPNBe=j zHJZ41Hjm1eQi>V#x8qVlIr^*_!w}#VLdacl+vNWPoLms&J*~GV00000NkvXXu0mjf Dm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d499b684c656fd8b739b0182fde1841e54f051da Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:06:08 -0400 Subject: [PATCH 4/9] Change name of package icon file --- source/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Directory.Build.props b/source/Directory.Build.props index 93941ae..dcf807b 100644 --- a/source/Directory.Build.props +++ b/source/Directory.Build.props @@ -18,7 +18,7 @@ Christopher Whitley and contributors Aristurtle https://github.com/AristurtleDev/AsepriteDotNet - aseprite-dotnet-nuget-icon.png + nuget-icon.png README.md Aseprite;reader;pixel art;png;spritesheet;tilesheet From 5ea22bea8756c9611969cfbc1e2cdf86ec8482e5 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:06:19 -0400 Subject: [PATCH 5/9] Add includes for nuget image and readme --- source/AsepriteDotNet/AsepriteDotNet.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/AsepriteDotNet/AsepriteDotNet.csproj b/source/AsepriteDotNet/AsepriteDotNet.csproj index 23947d5..5c19b32 100644 --- a/source/AsepriteDotNet/AsepriteDotNet.csproj +++ b/source/AsepriteDotNet/AsepriteDotNet.csproj @@ -16,6 +16,12 @@ + + + + + + From 6beb4b28974a16e54041f75474d93ed54194064f Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:06:44 -0400 Subject: [PATCH 6/9] Add Cake Frosting build project --- AsepriteDotNet.sln | 9 ++ build.ps1 | 2 + build.sh | 1 + build/AsepriteDotNet.Build.csproj | 10 ++ build/BuildContext.cs | 108 ++++++++++++++++++ build/Program.cs | 7 ++ build/Tasks/BuildTask.cs | 23 ++++ .../GitHubTasks/DeployNugetsToGithubTask.cs | 29 +++++ .../GitHubTasks/DownloadArtifactsTask.cs | 23 ++++ .../Tasks/GitHubTasks/UploadArtifactsTask.cs | 22 ++++ build/Tasks/PackTask.cs | 23 ++++ build/Tasks/Tasks.cs | 9 ++ build/Tasks/TestTask.cs | 24 ++++ 13 files changed, 290 insertions(+) create mode 100644 build.ps1 create mode 100644 build.sh create mode 100644 build/AsepriteDotNet.Build.csproj create mode 100644 build/BuildContext.cs create mode 100644 build/Program.cs create mode 100644 build/Tasks/BuildTask.cs create mode 100644 build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs create mode 100644 build/Tasks/GitHubTasks/DownloadArtifactsTask.cs create mode 100644 build/Tasks/GitHubTasks/UploadArtifactsTask.cs create mode 100644 build/Tasks/PackTask.cs create mode 100644 build/Tasks/Tasks.cs create mode 100644 build/Tasks/TestTask.cs diff --git a/AsepriteDotNet.sln b/AsepriteDotNet.sln index 68fcb47..c30eba5 100644 --- a/AsepriteDotNet.sln +++ b/AsepriteDotNet.sln @@ -26,6 +26,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorProcessingBenchmark", "benchmarks\ColorProcessingBenchmark\ColorProcessingBenchmark.csproj", "{201F4F7E-C68D-400C-AB87-9A77578DDBEB}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{1515BA5C-6095-4F5F-94E6-7D54D09321DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsepriteDotNet.Build", "build\AsepriteDotNet.Build.csproj", "{27B2E61B-9CA4-44D2-834F-772B7D233D9F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,6 +48,10 @@ Global {201F4F7E-C68D-400C-AB87-9A77578DDBEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {201F4F7E-C68D-400C-AB87-9A77578DDBEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {201F4F7E-C68D-400C-AB87-9A77578DDBEB}.Release|Any CPU.Build.0 = Release|Any CPU + {27B2E61B-9CA4-44D2-834F-772B7D233D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27B2E61B-9CA4-44D2-834F-772B7D233D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27B2E61B-9CA4-44D2-834F-772B7D233D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27B2E61B-9CA4-44D2-834F-772B7D233D9F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -52,5 +60,6 @@ Global {18E8668C-4BF2-4F9B-8016-E1F22BD53BCA} = {B63FA11D-4B29-45F2-AAD9-BF489AEE7BAA} {A289C75E-0BCF-421F-AAE0-050F45AC4F4E} = {5F219449-CE10-4DD9-BBE5-251720F3B5E3} {201F4F7E-C68D-400C-AB87-9A77578DDBEB} = {C4B76AC7-83D0-4F29-BAB9-3C0EAFB31C0D} + {27B2E61B-9CA4-44D2-834F-772B7D233D9F} = {1515BA5C-6095-4F5F-94E6-7D54D09321DE} EndGlobalSection EndGlobal diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..a707719 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,2 @@ +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..dfd6b85 --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +dotnet run --project ./build/Build.csproj -- "$@" diff --git a/build/AsepriteDotNet.Build.csproj b/build/AsepriteDotNet.Build.csproj new file mode 100644 index 0000000..4861f2f --- /dev/null +++ b/build/AsepriteDotNet.Build.csproj @@ -0,0 +1,10 @@ + + + Exe + net8.0 + $(MSBuildProjectDirectory) + + + + + \ No newline at end of file diff --git a/build/BuildContext.cs b/build/BuildContext.cs new file mode 100644 index 0000000..b8aef42 --- /dev/null +++ b/build/BuildContext.cs @@ -0,0 +1,108 @@ +using System.Net.Mime; +using Cake.Common; +using Cake.Common.Build; +using Cake.Common.Build.GitHubActions.Data; +using Cake.Common.IO; +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Build; +using Cake.Common.Tools.DotNet.MSBuild; +using Cake.Common.Tools.DotNet.Pack; +using Cake.Common.Tools.DotNet.Publish; +using Cake.Common.Tools.MSBuild; +using Cake.Common.Xml; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +public sealed class BuildContext : FrostingContext +{ + private const string DefaultRepositoryUrl = "https://github.com/AristurtleDev/AsepriteDotNet"; + private const string DefaultBuildConfiguration = "Release"; + + + public string Version { get; } + public string BuildOutput { get; } + public string RepositoryUrl { get; } + public string BuildConfiguration { get; } + public bool IsPreRelease { get; } + + public DirectoryPath NuGetsDirectory { get; } + public DotNetMSBuildSettings DotNetMSBuildSettings { get; } + public DotNetPublishSettings DotNetPublishSettings { get; } + public MSBuildSettings MSBuildSettings { get; } + public MSBuildSettings MSPackSettings { get; } + + public BuildContext(ICakeContext context) : base(context) + { + RepositoryUrl = context.Argument(nameof(RepositoryUrl), DefaultRepositoryUrl); + BuildConfiguration = context.Argument(nameof(BuildConfiguration), DefaultBuildConfiguration); + BuildOutput = context.Argument(nameof(BuildOutput), ".artifacts"); + NuGetsDirectory = $"{BuildOutput}/NuGet/"; + IsPreRelease = context.Argument(nameof(IsPreRelease), false); + + Version = context.XmlPeek("Directory.Build.props", "/Project/PropertyGroup/Version"); + if (context.BuildSystem().IsRunningOnGitHubActions) + { + GitHubActionsWorkflowInfo workflow = context.BuildSystem().GitHubActions.Environment.Workflow; + RepositoryUrl = $"https://github.com/{workflow.Repository}"; + + if (!RepositoryUrl.Equals(DefaultRepositoryUrl, StringComparison.OrdinalIgnoreCase)) + { + Version = $"{Version}.{workflow.RunNumber}-{workflow.RepositoryOwner}"; + } + else if (workflow.RefType == GitHubActionsRefType.Branch && !workflow.RefName.Equals("refs/head/main", StringComparison.OrdinalIgnoreCase)) + { + Version = $"{Version}.{workflow.RunNumber}-develop"; + } + else if (IsPreRelease) + { + Version = $"{Version}.{workflow.RunNumber}-prerelease"; + } + else + { + Version = $"{Version}.{workflow.RunNumber}"; + } + } + + DotNetMSBuildSettings = new DotNetMSBuildSettings(); + DotNetMSBuildSettings.WithProperty(nameof(Version), Version); + DotNetMSBuildSettings.WithProperty(nameof(RepositoryUrl), RepositoryUrl); + + MSBuildSettings = new MSBuildSettings + { + Verbosity = Verbosity.Minimal, + Configuration = BuildConfiguration + }; + MSBuildSettings.WithProperty(nameof(Version), Version); + MSBuildSettings.WithProperty(nameof(RepositoryUrl), RepositoryUrl); + + MSPackSettings = new MSBuildSettings() + { + Verbosity = Verbosity.Minimal, + Configuration = BuildConfiguration, + Restore = true + }; + MSPackSettings.WithProperty(nameof(Version), Version); + MSPackSettings.WithProperty(nameof(RepositoryUrl), RepositoryUrl); + MSPackSettings.WithProperty("OutputDirectory", NuGetsDirectory.FullPath); + MSPackSettings.WithTarget("Pack"); + + DotNetPublishSettings = new DotNetPublishSettings() + { + MSBuildSettings = DotNetMSBuildSettings, + Verbosity = DotNetVerbosity.Minimal, + Configuration = BuildConfiguration, + SelfContained = false + }; + + Console.WriteLine($"{nameof(Version)}: {Version}"); + Console.WriteLine($"{nameof(RepositoryUrl)}: {RepositoryUrl}"); + Console.WriteLine($"{nameof(BuildConfiguration)}: {BuildConfiguration}"); + + context.CreateDirectory(BuildOutput); + } + +} diff --git a/build/Program.cs b/build/Program.cs new file mode 100644 index 0000000..3f3271b --- /dev/null +++ b/build/Program.cs @@ -0,0 +1,7 @@ +using AsepriteDotNet.Build; +using Cake.Frosting; + +return new CakeHost() + .UseWorkingDirectory("../") + .UseContext() + .Run(args); diff --git a/build/Tasks/BuildTask.cs b/build/Tasks/BuildTask.cs new file mode 100644 index 0000000..4de0829 --- /dev/null +++ b/build/Tasks/BuildTask.cs @@ -0,0 +1,23 @@ +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Build; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("Build")] +public sealed class BuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + + DotNetBuildSettings settings = new DotNetBuildSettings() + { + MSBuildSettings = context.DotNetMSBuildSettings, + Verbosity = DotNetVerbosity.Minimal, + Configuration = context.BuildConfiguration + }; + + context.DotNetBuild("./source/AsepriteDotNet/AsepriteDotNet.csproj", settings); + } +} diff --git a/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs b/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs new file mode 100644 index 0000000..fe458f5 --- /dev/null +++ b/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs @@ -0,0 +1,29 @@ +using Cake.Common; +using Cake.Common.Build; +using Cake.Common.IO; +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.NuGet.Push; +using Cake.Core.IO; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("DeployNugetsToGithub")] +public sealed class DeployNugetsToGitHubTask : FrostingTask +{ + public override bool ShouldRun(BuildContext context) => context.BuildSystem().IsRunningOnGitHubActions; + + public override void Run(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + + string repositoryOwner = context.GitHubActions().Environment.Workflow.RepositoryOwner; + DotNetNuGetPushSettings settings = new DotNetNuGetPushSettings() + { + ApiKey = context.EnvironmentVariable("GITHUB_TOKEN"), + Source = $"https://nuget.pkg.github.com/{repositoryOwner}/index.json" + }; + + context.DotNetNuGetPush("nugets/*.nupkg", settings); + } +} diff --git a/build/Tasks/GitHubTasks/DownloadArtifactsTask.cs b/build/Tasks/GitHubTasks/DownloadArtifactsTask.cs new file mode 100644 index 0000000..4fc6dc8 --- /dev/null +++ b/build/Tasks/GitHubTasks/DownloadArtifactsTask.cs @@ -0,0 +1,23 @@ +using Cake.Common.Build; +using Cake.Common.IO; +using Cake.Core.IO; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("DownloadArtifacts")] +public sealed class DownloadArtifactsTask : AsyncFrostingTask +{ + public override bool ShouldRun(BuildContext context) => context.BuildSystem().IsRunningOnGitHubActions; + + public override async Task RunAsync(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + string path = "nugets"; + context.CreateDirectory(path); + await context.GitHubActions() + .Commands + .DownloadArtifact(path, path) + .ConfigureAwait(true); + } +} diff --git a/build/Tasks/GitHubTasks/UploadArtifactsTask.cs b/build/Tasks/GitHubTasks/UploadArtifactsTask.cs new file mode 100644 index 0000000..55531dc --- /dev/null +++ b/build/Tasks/GitHubTasks/UploadArtifactsTask.cs @@ -0,0 +1,22 @@ +using Cake.Common.Build; +using Cake.Core.IO; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("UploadArtifacts")] +public sealed class UploadArtifactsTask : AsyncFrostingTask +{ + public override bool ShouldRun(BuildContext context) => context.BuildSystem().IsRunningOnGitHubActions; + + public override async Task RunAsync(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + DirectoryPath path = context.NuGetsDirectory.FullPath; + string artifactName = "nugets"; + await context.GitHubActions() + .Commands + .UploadArtifact(path, artifactName) + .ConfigureAwait(true); + } +} diff --git a/build/Tasks/PackTask.cs b/build/Tasks/PackTask.cs new file mode 100644 index 0000000..4dcb248 --- /dev/null +++ b/build/Tasks/PackTask.cs @@ -0,0 +1,23 @@ +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Pack; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("Pack")] +public sealed class PackTask : FrostingTask +{ + public override void Run(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + DotNetPackSettings settings = new DotNetPackSettings() + { + MSBuildSettings = context.DotNetMSBuildSettings, + Verbosity = DotNetVerbosity.Minimal, + OutputDirectory = context.NuGetsDirectory, + Configuration = context.BuildConfiguration + }; + + context.DotNetPack("./source/AsepriteDotNet/AsepriteDotNet.csproj", settings); + } +} diff --git a/build/Tasks/Tasks.cs b/build/Tasks/Tasks.cs new file mode 100644 index 0000000..04223ea --- /dev/null +++ b/build/Tasks/Tasks.cs @@ -0,0 +1,9 @@ +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("Default")] +[IsDependentOn(typeof(BuildTask))] +[IsDependentOn(typeof(TestTask))] +[IsDependentOn(typeof(PackTask))] +public sealed class DefaultTask : FrostingTask { } diff --git a/build/Tasks/TestTask.cs b/build/Tasks/TestTask.cs new file mode 100644 index 0000000..ab6e1ac --- /dev/null +++ b/build/Tasks/TestTask.cs @@ -0,0 +1,24 @@ +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Build; +using Cake.Common.Tools.DotNet.Test; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("Test")] +public sealed class TestTask : FrostingTask +{ + public override void Run(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + + DotNetTestSettings settings = new DotNetTestSettings() + { + MSBuildSettings = context.DotNetMSBuildSettings, + Verbosity = DotNetVerbosity.Minimal, + Configuration = context.BuildConfiguration + }; + + context.DotNetTest("./tests/AsepriteDotNet.Tests/AsepriteDotNet.Tests.csproj", settings); + } +} From f54c7a7004236a8931681ef9941af87e0c04fef1 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:32:30 -0400 Subject: [PATCH 7/9] Depends on DownloadArtifacts --- build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs b/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs index fe458f5..f86fb1b 100644 --- a/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs +++ b/build/Tasks/GitHubTasks/DeployNugetsToGithubTask.cs @@ -9,6 +9,7 @@ namespace AsepriteDotNet.Build; [TaskName("DeployNugetsToGithub")] +[IsDependentOn(typeof(DownloadArtifactsTask))] public sealed class DeployNugetsToGitHubTask : FrostingTask { public override bool ShouldRun(BuildContext context) => context.BuildSystem().IsRunningOnGitHubActions; From eb06cee2b968f3c8ed741e8a54e9f7bf0399a052 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:32:40 -0400 Subject: [PATCH 8/9] Added DeployToNugets task --- build/Tasks/GitHubTasks/DeployNugetsTask.cs | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 build/Tasks/GitHubTasks/DeployNugetsTask.cs diff --git a/build/Tasks/GitHubTasks/DeployNugetsTask.cs b/build/Tasks/GitHubTasks/DeployNugetsTask.cs new file mode 100644 index 0000000..e3c9377 --- /dev/null +++ b/build/Tasks/GitHubTasks/DeployNugetsTask.cs @@ -0,0 +1,29 @@ +using Cake.Common; +using Cake.Common.Build; +using Cake.Common.IO; +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.NuGet.Push; +using Cake.Core.IO; +using Cake.Frosting; + +namespace AsepriteDotNet.Build; + +[TaskName("DeployNuGets")] +[IsDependentOn(typeof(DownloadArtifactsTask))] +public sealed class DeployNugetsTask : FrostingTask +{ + public override bool ShouldRun(BuildContext context) => context.BuildSystem().IsRunningOnGitHubActions; + + public override void Run(BuildContext context) + { + ArgumentNullException.ThrowIfNull(context); + + DotNetNuGetPushSettings settings = new DotNetNuGetPushSettings() + { + ApiKey = context.EnvironmentVariable("NUGET_API_KEY"), + Source = "https://api.nuget.org/v3/index.json" + }; + + context.DotNetNuGetPush("nugets/*.nupkg", settings); + } +} From c1509eeb55982f588d322c2d203c0149963dcaf0 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Mon, 18 Mar 2024 23:32:52 -0400 Subject: [PATCH 9/9] Added github action workflow --- .github/workflows/main.yml | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..684dd70 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,83 @@ +name: Build AsepriteDotNEt + +on: + pull_request: + branches: [ $default-branch ] + push: + branches: + - $default-branch + tags: + - 'version*' + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Expose GitHub Runtime + uses: crazy-max/ghaction-github-runtime@v3 + + - name: Clone Repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Build + run: dotnet run --project build/AsperiteDotNet.Build.csproj -- --target=Default + + - name: Upload Artifacts + run: dotnet run --project build/AsepriteDotNet.Build.csproj -- --target=UploadArtifacts + env: + ACTIONS_RUNTIME_TOKEN: ${{ env.ACTIONS_RUNTIME_TOKEN }} + ACTIONS_RUNTIME_URL: "${{ env.ACTIONS_RUNTIME_URL }}" + + deploy-to-github: + name: Deploy To GitHub + needs: [ build ] + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + permissions: + packages: write + contents: write + steps: + - name: Clone Repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: PUsh GitHub NuGets + run: dotnet run --project build/AsepriteDotNet.Build.csproj -- --target=DeployNuGetsToGitHub + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + deploy-to-nuget: + name: Deploy To NuGet + needs: [ build ] + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/version') }} + permissions: + packages: write + contents: write + steps: + - name: Clone Repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Push NuGets + run: dotnet run --project build/AsepriteDotNet.Build.csproj -- --target=DeployNuGets + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + + +