commit b91970d6142590aaaecdd47ff2e8c09f79189f5c
parent f4b3d7d66922b7567167e5c48e8b4feb3be87967
Author: arjoonn <arjoonn@midpathsoftware.com>
Date: Thu, 26 Feb 2026 07:30:29 +0000
Website (!6)
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jayporeci/pulls/6
Co-authored-by: arjoonn <arjoonn@midpathsoftware.com>
Co-committed-by: arjoonn <arjoonn@midpathsoftware.com>
Diffstat:
13 files changed, 649 insertions(+), 243 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -6,3 +6,5 @@ dist/
.coverage
prof/
./git-jci
+./description
+bin/*
diff --git a/.jci/run.sh b/.jci/run.sh
@@ -15,22 +15,101 @@ echo ""
cd "$JCI_REPO_ROOT"
-echo "Running tests..."
-go test ./...
+# ---------------------------------------------------------------------------
+# Helper: build one binary inside its own Docker container.
+#
+# Usage: build_target <GOOS> <GOARCH> <OUTPUT_BINARY_NAME> [GOARM]
+#
+# Each invocation is fire-and-forget (&); collect PIDs for later wait.
+# ---------------------------------------------------------------------------
+PIDS=()
+TARGETS=() # human-readable label per job, index-matched to PIDS
+
+build_target() {
+ local goos="$1"
+ local goarch="$2"
+ local output_name="$3"
+ local goarm="${4:-7}" # only meaningful when goarch=arm
+
+ local label="${goos}/${goarch}"
+ [[ "${goarch}" == "arm" ]] && label="${label}v${goarm}"
+
+ echo "[build] Spawning container for ${label} → bin/${output_name}"
+
+ docker run --rm \
+ -v "$PWD:/tmp/repo" \
+ -e GOOS="${goos}" \
+ -e GOARCH="${goarch}" \
+ -e GOARM="${goarm}" \
+ -e OUTPUT_BINARY_NAME="${output_name}" \
+ golang:1.25 \
+ "/tmp/repo/scripts/build_binary.sh" \
+ &
+
+ PIDS+=($!)
+ TARGETS+=("${label} → bin/${output_name}")
+}
+
+# ---------------------------------------------------------------------------
+# Step 1: Build Docker image (sequential — other steps depend on it)
+# ---------------------------------------------------------------------------
+echo "--- Step 1: Building Docker image via scripts/build_image.sh ---"
+scripts/build_image.sh
echo ""
-echo "Building static binary (CGO_ENABLED=0)..."
-CGO_ENABLED=0 go build -ldflags='-s -w -extldflags "-static"' -o "$JCI_OUTPUT_DIR/git-jci" ./cmd/git-jci
+# ---------------------------------------------------------------------------
+# Step 2: Cross-compile binaries in parallel
+#
+# Targets:
+# Linux amd64 git-jci-linux-amd64
+# Linux arm64 git-jci-linux-arm64
+# Linux arm (v7) git-jci-linux-armv7
+# Windows amd64 git-jci-windows-amd64.exe
+# Windows arm64 git-jci-windows-arm64.exe
+# ---------------------------------------------------------------------------
+echo "--- Step 2: Building binaries in parallel ---"
+mkdir -p bin
-# Verify it's static
+build_target linux amd64 git-jci-linux-amd64
+build_target linux arm64 git-jci-linux-arm64
+build_target linux arm git-jci-linux-armv7 7
+build_target windows amd64 git-jci-windows-amd64.exe
+build_target windows arm64 git-jci-windows-arm64.exe
+
+echo ""
+echo "Waiting for all build containers to finish..."
echo ""
-echo "Binary info:"
-file "$JCI_OUTPUT_DIR/git-jci"
-ls -lh "$JCI_OUTPUT_DIR/git-jci"
+
+# ---------------------------------------------------------------------------
+# Collect results — report each job individually so failures are obvious
+# ---------------------------------------------------------------------------
+FAILED=0
+for i in "${!PIDS[@]}"; do
+ pid="${PIDS[$i]}"
+ label="${TARGETS[$i]}"
+ if wait "${pid}"; then
+ echo "[OK] ${label}"
+ else
+ echo "[FAIL] ${label}"
+ FAILED=$((FAILED + 1))
+ fi
+done
echo ""
-echo "All steps completed successfully!"
+if [[ "${FAILED}" -gt 0 ]]; then
+ echo "${FAILED} build(s) failed. See output above for details."
+ exit 1
+fi
+
+echo "All binaries built successfully:"
+ls -lh bin/git-jci-*
echo ""
-echo "=== Installation ==="
-echo "Download and install with:"
-echo " curl -fsSL \$(git jci web --url)/git-jci -o /tmp/git-jci && sudo install /tmp/git-jci /usr/local/bin/"
+
+# ---------------------------------------------------------------------------
+# Step 3: Build site (sequential — needs the binaries to be present)
+# ---------------------------------------------------------------------------
+echo "--- Step 3: Building site inside jci container ---"
+docker run --rm -it -v "$PWD:/tmp/Jaypore CI" jci "/tmp/Jaypore CI/scripts/build_site.sh"
+echo ""
+
+echo "All steps completed successfully!"
diff --git a/CHANGELOG.md b/CHANGELOG.md
diff --git a/README.md b/README.md
@@ -1,11 +1,9 @@
# Jaypore CI
-A local-first CI system that stores results in git's custom refs.
+> Minimal, Offline, Local CI system.
## Installation
-### From source
-
```bash
go build -o git-jci ./cmd/git-jci
sudo mv git-jci /usr/local/bin/
diff --git a/scripts/build_binary.sh b/scripts/build_binary.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+set -euo pipefail
+
+# ---------------------------------------------------------------------------
+# Build a static git-jci binary for the requested platform.
+#
+# Required environment variables (set by the calling docker run command):
+# GOOS - Target OS (linux | windows | darwin)
+# GOARCH - Target arch (amd64 | arm64 | arm)
+# OUTPUT_BINARY_NAME - Final filename placed in ${OUTPUT_DIR}
+# e.g. git-jci-linux-amd64 / git-jci-windows-amd64.exe
+#
+# Optional:
+# GOARM - ARM version for GOARCH=arm (default: 7)
+# ---------------------------------------------------------------------------
+
+REPO_DIR="/tmp/repo"
+OUTPUT_DIR="${REPO_DIR}/bin"
+ENTRY_POINT="./cmd/git-jci"
+
+# Ensure required vars are present
+: "${GOOS:?GOOS must be set}"
+: "${GOARCH:?GOARCH must be set}"
+: "${OUTPUT_BINARY_NAME:?OUTPUT_BINARY_NAME must be set}"
+
+GOARM="${GOARM:-7}"
+
+cd "${REPO_DIR}"
+
+echo "[${OUTPUT_BINARY_NAME}] Downloading dependencies..."
+go mod download
+
+echo "[${OUTPUT_BINARY_NAME}] Building for GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM}..."
+mkdir -p "${OUTPUT_DIR}"
+
+CGO_ENABLED=0 \
+GOOS="${GOOS}" \
+GOARCH="${GOARCH}" \
+GOARM="${GOARM}" \
+go build \
+ -buildvcs=false \
+ -tags "netgo osusergo" \
+ -ldflags "-s -w -extldflags '-static'" \
+ -o "${OUTPUT_DIR}/${OUTPUT_BINARY_NAME}" \
+ "${ENTRY_POINT}"
+
+echo "[${OUTPUT_BINARY_NAME}] Binary built successfully: ${OUTPUT_DIR}/${OUTPUT_BINARY_NAME}"
diff --git a/scripts/build_image.sh b/scripts/build_image.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+
+IMAGE_NAME="jci"
+
+echo "Building Docker image '${IMAGE_NAME}'..."
+
+# Write a minimal Dockerfile that installs the required tools.
+# The repo is NOT baked into the image; it is mounted at runtime via -v.
+docker build -t "${IMAGE_NAME}" - <<'DOCKERFILE'
+FROM alpine:latest
+RUN apk add --no-cache git ca-certificates stagit bash
+WORKDIR /work
+CMD ["/bin/sh"]
+DOCKERFILE
+
+echo "Image '${IMAGE_NAME}' built successfully."
diff --git a/scripts/build_site.sh b/scripts/build_site.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+# Runs INSIDE the container.
+# The host repo is mounted at /tmp/repo (read-write).
+# Generated site is written to /tmp/repo/www_jci/public/releases.
+set -euo pipefail
+
+export REPO_DIR="/tmp/Jaypore CI"
+export PUBLIC_DIR="${REPO_DIR}/www_jci/public"
+export BUILD_DIR="/build"
+export RELEASES_DIR="${PUBLIC_DIR}/releases"
+export ASSETS_DIR="${PUBLIC_DIR}/assets"
+cd "$REPO_DIR"
+
+echo "==============="
+env
+echo "==============="
+
+# Ensure output directory exists
+mkdir -p "${RELEASES_DIR}"
+mkdir -p "${BUILD_DIR}"
+
+# Build a description line from the repo README for stagit
+(
+ cd "${REPO_DIR}"
+ head README.md | grep '>' > description 2>/dev/null || echo "jci" > description
+)
+
+cp -r "${REPO_DIR}/bin" "${REPO_DIR}/binaries"
+cp -r "${REPO_DIR}/bin" "${PUBLIC_DIR}/binaries"
+(cd "${BUILD_DIR}" && pwd && stagit -u /releases/git "${REPO_DIR}")
+cp -r "${BUILD_DIR}/." "${RELEASES_DIR}"
+
+# Clean up the temporary description file
+rm -f "${REPO_DIR}/description"
+rm -rf "${REPO_DIR}/binaries"
+
+cp "${ASSETS_DIR}/git.style.css" "${RELEASES_DIR}/style.css"
+cp "${ASSETS_DIR}/logo.png" "${RELEASES_DIR}/logo.png"
+cp "${ASSETS_DIR}/logo.png" "${RELEASES_DIR}/favicon.png"
+
+echo "Site generated at ${RELEASES_DIR}"
diff --git a/scripts/build_website.sh b/scripts/build_website.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+: "${DOCKER_BUILDKIT:=1}"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+PUBLIC_DIR="${REPO_ROOT}/www_jci/public"
+RELEASES_DIR="${PUBLIC_DIR}/releases"
+
+IMAGE_NAME="jci-stagit"
+CONTAINER_NAME="jci-stagit-builder"
+
+# Ensure releases directory exists
+mkdir -p "${RELEASES_DIR}"
+
+# Build docker image with stagit (using inline Dockerfile via here-doc)
+if ! docker image inspect "${IMAGE_NAME}">/dev/null 2>&1; then
+ tmp_dir="$(mktemp -d)"
+ mkdir -p "${tmp_dir}/repo" "${tmp_dir}/public_assets"
+ cp -R "${REPO_ROOT}"/.[!.]* "${tmp_dir}/repo" 2>/dev/null || true
+ cp -R "${REPO_ROOT}"/* "${tmp_dir}/repo"
+ cp "${PUBLIC_DIR}/assets/logo.png" "${tmp_dir}/public_assets/logo.png"
+ cp "${REPO_ROOT}/www_jci/Dockerfile" "${tmp_dir}/Dockerfile"
+ docker build -t "${IMAGE_NAME}" "${tmp_dir}"
+ rm -rf "${tmp_dir}"
+fi
+
+# Prepare context for stagit run
+BUILD_CONTEXT="$(mktemp -d)"
+mkdir -p "${BUILD_CONTEXT}/repo" "${BUILD_CONTEXT}/public_assets"
+cp -a "${REPO_ROOT}/." "${BUILD_CONTEXT}/repo"
+cp "${PUBLIC_DIR}/assets/logo.png" "${BUILD_CONTEXT}/public_assets/logo.png"
+if [ -f "${REPO_ROOT}/ui/src/libs/git.style.css" ]; then
+ cp "${REPO_ROOT}/ui/src/libs/git.style.css" "${BUILD_CONTEXT}/stagit_style.css"
+else
+ cat <<'EOF' > "${BUILD_CONTEXT}/stagit_style.css"
+:root {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;}
+EOF
+fi
+
+docker run --rm \
+ -v "${BUILD_CONTEXT}/repo:/tmp/repo" \
+ -v "${PUBLIC_DIR}/assets/logo.png:/data/www_jci/public/assets/logo.png" \
+ -v "${RELEASES_DIR}:/gitrepo" \
+ "${IMAGE_NAME}"
+
+rm -rf "${BUILD_CONTEXT}"
+
+echo "Releases site generated at ${RELEASES_DIR}"
diff --git a/www_jci/Dockerfile b/www_jci/Dockerfile
@@ -0,0 +1,12 @@
+FROM alpine:latest
+RUN apk add --no-cache git ca-certificates stagit
+WORKDIR /work
+ADD ./ /tmp
+RUN mkdir /gitrepo \
+ && (cd /tmp/repo && ls -alR /tmp && head README.md | grep '>' > description) \
+ && stagit -u /git /tmp/repo \
+ && rm /tmp/repo/description \
+ && cp /tmp/repo/www_jci/public/assets/git.style.css /gitrepo/style.css \
+ && cp /tmp/repo/www_jci/public/assets/logo.png /gitrepo/logo.png \
+ && cp /gitrepo/logo.png /gitrepo/favicon.png
+CMD ["/bin/sh"]
diff --git a/www_jci/public/.gitignore b/www_jci/public/.gitignore
@@ -0,0 +1,2 @@
+releases/
+binaries/
diff --git a/www_jci/public/assets/git.style.css b/www_jci/public/assets/git.style.css
@@ -0,0 +1,282 @@
+/* ── Jaypore CI · shared theme ── used by index.html, binaries.html, and stagit pages ── */
+
+:root {
+ --bg: #fbf9f4;
+ --ink: #14110f;
+ --muted: #6f6a65;
+ --line: #e6dfd2;
+ --accent: #14110f;
+}
+
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 2rem clamp(1rem, 4vw, 3rem) 4rem;
+ font-family: "iA Writer Quattro", "Inter", "SF Pro", system-ui,
+ -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ background: var(--bg);
+ color: var(--ink);
+ line-height: 1.6;
+}
+
+/* ── centring wrapper (used by stagit pages directly on body, by index.html via <main>) ── */
+body, main {
+ max-width: 1080px;
+ margin-inline: auto;
+}
+
+/* ── headings ── */
+h1, h2, h3, h4, h5, h6 {
+ margin: 0 0 0.5rem;
+ line-height: 1.25;
+}
+
+h1 { font-size: clamp(2rem, 5vw, 2.8rem); margin-bottom: 0.75rem; }
+h2 { font-size: 1.6rem; }
+h3, h4, h5, h6 { font-size: 1rem; }
+
+img, h1, h2 {
+ vertical-align: middle;
+}
+
+img {
+ border: 0;
+}
+[src="logo.png"], [src="../logo.png"] {
+ width: 80px !important;
+ height: 80px !important;
+}
+
+p {
+ margin: 0 0 1rem;
+}
+
+ul {
+ margin: 0 0 1rem;
+ padding-left: 1.1rem;
+}
+
+li + li {
+ margin-top: 0.3rem;
+}
+
+/* ── links ── */
+a {
+ color: var(--ink);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+a:hover,
+a:focus-visible {
+ color: var(--muted);
+}
+
+a.d,
+a.h,
+a.i,
+a.line {
+ text-decoration: none;
+}
+
+#blob a {
+ color: var(--muted);
+}
+
+#blob a:hover {
+ color: var(--ink);
+ text-decoration: none;
+}
+
+/* ── tables ── */
+table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.95rem;
+}
+
+table thead td {
+ font-weight: 600;
+ text-transform: uppercase;
+ font-size: 0.75rem;
+ letter-spacing: 0.18em;
+ color: var(--muted);
+ padding-bottom: 0.5rem;
+}
+
+table td {
+ padding: 0.35em 0.5em;
+}
+
+#content table td {
+ vertical-align: top;
+ white-space: nowrap;
+}
+
+#branches tr:hover td,
+#tags tr:hover td,
+#index tr:hover td,
+#log tr:hover td,
+#files tr:hover td {
+ background-color: var(--line);
+}
+
+#index tr td:nth-child(2),
+#tags tr td:nth-child(3),
+#branches tr td:nth-child(3),
+#log tr td:nth-child(2) {
+ white-space: normal;
+}
+
+td.num {
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+/* ── misc ── */
+.desc {
+ color: var(--muted);
+}
+
+.muted {
+ color: var(--muted);
+}
+
+hr {
+ border: 0;
+ border-top: 1px solid var(--line);
+ height: 1px;
+ margin: 2rem 0;
+}
+
+a:target {
+ background-color: var(--line);
+}
+
+/* ── code / pre ── */
+code, pre {
+ font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.92em;
+ background: var(--line);
+ padding: 2px 4px;
+ border-radius: 2px;
+}
+
+pre {
+ padding: 1rem 0px;
+ border-radius: 6px;
+ overflow-x: auto;
+ line-height: 1.55;
+}
+
+/* ── diff colours ── */
+pre a.h { color: var(--ink); font-weight: 600; }
+
+.A,
+span.i,
+pre a.i { color: #2d7a3a; } /* addition – green, readable on --bg */
+
+.D,
+span.d,
+pre a.d { color: #b93a3a; } /* deletion – red, readable on --bg */
+
+pre a.h:hover,
+pre a.i:hover,
+pre a.d:hover {
+ text-decoration: none;
+}
+
+/* ── index.html landing-page components ── */
+header {
+ margin-bottom: 3rem;
+}
+
+nav {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 0.95rem;
+ margin-bottom: 2rem;
+}
+
+nav a {
+ color: var(--muted);
+ text-decoration: none;
+ margin-left: 1.25rem;
+}
+
+nav a:hover,
+nav a:focus-visible {
+ color: var(--ink);
+}
+
+.lede {
+ font-size: 1.1rem;
+ color: var(--muted);
+}
+
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.75rem 1.5rem;
+ border: 1px solid var(--ink);
+ background: transparent;
+ font-weight: 600;
+ border-radius: 999px;
+ cursor: pointer;
+}
+
+.button:focus-visible,
+.button:hover {
+ background: var(--ink);
+ color: var(--bg);
+}
+
+section + section {
+ margin-top: 3rem;
+ padding-top: 3rem;
+ border-top: 1px solid var(--line);
+}
+
+.label {
+ display: block;
+ text-transform: uppercase;
+ font-size: 0.75rem;
+ letter-spacing: 0.18em;
+ color: var(--muted);
+ margin-bottom: 0.4rem;
+}
+
+.note {
+ font-size: 0.88rem;
+ color: var(--muted);
+}
+
+footer {
+ margin-top: 4rem;
+ padding-top: 2rem;
+ border-top: 1px solid var(--line);
+ font-size: 0.9rem;
+ color: var(--muted);
+}
+
+@keyframes rzp-shimmer {
+ 0% { background-position: -340px 0; }
+ 100% { background-position: 340px 0; }
+}
+
+.rzp-skeleton {
+ border-radius: 10px;
+ background: linear-gradient(
+ 90deg,
+ var(--line) 25%,
+ var(--bg) 50%,
+ var(--line) 75%
+ );
+ background-size: 340px 100%;
+ animation: rzp-shimmer 1.4s ease-in-out infinite;
+}
diff --git a/www_jci/public/binaries.html b/www_jci/public/binaries.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Downloads - Jaypore CI - > Minimal, Offline, Local CI system.
+</title>
+<link rel="icon" type="image/png" href="releases/favicon.png" />
+<link rel="alternate" type="application/atom+xml" title="Jaypore CI Atom Feed" href="releases/atom.xml" />
+<link rel="alternate" type="application/atom+xml" title="Jaypore CI Atom Feed (tags)" href="releases/tags.xml" />
+<link rel="stylesheet" type="text/css" href="releases/style.css" />
+</head>
+<body>
+<table><tr><td><a href="index.html"><img src="assets/logo.png" alt="" width="32" height="32" /></a></td><td><h1>Jaypore CI</h1><span class="desc">> Minimal, Offline, Local CI system.
+</span></td></tr><tr><td></td><td>
+<a href="releases/log.html">Log</a> | <a href="releases/files.html">Files</a> | <a href="releases/refs.html">Refs</a> | <a href="releases/file/README.md.html">README</a></td></tr></table>
+<hr/>
+<div id="content">
+<h2>Downloads</h2>
+<p>Pre-built binaries for the latest release of <strong>git-jci</strong>. Pick the one that matches your platform:</p>
+<table id="files"><thead>
+<tr><td><b>Mode</b></td><td><b>Name</b></td><td class="num" align="right"><b>Size</b></td></tr>
+</thead><tbody>
+<tr><td>-rwxr-xr-x</td><td><a href="binaries/git-jci-linux-amd64">git-jci-linux-amd64</a></td><td class="num" align="right">6.0M</td></tr>
+<tr><td>-rwxr-xr-x</td><td><a href="binaries/git-jci-linux-arm64">git-jci-linux-arm64</a></td><td class="num" align="right">5.6M</td></tr>
+<tr><td>-rwxr-xr-x</td><td><a href="binaries/git-jci-linux-armv7">git-jci-linux-armv7</a></td><td class="num" align="right">5.9M</td></tr>
+<tr><td>-rwxr-xr-x</td><td><a href="binaries/git-jci-windows-amd64.exe">git-jci-windows-amd64.exe</a></td><td class="num" align="right">6.2M</td></tr>
+<tr><td>-rwxr-xr-x</td><td><a href="binaries/git-jci-windows-arm64.exe">git-jci-windows-arm64.exe</a></td><td class="num" align="right">5.6M</td></tr>
+</tbody></table></div>
+</body>
+</html>
diff --git a/www_jci/public/index.html b/www_jci/public/index.html
@@ -1,240 +1,81 @@
-<!DOCTYPE html>
+<!doctype html>
<html lang="en">
<head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>JCI Project Overview</title>
- <style>
- :root {
- color-scheme: light dark;
- --bg: #f8f9fb;
- --surface: #ffffff;
- --text: #111217;
- --muted: #5b5f6d;
- --border: rgba(15, 19, 39, 0.08);
- --accent: #111827;
- }
-
- * {
- box-sizing: border-box;
- }
-
- body {
- margin: 0;
- font-family: "Inter", "SF Pro Display", "Segoe UI", system-ui, -apple-system,
- BlinkMacSystemFont, sans-serif;
- background: var(--bg);
- color: var(--text);
- line-height: 1.6;
- -webkit-font-smoothing: antialiased;
- }
-
- header {
- padding: 2rem clamp(1.5rem, 5vw, 5rem) 0;
- }
-
- nav {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 4rem;
- font-size: 0.92rem;
- }
-
- nav .brand {
- font-weight: 600;
- letter-spacing: 0.02em;
- }
-
- nav ul {
- list-style: none;
- display: flex;
- gap: 1.5rem;
- margin: 0;
- padding: 0;
- }
-
- nav a {
- text-decoration: none;
- color: var(--muted);
- transition: color 0.2s ease;
- }
-
- nav a:hover,
- nav a:focus-visible {
- color: var(--text);
- }
-
- .hero {
- max-width: 720px;
- }
-
- .hero h1 {
- font-size: clamp(2.5rem, 4vw, 3.6rem);
- line-height: 1.1;
- margin-bottom: 1rem;
- }
-
- .hero p {
- font-size: 1.08rem;
- color: var(--muted);
- margin-bottom: 2.5rem;
- }
-
- .cta-row {
- display: flex;
- flex-wrap: wrap;
- gap: 1rem;
- align-items: center;
- }
-
- .cta-primary {
- padding: 0.95rem 1.75rem;
- border-radius: 999px;
- border: none;
- cursor: pointer;
- background: var(--accent);
- color: white;
- font-size: 1rem;
- font-weight: 600;
- letter-spacing: 0.01em;
- }
-
- .cta-note {
- font-size: 0.9rem;
- color: var(--muted);
- }
-
- section {
- padding: 4rem clamp(1.5rem, 5vw, 5rem);
- }
-
- .grid {
- display: grid;
- gap: 1.5rem;
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
- }
-
- .card {
- background: var(--surface);
- border: 1px solid var(--border);
- border-radius: 20px;
- padding: 1.75rem;
- display: flex;
- flex-direction: column;
- gap: 0.75rem;
- min-height: 220px;
- }
-
- .card h3 {
- margin: 0;
- font-size: 1.2rem;
- }
-
- .card p {
- margin: 0;
- color: var(--muted);
- }
-
- .meta {
- padding-top: 0;
- }
-
- .meta-list {
- display: flex;
- flex-wrap: wrap;
- gap: 1.5rem;
- margin: 2rem 0 0;
- padding: 0;
- list-style: none;
- color: var(--muted);
- font-size: 0.95rem;
- }
-
- footer {
- padding: 2rem clamp(1.5rem, 5vw, 5rem) 3rem;
- color: var(--muted);
- font-size: 0.88rem;
- }
-
- @media (max-width: 600px) {
- nav {
- flex-direction: column;
- align-items: flex-start;
- gap: 1rem;
- }
-
- .card {
- min-height: auto;
- }
- }
- </style>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title>Jaypore CI</title>
+ <link rel="stylesheet" type="text/css" href="assets/git.style.css" />
+ <link rel="icon" type="image/png" href="assets/logo.png" />
</head>
<body>
- <header>
- <nav>
- <div class="brand">JCI</div>
+ <main>
+ <header>
+ <nav>
+ <strong>Jaypore CI</strong>
+ <div>
+ <a href="/binaries.html">Binaries</a>
+ <a href="/releases/file/README.md.html">Docs</a>
+ </div>
+ </nav>
+ <h1><img src="assets/logo.png" alt="JCI logo" style="height:1em;width:auto;vertical-align:middle;margin-right:0.4em;">Minimal. Offline. Local.</h1>
+ <p class="lede">
+ Jaypore CI is a minimal single-binary CI system, that can run offline on your own laptop.
+ </p>
+ <p class="lede">
+ It supports complex workflows, has a web interface, and has collaboration via git.
+ </p>
+ </header>
+
+ <section id="releases">
+ <span class="label">Binaries</span>
+ <h2>Download binaries</h2>
+ <p>
+ Download a <a href='binaries.html'>binary</a> of your choice and move it to <code>/usr/bin/git-jci</code>. Git will automatically start picking up the command and you can start running as <code>git jci web</code>.
+ </p>
+ <p>
+ </p>
+ <form id="rzp-form" class="rzp-skeleton" style="display:inline-block;min-width:172px;min-height:48px;"><script src="https://checkout.razorpay.com/v1/payment-button.js" data-payment_button_id="pl_SKcbmqJhCoHh1L" async> </script> </form>
+ <script>
+ (function () {
+ var form = document.getElementById('rzp-form');
+ var observer = new MutationObserver(function () {
+ if (form.querySelector('button,iframe,[class*="razorpay"]')) {
+ form.classList.remove('rzp-skeleton');
+ observer.disconnect();
+ }
+ });
+ observer.observe(form, { childList: true, subtree: true });
+ })();
+ </script>
+ </section>
+
+ <section id="docs">
+ <span class="label">Documentation</span>
+ <h2><a href='/releases/file/README.md.html'>README.md</a> for docs</h2>
+ <p>
+ Go through the <a href='/releases/file/README.md.html'>README.md</a> file to see how things work. A simple workflow is:
+ </p>
<ul>
- <li><a href="#source">Source code</a></li>
- <li><a href="#docs">Docs</a></li>
+ <li><code>git jci run</code> executes your pipeline. Run this mannually, or via pre-commit, or cron as shown below.</li>
+ <li><code>git jci web</code> runs a web server, browse the CI results locally.</li>
+ <li><code>git jci push/pull</code> syncs refs with your remotes.</li>
+ <li><code>git jci cron sync</code> syncs your cron spec with system cron so that you can run midnight builds for example.</li>
</ul>
- </nav>
- <div class="hero">
- <h1>Jaypore CI</h1>
+ </section>
+
+ <section id="discussions">
+ <span class="label">Discussions</span>
+ <h2>Bugs / Ideas / Support / Discussions</h2>
<p>
- Minimal. Offline. Local.
+ Please email us at <a href='mailto:support@midpathsoftware.com'>support@midpathsoftware.com</a> for anything you need.
</p>
- <div class="cta-row">
- <button class="cta-primary" aria-label="Buy this version for INR 100">
- Buy for $5
- </button>
- <span class="cta-note">Razorpay integration placeholder</span>
- </div>
- </div>
- </header>
-
- <section id="source">
- <div class="grid">
- <article class="card">
- <h3>Source code</h3>
- <p>
- Explore a modular Go + Node toolchain designed for command-line and
- web surfaces. Every component is documented and version-controlled to
- make auditing and contributions straightforward.
- </p>
- <p>
- <strong>Highlights:</strong><br />
- Clear folder layout, reproducible builds, thoughtful CLI ergonomics.
- </p>
- </article>
- <article class="card" id="docs">
- <h3>Docs</h3>
- <p>
- Lightweight documentation outlines project concepts, build targets,
- and extension hooks. Follow concise guides inspired by UV and Exe to
- move from setup to contribution in minutes.
- </p>
- <p>
- <strong>Includes:</strong><br />
- Architecture notes, extension patterns, release checklists.
- </p>
- </article>
- </div>
- </section>
-
- <section class="meta">
- <h2>Project focus</h2>
- <ul class="meta-list">
- <li>Minimal surface area, maximum clarity.</li>
- <li>Composable CLI experiences with clean UX.</li>
- <li>Self-hosted assets ready for static deployment.</li>
- </ul>
- </section>
+ </section>
- <footer>
- © <span id="year"></span> JCI. Built for teams who prefer fewer tabs and
- faster ships.
- </footer>
+ <footer>
+ © <span id="year"></span> Jaypore CI. Built for laptops, not data
+ centers.
+ </footer>
+ </main>
<script>
document.getElementById("year").textContent = new Date().getFullYear();
</script>