Skip to content

Project Spec #1

Description

@cmbrose

Project: sprite-kit (GitHub Action for Sprite-backed persistent CI steps)

Output
• A new dedicated GitHub repository named sprite-kit containing:
• Two GitHub Actions: init and run
• User documentation (README + examples + troubleshooting + security notes)
• CI for the action (lint/test/build) and release tagging

Goals

Primary
• Enable a workflow pattern where:
• A job creates/attaches to a named Sprite.
• Each wrapped step runs bash inside the Sprite.
• On success, the step creates a checkpoint tagged to that step.
• On rerun, previously-successful wrapped steps no-op; the first not-yet-successful wrapped step restores to the last successful checkpoint and retries.

Non-goals (initial design)
• Cross-run persistence (across different run_ids / unrelated reruns)
• Multi-job shared sprites (per-job, per-run; matrix-safe by name derivation)
• Rich output propagation from inside-sprite execution beyond logs (v1)

High-level design

Two actions in one repository
• your-org/sprite-kit/init@v1
• your-org/sprite-kit/run@v1

Implementation:
• TypeScript/Node20 JavaScript actions (not composite) for consistent behavior and API ergonomics.
• Compiled JS committed under each action directory (via ncc) for release tags.

Detailed behavior

Identity and persistence model

Sprite name (stable across reruns)
• Deterministically derive sprite_name from GitHub context stable on rerun:
• repository, workflow, run_id, job, and matrix discriminator (if any).
• Canonical name example:
• gh-{owner}-{repo}-{workflow}-{run_id}-{job}-{matrixHash}
• Normalize charset; truncate with hash suffix if needed.
• Allow override via input.

Step success marker
• “Step succeeded” == a checkpoint exists for that step.
• Each successful run invocation creates a checkpoint with a structured comment:
• ghrun={run_id};job={job_key};step={step_key}
• On rerun, run lists checkpoints and no-ops if it finds a matching checkpoint comment for step_key.

Last successful checkpoint pointer
• No separate mutable marker file is required.
• init computes last_ok_checkpoint_id by:
• filtering checkpoints to those matching ghrun={run_id};job={job_key};step=*
• selecting newest by create_time
• run restores to last_ok_checkpoint_id when needed.

Token and configuration model

Sprites token
• Users set a single env var once at workflow or job scope:
• SPRITES_TOKEN: ${{ secrets.SPRITES_TOKEN }}
• Both actions read SPRITES_TOKEN by default.
• Optional token input exists to override (advanced use); if neither is provided, the action fails with a clear error.

Action interfaces

init action

Inputs
• token (optional): overrides SPRITES_TOKEN
• sprite_name (optional): explicit sprite name override
• name_prefix (optional): prefix added to computed name
• job_key (optional): defaults to ${{ github.job }}
• matrix_key (optional): explicit matrix discriminator; otherwise derive from available context
• create (optional, default true): create sprite if missing
• bootstrap_command (optional): bash to run inside sprite after attach (no checkpointing in v1)
• log_level (optional): info|debug

Outputs
• sprite_name
• job_key
• run_id
• last_ok_checkpoint_id (empty if none)
• restore_needed (true if rerun attempt and last_ok_checkpoint_id exists)

Behavior
1. Compute sprite_name, job_key, run_id.
2. Ensure sprite exists (create if missing).
3. List checkpoints and compute last_ok_checkpoint_id.
4. Emit outputs.

run action

Inputs
• token (optional): overrides SPRITES_TOKEN
• sprite_name (required)
• job_key (required)
• run_id (required)
• last_ok_checkpoint_id (optional)
• step_key (required): stable identifier (e.g., deps, build, test-unit)
• command (required): bash script content to execute inside sprite
• shell (optional, default bash)
• checkpoint (optional, default true)
• timeout_seconds (optional)
• restore_mode (optional, default once-per-attempt): never | once-per-attempt | always-before-step
• log_level (optional): info|debug

Outputs
• skipped (true|false)
• checkpoint_id (if created)
• restored (true|false)

Behavior
1. List checkpoints; if checkpoint exists for this step_key → set skipped=true, exit 0.
2. If rerun attempt and restore not yet done and last_ok_checkpoint_id exists:
• restore to last_ok_checkpoint_id
• mark restore done for this job attempt via $GITHUB_ENV (e.g., SPRITES_RESTORED=1)
3. Execute command inside sprite (stream logs).
4. On success and checkpoint=true, create checkpoint with comment ghrun=...;job=...;step=... and output its id.
5. On failure, propagate failure (exit non-zero).

Repository structure

sprite-kit/
init/
action.yml
src/index.ts
dist/index.js
run/
action.yml
src/index.ts
dist/index.js
packages/
sprites-client/
src/http.ts
src/types.ts
src/sprite.ts
src/checkpoints.ts
src/exec.ts
docs/
examples/
basic.yml
matrix.yml
rerun.yml
keep-for-debug.yml
usage.md
troubleshooting.md
security.md
.github/workflows/
ci.yml
release.yml
README.md
LICENSE
package.json
tsconfig.json

Sprites API integration (client requirements)

Implement a minimal client with:
• getOrCreateSprite(name)
• listCheckpoints(sprite)
• createCheckpoint(sprite, comment)
• restoreCheckpoint(sprite, checkpointId)
• exec(sprite, command) (stream stdout/stderr)

Reliability requirements:
• retry transient errors (network, 5xx)
• clear errors including sprite_name + step_key
• secret masking; never log the token

User-facing workflow examples (docs)

Basic usage
• env: SPRITES_TOKEN at workflow/job scope
• init once
• run for deps, build, test

Rerun behavior
• Demonstrate: fail test on attempt 1; rerun skips deps/build, restores to build checkpoint, retries test.

Matrix usage
• Show safe sprite naming: include matrix discriminator or rely on derived hash.

Keep Sprite for debugging
• Document keeping the sprite after failure (initially manual; optional future cleanup action).

Documentation deliverables

README.md
• What sprite-kit does + quickstart snippet
• Concepts: step_key stability, checkpoint comments, restore-once behavior
• Inputs/outputs tables for init and run
• Limitations/assumptions

docs/usage.md
• Full configuration reference
• Name derivation rules, overrides
• Skip/restore algorithm details
• Command execution considerations

docs/troubleshooting.md
• Unexpected skips (changed step_key, existing checkpoint)
• No restore happened (no last_ok_checkpoint_id)
• Name collisions (matrix discriminator missing)
• Runner-side state mismatch (non-wrapped steps)

docs/security.md
• Token handling and least privilege
• Executing untrusted code considerations
• Logging/redaction guidance

Limitations to document (v1)
• Skipping is driven by checkpoint presence; step_key changes invalidate matching.
• Skipped steps won’t recreate any runner-side artifacts/outputs; prefer keeping stateful work inside the Sprite.
• Per-job/per-run scope; matrix jobs require unique identity to avoid collisions.

Testing plan

Unit tests
• Name derivation normalization + hashing
• Checkpoint filtering + newest-selection logic
• Skip decision and restore-once marker behavior
• Comment encoding/decoding

CI
• lint + typecheck + unit tests on PR
• build dist with ncc and enforce git diff --exit-code so dist remains in sync

Optional integration test
• Manual/nightly workflow using a real token to validate create/exec/checkpoint/restore path.

Milestones
1. Repository scaffolding (sprite-kit)

•	TS/Node toolchain, CI, action layout (init/, run/)

2.	Sprites client implementation

•	HTTP layer, retries, types, logging, exec streaming

3.	init action

•	identity derivation, get/create sprite, compute last_ok_checkpoint_id, outputs

4.	run action

•	skip logic, restore-once logic, exec + checkpoint-on-success, outputs

5.	Documentation + examples

•	README + docs + example workflows

6.	Release

•	Tag v1 and publish release notes; validate your-org/sprite-kit/init@v1 and .../run@v1 usage

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions