153 lines
12 KiB
Django/Jinja
153 lines
12 KiB
Django/Jinja
{% extends "base.html.jinja" %}
|
|
|
|
{% block content %}
|
|
<section class="max-w-5xl mx-auto px-4 py-12">
|
|
<div class="flex items-center justify-between mb-8">
|
|
<h1 class="text-2xl font-bold">Continuous deployment</h1>
|
|
</div>
|
|
|
|
{% if timeline | length > 0 %}
|
|
<swim-lanes>
|
|
{# ── Environment swim lanes ──────────────────────────────── #}
|
|
{% for lane in lanes %}
|
|
<div data-lane="{{ lane.name }}"></div>
|
|
{% endfor %}
|
|
|
|
{# ── Release timeline ─────────────────────────────────────── #}
|
|
<div data-swimlane-timeline class="flex-1 space-y-3 min-w-0 ml-2">
|
|
{% for item in timeline %}
|
|
|
|
{% if item.kind == "release" %}
|
|
{# ── Visible release card ──────────────────────────────── #}
|
|
{% set release = item.release %}
|
|
<div data-release data-envs="{{ release.dest_envs }}" class="border border-gray-200 rounded-lg overflow-hidden">
|
|
{# Release header #}
|
|
<div class="px-4 py-3 flex items-center gap-3 flex-wrap">
|
|
<div class="flex items-center gap-2 min-w-0 flex-1">
|
|
<span class="inline-block w-6 h-6 rounded-full bg-gray-200 shrink-0" data-avatar></span>
|
|
<a href="/orgs/{{ org_name }}/projects/{{ release.project_name }}/releases/{{ release.slug }}" class="font-medium text-gray-900 hover:text-black truncate">
|
|
{{ release.title }}
|
|
</a>
|
|
</div>
|
|
<div class="flex items-center gap-4 text-xs text-gray-500 shrink-0 flex-wrap">
|
|
{% if release.branch %}
|
|
<span class="flex items-center gap-1">
|
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A2 2 0 013 12V7a4 4 0 014-4z"/></svg>
|
|
{{ release.branch }}
|
|
</span>
|
|
{% endif %}
|
|
{% if release.commit_sha %}
|
|
<span class="font-mono">{{ release.commit_sha[:7] }}</span>
|
|
{% endif %}
|
|
<span title="{{ release.created_at | datetime }}">{{ release.created_at | timeago }}</span>
|
|
{% if release.source_user %}
|
|
<span class="flex items-center gap-1">
|
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
|
|
{{ release.source_user }}
|
|
</span>
|
|
{% endif %}
|
|
<span class="text-gray-400">
|
|
<a href="/orgs/{{ org_name }}/projects/{{ release.project_name }}" class="hover:text-gray-700">{{ release.project_name }}</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{# Deployment steps (collapsed by default) #}
|
|
<details class="border-t border-gray-100 group">
|
|
<summary class="px-4 py-2 flex items-center gap-2 text-sm cursor-pointer list-none hover:bg-gray-50">
|
|
<svg class="w-4 h-4 text-green-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
<span class="text-gray-600 text-sm">Deployed to</span>
|
|
{% for dest in release.destinations %}
|
|
<span class="inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full {% if 'prod' in dest.environment and 'preprod' not in dest.environment %}bg-pink-100 text-pink-800{% elif 'preprod' in dest.environment or 'pre-prod' in dest.environment %}bg-orange-100 text-orange-800{% elif 'stag' in dest.environment %}bg-yellow-100 text-yellow-800{% elif 'dev' in dest.environment %}bg-violet-100 text-violet-800{% else %}bg-gray-100 text-gray-700{% endif %}">
|
|
{{ dest.environment }}
|
|
<span class="w-1.5 h-1.5 rounded-full {% if 'prod' in dest.environment and 'preprod' not in dest.environment %}bg-pink-500{% elif 'preprod' in dest.environment or 'pre-prod' in dest.environment %}bg-orange-500{% elif 'stag' in dest.environment %}bg-yellow-500{% elif 'dev' in dest.environment %}bg-violet-500{% else %}bg-gray-400{% endif %}"></span>
|
|
</span>
|
|
{% endfor %}
|
|
<svg class="w-3 h-3 text-gray-400 shrink-0 ml-auto transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
|
</summary>
|
|
{% for dest in release.destinations %}
|
|
<div class="px-4 py-2 flex items-center gap-3 text-sm {% if not loop.last %}border-b border-gray-50{% endif %} border-t border-gray-50">
|
|
<svg class="w-4 h-4 text-green-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
<span class="text-gray-600">Deployed to</span>
|
|
<span class="inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full {% if 'prod' in dest.environment and 'preprod' not in dest.environment %}bg-pink-100 text-pink-800{% elif 'preprod' in dest.environment or 'pre-prod' in dest.environment %}bg-orange-100 text-orange-800{% elif 'stag' in dest.environment %}bg-yellow-100 text-yellow-800{% elif 'dev' in dest.environment %}bg-violet-100 text-violet-800{% else %}bg-gray-100 text-gray-700{% endif %}">
|
|
{{ dest.environment }}
|
|
<span class="w-1.5 h-1.5 rounded-full {% if 'prod' in dest.environment and 'preprod' not in dest.environment %}bg-pink-500{% elif 'preprod' in dest.environment or 'pre-prod' in dest.environment %}bg-orange-500{% elif 'stag' in dest.environment %}bg-yellow-500{% elif 'dev' in dest.environment %}bg-violet-500{% else %}bg-gray-400{% endif %}"></span>
|
|
</span>
|
|
<span class="text-gray-400 text-xs">{{ dest.name }}</span>
|
|
{% if dest.type_name %}
|
|
<span class="text-gray-400 text-xs">({{ dest.type_name }}{% if dest.type_version %} v{{ dest.type_version }}{% endif %})</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</details>
|
|
</div>
|
|
|
|
{% elif item.kind == "hidden" %}
|
|
{# ── Hidden commits group ──────────────────────────────── #}
|
|
<details class="group">
|
|
<summary class="flex items-center gap-2 py-2 px-1 text-sm text-gray-400 cursor-pointer hover:text-gray-600 list-none">
|
|
<svg class="w-3 h-3 transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
|
{{ item.count }} hidden commit{{ "s" if item.count != 1 }}
|
|
<span class="text-gray-300">·</span>
|
|
<span class="group-open:hidden">Show commit{{ "s" if item.count != 1 }}</span>
|
|
<span class="hidden group-open:inline">Hide commit{{ "s" if item.count != 1 }}</span>
|
|
</summary>
|
|
<div class="space-y-3 mt-1">
|
|
{% for release in item.releases %}
|
|
<div data-release data-envs="" class="border border-gray-200 rounded-lg overflow-hidden opacity-75">
|
|
<div class="px-4 py-3 flex items-center gap-3 flex-wrap">
|
|
<div class="flex items-center gap-2 min-w-0 flex-1">
|
|
<span class="inline-block w-6 h-6 rounded-full bg-gray-200 shrink-0" data-avatar></span>
|
|
<a href="/orgs/{{ org_name }}/projects/{{ release.project_name }}/releases/{{ release.slug }}" class="font-medium text-gray-900 hover:text-black truncate">
|
|
{{ release.title }}
|
|
</a>
|
|
</div>
|
|
<div class="flex items-center gap-4 text-xs text-gray-500 shrink-0 flex-wrap">
|
|
{% if release.commit_sha %}
|
|
<span class="font-mono">{{ release.commit_sha[:7] }}</span>
|
|
{% endif %}
|
|
<span title="{{ release.created_at | datetime }}">{{ release.created_at | timeago }}</span>
|
|
{% if release.source_user %}
|
|
<span class="flex items-center gap-1">
|
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
|
|
{{ release.source_user }}
|
|
</span>
|
|
{% endif %}
|
|
<span class="text-gray-400">
|
|
<a href="/orgs/{{ org_name }}/projects/{{ release.project_name }}" class="hover:text-gray-700">{{ release.project_name }}</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</details>
|
|
{% endif %}
|
|
|
|
{% endfor %}
|
|
</div>
|
|
</swim-lanes>
|
|
|
|
{% else %}
|
|
{# ── Empty state ──────────────────────────────────────────── #}
|
|
<div class="border border-gray-200 rounded-lg p-12 text-center">
|
|
<div class="max-w-md mx-auto">
|
|
<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-gray-100 flex items-center justify-center">
|
|
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/></svg>
|
|
</div>
|
|
<p class="font-medium text-gray-900 mb-1">No releases yet</p>
|
|
<p class="text-sm text-gray-500 mb-6">Releases appear when you deploy with Forest CLI.</p>
|
|
<div class="bg-gray-50 rounded-lg p-4 text-left">
|
|
<p class="text-xs font-medium text-gray-700 mb-2">Get started with the CLI:</p>
|
|
<pre class="text-xs text-gray-600 overflow-x-auto"><code>forest release create \
|
|
--org {{ org_name }} \
|
|
--project my-project \
|
|
--dest staging:my-service</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
<script src="/static/js/swim-lanes.js"></script>
|
|
{% endblock %}
|