worky

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

worky

worky

A Go library for building interactive, self-contained workshop tools.

Go Reference License

The problem: running a workshop means participants fight setup — missing tools, broken environments, unclear progress. Worky fixes this with a single binary that locks chapters until checks pass and shows live status in the browser.

Participants download one file, run one command, and follow along in their own browser. No Docker, no cloud, no separate web server.


Try the demo

The demo/ directory is a fully working workshop you can run right now:

cd demo
go mod tidy
go run . serve --open

Then complete each chapter:

# Chapter 00 — Getting Started
touch done.txt
export WORKSHOP_USER=yourname
go run . check

# Chapter 01 — Hello Worky
echo "Hello, Worky!" > hello.txt
go run . check

# Chapter 02 — Finishing Up
echo "# Workshop Complete" > complete.md
go run . check

Watch the sidebar icons update live in your browser as each chapter unlocks. Run go run . status to see all chapters, go run . reset to start over.


How it works

A workshop is a Go binary that embeds a site/ directory of HTML and Markdown files:

your-workshop/
├── main.go          # imports worky, wires chapters + checks + embedded site
├── site/
│   ├── index.html           # home page
│   ├── workshop.css
│   ├── workshop-progress.js
│   └── 00-setup/
│       └── index.md         # chapter content (Markdown, rendered server-side)
└── go.mod

The worky server renders .md files on the fly and serves .html files as-is — no build step, no external tools.

If you prefer external slides (Google Slides, Marp, Reveal.js), skip writing chapter content — the built-in status page shows chapter progress with live 🔒/🔓/✅ icons regardless.

Participants:

  1. Download the binary (or git clone + go build)
  2. Run ./my-workshop serve --open
  3. Follow along in the browser, run ./my-workshop check to unlock each chapter

Create a workshop in 30 seconds

# Install the CLI
go install github.com/davideimola/worky/cmd/worky@latest

# Scaffold a new workshop
worky init "My Workshop"
cd my-workshop
go mod tidy
make serve

Add more chapters as your workshop grows:

worky new chapter 01 "Hello World"
# → creates site/01-hello-world/index.md
# → prints the Go snippet to add to main.go

Build for distribution:

worky build   # go build

Manual setup

1. Create the Go project

go mod init github.com/acme/my-workshop
go get github.com/davideimola/worky

2. Write main.go

package main

import (
    "embed"

    "github.com/davideimola/worky"
)

//go:embed all:site
var site embed.FS

func main() {
    worky.New(worky.Config{
        Name:    "My Workshop",
        HomeDir: ".my-workshop",
        Port:    8080,
        SiteFS:  site,
        Chapters: []worky.Chapter{
            {
                ID:   "00",
                Name: "Setup",
                Slug: "00-setup",
                Checks: []worky.Check{
                    {Description: "Docker is running", Run: myDockerCheck},
                },
            },
        },
    }).Run()
}

3. Write content

Create site/00-setup/index.md:

# Setup

Welcome to the workshop! Make sure your environment is ready.

## Steps

1. Start Docker Desktop
2. Run `./my-workshop check` to verify and unlock the next chapter

4. Build and distribute

go build -o bin/my-workshop .
GOOS=linux  GOARCH=amd64 go build -o bin/my-workshop-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -o bin/my-workshop-darwin-arm64 .

CLI commands

Command Description
serve [--port N] [--open] [--detach] Start the local web server
check [chapter-id] Run checks for a chapter and unlock the next one
status Show all chapters with lock/unlock/complete icons
reset Clear all progress
stop Kill the background server
logs [-f] Show (and optionally follow) server logs

If chapter-id is omitted, check auto-detects the first incomplete unlocked chapter.


Progress storage

Progress is stored in ~/<HomeDir>/progress.json. The first chapter is always unlocked. Running check for a chapter marks it complete and unlocks the next one.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chapter

type Chapter struct {
	ID     string  // "00", "01", etc.
	Name   string  // Display name
	Slug   string  // URL slug, e.g. "00-setup"
	Checks []Check // Validation steps for this chapter
}

Chapter represents a workshop chapter.

type ChapterStatus

type ChapterStatus struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Slug      string `json:"slug"`
	Unlocked  bool   `json:"unlocked"`
	Completed bool   `json:"completed"`
}

ChapterStatus is used in the API response.

type Check

type Check struct {
	Description string
	Run         func(context.Context) error
	// Timeout is the maximum time allowed for a single attempt. Zero means no timeout.
	Timeout time.Duration
	// Retries is the number of additional attempts after the first failure. Zero means run once.
	Retries int
	// RetryDelay is the pause between retry attempts. Zero means no delay.
	RetryDelay time.Duration
}

Check is a single validation step.

type CheckResult

type CheckResult struct {
	Description string `json:"description"`
	Passed      bool   `json:"passed"`
	Error       string `json:"error,omitempty"`
}

CheckResult holds the outcome of a single check.

type CheckResultStore

type CheckResultStore map[string][]CheckResult

CheckResultStore maps chapter IDs to their last check results.

type Config

type Config struct {
	Name     string // Workshop display name
	HomeDir  string // State directory under home (default: ".worky")
	Port     int    // Default HTTP port (default: 8080)
	SiteFS   fs.FS  // Embedded Hugo output (optional; if nil, a built-in UI is served)
	Chapters []Chapter
}

Config holds the workshop configuration provided by the creator.

type ProgressResponse

type ProgressResponse struct {
	Completed []string        `json:"completed"`
	Unlocked  []string        `json:"unlocked"`
	Chapters  []ChapterStatus `json:"chapters"`
}

ProgressResponse is the /api/progress response.

type State

type State struct {
	Completed []string `json:"completed"`
	Unlocked  []string `json:"unlocked"`
}

State holds the workshop progression state.

func (*State) Complete

func (s *State) Complete(id, nextID string)

Complete marks a chapter as completed and unlocks the next one.

func (*State) IsCompleted

func (s *State) IsCompleted(id string) bool

IsCompleted reports whether a chapter is completed.

func (*State) IsUnlocked

func (s *State) IsUnlocked(id string) bool

IsUnlocked reports whether a chapter is unlocked.

func (*State) Unlock

func (s *State) Unlock(id string)

Unlock makes a chapter accessible without completing it.

type Workshop

type Workshop struct {
	// contains filtered or unexported fields
}

Workshop is the runnable workshop instance.

func New

func New(cfg Config) *Workshop

New creates a new Workshop with the given config, applying defaults. It panics if chapter IDs or slugs are not unique.

func (*Workshop) Run

func (w *Workshop) Run()

Run builds the CLI and executes it.

Directories

Path Synopsis
Package checks provides pre-built check functions for use with worky workshops.
Package checks provides pre-built check functions for use with worky workshops.
cmd
worky command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL