Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 7 additions & 86 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,27 @@
# GitHub Copilot Instructions

This file provides general project context for GitHub Copilot. For review-specific guidance, see:
For review-specific guidance, see:

- **Code Review**: `.github/agents/code-review.agent.md`
- **Documentation Review**: `.github/skills/documentation-review/SKILL.md`

## Project Overview

Workshop is a tool for defining and handling ephemeral development environments. It uses a client-server architecture where `workshopd` daemon exposes a RESTful API to clients. The project is written in Go and distributed as a Snap package.

## Tech Stack

- **Language**: Go 1.25+
- **Container Backend**: LXD (primary abstraction target)
- **Packaging**: Snap (Snapcraft)
- **CLI Framework**: Cobra
- **Testing**: Go unit tests (gocheck) + Spread (integration/e2e)
- **Documentation**: Sphinx (reStructuredText)
- **CI**: GitHub Actions

## Repository Structure
Workshop is a tool for defining and handling ephemeral development environments. It uses a client-server architecture where `workshopd` daemon exposes a RESTful API to clients. The project is written in Go, packaged as a Snap, and uses LXD as the container backend. Unit tests use gocheck; end-to-end tests use Spread.

### Core Directories
- `cmd/` — CLI entry points (`workshop`, `workshopd`, `workshopctl`, `sdk`, `internal`)
- `client/` — Go client library for RESTful API communication
- `internal/` — Private packages (`daemon`, `overlord`, `workshop`, `interfaces`, `sdk`, etc.)
- `tests/` — E2E test suites using Spread (`main/`, `integration/`, `docs-*/`, `lib/`)
- `snap/` — Snap packaging configuration
- `docs/` — Sphinx documentation source and configuration (`Makefile`, `conf.py`, `contributing.rst`)

### Build Configuration
- `go.mod` / `go.sum` — Go module dependencies
- `.golangci.yaml` / `.golangci.incremental.yaml` — Linting configuration
- `.spread.yaml` — E2E testing with Spread framework
- `snap/snapcraft.yaml` — Snap package definition
- `docs/Makefile` — Sphinx documentation build script
Key directories: `cmd/` (CLI entry points), `client/` (Go client library), `internal/` (core packages: `daemon`, `overlord`, `workshop`, `interfaces`, `sdk`).

## Coding Guidelines

See [`docs/coding-style-guide.md`](../docs/coding-style-guide.md) for detailed standards. Key points:

- **Error messages**: Lowercase, no trailing punctuation, actionable (`what was attempted: why it went wrong`)
- **Error handling**: Consistent `if err := f(); err != nil { return err }` pattern
- **Code organization**: Early returns over nested conditions; keep coupled elements adjacent
- **Testing**: Unit tests (`*_test.go`) adjacent to implementation; Spread tests for integration

## Common Tasks

### Running Tests
- **Unit Tests**: Run `go test ./...` in the root directory.
- **E2E Tests**: Use `spread` (requires external setup). Example: `spread tests/integration/`.
See [`docs/coding-style-guide.md`](../docs/coding-style-guide.md) for detailed standards.

### Linting
- Run `golangci-lint run` to check for style and error handling issues.
## Development Workflow

### Building Documentation
- Navigate to `docs/` and run `make html` to build static HTML.
- Run `make run` in `docs/` to build and serve locally.

### Running Locally
- Quick path: `go tool try` — builds, starts `workshopd` against a temporary session, and drops into a pre-configured subshell. Exit to tear down; `--keep` retains the session.
- Manual: `go install ./cmd/...` then `workshopd run --create-dirs` (requires `WORKSHOP_DATA` and `WORKSHOP_CACHE` env vars set).

## Available Resources

- **Contributing Guide**: [`docs/contributing/development.rst`](../docs/contributing/development.rst) — Setup, testing, workflow
- **Documentation Style**: [`docs/doc-style-guide.md`](../docs/doc-style-guide.md) — reST/Markdown conventions
- **PR Template**: [`.github/pull_request_template.md`](.github/pull_request_template.md) — Self-review checklist
- **Code Review Agent**: [`.github/agents/code-review.agent.md`](.github/agents/code-review.agent.md) — For PR code reviews
- **Documentation Review Skill**: [`.github/skills/documentation-review/SKILL.md`](skills/documentation-review/SKILL.md) — For documentation reviews
See [`docs/contributing/development.rst`](../docs/contributing/development.rst) for setup, testing, and workflow details.

## Related Repositories

These external repositories provide authoritative context for the Workshop project:

- https://github.com/canonical/sdkcraft — SDKcraft utility codebase for packaging and publishing SDKs
- Individual SDK implementations live as `canonical/<name>-sdk` repositories (e.g. `ollama-sdk`, `cuda-toolkit-sdk`, `zephyr-sdk`); see `docs/reference/reference-implementations.rst` for the curated themed list
- https://github.com/canonical/reference-workshops — Reference workshop implementations demonstrating SDK usage patterns

## GitHub Actions Workflows

- `lint.yaml` — golangci-lint on Go code
- `unit-tests.yaml` — Go unit tests
- `spread.yaml` — E2E tests with Spread
- `cover.yaml` — Coverage reports
- `automatic-doc-checks.yml` — Sphinx builds (fail on warnings)
- `doc-cover.yaml` — Documentation coverage map generation
- `doc-update-sdk-schema.yml` — Updates SDK schema in docs
- `fix-redirected-links.yml` — Updates selected redirecting documentation links
- `release.yaml` — Builds release snaps + generates CLI reference PR
- `fixup.yaml` — Commit message format validation
- `scanning.yml` — Security scanning
- `markdown-style-checks.yml` — Markdown linting
- `build-deps.yaml` — Build dependencies check
- `lxd-candidate-check.yaml` — Checks LXD candidate channel
- `staging.yaml` — Rejects staged test SDKs
- `update-sphinx-stack.yaml` — Updates Sphinx Stack files and documentation dependencies
- `zizmor.yaml` — Audits GitHub Actions workflows

## Evolution Note

These instructions are living documentation. When Copilot misbehaves:
1. Note the specific failure mode
2. Identify which instruction file should address it
3. Propose minimal, high-signal edits (avoid essay-style additions)
4. Test with focused prompts before committing changes
5 changes: 5 additions & 0 deletions .github/knowledge.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
applyTo: "**"
---

Before implementing, reviewing, or planning any change, read the relevant subsystem note in [`.github/knowledge/`](.github/knowledge/).
4 changes: 4 additions & 0 deletions .github/knowledge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Workshop knowledge base

Curated, agent-facing notes about how Workshop actually works: architecture
maps, invariants, and gotchas.
59 changes: 59 additions & 0 deletions .github/knowledge/daemon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# workshopd daemon

**Scope**: Consult when working on the daemon lifecycle, state manangers
snapcraft.yaml or snap hooks or anything that reads/writes the data, common, or cache directories.

Daemon lifecycle logic lives in `internal/daemon`.

State managers are located in `internal/overlord/cmdstate`, `internal/overlord/healthstate`, `internal/overlord/hookstate`,
`internal/overlord/ifacestate`, `internal/overlord/sdkstate`, `internal/overlord/workshopstate`.

## Socket-activated daemon

`workshopd` is a **socket-activated systemd notify daemon** that deactivates
if the ensure cycle has run at least once, there are no active connections
for a period of time and no active changes running.

Implications of the socket activation that the daemon relies on:

- `workshop.bin` mounts are recreated for all existing
workshops on every daemon start up.
- All interface connections are reloaded from `state.json` on every daemon start up (see
`internal/overlord/ifacestate/ifacemgr.go:InterfaceManager.StartUp()`).
- Xauthority cookie is updated on every daemon startup. That allows the daemon to have
an up to date cookie after the user's logout from an X11 session with a subsequent login.
- Anything that is not written to `state.json` or to the workshop's OR SDK's
LXD configuration is lost on deactivation. E.g. the state's cache that the daemon uses for verbose logs, see `internal/overlord/state/state.go:State.Cache()`.

## Degraded mode

Triggered when `syscheck.CheckSystem()` fails (LXD missing/incompatible/down; out of storage space).

- Blocks all non-`GET` requests with the degraded error; `GET` still works.
- A recovery ticker re-runs `CheckSystem()` continuously and clears degraded
mode automatically — it also detects LXD disappearing _after_ startup.

## Directory layout

Three host roots, each overridable by an env var, with snap-specific defaults:

| Role | Var | Default (non-snap) | Snap value | Refresh behavior |
| ------ | ----------------- | --------------------- | ----------------------------- | -------------------------------------------- |
| Data | `WORKSHOP_DATA` | `/var/lib/workshop` | `$SNAP_DATA` | **Per revision** (tied to installed version) |
| Common | `WORKSHOP_COMMON` | falls back to Data | `$SNAP_COMMON/workshop` | **Shared** across revisions |
| Cache | `WORKSHOP_CACHE` | `/var/cache/workshop` | `$SNAP_COMMON/workshop/cache` | Shared |

Key distinction: **Data** is revision-scoped (`$SNAP_DATA`), **Common** survives
snap refreshes (`$SNAP_COMMON`).

## Invariants

**Security backends and interfaces are registered before `ensureBackendInit` runs.**

- **Why**: if `ensureBackendInit` runs first, `repo.Plug` returns nil and
`reloadConnections` silently skips every connection; `backendReady` then
latches true, so the later `StartUp()` call is a no-op.
- **Where enforced**: registration ordering in `InterfaceManager.StartUp()`,
`internal/overlord/ifacestate/ifacemgr.go`.
- **How it breaks**: reordering registration or the syscheck - connections never
load and no error is surfaced.
Loading