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
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)