|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 6 | + <title>envlint | Validate .env Files in CI</title> |
| 7 | + <meta name="description" content="Validate your .env files in CI before your app even starts. Works with any language. Schema-based validation for environment variables."> |
| 8 | + <link rel="preconnect" href="https://fonts.googleapis.com"> |
| 9 | + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| 10 | + <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> |
| 11 | + <style> |
| 12 | + *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| 13 | + :root { |
| 14 | + --bg: #030014; |
| 15 | + --text: #ffffff; |
| 16 | + --accent-1: #14b8a6; |
| 17 | + --accent-2: #06b6d4; |
| 18 | + --accent-3: #22c55e; |
| 19 | + --card-bg: rgba(20, 184, 166, 0.08); |
| 20 | + --card-border: rgba(20, 184, 166, 0.2); |
| 21 | + } |
| 22 | + html { scroll-behavior: smooth; } |
| 23 | + body { |
| 24 | + font-family: 'Space Grotesk', -apple-system, sans-serif; |
| 25 | + background: var(--bg); |
| 26 | + color: var(--text); |
| 27 | + line-height: 1.6; |
| 28 | + overflow-x: hidden; |
| 29 | + } |
| 30 | + code, pre { font-family: 'JetBrains Mono', monospace; } |
| 31 | + .bg-gradient { |
| 32 | + position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; |
| 33 | + } |
| 34 | + .bg-gradient::before { |
| 35 | + content: ''; position: absolute; width: 150%; height: 150%; top: -25%; left: -25%; |
| 36 | + background: |
| 37 | + radial-gradient(ellipse at 20% 30%, rgba(20, 184, 166, 0.25) 0%, transparent 50%), |
| 38 | + radial-gradient(ellipse at 80% 50%, rgba(6, 182, 212, 0.2) 0%, transparent 50%), |
| 39 | + radial-gradient(ellipse at 40% 80%, rgba(34, 197, 94, 0.15) 0%, transparent 50%); |
| 40 | + animation: gradient-shift 20s ease-in-out infinite; |
| 41 | + } |
| 42 | + @keyframes gradient-shift { |
| 43 | + 0%, 100% { transform: translate(0, 0) rotate(0deg); } |
| 44 | + 50% { transform: translate(2%, 2%) rotate(1deg); } |
| 45 | + } |
| 46 | + .container { max-width: 900px; margin: 0 auto; padding: 0 24px; } |
| 47 | + |
| 48 | + .hero { |
| 49 | + min-height: 70vh; display: flex; flex-direction: column; justify-content: center; |
| 50 | + padding: 100px 0 60px; text-align: center; |
| 51 | + } |
| 52 | + .logo { font-size: 4rem; margin-bottom: 16px; } |
| 53 | + .hero h1 { |
| 54 | + font-size: clamp(2.5rem, 6vw, 4rem); font-weight: 700; margin-bottom: 16px; |
| 55 | + background: linear-gradient(135deg, var(--accent-1), var(--accent-2)); |
| 56 | + -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; |
| 57 | + } |
| 58 | + .hero .tagline { |
| 59 | + font-size: 1.25rem; color: rgba(255,255,255,0.7); max-width: 600px; margin: 0 auto 32px; |
| 60 | + } |
| 61 | + .badges { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-bottom: 32px; } |
| 62 | + .badge { |
| 63 | + padding: 8px 16px; background: var(--card-bg); border: 1px solid var(--card-border); |
| 64 | + border-radius: 999px; font-size: 0.85rem; color: var(--accent-1); |
| 65 | + } |
| 66 | + .badge-highlight { |
| 67 | + background: rgba(34, 197, 94, 0.15); border-color: rgba(34, 197, 94, 0.3); |
| 68 | + color: #22c55e; |
| 69 | + } |
| 70 | + .buttons { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; } |
| 71 | + .btn { |
| 72 | + display: inline-flex; align-items: center; gap: 8px; padding: 14px 28px; |
| 73 | + border-radius: 12px; font-size: 1rem; font-weight: 600; text-decoration: none; |
| 74 | + transition: all 0.3s ease; |
| 75 | + } |
| 76 | + .btn-primary { |
| 77 | + background: linear-gradient(135deg, var(--accent-1), var(--accent-2)); color: white; |
| 78 | + } |
| 79 | + .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 20px 40px rgba(20, 184, 166, 0.3); } |
| 80 | + .btn-secondary { |
| 81 | + background: var(--card-bg); color: white; border: 1px solid var(--card-border); |
| 82 | + } |
| 83 | + .btn-secondary:hover { border-color: var(--accent-1); } |
| 84 | + .btn svg { width: 20px; height: 20px; } |
| 85 | + |
| 86 | + .install { |
| 87 | + background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px; |
| 88 | + padding: 20px 24px; margin-bottom: 60px; display: flex; align-items: center; |
| 89 | + justify-content: space-between; gap: 16px; flex-wrap: wrap; |
| 90 | + } |
| 91 | + .install code { font-size: 1.1rem; color: var(--accent-3); } |
| 92 | + .install button { |
| 93 | + background: rgba(255,255,255,0.1); border: none; padding: 8px 16px; |
| 94 | + border-radius: 8px; color: white; cursor: pointer; font-size: 0.9rem; |
| 95 | + } |
| 96 | + .install button:hover { background: rgba(255,255,255,0.2); } |
| 97 | + |
| 98 | + .features { padding: 60px 0; } |
| 99 | + .section-title { font-size: 2rem; font-weight: 700; text-align: center; margin-bottom: 48px; } |
| 100 | + .feature-grid { |
| 101 | + display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 24px; |
| 102 | + } |
| 103 | + .feature { |
| 104 | + background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px; |
| 105 | + padding: 24px; transition: all 0.3s ease; |
| 106 | + } |
| 107 | + .feature:hover { border-color: var(--accent-1); transform: translateY(-4px); } |
| 108 | + .feature-icon { font-size: 2rem; margin-bottom: 12px; } |
| 109 | + .feature h3 { font-size: 1.1rem; margin-bottom: 8px; } |
| 110 | + .feature p { font-size: 0.9rem; color: rgba(255,255,255,0.7); } |
| 111 | + |
| 112 | + .code-section { padding: 60px 0; } |
| 113 | + .code-block { |
| 114 | + background: #0d1117; border: 1px solid #30363d; border-radius: 12px; overflow: hidden; |
| 115 | + margin-bottom: 24px; |
| 116 | + } |
| 117 | + .code-header { |
| 118 | + background: #161b22; padding: 12px 16px; border-bottom: 1px solid #30363d; |
| 119 | + font-size: 0.85rem; color: rgba(255,255,255,0.6); |
| 120 | + } |
| 121 | + .code-block pre { padding: 20px; overflow-x: auto; font-size: 0.9rem; line-height: 1.6; } |
| 122 | + .code-block code { color: #e6edf3; } |
| 123 | + .comment { color: #8b949e; } |
| 124 | + .keyword { color: #ff7b72; } |
| 125 | + .string { color: #a5d6ff; } |
| 126 | + .key { color: #7ee787; } |
| 127 | + |
| 128 | + .types { padding: 40px 0; } |
| 129 | + .type-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; } |
| 130 | + .type-tag { |
| 131 | + padding: 12px 16px; background: var(--card-bg); border: 1px solid var(--card-border); |
| 132 | + border-radius: 10px; text-align: center; font-size: 0.9rem; font-weight: 500; |
| 133 | + } |
| 134 | + |
| 135 | + footer { |
| 136 | + padding: 40px 0; text-align: center; border-top: 1px solid var(--card-border); margin-top: 60px; |
| 137 | + } |
| 138 | + footer p { color: rgba(255,255,255,0.5); font-size: 0.9rem; } |
| 139 | + footer a { color: var(--accent-1); text-decoration: none; } |
| 140 | + footer a:hover { text-decoration: underline; } |
| 141 | + |
| 142 | + @media (max-width: 600px) { |
| 143 | + .hero { padding: 60px 0 40px; min-height: auto; } |
| 144 | + .install { flex-direction: column; text-align: center; } |
| 145 | + } |
| 146 | + </style> |
| 147 | +</head> |
| 148 | +<body> |
| 149 | + <div class="bg-gradient"></div> |
| 150 | + |
| 151 | + <section class="hero"> |
| 152 | + <div class="container"> |
| 153 | + <div class="logo">✅</div> |
| 154 | + <h1>envlint</h1> |
| 155 | + <p class="tagline">Validate your .env files in CI before your app even starts. Works with any language. Schema-based validation for environment variables.</p> |
| 156 | + <div class="badges"> |
| 157 | + <span class="badge">Python CLI</span> |
| 158 | + <span class="badge">Language Agnostic</span> |
| 159 | + <span class="badge badge-highlight">CI/CD Ready</span> |
| 160 | + <span class="badge">GPL-3.0</span> |
| 161 | + </div> |
| 162 | + <div class="buttons"> |
| 163 | + <a href="https://github.com/cainky/envlint" target="_blank" class="btn btn-primary"> |
| 164 | + <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg> |
| 165 | + View on GitHub |
| 166 | + </a> |
| 167 | + <a href="https://pypi.org/project/envlint/" target="_blank" class="btn btn-secondary"> |
| 168 | + <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg> |
| 169 | + PyPI Package |
| 170 | + </a> |
| 171 | + </div> |
| 172 | + </div> |
| 173 | + </section> |
| 174 | + |
| 175 | + <div class="container"> |
| 176 | + <div class="install"> |
| 177 | + <code>pip install envlint</code> |
| 178 | + <button onclick="navigator.clipboard.writeText('pip install envlint')">Copy</button> |
| 179 | + </div> |
| 180 | + </div> |
| 181 | + |
| 182 | + <section class="features"> |
| 183 | + <div class="container"> |
| 184 | + <h2 class="section-title">Features</h2> |
| 185 | + <div class="feature-grid"> |
| 186 | + <div class="feature"> |
| 187 | + <div class="feature-icon">🌐</div> |
| 188 | + <h3>Language Agnostic</h3> |
| 189 | + <p>One schema works across all your projects - Python, Node, Go, Ruby, or any stack.</p> |
| 190 | + </div> |
| 191 | + <div class="feature"> |
| 192 | + <div class="feature-icon">🔒</div> |
| 193 | + <h3>Build-Time Validation</h3> |
| 194 | + <p>Catch missing or invalid env vars before deployment, not in production.</p> |
| 195 | + </div> |
| 196 | + <div class="feature"> |
| 197 | + <div class="feature-icon">⚡</div> |
| 198 | + <h3>Zero Runtime Impact</h3> |
| 199 | + <p>No dependencies or code changes needed. Just validate and deploy.</p> |
| 200 | + </div> |
| 201 | + <div class="feature"> |
| 202 | + <div class="feature-icon">🔄</div> |
| 203 | + <h3>CI/CD Native</h3> |
| 204 | + <p>Exit codes designed for automation. Easy GitHub Actions integration.</p> |
| 205 | + </div> |
| 206 | + <div class="feature"> |
| 207 | + <div class="feature-icon">📝</div> |
| 208 | + <h3>YAML Schema</h3> |
| 209 | + <p>Human-readable schema files with types, patterns, and defaults.</p> |
| 210 | + </div> |
| 211 | + <div class="feature"> |
| 212 | + <div class="feature-icon">🚀</div> |
| 213 | + <h3>Auto-Generate Schema</h3> |
| 214 | + <p>Bootstrap from existing .env files with <code>envlint init</code>.</p> |
| 215 | + </div> |
| 216 | + </div> |
| 217 | + </div> |
| 218 | + </section> |
| 219 | + |
| 220 | + <section class="types"> |
| 221 | + <div class="container"> |
| 222 | + <h2 class="section-title">Supported Types</h2> |
| 223 | + <div class="type-grid"> |
| 224 | + <div class="type-tag">string</div> |
| 225 | + <div class="type-tag">integer</div> |
| 226 | + <div class="type-tag">float</div> |
| 227 | + <div class="type-tag">boolean</div> |
| 228 | + <div class="type-tag">url</div> |
| 229 | + <div class="type-tag">email</div> |
| 230 | + <div class="type-tag">port</div> |
| 231 | + <div class="type-tag">path</div> |
| 232 | + </div> |
| 233 | + </div> |
| 234 | + </section> |
| 235 | + |
| 236 | + <section class="code-section"> |
| 237 | + <div class="container"> |
| 238 | + <h2 class="section-title">Quick Start</h2> |
| 239 | + <div class="code-block"> |
| 240 | + <div class="code-header">.env.schema</div> |
| 241 | + <pre><code><span class="key">DATABASE_URL</span>: |
| 242 | + <span class="key">type</span>: <span class="string">url</span> |
| 243 | + <span class="key">required</span>: <span class="string">true</span> |
| 244 | + |
| 245 | +<span class="key">API_PORT</span>: |
| 246 | + <span class="key">type</span>: <span class="string">port</span> |
| 247 | + <span class="key">default</span>: <span class="string">3000</span> |
| 248 | + |
| 249 | +<span class="key">DEBUG</span>: |
| 250 | + <span class="key">type</span>: <span class="string">boolean</span> |
| 251 | + <span class="key">default</span>: <span class="string">false</span> |
| 252 | + |
| 253 | +<span class="key">ADMIN_EMAIL</span>: |
| 254 | + <span class="key">type</span>: <span class="string">email</span> |
| 255 | + <span class="key">required</span>: <span class="string">true</span></code></pre> |
| 256 | + </div> |
| 257 | + <div class="code-block"> |
| 258 | + <div class="code-header">Terminal</div> |
| 259 | + <pre><code><span class="comment"># Validate your .env file</span> |
| 260 | +$ envlint check |
| 261 | +<span class="string">✓ All 4 variables validated successfully</span> |
| 262 | + |
| 263 | +<span class="comment"># Strict mode - fail on undefined vars</span> |
| 264 | +$ envlint check --strict |
| 265 | + |
| 266 | +<span class="comment"># Generate schema from existing .env</span> |
| 267 | +$ envlint init --from-env</code></pre> |
| 268 | + </div> |
| 269 | + </div> |
| 270 | + </section> |
| 271 | + |
| 272 | + <footer> |
| 273 | + <div class="container"> |
| 274 | + <p>Built by <a href="https://kylecain.me">Kyle Cain</a> · <a href="https://github.com/cainky/envlint">GitHub</a></p> |
| 275 | + </div> |
| 276 | + </footer> |
| 277 | +</body> |
| 278 | +</html> |
0 commit comments