commit 93b9f6689441b680d72a109f39fb3cf02d104b81
parent 522f246bc616fdd5d8fefb415717d4d46a2b2071
Author: Arjoonn Sharma <arjoonn@midpathsoftware.com>
Date: Sat, 21 Mar 2026 19:24:22 +0000
Better docs (!13)
Reviewed-on: https://gitea.midpathsoftware.com/midpath/jayporeci/pulls/13
Co-authored-by: Arjoonn Sharma <arjoonn@midpathsoftware.com>
Co-committed-by: Arjoonn Sharma <arjoonn@midpathsoftware.com>
Diffstat:
3 files changed, 125 insertions(+), 45 deletions(-)
diff --git a/README.md b/README.md
@@ -6,9 +6,12 @@
- [Install](#install)
- [Config](#config)
+ - [Scheduling with crontab](#scheduling-with-crontab)
- [Environment Vars](#environment-vars)
- [Example workflow](#example-workflow)
- [How it works](#how-it-works)
+ - [Viewing results](#viewing-results)
+ - [Sharing results with teammates](#sharing-results-with-teammates)
- [FAQ / Needs / Wants / Todos](#faq-needs-wants-todos)
- [Examples](#examples)
- [Golang Lint Build Test](#golang-lint-build-test)
@@ -31,98 +34,172 @@
## Install
+Download the binary for your platform from [www.jayporeci.in](https://www.jayporeci.in).
+Available builds:
+
+| Platform | Binary |
+|----------|--------|
+| Linux x86-64 | `git-jci-linux-amd64` |
+| Linux ARM64 | `git-jci-linux-arm64` |
+| Linux ARMv7 | `git-jci-linux-armv7` |
+| Linux 32-bit | `git-jci-linux-386` |
+| Windows x86-64 | `git-jci-windows-amd64.exe` |
+| Windows ARM64 | `git-jci-windows-arm64.exe` |
+
+> macOS is not currently offered as a pre-built binary. You can build from source with `go build ./cmd/git-jci`.
+
+After downloading, move the binary to somewhere on your `PATH`:
+
```bash
-# Download an appropriate binary from www.jayporeci.in
-# After that move it to some location on your PATH
-sudo mv git-jci /usr/local/bin/
+sudo mv git-jci-linux-amd64 /usr/local/bin/git-jci
```
-The binary is fully static (no dependencies) and works on most systems. If you are having issues, please contact us.
-Once installed, git will automatically find it as a subcommand and you can start using it via `git jci run`
+The binary is fully static (no dependencies) and works on most systems. Once installed, git will automatically find it as a subcommand and you can use it via `git jci`.
## Config
-Create a `.jci` folder in your repository. You can place a `run.sh` file and a `crontab` file in it.
-
-> Make sure that run.sh is executable!
+Create a `.jci` folder in your repository and add a `run.sh` file. Optionally add a `crontab` file for scheduled runs.
```
.jci/
-├── crontab
-└── run.sh
+├── run.sh # required: defines your CI pipeline
+└── crontab # optional: schedule recurring runs
+```
+
+**Make `run.sh` executable:**
+
+```bash
+chmod +x .jci/run.sh
```
-You can put anything in `run.sh`. Call a python program / run docker commands / replace it with a binary from a rust project that does something else entirely!
+`run.sh` can be anything ; a shell script, a wrapper that calls a Python program, a binary, a docker-compose invocation ; whatever you like.
-`crontab` is used to schedule things. You can run things like midnight tests / builds, SLA checks, repo auto-commits, repo time trackers etc.
+### Scheduling with crontab
+`.jci/crontab` uses standard 5-field cron syntax with two optional keyword arguments:
+
+```
+# SCHEDULE [branch:BRANCH] [name:NAME]
+0 0 * * * # midnight, current branch
+0 0 * * * branch:main # midnight, main branch
+*/15 * * * * name:quick-check # every 15 min, named job
+0 0 * * * branch:main name:nightly # named midnight build on main
+```
+
+After editing `.jci/crontab`, run `git jci cron sync` to install the entries into your system's crontab. Cron jobs run with the repository root as the working directory and have access to all three `JCI_*` environment variables.
## Environment Vars
-Your `run.sh` script has access to:
+Your `run.sh` script receives these environment variables:
| Variable | Description |
|----------|-------------|
| `JCI_COMMIT` | Full commit hash |
-| `JCI_REPO_ROOT` | Repository root path |
-| `JCI_OUTPUT_DIR` | Output directory for artifacts |
+| `JCI_REPO_ROOT` | Absolute path to the repository root |
+| `JCI_OUTPUT_DIR` | Absolute path to the output directory for this run |
+
+All three are always set. The script's initial working directory is `JCI_REPO_ROOT`. Any files written to `JCI_OUTPUT_DIR` become CI artifacts , browsable and downloadable via `git jci web`. The output directory is a temporary directory that is cleaned up after its contents are stored in git.
-The script runs with `cwd` set to `JCI_OUTPUT_DIR`. Any files created there become CI artifacts.
+> **Non-zero exit = failure.** If `run.sh` exits with a non-zero code, the run is marked as failed (`✗ err`). The artifacts are still saved and viewable.
## Example workflow
```bash
-cd repo-dir && git status # enter the repository and check the working tree
-git add -A # stage every modified, deleted, or new file
-git commit -m "..." # record the staged changes in a new commit
-git jci run # execute .jci/run.sh manually and capture artifacts for this commit. You could also use git hooks to run this automatically on commit.
-git jci web # launch the local viewer to inspect the latest CI results
-git jci push # push the commit's CI artifacts to the default remote
-git jci pull # fetch updated CI artifacts from the remote
-git jci prune # delete CI refs for commits that no longer exist locally
-git jci cron ls # list cron jobs that are there in .jci/crontab
-git jci cron sync # sync local machine's crontab with the current contents of .jci/crontab
+# 1. Enter the repository
+cd my-project
+
+# 2. Create the CI script (only needed once)
+mkdir -p .jci
+cat > .jci/run.sh << 'EOF'
+#!/usr/bin/env bash
+set -euo pipefail
+cd "$JCI_REPO_ROOT"
+echo "Running tests..."
+go test ./... | tee "$JCI_OUTPUT_DIR/test-results.txt"
+EOF
+chmod +x .jci/run.sh
+
+# 3. Commit and run CI
+git add -A
+git commit -m "add CI"
+git jci run # execute .jci/run.sh and store results for this commit
+
+# 4. View results locally (opens at http://localhost:8000)
+git jci web
+
+# 5. Share results with teammates
+git jci push # push CI refs to origin
+git jci pull # fetch CI refs pushed by teammates
+
+# 6. Maintenance
+git jci prune # dry-run: show what would be removed
+git jci prune --commit # actually delete CI refs for gone commits
+git jci prune --commit --older-than=14d # also delete refs older than 14 days
+
+# 7. Cron
+git jci cron ls # list scheduled jobs in .jci/crontab
+git jci cron sync # install/update entries in the system crontab
```
+You can also trigger `git jci run` automatically via [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) so CI runs on every commit without any manual step.
+
## How it works
-CI results are stored as git tree objects under the `refs/jci/` namespace.
-This keeps them separate from your regular branches and tags, but still
-part of the git repository.
+CI results are stored as git tree objects under the `refs/jci/` namespace (or `refs/jci-runs/` when a commit has multiple runs). This keeps them completely separate from your regular branches and tags while still being part of the repository.
-- Results are not checked out to the working directory
-- They can be pushed/pulled like any other refs
+- Results are **never** checked out to the working directory
+- They are pushed and pulled exactly like any other git refs but only when you explicitly run `git jci push` / `git jci pull`. A plain `git push` does **not** send them.
- They are garbage collected when the original commit is gone (via `prune`)
-- Each commit's CI output is stored as a separate commit object
+- Each run produces a `run.output.txt` artifact plus whatever else your `run.sh` writes to `JCI_OUTPUT_DIR`
+
+### Viewing results
+
+`git jci web` starts a local web server at **http://localhost:8000**. It shows all commits on your branches, their CI status, and lets you browse and download artifacts. Clicking a commit shows its output files in a three-panel layout:
+
+
+
+You can pass a custom port: `git jci web 9000`.
+
+### Sharing results with teammates
+
+```bash
+# You: after running CI
+git jci push # pushes refs/jci/* to origin
+
+# Teammate: to see your results
+git jci pull # fetches refs/jci/* from origin
+git jci web # now shows your CI results too
+```
+
+CI refs are pushed to and pulled from `origin` by default. You can specify a different remote: `git jci push my-remote`.
## FAQ / Needs / Wants / Todos
- [x] Complex pipeline definitions
- - `run.sh` can be an executable. Do whatever you like!
+ - `run.sh` can be any executable. Do whatever you like!
- [x] Artifacts
- - Anything in `JCI_OUTPUT_DIR` is an artifact!
+ - Anything written to `JCI_OUTPUT_DIR` is an artifact, browsable via `git jci web`.
- [x] Debug CI locally
- - Just execute `run.sh` locally.
+ - Just run `.jci/run.sh` directly. Set the three env vars yourself to simulate a CI environment.
- [x] Automate unit, integration, and end-to-end test suites on every commit
- Link [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) and run CI whenever you want.
- - For integration / end to end, I like to use a docker-compose that will compose up services / databases etc, and one container is a test driver like [locust](https://locust.io/).
+ - For integration / end to end, I like to use a docker-compose that composes up services / databases etc, with one container acting as a test driver like [locust](https://locust.io/).
- [x] Run linting and static analysis to enforce coding standards
- Link [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) and run CI whenever you want.
- [x] Produce code coverage reports and surface regressions
- - Outputs are placed in `JCI_OUTPUT_DIR`. We can put HTML coverage reports here if needed and view via CI browser.
- - For regressions, the `run.sh` can commit examples created by things like
+ - Write HTML coverage reports to `JCI_OUTPUT_DIR` and view them via `git jci web`.
+ - For regressions, `run.sh` can commit example files created by tools like
[hypothesis](https://hypothesis.readthedocs.io/en/latest/) back to the
- repo. This ensures that next runs will use those examples and test for
- regresssions.
+ repo. This ensures that next runs will use those examples and test for regressions.
- [x] Build, package, and archive release artifacts across target platforms
- I like building a docker image, building stuff inside that, then publishing.
- - Refer to the [scripts/](http://127.0.0.1:8000/releases/files.html) files for examples on how to build/render etc.
+ - See [`.jci/run.sh`](.jci/run.sh) in this repository for a real-world cross-compile + publish example.
- [x] Perform dependency and source code security scans (SCA/SAST)
- I like to run [Truffle Hog](https://github.com/trufflesecurity/trufflehog) to prevent accidental leaks.
- [x] Generate documentation sites and preview environments for review
- This Jaypore CI site itself is generated and published via CI.
- [x] Schedule recurring workflows (cron-style) for maintenance tasks
- - I run a nightly build via cron to ensure that I catch any dependency failures / security breaks. See [.jci/crontab](.jci/crontab.html) for an example.
+ - I run a nightly build via cron to ensure I catch dependency failures / security breaks. See [.jci/crontab](.jci/crontab) for an example.
- [x] Notify developers and stakeholders when CI statuses change or regress
- As part of our scripts, we can call telegram / slack / email APIs and inform devs of changes.
- [ ] Built-in secrets management with masking, rotation, and per-environment scoping
@@ -138,7 +215,6 @@ part of the git repository.
- [ ] Deployment environments with history, approvals, and promotion policies
- [ ] First-class integration with observability / error tracking tools (e.g., Sentry)
- [ ] Ecosystem of reusable actions/tasks with versioned catalogs and templates
- - This is already there? Not sure if this is something we even need to solve?
- [ ] Validate infrastructure-as-code changes and deployment pipelines via dry runs
@@ -638,7 +714,7 @@ run_js=false
run_go=false
if [ -z "$changed_files" ]; then
- echo "No diff detected (first commit or shallow clone) — running all sub-pipelines."
+ echo "No diff detected (first commit or shallow clone) running all sub-pipelines."
run_python=true
run_js=true
run_go=true
@@ -681,7 +757,7 @@ if $run_python; then
echo "Running: flake8 06-sub-pipelines/python-app/"
flake8 06-sub-pipelines/python-app/ 2>&1 && echo "Lint: PASS" || echo "Lint: FAIL"
else
- echo "No Python linter found (ruff/flake8) — checking syntax only."
+ echo "No Python linter found (ruff/flake8) checking syntax only."
find 06-sub-pipelines/python-app -name '*.py' -exec python3 -m py_compile {} + 2>&1 \
&& echo "Syntax check: PASS" || echo "Syntax check: FAIL"
fi
diff --git a/scripts/build_site.sh b/scripts/build_site.sh
@@ -26,6 +26,10 @@ mkdir -p "${BUILD_DIR}"
head README.md | grep '>' > description 2>/dev/null || echo "jci" > description
)
+# Regenerate the README table of contents before building the site
+echo "Regenerating README table of contents..."
+bash "${REPO_DIR}/scripts/generate_readme_index.sh" "${REPO_DIR}/README.md"
+
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}")
diff --git a/sshot.png b/sshot.png
Binary files differ.