pipevue / index.html
S-Dreamer's picture
Add 3 files
09efea8 verified
<!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 -->
<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>
<!-- Workflow Stats -->
<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>
<!-- Main Content -->
<div class="grid grid-cols-1 lg:grid-cols-5 gap-8">
<!-- Triggers Panel -->
<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>
<!-- Environments -->
<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>
<!-- Secrets -->
<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>
<!-- Main Workflow Visualization -->
<div class="lg:col-span-3">
<!-- Job: Test & Build -->
<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">
<!-- Steps -->
<div class="steps-container">
<!-- Checkout -->
<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>
<!-- Python Setup -->
<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>
<!-- Cache -->
<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>
<!-- Dependencies -->
<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>
<!-- Tests -->
<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>
<!-- Build -->
<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>
<!-- Upload -->
<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>
<!-- Job Connection Arrow -->
<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>
<!-- Job: Publish to PyPI -->
<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">
<!-- Steps -->
<div class="steps-container">
<!-- Download -->
<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>
<!-- Twine Upload -->
<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>
<!-- PyPA Upload -->
<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>
<!-- Cleanup -->
<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>
<!-- Right Sidebar -->
<div class="lg:col-span-1">
<div class="sticky top-4 space-y-6">
<!-- YAML Code Viewer -->
<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>
<!-- Execution Timeline -->
<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>
<!-- Optimization Tips -->
<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>
<!-- Explanation Modal -->
<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">
<!-- Test & Build Job Explanation -->
<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>
<!-- Publish Job Explanation -->
<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>
<!-- Share Modal -->
<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>
// Highlight YAML code when clicking on steps
document.querySelectorAll('.steps-container > div').forEach(step => {
step.addEventListener('click', function() {
// Remove all highlights
document.querySelectorAll('.highlight').forEach(el => {
el.classList.remove('highlight');
});
// Get the step name
const stepName = this.querySelector('.font-medium').textContent.trim();
// Find and highlight corresponding YAML (this is simplified)
const preTag = document.querySelector('pre.code-block');
const text = preTag.textContent;
// Create a regex pattern to find the step
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;
// Create a range wrapper (this is a simplified approach)
const highlightedHTML = text.substring(0, startIdx) +
'<span class="highlight">' + text.substring(startIdx, endIdx) + '</span>' +
text.substring(endIdx);
preTag.innerHTML = highlightedHTML;
// Scroll to the highlighted section
const highlighted = preTag.querySelector('.highlight');
if (highlighted) {
highlighted.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
});
});
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Modal functionality
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');
});
});
// Share modal functionality
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');
});
});
// Validate YAML button functionality
document.getElementById('validate-btn').addEventListener('click', function() {
// Simple validation - in a real app this would actually parse the YAML
const isValid = true; // Assume valid for this demo
if (isValid) {
// Show success notification
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);
// Remove notification after 3 seconds
setTimeout(() => {
notification.classList.add('animate-fade-out');
setTimeout(() => notification.remove(), 300);
}, 3000);
}
});
// Add pulse animation to job containers when they're active
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>