diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 767628a..6195296 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,11 @@ jobs: run: | go build -tags web -o vmsctl-web ./cmd/vmsctl ./vmsctl-web version + - name: The static live demo builds (site/demo, published by the Pages workflow) + run: | + go build -o vmsctl ./cmd/vmsctl + ./scripts/build-demo.sh + test -s site/demo/index.html && test -s site/demo/demo-data.json # Builds every release artifact as a snapshot (unsigned, unpublished), so the # release pipeline is tested before a tag needs it (docs/RELEASING.md). diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 4d1ede6..5fb8958 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,11 +1,12 @@ name: Pages # Publishes site/ to GitHub Pages when a change to it reaches main, and by hand (Actions, Run workflow). A custom domain needs the -# owner's approval. +# owner's approval. The live demo (site/demo) is built here, not kept in the repository: the dashboard with the recorded answers of +# `vmsctl server demo` (scripts/build-demo.sh), so it follows the dashboard and the rules. on: push: branches: [main] - paths: ["site/**", ".github/workflows/pages.yml"] + paths: ["site/**", "web/**", "scripts/build-demo.sh", "internal/server/**", "rules/**", "profiles/**", ".github/workflows/pages.yml"] workflow_dispatch: permissions: @@ -28,6 +29,17 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: stable + cache: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 22 + - name: Build the live demo into site/demo + run: | + go build -trimpath -o vmsctl ./cmd/vmsctl + ./scripts/build-demo.sh - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: diff --git a/.gitignore b/.gitignore index e96dba1..1bf46f5 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ CLAUDE.local.md /.tools/ /web/node_modules/ /ctx/ + +# The live demo is built by scripts/build-demo.sh (the Pages workflow); never committed +/site/demo/ diff --git a/CLAUDE.md b/CLAUDE.md index f4d7646..532a50c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,6 +18,8 @@ Released (see [docs/ROADMAP.md](docs/ROADMAP.md) for what each version added). K cross-compile with `GOOS=linux GOARCH=arm64 make build` - `make web` — build the dashboard (`web/`, Node 22) into `internal/server/webdist`; `make build-web` builds `./vmsctl` with it embedded (`-tags web`, what a release has) +- `make demo-site` — the static live demo into `site/demo` (the dashboard built with `--mode demo`, reading the recorded answers of + `vmsctl server demo`); CI builds it, and the Pages workflow publishes it. Never committed - `make test` — unit tests with the race detector - `make lint` — golangci-lint for linux/amd64 (formatting, plus rules that enforce ADR 0004: no exec, raw syscalls, host writes or direct host reads outside `internal/hostfs`), `make lint-guards` @@ -64,8 +66,8 @@ required check on `main`. | `action.yml` | The GitHub Action (composite): installs a verified release and scans an image, an archive or a root | | `packaging/container/` | The Dockerfile of the release image on ghcr.io (`.github/workflows/container.yml`) | | `install.sh` | The installer: downloads a release, verifies it, installs the binary | -| `scripts/` | Release checks, the translation check and lint guards (`check-release.sh`, `check-translations.sh`, `lint-guards.sh`) | -| `site/` | The project page for GitHub Pages (English, and `ko/`); published by `.github/workflows/pages.yml` once Pages is enabled | +| `scripts/` | Release checks, the translation check, lint guards and the live demo build (`check-release.sh`, `check-translations.sh`, `lint-guards.sh`, `build-demo.sh`) | +| `site/` | The project page for GitHub Pages (English, and `ko/`, with the generated KISA page `ko/kisa/`) and the live demo `demo/` (built, not committed); published by `.github/workflows/pages.yml` | | `.goreleaser.yaml` | Release build: archives, packages, SBOMs, checksums and signing (docs/RELEASING.md) | ## Security invariants — never violate diff --git a/Makefile b/Makefile index 9c2beb0..c383791 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ GORELEASER := $(TOOLS)/goreleaser-$(GORELEASER_VERSION) SNAPSHOT_SKIP ?= sign .DEFAULT_GOAL := help -.PHONY: help build build-web web test lint lint-guards fmt tidy vuln snapshot docs e2e clean +.PHONY: help build build-web web demo-site test lint lint-guards fmt tidy vuln snapshot docs e2e clean help: ## Show this help @awk 'BEGIN {FS = ":.*## "} /^[a-z0-9-]+:.*## / {printf " %-12s %s\n", $$1, $$2}' $(MAKEFILE_LIST) @@ -40,6 +40,9 @@ web: ## Build the dashboard into internal/server/webdist (needs Node 22) build-web: web ## Build ./vmsctl with the dashboard embedded in `vmsctl server` CGO_ENABLED=0 go build -trimpath -buildvcs=false -tags web -ldflags '$(LDFLAGS)' -o $(BINARY) ./cmd/vmsctl +demo-site: build ## Build the static live demo into site/demo (the dashboard with recorded answers of `vmsctl server demo`) + ./scripts/build-demo.sh + test: ## Run unit tests with the race detector go test -race ./... diff --git a/README.ko.md b/README.ko.md index 37f513b..8dc8d13 100644 --- a/README.ko.md +++ b/README.ko.md @@ -1,4 +1,4 @@ - + # vmsctl [![CI](https://github.com/with-developer/vmsctl/actions/workflows/ci.yml/badge.svg)](https://github.com/with-developer/vmsctl/actions/workflows/ci.yml) @@ -10,7 +10,7 @@ vmsctl은 서버의 보안 설정을 읽어서 점검 기준(주요정보통신 가이드가 공통으로 요구하는 간추린 기준)의 점검 항목마다 결과를 냅니다. 여러 서버의 결과는 대시보드 하나로 봅니다. 서버를 바꾸는 일은 없고, 판단하지 못한 것을 PASS로 추측하지도 않습니다. -[빠른 시작](#빠른-시작) · [대시보드](#여러-서버를-한눈에-보는-대시보드) · [문서](docs/README.ko.md) · [English](README.md) +[**라이브 데모**](https://with-developer.github.io/vmsctl/demo/) · [빠른 시작](#빠른-시작) · [대시보드](#여러-서버를-한눈에-보는-대시보드) · [문서](docs/README.ko.md) · [English](README.md) ![vmsctl 대시보드: 서버 전체의 통과율, 상태별 결과, 시간에 따른 통과율](docs/demo/dashboard-overview.png) @@ -24,7 +24,7 @@ vmsctl은 서버의 보안 설정을 읽어서 점검 기준(주요정보통신 악화한 항목, 서명된 결과, 모든 호스트의 모든 점검 항목을 한 장에 보여 주는 매트릭스를 제공합니다. - **파이프라인에 바로 씁니다.** 스키마가 있는 JSON, SARIF, JUnit, Markdown, CSV와 [GitHub Action](docs/ci.ko.md)을 지원합니다. -**호스트 없이 대시보드부터 보려면:** `vmsctl server demo` ([먼저 설치](#빠른-시작)) +**호스트 없이 대시보드부터 보려면:** 브라우저에서 바로 여는 [라이브 데모](https://with-developer.github.io/vmsctl/demo/)(가상의 데이터)나, 내 컴퓨터에서 `vmsctl server demo` ([먼저 설치](#빠른-시작)) ## 점검 결과는 이렇게 보입니다 diff --git a/README.md b/README.md index 75fd732..66471f3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ vmsctl reads a host's security configuration, reports a result for every control (Korea's KISA CII Unix guide, and a short baseline most guides agree on), and shows the whole fleet in a dashboard. It never changes anything, and it never guesses a PASS. -[Quick start](#quick-start) · [Dashboard](#a-dashboard-for-the-whole-fleet) · [Docs](docs/README.md) · [한국어](README.ko.md) +[**Live demo**](https://with-developer.github.io/vmsctl/demo/) · [Quick start](#quick-start) · [Dashboard](#a-dashboard-for-the-whole-fleet) · [Docs](docs/README.md) · [한국어](README.ko.md) ![The vmsctl dashboard: pass rate of the fleet, results by status and the pass rate over time](docs/demo/dashboard-overview.png) @@ -23,7 +23,7 @@ It never changes anything, and it never guesses a PASS. regressions, signed results and a matrix of every control on every host. - **Made for pipelines.** JSON with a schema, SARIF, JUnit, Markdown, CSV and a [GitHub Action](docs/ci.md). -**Try the dashboard without a host:** `vmsctl server demo` ([install first](#quick-start)). +**Try the dashboard without a host:** the [live demo](https://with-developer.github.io/vmsctl/demo/) in your browser (made-up data), or `vmsctl server demo` on your machine ([install first](#quick-start)). ## What a scan looks like diff --git a/scripts/build-demo.sh b/scripts/build-demo.sh new file mode 100755 index 0000000..3fd35a7 --- /dev/null +++ b/scripts/build-demo.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Builds the static live demo into site/demo (published by .github/workflows/pages.yml): the dashboard built with `--mode demo`, and +# the answers of `vmsctl server demo` recorded into demo-data.json next to it. Needs ./vmsctl (make build), node 22 and npm. +set -eu +cd "$(dirname "$0")/.." +bin=${VMSCTL:-./vmsctl} +port=${DEMO_PORT:-18943} +log=$(mktemp) +"$bin" server demo --listen "127.0.0.1:$port" >"$log" 2>&1 & +pid=$! +trap 'kill $pid 2>/dev/null || true; rm -f "$log"' EXIT INT TERM +i=0 +while ! grep -q 'hlt_api_' "$log"; do + if ! kill -0 "$pid" 2>/dev/null; then + cat "$log" >&2 + echo "the demo server stopped" >&2 + exit 1 + fi + sleep 0.5 + i=$((i + 1)) + if [ "$i" -ge 60 ]; then + cat "$log" >&2 + echo "the demo server did not start" >&2 + exit 1 + fi +done +token=$(grep -o 'hlt_api_[A-Za-z0-9_-]*' "$log" | head -1) +(cd web && npm ci --no-audit --no-fund >/dev/null && npm run build:demo) +node web/scripts/record-demo.mjs "http://127.0.0.1:$port" "$token" site/demo/demo-data.json diff --git a/site/index.html b/site/index.html index 0a366f2..f248edc 100644 --- a/site/index.html +++ b/site/index.html @@ -36,6 +36,7 @@ vmsctl