diff --git a/README.md b/README.md index 0b0b4af..a1edfd1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Developer-oriented DevOps scaffolding tool. DevOpsGen generates deterministic Dockerfiles, GitHub Actions workflows, and Kubernetes manifests from a small, explicit configuration. +> Documentation: **[React documentation site](docs/web/dist/index.html)** — +> screenshots of every command, the interactive wizard, flags reference and +> exit codes. Build it with `npm run build` inside `docs/web`. + ## Features - Interactive TUI wizard and non-interactive headless modes. diff --git a/docs/web/.gitignore b/docs/web/.gitignore new file mode 100644 index 0000000..c824a78 --- /dev/null +++ b/docs/web/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.ssr/ +.DS_Store diff --git a/docs/web/dist/assets/index--kteW9Ru.js b/docs/web/dist/assets/index--kteW9Ru.js new file mode 100644 index 0000000..3a3ae40 --- /dev/null +++ b/docs/web/dist/assets/index--kteW9Ru.js @@ -0,0 +1,24 @@ +var e=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t,t.exports),e=null),t.exports);(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var t=e((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.for(`react.view_transition`),m=Symbol.iterator;function h(e){return typeof e!=`object`||!e?null:(e=m&&e[m]||e[`@@iterator`],typeof e==`function`?e:null)}var g={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_=Object.assign,v={};function y(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}y.prototype.isReactComponent={},y.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},y.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function b(){}b.prototype=y.prototype;function ee(e,t,n){this.props=e,this.context=t,this.refs=v,this.updater=n||g}var te=ee.prototype=new b;te.constructor=ee,_(te,y.prototype),te.isPureReactComponent=!0;var ne=Array.isArray;function re(){}var x={H:null,A:null,T:null,S:null},ie=Object.prototype.hasOwnProperty;function S(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ae(e,t){return S(e.type,t,e.props)}function oe(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function se(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ce=/\/+/g;function le(e,t){return typeof e==`object`&&e&&e.key!=null?se(``+e.key):t.toString(36)}function ue(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(re,re):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function de(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,de(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+le(e,0):a,ne(o)?(i=``,c!=null&&(i=c.replace(ce,`$&/`)+`/`),de(o,r,i,``,function(e){return e})):o!=null&&(oe(o)&&(o=ae(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ce,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(ne(e))for(var u=0;u{n.exports=t()})),r=e((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function ee(e){if(h=!1,b(e),!m){if(n(c)!==null)m=!0,te||(te=!0,ae());else{var t=n(l);t!==null&&ce(ee,t.startTime-e)}}}var te=!1,ne=-1,re=5,x=-1;function ie(){return g?!0:!(e.unstable_now()-xt&&ie());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&ce(ee,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?ae():te=!1}}}var ae;if(typeof y==`function`)ae=function(){y(S)};else if(typeof MessageChannel<`u`){var oe=new MessageChannel,se=oe.port2;oe.port1.onmessage=S,ae=function(){se.postMessage(null)}}else ae=function(){_(S,0)};function ce(t,n){ne=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(ne),ne=-1):h=!0,ce(ee,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,te||(te=!0,ae()))),r},e.unstable_shouldYield=ie,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),i=e(((e,t)=>{t.exports=r()})),a=e((e=>{var t=n();function r(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<`u`&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE==`function`)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=a()})),s=e((e=>{var t=i(),r=n(),a=o();function s(e){var t=`https://react.dev/errors/`+e;if(1Ae||(e.current=ke[Ae],ke[Ae]=null,Ae--)}function T(e,t){Ae++,ke[Ae]=e.current,e.current=t}var Ne=je(null),Pe=je(null),Fe=je(null),Ie=je(null);function Le(e,t){switch(T(Fe,t),T(Pe,e),T(Ne,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?up(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=up(t),e=dp(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}Me(Ne),T(Ne,e)}function Re(){Me(Ne),Me(Pe),Me(Fe)}function ze(e){var t=e.memoizedState;t!==null&&(sh._currentValue=t.memoizedState,T(Ie,e)),t=Ne.current;var n=dp(t,e.type);t!==n&&(T(Pe,e),T(Ne,n))}function Be(e){Pe.current===e&&(Me(Ne),Me(Pe)),Ie.current===e&&(Me(Ie),sh._currentValue=Oe)}var Ve,He;function Ue(e){if(Ve===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);Ve=t&&t[1]||``,He=-1)`:-1i||c[r]!==l[i]){var u=` +`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{We=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Ue(n):``}function Ke(e,t){switch(e.tag){case 26:case 27:case 5:return Ue(e.type);case 16:return Ue(`Lazy`);case 13:return e.child!==t&&t!==null?Ue(`Suspense Fallback`):Ue(`Suspense`);case 19:return Ue(`SuspenseList`);case 0:case 15:return Ge(e.type,!1);case 11:return Ge(e.type.render,!1);case 1:return Ge(e.type,!0);case 31:return Ue(`Activity`);case 30:return Ue(`ViewTransition`);default:return``}}function qe(e){try{var t=``,n=null;do t+=Ke(e,n),n=e,e=e.return;while(e);return t}catch(e){return` +Error generating stack: `+e.message+` +`+e.stack}}var Je=Object.prototype.hasOwnProperty,Ye=t.unstable_scheduleCallback,Xe=t.unstable_cancelCallback,Ze=t.unstable_shouldYield,Qe=t.unstable_requestPaint,$e=t.unstable_now,et=t.unstable_getCurrentPriorityLevel,tt=t.unstable_ImmediatePriority,nt=t.unstable_UserBlockingPriority,rt=t.unstable_NormalPriority,it=t.unstable_LowPriority,at=t.unstable_IdlePriority,ot=t.log,st=t.unstable_setDisableYieldValue,ct=null,lt=null;function ut(e){if(typeof ot==`function`&&st(e),lt&&typeof lt.setStrictMode==`function`)try{lt.setStrictMode(ct,e)}catch{}}var dt=Math.clz32?Math.clz32:mt,ft=Math.log,pt=Math.LN2;function mt(e){return e>>>=0,e===0?32:31-(ft(e)/pt|0)|0}var ht=256,gt=262144,_t=4194304;function vt(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&-e;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function yt(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=vt(n))):i=vt(o):i=vt(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=vt(n))):i=vt(o)):i=vt(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function bt(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function xt(e,t){t&8&&(t|=t&32);var n=e.entangledLanes;if(n!==0)for(e=e.entanglements,n&=t;0n;n++)t.push(e);return t}function Tt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Et(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0=wr),Dr=` `,Or=!1;function kr(e,t){switch(e){case`keyup`:return Sr.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function Ar(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var jr=!1;function Mr(e,t){switch(e){case`compositionend`:return Ar(t);case`keypress`:return t.which===32?(Or=!0,Dr):null;case`textInput`:return e=t.data,e===Dr&&Or?null:e;default:return null}}function Nr(e,t){if(jr)return e===`compositionend`||!Cr&&kr(e,t)?(e=Gn(),Wn=Un=Hn=null,jr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=ni(n)}}function ii(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?ii(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ai(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=ti(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=ti(e.document)}return t}function oi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var si=zn&&`documentMode`in document&&11>=document.documentMode,ci=null,li=null,ui=null,di=!1;function fi(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;di||ci==null||ci!==ti(r)||(r=ci,`selectionStart`in r&&oi(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),ui&&ei(ui,r)||(ui=r,r=Jf(li,`onSelect`),0>=o,i-=o,ca=1<<32-dt(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),o=a(_,o,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),A&&ua(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(i,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(i,h),o=a(y,o,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(i,h),A&&ua(i,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(i,v.value,l),v!==null&&(o=a(v,o,g),d===null?u=v:d.sibling=v,d=v);return A&&ua(i,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,i,g,v.value,l),v!==null&&(e&&(_=v.alternate,_!==null&&h.delete(_.key===null?g:_.key)),o=a(v,o,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(i,e)}),A&&ua(i,g),u}function _(e,r,a,c){if(typeof a==`object`&&a&&a.type===ce&&a.key===null&&a.props.ref===void 0&&(a=a.props.children),typeof a==`object`&&a){switch(a.$$typeof){case oe:a:{for(var l=a.key;r!==null;){if(r.key===l){if(l=a.type,l===ce){if(r.tag===7){n(e,r.sibling),c=i(r,a.props.children),vo(c,a),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===_e&&uo(l)===r.type){n(e,r.sibling),c=i(r,a.props),vo(c,a),c.return=e,e=c;break a}n(e,r);break}t(e,r),r=r.sibling}a.type===ce?(c=Yi(a.props.children,e.mode,c,a.key),vo(c,a),c.return=e,e=c):(c=Ji(a.type,a.key,a.props,null,e.mode,c),vo(c,a),c.return=e,e=c)}return o(e);case se:a:{for(l=a.key;r!==null;){if(r.key===l){if(r.tag===4&&r.stateNode.containerInfo===a.containerInfo&&r.stateNode.implementation===a.implementation){n(e,r.sibling),c=i(r,a.children||[]),c.return=e,e=c;break a}n(e,r);break}t(e,r),r=r.sibling}c=Qi(a,e.mode,c),c.return=e,e=c}return o(e);case _e:return a=uo(a),_(e,r,a,c)}if(De(a))return h(e,r,a,c);if(we(a)){if(l=we(a),typeof l!=`function`)throw Error(s(150));return a=l.call(a),g(e,r,a,c)}if(typeof a.then==`function`)return _(e,r,_o(a),c);if(a.$$typeof===fe)return _(e,r,Ia(e,a),c);yo(e,a)}return typeof a==`string`&&a!==``||typeof a==`number`||typeof a==`bigint`?(a=``+a,r!==null&&r.tag===6?(n(e,r.sibling),c=i(r,a),c.return=e,e=c):(n(e,r),c=Xi(a,e.mode,c),c.return=e,e=c),o(e)):n(e,r)}return function(e,t,n,r){try{go=0;var i=_(e,t,n,r);return ho=null,i}catch(t){if(t===io||t===oo)throw t;var a=Wi(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var xo=bo(!0),So=bo(!1),Co=!1;function wo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function To(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Eo(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Do(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,W&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=Vi(e),Bi(e,null,n),t}return Li(e,r,t,n),Vi(e)}function Oo(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ot(e,n)}}function ko(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n}else e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ao=!1;function jo(){if(Ao){var e=Ya;if(e!==null)throw e}}function Mo(e,t,n,r){Ao=!1;var i=e.updateQueue;Co=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(q&f)===f:(r&f)===f){f!==0&&f===Ja&&(Ao=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var m=e,h=s;f=t;var g=n;switch(h.tag){case 1:if(m=h.payload,typeof m==`function`){d=m.call(g,d,f);break a}d=m;break a;case 3:m.flags=m.flags&-65537|128;case 0:if(m=h.payload,f=typeof m==`function`?m.call(g,d,f):m,f==null)break a;d=S({},d,f);break a;case 2:Co=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),sd|=o,e.lanes=o,e.memoizedState=d}}function No(e,t){if(typeof e!=`function`)throw Error(s(191,e));e.call(t)}function Po(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=C.T,s={};s.types=o===null?null:o.types,C.T=s,_c(e,!1,t,n);try{var c=i(),l=C.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?gc(e,t,Qa(c,r),jd(e)):gc(e,t,r,jd(e))}catch(n){gc(e,t,{then:function(){},status:`rejected`,reason:n},jd())}finally{w.p=a,o!==null&&s.types!==null&&(o.types=s.types),C.T=o}}function oc(){}function sc(e,t,n,r){if(e.tag!==5)throw Error(s(476));var i=cc(e).queue;ac(e,i,t,Oe,n===null?oc:function(){return lc(e),n(r)})}function cc(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Oe,baseState:Oe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:vs,lastRenderedState:Oe},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:vs,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function lc(e){var t=cc(e);t.next===null&&(t=e.alternate.memoizedState),gc(e,t.next.queue,{},jd())}function uc(){return Fa(sh)}function dc(){return I().memoizedState}function fc(){return I().memoizedState}function pc(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=jd();e=Eo(n);var r=Do(t,e,n);r!==null&&(Pd(r,t,n),Oo(r,t,n)),t={cache:Va()},e.payload=t;return}t=t.return}}function mc(e,t,n){var r=jd();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},vc(e)?yc(t,n):(n=Ri(e,t,n,r),n!==null&&(Pd(n,e,r),bc(n,t,r)))}function hc(e,t,n){gc(e,t,n,jd())}function gc(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(vc(e))yc(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,$r(s,o))return Li(e,t,i,0),G===null&&Ii(),!1}catch{}if(n=Ri(e,t,i,r),n!==null)return Pd(n,e,r),bc(n,t,r),!0}return!1}function _c(e,t,n,r){if(r={lane:2,revertLane:Pf(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},vc(e)){if(t)throw Error(s(479))}else t=Ri(e,n,r,2),t!==null&&Pd(t,e,2)}function vc(e){var t=e.alternate;return e===M||t!==null&&t===M}function yc(e,t){$o=Qo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function bc(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ot(e,n)}}var xc={readContext:Fa,use:gs,useCallback:F,useContext:F,useEffect:F,useImperativeHandle:F,useLayoutEffect:F,useInsertionEffect:F,useMemo:F,useReducer:F,useRef:F,useState:F,useDebugValue:F,useDeferredValue:F,useTransition:F,useSyncExternalStore:F,useId:F,useHostTransitionStatus:F,useFormState:F,useActionState:F,useOptimistic:F,useMemoCache:F,useCacheRefresh:F,useEffectEvent:F},Sc={readContext:Fa,use:gs,useCallback:function(e,t){return ps().memoizedState=[e,t===void 0?null:t],e},useContext:Fa,useEffect:Ks,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),Ws(4194308,4,Qs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ws(4194308,4,e,t)},useInsertionEffect:function(e,t){Ws(4,2,e,t)},useMemo:function(e,t){var n=ps();t=t===void 0?null:t;var r=e();if(es){ut(!0);try{e()}finally{ut(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=ps();if(n!==void 0){var i=n(t);if(es){ut(!0);try{n(t)}finally{ut(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=mc.bind(null,M,e),[r.memoizedState,e]},useRef:function(e){var t=ps();return e={current:e},t.memoizedState=e},useState:function(e){e=Os(e);var t=e.queue,n=hc.bind(null,M,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:ec,useDeferredValue:function(e,t){return rc(ps(),e,t)},useTransition:function(){var e=Os(!1);return e=ac.bind(null,M,e.queue,!0,!1),ps().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=M,i=ps();if(A){if(n===void 0)throw Error(s(407));n=n()}else{if(n=t(),G===null)throw Error(s(349));q&127||Cs(r,t,n)}i.memoizedState=n;var a={value:n,getSnapshot:t};return i.queue=a,Ks(Ts.bind(null,r,a,e),[e]),r.flags|=2048,Hs(9,{destroy:void 0},ws.bind(null,r,a,n,t),null),n},useId:function(){var e=ps(),t=G.identifierPrefix;if(A){var n=la,r=ca;n=(r&~(1<<32-dt(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=ts++,0<\/script>`,a=a.removeChild(a.firstChild);break;case`select`:a=typeof r.is==`string`?o.createElement(`select`,{is:r.is}):o.createElement(`select`),r.multiple?a.multiple=!0:r.size&&(a.size=r.size);break;default:a=typeof r.is==`string`?o.createElement(i,{is:r.is}):o.createElement(i)}}a[Ft]=t,a[It]=r;a:for(o=t.child;o!==null;){if(o.tag===5||o.tag===6)a.appendChild(o.stateNode);else if(o.tag!==4&&o.tag!==27&&o.child!==null){o.child.return=o,o=o.child;continue}if(o===t)break a;for(;o.sibling===null;){if(o.return===null||o.return===t)break a;o=o.return}o.sibling.return=o.return,o=o.sibling}t.stateNode=a;a:switch(np(a,i,r),i){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&vl(t)}}return z(t),t.subtreeFlags&=-33554433,yl(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&vl(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(s(166));if(e=Fe.current,xa(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,i=O,i!==null)switch(i.tag){case 27:case 5:r=i.memoizedProps}e[Ft]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||ep(e.nodeValue,n)),e||va(t,!0)}else e=lp(e).createTextNode(r),e[Ft]=t,t.stateNode=e}return z(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=xa(t),n!==null){if(e===null){if(!r)throw Error(s(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(s(557));e[Ft]=t}else Sa(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;z(t),e=!1}else n=Ca(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(Ko(t),t):(Ko(t),null);if(t.flags&128)throw Error(s(558))}return z(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(i=xa(t),r!==null&&r.dehydrated!==null){if(e===null){if(!i)throw Error(s(318));if(i=t.memoizedState,i=i===null?null:i.dehydrated,!i)throw Error(s(317));i[Ft]=t}else Sa(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;z(t),i=!1}else i=Ca(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=i),i=!0;if(!i)return t.flags&256?(Ko(t),t):(Ko(t),null)}return Ko(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,i=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(i=r.alternate.memoizedState.cachePool.pool),a=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(a=r.memoizedState.cachePool.pool),a!==i&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),xl(t,t.updateQueue),z(t),null);case 4:return Re(),e===null&&Wf(t.stateNode.containerInfo),t.flags|=67108864,z(t),null;case 10:return ka(t.type),z(t),null;case 19:if(Yo(t),r=t.memoizedState,r===null)return z(t),null;if(i=!!(t.flags&128),a=r.rendering,a===null){if(i)Sl(r,!1);else{if(Y!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(a=Xo(e),a!==null){for(t.flags|=128,Sl(r,!1),e=a.updateQueue,t.updateQueue=e,xl(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)qi(n,e),n=n.sibling;return Jo(t,qo.current&1|2),A&&ua(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&$e()>_d&&(t.flags|=128,i=!0,Sl(r,!1),t.lanes=4194304)}}else{if(!i){if(e=Xo(a),e!==null){if(t.flags|=128,i=!0,e=e.updateQueue,t.updateQueue=e,xl(t,e),Sl(r,!0),r.tail===null&&r.tailMode!==`collapsed`&&r.tailMode!==`visible`&&!a.alternate&&!A)return z(t),null}else 2*$e()-r.renderingStartTime>_d&&n!==536870912&&(t.flags|=128,i=!0,Sl(r,!1),t.lanes=4194304)}r.isBackwards?(a.sibling=t.child,t.child=a):(e=r.last,e===null?t.child=a:e.sibling=a,r.last=a)}if(r.tail!==null){e=r.tail;a:{for(n=e;n!==null;){if(n.alternate!==null){n=!1;break a}n=n.sibling}n=!0}return r.rendering=e,r.tail=e.sibling,r.renderingStartTime=$e(),e.sibling=null,a=qo.current,a=i?a&1|2:a&1,r.tailMode===`visible`||r.tailMode===`collapsed`||!n||A?Jo(t,a):(n=a,T(Bo,t),T(qo,n),Vo===null&&(Vo=t)),A&&ua(t,r.treeForkCount),e}return z(t),null;case 22:case 23:return Ko(t),zo(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(z(t),t.subtreeFlags&6&&(t.flags|=8192)):z(t),n=t.updateQueue,n!==null&&xl(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&Me(eo),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),ka(j),z(t),null;case 25:return null;case 30:return t.flags|=33554432,z(t),null}throw Error(s(156,t.tag))}function wl(e,t){switch(pa(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return ka(j),Re(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Be(t),null;case 31:if(t.memoizedState!==null){if(Ko(t),t.alternate===null)throw Error(s(340));Sa()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Ko(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(s(340));Sa()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Yo(t),e=t.flags,e&65536?(t.flags=e&-65537|128,e=t.memoizedState,e!==null&&(e.rendering=null,e.tail=null),t.flags|=4,t):null;case 4:return Re(),null;case 10:return ka(t.type),null;case 22:case 23:return Ko(t),zo(),e!==null&&Me(eo),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return ka(j),null;case 25:return null;default:return null}}function Tl(e,t){switch(pa(t),t.tag){case 3:ka(j),Re();break;case 26:case 27:case 5:Be(t);break;case 4:Re();break;case 31:t.memoizedState!==null&&Ko(t);break;case 13:Ko(t);break;case 19:Yo(t);break;case 10:ka(t.type);break;case 22:case 23:Ko(t),zo(),e!==null&&Me(eo);break;case 24:ka(j)}}function El(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Z(t,t.return,e)}}function Dl(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Z(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Z(t,t.return,e)}}function Ol(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Po(t,n)}catch(t){Z(e,e.return,t)}}}function kl(e,t,n){n.props=kc(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Z(e,t,n)}}function Al(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:var i=e.stateNode,a=ki(e.memoizedProps,i);(i.ref===null||i.ref.name!==a)&&(i.ref=Pp(a)),r=i.ref;break;case 7:if(e.stateNode===null){var o=new Fp(e);h(e.child,!1,Qp,o,void 0,void 0),e.stateNode=o}r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Z(e,t,n)}}function jl(e,t){var n=e.ref,r=e.refCleanup;if(n!==null){if(typeof r==`function`)try{r()}catch(n){Z(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Z(e,t,n)}else n.current=null}}function Ml(e,t){if((e.tag===5||e.tag===27||e.tag===6)&&e.alternate===null&&t!==null)for(var n=0;n title`))),np(r,t,n),r[Ft]=e,E(r),t=r;break a;case`link`:if(a=Gm(`link`,`href`,i).get(t+(n.href||``))){for(o=0;og&&(o=g,g=h,h=o);var _=ri(s,h),v=ri(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,C.T=null,n=wd,wd=null;var a=bd,o=Sd;if(X=0,xd=bd=null,Sd=0,W&6)throw Error(s(331));var c=W;if(W|=4,Zu(a.current),Uu(a,a.current,o,n),W=c,Df(0,!1),lt&&typeof lt.onPostCommitFiberRoot==`function`)try{lt.onPostCommitFiberRoot(ct,a)}catch{}return!0}finally{w.p=i,C.T=r,uf(e,t)}}function pf(e,t,n){t=ea(n,t),t=Fc(e.stateNode,t,2),e=Do(e,t,2),e!==null&&(Tt(e,2),Ef(e))}function Z(e,t,n){if(e.tag===3)pf(e,e,n);else for(;t!==null;){if(t.tag===3){pf(t,e,n);break}if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(yd===null||!yd.has(r))){e=ea(n,e),n=Ic(2),r=Do(t,n,2),r!==null&&(Lc(n,r,t,e),Tt(r,2),Ef(r));break}}t=t.return}}function mf(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new td;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(ad=!0,i.add(n),e=hf.bind(null,e,t,n),t.then(e,e))}function hf(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,G===e&&(q&n)===n&&(Y===4||Y===3&&(q&62914560)===q&&300>$e()-hd?W&2?ld|=n:Vd(e,0):ld|=n,dd===q&&(dd=0)),Ef(e)}function gf(e,t){t===0&&(t=Ct()),e=zi(e,t),e!==null&&(Tt(e,t),Ef(e))}function _f(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),gf(e,n)}function vf(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,i=e.memoizedState;i!==null&&(n=i.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(s(314))}r!==null&&r.delete(t),gf(e,n)}function yf(e,t){return Ye(e,t)}var bf=null,xf=null,Sf=!1,Cf=!1,wf=!1,Tf=0;function Ef(e){e!==xf&&e.next===null&&(xf===null?bf=xf=e:xf=xf.next=e),Cf=!0,Sf||(Sf=!0,Nf())}function Df(e,t){if(!wf&&Cf){wf=!0;do for(var n=!1,r=bf;r!==null;){if(!t){if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-dt(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,Mf(r,a))}else a=q,a=yt(r,r===G?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||bt(r,a)||(n=!0,Mf(r,a))}r=r.next}while(n);wf=!1}}function Of(){kf()}function kf(){Cf=Sf=!1;var e=0;Tf!==0&&hp()&&(e=Tf);for(var t=$e(),n=null,r=bf;r!==null;){var i=r.next,a=Af(r,t);a===0?(r.next=null,n===null?bf=i:n.next=i,i===null&&(xf=n)):(n=r,(e!==0||a&3)&&(Cf=!0)),r=i}X!==0&&X!==5||Df(e,!1),Tf!==0&&(Tf=0)}function Af(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&ap(d)&&(c=c.responseEnd,o+=u*(c$m){s.length=o;break}f=new Promise(jp.bind(f)),s.push(f)}}}if(0`u`?null:document;function Tm(e,t,n){var r=wm;if(r&&typeof t==`string`&&t){var i=hn(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),ym.has(i)||(ym.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),np(t,`link`,e),E(t),r.head.appendChild(t)))}}function Em(e){xm.D(e),Tm(`dns-prefetch`,e,null)}function Dm(e,t){xm.C(e,t),Tm(`preconnect`,e,t)}function Om(e,t,n){xm.L(e,t,n);var r=wm;if(r&&e&&t){var i=`link[rel="preload"][as="`+hn(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+hn(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+hn(n.imageSizes)+`"]`)):i+=`[href="`+hn(e)+`"]`;var a=i;switch(t){case`style`:a=Pm(e);break;case`script`:a=Rm(e)}if(!(vm.has(a)||(e=S({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),vm.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Fm(a))||t===`script`&&r.querySelector(zm(a))))){var o=r.createElement(`link`);np(o,`link`,e),t===`style`&&(o[Ut]=!0,o.onload=o.onerror=function(){Yt(o)}),E(o),r.head.appendChild(o)}}}function km(e,t){xm.m(e,t);var n=wm;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+hn(r)+`"][href="`+hn(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Rm(e)}if(!vm.has(a)&&(e=S({rel:`modulepreload`,href:e},t),vm.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(zm(a)))return}r=n.createElement(`link`),np(r,`link`,e),E(r),n.head.appendChild(r)}}}function Am(e,t,n){xm.S(e,t,n);var r=wm;if(r&&e){var i=Jt(r).hoistableStyles,a=Pm(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Fm(a)))s.loading=5;else{e=S({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=vm.get(a))&&Hm(e,n);var c=o=r.createElement(`link`);E(c),np(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Vm(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function jm(e,t){xm.X(e,t);var n=wm;if(n&&e){var r=Jt(n).hoistableScripts,i=Rm(e),a=r.get(i);a||(a=n.querySelector(zm(i)),a||(e=S({src:e,async:!0},t),(t=vm.get(i))&&Um(e,t),a=n.createElement(`script`),E(a),np(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Mm(e,t){xm.M(e,t);var n=wm;if(n&&e){var r=Jt(n).hoistableScripts,i=Rm(e),a=r.get(i);a||(a=n.querySelector(zm(i)),a||(e=S({src:e,async:!0,type:`module`},t),(t=vm.get(i))&&Um(e,t),a=n.createElement(`script`),E(a),np(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Nm(e,t,n,r){var i=(i=Fe.current)?bm(i):null;if(!i)throw Error(s(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(n=Pm(n.href),t=Jt(i).hoistableStyles,r=t.get(n),r||(r={type:`style`,instance:null,count:0,state:null},t.set(n,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Pm(n.href);var a=Jt(i).hoistableStyles,o=a.get(e);if(o||(i=i.ownerDocument||i,o={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},a.set(e,o),(a=i.querySelector(Fm(e)))?a._p||(o.instance=a,o.state.loading=5):(a=vm.get(e),a||(a={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},vm.set(e,a)),Lm(i,e,a,o.state))),t&&r===null)throw Error(s(528,``));return o}if(t&&r!==null)throw Error(s(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(n=Rm(n),t=Jt(i).hoistableScripts,r=t.get(n),r||(r={type:`script`,instance:null,count:0,state:null},t.set(n,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(s(444,e))}}function Pm(e){return`href="`+hn(e)+`"`}function Fm(e){return`link[rel="stylesheet"][`+e+`]`}function Im(e){return S({},e,{"data-precedence":e.precedence,precedence:null})}function Lm(e,t,n,r){if(t=e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)){if(!0!==t[Ut]){r.loading=1;return}}else t=e.createElement(`link`),t[Ut]=!0,t.onload=t.onerror=Yt.bind(null,t),np(t,`link`,n),E(t),e.head.appendChild(t);r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2})}function Rm(e){return`[src="`+hn(e)+`"]`}function zm(e){return`script[async]`+e}function Bm(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+hn(n.href)+`"]`);if(r)return t.instance=r,E(r),r;var i=S({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),E(r),np(r,`style`,i),Vm(r,n.precedence,e),t.instance=r;case`stylesheet`:i=Pm(n.href);var a=e.querySelector(Fm(i));if(a)return t.state.loading|=4,t.instance=a,E(a),a;r=Im(n),(i=vm.get(i))&&Hm(r,i),a=(e.ownerDocument||e).createElement(`link`),E(a);var o=a;return o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),np(a,`link`,r),t.state.loading|=4,Vm(a,n.precedence,e),t.instance=a;case`script`:return a=Rm(n.src),(i=e.querySelector(zm(a)))?(t.instance=i,E(i),i):(r=n,(i=vm.get(a))&&(r=S({},n),Um(r,i)),e=e.ownerDocument||e,i=e.createElement(`script`),E(i),np(i,`link`,r),e.head.appendChild(i),t.instance=i);case`void`:return null;default:throw Error(s(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Vm(r,n.precedence,e));return t.instance}function Vm(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function qm(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Jm(e,t){return e===`img`&&t.src!=null&&t.src!==``&&t.onLoad==null&&t.loading!==`lazy`}function Ym(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Xm(e){return(e.width||100)*(e.height||100)*(typeof devicePixelRatio==`number`?devicePixelRatio:1)*.25}function Zm(e,t){typeof t.decode==`function`&&(e.imgCount++,t.complete||(e.imgBytes+=Xm(t),e.suspenseyImages.push(t)),e=rh.bind(e),t.decode().then(e,e))}function Qm(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Pm(r.href),a=t.querySelector(Fm(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=nh.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,E(a);return}a=t.ownerDocument||t,r=Im(r),(i=vm.get(i))&&Hm(r,i),a=a.createElement(`link`),E(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),np(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=nh.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var $m=0;function eh(e,t){return e.stylesheets&&e.count===0&&ah(e,e.stylesheets),0$m?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function th(e){if(e.count===0&&(e.imgCount===0||!e.waitingForImages)){if(e.stylesheets)ah(e,e.stylesheets);else if(e.unsuspend){var t=e.unsuspend;e.unsuspend=null,t()}}}function nh(){this.count--,th(this)}function rh(){this.imgCount--,th(this)}var ih=null;function ah(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,ih=new Map,t.forEach(oh,e),ih=null,nh.call(e))}function oh(e,t){if(!(t.state.loading&4)){var n=ih.get(e);if(n)var r=n.get(null);else{n=new Map,ih.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<`u`&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE==`function`)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=s()}));n();var l=c(),u=e((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),d=e(((e,t)=>{t.exports=u()}))();function f({rel:e,name:t,alt:n,caption:r,feature:i=``}){return(0,d.jsxs)(`figure`,{className:`shot ${i}`,children:[(0,d.jsx)(`a`,{href:`${e}img/${t}`,target:`_blank`,rel:`noopener noreferrer`,children:(0,d.jsx)(`img`,{src:`${e}img/${t}`,alt:n,loading:`lazy`,decoding:`async`})}),r&&(0,d.jsx)(`figcaption`,{children:r})]})}function p({children:e,label:t}){return(0,d.jsxs)(`div`,{className:`code-block`,children:[t&&(0,d.jsx)(`span`,{className:`code-label`,children:t}),(0,d.jsx)(`pre`,{children:(0,d.jsx)(`code`,{children:e})})]})}function m({children:e}){return(0,d.jsx)(`kbd`,{className:`kbd`,children:e})}function h({tone:e=`ok`,children:t}){return(0,d.jsx)(`span`,{className:`badge badge-${e}`,children:t})}function g({kicker:e,title:t,lede:n}){return(0,d.jsxs)(`header`,{className:`page-head`,children:[e&&(0,d.jsx)(`p`,{className:`kicker`,children:e}),(0,d.jsx)(`h1`,{children:t}),n&&(0,d.jsx)(`p`,{className:`lede`,children:n})]})}function _({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsxs)(`div`,{className:`hero`,children:[(0,d.jsxs)(`div`,{children:[(0,d.jsx)(`p`,{className:`kicker yellow`,children:`docker · github actions · kubernetes`}),(0,d.jsxs)(`h1`,{children:[`DevOps scaffolding,`,(0,d.jsx)(`br`,{}),`deterministically.`]}),(0,d.jsx)(`p`,{className:`lede`,children:`DevOpsGen reads your Go or Node.js project and writes a complete, opinionated DevOps baseline: hardened Dockerfile, GitHub Actions pipeline and ready-to-apply Kubernetes manifests — with dry-run plans, automatic backups and config validation built in.`}),(0,d.jsxs)(`div`,{className:`hero-cta`,children:[(0,d.jsx)(`a`,{className:`btn`,href:`${e}getting-started/`,children:`Get started`}),(0,d.jsx)(`a`,{className:`btn btn-pink`,href:`${e}wizard/`,children:`Take the wizard tour`})]})]}),(0,d.jsx)(f,{rel:e,name:`wizard.png`,alt:`DevOpsGen interactive wizard running in a Windows terminal, asking for the project stack`,caption:`The interactive wizard — press Enter to accept smart defaults.`,feature:`shot-feature`})]}),(0,d.jsx)(`h2`,{children:`Why DevOpsGen`}),(0,d.jsxs)(`div`,{className:`cards`,children:[(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Deterministic`}),(0,d.jsxs)(`p`,{children:[`Same inputs, same bytes — every run. Machine-friendly for code review: a second generation with `,(0,d.jsx)(`code`,{children:`--force`}),` produces bit-identical files.`]})]}),(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Safe by default`}),(0,d.jsxs)(`p`,{children:[`Existing files are never overwritten without `,(0,d.jsx)(`code`,{children:`--force`}),`. Overwrites get`,` `,(0,d.jsx)(`code`,{children:`.bak`}),` backups, failed runs roll back automatically, and`,` `,(0,d.jsx)(`code`,{children:`--dry-run`}),` prints the full plan first.`]})]}),(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Validated output`}),(0,d.jsxs)(`p`,{children:[`Built-in validation checks Dockerfile `,(0,d.jsx)(`code`,{children:`FROM`}),` digests, workflow action pinning, Kubernetes naming and health probes before anything ships.`]})]}),(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Stack aware`}),(0,d.jsxs)(`p`,{children:[`Detects Go and Node.js projects (npm, pnpm, Express, Next.js, Vite, NestJS) from`,` `,(0,d.jsx)(`code`,{children:`go.mod`}),` / `,(0,d.jsx)(`code`,{children:`package.json`}),` and tailors templates accordingly.`]})]})]}),(0,d.jsx)(`h2`,{children:`Everything a project needs on day one`}),(0,d.jsxs)(`p`,{children:[`One `,(0,d.jsx)(`code`,{children:`init`}),` command writes the whole baseline tree. Preview the real terminal output of every command below — all screenshots are live captures, not mockups.`]}),(0,d.jsx)(p,{label:`generated in one command`,children:`$ devopsgen init --stack go --ci github-actions --registry ghcr --image you/myapp --port 8080 +created 5 file(s), replaced 0 file(s) in . +exit code: 0`}),(0,d.jsxs)(`div`,{className:`screen-strip`,children:[(0,d.jsx)(f,{rel:e,name:`init-success.png`,alt:`Terminal showing a successful DevOpsGen init run creating five files with exit code 0`,caption:`init — first run creates the complete baseline and exits 0.`}),(0,d.jsx)(f,{rel:e,name:`init-conflict.png`,alt:`Terminal showing DevOpsGen refusing to overwrite five files and exiting with code 3`,caption:`init on a second run refuses to overwrite files — exit code 3 without --force.`}),(0,d.jsx)(f,{rel:e,name:`dry-run.png`,alt:`Terminal showing a DevOpsGen dry-run plan listing replace and conflict entries before exiting with code 3`,caption:`--dry-run prints the exact write plan, unresolved conflicts included.`})]}),(0,d.jsx)(`h2`,{children:`What gets generated`}),(0,d.jsx)(`div`,{className:`table-wrap`,children:(0,d.jsxs)(`table`,{children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`th`,{children:`Template set`}),(0,d.jsx)(`th`,{children:`Files`}),(0,d.jsx)(`th`,{children:`Purpose`})]})}),(0,d.jsxs)(`tbody`,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsxs)(`td`,{children:[(0,d.jsx)(`code`,{children:`docker`}),` (+ `,(0,d.jsx)(`code`,{children:`.dockerignore`}),`)`]}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`Dockerfile`})}),(0,d.jsx)(`td`,{children:`Hardened image build with non-root user, health check hooks, install/prune commands derived from package manager (npm / pnpm) and lockfile detection.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`github-actions`})}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`.github/workflows/ci.yaml`})}),(0,d.jsx)(`td`,{children:`Test/build across Go and Node.js versions, docker build & push, coverage floors, manifest validation.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`kubernetes`})}),(0,d.jsxs)(`td`,{children:[(0,d.jsx)(`code`,{children:`deployment.yaml`}),`, `,(0,d.jsx)(`code`,{children:`service.yaml`}),`, optional `,(0,d.jsx)(`code`,{children:`ingress.yaml`})]}),(0,d.jsx)(`td`,{children:`Non-root security context, probes using your health path, RFC 1123-validated names, optional host-based Ingress.`})]})]})]})}),(0,d.jsx)(`h2`,{children:`Explore the docs`}),(0,d.jsx)(`div`,{className:`cards core`,children:x.filter(e=>e.path!==``).map(t=>(0,d.jsxs)(`a`,{className:`card`,style:{display:`block`},href:`${e}${t.path}/`,children:[(0,d.jsx)(`h3`,{children:t.path.replace(/-/g,` `)}),(0,d.jsx)(`p`,{children:t.description})]},t.path))}),(0,d.jsx)(`h2`,{children:`Command cheatsheet`}),(0,d.jsxs)(`div`,{className:`screen-strip`,children:[(0,d.jsx)(f,{rel:e,name:`help.png`,alt:`Terminal showing the full devopsgen help output listing all commands`,caption:`devopsgen --help — all commands at a glance.`}),(0,d.jsx)(f,{rel:e,name:`list.png`,alt:`Terminal showing the devopsgen list output with available stacks, app types and registries`,caption:`devopsgen list — supported stacks, app types and registries.`}),(0,d.jsx)(f,{rel:e,name:`version.png`,alt:`Terminal showing the devopsgen version output`,caption:`devopsgen version.`})]})]})}function v({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`getting started`,title:`Zero to CI in one command`,lede:`Install DevOpsGen, point it at any Go or Node.js project, and get a full Docker + GitHub Actions + Kubernetes baseline in seconds.`}),(0,d.jsx)(`h2`,{children:`Install`}),(0,d.jsx)(p,{label:`go install (go 1.22+)`,children:`go install devopsgen.dev/devopsgen@latest`}),(0,d.jsxs)(`p`,{children:[`Or grab a prebuilt binary from the project`,` `,(0,d.jsx)(`a`,{href:`https://github.com/Bangkahdev/DevOpsGen/releases`,target:`_blank`,rel:`noopener noreferrer`,children:`GitHub releases`}),`, or build from source:`]}),(0,d.jsx)(p,{label:`from source`,children:`git clone https://github.com/Bangkahdev/DevOpsGen.git +cd DevOpsGen +make build`}),(0,d.jsx)(`h2`,{children:`Two ways to run it`}),(0,d.jsxs)(`ul`,{className:`step-list`,children:[(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Interactive:`}),` run `,(0,d.jsx)(`code`,{children:`devopsgen init`}),` in a terminal — the wizard detects your stack and interviews you step by step.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Headless:`}),` pass every required parameter as flags — the process never waits for stdin, which makes it script- and CI-friendly.`]})]}),(0,d.jsx)(`h2`,{children:`Headless quickstart`}),(0,d.jsx)(p,{label:`inside your project`,children:`devopsgen init --stack go --ci github-actions --registry ghcr --image you/myapp --port 8080`}),(0,d.jsx)(f,{rel:e,name:`init-success.png`,alt:`Terminal capture of devopsgen init completing successfully and reporting five files created`,caption:`A successful first run — five files created, exit code 0.`,feature:`shot-feature`}),(0,d.jsx)(`p`,{children:`Inside a Go project this writes:`}),(0,d.jsx)(p,{label:`what you get`,children:`.github/workflows/ci.yaml # test, vet, build, docker publish +Kubernetes/deployment.yaml # non-root, probes, DNS-1053 names +Kubernetes/service.yaml # cluster service +Kubernetes/ingress.yaml # optional, needs --ingress +Dockerfile # hardened multi-stage build + .dockerignore`}),(0,d.jsxs)(`div`,{className:`callout`,children:[`Second runs are safe: DevOpsGen refuses to overwrite without `,(0,d.jsx)(`code`,{children:`--force`}),` and exits with code 3 — see `,(0,d.jsx)(`a`,{href:`${e}init/`,children:`the init reference`}),`.`]}),(0,d.jsx)(`h2`,{children:`Next steps`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsxs)(`li`,{children:[`Prefer point-and-press? Tour the `,(0,d.jsx)(`a`,{href:`${e}wizard/`,children:`interactive wizard`}),`.`]}),(0,d.jsxs)(`li`,{children:[`Every `,(0,d.jsx)(`code`,{children:`init`}),` flag explained: `,(0,d.jsx)(`a`,{href:`${e}init/`,children:`init command`}),`.`]}),(0,d.jsxs)(`li`,{children:[`Building your own CI around it? Read the `,(0,d.jsx)(`a`,{href:`${e}exit-codes/`,children:`exit codes`}),`.`]})]})]})}function y({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`interactive wizard`,title:`The TUI wizard`,lede:`When you run devopsgen init in a real terminal without a flags-only invocation, a keyboard-driven wizard takes over — Enter accepts the recommended default at every step.`}),(0,d.jsx)(f,{rel:e,name:`wizard.png`,alt:`DevOpsGen wizard TUI showing the stack selection list with a highlighted highlighted selection and instructions`,caption:`Step 1 — stack choice. DevOpsGen pre-selects what its detector recommends.`,feature:`shot-feature`}),(0,d.jsx)(`h2`,{children:`How the interview flows`}),(0,d.jsxs)(`ul`,{className:`step-list`,children:[(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Stack`}),` — `,(0,d.jsx)(`code`,{children:`go`}),` or `,(0,d.jsx)(`code`,{children:`nodejs`}),`, pre-selected by the detector (`,(0,d.jsx)(`a`,{href:`${e}detect/`,children:`detect`}),` runs first).`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Application type`}),` (Node.js only) — express, nextjs, vite, nestjs or generic; Go projects build binaries by default.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Port`}),` — the application port, 1–65535, validated on the spot.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Container image`}),` — `,(0,d.jsx)(`code`,{children:`namespace/repository`}),`, no host, no tag.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Registry`}),` — Docker Hub or GHCR (also shipped as the`,` `,(0,d.jsx)(`code`,{children:`--registry`}),` flag).`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Kubernetes`}),` — manifests are generated by default; opt into a host-based `,(0,d.jsx)(`strong`,{children:`Ingress`}),` with an RFC 1123 hostname.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`strong`,{children:`Summary & confirm`}),` — review every chosen value, confirm, and the files are written to the project root.`]})]}),(0,d.jsxs)(`div`,{className:`callout callout-info`,children:[`A wizard run is fully replayable: the same answers as equivalent flags. To automate the same result, pass the flags headless (`,(0,d.jsx)(`a`,{href:`${e}init/`,children:`init reference`}),`).`]}),(0,d.jsx)(`h2`,{children:`Keyboard`}),(0,d.jsx)(`div`,{className:`table-wrap`,children:(0,d.jsxs)(`table`,{children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`th`,{children:`Key`}),(0,d.jsx)(`th`,{children:`Action`})]})}),(0,d.jsxs)(`tbody`,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(m,{children:`Enter`})}),(0,d.jsx)(`td`,{children:`Confirm current selection / printed default`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsxs)(`td`,{children:[(0,d.jsx)(m,{children:`↑`}),` `,(0,d.jsx)(m,{children:`↓`})]}),(0,d.jsx)(`td`,{children:`Move within list choices`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:`typed text`}),(0,d.jsx)(`td`,{children:`Fill textual answers (port, image, host)`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(m,{children:`Ctrl+C`})}),(0,d.jsx)(`td`,{children:`Abort the wizard without writing anything`})]})]})]})}),(0,d.jsx)(`h2`,{children:`Safety during the run`}),(0,d.jsxs)(`p`,{children:[`Everything until the final confirmation is bound to memory only — aborting leaves the project untouched. The write itself follows the same rules as headless mode: never-overwrite-unless-`,(0,d.jsx)(`code`,{children:`--force`}),`, backups, and rollback on mid-run failure.`]})]})}function b({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`command reference`,title:`devopsgen init`,lede:`Initialize DevOps configuration. In an interactive terminal with no complete configuration the TUI wizard is launched; when all required parameters are supplied via flags the process runs headless and never waits for stdin.`}),(0,d.jsx)(p,{label:`synopsis`,children:`devopsgen init [flags]`}),(0,d.jsx)(`h2`,{children:`Flags`}),(0,d.jsx)(`div`,{className:`table-wrap`,children:(0,d.jsxs)(`table`,{children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`th`,{children:`Flag`}),(0,d.jsx)(`th`,{children:`Default`}),(0,d.jsx)(`th`,{children:`Description`})]})}),(0,d.jsxs)(`tbody`,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--stack`}),(0,d.jsx)(`td`,{children:`auto-detected`}),(0,d.jsxs)(`td`,{children:[`Project stack: `,(0,d.jsx)(`code`,{children:`go`}),` or `,(0,d.jsx)(`code`,{children:`nodejs`}),`.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--app-type`}),(0,d.jsx)(`td`,{children:`auto-detected`}),(0,d.jsxs)(`td`,{children:[`Application type (see `,(0,d.jsx)(`code`,{children:`devopsgen list`}),`).`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--ci`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`github-actions`})}),(0,d.jsx)(`td`,{children:`CI provider.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--registry`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`dockerhub`})}),(0,d.jsxs)(`td`,{children:[`Container registry: `,(0,d.jsx)(`code`,{children:`dockerhub`}),` or `,(0,d.jsx)(`code`,{children:`ghcr`}),`.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--image`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsxs)(`td`,{children:[`Image repository as `,(0,d.jsx)(`code`,{children:`namespace/repository`}),` — no host and no tag.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--tag`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`latest`})}),(0,d.jsx)(`td`,{children:`Additional image tag alias.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--port`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`Application port, 1–65535.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--k8s`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`true`})}),(0,d.jsx)(`td`,{children:`Generate Kubernetes manifests.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--ingress`}),(0,d.jsx)(`td`,{children:`off`}),(0,d.jsxs)(`td`,{children:[`Enable Kubernetes Ingress (requires `,(0,d.jsx)(`code`,{children:`--ingress-host`}),`).`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--ingress-host`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`Ingress hostname, validated as RFC 1123.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--health-path`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`HTTP health probe path wired into manifests.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--build-cmd`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`Build command.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--build-path`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`.`})}),(0,d.jsx)(`td`,{children:`Go package path to build inside the container.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--start-cmd`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`Startup command.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--test-cmd`}),(0,d.jsx)(`td`,{children:`—`}),(0,d.jsx)(`td`,{children:`Test command.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--output-dir`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`.`})}),(0,d.jsx)(`td`,{children:`Output directory.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--dry-run`}),(0,d.jsx)(`td`,{children:`off`}),(0,d.jsx)(`td`,{children:`Simulate without writing anything.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`flag-name`,children:`--force`}),(0,d.jsx)(`td`,{children:`off`}),(0,d.jsx)(`td`,{children:`Overwrite existing regular files.`})]})]})]})}),(0,d.jsx)(`h2`,{children:`Success & conflicts`}),(0,d.jsxs)(`p`,{children:[`A clean run writes every template and exits with`,` `,(0,d.jsx)(h,{tone:`ok`,children:`0`}),`. A second run over an existing project stops before touching anything and exits with `,(0,d.jsx)(h,{tone:`warn`,children:`3`}),` until you add `,(0,d.jsx)(`code`,{children:`--force`}),`:`]}),(0,d.jsxs)(`div`,{className:`screen-strip`,children:[(0,d.jsx)(f,{rel:e,name:`init-success.png`,alt:`devopsgen init success output with exit code 0`,caption:`First run: five files created, exit code 0.`}),(0,d.jsx)(f,{rel:e,name:`init-conflict.png`,alt:`devopsgen init conflict error refusing to overwrite files with exit code 3`,caption:`Re-run without --force: exit code 3, zero files touched.`})]}),(0,d.jsx)(`h2`,{children:`Preview plans with --dry-run`}),(0,d.jsxs)(`p`,{children:[(0,d.jsx)(`code`,{children:`--dry-run`}),` prints the byte-plan: which files would be created, which would be replaced, and which are in conflict. With a conflict present it exits`,` `,(0,d.jsx)(h,{tone:`warn`,children:`3`}),`; combined with `,(0,d.jsx)(`code`,{children:`--force`}),` a fully green plan exits`,` `,(0,d.jsx)(h,{tone:`ok`,children:`0`}),` without writing anything:`]}),(0,d.jsxs)(`div`,{className:`screen-strip`,children:[(0,d.jsx)(f,{rel:e,name:`dry-run.png`,alt:`devopsgen dry-run plan output showing replace and conflict entries with exit code 3`,caption:`--dry-run over an existing project: conflict entries, exit code 3.`}),(0,d.jsx)(f,{rel:e,name:`dry-run-force.png`,alt:`devopsgen dry-run force plan output where every file would be replaced, exit code 0`,caption:`--dry-run --force: every file replaced in plan form, exit code 0.`})]}),(0,d.jsx)(`h2`,{children:`Config validation`}),(0,d.jsxs)(`p`,{children:[`Invalid configuration fails fast with `,(0,d.jsx)(h,{tone:`err`,children:`2`}),` before any file is planned — e.g. an image that must be `,(0,d.jsx)(`code`,{children:`namespace/repository`}),` without host or tag, a port outside 1–65535, or an Ingress without a host:`]}),(0,d.jsx)(f,{rel:e,name:`invalid-config.png`,alt:`devopsgen init failing config validation on an invalid image with exit code 2`,caption:`Config errors exit with code 2 and never write files.`,feature:`shot-feature`}),(0,d.jsxs)(`div`,{className:`callout callout-ok`,children:[`Overwrites performed with `,(0,d.jsx)(`code`,{children:`--force`}),` create `,(0,d.jsx)(`code`,{children:`.bak`}),` backups, and any failure mid-run automatically rolls back to the pre-run state.`]})]})}function ee({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`command reference`,title:`devopsgen detect`,lede:`Inspect the current project and report what DevOpsGen would scaffold for it: the programming stack, the exact files that shaped the decision, and the candidate application types.`}),(0,d.jsx)(f,{rel:e,name:`detect.png`,alt:`devopsgen detect output in a terminal reporting stack go with indicators go.mod`,caption:`devopsgen detect inside a Go module — chain of evidence straight from the filesystem.`,feature:`shot-feature`}),(0,d.jsx)(`h2`,{children:`Reading the output`}),(0,d.jsx)(`div`,{className:`table-wrap`,children:(0,d.jsxs)(`table`,{children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`th`,{children:`Line`}),(0,d.jsx)(`th`,{children:`Meaning`})]})}),(0,d.jsxs)(`tbody`,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`stack: go`})}),(0,d.jsxs)(`td`,{children:[`The detected programming stack (`,(0,d.jsx)(`code`,{children:`go`}),` or `,(0,d.jsx)(`code`,{children:`nodejs`}),`).`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`indicators: go.mod, ...`})}),(0,d.jsx)(`td`,{children:`The files that produced evidence — your audit trail for why the stack matched.`})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{children:(0,d.jsx)(`code`,{children:`app-type candidates: binary`})}),(0,d.jsx)(`td`,{children:`Application types plausible for this project; the wizard default comes from here too.`})]})]})]})}),(0,d.jsx)(`h2`,{children:`How it decides`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsxs)(`li`,{children:[`Go — `,(0,d.jsx)(`code`,{children:`go.mod`}),` and its module directive looking like`,` `,(0,d.jsx)(`code`,{children:`module example.com/app`}),`.`]}),(0,d.jsxs)(`li`,{children:[`Node.js — `,(0,d.jsx)(`code`,{children:`package.json`}),`, plus its dependencies, and lockfile flavours (npm / pnpm) that later steer Docker image commands.`]}),(0,d.jsxs)(`li`,{children:[`Ambiguity or zero indicators produce a distinct exit code (see`,` `,(0,d.jsx)(`a`,{href:`${e}exit-codes/`,children:`exit codes`}),`), so scripts can branch safely.`]})]}),(0,d.jsxs)(`div`,{className:`callout`,children:[`Detection semantics: DevOpsGen detects in the current working directory, not in`,` `,(0,d.jsx)(`code`,{children:`--output-dir`}),`. Run it from the project you want scanned.`]})]})}function te({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`command reference`,title:`devopsgen validate`,lede:`Statically lint DevOpsGen output — Dockerfiles, GitHub Actions workflows and Kubernetes manifests — so problems surface on your laptop instead of pipeline number forty.`}),(0,d.jsx)(f,{rel:e,name:`validate.png`,alt:`devopsgen validate passing with the message internal validation passed and exit code 0`,caption:`Everything checks out — exit code 0.`,feature:`shot-feature`}),(0,d.jsx)(p,{label:`synopsis`,children:`devopsgen validate [flags] # validate the current project +devopsgen validate --output-dir ./configs`}),(0,d.jsx)(`h2`,{children:`What it checks`}),(0,d.jsxs)(`div`,{className:`cards`,children:[(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Docker`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsxs)(`li`,{children:[(0,d.jsx)(`code`,{children:`FROM`}),` base images are pinned to digest form`]}),(0,d.jsx)(`li`,{children:`Required install/prune steps match the package manager`}),(0,d.jsx)(`li`,{children:`Non-root user declared`})]})]}),(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`GitHub Actions`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsx)(`li`,{children:`Workflow documents carry required structure and names`}),(0,d.jsx)(`li`,{children:`Actions are pinned, config files complete`}),(0,d.jsx)(`li`,{children:`Checkout steps reference valid refs`})]})]}),(0,d.jsxs)(`div`,{className:`card`,children:[(0,d.jsx)(`h3`,{children:`Kubernetes`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsx)(`li`,{children:`Resources are DNS 1123/1035 rename-clean`}),(0,d.jsx)(`li`,{children:`Service ↔ Ingress wiring is consistent`}),(0,d.jsx)(`li`,{children:`Probe/health requirements satisfied`})]})]})]}),(0,d.jsx)(`h2`,{children:`Exit behaviour`}),(0,d.jsxs)(`ul`,{children:[(0,d.jsxs)(`li`,{children:[(0,d.jsx)(h,{tone:`ok`,children:`0`}),` — all documents passed.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(h,{tone:`err`,children:`4`}),` — at least one document failed validation.`]}),(0,d.jsxs)(`li`,{children:[(0,d.jsx)(h,{tone:`err`,children:`2`}),` — malformed input (unreadable paths, invalid flags).`]})]}),(0,d.jsxs)(`p`,{children:[`Combine with `,(0,d.jsx)(`code`,{children:`devopsgen init`}),` in CI: generate, commit, validate, deploy — the`,` `,(0,d.jsx)(`a`,{href:`${e}exit-codes/`,children:`exit codes`}),` page maps every failure class.`]})]})}function ne({rel:e}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(g,{kicker:`ci integration`,title:`Exit codes`,lede:`Every DevOpsGen command exits with a stable, documented status code so shells, scripts and CI jobs can branch deterministically — no output parsing required.`}),(0,d.jsx)(`div`,{className:`table-wrap`,children:(0,d.jsxs)(`table`,{children:[(0,d.jsx)(`thead`,{children:(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`th`,{children:`Code`}),(0,d.jsx)(`th`,{children:`Classification`}),(0,d.jsx)(`th`,{children:`Meaning`}),(0,d.jsx)(`th`,{children:`Typical example`})]})}),(0,d.jsxs)(`tbody`,{children:[(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`0`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-ok`,children:`success`})}),(0,d.jsx)(`td`,{children:`Everything requested completed; dry-run plans that resolved cleanly also end here.`}),(0,d.jsxs)(`td`,{children:[(0,d.jsx)(`code`,{children:`init`}),` created 5 files; `,(0,d.jsx)(`code`,{children:`--dry-run --force`}),` plan is green.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`1`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-err`,children:`general error`})}),(0,d.jsx)(`td`,{children:`Unexpected failure — I/O errors, template issues, or a run that failed mid-write and rolled back.`}),(0,d.jsxs)(`td`,{children:[`Disk full while writing `,(0,d.jsx)(`code`,{children:`deployment.yaml`}),`.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`2`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-err`,children:`invalid input`})}),(0,d.jsx)(`td`,{children:`Configuration failed validation before anything was planned or written.`}),(0,d.jsxs)(`td`,{children:[(0,d.jsx)(`code`,{children:`--image BANGKAH/my-app`}),`; port `,(0,d.jsx)(`code`,{children:`-1`}),`; Ingress without host.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`3`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-warn`,children:`conflict`})}),(0,d.jsxs)(`td`,{children:[`Target files already exist and would be overwritten; nothing was written. Retried with `,(0,d.jsx)(`code`,{children:`--force`}),`.`]}),(0,d.jsxs)(`td`,{children:[`Second `,(0,d.jsx)(`code`,{children:`init`}),` without `,(0,d.jsx)(`code`,{children:`--force`}),`.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`4`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-err`,children:`validation failed`})}),(0,d.jsx)(`td`,{children:`Generated documents exist but do not pass static lint rules.`}),(0,d.jsxs)(`td`,{children:[`Unpinned workflow action in `,(0,d.jsx)(`code`,{children:`ci.yaml`}),`.`]})]}),(0,d.jsxs)(`tr`,{children:[(0,d.jsx)(`td`,{className:`num`,children:`5`}),(0,d.jsx)(`td`,{children:(0,d.jsx)(`span`,{className:`badge badge-warn`,children:`detection`})}),(0,d.jsx)(`td`,{children:`Stack detection was ambiguous or found nothing at all.`}),(0,d.jsxs)(`td`,{children:[`Empty directory without `,(0,d.jsx)(`code`,{children:`go.mod`}),` / `,(0,d.jsx)(`code`,{children:`package.json`}),`.`]})]})]})]})}),(0,d.jsx)(`h2`,{children:`In practice`}),(0,d.jsxs)(`div`,{className:`screen-strip`,children:[(0,d.jsx)(f,{rel:e,name:`invalid-config.png`,alt:`devopsgen exiting with code 2 after failing image validation`,caption:`Exit 2 — config rejected before planning.`}),(0,d.jsx)(f,{rel:e,name:`init-conflict.png`,alt:`devopsgen exiting with code 3 on existing files`,caption:`Exit 3 — conflict, nothing written.`})]}),(0,d.jsx)(p,{label:`shell script branching`,children:`devopsgen init --stack go --registry ghcr --image you/myapp --port 8080 +case $? in + 0) echo "ready" ;; + 3) devopsgen init --force --stack go --registry ghcr --image you/myapp --port 8080 ;; + 2) echo "fix your flags" >&2 ;; + *) echo "see devopsgen validate output" >&2 ;; +esac`}),(0,d.jsx)(`div`,{className:`callout callout-info`,children:`Codes are part of DevOpsGen's compatibility promise: bumping a major version may add new codes, but existing ones keep their meaning.`})]})}var re=`https://bangkahdev.github.io/DevOpsGen/`,x=[{path:``,component:_,title:`DevOpsGen — DevOps scaffolding for Go & Node.js projects`,description:`DevOpsGen generates production-ready Dockerfiles, GitHub Actions pipelines and Kubernetes manifests for Go and Node.js projects, with safe dry-run plans, automatic backups and config validation.`,keywords:`DevOpsGen, dockerfile generator, github actions generator, kubernetes manifests, go, nodejs, scaffolding, ci cd`,ogImage:`img/init-success.png`},{path:`getting-started`,component:v,title:`Getting Started — DevOpsGen Docs`,description:`Install DevOpsGen via go install or a release binary, then generate a complete Docker, CI and Kubernetes setup for your project with a single command.`,keywords:`DevOpsGen install, go install, quickstart, getting started`,ogImage:`img/init-success.png`},{path:`wizard`,component:y,title:`Interactive Wizard — DevOpsGen Docs`,description:`DevOpsGen launches an interactive TUI wizard that walks through stack detection, ports, container images and Kubernetes options, then writes the generated configuration.`,keywords:`DevOpsGen wizard, tui, bubbletea, interactive`,ogImage:`img/wizard.png`},{path:`init`,component:b,title:`init command — DevOpsGen Docs`,description:`Full reference for devopsgen init flags: stack, app type, CI provider, registry, image, port, Kubernetes ingress, health path, dry-run and force overwrite behaviour.`,keywords:`devopsgen init, flags, dry-run, force, image, ingress`,ogImage:`img/init-success.png`},{path:`detect`,component:ee,title:`detect command — DevOpsGen Docs`,description:`devopsgen detect inspects the current project and reports the programming stack, the files it based its decision on, and candidate application types.`,keywords:`devopsgen detect, stack detection, go.mod, package.json`,ogImage:`img/detect.png`},{path:`validate`,component:te,title:`validate command — DevOpsGen Docs`,description:`devopsgen validate statically checks generated Dockerfiles, GitHub Actions workflows and Kubernetes manifests: FROM digests, action pinning, naming rules, probes and more.`,keywords:`devopsgen validate, dockerfile lint, workflow lint, kubernetes manifest lint`,ogImage:`img/validate.png`},{path:`exit-codes`,component:ne,title:`Exit codes — DevOpsGen Docs`,description:`Every devopsgen command exits with a documented status code: 0 success, 1 general error, 2 invalid configuration, 3 file conflicts, 4 validation failure, 5 ambiguous detection.`,keywords:`devopsgen exit codes, ci integration, pipeline status`,ogImage:`img/invalid-config.png`}];function ie({rel:e,children:t}){return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(`a`,{className:`skip`,href:`#main`,children:`skip to content`}),(0,d.jsxs)(`header`,{className:`topbar`,children:[(0,d.jsxs)(`a`,{className:`brand`,href:e||`./`,children:[(0,d.jsx)(`span`,{className:`brand-mark`,children:`DG`}),(0,d.jsxs)(`span`,{children:[`DevOpsGen`,(0,d.jsx)(`span`,{className:`brand-sub`,children:`DOCS`})]})]}),(0,d.jsx)(`nav`,{"aria-label":`Main navigation`,children:(0,d.jsx)(`ul`,{children:x.filter(e=>e.path!==``).map(t=>(0,d.jsx)(`li`,{children:(0,d.jsx)(`a`,{className:e===``?`nav-link`:`nav-link nav-sub`,href:`${e}${t.path}/`,children:t.path.replace(/-/g,` `)})},t.path))})}),(0,d.jsx)(`a`,{className:`gh-pill`,href:`https://github.com/Bangkahdev/DevOpsGen`,rel:`noopener noreferrer`,target:`_blank`,children:`GitHub`})]}),(0,d.jsx)(`main`,{id:`main`,children:t}),(0,d.jsxs)(`footer`,{className:`footer`,children:[(0,d.jsxs)(`p`,{children:[`DevOpsGen — deterministic DevOps scaffolding for Go & Node.js.`,` `,(0,d.jsx)(`a`,{href:`${e}getting-started/`,children:`Get started`}),` or browse the`,` `,(0,d.jsx)(`a`,{href:`${e}exit-codes/`,children:`exit codes`}),`.`]}),(0,d.jsxs)(`p`,{className:`footer-links`,children:[(0,d.jsx)(`a`,{href:re,children:`canonical site`}),` · `,(0,d.jsx)(`a`,{href:`https://github.com/Bangkahdev/DevOpsGen`,children:`source code`})]})]})]})}var S=x.find(e=>e.path===location.pathname.replace(/\/$/,``).replace(/^.*\//,``))??null,ae=S&&S.path===``?``:`../`,oe=S??x[0];(0,l.createRoot)(document.getElementById(`root`)).render((0,d.jsx)(ie,{rel:ae,children:(0,d.jsx)(oe.component,{rel:ae})})); \ No newline at end of file diff --git a/docs/web/dist/assets/index-CVCe6Y9v.css b/docs/web/dist/assets/index-CVCe6Y9v.css new file mode 100644 index 0000000..58f7954 --- /dev/null +++ b/docs/web/dist/assets/index-CVCe6Y9v.css @@ -0,0 +1 @@ +:root{--paper:#faf7f0;--ink:#37322b;--outline:#4a443c;--border:3px solid var(--outline);--shadow:6px 6px 0 var(--outline);--shadow-sm:3px 3px 0 var(--outline);--yellow:#f2e3bd;--pink:#f3cfcb;--blue:#c9dced;--lime:#d3e6cf;--purple:#ddcfe9;--red-b:#e8b0a6;--green-b:#b5d9c2;--nav-bg:#3b352e;--nav-ink:#faf3e3;--font:"Segoe UI", "Verdana", "Tahoma", sans-serif;--mono:"Cascadia Code", "Consolas", "JetBrains Mono", monospace}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:var(--font);background:var(--paper);color:var(--ink);-webkit-font-smoothing:antialiased;margin:0;line-height:1.65}body:before{content:"";pointer-events:none;z-index:0;background-image:linear-gradient(#4a443c0d 1px,#0000 1px),linear-gradient(90deg,#4a443c0d 1px,#0000 1px);background-size:32px 32px;position:fixed;inset:0}.skip{background:var(--yellow);border:var(--border);z-index:99;padding:8px 16px;font-weight:700;position:absolute;top:0;left:-9999px}.skip:focus{left:8px}.topbar{z-index:50;background:var(--nav-bg);color:var(--nav-ink);border:var(--border);box-shadow:var(--shadow);flex-wrap:wrap;align-items:center;gap:16px;margin:8px 12px;padding:8px 14px;display:flex;position:sticky;top:8px}.brand{color:var(--nav-ink);letter-spacing:.3px;align-items:center;gap:10px;font-size:1.1rem;font-weight:800;text-decoration:none;display:inline-flex}.brand-mark{background:var(--pink);color:var(--ink);border:2px solid var(--nav-ink);padding:2px 8px;font-weight:800;transform:rotate(-1.5deg)}.brand-sub{background:var(--blue);color:var(--ink);margin-left:8px;padding:1px 6px;font-size:.62rem;transform:rotate(1deg)}.topbar nav ul{flex-wrap:wrap;gap:4px;margin:0;padding:0;list-style:none;display:flex}.nav-link{color:var(--nav-ink);text-transform:uppercase;letter-spacing:.8px;border:2px solid #0000;padding:4px 10px;font-size:.82rem;font-weight:700;text-decoration:none;display:inline-block}.nav-link:hover{background:var(--nav-ink);color:var(--nav-bg);border-color:var(--nav-ink)}.gh-pill{background:var(--yellow);color:var(--ink);border:2px solid var(--nav-ink);box-shadow:2px 2px 0 var(--nav-ink);margin-left:auto;padding:4px 14px;font-weight:800;text-decoration:none}.gh-pill:hover{box-shadow:3px 3px 0 var(--nav-ink);transform:translate(-1px,-1px)}main{z-index:1;max-width:1080px;margin:36px auto;padding:0 20px;position:relative}.page-head{margin-bottom:28px}.kicker{background:var(--lime);border:var(--border);box-shadow:var(--shadow-sm);text-transform:uppercase;letter-spacing:1.6px;margin:0 0 14px;padding:4px 12px;font-size:.75rem;font-weight:800;display:inline-block}.violet{background:var(--purple)}.blue{background:var(--blue)}.pink{background:var(--pink)}.yellow{background:var(--yellow)}h1{letter-spacing:-.5px;text-transform:uppercase;margin:0 0 16px;font-size:clamp(2.1rem,5vw,3.4rem);font-weight:800;line-height:1.08}.lede{background:var(--yellow);border:var(--border);max-width:740px;box-shadow:var(--shadow-sm);padding:12px 16px;font-size:1.12rem;display:inline-block}h2{border-left:10px solid var(--outline);margin:44px 0 14px;padding-left:12px;font-size:1.55rem;font-weight:800}h3{margin:26px 0 10px;font-size:1.12rem;font-weight:800}p,li{font-size:1.02rem}strong{background:var(--lime);padding:0 3px}code{font-family:var(--mono);background:var(--nav-bg);color:var(--nav-ink);padding:2px 6px;font-size:.92em}a{color:var(--ink);text-decoration-thickness:2px;-webkit-text-decoration-color:var(--red-b);text-decoration-color:var(--red-b)}a:hover{background:var(--pink)}.hero{grid-template-columns:1.1fr .9fr;align-items:start;gap:24px;margin-bottom:20px;display:grid}.hero-cta{flex-wrap:wrap;gap:14px;margin-top:8px;display:flex}.btn{text-transform:uppercase;border:var(--border);box-shadow:var(--shadow);background:var(--blue);color:var(--ink);padding:11px 20px;font-size:.95rem;font-weight:800;text-decoration:none;display:inline-block}.btn:hover{box-shadow:8px 8px 0 var(--outline);transform:translate(-2px,-2px)}.btn-pink{background:var(--pink)}.btn-yellow{background:var(--yellow)}.cards{grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:20px;display:grid}.card{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;padding:18px;position:relative}.card h3{margin-top:0}.card:nth-child(3n+1){background:var(--yellow)}.card:nth-child(3n+2){background:#fffdf8}.card:nth-child(3n){background:var(--blue)}.card:hover{box-shadow:8px 8px 0 var(--outline);transform:translate(-2px,-2px)}.core .card{background:#fffdf8}.core .card:hover{background:var(--lime)}.shot{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;max-width:100%;margin:22px 0;padding:12px}.shot img{border:2px solid var(--outline);width:100%;height:auto;display:block}.shot figcaption{border-top:2px dashed var(--outline);margin-top:10px;padding-top:8px;font-size:.9rem;font-weight:600}.shot a:hover{background:0 0}.shot img:hover{transform:translate(-1px,-1px)}.shot-feature{max-width:860px}.screen-strip{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));align-items:start;gap:22px;display:grid}.screen-strip .shot{margin:0 0 12px}.code-block{background:var(--nav-bg);color:var(--nav-ink);border:var(--border);box-shadow:var(--shadow);margin:18px 0;position:relative}.code-label{background:var(--purple);border:2px solid var(--outline);text-transform:uppercase;letter-spacing:1px;color:var(--ink);padding:2px 10px;font-size:.72rem;font-weight:800;position:absolute;top:-15px;left:12px}.code-block pre{font-family:var(--mono);margin:0;padding:16px 18px;font-size:.88rem;line-height:1.55;overflow-x:auto}.code-block code{color:inherit;background:0 0;padding:0}.table-wrap{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;margin:18px 0;overflow-x:auto}table{border-collapse:collapse;width:100%}th,td{border:2px solid var(--outline);text-align:left;vertical-align:top;padding:10px 12px}th{background:var(--nav-bg);color:var(--nav-ink);text-transform:uppercase;letter-spacing:1px;font-size:.78rem}tbody tr:nth-child(2n) td{background:var(--yellow)}td code{white-space:nowrap}.num{font-size:1.05rem;font-weight:800}.badge{text-transform:uppercase;letter-spacing:.8px;border:2px solid var(--outline);margin-right:6px;padding:3px 10px;font-size:.78rem;font-weight:800;display:inline-block;transform:rotate(-1deg)}.badge-ok{background:var(--green-b);color:var(--ink)}.badge-warn{background:var(--yellow)}.badge-err{background:var(--red-b);color:var(--ink)}.badge-info{background:var(--blue)}.kbd{font-family:var(--mono);border:2px solid var(--outline);box-shadow:2px 2px 0 var(--outline);background:#fffdf8;padding:2px 8px;font-size:.85rem;font-weight:700;display:inline-block}.callout{border:var(--border);box-shadow:var(--shadow-sm);background:var(--pink);margin:22px 0;padding:14px 18px;font-weight:600}.callout-info{background:var(--blue)}.callout-ok{background:var(--lime)}.file-tree{font-family:var(--mono);white-space:pre;font-size:.9rem;overflow-x:auto}.footer{z-index:1;border:var(--border);box-shadow:var(--shadow);background:var(--nav-bg);color:var(--nav-ink);margin:48px 12px 12px;padding:18px 22px;font-size:.9rem;position:relative}.footer a{color:var(--yellow);-webkit-text-decoration-color:var(--yellow);text-decoration-color:var(--yellow)}.footer a:hover{background:var(--yellow);color:var(--ink)}.footer-links{margin-top:6px}ul{padding-left:22px}li+li{margin-top:4px}.step-list{counter-reset:steps;padding-left:0;list-style:none}.step-list li{counter-increment:steps;margin-top:14px;padding-left:52px;position:relative}.step-list li:before{content:counter(steps);background:var(--purple);width:32px;height:32px;color:var(--ink);border:2px solid var(--outline);box-shadow:2px 2px 0 var(--outline);place-items:center;font-weight:800;display:grid;position:absolute;top:-4px;left:0}.flag-name{font-family:var(--mono);font-weight:700}@media (width<=720px){.hero{grid-template-columns:1fr}.topbar{gap:8px}.gh-pill{margin-left:0}main{margin:18px auto}} diff --git a/docs/web/dist/assets/site.css b/docs/web/dist/assets/site.css new file mode 100644 index 0000000..58f7954 --- /dev/null +++ b/docs/web/dist/assets/site.css @@ -0,0 +1 @@ +:root{--paper:#faf7f0;--ink:#37322b;--outline:#4a443c;--border:3px solid var(--outline);--shadow:6px 6px 0 var(--outline);--shadow-sm:3px 3px 0 var(--outline);--yellow:#f2e3bd;--pink:#f3cfcb;--blue:#c9dced;--lime:#d3e6cf;--purple:#ddcfe9;--red-b:#e8b0a6;--green-b:#b5d9c2;--nav-bg:#3b352e;--nav-ink:#faf3e3;--font:"Segoe UI", "Verdana", "Tahoma", sans-serif;--mono:"Cascadia Code", "Consolas", "JetBrains Mono", monospace}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:var(--font);background:var(--paper);color:var(--ink);-webkit-font-smoothing:antialiased;margin:0;line-height:1.65}body:before{content:"";pointer-events:none;z-index:0;background-image:linear-gradient(#4a443c0d 1px,#0000 1px),linear-gradient(90deg,#4a443c0d 1px,#0000 1px);background-size:32px 32px;position:fixed;inset:0}.skip{background:var(--yellow);border:var(--border);z-index:99;padding:8px 16px;font-weight:700;position:absolute;top:0;left:-9999px}.skip:focus{left:8px}.topbar{z-index:50;background:var(--nav-bg);color:var(--nav-ink);border:var(--border);box-shadow:var(--shadow);flex-wrap:wrap;align-items:center;gap:16px;margin:8px 12px;padding:8px 14px;display:flex;position:sticky;top:8px}.brand{color:var(--nav-ink);letter-spacing:.3px;align-items:center;gap:10px;font-size:1.1rem;font-weight:800;text-decoration:none;display:inline-flex}.brand-mark{background:var(--pink);color:var(--ink);border:2px solid var(--nav-ink);padding:2px 8px;font-weight:800;transform:rotate(-1.5deg)}.brand-sub{background:var(--blue);color:var(--ink);margin-left:8px;padding:1px 6px;font-size:.62rem;transform:rotate(1deg)}.topbar nav ul{flex-wrap:wrap;gap:4px;margin:0;padding:0;list-style:none;display:flex}.nav-link{color:var(--nav-ink);text-transform:uppercase;letter-spacing:.8px;border:2px solid #0000;padding:4px 10px;font-size:.82rem;font-weight:700;text-decoration:none;display:inline-block}.nav-link:hover{background:var(--nav-ink);color:var(--nav-bg);border-color:var(--nav-ink)}.gh-pill{background:var(--yellow);color:var(--ink);border:2px solid var(--nav-ink);box-shadow:2px 2px 0 var(--nav-ink);margin-left:auto;padding:4px 14px;font-weight:800;text-decoration:none}.gh-pill:hover{box-shadow:3px 3px 0 var(--nav-ink);transform:translate(-1px,-1px)}main{z-index:1;max-width:1080px;margin:36px auto;padding:0 20px;position:relative}.page-head{margin-bottom:28px}.kicker{background:var(--lime);border:var(--border);box-shadow:var(--shadow-sm);text-transform:uppercase;letter-spacing:1.6px;margin:0 0 14px;padding:4px 12px;font-size:.75rem;font-weight:800;display:inline-block}.violet{background:var(--purple)}.blue{background:var(--blue)}.pink{background:var(--pink)}.yellow{background:var(--yellow)}h1{letter-spacing:-.5px;text-transform:uppercase;margin:0 0 16px;font-size:clamp(2.1rem,5vw,3.4rem);font-weight:800;line-height:1.08}.lede{background:var(--yellow);border:var(--border);max-width:740px;box-shadow:var(--shadow-sm);padding:12px 16px;font-size:1.12rem;display:inline-block}h2{border-left:10px solid var(--outline);margin:44px 0 14px;padding-left:12px;font-size:1.55rem;font-weight:800}h3{margin:26px 0 10px;font-size:1.12rem;font-weight:800}p,li{font-size:1.02rem}strong{background:var(--lime);padding:0 3px}code{font-family:var(--mono);background:var(--nav-bg);color:var(--nav-ink);padding:2px 6px;font-size:.92em}a{color:var(--ink);text-decoration-thickness:2px;-webkit-text-decoration-color:var(--red-b);text-decoration-color:var(--red-b)}a:hover{background:var(--pink)}.hero{grid-template-columns:1.1fr .9fr;align-items:start;gap:24px;margin-bottom:20px;display:grid}.hero-cta{flex-wrap:wrap;gap:14px;margin-top:8px;display:flex}.btn{text-transform:uppercase;border:var(--border);box-shadow:var(--shadow);background:var(--blue);color:var(--ink);padding:11px 20px;font-size:.95rem;font-weight:800;text-decoration:none;display:inline-block}.btn:hover{box-shadow:8px 8px 0 var(--outline);transform:translate(-2px,-2px)}.btn-pink{background:var(--pink)}.btn-yellow{background:var(--yellow)}.cards{grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:20px;display:grid}.card{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;padding:18px;position:relative}.card h3{margin-top:0}.card:nth-child(3n+1){background:var(--yellow)}.card:nth-child(3n+2){background:#fffdf8}.card:nth-child(3n){background:var(--blue)}.card:hover{box-shadow:8px 8px 0 var(--outline);transform:translate(-2px,-2px)}.core .card{background:#fffdf8}.core .card:hover{background:var(--lime)}.shot{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;max-width:100%;margin:22px 0;padding:12px}.shot img{border:2px solid var(--outline);width:100%;height:auto;display:block}.shot figcaption{border-top:2px dashed var(--outline);margin-top:10px;padding-top:8px;font-size:.9rem;font-weight:600}.shot a:hover{background:0 0}.shot img:hover{transform:translate(-1px,-1px)}.shot-feature{max-width:860px}.screen-strip{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));align-items:start;gap:22px;display:grid}.screen-strip .shot{margin:0 0 12px}.code-block{background:var(--nav-bg);color:var(--nav-ink);border:var(--border);box-shadow:var(--shadow);margin:18px 0;position:relative}.code-label{background:var(--purple);border:2px solid var(--outline);text-transform:uppercase;letter-spacing:1px;color:var(--ink);padding:2px 10px;font-size:.72rem;font-weight:800;position:absolute;top:-15px;left:12px}.code-block pre{font-family:var(--mono);margin:0;padding:16px 18px;font-size:.88rem;line-height:1.55;overflow-x:auto}.code-block code{color:inherit;background:0 0;padding:0}.table-wrap{border:var(--border);box-shadow:var(--shadow);background:#fffdf8;margin:18px 0;overflow-x:auto}table{border-collapse:collapse;width:100%}th,td{border:2px solid var(--outline);text-align:left;vertical-align:top;padding:10px 12px}th{background:var(--nav-bg);color:var(--nav-ink);text-transform:uppercase;letter-spacing:1px;font-size:.78rem}tbody tr:nth-child(2n) td{background:var(--yellow)}td code{white-space:nowrap}.num{font-size:1.05rem;font-weight:800}.badge{text-transform:uppercase;letter-spacing:.8px;border:2px solid var(--outline);margin-right:6px;padding:3px 10px;font-size:.78rem;font-weight:800;display:inline-block;transform:rotate(-1deg)}.badge-ok{background:var(--green-b);color:var(--ink)}.badge-warn{background:var(--yellow)}.badge-err{background:var(--red-b);color:var(--ink)}.badge-info{background:var(--blue)}.kbd{font-family:var(--mono);border:2px solid var(--outline);box-shadow:2px 2px 0 var(--outline);background:#fffdf8;padding:2px 8px;font-size:.85rem;font-weight:700;display:inline-block}.callout{border:var(--border);box-shadow:var(--shadow-sm);background:var(--pink);margin:22px 0;padding:14px 18px;font-weight:600}.callout-info{background:var(--blue)}.callout-ok{background:var(--lime)}.file-tree{font-family:var(--mono);white-space:pre;font-size:.9rem;overflow-x:auto}.footer{z-index:1;border:var(--border);box-shadow:var(--shadow);background:var(--nav-bg);color:var(--nav-ink);margin:48px 12px 12px;padding:18px 22px;font-size:.9rem;position:relative}.footer a{color:var(--yellow);-webkit-text-decoration-color:var(--yellow);text-decoration-color:var(--yellow)}.footer a:hover{background:var(--yellow);color:var(--ink)}.footer-links{margin-top:6px}ul{padding-left:22px}li+li{margin-top:4px}.step-list{counter-reset:steps;padding-left:0;list-style:none}.step-list li{counter-increment:steps;margin-top:14px;padding-left:52px;position:relative}.step-list li:before{content:counter(steps);background:var(--purple);width:32px;height:32px;color:var(--ink);border:2px solid var(--outline);box-shadow:2px 2px 0 var(--outline);place-items:center;font-weight:800;display:grid;position:absolute;top:-4px;left:0}.flag-name{font-family:var(--mono);font-weight:700}@media (width<=720px){.hero{grid-template-columns:1fr}.topbar{gap:8px}.gh-pill{margin-left:0}main{margin:18px auto}} diff --git a/docs/web/dist/detect/index.html b/docs/web/dist/detect/index.html new file mode 100644 index 0000000..0773353 --- /dev/null +++ b/docs/web/dist/detect/index.html @@ -0,0 +1,26 @@ + + + + + + detect command — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

command reference

devopsgen detect

Inspect the current project and report what DevOpsGen would scaffold for it: the programming stack, the exact files that shaped the decision, and the candidate application types.

devopsgen detect output in a terminal reporting stack go with indicators go.mod
devopsgen detect inside a Go module — chain of evidence straight from the filesystem.

Reading the output

LineMeaning
stack: goThe detected programming stack (go or nodejs).
indicators: go.mod, ...The files that produced evidence — your audit trail for why the stack matched.
app-type candidates: binaryApplication types plausible for this project; the wizard default comes from here too.

How it decides

  • Go — go.mod and its module directive looking like module example.com/app.
  • Node.js — package.json, plus its dependencies, and lockfile flavours (npm / pnpm) that later steer Docker image commands.
  • Ambiguity or zero indicators produce a distinct exit code (see exit codes), so scripts can branch safely.
Detection semantics: DevOpsGen detects in the current working directory, not in --output-dir. Run it from the project you want scanned.
+ + \ No newline at end of file diff --git a/docs/web/dist/exit-codes/index.html b/docs/web/dist/exit-codes/index.html new file mode 100644 index 0000000..a4cb6b7 --- /dev/null +++ b/docs/web/dist/exit-codes/index.html @@ -0,0 +1,32 @@ + + + + + + Exit codes — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

ci integration

Exit codes

Every DevOpsGen command exits with a stable, documented status code so shells, scripts and CI jobs can branch deterministically — no output parsing required.

CodeClassificationMeaningTypical example
0successEverything requested completed; dry-run plans that resolved cleanly also end here.init created 5 files; --dry-run --force plan is green.
1general errorUnexpected failure — I/O errors, template issues, or a run that failed mid-write and rolled back.Disk full while writing deployment.yaml.
2invalid inputConfiguration failed validation before anything was planned or written.--image BANGKAH/my-app; port -1; Ingress without host.
3conflictTarget files already exist and would be overwritten; nothing was written. Retried with --force.Second init without --force.
4validation failedGenerated documents exist but do not pass static lint rules.Unpinned workflow action in ci.yaml.
5detectionStack detection was ambiguous or found nothing at all.Empty directory without go.mod / package.json.

In practice

devopsgen exiting with code 2 after failing image validation
Exit 2 — config rejected before planning.
devopsgen exiting with code 3 on existing files
Exit 3 — conflict, nothing written.
shell script branching
devopsgen init --stack go --registry ghcr --image you/myapp --port 8080
+case $? in
+  0) echo "ready" ;;
+  3) devopsgen init --force --stack go --registry ghcr --image you/myapp --port 8080 ;;
+  2) echo "fix your flags" >&2 ;;
+  *) echo "see devopsgen validate output" >&2 ;;
+esac
Codes are part of DevOpsGen's compatibility promise: bumping a major version may add new codes, but existing ones keep their meaning.
+ + \ No newline at end of file diff --git a/docs/web/dist/getting-started/index.html b/docs/web/dist/getting-started/index.html new file mode 100644 index 0000000..5b2cb75 --- /dev/null +++ b/docs/web/dist/getting-started/index.html @@ -0,0 +1,32 @@ + + + + + + Getting Started — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

getting started

Zero to CI in one command

Install DevOpsGen, point it at any Go or Node.js project, and get a full Docker + GitHub Actions + Kubernetes baseline in seconds.

Install

go install (go 1.22+)
go install devopsgen.dev/devopsgen@latest

Or grab a prebuilt binary from the project GitHub releases, or build from source:

from source
git clone https://github.com/Bangkahdev/DevOpsGen.git
+cd DevOpsGen
+make build

Two ways to run it

  • Interactive: run devopsgen init in a terminal — the wizard detects your stack and interviews you step by step.
  • Headless: pass every required parameter as flags — the process never waits for stdin, which makes it script- and CI-friendly.

Headless quickstart

inside your project
devopsgen init   --stack go   --ci github-actions   --registry ghcr   --image you/myapp   --port 8080
Terminal capture of devopsgen init completing successfully and reporting five files created
A successful first run — five files created, exit code 0.

Inside a Go project this writes:

what you get
.github/workflows/ci.yaml   # test, vet, build, docker publish
+Kubernetes/deployment.yaml  # non-root, probes, DNS-1053 names
+Kubernetes/service.yaml     # cluster service
+Kubernetes/ingress.yaml     # optional, needs --ingress
+Dockerfile                  # hardened multi-stage build + .dockerignore
Second runs are safe: DevOpsGen refuses to overwrite without --force and exits with code 3 — see the init reference.

Next steps

+ + \ No newline at end of file diff --git a/docs/web/dist/img/detect.png b/docs/web/dist/img/detect.png new file mode 100644 index 0000000..f60b80a Binary files /dev/null and b/docs/web/dist/img/detect.png differ diff --git a/docs/web/dist/img/dry-run-force.png b/docs/web/dist/img/dry-run-force.png new file mode 100644 index 0000000..55ea52b Binary files /dev/null and b/docs/web/dist/img/dry-run-force.png differ diff --git a/docs/web/dist/img/dry-run.png b/docs/web/dist/img/dry-run.png new file mode 100644 index 0000000..9a3bd6e Binary files /dev/null and b/docs/web/dist/img/dry-run.png differ diff --git a/docs/web/dist/img/help.png b/docs/web/dist/img/help.png new file mode 100644 index 0000000..9ad0437 Binary files /dev/null and b/docs/web/dist/img/help.png differ diff --git a/docs/web/dist/img/init-conflict.png b/docs/web/dist/img/init-conflict.png new file mode 100644 index 0000000..0be48f9 Binary files /dev/null and b/docs/web/dist/img/init-conflict.png differ diff --git a/docs/web/dist/img/init-success.png b/docs/web/dist/img/init-success.png new file mode 100644 index 0000000..0be48f9 Binary files /dev/null and b/docs/web/dist/img/init-success.png differ diff --git a/docs/web/dist/img/invalid-config.png b/docs/web/dist/img/invalid-config.png new file mode 100644 index 0000000..97795c1 Binary files /dev/null and b/docs/web/dist/img/invalid-config.png differ diff --git a/docs/web/dist/img/list.png b/docs/web/dist/img/list.png new file mode 100644 index 0000000..2ed629b Binary files /dev/null and b/docs/web/dist/img/list.png differ diff --git a/docs/web/dist/img/validate.png b/docs/web/dist/img/validate.png new file mode 100644 index 0000000..7f4b32c Binary files /dev/null and b/docs/web/dist/img/validate.png differ diff --git a/docs/web/dist/img/version.png b/docs/web/dist/img/version.png new file mode 100644 index 0000000..355cdf6 Binary files /dev/null and b/docs/web/dist/img/version.png differ diff --git a/docs/web/dist/img/wizard.png b/docs/web/dist/img/wizard.png new file mode 100644 index 0000000..8e7944b Binary files /dev/null and b/docs/web/dist/img/wizard.png differ diff --git a/docs/web/dist/index.html b/docs/web/dist/index.html new file mode 100644 index 0000000..ecdebf4 --- /dev/null +++ b/docs/web/dist/index.html @@ -0,0 +1,28 @@ + + + + + + DevOpsGen — DevOps scaffolding for Go & Node.js projects + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

docker · github actions · kubernetes

DevOps scaffolding,
deterministically.

DevOpsGen reads your Go or Node.js project and writes a complete, opinionated DevOps baseline: hardened Dockerfile, GitHub Actions pipeline and ready-to-apply Kubernetes manifests — with dry-run plans, automatic backups and config validation built in.

DevOpsGen interactive wizard running in a Windows terminal, asking for the project stack
The interactive wizard — press Enter to accept smart defaults.

Why DevOpsGen

Deterministic

Same inputs, same bytes — every run. Machine-friendly for code review: a second generation with --force produces bit-identical files.

Safe by default

Existing files are never overwritten without --force. Overwrites get .bak backups, failed runs roll back automatically, and --dry-run prints the full plan first.

Validated output

Built-in validation checks Dockerfile FROM digests, workflow action pinning, Kubernetes naming and health probes before anything ships.

Stack aware

Detects Go and Node.js projects (npm, pnpm, Express, Next.js, Vite, NestJS) from go.mod / package.json and tailors templates accordingly.

Everything a project needs on day one

One init command writes the whole baseline tree. Preview the real terminal output of every command below — all screenshots are live captures, not mockups.

generated in one command
$ devopsgen init --stack go --ci github-actions --registry ghcr --image you/myapp --port 8080
+created 5 file(s), replaced 0 file(s) in .
+exit code: 0
Terminal showing a successful DevOpsGen init run creating five files with exit code 0
init — first run creates the complete baseline and exits 0.
Terminal showing DevOpsGen refusing to overwrite five files and exiting with code 3
init on a second run refuses to overwrite files — exit code 3 without --force.
Terminal showing a DevOpsGen dry-run plan listing replace and conflict entries before exiting with code 3
--dry-run prints the exact write plan, unresolved conflicts included.

What gets generated

Template setFilesPurpose
docker (+ .dockerignore)DockerfileHardened image build with non-root user, health check hooks, install/prune commands derived from package manager (npm / pnpm) and lockfile detection.
github-actions.github/workflows/ci.yamlTest/build across Go and Node.js versions, docker build & push, coverage floors, manifest validation.
kubernetesdeployment.yaml, service.yaml, optional ingress.yamlNon-root security context, probes using your health path, RFC 1123-validated names, optional host-based Ingress.

Explore the docs

Command cheatsheet

Terminal showing the full devopsgen help output listing all commands
devopsgen --help — all commands at a glance.
Terminal showing the devopsgen list output with available stacks, app types and registries
devopsgen list — supported stacks, app types and registries.
Terminal showing the devopsgen version output
devopsgen version.
+ + \ No newline at end of file diff --git a/docs/web/dist/init/index.html b/docs/web/dist/init/index.html new file mode 100644 index 0000000..fd0e19d --- /dev/null +++ b/docs/web/dist/init/index.html @@ -0,0 +1,26 @@ + + + + + + init command — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

command reference

devopsgen init

Initialize DevOps configuration. In an interactive terminal with no complete configuration the TUI wizard is launched; when all required parameters are supplied via flags the process runs headless and never waits for stdin.

synopsis
devopsgen init [flags]

Flags

FlagDefaultDescription
--stackauto-detectedProject stack: go or nodejs.
--app-typeauto-detectedApplication type (see devopsgen list).
--cigithub-actionsCI provider.
--registrydockerhubContainer registry: dockerhub or ghcr.
--image—Image repository as namespace/repository — no host and no tag.
--taglatestAdditional image tag alias.
--port—Application port, 1–65535.
--k8strueGenerate Kubernetes manifests.
--ingressoffEnable Kubernetes Ingress (requires --ingress-host).
--ingress-host—Ingress hostname, validated as RFC 1123.
--health-path—HTTP health probe path wired into manifests.
--build-cmd—Build command.
--build-path.Go package path to build inside the container.
--start-cmd—Startup command.
--test-cmd—Test command.
--output-dir.Output directory.
--dry-runoffSimulate without writing anything.
--forceoffOverwrite existing regular files.

Success & conflicts

A clean run writes every template and exits with 0. A second run over an existing project stops before touching anything and exits with 3 until you add --force:

devopsgen init success output with exit code 0
First run: five files created, exit code 0.
devopsgen init conflict error refusing to overwrite files with exit code 3
Re-run without --force: exit code 3, zero files touched.

Preview plans with --dry-run

--dry-run prints the byte-plan: which files would be created, which would be replaced, and which are in conflict. With a conflict present it exits 3; combined with --force a fully green plan exits 0 without writing anything:

devopsgen dry-run plan output showing replace and conflict entries with exit code 3
--dry-run over an existing project: conflict entries, exit code 3.
devopsgen dry-run force plan output where every file would be replaced, exit code 0
--dry-run --force: every file replaced in plan form, exit code 0.

Config validation

Invalid configuration fails fast with 2 before any file is planned — e.g. an image that must be namespace/repository without host or tag, a port outside 1–65535, or an Ingress without a host:

devopsgen init failing config validation on an invalid image with exit code 2
Config errors exit with code 2 and never write files.
Overwrites performed with --force create .bak backups, and any failure mid-run automatically rolls back to the pre-run state.
+ + \ No newline at end of file diff --git a/docs/web/dist/robots.txt b/docs/web/dist/robots.txt new file mode 100644 index 0000000..43ea56e --- /dev/null +++ b/docs/web/dist/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://bangkahdev.github.io/DevOpsGen/sitemap.xml diff --git a/docs/web/dist/sitemap.xml b/docs/web/dist/sitemap.xml new file mode 100644 index 0000000..46517e4 --- /dev/null +++ b/docs/web/dist/sitemap.xml @@ -0,0 +1,10 @@ + + + https://bangkahdev.github.io/DevOpsGen/2026-10-22weekly1.0 + https://bangkahdev.github.io/DevOpsGen/getting-started/2026-10-22monthly0.8 + https://bangkahdev.github.io/DevOpsGen/wizard/2026-10-22monthly0.8 + https://bangkahdev.github.io/DevOpsGen/init/2026-10-22monthly0.8 + https://bangkahdev.github.io/DevOpsGen/detect/2026-10-22monthly0.8 + https://bangkahdev.github.io/DevOpsGen/validate/2026-10-22monthly0.8 + https://bangkahdev.github.io/DevOpsGen/exit-codes/2026-10-22monthly0.8 + diff --git a/docs/web/dist/validate/index.html b/docs/web/dist/validate/index.html new file mode 100644 index 0000000..0437481 --- /dev/null +++ b/docs/web/dist/validate/index.html @@ -0,0 +1,27 @@ + + + + + + validate command — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

command reference

devopsgen validate

Statically lint DevOpsGen output — Dockerfiles, GitHub Actions workflows and Kubernetes manifests — so problems surface on your laptop instead of pipeline number forty.

devopsgen validate passing with the message internal validation passed and exit code 0
Everything checks out — exit code 0.
synopsis
devopsgen validate [flags]          # validate the current project
+devopsgen validate --output-dir ./configs

What it checks

Docker

  • FROM base images are pinned to digest form
  • Required install/prune steps match the package manager
  • Non-root user declared

GitHub Actions

  • Workflow documents carry required structure and names
  • Actions are pinned, config files complete
  • Checkout steps reference valid refs

Kubernetes

  • Resources are DNS 1123/1035 rename-clean
  • Service ↔ Ingress wiring is consistent
  • Probe/health requirements satisfied

Exit behaviour

  • 0 — all documents passed.
  • 4 — at least one document failed validation.
  • 2 — malformed input (unreadable paths, invalid flags).

Combine with devopsgen init in CI: generate, commit, validate, deploy — the exit codes page maps every failure class.

+ + \ No newline at end of file diff --git a/docs/web/dist/wizard/index.html b/docs/web/dist/wizard/index.html new file mode 100644 index 0000000..9c7bb0a --- /dev/null +++ b/docs/web/dist/wizard/index.html @@ -0,0 +1,26 @@ + + + + + + Interactive Wizard — DevOpsGen Docs + + + + + + + + + + + + + + + + + +
DGDevOpsGenDOCSGitHub

interactive wizard

The TUI wizard

When you run devopsgen init in a real terminal without a flags-only invocation, a keyboard-driven wizard takes over — Enter accepts the recommended default at every step.

DevOpsGen wizard TUI showing the stack selection list with a highlighted highlighted selection and instructions
Step 1 — stack choice. DevOpsGen pre-selects what its detector recommends.

How the interview flows

  • Stack — go or nodejs, pre-selected by the detector (detect runs first).
  • Application type (Node.js only) — express, nextjs, vite, nestjs or generic; Go projects build binaries by default.
  • Port — the application port, 1–65535, validated on the spot.
  • Container image — namespace/repository, no host, no tag.
  • Registry — Docker Hub or GHCR (also shipped as the --registry flag).
  • Kubernetes — manifests are generated by default; opt into a host-based Ingress with an RFC 1123 hostname.
  • Summary & confirm — review every chosen value, confirm, and the files are written to the project root.
A wizard run is fully replayable: the same answers as equivalent flags. To automate the same result, pass the flags headless (init reference).

Keyboard

KeyAction
EnterConfirm current selection / printed default
↑ ↓Move within list choices
typed textFill textual answers (port, image, host)
Ctrl+CAbort the wizard without writing anything

Safety during the run

Everything until the final confirmation is bound to memory only — aborting leaves the project untouched. The write itself follows the same rules as headless mode: never-overwrite-unless---force, backups, and rollback on mid-run failure.

+ + \ No newline at end of file diff --git a/docs/web/index.html b/docs/web/index.html new file mode 100644 index 0000000..05d26ef --- /dev/null +++ b/docs/web/index.html @@ -0,0 +1,12 @@ + + + + + + DevOpsGen Docs + + +
+ + + diff --git a/docs/web/package-lock.json b/docs/web/package-lock.json new file mode 100644 index 0000000..0d9ad62 --- /dev/null +++ b/docs/web/package-lock.json @@ -0,0 +1,891 @@ +{ + "name": "web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^6.1.2", + "vite": "^8.3.5" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.155.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/docs/web/package.json b/docs/web/package.json new file mode 100644 index 0000000..e3eae32 --- /dev/null +++ b/docs/web/package.json @@ -0,0 +1,20 @@ +{ + "name": "devopsgen-docs", + "private": true, + "version": "1.0.0", + "description": "React documentation site for DevOpsGen, statically prerendered for SEO", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build && vite build --ssr src/entry-server.jsx --outDir .ssr && node scripts/prerender.mjs", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^6.1.2", + "vite": "^8.3.5" + } +} diff --git a/docs/web/public/img/detect.png b/docs/web/public/img/detect.png new file mode 100644 index 0000000..f60b80a Binary files /dev/null and b/docs/web/public/img/detect.png differ diff --git a/docs/web/public/img/dry-run-force.png b/docs/web/public/img/dry-run-force.png new file mode 100644 index 0000000..55ea52b Binary files /dev/null and b/docs/web/public/img/dry-run-force.png differ diff --git a/docs/web/public/img/dry-run.png b/docs/web/public/img/dry-run.png new file mode 100644 index 0000000..9a3bd6e Binary files /dev/null and b/docs/web/public/img/dry-run.png differ diff --git a/docs/web/public/img/help.png b/docs/web/public/img/help.png new file mode 100644 index 0000000..9ad0437 Binary files /dev/null and b/docs/web/public/img/help.png differ diff --git a/docs/web/public/img/init-conflict.png b/docs/web/public/img/init-conflict.png new file mode 100644 index 0000000..0be48f9 Binary files /dev/null and b/docs/web/public/img/init-conflict.png differ diff --git a/docs/web/public/img/init-success.png b/docs/web/public/img/init-success.png new file mode 100644 index 0000000..0be48f9 Binary files /dev/null and b/docs/web/public/img/init-success.png differ diff --git a/docs/web/public/img/invalid-config.png b/docs/web/public/img/invalid-config.png new file mode 100644 index 0000000..97795c1 Binary files /dev/null and b/docs/web/public/img/invalid-config.png differ diff --git a/docs/web/public/img/list.png b/docs/web/public/img/list.png new file mode 100644 index 0000000..2ed629b Binary files /dev/null and b/docs/web/public/img/list.png differ diff --git a/docs/web/public/img/validate.png b/docs/web/public/img/validate.png new file mode 100644 index 0000000..7f4b32c Binary files /dev/null and b/docs/web/public/img/validate.png differ diff --git a/docs/web/public/img/version.png b/docs/web/public/img/version.png new file mode 100644 index 0000000..355cdf6 Binary files /dev/null and b/docs/web/public/img/version.png differ diff --git a/docs/web/public/img/wizard.png b/docs/web/public/img/wizard.png new file mode 100644 index 0000000..8e7944b Binary files /dev/null and b/docs/web/public/img/wizard.png differ diff --git a/docs/web/scripts/prerender.mjs b/docs/web/scripts/prerender.mjs new file mode 100644 index 0000000..47f2599 --- /dev/null +++ b/docs/web/scripts/prerender.mjs @@ -0,0 +1,67 @@ +import { mkdir, writeFile, copyFile, readdir } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(here, '..'); + +const { ROUTES, SITE_URL, renderDocument } = await import( + pathToFileURL(path.join(root, '.ssr', 'entry-server.js')).href +); + +const outDir = path.join(root, 'dist'); + +for (const route of ROUTES) { + const html = renderDocument(route); + if (route.path === '') { + await writeFile(path.join(outDir, 'index.html'), html, 'utf8'); + } else { + const dir = path.join(outDir, route.path); + await mkdir(dir, { recursive: true }); + await writeFile(path.join(dir, 'index.html'), html, 'utf8'); + } + console.log(`prerendered: ${route.path === '' ? '/ (home)' : '/' + route.path + '/'}`); +} + +// lastmod = build time (freshness signal) +const buildDate = new Date().toISOString().slice(0, 10); +const sitemapEntries = ROUTES.map((route) => { + const url = route.path === '' ? SITE_URL : SITE_URL + route.path + '/'; + const priority = route.path === '' ? '1.0' : '0.8'; + const freq = route.path === '' ? 'weekly' : 'monthly'; + return ` ${url}${buildDate}${freq}${priority}`; +}).join('\n'); + +await writeFile( + path.join(outDir, 'sitemap.xml'), + ` + +${sitemapEntries} + +`, + 'utf8', +); + +await writeFile( + path.join(outDir, 'robots.txt'), + `User-agent: * +Allow: / + +Sitemap: ${SITE_URL}sitemap.xml +`, + 'utf8', +); + +// Final pages are JS-free (content fully prerendered); the compiled CSS from the +// client build is copied to a stable path so every page references it relatively. +const assetsDir = path.join(outDir, 'assets'); +const assetEntries = await readdir(assetsDir); +const css = assetEntries.find((f) => f.endsWith('.css')); +if (css) { + await copyFile(path.join(assetsDir, css), path.join(assetsDir, 'site.css')); + console.log('site.css ready (from ' + css + ')'); +} else { + throw new Error('no css asset emitted by vite build'); +} + +console.log('sitemap + robots written'); diff --git a/docs/web/src/components/Code.jsx b/docs/web/src/components/Code.jsx new file mode 100644 index 0000000..8fffd5c --- /dev/null +++ b/docs/web/src/components/Code.jsx @@ -0,0 +1,10 @@ +export default function Code({ children, label }) { + return ( +
+ {label && {label}} +
+        {children}
+      
+
+ ); +} diff --git a/docs/web/src/components/Layout.jsx b/docs/web/src/components/Layout.jsx new file mode 100644 index 0000000..cac643f --- /dev/null +++ b/docs/web/src/components/Layout.jsx @@ -0,0 +1,43 @@ +import { ROUTES, SITE_URL } from '../routes.jsx'; + +export default function Layout({ rel, children }) { + return ( + <> + skip to content +
+ + DG + DevOpsGenDOCS + + + + GitHub + +
+
{children}
+ + + ); +} diff --git a/docs/web/src/components/Shot.jsx b/docs/web/src/components/Shot.jsx new file mode 100644 index 0000000..d52f59f --- /dev/null +++ b/docs/web/src/components/Shot.jsx @@ -0,0 +1,10 @@ +export default function Shot({ rel, name, alt, caption, feature = '' }) { + return ( +
+ + {alt} + + {caption &&
{caption}
} +
+ ); +} diff --git a/docs/web/src/components/Ui.jsx b/docs/web/src/components/Ui.jsx new file mode 100644 index 0000000..966fae3 --- /dev/null +++ b/docs/web/src/components/Ui.jsx @@ -0,0 +1,17 @@ +export function Kbd({ children }) { + return {children}; +} + +export function Badge({ tone = 'ok', children }) { + return {children}; +} + +export function PageHead({ kicker, title, lede }) { + return ( +
+ {kicker &&

{kicker}

} +

{title}

+ {lede &&

{lede}

} +
+ ); +} diff --git a/docs/web/src/entry-client.jsx b/docs/web/src/entry-client.jsx new file mode 100644 index 0000000..4139fec --- /dev/null +++ b/docs/web/src/entry-client.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import './styles/site.css'; +import Layout from './components/Layout.jsx'; +import { ROUTES } from './routes.jsx'; + +const route = ROUTES.find((r) => r.path === location.pathname.replace(/\/$/, '').replace(/^.*\//, '')) ?? + null; + +const rel = route && route.path === '' ? '' : '../'; +const page = route ?? ROUTES[0]; + +createRoot(document.getElementById('root')).render( + + + , +); diff --git a/docs/web/src/entry-server.jsx b/docs/web/src/entry-server.jsx new file mode 100644 index 0000000..4b6c466 --- /dev/null +++ b/docs/web/src/entry-server.jsx @@ -0,0 +1,79 @@ +import { renderToStaticMarkup } from 'react-dom/server'; +import React from 'react'; +import { ROUTES, SITE_URL, SITE_NAME } from './routes.jsx'; +import Layout from './components/Layout.jsx'; + +export { ROUTES, SITE_URL, SITE_NAME }; + +const escapeHtml = (s) => + s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + +export function relPrefix(path) { + return path === '' ? '' : '../'; +} + +export function pageUrl(path) { + return `${SITE_URL}${path === '' ? '' : path + '/'}`; +} + +export function renderDocument(route) { + const rel = relPrefix(route.path); + const url = pageUrl(route.path); + const image = `${SITE_URL}${route.ogImage}`; + const body = renderToStaticMarkup( + + + , + ); + const r = route.path === '' ? [{ '@type': 'ListItem', position: 1, name: 'Home', item: url }] : [ + { '@type': 'ListItem', position: 1, name: 'Home', item: SITE_URL }, + { + '@type': 'ListItem', + position: 2, + name: route.path.replace(/-/g, ' '), + item: url, + }, + ]; + const jsonLd = { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'SoftwareApplication', + name: 'DevOpsGen', + applicationCategory: 'DeveloperApplication', + operatingSystem: 'Linux, Windows, macOS', + description: route.description, + url: SITE_URL, + offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' }, + }, + { '@type': 'BreadcrumbList', itemListElement: r }, + ], + }; + + return ` + + + + + ${escapeHtml(route.title)} + + + + + + + + + + + + + + + + + +${body} + +`; +} diff --git a/docs/web/src/pages/Detect.jsx b/docs/web/src/pages/Detect.jsx new file mode 100644 index 0000000..0dd8d7b --- /dev/null +++ b/docs/web/src/pages/Detect.jsx @@ -0,0 +1,69 @@ +import Shot from '../components/Shot.jsx'; +import { PageHead } from '../components/Ui.jsx'; + +export default function Detect({ rel }) { + return ( + <> + + + + +

Reading the output

+
+ + + + + + + + + + + + + + + + + + + + + +
LineMeaning
stack: goThe detected programming stack (go or nodejs).
indicators: go.mod, ...The files that produced evidence — your audit trail for why the stack matched.
app-type candidates: binaryApplication types plausible for this project; the wizard default comes from here too.
+
+ +

How it decides

+
    +
  • + Go — go.mod and its module directive looking like{' '} + module example.com/app. +
  • +
  • + Node.js — package.json, plus its dependencies, and lockfile flavours + (npm / pnpm) that later steer Docker image commands. +
  • +
  • + Ambiguity or zero indicators produce a distinct exit code (see{' '} + exit codes), so scripts can branch safely. +
  • +
+ +
+ Detection semantics: DevOpsGen detects in the current working directory, not in{' '} + --output-dir. Run it from the project you want scanned. +
+ + ); +} diff --git a/docs/web/src/pages/ExitCodes.jsx b/docs/web/src/pages/ExitCodes.jsx new file mode 100644 index 0000000..46a1253 --- /dev/null +++ b/docs/web/src/pages/ExitCodes.jsx @@ -0,0 +1,101 @@ +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead } from '../components/Ui.jsx'; + +export default function ExitCodes({ rel }) { + return ( + <> + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeClassificationMeaningTypical example
0successEverything requested completed; dry-run plans that resolved cleanly also end here.init created 5 files; --dry-run --force plan is green.
1general error + Unexpected failure — I/O errors, template issues, or a run that failed mid-write + and rolled back. + Disk full while writing deployment.yaml.
2invalid inputConfiguration failed validation before anything was planned or written.--image BANGKAH/my-app; port -1; Ingress without host.
3conflict + Target files already exist and would be overwritten; nothing was written. + Retried with --force. + Second init without --force.
4validation failedGenerated documents exist but do not pass static lint rules.Unpinned workflow action in ci.yaml.
5detectionStack detection was ambiguous or found nothing at all.Empty directory without go.mod / package.json.
+
+ +

In practice

+
+ + +
+ + {`devopsgen init --stack go --registry ghcr --image you/myapp --port 8080 +case $? in + 0) echo "ready" ;; + 3) devopsgen init --force --stack go --registry ghcr --image you/myapp --port 8080 ;; + 2) echo "fix your flags" >&2 ;; + *) echo "see devopsgen validate output" >&2 ;; +esac`} + +
+ Codes are part of DevOpsGen's compatibility promise: bumping a major version may add new + codes, but existing ones keep their meaning. +
+ + ); +} diff --git a/docs/web/src/pages/GettingStarted.jsx b/docs/web/src/pages/GettingStarted.jsx new file mode 100644 index 0000000..50ac56a --- /dev/null +++ b/docs/web/src/pages/GettingStarted.jsx @@ -0,0 +1,78 @@ +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead } from '../components/Ui.jsx'; + +export default function GettingStarted({ rel }) { + return ( + <> + + +

Install

+ {`go install devopsgen.dev/devopsgen@latest`} +

+ Or grab a prebuilt binary from the project{' '} + + GitHub releases + + , or build from source: +

+ {`git clone https://github.com/Bangkahdev/DevOpsGen.git +cd DevOpsGen +make build`} + +

Two ways to run it

+
    +
  • + Interactive: run devopsgen init in a terminal — the wizard + detects your stack and interviews you step by step. +
  • +
  • + Headless: pass every required parameter as flags — the process never + waits for stdin, which makes it script- and CI-friendly. +
  • +
+ +

Headless quickstart

+ {`devopsgen init \ + --stack go \ + --ci github-actions \ + --registry ghcr \ + --image you/myapp \ + --port 8080`} + +

Inside a Go project this writes:

+ {`.github/workflows/ci.yaml # test, vet, build, docker publish +Kubernetes/deployment.yaml # non-root, probes, DNS-1053 names +Kubernetes/service.yaml # cluster service +Kubernetes/ingress.yaml # optional, needs --ingress +Dockerfile # hardened multi-stage build + .dockerignore`} +
+ Second runs are safe: DevOpsGen refuses to overwrite without --force and + exits with code 3 — see the init reference. +
+ +

Next steps

+ + + ); +} diff --git a/docs/web/src/pages/Home.jsx b/docs/web/src/pages/Home.jsx new file mode 100644 index 0000000..c8e03eb --- /dev/null +++ b/docs/web/src/pages/Home.jsx @@ -0,0 +1,174 @@ +import { ROUTES, SITE_NAME } from '../routes.jsx'; +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead } from '../components/Ui.jsx'; + +export default function Home({ rel }) { + return ( + <> +
+
+

docker · github actions · kubernetes

+

+ DevOps scaffolding, +
+ deterministically. +

+

+ DevOpsGen reads your Go or Node.js project and writes a complete, opinionated DevOps + baseline: hardened Dockerfile, GitHub Actions pipeline and ready-to-apply Kubernetes + manifests — with dry-run plans, automatic backups and config validation built in. +

+ +
+ +
+ +

Why DevOpsGen

+
+
+

Deterministic

+

+ Same inputs, same bytes — every run. Machine-friendly for code review: a second + generation with --force produces bit-identical files. +

+
+
+

Safe by default

+

+ Existing files are never overwritten without --force. Overwrites get{' '} + .bak backups, failed runs roll back automatically, and{' '} + --dry-run prints the full plan first. +

+
+
+

Validated output

+

+ Built-in validation checks Dockerfile FROM digests, workflow action + pinning, Kubernetes naming and health probes before anything ships. +

+
+
+

Stack aware

+

+ Detects Go and Node.js projects (npm, pnpm, Express, Next.js, Vite, NestJS) from{' '} + go.mod / package.json and tailors templates accordingly. +

+
+
+ +

Everything a project needs on day one

+

+ One init command writes the whole baseline tree. Preview the real terminal + output of every command below — all screenshots are live captures, not mockups. +

+ {`$ devopsgen init --stack go --ci github-actions --registry ghcr --image you/myapp --port 8080 +created 5 file(s), replaced 0 file(s) in . +exit code: 0`} +
+ + + +
+ +

What gets generated

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Template setFilesPurpose
docker (+ .dockerignore)Dockerfile + Hardened image build with non-root user, health check hooks, install/prune + commands derived from package manager (npm / pnpm) and lockfile detection. +
github-actions.github/workflows/ci.yaml + Test/build across Go and Node.js versions, docker build & push, coverage + floors, manifest validation. +
kubernetesdeployment.yaml, service.yaml, optional ingress.yaml + Non-root security context, probes using your health path, RFC 1123-validated + names, optional host-based Ingress. +
+
+ +

Explore the docs

+
+ {ROUTES.filter((r) => r.path !== '').map((r) => ( + +

{r.path.replace(/-/g, ' ')}

+

{r.description}

+
+ ))} +
+ +

Command cheatsheet

+
+ + + +
+ + ); +} diff --git a/docs/web/src/pages/InitCmd.jsx b/docs/web/src/pages/InitCmd.jsx new file mode 100644 index 0000000..752869e --- /dev/null +++ b/docs/web/src/pages/InitCmd.jsx @@ -0,0 +1,185 @@ +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead, Badge } from '../components/Ui.jsx'; + +export default function InitCmd({ rel }) { + return ( + <> + + + {`devopsgen init [flags]`} + +

Flags

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FlagDefaultDescription
--stackauto-detectedProject stack: go or nodejs.
--app-typeauto-detectedApplication type (see devopsgen list).
--cigithub-actionsCI provider.
--registrydockerhubContainer registry: dockerhub or ghcr.
--image— + Image repository as namespace/repository — no host and no tag. +
--taglatestAdditional image tag alias.
--port—Application port, 1–65535.
--k8strueGenerate Kubernetes manifests.
--ingressoffEnable Kubernetes Ingress (requires --ingress-host).
--ingress-host—Ingress hostname, validated as RFC 1123.
--health-path—HTTP health probe path wired into manifests.
--build-cmd—Build command.
--build-path.Go package path to build inside the container.
--start-cmd—Startup command.
--test-cmd—Test command.
--output-dir.Output directory.
--dry-runoffSimulate without writing anything.
--forceoffOverwrite existing regular files.
+
+ +

Success & conflicts

+

+ A clean run writes every template and exits with{' '} + 0. A second run over an existing project stops before touching + anything and exits with 3 until you add --force: +

+
+ + +
+ +

Preview plans with --dry-run

+

+ --dry-run prints the byte-plan: which files would be created, which would be + replaced, and which are in conflict. With a conflict present it exits{' '} + 3; combined with --force a fully green plan exits{' '} + 0 without writing anything: +

+
+ + +
+ +

Config validation

+

+ Invalid configuration fails fast with 2 before any file is + planned — e.g. an image that must be namespace/repository without host or + tag, a port outside 1–65535, or an Ingress without a host: +

+ +
+ Overwrites performed with --force create .bak backups, and any + failure mid-run automatically rolls back to the pre-run state. +
+ + ); +} diff --git a/docs/web/src/pages/Validate.jsx b/docs/web/src/pages/Validate.jsx new file mode 100644 index 0000000..5f73f6d --- /dev/null +++ b/docs/web/src/pages/Validate.jsx @@ -0,0 +1,71 @@ +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead, Badge } from '../components/Ui.jsx'; + +export default function Validate({ rel }) { + return ( + <> + + + + + {`devopsgen validate [flags] # validate the current project +devopsgen validate --output-dir ./configs`} + +

What it checks

+
+
+

Docker

+
    +
  • FROM base images are pinned to digest form
  • +
  • Required install/prune steps match the package manager
  • +
  • Non-root user declared
  • +
+
+
+

GitHub Actions

+
    +
  • Workflow documents carry required structure and names
  • +
  • Actions are pinned, config files complete
  • +
  • Checkout steps reference valid refs
  • +
+
+
+

Kubernetes

+
    +
  • Resources are DNS 1123/1035 rename-clean
  • +
  • Service ↔ Ingress wiring is consistent
  • +
  • Probe/health requirements satisfied
  • +
+
+
+ +

Exit behaviour

+
    +
  • + 0 — all documents passed. +
  • +
  • + 4 — at least one document failed validation. +
  • +
  • + 2 — malformed input (unreadable paths, invalid flags). +
  • +
+

+ Combine with devopsgen init in CI: generate, commit, validate, deploy — the{' '} + exit codes page maps every failure class. +

+ + ); +} diff --git a/docs/web/src/pages/Wizard.jsx b/docs/web/src/pages/Wizard.jsx new file mode 100644 index 0000000..5fa8ae2 --- /dev/null +++ b/docs/web/src/pages/Wizard.jsx @@ -0,0 +1,95 @@ +import Shot from '../components/Shot.jsx'; +import Code from '../components/Code.jsx'; +import { PageHead, Kbd } from '../components/Ui.jsx'; + +export default function Wizard({ rel }) { + return ( + <> + + + + +

How the interview flows

+
    +
  • + Stack — go or nodejs, pre-selected by the + detector (detect runs first). +
  • +
  • + Application type (Node.js only) — express, nextjs, vite, nestjs or + generic; Go projects build binaries by default. +
  • +
  • + Port — the application port, 1–65535, validated on the spot. +
  • +
  • + Container image — namespace/repository, no host, no tag. +
  • +
  • + Registry — Docker Hub or GHCR (also shipped as the{' '} + --registry flag). +
  • +
  • + Kubernetes — manifests are generated by default; opt into a + host-based Ingress with an RFC 1123 hostname. +
  • +
  • + Summary & confirm — review every chosen value, confirm, and the + files are written to the project root. +
  • +
+ +
+ A wizard run is fully replayable: the same answers as equivalent flags. To automate the + same result, pass the flags headless (init reference). +
+ +

Keyboard

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
KeyAction
EnterConfirm current selection / printed default
↑ ↓Move within list choices
typed textFill textual answers (port, image, host)
Ctrl+CAbort the wizard without writing anything
+
+ +

Safety during the run

+

+ Everything until the final confirmation is bound to memory only — aborting leaves the + project untouched. The write itself follows the same rules as headless mode: + never-overwrite-unless---force, backups, and rollback on mid-run failure. +

+ + ); +} diff --git a/docs/web/src/routes.jsx b/docs/web/src/routes.jsx new file mode 100644 index 0000000..f3320cf --- /dev/null +++ b/docs/web/src/routes.jsx @@ -0,0 +1,76 @@ +import Home from './pages/Home.jsx'; +import GettingStarted from './pages/GettingStarted.jsx'; +import Wizard from './pages/Wizard.jsx'; +import InitCmd from './pages/InitCmd.jsx'; +import Detect from './pages/Detect.jsx'; +import Validate from './pages/Validate.jsx'; +import ExitCodes from './pages/ExitCodes.jsx'; + +export const SITE_URL = 'https://bangkahdev.github.io/DevOpsGen/'; +export const SITE_NAME = 'DevOpsGen'; + +export const ROUTES = [ + { + path: '', + component: Home, + title: 'DevOpsGen — DevOps scaffolding for Go & Node.js projects', + description: + 'DevOpsGen generates production-ready Dockerfiles, GitHub Actions pipelines and Kubernetes manifests for Go and Node.js projects, with safe dry-run plans, automatic backups and config validation.', + keywords: 'DevOpsGen, dockerfile generator, github actions generator, kubernetes manifests, go, nodejs, scaffolding, ci cd', + ogImage: 'img/init-success.png', + }, + { + path: 'getting-started', + component: GettingStarted, + title: 'Getting Started — DevOpsGen Docs', + description: + 'Install DevOpsGen via go install or a release binary, then generate a complete Docker, CI and Kubernetes setup for your project with a single command.', + keywords: 'DevOpsGen install, go install, quickstart, getting started', + ogImage: 'img/init-success.png', + }, + { + path: 'wizard', + component: Wizard, + title: 'Interactive Wizard — DevOpsGen Docs', + description: + 'DevOpsGen launches an interactive TUI wizard that walks through stack detection, ports, container images and Kubernetes options, then writes the generated configuration.', + keywords: 'DevOpsGen wizard, tui, bubbletea, interactive', + ogImage: 'img/wizard.png', + }, + { + path: 'init', + component: InitCmd, + title: 'init command — DevOpsGen Docs', + description: + 'Full reference for devopsgen init flags: stack, app type, CI provider, registry, image, port, Kubernetes ingress, health path, dry-run and force overwrite behaviour.', + keywords: 'devopsgen init, flags, dry-run, force, image, ingress', + ogImage: 'img/init-success.png', + }, + { + path: 'detect', + component: Detect, + title: 'detect command — DevOpsGen Docs', + description: + 'devopsgen detect inspects the current project and reports the programming stack, the files it based its decision on, and candidate application types.', + keywords: 'devopsgen detect, stack detection, go.mod, package.json', + ogImage: 'img/detect.png', + }, + { + path: 'validate', + component: Validate, + title: 'validate command — DevOpsGen Docs', + description: + 'devopsgen validate statically checks generated Dockerfiles, GitHub Actions workflows and Kubernetes manifests: FROM digests, action pinning, naming rules, probes and more.', + keywords: 'devopsgen validate, dockerfile lint, workflow lint, kubernetes manifest lint', + ogImage: 'img/validate.png', + }, + { + path: 'exit-codes', + component: ExitCodes, + title: 'Exit codes — DevOpsGen Docs', + description: + 'Every devopsgen command exits with a documented status code: 0 success, 1 general error, 2 invalid configuration, 3 file conflicts, 4 validation failure, 5 ambiguous detection.', + keywords: 'devopsgen exit codes, ci integration, pipeline status', + ogImage: 'img/invalid-config.png', + }, +]; diff --git a/docs/web/src/styles/site.css b/docs/web/src/styles/site.css new file mode 100644 index 0000000..59f2727 --- /dev/null +++ b/docs/web/src/styles/site.css @@ -0,0 +1,425 @@ +/* ============ DevOpsGen Docs — Neo Brutalism, soft & slightly formal ============ */ +:root { + --paper: #faf7f0; + --ink: #37322b; /* warm charcoal instead of pure black */ + --outline: #4a443c; /* border/shadow tone */ + --border: 3px solid var(--outline); + --shadow: 6px 6px 0 var(--outline); + --shadow-sm: 3px 3px 0 var(--outline); + /* soft palette */ + --yellow: #f2e3bd; /* soft butter */ + --pink: #f3cfcb; /* dusty rose */ + --blue: #c9dced; /* soft periwinkle */ + --lime: #d3e6cf; /* soft sage */ + --purple: #ddcfe9; /* soft lavender */ + --red-b: #e8b0a6; /* muted terracotta */ + --green-b: #b5d9c2; /* soft mint */ + --nav-bg: #3b352e; + --nav-ink: #faf3e3; + --font: "Segoe UI", "Verdana", "Tahoma", sans-serif; + --mono: "Cascadia Code", "Consolas", "JetBrains Mono", monospace; +} + +* { box-sizing: border-box; } + +html { scroll-behavior: smooth; } + +body { + margin: 0; + font-family: var(--font); + background: var(--paper); + color: var(--ink); + line-height: 1.65; + -webkit-font-smoothing: antialiased; +} + +/* faint warm grid */ +body::before { + content: ""; + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + background-image: + linear-gradient(rgba(74, 68, 60, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(74, 68, 60, 0.05) 1px, transparent 1px); + background-size: 32px 32px; +} + +.skip { + position: absolute; + left: -9999px; + top: 0; + background: var(--yellow); + border: var(--border); + padding: 8px 16px; + font-weight: 700; + z-index: 99; +} +.skip:focus { left: 8px; } + +/* ---------- top bar ---------- */ +.topbar { + position: sticky; + top: 8px; + z-index: 50; + margin: 8px 12px; + padding: 8px 14px; + background: var(--nav-bg); + color: var(--nav-ink); + border: var(--border); + box-shadow: var(--shadow); + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + color: var(--nav-ink); + text-decoration: none; + font-weight: 800; + font-size: 1.1rem; + letter-spacing: 0.3px; +} + +.brand-mark { + background: var(--pink); + color: var(--ink); + border: 2px solid var(--nav-ink); + padding: 2px 8px; + font-weight: 800; + transform: rotate(-1.5deg); +} + +.brand-sub { + background: var(--blue); + color: var(--ink); + font-size: 0.62rem; + padding: 1px 6px; + margin-left: 8px; + transform: rotate(1deg); +} + +.topbar nav ul { + display: flex; + flex-wrap: wrap; + gap: 4px; + list-style: none; + margin: 0; + padding: 0; +} + +.nav-link { + display: inline-block; + color: var(--nav-ink); + text-decoration: none; + font-weight: 700; + font-size: 0.82rem; + text-transform: uppercase; + letter-spacing: 0.8px; + padding: 4px 10px; + border: 2px solid transparent; +} +.nav-link:hover { background: var(--nav-ink); color: var(--nav-bg); border-color: var(--nav-ink); } + +.gh-pill { + margin-left: auto; + background: var(--yellow); + color: var(--ink); + text-decoration: none; + font-weight: 800; + padding: 4px 14px; + border: 2px solid var(--nav-ink); + box-shadow: 2px 2px 0 var(--nav-ink); +} +.gh-pill:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--nav-ink); } + +/* ---------- layout ---------- */ +main { + position: relative; + z-index: 1; + max-width: 1080px; + margin: 36px auto; + padding: 0 20px; +} + +.page-head { margin-bottom: 28px; } + +.kicker { + display: inline-block; + background: var(--lime); + border: var(--border); + box-shadow: var(--shadow-sm); + font-weight: 800; + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 1.6px; + padding: 4px 12px; + margin: 0 0 14px 0; +} + +.violet { background: var(--purple); } +.blue { background: var(--blue); } +.pink { background: var(--pink); } +.yellow { background: var(--yellow); } + +h1 { + font-size: clamp(2.1rem, 5vw, 3.4rem); + line-height: 1.08; + margin: 0 0 16px 0; + font-weight: 800; + letter-spacing: -0.5px; + text-transform: uppercase; +} + +.lede { + font-size: 1.12rem; + max-width: 740px; + background: var(--yellow); + border: var(--border); + box-shadow: var(--shadow-sm); + padding: 12px 16px; + display: inline-block; +} + +h2 { + font-size: 1.55rem; + font-weight: 800; + margin: 44px 0 14px; + padding-left: 12px; + border-left: 10px solid var(--outline); +} + +h3 { + font-size: 1.12rem; + font-weight: 800; + margin: 26px 0 10px; +} + +p, li { font-size: 1.02rem; } +strong { background: var(--lime); padding: 0 3px; } +code { font-family: var(--mono); font-size: 0.92em; background: var(--nav-bg); color: var(--nav-ink); padding: 2px 6px; } + +a { color: var(--ink); text-decoration-thickness: 2px; text-decoration-color: var(--red-b); } +a:hover { background: var(--pink); } + +/* ---------- hero (home) ---------- */ +.hero { + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 24px; + align-items: start; + margin-bottom: 20px; +} + +.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; } + +.btn { + display: inline-block; + font-weight: 800; + text-transform: uppercase; + text-decoration: none; + padding: 11px 20px; + border: var(--border); + box-shadow: var(--shadow); + font-size: 0.95rem; + background: var(--blue); + color: var(--ink); +} +.btn:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--outline); } +.btn-pink { background: var(--pink); } +.btn-yellow { background: var(--yellow); } + +/* ---------- cards ---------- */ +.cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 20px; +} + +.card { + background: #fffdf8; + border: var(--border); + box-shadow: var(--shadow); + padding: 18px; + position: relative; +} +.card h3 { margin-top: 0; } +.card:nth-child(3n + 1) { background: var(--yellow); } +.card:nth-child(3n + 2) { background: #fffdf8; } +.card:nth-child(3n) { background: var(--blue); } +.card:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--outline); } + +.core .card { background: #fffdf8; } +.core .card:hover { background: var(--lime); } + +/* ---------- screenshots ---------- */ +.shot { + margin: 22px 0; + padding: 12px; + background: #fffdf8; + border: var(--border); + box-shadow: var(--shadow); + max-width: 100%; +} +.shot img { display: block; width: 100%; height: auto; border: 2px solid var(--outline); } +.shot figcaption { + margin-top: 10px; + font-weight: 600; + font-size: 0.9rem; + border-top: 2px dashed var(--outline); + padding-top: 8px; +} +.shot a:hover { background: transparent; } +.shot img:hover { transform: translate(-1px, -1px); } +.shot-feature { max-width: 860px; } + +.screen-strip { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 22px; + align-items: start; +} +.screen-strip .shot { margin: 0 0 12px 0; } + +/* ---------- code ---------- */ +.code-block { + position: relative; + background: var(--nav-bg); + color: var(--nav-ink); + border: var(--border); + box-shadow: var(--shadow); + margin: 18px 0; +} +.code-label { + position: absolute; + top: -15px; + left: 12px; + background: var(--purple); + border: 2px solid var(--outline); + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; + padding: 2px 10px; + letter-spacing: 1px; + color: var(--ink); +} +.code-block pre { + margin: 0; + padding: 16px 18px; + overflow-x: auto; + font-family: var(--mono); + font-size: 0.88rem; + line-height: 1.55; +} +.code-block code { background: transparent; color: inherit; padding: 0; } + +/* ---------- tables ---------- */ +.table-wrap { overflow-x: auto; border: var(--border); box-shadow: var(--shadow); background: #fffdf8; margin: 18px 0; } +table { border-collapse: collapse; width: 100%; } +th, td { border: 2px solid var(--outline); padding: 10px 12px; text-align: left; vertical-align: top; } +th { background: var(--nav-bg); color: var(--nav-ink); text-transform: uppercase; font-size: 0.78rem; letter-spacing: 1px; } +tbody tr:nth-child(2n) td { background: var(--yellow); } +td code { white-space: nowrap; } +.num { font-weight: 800; font-size: 1.05rem; } + +/* ---------- badges & kbd ---------- */ +.badge { + display: inline-block; + font-weight: 800; + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.8px; + padding: 3px 10px; + border: 2px solid var(--outline); + transform: rotate(-1deg); + margin-right: 6px; +} +.badge-ok { background: var(--green-b); color: var(--ink); } +.badge-warn { background: var(--yellow); } +.badge-err { background: var(--red-b); color: var(--ink); } +.badge-info { background: var(--blue); } + +.kbd { + font-family: var(--mono); + font-weight: 700; + border: 2px solid var(--outline); + box-shadow: 2px 2px 0 var(--outline); + padding: 2px 8px; + font-size: 0.85rem; + background: #fffdf8; + display: inline-block; +} + +/* ---------- misc ---------- */ +.callout { + border: var(--border); + box-shadow: var(--shadow-sm); + background: var(--pink); + padding: 14px 18px; + margin: 22px 0; + font-weight: 600; +} +.callout-info { background: var(--blue); } +.callout-ok { background: var(--lime); } + +.file-tree { + font-family: var(--mono); + font-size: 0.9rem; + white-space: pre; + overflow-x: auto; +} + +.footer { + position: relative; + z-index: 1; + margin: 48px 12px 12px; + padding: 18px 22px; + border: var(--border); + box-shadow: var(--shadow); + background: var(--nav-bg); + color: var(--nav-ink); + font-size: 0.9rem; +} +.footer a { color: var(--yellow); text-decoration-color: var(--yellow); } +.footer a:hover { background: var(--yellow); color: var(--ink); } +.footer-links { margin-top: 6px; } + +ul { padding-left: 22px; } +li + li { margin-top: 4px; } + +.step-list { counter-reset: steps; list-style: none; padding-left: 0; } +.step-list li { + counter-increment: steps; + position: relative; + padding-left: 52px; + margin-top: 14px; +} +.step-list li::before { + content: counter(steps); + position: absolute; + left: 0; + top: -4px; + width: 32px; + height: 32px; + display: grid; + place-items: center; + background: var(--purple); + color: var(--ink); + border: 2px solid var(--outline); + box-shadow: 2px 2px 0 var(--outline); + font-weight: 800; +} + +.flag-name { font-family: var(--mono); font-weight: 700; } + +@media (max-width: 720px) { + .hero { grid-template-columns: 1fr; } + .topbar { gap: 8px; } + .gh-pill { margin-left: 0; } + main { margin: 18px auto; } +} diff --git a/docs/web/vite.config.mjs b/docs/web/vite.config.mjs new file mode 100644 index 0000000..c7ab49d --- /dev/null +++ b/docs/web/vite.config.mjs @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + base: './', + plugins: [react()], + build: { + outDir: 'dist', + }, +});