Commit 50d7d59
Eric Bower
·
2026-08-15 21:00:22 -0400 EDT
parent 50e9dba
chore: rm proposal
1 files changed,
+0,
-196
+0,
-196
| ... | ... | @@ -1,196 +0,0 @@ | |
| 1 | - | # Proposal: Integrating Pici with GitHub Actions & `pipe.pico.sh` Remote Debugging | |
| 2 | - | ||
| 3 | - | ## 1. Executive Summary | |
| 4 | - | ||
| 5 | - | Migrating existing projects away from GitHub Actions to a dedicated self-hosted CI system is difficult due to ecosystem inertia and configuration friction. | |
| 6 | - | ||
| 7 | - | This proposal outlines a strategy to provide an **official custom GitHub Action (`pico-sh/pici-action`)** powered by `pici run`. Developers write standard, parallel bash workflows (`pico.sh`) using `zmx`, execute them with 1:1 parity on both local workstations and GitHub Actions runners, and gain **live, interactive terminal debugging on failure** powered by native `pipe.pico.sh` bridging in Go. | |
| 8 | - | ||
| 9 | - | --- | |
| 10 | - | ||
| 11 | - | ## 2. Core Design Goals | |
| 12 | - | ||
| 13 | - | 1. **1-Step GitHub Action Experience**: A custom composite action (`pico-sh/pici-action@v1`) that installs `pici` + `zmx`, configures SSH keys, executes the workflow in-place, and uploads artifacts with zero boilerplate. | |
| 14 | - | 2. **1:1 Local & Remote Parity**: `pici run` provides identical execution semantics across environments: metadata environment variable injection (`PICI_JOB`, `PICI_REPO`, `PICI_BRANCH`, `PICI_COMMIT`), `pico.sh` execution via `zmx`, live progress streaming, artifact rendering, and exit code propagation. | |
| 15 | - | 3. **Explicit Workspace Semantics**: By default, `pici run` rsyncs to an isolated `/tmp` workspace to keep local working trees clean. In CI environments where the runner is already an ephemeral container/VM, an explicit `--in-place` flag instructs `pici run` to execute directly without rsyncing. | |
| 16 | - | 4. **Native Docker Compatibility**: Mounts like `$(pwd):/app` work seamlessly in both local and CI environments. | |
| 17 | - | 5. **Native Go Failure Hook (`pici debug`)**: When a job fails, `pici` directly opens a secure, authenticated PTY bridge to `pipe.pico.sh` in Go, avoiding brittle bash FIFOs or background SSH wrappers. | |
| 18 | - | 6. **Security & Secret Scrubbing**: `pici` controls the debug shell environment and scrubs `GITHUB_TOKEN` and repository secrets before spawning interactive shells. | |
| 19 | - | ||
| 20 | - | --- | |
| 21 | - | ||
| 22 | - | ## 3. The Custom GitHub Action: `pico-sh/pici-action` | |
| 23 | - | ||
| 24 | - | We are creating and publishing an official custom action—`pico-sh/pici-action`—that serves as the turnkey entrypoint for GitHub Actions users. | |
| 25 | - | ||
| 26 | - | ### Standard Workflow (`.github/workflows/ci.yml`) | |
| 27 | - | ||
| 28 | - | With the custom action, adding Pici CI with remote debugging to any repo is a single step: | |
| 29 | - | ||
| 30 | - | ```yaml | |
| 31 | - | name: CI | |
| 32 | - | on: [push, pull_request] | |
| 33 | - | ||
| 34 | - | jobs: | |
| 35 | - | ci: | |
| 36 | - | runs-on: ubuntu-latest | |
| 37 | - | steps: | |
| 38 | - | - uses: actions/checkout@v4 | |
| 39 | - | ||
| 40 | - | - name: Run CI with Pici | |
| 41 | - | uses: picosh/pici-action@v1 | |
| 42 | - | with: | |
| 43 | - | pico_ssh_key: ${{ secrets.PICO_SSH_KEY }} | |
| 44 | - | ``` | |
| 45 | - | ||
| 46 | - | ### Action Configuration & Inputs | |
| 47 | - | ||
| 48 | - | | Input | Description | Default | | |
| 49 | - | | :--- | :--- | :--- | | |
| 50 | - | | `pico_ssh_key` | SSH private key registered with pico.sh for authenticated debug relay access. | `""` (optional, required for debug relay) | | |
| 51 | - | | `debug_on_fail` | Automatically open an interactive `pipe.pico.sh` debug bridge if a job fails. If omitted or `auto`, automatically enables when re-running with GitHub debug logging (`RUNNER_DEBUG=1`). | `"auto"` | | |
| 52 | - | ||
| 53 | - | ### What `pico-sh/pici-action` Does Under the Hood | |
| 54 | - | 1. **Tool Installation**: Installs the static `pici` and pinned `zmx` binaries (with SHA256 checksum verification) to `/usr/local/bin`. | |
| 55 | - | 2. **Credential Setup**: Writes `pico_ssh_key` to a secure temp keyfile or SSH agent if provided. | |
| 56 | - | 3. **Environment Propagation**: Passes GitHub Actions run metadata to `pici run`: | |
| 57 | - | - `PICI_JOB="${GITHUB_RUN_ID}"` | |
| 58 | - | - `PICI_EVENT="git.${GITHUB_EVENT_NAME}"` | |
| 59 | - | - `PICI_BRANCH="${GITHUB_REF_NAME}"` | |
| 60 | - | - `PICI_COMMIT="${GITHUB_SHA}"` | |
| 61 | - | 4. **Execution (`pici run`)**: Invokes `pici run --in-place` (enabling `--debug-on-fail` if `debug_on_fail: true` or if GitHub's native debug re-run is active via `RUNNER_DEBUG=1`). | |
| 62 | - | 5. **Job Summaries & Artifact Upload**: | |
| 63 | - | - `pici` formats the run status table and failed logs into `$GITHUB_STEP_SUMMARY`. | |
| 64 | - | - If `upload_artifacts` is enabled, the action uploads `/tmp/pici-artifacts/<repo>/<jobid>/` as a workflow artifact. | |
| 65 | - | ||
| 66 | - | --- | |
| 67 | - | ||
| 68 | - | ## 4. Direct CLI Execution (Without the Action) | |
| 69 | - | ||
| 70 | - | For users who prefer manual step-by-step control or non-GitHub CI environments, `pici run` can be invoked directly: | |
| 71 | - | ||
| 72 | - | ```yaml | |
| 73 | - | - name: Run CI | |
| 74 | - | run: | | |
| 75 | - | pici run --in-place \ | |
| 76 | - | -e PICI_JOB="$GITHUB_RUN_ID" \ | |
| 77 | - | -e PICI_EVENT=git.push \ | |
| 78 | - | -e PICI_BRANCH="$GITHUB_REF_NAME" \ | |
| 79 | - | -e PICI_COMMIT="$GITHUB_SHA" | |
| 80 | - | ``` | |
| 81 | - | ||
| 82 | - | --- | |
| 83 | - | ||
| 84 | - | ## 5. Native Go Debug Bridge (`pipe.pico.sh`) | |
| 85 | - | ||
| 86 | - | Rather than relying on bash scripts with named FIFOs (`mkfifo`) and background `ssh -N` commands, the relay bridge is implemented directly in Go inside `pici`: | |
| 87 | - | ||
| 88 | - | ### How it works | |
| 89 | - | 1. **Hook into Failure Path**: When `resolveJobExitCode()` detects a non-zero exit in `waitAndReport()` and debug mode is enabled (via `--debug-on-fail`, `-e PICI_DEBUG=1`, or GitHub Actions' standard `RUNNER_DEBUG=1`), `pici` initializes the debug session. | |
| 90 | - | 2. **Native SSH Dialing**: `pici` dials `pipe.pico.sh` using the existing `-pk` / `-ck` SSH key infrastructure. | |
| 91 | - | 3. **PTY Allocation (`creack/pty`)**: Spawns an interactive shell (`bash -i` or `$SHELL`) connected to the bidirectional SSH stream. | |
| 92 | - | 4. **Attach Instructions & Notifications**: Prints the `ssh -t pipe.pico.sh pipe <topic>` command to stdout and appends it to `$GITHUB_STEP_SUMMARY`. | |
| 93 | - | 5. **Idle Timeout & Clean Exit**: Waits for a connection with an idle timeout (e.g., 15 minutes). Once closed or timed out, `pici` terminates and exits with the original non-zero job exit code. | |
| 94 | - | 6. **Standalone `pici debug`**: `pici debug <repo>/<jobid>` attaches to running or stalled jobs by targeting the deterministic session prefix (`ci.<repo>.<jobid>.`). | |
| 95 | - | ||
| 96 | - | ### Security Advantages | |
| 97 | - | - **Environment Scrubbing**: Sensitive CI environment variables (`GITHUB_TOKEN`, `ACTIONS_RUNTIME_TOKEN`, injected secrets) are stripped from the debug shell process environment before launching. | |
| 98 | - | - **Host Key Pinning**: The `pipe.pico.sh` host key is verified natively in Go without passing `-o StrictHostKeyChecking=no`. | |
| 99 | - | ||
| 100 | - | --- | |
| 101 | - | ||
| 102 | - | ## 6. Implementation & Verification Plan (Test Repository) | |
| 103 | - | ||
| 104 | - | To validate the complete flow end-to-end before public release, we will use a dedicated test repository (e.g., `picosh/pici-test`) to confirm local parity, GitHub Actions execution, and interactive failure recovery. | |
| 105 | - | ||
| 106 | - | ### Step 1: Create the Test Repository Structure | |
| 107 | - | ||
| 108 | - | ``` | |
| 109 | - | pici-test/ | |
| 110 | - | ├── .github/ | |
| 111 | - | │ └── workflows/ | |
| 112 | - | │ ├── ci-action.yml # Tests the custom picosh/pici-action | |
| 113 | - | │ └── ci-direct.yml # Tests direct pici binary execution | |
| 114 | - | ├── src/ | |
| 115 | - | │ └── main_test.go | |
| 116 | - | ├── pico.sh # Core CI script with configurable test modes | |
| 117 | - | └── README.md | |
| 118 | - | ``` | |
| 119 | - | ||
| 120 | - | ### Step 2: Implement the Configurable `pico.sh` | |
| 121 | - | ||
| 122 | - | The test script supports environment variables to deterministically simulate success, failure, Docker steps, and hangs: | |
| 123 | - | ||
| 124 | - | ```bash | |
| 125 | - | #!/usr/bin/env bash | |
| 126 | - | set -euo pipefail | |
| 127 | - | ||
| 128 | - | JOB_ID="${PICI_JOB:-local}" | |
| 129 | - | REPO="${PICI_REPO:-pici-test}" | |
| 130 | - | EVENT="${PICI_EVENT:-local}" | |
| 131 | - | ZMX_SESSION_PREFIX="${ZMX_SESSION_PREFIX:-local.}" | |
| 132 | - | ||
| 133 | - | echo "==> Running CI for $REPO (job: $JOB_ID, event: $EVENT)" | |
| 134 | - | ||
| 135 | - | # 1. Parallel test steps | |
| 136 | - | zmx run unit-tests -d bash -c 'echo "Running unit tests..."; sleep 2; echo "Tests passed!"' | |
| 137 | - | zmx run lint -d bash -c 'echo "Running linter..."; sleep 1; echo "Lint clean!"' | |
| 138 | - | ||
| 139 | - | # 2. Simulated failure scenario (when TEST_FAILURE=1) | |
| 140 | - | if [ "${TEST_FAILURE:-0}" = "1" ]; then | |
| 141 | - | zmx run failing-step -d bash -c 'echo "Simulating fatal compilation error..."; sleep 2; exit 42' | |
| 142 | - | fi | |
| 143 | - | ||
| 144 | - | # 3. Simulated hang scenario (when TEST_HANG=1) | |
| 145 | - | if [ "${TEST_HANG:-0}" = "1" ]; then | |
| 146 | - | zmx run hung-test -d bash -c 'echo "Simulating hung test process..."; sleep 3600' | |
| 147 | - | fi | |
| 148 | - | ||
| 149 | - | # 4. Docker container step | |
| 150 | - | zmx run docker-step -d docker run --rm alpine:latest sh -c "echo 'Docker step executed inside alpine container'" | |
| 151 | - | ||
| 152 | - | # 5. Wait for all steps | |
| 153 | - | zmx wait "*" | |
| 154 | - | ||
| 155 | - | printf "\x1b[32mAll test steps succeeded!\x1b[0m\n" | |
| 156 | - | ``` | |
| 157 | - | ||
| 158 | - | ### Step 3: Configure GitHub Actions Workflows | |
| 159 | - | ||
| 160 | - | #### Test Workflow 1: Using `picosh/pici-action` (`.github/workflows/ci-action.yml`) | |
| 161 | - | ||
| 162 | - | ```yaml | |
| 163 | - | name: Pici Action Validation | |
| 164 | - | on: | |
| 165 | - | push: | |
| 166 | - | workflow_dispatch: | |
| 167 | - | inputs: | |
| 168 | - | test_failure: | |
| 169 | - | description: "Simulate step failure (exit 42)" | |
| 170 | - | type: boolean | |
| 171 | - | default: false | |
| 172 | - | ||
| 173 | - | jobs: | |
| 174 | - | test-ci: | |
| 175 | - | runs-on: ubuntu-latest | |
| 176 | - | steps: | |
| 177 | - | - uses: actions/checkout@v4 | |
| 178 | - | ||
| 179 | - | - name: Run Pici CI | |
| 180 | - | uses: picosh/pici-action@main | |
| 181 | - | with: | |
| 182 | - | pico_ssh_key: ${{ secrets.PICO_SSH_KEY }} | |
| 183 | - | debug_on_fail: "true" | |
| 184 | - | env: | |
| 185 | - | TEST_FAILURE: ${{ github.event.inputs.test_failure && '1' || '0' }} | |
| 186 | - | ``` | |
| 187 | - | ||
| 188 | - | ### Step 4: Verification Matrix & Acceptance Criteria | |
| 189 | - | ||
| 190 | - | | Test Scenario | Trigger | Expected GHA Behavior | Acceptance Criteria | | |
| 191 | - | | :--- | :--- | :--- | :--- | | |
| 192 | - | | **1. Happy Path** | Push commit with `TEST_FAILURE=0` | All `zmx` sessions pass. | • Workflow exits with `0` (green).<br>• GHA Step Summary displays full table with durations.<br>• HTML report uploaded as workflow artifact. | | |
| 193 | - | | **2. Failure & Remote Attach** | Dispatch workflow with `test_failure=true` | `failing-step` exits `42`. | • `waitAndReport` catches exit `42`.<br>• Relay bridge opens on `pipe.pico.sh`.<br>• `ssh -t pipe.pico.sh pipe <topic>` printed to logs and step summary.<br>• Connecting via terminal drops into interactive debug shell with active `zmx` sessions.<br>• Exiting debug shell ends GHA job with code `42` (red). | | |
| 194 | - | | **3. Secret Scrubbing Audit** | During interactive debug session | Inspect environment variables inside attached shell. | • `GITHUB_TOKEN`, `ACTIONS_RUNTIME_TOKEN`, and `PICO_SSH_KEY` are absent from `env`. | | |
| 195 | - | | **4. Docker Step Execution** | Standard run | Alpine container runs via `docker run --rm`. | • Docker mounts `$(pwd)` correctly in-place.<br>• Container logs captured in `zmx history docker-step`. | | |
| 196 | - | | **5. Idle Timeout Cleanup** | Failure triggered without developer attaching | Wait for configured idle timeout (e.g. 15m). | • Bridge cleanly closes upon timeout.<br>• GHA runner exits with the original step failure code (does not hang indefinitely). | |