| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>GitHub Actions Visualizer</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @keyframes pulse { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| } |
| .pulse-animation { |
| animation: pulse 2s infinite; |
| } |
| .workflow-node { |
| transition: all 0.3s ease; |
| cursor: pointer; |
| } |
| .workflow-node:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); |
| } |
| .code-block { |
| font-family: 'Courier New', monospace; |
| tab-size: 2; |
| } |
| .highlight { |
| animation: highlight 1.5s ease-out; |
| } |
| @keyframes highlight { |
| 0% { background-color: rgba(240, 255, 0, 0.6); } |
| 100% { background-color: transparent; } |
| } |
| .job-container { |
| border-left: 4px solid transparent; |
| transition: border-color 0.3s ease; |
| } |
| .job-container:hover { |
| border-left-color: #4F46E5; |
| } |
| .arrow { |
| position: relative; |
| } |
| .arrow:after { |
| content: ""; |
| position: absolute; |
| bottom: -20px; |
| left: 50%; |
| transform: translateX(-50%); |
| width: 0; |
| height: 0; |
| border-left: 10px solid transparent; |
| border-right: 10px solid transparent; |
| border-top: 10px solid #4F46E5; |
| } |
| .animate-fade-in { |
| animation: fadeIn 0.3s ease-in; |
| } |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| .animate-fade-out { |
| animation: fadeOut 0.3s ease-out; |
| } |
| @keyframes fadeOut { |
| from { opacity: 1; transform: translateY(0); } |
| to { opacity: 0; transform: translateY(10px); } |
| } |
| .timeline-item { |
| position: relative; |
| padding-left: 2rem; |
| } |
| .timeline-item:before { |
| content: ""; |
| position: absolute; |
| left: 0; |
| top: 0; |
| width: 2px; |
| height: 100%; |
| background-color: #E5E7EB; |
| } |
| .timeline-dot { |
| position: absolute; |
| left: -0.5rem; |
| top: 0.25rem; |
| width: 1rem; |
| height: 1rem; |
| border-radius: 50%; |
| background-color: #4F46E5; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50"> |
| <div class="container mx-auto px-4 py-8"> |
| |
| <header class="mb-10"> |
| <div class="flex items-center justify-between"> |
| <div> |
| <h1 class="text-3xl font-bold text-gray-900 flex items-center"> |
| <i class="fas fa-project-diagram text-indigo-600 mr-3"></i> |
| GitHub Actions Workflow Visualizer |
| </h1> |
| <p class="text-gray-600 mt-2">Understand, optimize, and collaborate on your CI/CD pipelines</p> |
| </div> |
| <div class="flex space-x-3"> |
| <button id="share-btn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center space-x-2"> |
| <i class="fas fa-share-alt"></i> |
| <span>Share</span> |
| </button> |
| <button id="explain-btn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition flex items-center space-x-2"> |
| <i class="fas fa-question-circle"></i> |
| <span>Explain Workflow</span> |
| </button> |
| <button id="validate-btn" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center space-x-2"> |
| <i class="fas fa-check-circle"></i> |
| <span>Validate YAML</span> |
| </button> |
| </div> |
| </div> |
| |
| |
| <div class="mt-6 grid grid-cols-1 md:grid-cols-4 gap-4"> |
| <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| <div class="flex items-center"> |
| <div class="p-2 rounded-full bg-indigo-100 mr-3"> |
| <i class="fas fa-clock text-indigo-600"></i> |
| </div> |
| <div> |
| <div class="text-sm text-gray-500">Total Duration</div> |
| <div class="font-semibold">~3 minutes</div> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| <div class="flex items-center"> |
| <div class="p-2 rounded-full bg-green-100 mr-3"> |
| <i class="fas fa-tasks text-green-600"></i> |
| </div> |
| <div> |
| <div class="text-sm text-gray-500">Jobs</div> |
| <div class="font-semibold">2</div> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| <div class="flex items-center"> |
| <div class="p-2 rounded-full bg-blue-100 mr-3"> |
| <i class="fas fa-code-branch text-blue-600"></i> |
| </div> |
| <div> |
| <div class="text-sm text-gray-500">Steps</div> |
| <div class="font-semibold">12</div> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100"> |
| <div class="flex items-center"> |
| <div class="p-2 rounded-full bg-purple-100 mr-3"> |
| <i class="fas fa-bolt text-purple-600"></i> |
| </div> |
| <div> |
| <div class="text-sm text-gray-500">Triggers</div> |
| <div class="font-semibold">2</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <div class="grid grid-cols-1 lg:grid-cols-5 gap-8"> |
| |
| <div class="lg:col-span-1"> |
| <div class="bg-white rounded-lg shadow-md p-6 h-fit sticky top-4"> |
| <div class="flex items-center justify-between mb-4"> |
| <h3 class="text-lg font-semibold text-indigo-700 flex items-center"> |
| <i class="fas fa-bolt mr-2"></i> |
| Triggers |
| </h3> |
| <button id="add-trigger" class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded"> |
| <i class="fas fa-plus mr-1"></i> Add |
| </button> |
| </div> |
| <div class="space-y-4"> |
| <div class="workflow-node p-4 bg-indigo-50 rounded-lg border border-indigo-100"> |
| <div class="flex items-start"> |
| <div class="mr-3 text-indigo-500"> |
| <i class="fas fa-tag"></i> |
| </div> |
| <div> |
| <h4 class="font-medium">Tag Push</h4> |
| <p class="text-sm text-gray-600 mt-1">Tags matching v*.*.*</p> |
| </div> |
| </div> |
| </div> |
| <div class="workflow-node p-4 bg-indigo-50 rounded-lg border border-indigo-100"> |
| <div class="flex items-start"> |
| <div class="mr-3 text-indigo-500"> |
| <i class="fas fa-flag"></i> |
| </div> |
| <div> |
| <h4 class="font-medium">Release</h4> |
| <p class="text-sm text-gray-600 mt-1">When a release is published</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mt-8"> |
| <h3 class="text-lg font-semibold text-indigo-700 flex items-center mb-4"> |
| <i class="fas fa-server mr-2"></i> |
| Environments |
| </h3> |
| <div class="space-y-2"> |
| <div class="flex items-center p-2 bg-green-50 rounded border border-green-100"> |
| <div class="w-3 h-3 rounded-full bg-green-500 mr-2"></div> |
| <span class="text-sm font-medium">pypi</span> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mt-8"> |
| <h3 class="text-lg font-semibold text-indigo-700 flex items-center mb-4"> |
| <i class="fas fa-lock mr-2"></i> |
| Secrets |
| </h3> |
| <div class="space-y-2"> |
| <div class="flex items-center justify-between p-2 bg-gray-50 rounded border border-gray-100"> |
| <div class="flex items-center"> |
| <i class="fas fa-key text-gray-500 mr-2"></i> |
| <span class="text-sm font-medium">PYPI_API_TOKEN</span> |
| </div> |
| <span class="text-xs bg-gray-100 px-2 py-1 rounded">Used</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="lg:col-span-3"> |
| |
| <div class="bg-white rounded-lg shadow-md overflow-hidden mb-6" id="test-build-job"> |
| <div class="job-container bg-gradient-to-r from-indigo-50 to-white px-6 py-4 border-b border-gray-200"> |
| <div class="flex items-center justify-between"> |
| <div class="flex items-center"> |
| <div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center mr-4"> |
| <i class="fas fa-flask text-indigo-600"></i> |
| </div> |
| <div> |
| <h3 class="text-lg font-semibold">Test & Build</h3> |
| <div class="text-sm text-gray-500 flex items-center mt-1"> |
| <span class="mr-4"><i class="fas fa-server mr-1"></i> ubuntu-latest</span> |
| <span><i class="fas fa-clock mr-1"></i> ~2m</span> |
| </div> |
| </div> |
| </div> |
| <div class="flex items-center"> |
| <span class="px-3 py-1 bg-indigo-100 text-indigo-800 text-xs font-medium rounded-full mr-2">Required</span> |
| <button class="text-gray-500 hover:text-indigo-600"> |
| <i class="fas fa-ellipsis-v"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| <div class="px-6 py-4"> |
| <div class="space-y-4"> |
| |
| <div class="steps-container"> |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Checkout code</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">actions/checkout@v4</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 1.2s</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline">View logs</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Set up Python 3.9</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">actions/setup-python@v5</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 2.8s</span> |
| </div> |
| <div class="flex space-x-2"> |
| <button class="text-xs text-blue-600 hover:underline">View logs</button> |
| <button class="text-xs text-purple-600 hover:underline">Parameters</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Cache pip</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">actions/cache@v3</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Cache hit in 1.5s</span> |
| </div> |
| <div class="flex space-x-2"> |
| <button class="text-xs text-blue-600 hover:underline">View logs</button> |
| <button class="text-xs text-purple-600 hover:underline">Cache key</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium">Install dependencies</div> |
| <div class="text-sm text-gray-600 mt-1"> |
| <pre class="bg-gray-100 p-2 rounded text-xs">python -m pip install --upgrade pip |
| if [ -f requirements.txt ]; then pip install -r requirements.txt; fi</pre> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 12.4s</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline">View logs</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium">Run pytest</div> |
| <div class="text-sm text-gray-600 mt-1"> |
| <pre class="bg-gray-100 p-2 rounded text-xs">pytest --maxfail=1 --disable-warnings -q</pre> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 45.2s</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-green-100 text-green-800 px-2 py-1 rounded mr-2">12 passed</span> |
| <span class="bg-yellow-100 text-yellow-800 px-2 py-1 rounded">0 skipped</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline mt-1">View test report</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium">Build distributions</div> |
| <div class="text-sm text-gray-600 mt-1"> |
| <pre class="bg-gray-100 p-2 rounded text-xs">python -m pip install --upgrade build |
| python -m build --sdist --wheel</pre> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 18.7s</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-blue-100 text-blue-800 px-2 py-1 rounded mr-2">sdist</span> |
| <span class="bg-purple-100 text-purple-800 px-2 py-1 rounded">wheel</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline mt-1">View artifacts</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Upload build artifacts</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">actions/upload-artifact@v4</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 3.5s</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-gray-100 text-gray-800 px-2 py-1 rounded">dist</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline mt-1">View artifact details</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex justify-center mb-6 arrow"> |
| <div class="w-12 h-12 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-600"> |
| <i class="fas fa-arrow-down"></i> |
| </div> |
| </div> |
| |
| |
| <div class="bg-white rounded-lg shadow-md overflow-hidden" id="publish-job"> |
| <div class="job-container bg-gradient-to-r from-indigo-50 to-white px-6 py-4 border-b border-gray-200"> |
| <div class="flex items-center justify-between"> |
| <div class="flex items-center"> |
| <div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center mr-4"> |
| <i class="fas fa-upload text-indigo-600"></i> |
| </div> |
| <div> |
| <h3 class="text-lg font-semibold">Publish to PyPI</h3> |
| <div class="text-sm text-gray-500 flex items-center mt-1"> |
| <span class="mr-4"><i class="fas fa-server mr-1"></i> ubuntu-latest</span> |
| <span><i class="fas fa-clock mr-1"></i> ~1m</span> |
| </div> |
| </div> |
| </div> |
| <div class="flex items-center"> |
| <span class="inline-block px-3 py-1 bg-indigo-100 text-indigo-800 text-xs font-medium rounded-full mr-2">Dependent</span> |
| <span class="px-3 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full mr-2">PyPI</span> |
| <button class="text-gray-500 hover:text-indigo-600"> |
| <i class="fas fa-ellipsis-v"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| <div class="px-6 py-4"> |
| <div class="space-y-4"> |
| |
| <div class="steps-container"> |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Download build artifacts</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">actions/download-artifact@v4</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 0.8s</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-gray-100 text-gray-800 px-2 py-1 rounded">dist</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline mt-1">View artifact details</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium">Publish with twine (tags)</div> |
| <div class="text-sm text-gray-600 mt-1"> |
| <pre class="bg-gray-100 p-2 rounded text-xs">python -m pip install --upgrade twine |
| python -m twine upload dist/*</pre> |
| <div class="mt-2"> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">Variables:</span> |
| <div class="text-xs bg-gray-100 p-1 rounded mt-1"> |
| TWINE_USERNAME: __token__<br> |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} |
| </div> |
| </div> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 15.3s</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-green-100 text-green-800 px-2 py-1 rounded">Uploaded 2 files</span> |
| </div> |
| <button class="text-xs text-blue-600 hover:underline mt-1">View PyPI package</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start mb-3"> |
| <div class="mt-1 mr-3 text-yellow-500"> |
| <i class="fas fa-pause-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium flex justify-between items-center"> |
| <span>Publish with pypa/gh-action-pypi-publish</span> |
| <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">pypa/gh-action-pypi-publish@release/v1</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-yellow-600 mb-1"> |
| <i class="fas fa-pause-circle mr-1"></i> |
| <span>Condition not met (release not published)</span> |
| </div> |
| <div class="flex items-center text-xs mt-1"> |
| <span class="bg-gray-100 text-gray-800 px-2 py-1 rounded">When release is published</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex items-start"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="flex-1 bg-gray-50 p-3 rounded border border-gray-100"> |
| <div class="font-medium">Cleanup</div> |
| <div class="text-sm text-gray-600 mt-1"> |
| <pre class="bg-gray-100 p-2 rounded text-xs">rm -rf dist build *.egg-info</pre> |
| </div> |
| <div class="text-xs text-gray-600 mt-2"> |
| <div class="flex items-center text-green-600 mb-1"> |
| <i class="fas fa-check-circle mr-1"></i> |
| <span>Completed in 0.4s</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="lg:col-span-1"> |
| <div class="sticky top-4 space-y-6"> |
| |
| <div class="bg-white rounded-lg shadow-md p-6"> |
| <div class="flex items-center justify-between mb-4"> |
| <h3 class="text-lg font-semibold text-gray-700 flex items-center"> |
| <i class="fas fa-file-code mr-2"></i> |
| Workflow YAML |
| </h3> |
| <div class="flex space-x-2"> |
| <button class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded"> |
| <i class="fas fa-copy"></i> |
| </button> |
| <button class="text-xs bg-gray-100 hover:bg-gray-200 px-2 py-1 rounded"> |
| <i class="fas fa-download"></i> |
| </button> |
| </div> |
| </div> |
| <div class="overflow-auto max-h-[400px]"> |
| <pre class="text-xs code-block bg-gray-50 p-4 rounded-md overflow-auto">name: Publish Python Package |
|
|
| on: |
| push: |
| tags: |
| - 'v*.*.*' |
| release: |
| types: [published] |
|
|
| permissions: |
| contents: read |
| id-token: write |
|
|
| jobs: |
| test-and-build: |
| name: Test & Build |
| runs-on: ubuntu-latest |
|
|
| steps: |
| - name: Checkout code |
| uses: actions/checkout@v4 |
|
|
| - name: Set up Python 3.9 |
| uses: actions/setup-python@v5 |
| with: |
| python-version: '3.9' |
|
|
| - name: Cache pip |
| uses: actions/cache@v3 |
| with: |
| path: ~/.cache/pip |
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} |
| restore-keys: ${{ runner.os }}-pip- |
|
|
| - name: Install dependencies |
| run: | |
| python -m pip install --upgrade pip |
| if [ -f requirements.txt ]; then pip install -r requirements.txt; fi |
|
|
| - name: Run pytest |
| run: pytest --maxfail=1 --disable-warnings -q |
|
|
| - name: Build distributions |
| run: | |
| python -m pip install --upgrade build |
| python -m build --sdist --wheel |
|
|
| - name: Upload build artifacts |
| uses: actions/upload-artifact@v4 |
| with: |
| name: dist |
| path: dist/ |
|
|
| publish-to-pypi: |
| name: Publish to PyPI |
| needs: test-and-build |
| runs-on: ubuntu-latest |
| environment: |
| name: pypi |
|
|
| steps: |
| - name: Download build artifacts |
| uses: actions/download-artifact@v4 |
| with: |
| name: dist |
| path: dist/ |
|
|
| - name: Publish with twine |
| if: startsWith(github.ref, 'refs/tags/') |
| env: |
| TWINE_USERNAME: __token__ |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} |
| run: python -m pip install --upgrade twine \ |
| && python -m twine upload dist/* |
|
|
| - name: Publish with pypa/gh-action-pypi-publish |
| if: github.event_name == 'release' |
| uses: pypa/gh-action-pypi-publish@release/v1 |
| with: |
| packages-dir: dist/ |
|
|
| - name: Cleanup |
| if: always() |
| run: rm -rf dist build *.egg-info</pre> |
| </div> |
| </div> |
| |
| |
| <div class="bg-white rounded-lg shadow-md p-6"> |
| <h3 class="text-lg font-semibold text-gray-700 flex items-center mb-4"> |
| <i class="fas fa-history mr-2"></i> |
| Execution Timeline |
| </h3> |
| <div class="space-y-4"> |
| <div class="timeline-item"> |
| <div class="timeline-dot"></div> |
| <div class="text-sm font-medium">Workflow triggered</div> |
| <div class="text-xs text-gray-500 mt-1">Tag push: v1.2.3</div> |
| <div class="text-xs text-gray-400">2 minutes ago</div> |
| </div> |
| <div class="timeline-item"> |
| <div class="timeline-dot"></div> |
| <div class="text-sm font-medium">Test & Build job started</div> |
| <div class="text-xs text-gray-500 mt-1">ubuntu-latest</div> |
| <div class="text-xs text-gray-400">1 minute ago</div> |
| </div> |
| <div class="timeline-item"> |
| <div class="timeline-dot"></div> |
| <div class="text-sm font-medium">Test & Build job completed</div> |
| <div class="text-xs text-green-600 mt-1">Success</div> |
| <div class="text-xs text-gray-400">30 seconds ago</div> |
| </div> |
| <div class="timeline-item"> |
| <div class="timeline-dot"></div> |
| <div class="text-sm font-medium">Publish to PyPI job started</div> |
| <div class="text-xs text-gray-500 mt-1">ubuntu-latest</div> |
| <div class="text-xs text-gray-400">25 seconds ago</div> |
| </div> |
| <div class="timeline-item"> |
| <div class="timeline-dot"></div> |
| <div class="text-sm font-medium">Publish to PyPI job completed</div> |
| <div class="text-xs text-green-600 mt-1">Success</div> |
| <div class="text-xs text-gray-400">10 seconds ago</div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="bg-white rounded-lg shadow-md p-6"> |
| <h3 class="text-lg font-semibold text-gray-700 flex items-center mb-4"> |
| <i class="fas fa-lightbulb mr-2"></i> |
| Optimization Tips |
| </h3> |
| <div class="space-y-3"> |
| <div class="flex items-start"> |
| <div class="mt-1 mr-3 text-yellow-500"> |
| <i class="fas fa-info-circle"></i> |
| </div> |
| <div> |
| <div class="text-sm font-medium">Cache Node Modules</div> |
| <p class="text-xs text-gray-600 mt-1">Consider caching node_modules to speed up npm/yarn installs.</p> |
| </div> |
| </div> |
| <div class="flex items-start"> |
| <div class="mt-1 mr-3 text-blue-500"> |
| <i class="fas fa-bolt"></i> |
| </div> |
| <div> |
| <div class="text-sm font-medium">Parallelize Tests</div> |
| <p class="text-xs text-gray-600 mt-1">Use pytest-xdist to run tests in parallel and reduce execution time.</p> |
| </div> |
| </div> |
| <div class="flex items-start"> |
| <div class="mt-1 mr-3 text-green-500"> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div> |
| <div class="text-sm font-medium">Matrix Strategy</div> |
| <p class="text-xs text-gray-600 mt-1">Test against multiple Python versions using a matrix strategy.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="explanation-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="bg-white rounded-lg max-w-2xl w-full max-h-[80vh] overflow-y-auto animate-fade-in"> |
| <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center"> |
| <h3 class="text-lg font-semibold text-gray-800">Workflow Explanation</h3> |
| <button id="close-modal" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="p-6"> |
| <h4 class="font-medium text-indigo-700 mb-2">Python Package Publishing Workflow</h4> |
| <p class="mb-4 text-gray-700">This workflow automates the testing, building, and publishing of Python packages to PyPI when either:</p> |
| |
| <ul class="list-disc pl-5 mb-4 space-y-2 text-gray-700"> |
| <li>A new git tag is pushed matching the pattern v*.*.* (e.g., v1.0.0)</li> |
| <li>A new GitHub release is published</li> |
| </ul> |
| |
| <h4 class="font-medium text-indigo-700 mb-2 mt-6">Jobs Overview</h4> |
| |
| <div class="space-y-6"> |
| |
| <div> |
| <div class="flex items-center mb-2"> |
| <div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center mr-3"> |
| <i class="fas fa-flask text-indigo-600 text-sm"></i> |
| </div> |
| <h5 class="font-medium">Test & Build Job</h5> |
| </div> |
| <div class="ml-11 text-gray-700"> |
| <p class="mb-2">This job runs on Ubuntu and performs the following steps:</p> |
| <ul class="list-disc pl-5 space-y-1 text-sm"> |
| <li>Checks out your repository code</li> |
| <li>Sets up Python 3.9 environment</li> |
| <li>Caches pip dependencies for faster subsequent runs</li> |
| <li>Installs required Python dependencies</li> |
| <li>Runs pytest for test validation</li> |
| <li>Builds source distribution (sdist) and wheel distribution</li> |
| <li>Uploads the built distributions as artifacts for the next job</li> |
| </ul> |
| </div> |
| </div> |
| |
| |
| <div> |
| <div class="flex items-center mb-2"> |
| <div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center mr-3"> |
| <i class="fas fa-upload text-indigo-600 text-sm"></i> |
| </div> |
| <h5 class="font-medium">Publish to PyPI Job</h5> |
| </div> |
| <div class="ml-11 text-gray-700"> |
| <p class="mb-2">This job publishes the package to PyPI and requires the Test & Build job to complete first. It includes two publishing methods:</p> |
| <ul class="list-disc pl-5 space-y-1 text-sm"> |
| <li><strong>Twine Upload:</strong> Used when a tag is pushed, uses PyPI API token from GitHub secrets</li> |
| <li><strong>pypa/gh-action-pypi-publish:</strong> Used when a release is published, simplifies authentication</li> |
| </ul> |
| <p class="mt-2 text-sm">Both methods run in the 'pypi' environment for security context.</p> |
| </div> |
| </div> |
| </div> |
| |
| <h4 class="font-medium text-indigo-700 mb-2 mt-6">Security Considerations</h4> |
| <div class="bg-blue-50 p-4 rounded-md"> |
| <ul class="list-disc pl-5 space-y-1 text-sm text-gray-700"> |
| <li>Uses minimal permissions (read for contents, write for id-token)</li> |
| <li>Stores PyPI API token in GitHub secrets (not visible in logs)</li> |
| <li>Runs in a dedicated pypi environment</li> |
| </ul> |
| </div> |
| |
| <h4 class="font-medium text-indigo-700 mb-2 mt-6">Best Practices</h4> |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4"> |
| <div class="bg-green-50 p-3 rounded-md border border-green-100"> |
| <div class="flex items-center mb-1"> |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> |
| <span class="font-medium">Cache Dependencies</span> |
| </div> |
| <p class="text-xs text-gray-700">Using actions/cache reduces workflow execution time by reusing dependencies.</p> |
| </div> |
| <div class="bg-green-50 p-3 rounded-md border border-green-100"> |
| <div class="flex items-center mb-1"> |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> |
| <span class="font-medium">Artifact Management</span> |
| </div> |
| <p class="text-xs text-gray-700">Uploading build artifacts between jobs maintains workflow state.</p> |
| </div> |
| <div class="bg-green-50 p-3 rounded-md border border-green-100"> |
| <div class="flex items-center mb-1"> |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> |
| <span class="font-medium">Conditional Steps</span> |
| </div> |
| <p class="text-xs text-gray-700">Using 'if' conditions makes steps run only when needed.</p> |
| </div> |
| <div class="bg-green-50 p-3 rounded-md border border-green-100"> |
| <div class="flex items-center mb-1"> |
| <i class="fas fa-check-circle text-green-500 mr-2"></i> |
| <span class="font-medium">Cleanup</span> |
| </div> |
| <p class="text-xs text-gray-700">The 'always()' condition ensures cleanup runs regardless of job status.</p> |
| </div> |
| </div> |
| </div> |
| <div class="px-6 py-3 border-t border-gray-200 flex justify-end"> |
| <button id="close-modal-2" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition"> |
| Got it! |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div id="share-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="bg-white rounded-lg max-w-md w-full animate-fade-in"> |
| <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center"> |
| <h3 class="text-lg font-semibold text-gray-800">Share Workflow</h3> |
| <button id="close-share-modal" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="p-6"> |
| <div class="mb-4"> |
| <label class="block text-sm font-medium text-gray-700 mb-2">Share Link</label> |
| <div class="flex"> |
| <input type="text" value="https://actions-visualizer.example.com/workflow/abc123" class="flex-1 border border-gray-300 rounded-l-md px-3 py-2 text-sm" readonly> |
| <button class="bg-indigo-600 text-white px-4 py-2 rounded-r-md hover:bg-indigo-700"> |
| <i class="fas fa-copy"></i> |
| </button> |
| </div> |
| </div> |
| |
| <div class="mb-4"> |
| <label class="block text-sm font-medium text-gray-700 mb-2">Access Level</label> |
| <div class="flex space-x-4"> |
| <label class="inline-flex items-center"> |
| <input type="radio" name="access" checked class="h-4 w-4 text-indigo-600"> |
| <span class="ml-2 text-sm">View Only</span> |
| </label> |
| <label class="inline-flex items-center"> |
| <input type="radio" name="access" class="h-4 w-4 text-indigo-600"> |
| <span class="ml-2 text-sm">Can Edit</span> |
| </label> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-2">Share Via</label> |
| <div class="flex space-x-3"> |
| <button class="w-10 h-10 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center hover:bg-blue-200"> |
| <i class="fab fa-twitter"></i> |
| </button> |
| <button class="w-10 h-10 rounded-full bg-gray-100 text-gray-600 flex items-center justify-center hover:bg-gray-200"> |
| <i class="fab fa-github"></i> |
| </button> |
| <button class="w-10 h-10 rounded-full bg-red-100 text-red-600 flex items-center justify-center hover:bg-red-200"> |
| <i class="fab fa-google"></i> |
| </button> |
| <button class="w-10 h-10 rounded-full bg-indigo-100 text-indigo-600 flex items-center justify-center hover:bg-indigo-200"> |
| <i class="fas fa-envelope"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| <div class="px-6 py-3 border-t border-gray-200 flex justify-end"> |
| <button id="close-share-modal-2" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition"> |
| Done |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| document.querySelectorAll('.steps-container > div').forEach(step => { |
| step.addEventListener('click', function() { |
| |
| document.querySelectorAll('.highlight').forEach(el => { |
| el.classList.remove('highlight'); |
| }); |
| |
| |
| const stepName = this.querySelector('.font-medium').textContent.trim(); |
| |
| |
| const preTag = document.querySelector('pre.code-block'); |
| const text = preTag.textContent; |
| |
| |
| const pattern = new RegExp(`- name: ${escapeRegExp(stepName)}.*?\\n\\s{4}((uses|run|with|env):.*?\\n)+(?= -| [a-z]|$)`, 'gs'); |
| |
| const match = text.match(pattern); |
| if (match) { |
| const startIdx = text.indexOf(match[0]); |
| const endIdx = startIdx + match[0].length; |
| |
| |
| const highlightedHTML = text.substring(0, startIdx) + |
| '<span class="highlight">' + text.substring(startIdx, endIdx) + '</span>' + |
| text.substring(endIdx); |
| |
| preTag.innerHTML = highlightedHTML; |
| |
| |
| const highlighted = preTag.querySelector('.highlight'); |
| if (highlighted) { |
| highlighted.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
| } |
| } |
| }); |
| }); |
| |
| function escapeRegExp(string) { |
| return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| } |
| |
| |
| const explainBtn = document.getElementById('explain-btn'); |
| const modal = document.getElementById('explanation-modal'); |
| const closeModalBtns = document.querySelectorAll('#close-modal, #close-modal-2'); |
| |
| explainBtn.addEventListener('click', () => { |
| modal.classList.remove('hidden'); |
| modal.classList.add('flex'); |
| }); |
| |
| closeModalBtns.forEach(btn => { |
| btn.addEventListener('click', () => { |
| modal.classList.add('hidden'); |
| modal.classList.remove('flex'); |
| }); |
| }); |
| |
| |
| const shareBtn = document.getElementById('share-btn'); |
| const shareModal = document.getElementById('share-modal'); |
| const closeShareModalBtns = document.querySelectorAll('#close-share-modal, #close-share-modal-2'); |
| |
| shareBtn.addEventListener('click', () => { |
| shareModal.classList.remove('hidden'); |
| shareModal.classList.add('flex'); |
| }); |
| |
| closeShareModalBtns.forEach(btn => { |
| btn.addEventListener('click', () => { |
| shareModal.classList.add('hidden'); |
| shareModal.classList.remove('flex'); |
| }); |
| }); |
| |
| |
| document.getElementById('validate-btn').addEventListener('click', function() { |
| |
| const isValid = true; |
| |
| if (isValid) { |
| |
| const notification = document.createElement('div'); |
| notification.className = 'fixed top-4 right-4 px-6 py-3 bg-green-100 text-green-800 rounded-lg shadow-lg flex items-center animate-fade-in'; |
| notification.innerHTML = ` |
| <i class="fas fa-check-circle mr-2"></i> |
| <span>Workflow YAML is valid!</span> |
| `; |
| document.body.appendChild(notification); |
| |
| |
| setTimeout(() => { |
| notification.classList.add('animate-fade-out'); |
| setTimeout(() => notification.remove(), 300); |
| }, 3000); |
| } |
| }); |
| |
| |
| document.querySelectorAll('.job-container').forEach(container => { |
| container.addEventListener('mouseenter', () => { |
| container.classList.add('pulse-animation'); |
| }); |
| |
| container.addEventListener('mouseleave', () => { |
| container.classList.remove('pulse-animation'); |
| }); |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=S-Dreamer/pipevue" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |