{
    "componentChunkName": "component---src-templates-issues-tsx",
    "path": "/issues/569",
    "result": {"data":{"issuesJson":{"id":"d17c7839-ca84-511f-81c0-f167d5ec8281","title":"stepchowfun/toast: 一个基于名为 toastfile 的 YAML 文件中定义任务，用于容器化工作流程的工具。","number":569,"bodyHTML":"<div align=\"center\" dir=\"auto\">\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://camo.githubusercontent.com/a15b413168923d02e21292705ceaf86b92675f98e250c5c896a02ba7504a6eae/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f657279616a662f7475406d61696e2f696d672f696d6167655f32303234303432305f3231343430382e676966\"><img src=\"https://camo.githubusercontent.com/a15b413168923d02e21292705ceaf86b92675f98e250c5c896a02ba7504a6eae/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f657279616a662f7475406d61696e2f696d672f696d6167655f32303234303432305f3231343430382e676966\" width=\"100%\" height=\"3\" data-animated-image=\"\" data-canonical-src=\"https://cdn.jsdelivr.net/gh/eryajf/tu@main/img/image_20240420_214408.gif\" style=\"max-width: 100%; height: auto; max-height: 3px;\"></a><br><br></p>\n<markdown-accessiblity-table><table role=\"table\">\n<thead>\n<tr>\n<th align=\"right\">Repos</th>\n<th align=\"left\"><a href=\"https://github.com/stepchowfun/toast\">stepchowfun/toast</a></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"right\"><strong>Views</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/f5d186b86e450516d0743eb1a1f9d315519a1bbbc24d578ccba946ced350cd5d/68747470733a2f2f76696577732e77686174696c656172656e65642e746f6461792f76696577732f6769746875622f7374657063686f7766756e2f746f6173742e737667\" alt=\"views\" data-canonical-src=\"https://views.whatilearened.today/views/github/stepchowfun/toast.svg\" style=\"max-width: 100%;\"></a></td>\n</tr>\n<tr>\n<td align=\"right\"><strong>Stars</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/23884cfcc9687dee8c8afdb7ab22cfc64744367b446383a60a462b7ad9707021/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f7374657063686f7766756e2f746f6173743f636f6c6f723d663266303864266c6f676f3d556e64657274616c65266c6f676f436f6c6f723d656234363330\" alt=\"stars\" data-canonical-src=\"https://img.shields.io/github/stars/stepchowfun/toast?color=f2f08d&amp;logo=Undertale&amp;logoColor=eb4630\" style=\"max-width: 100%;\"></a></td>\n</tr>\n<tr>\n<td align=\"right\"><strong>Forks</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/a500e6cc55578c4b5a76a47f68905f273292cc2bffa0ed92d73c4a800691caa4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f7374657063686f7766756e2f746f6173743f636f6c6f723d626138366562266c6f676f3d48616e647368616b65266c6f676f436f6c6f723d656136616136\" alt=\"forks\" data-canonical-src=\"https://img.shields.io/github/forks/stepchowfun/toast?color=ba86eb&amp;logo=Handshake&amp;logoColor=ea6aa6\" style=\"max-width: 100%;\"></a></td>\n</tr>\n<tr>\n<td align=\"right\"><strong>License</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/0c93ca71e73bb599dbfbd81eb5bbb82f63a427dc55ced5d8a7de2d4025f1f1c5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c2d4e4f4e452d626c75653f6c6f676f3d756e6c6963656e7365\" alt=\"license\" data-canonical-src=\"https://img.shields.io/badge/L-NONE-blue?logo=unlicense\" style=\"max-width: 100%;\"></a></td>\n</tr>\n<tr>\n<td align=\"right\"><strong>UpdatedAt</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/28250a7a81bd0c1457d179639dffecfc3058670843c515f38b4b3dc9f0c202c2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f7374657063686f7766756e2f746f6173743f646973706c61795f74696d657374616d703d636f6d6d6974746572266c6f676f3d64617461253341696d616765253246706e672533426261736536342532436956424f5277304b47676f414141414e5355684555674141414234414141416543415941414141374d4b3669414141414358424957584d41414173544141414c457745416d7077594141414648556c45515652346e4c5658375539615678776d3236646c582532466468627825324232253246516e626b7655627264554151674a4a496655444e574454546d4f76314651554661504a346b7573725630314a7431734e70637453327533626f745a746d786439744a5346536f714b71494635625944464c436f76436c656673747a4a387758524c445a4c7a6e6b634f343535376e3364332532466e655a3472454f515a74315771462532423858467234253242664f4c4565326a6f59307a776638546f795a50766a45676b4c592532426b306d6d72584f36644b696e787a476b30666a5430782532425279723055716e6359637a4d317263347451253242497270324c4758646f344e4334567657715453373277716c632532427231334f527269364b6458646e624f484c6c776c7a4a70524b6e3657342532424b36706f4f434e6e494166486a253246253242776168495a4234764b6e715a6630714a704e49716c2532467638545530634e6c3572627963337730516e54703379324c54614a374d314e617844723266526e315370504736476961323174664533345463614f617931694d55564f5146624659714957536f31573854694156616e43306576584f4542375770313448466232327a453566714c4f433545524c515769644e614e49347545636339693768634a6c644868324e57725135674464624f6c7061756d49754c62326174415144506e7a3062384e6257636d785656524a33486d7870536377777a4f4a6d4b44524765384c6c435a444c4739673754496e5631516c376462586256316533675430574b697243466f6d6b5079767758466c5a474a4f6a563625324253743634757564445a4f55354555636f782532464d455144567674364d62634e3237593341797a687633737061557242365a3971724a534f3139657a6a387032704c426b4a792532426343484a7857493567573574636652527a31645531394766476b6f7539505a4f34736d52396a4746777065783444793362766e6d4b79706f74625531335669646a71595a68726a343972764d457425324625324659714c7a396430304e6a57253246637a67796f394f35317a7336614e6c6f354644747530445a675146686c47584a30644241446f4e6864367576703657686f617967374e394c564748386d443462253246486e667455516f4e4734764c51306769784d716c5725324234714f6a744e48446733723166365969525347785279375576714c62395534716b716e78504f4e7661356c447048723125324279794b524e4b57425a793565644d554842326d3572792532422532424551772532427a6766347a6f39253246306f634e3357527a4c4277344a2532424a7950574372716d4967475974554f735744346f785a46516f50556a4770564871493425324661666b51504336665a5165654d31253242764c7549516e6a754257514444437363726d485039656f4e5041744271664f6e5875614b25324268364a45624772732532427076764d6d78654962683836336162565065597953456f39464a48704e4d464a59253242503563575a6b6667374f3174537759436553514c54593345385130392532464256504a556c78516a7368543142736343593032723955445542667643484236367063665041336c794165336e6772332532463437584467614a7a736c7937787741364e5a746c555550437541487161536a5549502532427375653842525741432532464e6651374a5a505a35397330476a3756747049537a34685125324243706658474d4b685a63764c70554b7866574d386f68766637702532464f446a4842666356463849736b383267314648796b63564645253242555a414158343761452532464d6c34504f3530506e75683025324678346e6d637957507364774468427836436d6b4c562532466746446a594b784f4a4f4674623077517949684c39527943774b33414f664947703159464542686e4d4a634c525745624b644b516f55366e63545a6b494544696341792532463831645675454d3552775066657934784f7830496b25324649324e6e466b692532425761664f6f464959466367595a43797864376543556a626334416d46363566742532466b4d686b336f25324235686337734d4a79716a4a45477659466151464972375930775077384246415925324236253242766d6b3377367a7a5275444d6d5a5652736668385274413075455453443775434254364459514e36697665553066706b594c6a453671725672744f78654e4b553954484c5a4a396b4255324a427342685635416976422532466f4b61514e4b675043427744596149665a4334616454684f71463457454e56694c4a77566f586f62664c424b56773637414f6154734c6336355461564b32317651344c613939654b6359673576627873624f627a5451394e37554b4467554f3177446a6944494942736868357a746733396e514d4c535a4248344f7a424f55444551586d323036643366634c67737762585141345068634b336e6873775531427a3877765130395248472532466f597933656a6677424e6d746f59714c4130586741414141424a52553545726b4a676767253344253344266c6162656c3d557064617465644174\" alt=\"last-commit\" data-canonical-src=\"https://img.shields.io/github/last-commit/stepchowfun/toast?display_timestamp=committer&amp;logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHUlEQVR4nLVX7U9aVxwm26dlX%2Fdhbx%2B2%2FQnbkvUbrdUAQgJJIfUDNWDTTmOv1FQUFaPJ4kusrV01Jt1sNpctS2u3botZtmxd9tJSFSoqKqIF5bYDFLCovClefstzJ8wXRLDZLznkcO4557n3d3%2FneZ4rEOQZt1WqF%2B8XFr4%2BfOLEe2joY0zwf8ToyZPvjEgkLY%2Bk0mmrXO6dKinxzGk0fjT0x%2BRyr0UqncYczM1rc4tQ%2BIrp2LGXdo4NC4VvWqTS72wqlc%2Br13ORri6KdXdnbOHLlwlzJpRKn6W4%2BK6poOCNnIAfHj%2F%2BwahIZB4vKnqZf0qJpNIql%2Fv8TU0cNl5rbyc3w0QnTp3y2LTaJ7M1NaxDr2fRn1SpPG6Gia21tfE34TcaOay1iMUVOQFbFYqIWSo1W8TiAVanC0evXOEB7Wp14HFb22zE5fqLOC5ERLQWidNaNI4uEcc9i7hcJldHh2NWrQ5gDdbOlpaumIuLb2atAQDPnz0b8NbWcmxVVRJ3HmxpScwwzOJmKDRGe8LlCZDLG9g7TInV1Ql7dbXbV1e3gT0WKirCFomkPyvwXFlZGJOjV6%2BSt64uudDZOU5EUcox%2FMEQDVvt6MbcN27Y3Ayzhv3spaUrB6Z9qrJSO19ezj8p2pLBkJy%2BcCHJxWI5gW5tcfRRz1dU19GfGkou9PZO4smR9jGFwpex4Dy3bvnmKypotbU13VidjqYZhrj49rvMEt%2F%2FYqLz9d00NjW%2Fczgyo9O51zs6aNlo5FDtu0DZgQFhlGXJ0dBADoNhd6uvp6Whoayg7N9LVGH8mD4b%2FHnftUQoNG4vLQ0gixMqlW%2B4qOjtNHDg3r1f6YiRSGxRy7UvqLb9U4qkqnxPONva5lDpHr1%2ByyKRNKWBZy5edMUHB2m5ry%2B%2BEQw%2Bzgf4zo9%2F0ocN3WRzLBw4J%2BJyPWCrqmIgGYtUOsWD4oxZFQoPUjGpVHqI4%2FafkQPC6fZQeeM1%2BvLuIQnjuBWQDDCscrmHP9eoNPAtBqfOnXuaK%2Bh6JEbGrs%2BpvvMmxeIbh863abVPeYySEo9FJHpNMFJY%2BP5cWZkfg7O1tSwYCeSQLTY3E8Q09%2FBVPJUlxQjshT1BscCY02r9UDUBfvCHB66pcfPA3lyAe3ngr3%2F47XDgaJzsly7xwA6NZtlUUPCuAHqaSjUIP%2Bsue8BRWAC%2FNfQ7JZPZ59s0Gj7VtpISz4hQ%2BCpfXGMKhZcvLpUKxfWM8ohvf7p%2FODjHBfcVF8Isk82g1FHykcVFE%2BUZAAX47aE%2FMl4PO50Pnuh0%2Fx4nmcyWPsdwDhBx6CmkLV%2FgFDjYKxOJOFtb0wQyIhL9RyCwK3AOfIGp1YFEBhnMJcLRWEbKdKQoU6ncTZkIEDicAy%2F81dVuEM5RwPfey4xOx0Ik%2FI2NnFki%2BWafOoFIYFcgYZCyxd7eCUjbc4AmF65ft%2FkMhk3o%2B5hc7sMJyqjJEGvYFaQFIr7Y0wPw8BFAY%2B6%2Bvmk3w6zzRuDMmZVRsfh8RtA0uETSD7uCBT6DYQN6iveU0fpkYLjE6qrVrtOxeNKU9THLZJ9kBU2JBsBhV5AivB%2FoKaQNKgPCBwDYaIfZC4adThOqF4WENViLJwVoXobfLBKVw67AOaTsLc65TaVK21vQ4La99eKcYg5vbxsbObzTQ9N7UKDgUO1wDjiDIIBshh5ztg39nQMLSZBH4OzBOUDEQXm206d3fcLgswbXQA4PhcK3nhswU1Bz8wvQ09RHG%2FoYy3ejfwBNmtoYqLA0XgAAAABJRU5ErkJggg%3D%3D&amp;label=UpdatedAt\" style=\"max-width: 100%;\"></a></td>\n</tr>\n<tr>\n<td align=\"right\"><strong>CreatedAt</strong></td>\n<td align=\"left\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/0653eb897759d994d4bc2600640210de14e4492fe7cc189233367eaa35d24b5e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f637265617465642d61742f7374657063686f7766756e2f746f6173743f6c6f676f3d64617461253341696d616765253246706e672533426261736536342532436956424f5277304b47676f414141414e5355684555674141414234414141416543415941414141374d4b3669414141414358424957584d41414173544141414c457745416d7077594141414648556c45515652346e4c5658375539615678776d3236646c582532466468627825324232253246516e626b7655627264554151674a4a496655444e574454546d4f76314651554661504a346b7573725630314a7431734e70637453327533626f745a746d786439744a5346536f714b71494635625944464c436f76436c656673747a4a387758524c445a4c7a6e6b634f343535376e3364332532466e655a3472454f515a74315771462532423858467234253242664f4c4565326a6f59307a776638546f795a50766a45676b4c592532426b306d6d72584f36644b696e787a476b30666a5430782532425279723055716e6359637a4d317263347451253242497270324c4758646f344e4334567657715453373277716c632532427231334f527269364b6458646e624f484c6c776c7a4a70524b6e3657342532424b36706f4f434e6e494166486a253246253242776168495a4234764b6e715a6630714a704e49716c2532467638545530634e6c3572627963337730516e54703379324c54614a374d314e617844723266526e315370504736476961323174664533345463614f617931694d55564f5146624659714957536f31573854694156616e43306576584f4542375770313448466232327a453566714c4f433545524c515769644e614e49347545636339693768634a6c644868324e57725135674464624f6c7061756d49754c62326174415144506e7a3062384e6257636d785656524a33486d7870536377777a4f4a6d4b44524765384c6c435a444c4739673754496e5631516c376462586256316533675430574b697243466f6d6b5079767758466c5a474a4f6a563625324253743634757564445a4f55354555636f782532464d455144567674364d62634e3237593341797a687633737061557242365a3971724a534f3139657a6a387032704c426b4a792532426343484a7857493567573574636652527a31645531394766476b6f7539505a4f34736d52396a4746777065783444793362766e6d4b79706f74625531335669646a71595a68726a343972764d457425324625324659714c7a396430304e6a57253246637a67796f394f35317a7336614e6c6f354644747530445a675146686c47584a30644241446f4e6864367576703657686f617967374e394c564748386d443462253246486e667455516f4e4734764c51306769784d716c5725324234714f6a744e48446733723166365969525347785279375576714c62395534716b716e78504f4e7661356c447048723125324279794b524e4b57425a793565644d554842326d3572792532422532424551772532427a6766347a6f39253246306f634e3357527a4c4277344a2532424a7950574372716d4967475974554f735744346f785a46516f50556a4770564871493425324661666b51504336665a5165654d31253242764c7549516e6a754257514444437363726d485039656f4e5041744271664f6e5875614b25324268364a45624772732532427076764d6d78654962683836336162565065597953456f39464a48704e4d464a59253242503563575a6b6667374f3174537759436553514c54593345385130392532464256504a556c78516a7368543142736343593032723955445542667643484236367063665041336c794165336e6772332532463437584467614a7a736c7937787741364e5a746c555550437541487161536a5549502532427375653842525741432532464e6651374a5a505a35397330476a3756747049537a34685125324243706658474d4b685a63764c70554b7866574d386f68766637702532464f446a4842666356463849736b383267314648796b63564645253242555a414158343761452532464d6c34504f3530506e75683025324678346e6d637957507364774468427836436d6b4c562532466746446a594b784f4a4f4674623077517949684c39527943774b33414f664947703159464542686e4d4a634c525745624b644b516f55366e63545a6b494544696341792532463831645675454d3552775066657934784f7830496b25324649324e6e466b692532425761664f6f464959466367595a43797864376543556a626334416d46363566742532466b4d686b336f25324235686337734d4a79716a4a45477659466151464972375930775077384246415925324236253242766d6b3377367a7a5275444d6d5a5652736668385274413075455453443775434254364459514e36697665553066706b594c6a453671725672744f78654e4b553954484c5a4a396b4255324a427342685635416976422532466f4b61514e4b675043427744596149665a4334616454684f71463457454e56694c4a77566f586f62664c424b56773637414f6154734c6336355461564b32317651344c613939654b6359673576627873624f627a5451394e37554b4467554f3177446a6944494942736868357a746733396e514d4c535a4248344f7a424f55444551586d323036643366634c67737762585141345068634b336e6873775531427a3877765130395248472532466f597933656a6677424e6d746f59714c4130586741414141424a52553545726b4a676767253344253344266c6162656c3d437265617465644174\" alt=\"create-at\" data-canonical-src=\"https://img.shields.io/github/created-at/stepchowfun/toast?logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHUlEQVR4nLVX7U9aVxwm26dlX%2Fdhbx%2B2%2FQnbkvUbrdUAQgJJIfUDNWDTTmOv1FQUFaPJ4kusrV01Jt1sNpctS2u3botZtmxd9tJSFSoqKqIF5bYDFLCovClefstzJ8wXRLDZLznkcO4557n3d3%2FneZ4rEOQZt1WqF%2B8XFr4%2BfOLEe2joY0zwf8ToyZPvjEgkLY%2Bk0mmrXO6dKinxzGk0fjT0x%2BRyr0UqncYczM1rc4tQ%2BIrp2LGXdo4NC4VvWqTS72wqlc%2Br13ORri6KdXdnbOHLlwlzJpRKn6W4%2BK6poOCNnIAfHj%2F%2BwahIZB4vKnqZf0qJpNIql%2Fv8TU0cNl5rbyc3w0QnTp3y2LTaJ7M1NaxDr2fRn1SpPG6Gia21tfE34TcaOay1iMUVOQFbFYqIWSo1W8TiAVanC0evXOEB7Wp14HFb22zE5fqLOC5ERLQWidNaNI4uEcc9i7hcJldHh2NWrQ5gDdbOlpaumIuLb2atAQDPnz0b8NbWcmxVVRJ3HmxpScwwzOJmKDRGe8LlCZDLG9g7TInV1Ql7dbXbV1e3gT0WKirCFomkPyvwXFlZGJOjV6%2BSt64uudDZOU5EUcox%2FMEQDVvt6MbcN27Y3Ayzhv3spaUrB6Z9qrJSO19ezj8p2pLBkJy%2BcCHJxWI5gW5tcfRRz1dU19GfGkou9PZO4smR9jGFwpex4Dy3bvnmKypotbU13VidjqYZhrj49rvMEt%2F%2FYqLz9d00NjW%2Fczgyo9O51zs6aNlo5FDtu0DZgQFhlGXJ0dBADoNhd6uvp6Whoayg7N9LVGH8mD4b%2FHnftUQoNG4vLQ0gixMqlW%2B4qOjtNHDg3r1f6YiRSGxRy7UvqLb9U4qkqnxPONva5lDpHr1%2ByyKRNKWBZy5edMUHB2m5ry%2B%2BEQw%2Bzgf4zo9%2F0ocN3WRzLBw4J%2BJyPWCrqmIgGYtUOsWD4oxZFQoPUjGpVHqI4%2FafkQPC6fZQeeM1%2BvLuIQnjuBWQDDCscrmHP9eoNPAtBqfOnXuaK%2Bh6JEbGrs%2BpvvMmxeIbh863abVPeYySEo9FJHpNMFJY%2BP5cWZkfg7O1tSwYCeSQLTY3E8Q09%2FBVPJUlxQjshT1BscCY02r9UDUBfvCHB66pcfPA3lyAe3ngr3%2F47XDgaJzsly7xwA6NZtlUUPCuAHqaSjUIP%2Bsue8BRWAC%2FNfQ7JZPZ59s0Gj7VtpISz4hQ%2BCpfXGMKhZcvLpUKxfWM8ohvf7p%2FODjHBfcVF8Isk82g1FHykcVFE%2BUZAAX47aE%2FMl4PO50Pnuh0%2Fx4nmcyWPsdwDhBx6CmkLV%2FgFDjYKxOJOFtb0wQyIhL9RyCwK3AOfIGp1YFEBhnMJcLRWEbKdKQoU6ncTZkIEDicAy%2F81dVuEM5RwPfey4xOx0Ik%2FI2NnFki%2BWafOoFIYFcgYZCyxd7eCUjbc4AmF65ft%2FkMhk3o%2B5hc7sMJyqjJEGvYFaQFIr7Y0wPw8BFAY%2B6%2Bvmk3w6zzRuDMmZVRsfh8RtA0uETSD7uCBT6DYQN6iveU0fpkYLjE6qrVrtOxeNKU9THLZJ9kBU2JBsBhV5AivB%2FoKaQNKgPCBwDYaIfZC4adThOqF4WENViLJwVoXobfLBKVw67AOaTsLc65TaVK21vQ4La99eKcYg5vbxsbObzTQ9N7UKDgUO1wDjiDIIBshh5ztg39nQMLSZBH4OzBOUDEQXm206d3fcLgswbXQA4PhcK3nhswU1Bz8wvQ09RHG%2FoYy3ejfwBNmtoYqLA0XgAAAABJRU5ErkJggg%3D%3D&amp;label=CreatedAt\" style=\"max-width: 100%;\"></a></td>\n</tr>\n</tbody>\n</table></markdown-accessiblity-table>\n<a href=\"https://github.com/opsre/awesome-ops\">\n</a><p dir=\"auto\"><a href=\"https://github.com/opsre/awesome-ops\"><img src=\"https://camo.githubusercontent.com/b964a36ea4078c39f603d46a29436371c7541a2f26e0228d7b21b9d9805b43d3/68747470733a2f2f736f6369616c6966792e6769742e63692f6f707372652f617765736f6d652d6f70732f696d6167653f6465736372697074696f6e3d3126666f6e743d42697474657226666f726b733d31266973737565733d31266c616e67756167653d31266c6f676f3d6874747073253341253246253246617661746172732e67697468756275736572636f6e74656e742e636f6d25324675253246313838353638303230266e616d653d31266f776e65723d31267061747465726e3d436972637569742b426f6172642670756c6c733d31267374617267617a6572733d31267468656d653d4c69676874\" alt=\"\" data-canonical-src=\"https://socialify.git.ci/opsre/awesome-ops/image?description=1&amp;font=Bitter&amp;forks=1&amp;issues=1&amp;language=1&amp;logo=https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F188568020&amp;name=1&amp;owner=1&amp;pattern=Circuit+Board&amp;pulls=1&amp;stargazers=1&amp;theme=Light\" style=\"max-width: 100%;\"></a></p>\n<a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://camo.githubusercontent.com/a15b413168923d02e21292705ceaf86b92675f98e250c5c896a02ba7504a6eae/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f657279616a662f7475406d61696e2f696d672f696d6167655f32303234303432305f3231343430382e676966\"><img src=\"https://camo.githubusercontent.com/a15b413168923d02e21292705ceaf86b92675f98e250c5c896a02ba7504a6eae/68747470733a2f2f63646e2e6a7364656c6976722e6e65742f67682f657279616a662f7475406d61696e2f696d672f696d6167655f32303234303432305f3231343430382e676966\" width=\"100%\" height=\"3\" data-animated-image=\"\" data-canonical-src=\"https://cdn.jsdelivr.net/gh/eryajf/tu@main/img/image_20240420_214408.gif\" style=\"max-width: 100%; height: auto; max-height: 3px;\"></a>\n</div>\n<h1 dir=\"auto\">Toast 🥂</h1>\n<p dir=\"auto\"><a href=\"https://github.com/stepchowfun/toast/actions?query=branch%3Amain\"><img src=\"https://github.com/stepchowfun/toast/actions/workflows/ci.yml/badge.svg?branch=main\" alt=\"Build status\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\"><em>Toast</em> is a tool for containerizing your workflows such as building and testing a project. You define tasks in a YAML file called a <em>toastfile</em>, and Toast runs them in a container based on a Docker image of your choosing. What constitutes a \"task\" is up to you: tasks can install system packages, compile an application, run a test suite, or even serve web pages. Tasks can depend on other tasks, so Toast can be understood as a high-level containerized build system.</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/welcome-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/welcome-0.svg?sanitize=true\" alt=\"Welcome to Toast.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">Here's the toastfile for the example shown above:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_gcc:\n    command: |\n      apt-get update\n      apt-get install --yes gcc\n\n  build:\n    dependencies:\n      - install_gcc\n    input_paths:\n      - main.c\n    command: gcc main.c\n\n  run:\n    dependencies:\n      - build\n    command: ./a.out\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_gcc</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes gcc</span>\n<span class=\"pl-s\"></span>\n<span class=\"pl-s\"></span>  <span class=\"pl-ent\">build</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">install_gcc</span>\n    <span class=\"pl-ent\">input_paths</span>:\n      - <span class=\"pl-s\">main.c</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">gcc main.c</span>\n\n  <span class=\"pl-ent\">run</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">build</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">./a.out</span></pre></div>\n<p dir=\"auto\">Toast caches each task by committing the container to an image. The image is tagged with a cryptographic hash of the shell command for the task, the contents of the files copied into the container, and all the other task inputs. This hash allows Toast to skip tasks that haven't changed since the last run.</p>\n<p dir=\"auto\">In addition to local caching, Toast can use a Docker registry as a remote cache. You, your teammates, and your continuous integration (CI) system can all share the same remote cache. Used in this way, your CI system can do all the heavy lifting like building and installing dependencies so you and your team can focus on development.</p>\n<p dir=\"auto\">Related tools:</p>\n<ul dir=\"auto\">\n<li><a href=\"https://docs.docker.com/compose/\" rel=\"nofollow\">Docker Compose</a>: Docker Compose is a convenient Docker-based development environment which shares many features with Toast. However, it doesn't support defining tasks (like <code class=\"notranslate\">lint</code>, <code class=\"notranslate\">test</code>, <code class=\"notranslate\">run</code>, etc.) or remote caching.</li>\n<li><a href=\"https://nixos.org/nix/\" rel=\"nofollow\">Nix</a>: Nix achieves reproducible builds by leveraging ideas from functional programming rather than containerization. We're big fans of Nix. However, Nix requires a larger commitment compared to Toast because you have to use the Nix package manager or write your own Nix derivations. For better or worse, Toast allows you to use familiar idioms like <code class=\"notranslate\">apt-get install ...</code>.</li>\n</ul>\n<p dir=\"auto\">To prevent Docker images from accumulating on your machine when using Docker-related tools such as Toast or Docker Compose, we recommend using <a href=\"https://github.com/stepchowfun/docuum\">Docuum</a> to perform least recently used (LRU) image eviction.</p>\n<h2 dir=\"auto\">Tutorial</h2>\n<h3 dir=\"auto\">Defining a simple task</h3>\n<p dir=\"auto\">Let's create a toastfile. Create a file named <code class=\"notranslate\">toast.yml</code> with the following contents:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  greet:\n    command: echo 'Hello, World!' # Toast will run this in a container.\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">greet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">echo 'Hello, World!' </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Toast will run this in a container.</span></pre></div>\n<p dir=\"auto\">Now run <code class=\"notranslate\">toast</code>. You should see the following:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/simple-task-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/simple-task-0.svg?sanitize=true\" alt=\"Defining a simple task.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">If you run it again, Toast will find that nothing has changed and skip the task:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/caching-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/caching-0.svg?sanitize=true\" alt=\"Caching.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">Toast caches tasks to save you time. For example, you don't want to reinstall your dependencies every time you run your tests. However, caching may not be appropriate for some tasks, like running a development server. You can disable caching for a specific task and all tasks that depend on it with the <code class=\"notranslate\">cache</code> option:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  greet:\n    cache: false # Don't cache this task.\n    command: echo 'Hello, World!'\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">greet</span>:\n    <span class=\"pl-ent\">cache</span>: <span class=\"pl-s\">false </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Don't cache this task.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">echo 'Hello, World!'</span></pre></div>\n<h3 dir=\"auto\">Adding a dependency</h3>\n<p dir=\"auto\">Let's make the greeting more fun with a program called <code class=\"notranslate\">figlet</code>. We'll add a task to install <code class=\"notranslate\">figlet</code>, and we'll change the <code class=\"notranslate\">greet</code> task to depend on it:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_figlet:\n    command: |\n      apt-get update\n      apt-get install --yes figlet\n\n  greet:\n    dependencies:\n      - install_figlet # Toast will run this task first.\n    command: figlet 'Hello, World!'\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_figlet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes figlet</span>\n<span class=\"pl-s\"></span>\n<span class=\"pl-s\"></span>  <span class=\"pl-ent\">greet</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">install_figlet </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Toast will run this task first.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">figlet 'Hello, World!'</span></pre></div>\n<p dir=\"auto\">Run <code class=\"notranslate\">toast</code> to see a marvelous greeting:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/dependencies-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/dependencies-0.svg?sanitize=true\" alt=\"Adding a dependency.\" style=\"max-width: 100%;\"></a></p>\n<h3 dir=\"auto\">Importing files from the host</h3>\n<p dir=\"auto\">Here's a more realistic example. Suppose you want to compile and run a simple C program. Create a file called <code class=\"notranslate\">main.c</code>:</p>\n<div class=\"highlight highlight-source-c notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"#include &lt;stdio.h&gt;\n\nint main(void) {\n  printf(&quot;Hello, World!\\n&quot;);\n  return 0;\n}\"><pre class=\"notranslate\"><span class=\"pl-k\">#include</span> <span class=\"pl-s\">&lt;stdio.h&gt;</span>\n\n<span class=\"pl-smi\">int</span> <span class=\"pl-en\">main</span>(<span class=\"pl-smi\">void</span>) {\n  <span class=\"pl-en\">printf</span>(<span class=\"pl-s\">\"Hello, World!\\n\"</span>);\n  <span class=\"pl-k\">return</span> <span class=\"pl-c1\">0</span>;\n}</pre></div>\n<p dir=\"auto\">Update <code class=\"notranslate\">toast.yml</code> to compile and run the program:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_gcc:\n    command: |\n      apt-get update\n      apt-get install --yes gcc\n\n  build:\n    dependencies:\n      - install_gcc\n    input_paths:\n      - main.c # Toast will copy this file into the container before running the command.\n    command: gcc main.c\n\n  run:\n    dependencies:\n      - build\n    command: ./a.out\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_gcc</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes gcc</span>\n<span class=\"pl-s\"></span>\n<span class=\"pl-s\"></span>  <span class=\"pl-ent\">build</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">install_gcc</span>\n    <span class=\"pl-ent\">input_paths</span>:\n      - <span class=\"pl-s\">main.c </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Toast will copy this file into the container before running the command.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">gcc main.c</span>\n\n  <span class=\"pl-ent\">run</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">build</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">./a.out</span></pre></div>\n<p dir=\"auto\">Notice the <code class=\"notranslate\">input_paths</code> array in the <code class=\"notranslate\">build</code> task. Here we're copying a single file into the container, but we could instead import the entire directory containing the toastfile with <code class=\"notranslate\">.</code>. By default, the files will be copied into a directory called <code class=\"notranslate\">/scratch</code> in the container. The commands will be run in that directory as well.</p>\n<p dir=\"auto\">Now if you run <code class=\"notranslate\">toast</code>, you'll see this:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/input-paths-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/input-paths-0.svg?sanitize=true\" alt=\"Importing files from the host.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">For subsequent runs, Toast will skip the task if nothing has changed. But if you update the greeting in <code class=\"notranslate\">main.c</code>, Toast will detect the change and rerun the <code class=\"notranslate\">build</code> and <code class=\"notranslate\">run</code> tasks on the next invocation.</p>\n<h3 dir=\"auto\">Exporting files from the container</h3>\n<p dir=\"auto\">A common use case for Toast is to build a project. Naturally, you might wonder how to access the build artifacts produced inside the container from the host machine. It's easy to do with <code class=\"notranslate\">output_paths</code>:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_gcc:\n    command: |\n      apt-get update\n      apt-get install --yes gcc\n\n  build:\n    dependencies:\n      - install_gcc\n    input_paths:\n      - main.c\n    output_paths:\n      - a.out # Toast will copy this file onto the host after running the command.\n    command: gcc main.c\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_gcc</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes gcc</span>\n<span class=\"pl-s\"></span>\n<span class=\"pl-s\"></span>  <span class=\"pl-ent\">build</span>:\n    <span class=\"pl-ent\">dependencies</span>:\n      - <span class=\"pl-s\">install_gcc</span>\n    <span class=\"pl-ent\">input_paths</span>:\n      - <span class=\"pl-s\">main.c</span>\n    <span class=\"pl-ent\">output_paths</span>:\n      - <span class=\"pl-s\">a.out </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Toast will copy this file onto the host after running the command.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">gcc main.c</span></pre></div>\n<p dir=\"auto\">When Toast runs the <code class=\"notranslate\">build</code> task, it will copy the <code class=\"notranslate\">a.out</code> file to the host.</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/output-paths-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/output-paths-0.svg?sanitize=true\" alt=\"Exporting files from the container.\" style=\"max-width: 100%;\"></a></p>\n<h3 dir=\"auto\">Passing arguments to a task</h3>\n<p dir=\"auto\">Sometimes it's useful for tasks to take arguments. For example, a <code class=\"notranslate\">deploy</code> task might want to know whether you want to deploy to the <code class=\"notranslate\">staging</code> or <code class=\"notranslate\">production</code> cluster. To do this, add an <code class=\"notranslate\">environment</code> section to your task:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  deploy:\n    cache: false\n    environment:\n      CLUSTER: staging # Deploy to staging by default.\n    command: echo &quot;Deploying to $CLUSTER...&quot;\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">deploy</span>:\n    <span class=\"pl-ent\">cache</span>: <span class=\"pl-c1\">false</span>\n    <span class=\"pl-ent\">environment</span>:\n      <span class=\"pl-ent\">CLUSTER</span>: <span class=\"pl-s\">staging </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Deploy to staging by default.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">echo \"Deploying to $CLUSTER...\"</span></pre></div>\n<p dir=\"auto\">When you run this task, Toast will read the value from the environment:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/arguments-explicit-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/arguments-explicit-0.svg?sanitize=true\" alt=\"Passing arguments to a task.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">If the variable doesn't exist in the environment, Toast will use the default value:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/arguments-default-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/arguments-default-0.svg?sanitize=true\" alt=\"Using argument defaults.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">If you don't want to have a default, set it to <code class=\"notranslate\">null</code>:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  deploy:\n    cache: false\n    environment:\n      CLUSTER: null # No default; this variable must be provided at runtime.\n    command: echo &quot;Deploying to $CLUSTER...&quot;\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">deploy</span>:\n    <span class=\"pl-ent\">cache</span>: <span class=\"pl-c1\">false</span>\n    <span class=\"pl-ent\">environment</span>:\n      <span class=\"pl-ent\">CLUSTER</span>: <span class=\"pl-s\">null </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> No default; this variable must be provided at runtime.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">echo \"Deploying to $CLUSTER...\"</span></pre></div>\n<p dir=\"auto\">Now if you run <code class=\"notranslate\">toast deploy</code> without specifying a <code class=\"notranslate\">CLUSTER</code>, Toast will complain about the missing variable and refuse to run the task.</p>\n<p dir=\"auto\">Environment variables listed in a task are also set for any tasks that run after it.</p>\n<h3 dir=\"auto\">Running a server and mounting paths into the container</h3>\n<p dir=\"auto\">Toast can be used for more than just building a project. Suppose you're developing a website. You can define a Toast task to run your web server! Create a file called <code class=\"notranslate\">index.html</code> with the following contents:</p>\n<div class=\"highlight highlight-text-html-basic notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;title&gt;Welcome to Toast!&lt;/title&gt;\n  &lt;/head&gt;\n  &lt;body&gt;\n    &lt;p&gt;Hello, World!&lt;/p&gt;\n  &lt;/body&gt;\n&lt;/html&gt;\"><pre class=\"notranslate\"><span class=\"pl-c1\">&lt;!DOCTYPE html<span class=\"pl-kos\">&gt;</span></span>\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">html</span><span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">head</span><span class=\"pl-kos\">&gt;</span>\n    <span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">title</span><span class=\"pl-kos\">&gt;</span>Welcome to Toast!<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">title</span><span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">head</span><span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">body</span><span class=\"pl-kos\">&gt;</span>\n    <span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">p</span><span class=\"pl-kos\">&gt;</span>Hello, World!<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">p</span><span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">body</span><span class=\"pl-kos\">&gt;</span>\n<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">html</span><span class=\"pl-kos\">&gt;</span></pre></div>\n<p dir=\"auto\">We can use a web server like <a href=\"https://www.nginx.com/\" rel=\"nofollow\">nginx</a>. The official <code class=\"notranslate\">nginx</code> Docker image will do, but you could also use a more general image and define a Toast task to install nginx.</p>\n<p dir=\"auto\">In our <code class=\"notranslate\">toast.yml</code> file, we'll use the <code class=\"notranslate\">ports</code> field to make the website accessible outside the container. We'll also use <code class=\"notranslate\">mount_paths</code> rather than <code class=\"notranslate\">input_paths</code> so we can edit the web page without having to restart the server.</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: nginx\ntasks:\n  serve:\n    cache: false # It doesn't make sense to cache this task.\n    mount_paths:\n      - index.html # Updates to this file will be visible inside the container.\n    ports:\n      - 3000:80 # Expose port 80 in the container as port 3000 on the host.\n    location: /usr/share/nginx/html/ # Nginx will serve the files in here.\n    command: nginx -g 'daemon off;' # Run in foreground mode.\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">nginx</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">serve</span>:\n    <span class=\"pl-ent\">cache</span>: <span class=\"pl-s\">false </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> It doesn't make sense to cache this task.</span>\n    <span class=\"pl-ent\">mount_paths</span>:\n      - <span class=\"pl-s\">index.html </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Updates to this file will be visible inside the container.</span>\n    <span class=\"pl-ent\">ports</span>:\n      - <span class=\"pl-c1\">3000:80</span> <span class=\"pl-c\"><span class=\"pl-c\">#</span> Expose port 80 in the container as port 3000 on the host.</span>\n    <span class=\"pl-ent\">location</span>: <span class=\"pl-s\">/usr/share/nginx/html/ </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Nginx will serve the files in here.</span>\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">nginx -g 'daemon off;' </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Run in foreground mode.</span></pre></div>\n<p dir=\"auto\">Now you can use Toast to run the server:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/server-1.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/server-1.svg?sanitize=true\" alt=\"Running a server.\" style=\"max-width: 100%;\"></a></p>\n<h3 dir=\"auto\">Configuring the shell</h3>\n<p dir=\"auto\">It's often desirable to configure the shell in some way before running any commands. Shells are typically configured with so-called \"startup files\" (e.g., <code class=\"notranslate\">~/.bashrc</code>). However, many shells skip loading such configuration files when running in non-interactive, non-login mode, which is how the shell is invoked by Toast. Toast provides an alternative mechanism to configure the shell that doesn't require creating any special files or invoking the shell in a particular way.</p>\n<p dir=\"auto\">Consider the following toastfile which uses Bash as the shell, since that's the default preferred login shell in Ubuntu:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_figlet:\n    command: |\n      apt-get update\n      apt-get install --yes figlet\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_figlet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes figlet</span></pre></div>\n<p dir=\"auto\">What happens if <code class=\"notranslate\">apt-get update</code> fails? Due to the way Bash works, the failure would be ignored and execution would continue to the subsequent line. You can fix this with <code class=\"notranslate\">set -e</code> as follows:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_figlet:\n    command: |\n      set -e # Make Bash fail fast.\n      apt-get update\n      apt-get install --yes figlet\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_figlet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      set -e # Make Bash fail fast.</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes figlet</span></pre></div>\n<p dir=\"auto\">However, it's tedious and error-prone to add that to each task separately. Instead, you can add it to every task at once by setting <code class=\"notranslate\">command_prefix</code> as follows:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ncommand_prefix: set -e # Make Bash fail fast.\ntasks:\n  install_figlet:\n    command: |\n      apt-get update\n      apt-get install --yes figlet\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">command_prefix</span>: <span class=\"pl-s\">set -e </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Make Bash fail fast.</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_figlet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes figlet</span></pre></div>\n<p dir=\"auto\">For Bash in particular, we recommend going even further and setting <code class=\"notranslate\">set -euxo pipefail</code> instead of just <code class=\"notranslate\">set -e</code>.</p>\n<h3 dir=\"auto\">Dropping into an interactive shell</h3>\n<p dir=\"auto\">If you run Toast with <code class=\"notranslate\">--shell</code>, Toast will drop you into an interactive shell inside the container when the requested tasks are finished, or if any of them fails. This feature is useful for debugging tasks or exploring what's in the container. Suppose you have the following toastfile:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: ubuntu\ntasks:\n  install_figlet:\n    command: |\n      apt-get update\n      apt-get install --yes figlet\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">ubuntu</span>\n<span class=\"pl-ent\">tasks</span>:\n  <span class=\"pl-ent\">install_figlet</span>:\n    <span class=\"pl-ent\">command</span>: <span class=\"pl-s\">|</span>\n<span class=\"pl-s\">      apt-get update</span>\n<span class=\"pl-s\">      apt-get install --yes figlet</span></pre></div>\n<p dir=\"auto\">You can run <code class=\"notranslate\">toast --shell</code> to play with the <code class=\"notranslate\">figlet</code> program:</p>\n<p dir=\"auto\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/shell-0.svg?sanitize=true\"><img src=\"https://raw.githubusercontent.com/stepchowfun/toast/main/media/shell-0.svg?sanitize=true\" alt=\"Dropping into a shell.\" style=\"max-width: 100%;\"></a></p>\n<p dir=\"auto\">When you're done, the container is deleted automatically.</p>\n<h2 dir=\"auto\">How Toast works</h2>\n<p dir=\"auto\">Given a set of tasks to run, Toast computes a <a href=\"https://en.wikipedia.org/wiki/Topological_sorting\" rel=\"nofollow\">topological sort</a> of the dependency DAG to determine in what order to run the tasks. Toast then builds a Docker image for each task based on the image from the previous task in the topological sort, or the base image in the case of the first task.</p>\n<p dir=\"auto\">The topological sort of an arbitrary DAG isn't necessarily unique. Toast uses an algorithm based on depth-first search, traversing children in lexicographical order. The algorithm is deterministic and invariant to the order in which tasks and dependencies are listed, so reordering tasks in a toastfile won't invalidate the cache. Furthermore, <code class=\"notranslate\">toast foo bar</code> and <code class=\"notranslate\">toast bar foo</code> are guaranteed to produce identical schedules to maximize cache utilization.</p>\n<p dir=\"auto\">For each task in the schedule, Toast first computes a cache key based on a hash of the shell command, the contents of the <code class=\"notranslate\">input_paths</code>, the cache key of the previous task in the schedule, etc. Toast will then look for a Docker image tagged with that cache key. If the image is found, Toast will skip the task. Otherwise, Toast will create a container, copy any <code class=\"notranslate\">input_paths</code> into it, run the shell command, copy any <code class=\"notranslate\">output_paths</code> from the container to the host, commit the container to an image, and delete the container. The image is tagged with the cache key so the task can be skipped for subsequent runs.</p>\n<p dir=\"auto\">Toast aims to make as few assumptions about the container environment as possible. Toast only assumes there is a program at <code class=\"notranslate\">/bin/su</code> which can be invoked as <code class=\"notranslate\">su -c COMMAND USER</code>. This program is used to run commands for tasks in the container as the appropriate user with their preferred shell. Every popular Linux distribution has a <code class=\"notranslate\">su</code> utility that supports this usage. Toast has integration tests to ensure it works with popular base images such as <code class=\"notranslate\">debian</code>, <code class=\"notranslate\">alpine</code>, <code class=\"notranslate\">busybox</code>, etc.</p>\n<h2 dir=\"auto\">Toastfile reference</h2>\n<p dir=\"auto\">A <em>toastfile</em> is a YAML file (typically named <code class=\"notranslate\">toast.yml</code>) that defines tasks and their dependencies. The schema contains the following top-level keys and defaults:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"image: &lt;required&gt;   # Docker image name with optional tag or digest\ndefault: null       # Name of default task to run or `null` to run all tasks by default\nlocation: /scratch  # Path in the container for running tasks\nuser: root          # Name of the user in the container for running tasks\ncommand_prefix: ''  # A string to be prepended to all commands by default\ntasks: {}           # Map from task name to task\"><pre class=\"notranslate\"><span class=\"pl-ent\">image</span>: <span class=\"pl-s\">&lt;required&gt;   </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Docker image name with optional tag or digest</span>\n<span class=\"pl-ent\">default</span>: <span class=\"pl-s\">null       </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Name of default task to run or `null` to run all tasks by default</span>\n<span class=\"pl-ent\">location</span>: <span class=\"pl-s\">/scratch  </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Path in the container for running tasks</span>\n<span class=\"pl-ent\">user</span>: <span class=\"pl-s\">root          </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Name of the user in the container for running tasks</span>\n<span class=\"pl-ent\">command_prefix</span>: <span class=\"pl-s\"><span class=\"pl-pds\">'</span><span class=\"pl-pds\">'</span></span>  <span class=\"pl-c\"><span class=\"pl-c\">#</span> A string to be prepended to all commands by default</span>\n<span class=\"pl-ent\">tasks</span>: <span class=\"pl-s\">{}           </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Map from task name to task</span></pre></div>\n<p dir=\"auto\">Tasks have the following schema and defaults:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"description: null           # A description of the task for the `--list` option\ndependencies: []            # Names of dependencies\ncache: true                 # Whether a task can be cached\nenvironment: {}             # Map from environment variable to optional default\ninput_paths: []             # Paths to copy into the container\nexcluded_input_paths: []    # A denylist for `input_paths`\noutput_paths: []            # Paths to copy out of the container if the task succeeds\noutput_paths_on_failure: [] # Paths to copy out of the container if the task fails\nmount_paths: []             # Paths to mount into the container\nmount_readonly: false       # Whether to mount the `mount_paths` as readonly\nports: []                   # Port mappings to publish\nlocation: null              # Overrides the corresponding top-level value\nuser: null                  # Overrides the corresponding top-level value\ncommand: ''                 # Shell command to run in the container\ncommand_prefix: null        # Overrides the corresponding top-level value\nextra_docker_arguments: []  # Additional arguments for `docker container create`\"><pre class=\"notranslate\"><span class=\"pl-ent\">description</span>: <span class=\"pl-s\">null           </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> A description of the task for the `--list` option</span>\n<span class=\"pl-ent\">dependencies</span>: <span class=\"pl-s\">[]            </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Names of dependencies</span>\n<span class=\"pl-ent\">cache</span>: <span class=\"pl-s\">true                 </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether a task can be cached</span>\n<span class=\"pl-ent\">environment</span>: <span class=\"pl-s\">{}             </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Map from environment variable to optional default</span>\n<span class=\"pl-ent\">input_paths</span>: <span class=\"pl-s\">[]             </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Paths to copy into the container</span>\n<span class=\"pl-ent\">excluded_input_paths</span>: <span class=\"pl-s\">[]    </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> A denylist for `input_paths`</span>\n<span class=\"pl-ent\">output_paths</span>: <span class=\"pl-s\">[]            </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Paths to copy out of the container if the task succeeds</span>\n<span class=\"pl-ent\">output_paths_on_failure</span>: <span class=\"pl-s\">[] </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Paths to copy out of the container if the task fails</span>\n<span class=\"pl-ent\">mount_paths</span>: <span class=\"pl-s\">[]             </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Paths to mount into the container</span>\n<span class=\"pl-ent\">mount_readonly</span>: <span class=\"pl-s\">false       </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether to mount the `mount_paths` as readonly</span>\n<span class=\"pl-ent\">ports</span>: <span class=\"pl-s\">[]                   </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Port mappings to publish</span>\n<span class=\"pl-ent\">location</span>: <span class=\"pl-s\">null              </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Overrides the corresponding top-level value</span>\n<span class=\"pl-ent\">user</span>: <span class=\"pl-s\">null                  </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Overrides the corresponding top-level value</span>\n<span class=\"pl-ent\">command</span>: <span class=\"pl-s\"><span class=\"pl-pds\">'</span><span class=\"pl-pds\">'</span></span>                 <span class=\"pl-c\"><span class=\"pl-c\">#</span> Shell command to run in the container</span>\n<span class=\"pl-ent\">command_prefix</span>: <span class=\"pl-s\">null        </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Overrides the corresponding top-level value</span>\n<span class=\"pl-ent\">extra_docker_arguments</span>: <span class=\"pl-s\">[]  </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Additional arguments for `docker container create`</span></pre></div>\n<p dir=\"auto\">The <a href=\"https://github.com/stepchowfun/toast/blob/main/toast.yml\">toastfile</a> for Toast itself is a comprehensive real-world example.</p>\n<h2 dir=\"auto\">Configuration</h2>\n<p dir=\"auto\">Toast can be customized with a YAML configuration file. The default location of the configuration file depends on the operating system:</p>\n<ul dir=\"auto\">\n<li>For macOS, the default location is <code class=\"notranslate\">$HOME/Library/Application Support/toast/toast.yml</code>.</li>\n<li>For other Unix platforms, Toast follows the <a href=\"https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\" rel=\"nofollow\">XDG Base Directory Specification</a>. The default location is <code class=\"notranslate\">$XDG_CONFIG_HOME/toast/toast.yml</code> or <code class=\"notranslate\">$HOME/.config/toast/toast.yml</code> if <code class=\"notranslate\">XDG_CONFIG_HOME</code> isn't set to an absolute path.</li>\n<li>For Windows, the default location is <code class=\"notranslate\">{FOLDERID_RoamingAppData}\\toast\\toast.yml</code>.</li>\n</ul>\n<p dir=\"auto\">The schema of the configuration file is described in the subsections below.</p>\n<h3 dir=\"auto\">Cache configuration</h3>\n<p dir=\"auto\">Toast supports local and remote caching. By default, only local caching is enabled. Remote caching requires that the Docker Engine is logged into a Docker registry (e.g., via <code class=\"notranslate\">docker login</code>).</p>\n<p dir=\"auto\">The cache-related fields and their default values are as follows:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"docker_repo: toast        # Docker repository\nread_local_cache: true    # Whether Toast should read from local cache\nwrite_local_cache: true   # Whether Toast should write to local cache\nread_remote_cache: false  # Whether Toast should read from remote cache\nwrite_remote_cache: false # Whether Toast should write to remote cache\"><pre class=\"notranslate\"><span class=\"pl-ent\">docker_repo</span>: <span class=\"pl-s\">toast        </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Docker repository</span>\n<span class=\"pl-ent\">read_local_cache</span>: <span class=\"pl-s\">true    </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether Toast should read from local cache</span>\n<span class=\"pl-ent\">write_local_cache</span>: <span class=\"pl-s\">true   </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether Toast should write to local cache</span>\n<span class=\"pl-ent\">read_remote_cache</span>: <span class=\"pl-s\">false  </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether Toast should read from remote cache</span>\n<span class=\"pl-ent\">write_remote_cache</span>: <span class=\"pl-s\">false </span><span class=\"pl-c\"><span class=\"pl-c\">#</span> Whether Toast should write to remote cache</span></pre></div>\n<p dir=\"auto\">Each of these options can be overridden via command-line options (see <a href=\"#command-line-options\">below</a>).</p>\n<p dir=\"auto\">A typical configuration for a CI environment will enable all forms of caching, whereas for local development you may want to set <code class=\"notranslate\">write_remote_cache: false</code> to avoid waiting for remote cache writes.</p>\n<h3 dir=\"auto\">Docker CLI</h3>\n<p dir=\"auto\">You can configure the Docker CLI binary used by Toast. Toast uses the <code class=\"notranslate\">PATH</code> environment variable to search for the specified binary. You can use this mechanism to switch to a drop-in replacement for the Docker CLI, such as Podman.</p>\n<p dir=\"auto\">The relevant field and its default value are as follows:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"docker_cli: docker\"><pre class=\"notranslate\"><span class=\"pl-ent\">docker_cli</span>: <span class=\"pl-s\">docker</span></pre></div>\n<h2 dir=\"auto\">Command-line options</h2>\n<p dir=\"auto\">By default, Toast looks for a toastfile called <code class=\"notranslate\">toast.yml</code> in the working directory, then in the parent directory, and so on. Any paths in the toastfile are relative to where the toastfile lives, not the working directory. This means you can run Toast from anywhere in your project and get the same results.</p>\n<p dir=\"auto\">Run <code class=\"notranslate\">toast</code> with no arguments to execute the default task, or all the tasks if the toastfile doesn't define a default. You can also execute specific tasks and their dependencies:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"toast task1 task2 task3…\"><pre class=\"notranslate\">toast task1 task2 task3…</pre></div>\n<p dir=\"auto\">Here are all the supported command-line options:</p>\n<div class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"USAGE:\n    toast [OPTIONS] [--] [TASKS]...\n\nOPTIONS:\n    -c, --config-file &lt;PATH&gt;\n            Sets the path of the config file\n\n        --docker-cli &lt;CLI&gt;\n            Sets the Docker CLI binary\n\n    -r, --docker-repo &lt;REPO&gt;\n            Sets the Docker repository for remote caching\n\n    -f, --file &lt;PATH&gt;\n            Sets the path to the toastfile\n\n        --force &lt;TASK&gt;...\n            Runs a task unconditionally, even if it’s cached\n\n        --force-all\n            Pulls the base image and runs all tasks unconditionally\n\n    -h, --help\n            Prints help information\n\n    -l, --list\n            Lists the tasks that have a description\n\n    -o, --output-dir &lt;PATH&gt;\n            Sets the output directory\n\n        --read-local-cache &lt;BOOL&gt;\n            Sets whether local cache reading is enabled\n\n        --read-remote-cache &lt;BOOL&gt;\n            Sets whether remote cache reading is enabled\n\n    -s, --shell\n            Drops you into a containerized shell after the tasks are finished\n\n    -v, --version\n            Prints version information\n\n        --write-local-cache &lt;BOOL&gt;\n            Sets whether local cache writing is enabled\n\n        --write-remote-cache &lt;BOOL&gt;\n            Sets whether remote cache writing is enabled\n\n\nARGS:\n    &lt;TASKS&gt;...\n            Sets the tasks to run\"><pre class=\"notranslate\"><code class=\"notranslate\">USAGE:\n    toast [OPTIONS] [--] [TASKS]...\n\nOPTIONS:\n    -c, --config-file &lt;PATH&gt;\n            Sets the path of the config file\n\n        --docker-cli &lt;CLI&gt;\n            Sets the Docker CLI binary\n\n    -r, --docker-repo &lt;REPO&gt;\n            Sets the Docker repository for remote caching\n\n    -f, --file &lt;PATH&gt;\n            Sets the path to the toastfile\n\n        --force &lt;TASK&gt;...\n            Runs a task unconditionally, even if it’s cached\n\n        --force-all\n            Pulls the base image and runs all tasks unconditionally\n\n    -h, --help\n            Prints help information\n\n    -l, --list\n            Lists the tasks that have a description\n\n    -o, --output-dir &lt;PATH&gt;\n            Sets the output directory\n\n        --read-local-cache &lt;BOOL&gt;\n            Sets whether local cache reading is enabled\n\n        --read-remote-cache &lt;BOOL&gt;\n            Sets whether remote cache reading is enabled\n\n    -s, --shell\n            Drops you into a containerized shell after the tasks are finished\n\n    -v, --version\n            Prints version information\n\n        --write-local-cache &lt;BOOL&gt;\n            Sets whether local cache writing is enabled\n\n        --write-remote-cache &lt;BOOL&gt;\n            Sets whether remote cache writing is enabled\n\n\nARGS:\n    &lt;TASKS&gt;...\n            Sets the tasks to run\n</code></pre></div>\n<h2 dir=\"auto\">Installation instructions</h2>\n<h3 dir=\"auto\">Installation on macOS or Linux (AArch64 or x86-64)</h3>\n<p dir=\"auto\">If you're running macOS or Linux (AArch64 or x86-64), you can install Toast with this command:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"curl https://raw.githubusercontent.com/stepchowfun/toast/main/install.sh -LSfs | sh\"><pre class=\"notranslate\">curl https://raw.githubusercontent.com/stepchowfun/toast/main/install.sh -LSfs <span class=\"pl-k\">|</span> sh</pre></div>\n<p dir=\"auto\">The same command can be used again to update to the latest version.</p>\n<p dir=\"auto\">The installation script supports the following optional environment variables:</p>\n<ul dir=\"auto\">\n<li><code class=\"notranslate\">VERSION=x.y.z</code> (defaults to the latest version)</li>\n<li><code class=\"notranslate\">PREFIX=/path/to/install</code> (defaults to <code class=\"notranslate\">/usr/local/bin</code>)</li>\n</ul>\n<p dir=\"auto\">For example, the following will install Toast into the working directory:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"curl https://raw.githubusercontent.com/stepchowfun/toast/main/install.sh -LSfs | PREFIX=. sh\"><pre class=\"notranslate\">curl https://raw.githubusercontent.com/stepchowfun/toast/main/install.sh -LSfs <span class=\"pl-k\">|</span> PREFIX=. sh</pre></div>\n<p dir=\"auto\">If you prefer not to use this installation method, you can download the binary from the <a href=\"https://github.com/stepchowfun/toast/releases\">releases page</a>, make it executable (e.g., with <code class=\"notranslate\">chmod</code>), and place it in some directory in your <a href=\"https://en.wikipedia.org/wiki/PATH_(variable)\" rel=\"nofollow\"><code class=\"notranslate\">PATH</code></a> (e.g., <code class=\"notranslate\">/usr/local/bin</code>).</p>\n<h3 dir=\"auto\">Installation on Windows (AArch64 or x86-64)</h3>\n<p dir=\"auto\">If you're running Windows (AArch64 or x86-64), download the latest binary from the <a href=\"https://github.com/stepchowfun/toast/releases\">releases page</a> and rename it to <code class=\"notranslate\">toast</code> (or <code class=\"notranslate\">toast.exe</code> if you have file extensions visible). Create a directory called <code class=\"notranslate\">Toast</code> in your <code class=\"notranslate\">%PROGRAMFILES%</code> directory (e.g., <code class=\"notranslate\">C:\\Program Files\\Toast</code>), and place the renamed binary in there. Then, in the \"Advanced\" tab of the \"System Properties\" section of Control Panel, click on \"Environment Variables...\" and add the full path to the new <code class=\"notranslate\">Toast</code> directory to the <code class=\"notranslate\">PATH</code> variable under \"System variables\". Note that the <code class=\"notranslate\">Program Files</code> directory might have a different name if Windows is configured for a language other than English.</p>\n<p dir=\"auto\">To update an existing installation, simply replace the existing binary.</p>\n<h3 dir=\"auto\">Installation with Homebrew</h3>\n<p dir=\"auto\">If you have <a href=\"https://brew.sh/\" rel=\"nofollow\">Homebrew</a>, you can install Toast as follows:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"brew install toast\"><pre class=\"notranslate\">brew install toast</pre></div>\n<p dir=\"auto\">You can update an existing installation with <code class=\"notranslate\">brew upgrade toast</code>.</p>\n<h3 dir=\"auto\">Installation with MacPorts</h3>\n<p dir=\"auto\">On macOS, you can also install Toast via <a href=\"https://www.macports.org\" rel=\"nofollow\">MacPorts</a> as follows:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"sudo port install toast\"><pre class=\"notranslate\">sudo port install toast</pre></div>\n<p dir=\"auto\">You can update an existing installation via:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"sudo port selfupdate\nsudo port upgrade toast\"><pre class=\"notranslate\">sudo port selfupdate\nsudo port upgrade toast</pre></div>\n<h3 dir=\"auto\">Installation with Cargo</h3>\n<p dir=\"auto\">If you have <a href=\"https://doc.rust-lang.org/cargo/\" rel=\"nofollow\">Cargo</a>, you can install Toast as follows:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cargo install toast\"><pre class=\"notranslate\">cargo install toast</pre></div>\n<p dir=\"auto\">You can run that command with <code class=\"notranslate\">--force</code> to update an existing installation.</p>\n<h2 dir=\"auto\">Running Toast in CI</h2>\n<p dir=\"auto\">The easiest way to run Toast in CI is to use <a href=\"https://help.github.com/en/actions\">GitHub Actions</a>. Toast provides a convenient GitHub action that you can use in your <a href=\"https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-and-managing-workflow-files-and-runs\">workflows</a>. Here's a simple workflow that runs Toast with no arguments:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# .github/workflows/ci.yml\nname: Continuous integration\non:\n  pull_request:\n  push:\n    branches:\n    - main\njobs:\n  ci:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - uses: stepchowfun/toast/.github/actions/toast@main\"><pre class=\"notranslate\"><span class=\"pl-c\"><span class=\"pl-c\">#</span> .github/workflows/ci.yml</span>\n<span class=\"pl-ent\">name</span>: <span class=\"pl-s\">Continuous integration</span>\n<span class=\"pl-ent\">on</span>:\n  <span class=\"pl-ent\">pull_request</span>:\n  <span class=\"pl-ent\">push</span>:\n    <span class=\"pl-ent\">branches</span>:\n    - <span class=\"pl-s\">main</span>\n<span class=\"pl-ent\">jobs</span>:\n  <span class=\"pl-ent\">ci</span>:\n    <span class=\"pl-ent\">runs-on</span>: <span class=\"pl-s\">ubuntu-latest</span>\n    <span class=\"pl-ent\">steps</span>:\n    - <span class=\"pl-ent\">uses</span>: <span class=\"pl-s\">actions/checkout@v4</span>\n    - <span class=\"pl-ent\">uses</span>: <span class=\"pl-s\">stepchowfun/toast/.github/actions/toast@main</span></pre></div>\n<p dir=\"auto\">Here's a more customized workflow that showcases all the options:</p>\n<div class=\"highlight highlight-source-yaml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# .github/workflows/ci.yml\nname: Continuous integration\non:\n  pull_request:\n  push:\n    branches:\n    - main\njobs:\n  ci:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - if: github.event_name == 'push'\n      uses: docker/login-action@v3\n      with:\n        username: DOCKER_USERNAME\n        password: ${{ secrets.DOCKER_PASSWORD }}\n    - uses: stepchowfun/toast/.github/actions/toast@main\n      with:\n        file: toastfiles/toast.yml\n        tasks: build lint test\n        docker_repo: DOCKER_USERNAME/DOCKER_REPO\n        read_remote_cache: true\n        write_remote_cache: ${{ github.event_name == 'push' }}\"><pre class=\"notranslate\"><span class=\"pl-c\"><span class=\"pl-c\">#</span> .github/workflows/ci.yml</span>\n<span class=\"pl-ent\">name</span>: <span class=\"pl-s\">Continuous integration</span>\n<span class=\"pl-ent\">on</span>:\n  <span class=\"pl-ent\">pull_request</span>:\n  <span class=\"pl-ent\">push</span>:\n    <span class=\"pl-ent\">branches</span>:\n    - <span class=\"pl-s\">main</span>\n<span class=\"pl-ent\">jobs</span>:\n  <span class=\"pl-ent\">ci</span>:\n    <span class=\"pl-ent\">runs-on</span>: <span class=\"pl-s\">ubuntu-latest</span>\n    <span class=\"pl-ent\">steps</span>:\n    - <span class=\"pl-ent\">uses</span>: <span class=\"pl-s\">actions/checkout@v4</span>\n    - <span class=\"pl-ent\">if</span>: <span class=\"pl-s\">github.event_name == 'push'</span>\n      <span class=\"pl-ent\">uses</span>: <span class=\"pl-s\">docker/login-action@v3</span>\n      <span class=\"pl-ent\">with</span>:\n        <span class=\"pl-ent\">username</span>: <span class=\"pl-s\">DOCKER_USERNAME</span>\n        <span class=\"pl-ent\">password</span>: <span class=\"pl-s\">${{ secrets.DOCKER_PASSWORD }}</span>\n    - <span class=\"pl-ent\">uses</span>: <span class=\"pl-s\">stepchowfun/toast/.github/actions/toast@main</span>\n      <span class=\"pl-ent\">with</span>:\n        <span class=\"pl-ent\">file</span>: <span class=\"pl-s\">toastfiles/toast.yml</span>\n        <span class=\"pl-ent\">tasks</span>: <span class=\"pl-s\">build lint test</span>\n        <span class=\"pl-ent\">docker_repo</span>: <span class=\"pl-s\">DOCKER_USERNAME/DOCKER_REPO</span>\n        <span class=\"pl-ent\">read_remote_cache</span>: <span class=\"pl-c1\">true</span>\n        <span class=\"pl-ent\">write_remote_cache</span>: <span class=\"pl-s\">${{ github.event_name == 'push' }}</span></pre></div>\n<h2 dir=\"auto\">Requirements</h2>\n<ul dir=\"auto\">\n<li>Toast requires <a href=\"https://www.docker.com/products/docker-engine\" rel=\"nofollow\">Docker Engine</a> 17.06.0 or later.</li>\n<li>Toast only works with Linux containers; Windows containers aren't currently supported. However, in addition to Linux hosts, Toast also supports macOS and Windows hosts with the appropriate virtualization capabilities thanks to <a href=\"https://www.docker.com/products/docker-desktop\" rel=\"nofollow\">Docker Desktop</a>.</li>\n</ul>\n<h2 dir=\"auto\">Acknowledgements</h2>\n<p dir=\"auto\">Toast was inspired by an in-house tool used at Airbnb for CI jobs. The design was heavily influenced by the lessons I learned working on that tool and building out Airbnb's CI system with the fabulous CI Infrastructure Team.</p>\n<p dir=\"auto\">Special thanks to Julia Wang (<a href=\"https://github.com/juliahw\">@juliahw</a>) for valuable early feedback. Thanks to her and Mark Tai (<a href=\"https://github.com/marktai\">@marktai</a>) for coming up with the name <em>Toast</em>.</p>\n<p dir=\"auto\">The terminal animations were produced with <a href=\"https://asciinema.org/\" rel=\"nofollow\">asciinema</a> and <a href=\"https://github.com/marionebl/svg-term-cli\">svg-term-cli</a>.</p>","updatedAt":"2025-05-22T01:51:14Z","upvoteCount":null,"author":{"login":"eryajf","avatarUrl":"https://avatars.githubusercontent.com/u/33259379?u=e4a4090a38ac2473aaed4ef9945233636776c6c3&v=4","url":"https://github.com/eryajf"},"category":null,"labels":{"edges":[{"node":{"name":"更多","color":"25B472"}},{"node":{"name":"命令行工具","color":"e05879"}},{"node":{"name":"stepchowfun","color":"a3bd31"}}]},"comments":{"edges":[]}}},"pageContext":{"number":569,"previous":{"title":"tl-open-source/tl-rtc-file: 基于 WebRTC 的媒体流传输工具","number":568},"next":{"title":"silenceshell/topic: 基于 Golang 实现的容器内运行 top 命令的工具","number":570}}},
    "staticQueryHashes": ["151096407","2861350382"]}