Files
honey-biscuit-workshop/clarification.md
T
Catty Steve a8ed935f6a feat(baker/finalize): Add finalize stage framework with plugin system
and template

Implements the finalize stage infrastructure for HoneyBiscuitWorkshop:
- New finalize module with config, error, event, plugin, stage, and
  template submodules
- Plugin system supporting internal (webhook, mail, satori, in-site) and
  external plugins
- FinalizeConfig schema with version validation for finalize.yml
- Add glob, duration-str, lettre dependencies to workshop-baker
- Expand project documentation (clarification.md, description.md)
- Update .gitignore and fix bake.rs pipeline_functions access

BREAKING CHANGE: Not a buildable version!
2026-04-16 23:33:03 +08:00

38 KiB

Updated Questions & Doubts

A. Plugin System

  1. fallible vs critical

    • You changed from critical: false to fallible: false
    • Is fallible: true the same as critical: false? (Double negative situation)
    • Consider: fail_build_on_error: true/false is clearer
  2. Plugin URI Format

    • git::github.com/user/repo@main/1234abcd
    • Is main the branch and 1234abcd the commit?
    • What if branch and commit don't match? Which takes precedence?
    • Are other protocols supported? (hg::, http::, file::?)
  3. Plugin Configuration Scope

    • In plugin: section, you define host, user, token
    • In publish:, you define image: "docker.io/user/image:tag"
    • Are these merged? Overridden?
    • What if publish.docker-push.image differs from plugin.docker-push.host?
  4. Multiple Plugin Implementations

    • Can docker-push: have multiple URIs under it?
    docker-push:
      "git::github.com/user/plugin-a@main": ...
      "git::github.com/user/plugin-b@main": ...  # Allowed?
    

B. Notification Priority

  1. Fallback Semantics

    • Comment: "inter-group falling back"
    • Does group 1 run only if group 0 fails?
    • Or does group 1 run after group 0 succeeds?
    • Or both groups run, but 1 is lower priority for delivery?
  2. Parallel Within Group

    • If webhook succeeds but satori fails in group 0, what happens?
    • Does group 1 get triggered because of partial failure?
    • Or is group 0 considered "succeeded" if at least one succeeds?
  3. Notification Failure vs Build Failure

    • If all notifications fail, does the build fail?
    • Is there a global fallible for notifications?

C. Stage-Based Notifications

  1. on_finish_of Integration

    on_finish_of:
      stage: "prebake.ready"
    
    • How does this integrate with PrebakeStage enum from codebase?
    • Current stages: Bootstrap -> Ready (from AGENTS.md)
    • Are all stages valid? (prebake.bootstrap, bake.complete, finalize.*?)
  2. on_finish_of vs on_success/on_failure

    • Can these coexist on the same notification?
    • Does on_finish_of trigger regardless of success/failure?
    • Is there on_start_of for "notify when stage begins"?
  3. Stage Timing

    • When on_finish_of: prebake.ready fires, is {{ build.status }} available?
    • Or is build status only meaningful after bake stage?

D. Artifact System

  1. Auto-Generated ID

    id: "<SOME_ARTIFACT_ID>" # Generated by Pipeline UUID & Path when not given
    
    • Is id optional now? Can you omit it entirely?
    • If auto-generated, how do you reference it in publish?
    • Or is publish always by path match, not id?
  2. Empty Path Semantics

    path: "" # Implies non-directory artifact
    
    • What is a "non-directory artifact"?
    • Is this for metadata-only artifacts?
    • Or does empty path mean "use default output path from bake.sh"?
  3. Retention Duration Parsing

    • "7d", 604800 (seconds?), "168h" - are these equivalent?
    • What's the canonical format? Should there be validation?
    • Consider standardizing: ISO 8601 duration (P7D)?
  4. Compression dir Option

    compression: "zstd" # gzip|none|dir allowed
    
    • What does compression: "dir" mean? No compression, keep as directory?
    • Should this be "directory" or "raw" for clarity?

E. Template Variables

  1. Variable Availability by Stage

    • {{ build.duration }} - available at prebake.ready? (Probably not)
    • {{ build.status }} - what values? "success" | "failure" | "running"?
    • Should there be stage-specific variable namespaces? ({{ prebake.* }}, {{ bake.* }})
  2. Target Namespace

    • {{ target.group_id }} - where is this defined?
    • Is target the notification target, or build target environment?
    • Can users define custom {{ target.* }} variables?

F. Schema & Validation

  1. Schema Field Purpose

    schema: "default"
    schema: "custom"
    
    • What does default schema do? Built-in payload format?
    • Is custom required when on_* templates are provided?
    • Can you use schema: "slack" or other named schemas?
  2. Version Compatibility

    • version: "0.0.1" - suggests breaking changes expected
    • How will version migrations work?
    • Should there be a schema_version separate from implementation version?

G. Missing Features (Intentional?)

  1. Conditional Artifacts

    • Can artifacts have conditions like bake.sh's @if()?
    • E.g., only publish docker image on main branch
  2. Artifact Dependencies

    • Can one artifact depend on another?
    • E.g., "publish only if artifact A was successfully uploaded"
  3. Cleanup Granularity

    cleanup:
      policy: "auto"
    
    • What other policies exist? "manual"? "on_failure"?
    • Is cleanup per-artifact or global?
  4. Timeouts & Retries for Notifications/Plugins

    • No timeout/retry config visible
    • Are these inherited from bake.sh decorators?
    • Should plugins have their own retry logic?

Summary of Design Gaps

Area Missing/Unclear
Plugin Config merging, multiple URIs, protocol support
Notification Fallback semantics, failure propagation, stage integration
Artifact Auto-ID referencing, empty path use case, conditions
Template Variable availability by stage, custom namespaces
Schema default/custom behavior, extensibility
Error Handling Timeouts, retries, failure escalation

What would you like to clarify or iterate on?


User

  1. fallible:true = critical: false, of course. This concept is the same as bake.sh
  2. if no according commit/tag in the branch, it means nothing and fail. Other protocols will be supported but some are sonner(file) and some are later(hg)
  3. Caller than Definition: parameters in publish overrides plugin No way could user success if image.host != host. host is only for authentication usage. image.host could be emitted if host given.
  4. not allowed. key is unique. 5/6/7. fallback explanation: (notification->no) no.0 starts all at once if one method in no.0 fails, no.1 is active (then no.2,...) the failure of no.0 will be part of no.1, this means baker will wait for no.0 to finish and then no.1 fallible could apply here
  5. for prebake/finalize, second field is static, for bake, second field is dynamic according to bake.sh / Function(class) validity checks upon execution, maybe early-checked at future
  6. yes maybe a condition system required
  7. Still WIP
  8. ID is optional and will be generated by system publish doesn't reference artifact, artifact reference publish
  9. Docker image as an example future url:// based path, such as docker://docker.io/user/image:tag adopted from RedoxOS, however too advanced to promote.
  10. equivalent. validation required. default unit is second
  11. dir will be used for software repo: they need structued directory than single .tar
  12. WIP
  13. WIP
  14. default will be defined by (internal) plugins custom required for on* schema could be defined in notification block for unified use
  15. We'd expect not to change schema after 1.0, though it's impossible migration system built-in for new version
  16. yes
  17. yes
  18. WIP
  19. WIP

Assistant (Sisyphus (Ultraworker) · GLM-5 · 57.4s)

Synthesized Design Understanding

Based on your clarifications, here's the refined design:

Plugin System

plugin:
  <logical-name>:           # Unique key
    "<protocol>::<uri>@<branch>/<commit>":
      # Auth/config parameters (can be overridden in publish)
      host: "docker.io"
      user: {{ dockerhub.user }}
      token: {{ secret.dockerhub.token }}
      fallible: false       # Same semantics as bake.sh @fallible
  • host = authentication endpoint only
  • publish.<name>.image can omit host if plugin host defined

Notification Flow

Priority 0: [webhook, satori] ──parallel──┬──► if ALL fail ──► Priority 1: [mail]
                                          │
                                          └──► wait for completion before next group
  • Within group: parallel
  • Across groups: sequential fallback on failure
  • fallible can apply per notification method

Artifact Publishing

artifact:
  - path: ""                  # Empty = URL-style artifact (docker://, etc.)
    publish:
      <plugin-name>:
        # Override plugin params here
        image: "docker.io/user/image:tag"  # host optional if plugin defines it
  • Artifact pushes to publish, not publish pulls artifact
  • ID auto-generated when omitted

Follow-Up Questions

A. Plugin Config Merging

  1. Deep vs Shallow Merge

    plugin:
      docker-push:
        host: "docker.io"
        auth:
          user: "admin"
          token: "xxx"
    
    publish:
      docker-push:
        auth:
          user: "override-user"  # Does this merge {user, token} or replace entire auth?
    
    • Is merge deep (nested objects merge) or shallow (replace entire key)?
  2. Required vs Optional Plugin Params

    • If plugin defines host and user as required, but publish omits user...
    • Is there a schema for plugin to declare required params?
    • Or is validation purely "fail at runtime if missing"?

B. Notification Fallback Edge Cases

  1. Partial Failure Within Group

    Priority 0: webhook ✓, satori ✗
    
    • Is group 0 considered "failed" if any method fails?
    • Or only if all methods fail?
    • This determines whether priority 1 triggers
  2. Timeout Interaction with Fallback

    0:
      webhook:
        url: "..."
        timeout: 30s    # Hypothetical
    1:
      mail: ...
    
    • If webhook times out at 30s, does mail immediately start?
    • Or does system wait for webhook's internal retry logic first?
  3. Fallback with fallible

    0:
      webhook:
        fallible: true
    1:
      mail:
        fallible: false
    
    • If webhook fails but is fallible: true, does mail still trigger?
    • Or does fallible: true mean "failure doesn't trigger fallback"?

C. Stage-Based Notifications

  1. Stage Naming Convention

    • prebake.ready - static name
    • bake.build - function name from bake.sh?
    • What about finalize stages? finalize.artifact? finalize.notification?
    • Are there built-in stages that always exist?
  2. Multiple on_finish_of Triggers

    satori:
      on_finish_of:
        - stage: "prebake.ready"
        - stage: "bake.test"
    
    • Can a notification trigger at multiple stages?
    • Or is it one on_finish_of per notification definition?
  3. on_success + on_finish_of Combination

    satori:
      on_finish_of:
        stage: "prebake.ready"
      on_success: |  # Build success, not stage success?
    
    • If on_finish_of: prebake.ready fires but build eventually fails...
    • Does on_success content get used or on_failure?
    • Or does on_finish_of have its own content only?

D. Artifact Path Schemes

  1. URL Scheme Examples

    path: "docker://docker.io/user/image:tag"
    path: "s3://bucket/key"
    path: "ghcr://user/repo:tag"
    
    • Are these the intended schemes? Or something else?
    • Does each scheme map to a specific plugin automatically?
  2. Mixed Path Types in Single Config

    artifact:
      - path: "/workspace/output.tar.gz"      # File
      - path: ""                               # Docker image (how to specify image name?)
      - path: "docker://docker.io/img:tag"    # Explicit scheme
    
    • When path: "", where does the image name come from? Only from publish.docker-push.image?
    • Can you have path: "" without publish? (Just store locally?)

E. Compression & Directory Artifacts

  1. dir Compression + Upload
    artifact:
      - path: "/workspace/output/"
        compression: "dir"
        publish:
          some-plugin: ...
    
    • When uploading a dir artifact, does plugin receive:
      • The directory path?
      • A tarball created on-the-fly?
      • Individual files?
    • How does docker-push handle compression: "dir"?

F. Conditional System (Planned)

  1. Condition Syntax
    artifact:
      - path: "..."
        publish:
          docker-push:
            image: "..."
            if: "${BUILD_BRANCH} == 'main'"    # Hypothetical
    
    • Will conditions use shell-style $VAR?
    • Or template-style {{ env.BRANCH }}?
    • Or bake.sh-style @if(WS_PIPELINE_ARCH == amd64)?

G. Error Handling

  1. Plugin Execution Failure

    artifact:
      - publish:
          docker-push:
            fallible: true
          another-plugin:
            fallible: false
    
    • If docker-push fails (fallible), does another-plugin still run?
    • Or do all plugins for an artifact run in parallel regardless?
  2. Artifact Collection Failure

    • What if path: "/workspace/target/release/*" matches nothing?
    • Is this a build failure or silently skipped?
    • Is there on: ["missing"] for handling missing artifacts?

Design Consistency Observations

Alignment with Existing Patterns

Concept prebake.yml bake.sh finalize.yml
Fallible - @fallible fallible: bool
Timeout - @timeout(30) WIP
Retry - @retry(3, 5) WIP
Parallel - @parallel Intra-group parallel ✓
Conditional - @if(cond) Planned ✓
After/Deps hooks.early/late @after(func) Priority groups ✓

Potential Inconsistencies

  1. retension typo - Should be retention

  2. Duration format inconsistency

    • prebake.yml: ttl_days: 30
    • finalize.yml: retension: "7d"
    • Consider standardizing to one format
  3. Plugin reference style

    • bake.sh: @after(func_name) - direct reference
    • finalize.yml: publish: docker-push: - nested config
    • Both valid, just noting the difference

What would you like to explore next?


User

  1. any
  2. overrides "auth" than "user"
  3. checker cries. checked implemented in plugin
  4. same as 3. timeout = single failure, not group failure
  5. fallible of a stage will stop error propagation, thus leaving 1 alone. for last stage, fallible means no other method of notification. (if none of notification FULLY works, system-level fallback, like user email via system(SHOULD be well-configured because user creation relies on this) and in-site notification)
  6. WIP cuz finalize itself is our plan
  7. yes, logical expression may be introduced
  8. every condition have its own content
  9. not quite planned by now. not completely correct? bake.sh shoudn't upload content to S3 and let finalize to fetch it for artifact however maybe we don't prohibit this. users may do everything in bake and skip finalize.
  10. path:none + publish: none means nothing, because the build environment will soon be destroyed if it's saved externally (eg. via docker socket), then it shoudn't exist as artifact
  11. plugin receive a prefetched directory at plugin workspace, this is ensured by rsync. docker-push should ignore compression (or maybe prohibit it)
  12. WIP
  13. if one fails but fallible, others will run as expected
  14. matches nothing invalidates artifact content, but plugin may handle "none" as content
  15. retention IS typo.
  16. we haven't implemented prebake.cache thus they might be unified

Assistant (Sisyphus (Ultraworker) · GLM-5 · 54.4s)

Updated Design Understanding

Notification Fallback (Clarified)

Priority 0: [webhook ✗, satori ✗] ──► ANY fails triggers fallback
                ↓
Priority 1: [mail]
                ↓
Priority N: [system-email, in-site] ──► Ultimate fallback (system-level)
  • fallible: true at group N = stop propagation, don't trigger group N+1
  • fallible: true at last defined group = no further notification attempts
  • System-level fallback exists outside user config (configured at user creation)

Plugin Execution

Artifact Collection (rsync to plugin workspace)
        ↓
Plugin receives: prefetched directory
        ↓
docker-push: ignores/prohibits compression: "dir"

Config Override Behavior

plugin:
  docker-push:
    auth:                    # ENTIRE key replaced
      user: "default"
      token: "xxx"

publish:
  docker-push:
    auth:
      user: "override"       # { user: "override" } - token LOST

Remaining Questions

A. Plugin Workspace & File Handling

  1. Plugin Workspace Location

    • Where is the plugin workspace? /workspace/.plugins/docker-push/?
    • Is this workspace shared across all artifacts using same plugin?
    • Or isolated per artifact?
  2. Large Artifact Handling

    • If artifact is 10GB, does rsync complete before plugin starts?
    • Or does plugin stream from rsync?
    • Is there a size limit/timeout for rsync?
  3. Plugin Working Directory

    • Does plugin receive path as-is, or resolved absolute path?
    • Example: path: "/workspace/target/release/*" → plugin sees /workspace/.plugins/docker-push/release/?

B. Artifact Validation

  1. Glob Expansion Timing

    path: "/workspace/target/release/*"
    
    • When is glob expanded? Config load? Execution time?
    • If expanded at load time to 3 files, is it 3 artifacts or 1 artifact with 3 files?
  2. Artifact ID Generation

    • Format: pipeline-uuid-path-hash? Or something else?
    • Is ID deterministic (same path = same ID) or random?

C. Publish Execution Order

  1. Multiple Plugins for One Artifact
    artifact:
      - path: "/workspace/output.tar.gz"
        publish:
          docker-push:
            image: "app:latest"
          s3-upload:           # Second plugin
            bucket: "my-bucket"
    
    • Execution order: sequential? parallel? configurable?
    • If docker-push fails, does s3-upload run?

D. Notification Content Templates

  1. Condition-Content Mapping

    satori:
      on_finish_of:
        stage: "prebake.ready"
        content: "Prebake done"
      on_success:
        content: "Build succeeded"
      on_failure:
        content: "Build failed"
    
    • If prebake.ready finishes but build later fails...
    • Does on_finish_of send at prebake.ready time?
    • Then on_failure sends at build end?
    • Both messages sent, right?
  2. Template Variable Scope

    on_finish_of:
      stage: "prebake.ready"
      content: "{{ build.status }}"  # Not available yet?
    
    • What happens when variable isn't available at that stage?
    • Empty string? Error? "unknown"?

E. Error Recovery

  1. Plugin Error Reporting

    • How does plugin report errors back to baker?
    • Exit code? JSON output? Structured log?
    • Is there a plugin API contract?
  2. Retrying Individual Methods

    notification:
      0:
        webhook:
          retry: 3            # Hypothetical
          retry_delay: 5s
    
    • Will retry be per-method, per-group, or global?
    • Or should plugins handle their own retry?

F. System-Level Fallback

  1. System Notification Configuration

    • Where is system email configured?
    • In prebake.yml? baker config? System-wide config?
    • What if system email also fails?
  2. In-Site Notification

    • Does this mean a web UI notification?
    • Stored in database? Shown in baker dashboard?

G. Typo & Format Fixes

  1. Proposed Fixes

    artifact:
      - path: "/workspace/output.tar.gz"
        retention: "7d"        # Fixed typo
        compression: "zstd"
        on:
        - success
    
    • Confirm: retensionretention for final version?
  2. Duration Format Unification

    # Option A: String with unit (current)
    retention: "7d"
    
    # Option B: Structured
    retention:
      days: 7
    
    # Option C: ISO 8601
    retention: "P7D"
    
    • Preference for consistency with future prebake.cache?

H. Edge Cases

  1. Empty Notification Group

    notification:
      0: {}          # Empty group?
      1:
        mail: ...
    
    • Is empty group valid? Ignored?
    • Or validation error?
  2. Circular Plugin Reference

    • Can plugin A's output be used as plugin B's input?
    • Example: docker-push → image scanner plugin
    • Or should this be a single composed plugin?

Summary: Design Maturity

Component Status Notes
Plugin system Clear URI, override, fallible defined
Notification priority Clear Fallback semantics defined
Notification stages ⚠️ WIP on_finish_of structure defined, finalize stages TBD
Artifact collection Clear rsync to plugin workspace
Artifact publishing Clear Sequential/parallel WIP
Conditional system ⚠️ WIP Planned
Error handling ⚠️ WIP Timeout/retry TBD
System fallback Clear Email + in-site notification
Typo fix Confirmed retention
Duration format ⚠️ TBD Unify with prebake.cache

What's the next aspect to design or clarify?


User

  1. defined by plugin itself
  2. rsync before plugin, UNLESS specified by plugin (maybe someone implements more robust method) (not completely rsync thou, hard link/filesystem features may be used)
  3. as 1
  4. glob->RE, checked when loading, compiled at running, 1s for total limitation(RE compile+match). one path means one artifact element
  5. hash of PUID+PATH, maybe MD5 is enough
  6. SHOULD be parallel, fails: see fallible
  7. both sent, one first.
  8. WIP as variable system not defined, just as example. build.status could mean "bake.fetch:3%" or "prebake:ready!" here.
  9. plugin is shell scripts/ rhai scripts. Via API/CLI(or, function, for shell) and exit-code. Log will be captured but not as evidence
  10. retry per method
  11. system email contigured via Web+Server(not started) If system email also fails: via in-site notification If none works: we admit we have no way to notify user. We give up. Admin will receive this message if the configures to do so.
  12. WebUI, stored in DB, in baker dashboard.
  13. we'll fix it
  14. ISO 8601 sounds good, P could be emitted because only period supported. Who'd like to hard-code specific time into static config? Maybe there is.
  15. 0 should be eliminated and email evolve to 0 every field could lie in any priority, thus webhook could lie in 999 treat as a failure if 0 is none
  16. make plugin callable w/o artifact chain loading might be reconsidered, because push and check may share the same INPUT than OUTPUT

Assistant (Sisyphus (Ultraworker) · GLM-5 · 54.3s)

Final Design Synthesis

Artifact Collection Flow

Glob Path → RE Compile (load) → Match (run, 1s timeout) → One artifact per match
                              ↓
                    Hash(PUID + PATH) → MD5 as ID
                              ↓
              Hard-link/rsync to plugin workspace (plugin-defined location)
                              ↓
                    Plugin executes (shell/rhai)
                              ↓
                    Exit code = success/failure

Notification Flow (Final)

Priority 0: [webhook, satori] ──parallel──► ALL succeed = done
         │                              │
         │                              └── ANY fails ──► Priority 1
         │                                        │
         ▼                                        ▼
    retry per method                      [mail] ──► ...
                                                      │
                                                      ▼
                                            System fallback: in-site DB
                                                      │
                                                      ▼
                                            Give up → Admin notification

Plugin Architecture

Plugin = Shell script | Rhai script
       └── Communicates via: API/CLI/function calls + exit code
       └── Log captured (informational, not evidence)
       └── Workspace location: plugin-defined
       └── File transfer: hard-link/rsync by default, plugin can override
       └── Callable without artifact (standalone execution)

Duration Format

retention: "7D"     # ISO 8601, P omitted
retention: "168H"   # Hours also valid
retention: "604800" # Seconds (no unit)

Final Questions

A. Plugin Without Artifact

  1. Standalone Plugin Definition

    plugin:
      health-check:          # No artifact input
        "git::github.com/user/health-plugin@main":
          endpoint: "https://api.example.com"
    
    # How is it invoked?
    
    • Where does standalone plugin get invoked? In notification block?
    • Or separate finalize.plugins: section?
    • Example syntax?
  2. Standalone Plugin Output

    • Does standalone plugin produce anything?
    • Or purely side-effect (HTTP call, etc.)?

B. Plugin Chain (Reconsidered)

  1. Shared Input Pattern
    Artifact A ──► docker-push ──► (image in registry)
                         │
                         └──► image-scanner ──► (scan results)
                               (same input: artifact A, not docker-push output)
    
    • Both plugins receive same artifact input?
    • Configuration example:
    artifact:
      - path: "/workspace/image.tar"
        publish:
          docker-push: { image: "app:latest" }
          image-scanner: { severity: "high" }  # Same input, parallel?
    

C. Priority Group Flexibility

  1. Non-Sequential Priority Numbers

    notification:
      0:
        webhook: ...
      999:
        mail: ...
    
    • Is 999 valid? Or auto-renumbered to 1?
    • What about gaps: 0, 5, 10?
    • Integers only, or can use named priorities? (high, low?)
  2. Empty Group Handling

    notification:
      0: {}
      1:
        mail: ...
    
    • Result: 0 eliminated, mail becomes 0?
    • Or: validation error at config load?

D. Plugin Language Details

  1. Rhai vs Shell Choice

    • Is this a plugin-level declaration?
    plugin:
      my-plugin:
        "git::..."
        runtime: "rhai"  # or "shell"?
    
    • Or detected by file extension? (plugin.rhai vs plugin.sh)
  2. Plugin API Contract

    • Shell: environment variables? CLI arguments? Stdin?
    • Rhai: injected functions/objects?
    • Example of plugin receiving artifact path and config?

E. Build Status Progress

  1. Progress Reporting
    build.status = "bake.fetch:3%"
    build.status = "prebake:ready!"
    
    • Is this a real-time string that updates?
    • Or structured: {{ build.stage }} + {{ build.progress }}?
    • How does plugin/notification read live progress?

F. Configuration Validation

  1. Early vs Late Validation

    • Glob → RE: checked at load time
    • Plugin existence: checked at load or run?
    • Secret reference ({{ secret.xxx }}): checked at load or run?
  2. Schema Validation Scope

    • Is there a JSON Schema for finalize.yml?
    • Or just Rust struct deserialization with custom validators?

G. Security Considerations

  1. Plugin Trust Model

    • Plugins are arbitrary shell scripts from Git repos
    • How is trust established?
    • Allowlist? Signature verification? User confirmation on first use?
  2. Secret Exposure to Plugins

    • {{ secret.dockerhub.token }} - passed to plugin how?
    • Environment variable? File? CLI arg (visible in ps)?
    • Plugin sandbox/isolation?

Design Maturity (Final)

Component Status Open Items
Plugin execution Trust model, secret passing
Plugin standalone ⚠️ Invocation syntax TBD
Plugin chain Shared input pattern confirmed
Artifact collection Hash ID, glob→RE
Notification priority Empty group handling
Notification fallback System fallback chain defined
Plugin runtime ⚠️ API contract details
Progress reporting ⚠️ Variable system WIP
Duration format ISO 8601, P omitted
Validation ⚠️ Schema definition
Typo retention confirmed

Is there anything else to clarify, or should we proceed to implementation planning?


User

  1. WIP
  2. MAY produce anything, may become another artifact however this is weird, users should do this in bake stage than finalize
  3. plugins could own TOKEN/LOCK and release upon finish eg docker-push acquires "IMAGE." token. image-scanner could start in parallel but soon be paused for no token available.
  4. integers only, 0~INT_MAX, we don't limit if someone writes 2147483647, either a. as long as they are happy or b. rejected by their superior
  5. mail should become 0 however we don't do this we warn about empty group
  6. runtime could be automatically detected
  7. shell: via CLI and VAR rhai: WIP
  8. stage maybe means LAST FINAL state, progress is dynamic.
  9. by design bakerd should fetch plugin as long as they're ready (git/http at prebake stage and file at finalize.begin) assume nothing changed git/http when baking because they require hash.

Assistant (Sisyphus (Ultraworker) · GLM-5 · 14.0s)


User

  1. WIP
  2. MAY produce anything, may become another artifact however this is weird, users should do this in bake stage than finalize
  3. plugins could own TOKEN/LOCK and release upon finish eg docker-push acquires "IMAGE." token. image-scanner could start in parallel but soon be paused for no token available.
  4. integers only, 0~INT_MAX, we don't limit if someone writes 2147483647, either a. as long as they are happy or b. rejected by their superior
  5. mail should become 0 however we don't do this we warn about empty group
  6. runtime could be automatically detected
  7. shell: via CLI and VAR rhai: WIP
  8. stage maybe means LAST FINAL state, progress is dynamic.
  9. by design bakerd should fetch plugin as long as they're ready (git/http at prebake stage and file at finalize.begin) assume nothing changed git/http when baking because they require hash.
  10. WIP
  11. allow ANY plugin cus plugin is in environment(docker/VM usually) bare-metal plugin requires more work and permission (PIP:Pipeline Implicit Parameters)
  12. Via baker. Isolation = how baker (and the whole bake process) is isolated.

Assistant (Sisyphus (Ultraworker) · GLM-5 · 61.4s)

New Discovery: Plugin Token/Lock System

This is a significant design element! Let me clarify:

Plugin Coordination via Tokens

Artifact A ──► docker-push ──► Acquire "IMAGE.docker.io/user/app"
                    │              │
                    │              └── Holds token until done
                    │
                    └──► image-scanner ──► Wait for "IMAGE.docker.io/user/app"
                                              │
                                              └── Resumes when token released

Follow-Up Questions

A. Token/Lock System

  1. Token Naming Convention

    • "IMAGE.<URL>" - is this a convention or built-in?
    • Can plugins define custom token names? e.g., "DEPLOY.production"
    • Is there a reserved namespace? (e.g., "SYSTEM.*")
  2. Token Scope

    artifact:
      - path: "/workspace/app.tar"
        publish:
          docker-push:
            image: "docker.io/user/app:v1"
            # Implicitly acquires "IMAGE.docker.io/user/app:v1"?
    
      - path: "/workspace/app.tar"  # Same artifact, different tag
        publish:
          docker-push:
            image: "docker.io/user/app:latest"
            # Different token? "IMAGE.docker.io/user/app:latest"?
    
    • Is token per image URL? Per artifact? Per plugin instance?
    • How does system know what token to acquire?
  3. Token Declaration

    • Does plugin declare required tokens in its schema?
    • Or baker infers from plugin config (e.g., image field)?
    # In plugin definition?
    plugin:
      docker-push:
        "git::...":
          tokens:
            - "IMAGE.{{ config.image }}"  # Template?
    
  4. Deadlock Prevention

    artifact:
      - publish:
          plugin-a:
            token: "RESOURCE.X"   # Acquires X, then Y
          plugin-b:
            token: "RESOURCE.Y"   # Acquires Y, then X
    
    • Does baker detect potential deadlocks?
    • Or is this user's responsibility?
  5. Token Timeout

    • If docker-push hangs forever, image-scanner waits forever?
    • Is there a token acquisition timeout?
    • Or inherit from plugin's timeout setting?

B. Plugin Fetch Timing

  1. Prebake-Stage Plugin Fetch

    prebake: fetch git/http plugins
    bake: (assume plugins unchanged)
    finalize.begin: fetch file:// plugins
    
    • Where are fetched plugins stored? /workspace/.plugins/?
    • Are they verified against hash before execution?
  2. file:// Plugin Path

    plugin:
      my-plugin:
        "file:///workspace/my-plugin.sh":
    
    • file:// means plugin is already in build environment?
    • Or on baker server (different machine)?
    • How does this work with Docker isolation?

C. Plugin Runtime Detection

  1. Detection Logic
    "file:///plugin.sh"     → shell
    "file:///plugin.rhai"   → rhai
    "git::github.com/user/plugin@main/plugin.sh" → shell
    
    • Detected by file extension in URI?
    • What if no extension? Default to shell?
    • Can user override detection?
    plugin:
      my-plugin:
        "file:///plugin.bin":
          runtime: "shell"    # Force shell
    

D. Shell Plugin Interface

  1. CLI Arguments Structure

    # Example invocation?
    ./plugin.sh \
      --artifact-path "/workspace/.plugins/docker-push/..." \
      --config '{"image": "docker.io/user/app:latest"}' \
      --secret-token "xxx"
    
    • What arguments are passed?
    • Config as JSON string? Environment variables? Both?
  2. Environment Variables

    ARTIFACT_PATH="/workspace/.plugins/..."
    PLUGIN_CONFIG_IMAGE="docker.io/user/app:latest"
    SECRET_DOCKERHUB_TOKEN="xxx"
    
    • What's the naming convention?
    • PLUGIN_CONFIG_* for config? SECRET_* for secrets?

E. Progress Reporting

  1. Progress Variable Semantics

    build.status = "bake.fetch:3%"
    
    • Is this a single string variable that updates?
    • Or structured: build.stage = "bake.fetch", build.progress = 3?
    • Can plugin/notification access {{ build.progress }}?
  2. Real-Time Access

    • Can notification's on_finish_of access live progress?
    • Or only "last final state" at the moment of trigger?

F. Bare-Metal Plugin Security (PIP)

  1. Pipeline Implicit Parameters (PIP)

    • What does this include?
    • Permission flags? Sandboxing options?
    • Example config?
  2. Bare-Metal Plugin Isolation

    environment:
      builder: "baremetal"
    
    finalize:
      plugin:
        docker-push: ...  # Needs special handling?
    
    • How does bare-metal differ from Docker/VM for plugins?
    • What restrictions apply?

Summary: Token System Design (Emerging)

# Hypothetical explicit token declaration
plugin:
  docker-push:
    "git::...":
      tokens:
        acquire: ["IMAGE.{{ image }}"]  # Acquired on start
        release: ["IMAGE.{{ image }}"]  # Released on finish
      # Or implicit from 'image' field?

artifact:
  - path: "/workspace/app.tar"
    publish:
      docker-push:
        image: "docker.io/user/app:latest"
        # System infers: acquire "IMAGE.docker.io/user/app:latest"
      
      image-scanner:
        image: "docker.io/user/app:latest"
        # System infers: wait for "IMAGE.docker.io/user/app:latest"
        # Or explicit:
        wait_tokens: ["IMAGE.docker.io/user/app:latest"]

This is a powerful coordination mechanism! Should tokens be:

  • Implicit: Inferred from plugin config fields
  • Explicit: Declared by plugin author in plugin schema
  • Mixed: Implicit with override capability

What's your preference?


User

Too many questions lol. Maybe answering after actually working on finalize. Lock/Token is managed by system so dead-lock detection should be easy.


Assistant (Sisyphus (Ultraworker) · GLM-5 · 23.6s)

Fair point! Let me wrap up with a concise summary.


finalize.yml.tmpl Design Summary

Confirmed Decisions

Area Decision
Version version: "0.0.1"
Plugin URI git::repo@branch/commit, file://, more protocols later
Plugin runtime Auto-detected by extension (.sh / .rhai)
Plugin config Shallow override from publish:
Plugin communication Shell: CLI args + ENV vars; Rhai: WIP
Plugin isolation Via baker environment (Docker/VM = any plugin, bare-metal = PIP)
Fallible Same semantics as bake.sh @fallible
Notification priority Integers 0~INT_MAX, intra-group parallel, inter-group sequential fallback
Notification fallback System-level: in-site DB → give up → admin
Empty group Warn, don't auto-fix
Artifact ID MD5(PUID + PATH), auto-generated if omitted
Artifact path Glob → RE, 1 match = 1 artifact
Artifact transfer Hard-link/rsync to plugin workspace
Duration format ISO 8601 without P: "7D", "168H", or seconds
Typo fix retensionretention
Token/Lock System-managed, plugin coordination

WIP / Deferred to Implementation

  • Standalone plugin invocation
  • Conditional system (if: on artifacts/notifications)
  • on_finish_of condition syntax
  • Variable system ({{ build.* }}, {{ target.* }})
  • Retry/timeout per notification method
  • Plugin token declaration (implicit vs explicit)
  • Cleanup policies
  • Schema validation

Ready to start implementation when you are. Good session!


Please read the conversation above and summarize.