Skip to content

Latest commit

 

History

History
457 lines (326 loc) · 14.7 KB

File metadata and controls

457 lines (326 loc) · 14.7 KB

ZecKit E2E – GitHub Action

A reusable GitHub Action that spins up a full ZecKit Zcash devnet with pre-built container images and runs the complete golden shielded-transaction flow end-to-end:

Generate UA → Fund → Autoshield → Shielded Send → Rescan/Sync → Verify

Available on the GitHub Marketplace.


Table of Contents

  1. Quick Start
  2. Inputs Reference
  3. Outputs Reference
  4. Usage Patterns
  5. Running Locally
  6. Artifacts
  7. Common Failure Modes & Troubleshooting

Quick Start

Simplest possible call (zaino backend, all defaults)

# .github/workflows/my-zcash-tests.yml
name: My Zcash Project E2E

on: [push, pull_request]

jobs:
  zeckit:
    name: ZecKit E2E
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: zecdev/ZecKit@v1
        with:
          ghcr_token: ${{ secrets.GITHUB_TOKEN }}

Full example with every input shown

jobs:
  zeckit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: ZecKit E2E golden flow
        id: e2e
        uses: zecdev/ZecKit@v1
        with:
          # Backend
          backend: zaino                        # 'zaino' or 'lwd'

          # Timeouts
          startup_timeout_minutes: '10'         # wait for healthy services
          block_wait_seconds: '75'              # wait for block confirmation

          # Transaction parameters
          send_amount: '0.05'                   # ZEC to send
          send_address: ''                      # empty = self-send (safe default)
          send_memo: 'My project E2E test'

          # Image selection
          image_prefix: 'ghcr.io/zecdev/zeckit'
          image_tag: ''                         # empty = auto-detect

          # Artifacts
          upload_artifacts: 'on-failure'        # 'always' | 'on-failure' | 'never'

          # Auth
          ghcr_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Use action outputs
        run: |
          echo "UA            : ${{ steps.e2e.outputs.unified_address }}"
          echo "Shield txid   : ${{ steps.e2e.outputs.shield_txid }}"
          echo "Send txid     : ${{ steps.e2e.outputs.send_txid }}"
          echo "Orchard final : ${{ steps.e2e.outputs.final_orchard_balance }} ZEC"
          echo "Block height  : ${{ steps.e2e.outputs.block_height }}"
          echo "Result        : ${{ steps.e2e.outputs.test_result }}"

Inputs Reference

Input Required Default Description
ghcr_token yes Token to pull pre-built images from GHCR. Pass ${{ secrets.GITHUB_TOKEN }}.
backend no zaino Light-client backend: zaino (Rust, ~30 % faster) or lwd (Lightwalletd, Go).
startup_timeout_minutes no 10 Minutes to wait for all services to become healthy. Zaino typically ready in 2-3 min; lwd in 3-4 min.
block_wait_seconds no 75 Seconds to wait for Zebra to mine a confirming block after broadcasting a transaction. Zebra regtest mines every 30-60 s.
send_amount no 0.05 Amount in ZEC sent during the shielded-send step.
send_address no '' Destination Unified Address for the shielded send. Empty string performs a self-send back to the faucet UA (safe, no external address needed).
send_memo no ZecKit E2E golden flow Memo text included in the shielded send transaction.
image_prefix no ghcr.io/zecdev/zeckit Registry prefix for pre-built images (resolves to …-zebra:TAG, …-zaino:TAG, …-faucet:TAG, etc.).
image_tag no '' Specific image tag to pull. Empty triggers auto-detection: sha-<short> → branch-name → mainlatest.
upload_artifacts no on-failure When to upload log artifacts: always, on-failure, or never.

Backend comparison

zaino lwd
Language Rust Go
Startup time 2-3 min 3-4 min
Sync speed Faster (~30 %) Baseline
Recommendation Development / CI Compatibility testing

Outputs Reference

All outputs are available under steps.<id>.outputs.* after the action completes.

Output Type Description
unified_address string Unified Address (UA) generated by the faucet wallet for this run.
transparent_address string Transparent (t-addr) of the faucet wallet.
shield_txid string Transaction ID of the autoshield (transparent → Orchard). Empty if transparent balance was below fee threshold.
send_txid string Transaction ID of the shielded Orchard → Orchard send.
final_orchard_balance number (str) Orchard balance in ZEC after the complete flow.
block_height number (str) Zcash regtest blockchain height at end of the run.
test_result pass | fail Overall golden-flow result. The action also exits non-zero on fail.

A machine-readable run-summary.json containing all output fields is always written inside the log artifact.


Usage Patterns

A. Composite Action (direct)

Use uses: zecdev/ZecKit@v1 in any step. Returns all outputs on the same job. Suitable when:

  • You need the outputs (addresses, txids) in subsequent steps.
  • You want to mix ZecKit E2E with other steps in the same job.
- uses: zecdev/ZecKit@v1
  id: zcash
  with:
    ghcr_token: ${{ secrets.GITHUB_TOKEN }}

- run: |
    echo "Shielded send confirmed: ${{ steps.zcash.outputs.send_txid }}"

B. Reusable Workflow (workflow_call)

Call .github/workflows/golden-e2e.yml from another workflow's jobs entry. Outputs are available under needs.<job>.outputs.*. Suitable when:

  • You want E2E to run as a dedicated named job with its own status badge.
  • You need to block other jobs on the E2E result without coupling steps.
jobs:
  # Pull in ZecKit E2E as a dedicated job block
  e2e:
    uses: zecdev/ZecKit/.github/workflows/golden-e2e.yml@v1
    with:
      backend: zaino
      startup_timeout_minutes: 10
    secrets:
      ghcr_token: ${{ secrets.GITHUB_TOKEN }}

  # Gate a downstream job on e2e passing
  deploy:
    needs: e2e
    if: needs.e2e.outputs.test_result == 'pass'
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying – E2E passed with txid ${{ needs.e2e.outputs.send_txid }}"

C. Matrix across both backends

jobs:
  e2e:
    strategy:
      matrix:
        backend: [zaino, lwd]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: zecdev/ZecKit@v1
        with:
          backend: ${{ matrix.backend }}
          ghcr_token: ${{ secrets.GITHUB_TOKEN }}

Running Locally

The action itself requires a GitHub Actions runner environment (it writes to $GITHUB_ENV, $GITHUB_OUTPUT, $GITHUB_STEP_SUMMARY). However, you can exercise the exact same flow locally using the zeckit CLI and curl.

Prerequisites

# Docker Engine 24+ and Compose v2
docker --version && docker compose version

# Rust toolchain (for CLI build)
rustup update stable

Step-by-step local golden flow

# 1. Clone and build CLI
git clone https://github.com/zecdev/ZecKit.git && cd ZecKit
cd cli && cargo build --release && cd ..

# 2. Login to GHCR so pre-built images can be pulled
echo "$CR_PAT" | docker login ghcr.io -u YOUR_GITHUB_HANDLE --password-stdin

# 3. Start the devnet (zaino backend, fresh volumes)
./cli/target/release/zeckit up --backend zaino --fresh
#   Wait for output: "Devnet is ready"

# 4. Generate Unified Address
curl http://localhost:8080/address | jq

# 5. Check wallet is funded (mining rewards appear after ~60 s)
curl http://localhost:8080/stats | jq '.transparent_balance, .orchard_balance'

# 6. Autoshield transparent → Orchard
curl -X POST http://localhost:8080/shield | jq
sleep 75   # one block confirmation window

# 7. Sync wallet
curl -X POST http://localhost:8080/sync | jq

# 8. Shielded send (Orchard → Orchard, self-send)
UA=$(curl -s http://localhost:8080/address | jq -r .unified_address)
curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d "{\"address\":\"$UA\",\"amount\":0.05,\"memo\":\"local test\"}" | jq
sleep 75

# 9. Verify final state
curl http://localhost:8080/stats | jq

# 10. Run the full built-in smoke test suite (covers same flow + more)
./cli/target/release/zeckit test

# 11. Tear down
./cli/target/release/zeckit down

Using act (GitHub Actions locally)

act can run the CI self-test workflow on your machine:

brew install act

# Provide a GITHUB_TOKEN with read:packages scope
act -j composite-action-test \
    -s GITHUB_TOKEN="$(gh auth token)" \
    --workflows .github/workflows/ci-action-test.yml

Note: act uses catthehacker/ubuntu:act-latest by default which may not have all Docker-in-Docker capabilities. Use a full Docker socket mount if you encounter docker: not found inside the runner:

act ... --bind

Artifacts

When upload_artifacts is always or on-failure (default), the action uploads a ZIP named zeckit-e2e-logs-<run_number> as a workflow artifact.

Artifact retention: 14 days (configurable in action.yml).

Artifact contents

File Description
run-summary.json Machine-readable JSON: backend, UA, txids, final balance, block height, test_result.
faucet-stats.json Raw /stats response at end of run.
zebra.log Full stdout/stderr from the Zebra container.
zaino.log Zaino indexer container logs (when backend=zaino).
lightwalletd.log Lightwalletd container logs (when backend=lwd).
faucet.log Faucet (Axum + Zingolib) container logs.
containers.log docker ps -a snapshot.
networks.log docker network ls snapshot.

Downloading artifacts via CLI

# List artifacts for a run
gh run view <run-id> --repo zecdev/ZecKit

# Download
gh run download <run-id> --repo zecdev/ZecKit -n zeckit-e2e-logs-<run-number>

Common Failure Modes & Troubleshooting

1. Services not healthy within timeout

Symptom: ::error::Zebra did not become available within 10 minutes or similar for the faucet.

Cause: Pulling images took too long, or a container OOM-killed on runner.

Fix:

  • Increase startup_timeout_minutes to 15 or 20.
  • Ensure the ghcr_token has read:packages scope — without it, pulls silently fail and compose falls back to a slow local build.
  • Check that the runner has ≥ 4 GB RAM and ≥ 5 GB free disk.

2. Insufficient Orchard balance for send

Symptom: ::error::Insufficient Orchard balance (0 ZEC) to send 0.05 ZEC

Cause: The shield step was skipped (no transparent balance above fee threshold) but no pre-existing Orchard balance exists. Can happen on a very fresh chain where mining hasn't produced enough rewards yet.

Fix: Increase block_wait_seconds to allow more mining time, or increase startup_timeout_minutes so the fund step waits longer for rewards. For a self-hosted runner with slow networks, also try setting image_tag: main to skip auto-detection overhead.


3. Shield fails with "no_funds"

Symptom: Shield step reports status: no_funds but subsequent send also fails.

Cause: Mining rewards are still pending (mempool not yet mined). Timing is probabilistic: Zebra mines every 30-60 s.

Fix: Increase block_wait_seconds to 120. This is safe — extra wait does not cause failures.


4. Shielded send returns non-"sent" status

Symptom: ::error::Shielded send failed with status 'error'

Cause: Wallet's internal spendable notes haven't caught up after shielding. The wallet needs a sync after the shield block is confirmed.

Fix: The action already performs a sync between shield and send. If this still fails, increase block_wait_seconds to give more time before the sync.


5. docker manifest inspect fails / wrong tag selected

Symptom: Action logs show No pre-built image found; docker compose will build locally (slow). and the job takes 20+ minutes.

Cause: The auto-detected tag doesn't match any published image. This happens on feature branches or forks where build-images.yml hasn't run yet.

Fix: Set image_tag: main explicitly to pull the latest stable images regardless of branch.

with:
  image_tag: 'main'
  ghcr_token: ${{ secrets.GITHUB_TOKEN }}

6. lwd backend takes too long

Symptom: Lightwalletd startup exceeds the default 10-minute timeout.

Cause: Lightwalletd syncs slower than Zaino, especially on cold starts.

Fix: Use startup_timeout_minutes: '15' and block_wait_seconds: '90' when running with backend: lwd.

with:
  backend: lwd
  startup_timeout_minutes: '15'
  block_wait_seconds: '90'
  ghcr_token: ${{ secrets.GITHUB_TOKEN }}

7. Port conflicts on self-hosted runners

Symptom: bind: address already in use for ports 8080, 8232, or 9067.

Cause: A previous run left containers running on the same runner.

Fix: Add a cleanup step before the action in your workflow:

- name: Pre-clean ZecKit
  run: |
    docker compose -f /path/to/ZecKit/docker-compose.yml down --remove-orphans 2>/dev/null || true
    docker stop zeckit-zebra zeckit-faucet 2>/dev/null || true

Or use docker run --network host alternatives. The action itself calls docker compose down at the end (if: always()), so subsequent runs on the same runner should not encounter this after the first cleanup.


8. jq or bc not found

Symptom: /bin/bash: jq: command not found

Cause: Minimal self-hosted runner image without standard utilities.

Fix: The action auto-installs jq and bc via apt-get if they are missing. If your runner doesn't have apt-get, pre-install them in your runner image.


Enabling debug logs

Set the secret ACTIONS_STEP_DEBUG to true in your repo's Actions secrets to get verbose shell (set -x) output from every step.


GitHub Marketplace

This action is published at: https://github.com/marketplace/actions/zeckit-e2e

Required files for Marketplace listing:

  • action.yml (root of repo) — present ✓
  • branding.icon / branding.color — set to shield / blue
  • Public repository — required for Marketplace visibility ✓
  • This documentation linked from the repo README ✓

To publish a new version tag and update the Marketplace listing:

git tag v1.x.x
git push origin v1.x.x

# Then move the major-version floating tag:
git tag -fa v1 -m "Update v1 to v1.x.x"
git push origin v1 --force