Created
February 27, 2026 12:45
-
-
Save pcorpet/048416b40737bbd66d095bac811091c1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>yarn.lock in Git</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .presentation { | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
| width: 95vw; | |
| height: 90vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .slides-container { | |
| flex: 1; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .slide { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| opacity: 0; | |
| transition: opacity 0.5s ease-in-out; | |
| padding: 60px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| overflow-y: auto; | |
| } | |
| .slide.active { | |
| opacity: 1; | |
| } | |
| .slide h1 { | |
| font-size: 3.5em; | |
| color: #667eea; | |
| margin-bottom: 30px; | |
| } | |
| .slide h2 { | |
| font-size: 2.5em; | |
| color: #667eea; | |
| margin-bottom: 40px; | |
| } | |
| .code-block { | |
| background: #f5f5f5; | |
| border-left: 4px solid #667eea; | |
| padding: 15px; | |
| margin: 10px 0; | |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | |
| font-size: 0.9em; | |
| border-radius: 4px; | |
| overflow-x: auto; | |
| line-height: 1.4; | |
| white-space: pre; | |
| } | |
| .code-label { | |
| font-weight: 600; | |
| color: #667eea; | |
| font-size: 0.9em; | |
| margin-bottom: 8px; | |
| text-transform: uppercase; | |
| } | |
| .file-comparison { | |
| display: flex; | |
| gap: 40px; | |
| margin-top: 30px; | |
| } | |
| .file-column { | |
| flex: 1; | |
| } | |
| .file-column h3 { | |
| font-size: 1.3em; | |
| color: #764ba2; | |
| margin-bottom: 15px; | |
| font-weight: 600; | |
| } | |
| .title-slide { | |
| justify-content: center; | |
| align-items: center; | |
| text-align: center; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| } | |
| .title-slide h1 { | |
| color: white; | |
| font-size: 4em; | |
| margin-bottom: 20px; | |
| } | |
| .title-slide p { | |
| color: #e0e0e0; | |
| font-size: 1.5em; | |
| } | |
| .highlight-yellow { | |
| background: #fff3cd; | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border-left: 4px solid #ffc107; | |
| font-size: 0.95em; | |
| line-height: 1.6; | |
| } | |
| .highlight-red { | |
| background: #ffe0e0; | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border-left: 4px solid #ff4444; | |
| font-size: 0.95em; | |
| line-height: 1.6; | |
| } | |
| .highlight-green { | |
| background: #e0ffe0; | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border-left: 4px solid #44aa44; | |
| font-size: 0.95em; | |
| line-height: 1.6; | |
| } | |
| .arrow { | |
| text-align: center; | |
| font-size: 2em; | |
| color: #667eea; | |
| margin: 15px 0; | |
| } | |
| .controls { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 20px 40px; | |
| background: #f9f9f9; | |
| border-top: 1px solid #eee; | |
| } | |
| button { | |
| background: #667eea; | |
| color: white; | |
| border: none; | |
| padding: 12px 24px; | |
| border-radius: 6px; | |
| font-size: 1em; | |
| cursor: pointer; | |
| transition: background 0.3s; | |
| } | |
| button:hover { | |
| background: #764ba2; | |
| } | |
| button:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| } | |
| .slide-counter { | |
| color: #667eea; | |
| font-weight: 600; | |
| font-size: 1.1em; | |
| } | |
| .two-row { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .change-indicator { | |
| display: inline-block; | |
| background: #ffe0e0; | |
| color: #cc0000; | |
| padding: 2px 8px; | |
| border-radius: 3px; | |
| font-size: 0.85em; | |
| margin-left: 10px; | |
| font-weight: 600; | |
| } | |
| .unchanged { | |
| opacity: 0.6; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="presentation"> | |
| <div class="slides-container"> | |
| <!-- Slide 1: Title --> | |
| <div class="slide active title-slide"> | |
| <div> | |
| <h1>yarn.lock in Git</h1> | |
| <p>Reproducible dependencies</p> | |
| </div> | |
| </div> | |
| <!-- Slide 2: Dependencies Definition --> | |
| <div class="slide"> | |
| <h2>Dependencies Definition</h2> | |
| <div class="file-comparison"> | |
| <div class="file-column"> | |
| <h3>package.json</h3> | |
| <div class="code-label">What you want</div> | |
| <div class="code-block">"react": "19.2.4"</div> | |
| <p style="margin-top: 10px; font-size: 0.95em; color: #666;">Exact or range</p> | |
| </div> | |
| <div class="arrow">→</div> | |
| <div class="file-column"> | |
| <h3>yarn.lock</h3> | |
| <div class="code-label">What got installed</div> | |
| <div class="code-block">version: 19.2.4 | |
| resolution: "react@npm:19.2.4" | |
| checksum: 10/abc123def456... | |
| dependencies: | |
| loose-envify: "npm:^1.4.0"</div> | |
| <p style="margin-top: 10px; font-size: 0.95em; color: #666;">Exact version + hash</p> | |
| </div> | |
| </div> | |
| <div class="highlight-yellow"> | |
| One generates from the other. Both are essential. | |
| </div> | |
| </div> | |
| <!-- Slide 3: Modifying package.json --> | |
| <div class="slide"> | |
| <h2>Modify package.json</h2> | |
| <div class="two-row"> | |
| <div> | |
| <div class="code-label">Change in package.json:</div> | |
| <div class="code-block">"react": "19.2.4" → "react": "20.0.0"</div> | |
| </div> | |
| <div class="arrow">↓ yarn install ↓</div> | |
| <div> | |
| <div class="code-label">yarn.lock updates:</div> | |
| <div class="code-block">version: 20.0.0 <span class="change-indicator">CHANGED</span> | |
| resolution: "react@npm:20.0.0" | |
| checksum: 10/xyz789abc... <span class="change-indicator">CHANGED</span> | |
| dependencies: | |
| loose-envify: "npm:^1.4.0"</div> | |
| </div> | |
| </div> | |
| <div class="highlight-yellow"> | |
| Commit both files together | |
| </div> | |
| </div> | |
| <!-- Slide 4: Deleting yarn.lock --> | |
| <div class="slide"> | |
| <h2>Delete yarn.lock & Recreate</h2> | |
| <div class="two-row"> | |
| <div> | |
| <div class="code-label">Delete yarn.lock</div> | |
| <div class="code-block">$ rm yarn.lock</div> | |
| </div> | |
| <div class="arrow">↓ yarn install (Today) ↓</div> | |
| <div> | |
| <div class="code-label">Creates new yarn.lock with latest available</div> | |
| <div class="code-block">version: 19.2.5 | |
| checksum: 10/aaa111bbb...</div> | |
| </div> | |
| <div class="arrow">↓ yarn install (Next week) ↓</div> | |
| <div> | |
| <div class="code-label">Different result!</div> | |
| <div class="code-block">version: 19.2.7 | |
| checksum: 10/ccc333ddd...</div> | |
| </div> | |
| </div> | |
| <div class="highlight-red"> | |
| ⚠️ Inconsistent installs, "works on my machine" returns | |
| </div> | |
| </div> | |
| <!-- Slide 5: Security Patch Upgrades --> | |
| <div class="slide"> | |
| <h2>Security Patch Upgrade</h2> | |
| <div class="two-row"> | |
| <div> | |
| <div class="code-label">package.json (unchanged)</div> | |
| <div class="code-block">"react": "19.2.4"</div> | |
| </div> | |
| <div class="arrow">↓ yarn upgrade react ↓</div> | |
| <div> | |
| <div class="code-label">yarn.lock (patches only)</div> | |
| <div class="code-block">version: 19.2.8 <span class="change-indicator">PATCHED</span> | |
| resolution: "react@npm:19.2.8" | |
| checksum: 10/eee444fff... <span class="change-indicator">CHANGED</span></div> | |
| </div> | |
| </div> | |
| <div class="highlight-green"> | |
| Safe, minimal changes, easy to review in PR | |
| </div> | |
| </div> | |
| <!-- Slide 6: Python Parallel --> | |
| <div class="slide"> | |
| <h2>Same Pattern Everywhere</h2> | |
| <div class="file-comparison"> | |
| <div class="file-column"> | |
| <h3>JavaScript/Node</h3> | |
| <div class="code-block">package.json | |
| + | |
| yarn.lock</div> | |
| </div> | |
| <div class="file-column" style="text-align: center; display: flex; align-items: center; justify-content: center;"> | |
| <div style="font-size: 2em; color: #667eea; font-weight: 600;">= Principle</div> | |
| </div> | |
| <div class="file-column"> | |
| <h3>Python</h3> | |
| <div class="code-block">pyproject.toml | |
| + | |
| poetry.lock</div> | |
| </div> | |
| </div> | |
| <div class="highlight-yellow" style="margin-top: 30px; text-align: center;"> | |
| Commit both. Always. Same everywhere. | |
| </div> | |
| <div class="highlight-green" style="margin-top: 20px; font-size: 0.95em;"> | |
| <strong>New enforcement rule:</strong><br> | |
| PRs cannot modify <code>yarn.lock</code> without modifying <code>package.json</code><br> | |
| <em>Exception: PRs that modify ONLY yarn.lock (no other files allowed)</em> | |
| </div> | |
| </div> | |
| <!-- Slide 7: Questions --> | |
| <div class="slide" style="justify-content: center; align-items: center; text-align: center;"> | |
| <h1 style="font-size: 4em;">Questions?</h1> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <button id="prev" onclick="previousSlide()">← Previous</button> | |
| <span class="slide-counter"><span id="current">1</span> / <span id="total">7</span></span> | |
| <button id="next" onclick="nextSlide()">Next →</button> | |
| </div> | |
| </div> | |
| <script> | |
| let currentSlide = 0; | |
| const slides = document.querySelectorAll('.slide'); | |
| const totalSlides = slides.length; | |
| function showSlide(n) { | |
| slides[currentSlide].classList.remove('active'); | |
| currentSlide = (n + totalSlides) % totalSlides; | |
| slides[currentSlide].classList.add('active'); | |
| document.getElementById('current').textContent = currentSlide + 1; | |
| document.getElementById('prev').disabled = currentSlide === 0; | |
| document.getElementById('next').disabled = currentSlide === totalSlides - 1; | |
| } | |
| function nextSlide() { | |
| if (currentSlide < totalSlides - 1) { | |
| showSlide(currentSlide + 1); | |
| } | |
| } | |
| function previousSlide() { | |
| if (currentSlide > 0) { | |
| showSlide(currentSlide - 1); | |
| } | |
| } | |
| // Keyboard navigation | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'ArrowRight') nextSlide(); | |
| if (e.key === 'ArrowLeft') previousSlide(); | |
| }); | |
| // Initialize | |
| showSlide(0); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment