diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml new file mode 100644 index 0000000..0fef342 --- /dev/null +++ b/.woodpecker/ci.yml @@ -0,0 +1,53 @@ +kind: pipeline +type: docker +name: ci + +trigger: + event: + - push + - pull_request + +steps: + - name: node-quality + image: node:20-bullseye + environment: + CI: "true" + commands: + - set -eo pipefail + - npm config set fund false + - npm config set audit false + - npm ci --prefix kraiken-lib --no-audit --no-fund + - ./scripts/build-kraiken-lib.sh + - npm ci --prefix landing --no-audit --no-fund + - npm ci --prefix web-app --no-audit --no-fund + - npm ci --prefix services/ponder --no-audit --no-fund + - npm ci --prefix services/txnBot --no-audit --no-fund + - npm run lint --prefix kraiken-lib + - npm test --prefix kraiken-lib -- --runInBand + - npm run lint --prefix landing + - npm run build --prefix landing + - npm run lint --prefix web-app + - npm run test --prefix web-app -- --run + - npm run build --prefix web-app + - npm run lint --prefix services/ponder + - npm run build --prefix services/ponder + - npm run lint --prefix services/txnBot + - npm run test --prefix services/txnBot + - npm run build --prefix services/txnBot + + - name: foundry-suite + image: ubuntu:22.04 + environment: + FOUNDRY_DIR: /root/.foundry + PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + commands: + - set -eo pipefail + - apt-get update + - apt-get install -y curl git build-essential pkg-config libssl-dev + - curl -L https://foundry.paradigm.xyz | bash + - foundryup + - cd onchain + - forge --version + - forge build --sizes + - forge test -vvv + - forge snapshot diff --git a/.woodpecker/contracts.yml b/.woodpecker/contracts.yml new file mode 100644 index 0000000..30ed14c --- /dev/null +++ b/.woodpecker/contracts.yml @@ -0,0 +1,58 @@ +kind: pipeline +type: docker +name: contracts-local-fork + +trigger: + event: + - push + - pull_request + +steps: + - name: forge-suite + image: ubuntu:22.04 + environment: + FOUNDRY_DIR: /root/.foundry + PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + HARB_ENV: BASE_SEPOLIA_LOCAL_FORK + commands: + - set -eo pipefail + - apt-get update + - apt-get install -y curl git build-essential pkg-config libssl-dev + - curl -L https://foundry.paradigm.xyz | bash + - foundryup + - cd onchain + - forge build + - forge test -vv --ffi + - forge snapshot + +--- + +kind: pipeline +type: docker +name: contracts-base-sepolia + +trigger: + event: + - push + - pull_request + +steps: + - name: forge-suite + image: ubuntu:22.04 + environment: + FOUNDRY_DIR: /root/.foundry + PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + HARB_ENV: BASE_SEPOLIA + BASE_SEPOLIA_RPC: + from_secret: base_sepolia_rpc + commands: + - set -eo pipefail + - apt-get update + - apt-get install -y curl git build-essential pkg-config libssl-dev + - curl -L https://foundry.paradigm.xyz | bash + - foundryup + - cd onchain + - export BASE_SEPOLIA_RPC="$BASE_SEPOLIA_RPC" + - forge build + - forge test -vv --ffi + - forge snapshot diff --git a/.woodpecker/e2e.yml b/.woodpecker/e2e.yml new file mode 100644 index 0000000..8100082 --- /dev/null +++ b/.woodpecker/e2e.yml @@ -0,0 +1,49 @@ +kind: pipeline +type: exec +name: e2e + +node: + podman: "true" + +trigger: + event: + - push + - pull_request + +steps: + - name: run-e2e + environment: + HARB_ENV: BASE_SEPOLIA_LOCAL_FORK + SKIP_WATCH: "1" + commands: + - bash -lc ' + set -euo pipefail + npm config set fund false + npm config set audit false + npm ci --prefix kraiken-lib --no-audit --no-fund + ./scripts/build-kraiken-lib.sh + npm ci --prefix landing --no-audit --no-fund + npm ci --prefix web-app --no-audit --no-fund + npm ci --prefix services/ponder --no-audit --no-fund + npm ci --prefix services/txnBot --no-audit --no-fund + npm ci --no-audit --no-fund + npx playwright install chromium + trap "./scripts/dev.sh stop || true" EXIT + ./scripts/dev.sh start + timeout 180 bash -lc "until curl -sf http://localhost:8081/api/graphql > /dev/null; do sleep 3; done" + npm run test:e2e + ' + + - name: collect-artifacts + when: + status: + - success + - failure + commands: + - bash -lc ' + set -euo pipefail + mkdir -p artifacts + if [ -d playwright-report ]; then tar -czf artifacts/playwright-report.tgz playwright-report; fi + if [ -d test-results ]; then tar -czf artifacts/test-results.tgz test-results; fi + if [ -d logs ]; then tar -czf artifacts/stack-logs.tgz logs; fi + ' diff --git a/.woodpecker/fuzz-nightly.yml b/.woodpecker/fuzz-nightly.yml new file mode 100644 index 0000000..578717a --- /dev/null +++ b/.woodpecker/fuzz-nightly.yml @@ -0,0 +1,35 @@ +kind: pipeline +type: docker +name: fuzz-nightly + +trigger: + event: + - cron + +steps: + - name: fuzz + image: ubuntu:22.04 + environment: + FOUNDRY_DIR: /root/.foundry + PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + commands: + - set -eo pipefail + - apt-get update + - apt-get install -y curl git build-essential bc + - curl -L https://foundry.paradigm.xyz | bash + - foundryup + - cd onchain + - forge --version + - ./analysis/run-fuzzing.sh BullMarketOptimizer runs=75 + + - name: package-results + image: alpine:3.20 + when: + status: + - success + - failure + commands: + - set -eo pipefail + - apk add --no-cache tar + - mkdir -p artifacts + - if [ -d onchain/analysis ]; then tar -czf artifacts/fuzz-results.tgz onchain/analysis; fi diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml new file mode 100644 index 0000000..a6b0783 --- /dev/null +++ b/.woodpecker/release.yml @@ -0,0 +1,141 @@ +kind: pipeline +type: exec +name: release + +node: + podman: "true" + +trigger: + event: + - tag + +steps: + - name: version-check + commands: + - bash -lc ' + set -euo pipefail + npm config set fund false + npm config set audit false + npm ci --prefix kraiken-lib --no-audit --no-fund + ./scripts/build-kraiken-lib.sh + node <<\"NODE\" + import fs from \"fs\"; + + const sol = fs.readFileSync(\"onchain/src/Kraiken.sol\", \"utf8\"); + const lib = fs.readFileSync(\"kraiken-lib/src/version.ts\", \"utf8\"); + + const contractVersionMatch = sol.match(/VERSION\\s*=\\s*(\\d+)/); + if (!contractVersionMatch) { + console.error(\"Unable to find VERSION constant in Kraiken.sol\"); + process.exit(1); + } + const contractVersion = Number(contractVersionMatch[1]); + + const libVersionMatch = lib.match(/KRAIKEN_LIB_VERSION\\s*=\\s*(\\d+)/); + if (!libVersionMatch) { + console.error(\"Unable to find KRAIKEN_LIB_VERSION in kraiken-lib/src/version.ts\"); + process.exit(1); + } + const libVersion = Number(libVersionMatch[1]); + + const compatMatch = lib.match(/COMPATIBLE_CONTRACT_VERSIONS\\s*=\\s*\\[([^\\]]*)\\]/); + if (!compatMatch) { + console.error(\"Unable to find COMPATIBLE_CONTRACT_VERSIONS in kraiken-lib/src/version.ts\"); + process.exit(1); + } + const compatibleVersions = compatMatch[1] + .split(\",\") + .map(v => v.trim()) + .filter(Boolean) + .map(Number); + + if (contractVersion !== libVersion) { + console.error(`Contract VERSION (${contractVersion}) and KRAIKEN_LIB_VERSION (${libVersion}) differ`); + process.exit(1); + } + if (!compatibleVersions.includes(contractVersion)) { + console.error(`Contract VERSION ${contractVersion} missing from COMPATIBLE_CONTRACT_VERSIONS [${compatibleVersions.join(\", \")}]`); + process.exit(1); + } + + console.log(`Version check passed for VERSION ${contractVersion}`); + NODE + ' + + - name: build-artifacts + commands: + - bash -lc ' + set -euo pipefail + npm config set fund false + npm config set audit false + npm ci --prefix kraiken-lib --no-audit --no-fund + ./scripts/build-kraiken-lib.sh + npm ci --prefix landing --no-audit --no-fund + npm ci --prefix web-app --no-audit --no-fund + npm ci --prefix services/ponder --no-audit --no-fund + npm ci --prefix services/txnBot --no-audit --no-fund + npm ci --no-audit --no-fund + export PATH=\"${HOME}/.foundry/bin:${PATH}\" + if ! command -v forge >/dev/null 2>&1; then + curl -L https://foundry.paradigm.xyz | bash + foundryup + else + foundryup + fi + forge --version + (cd onchain && forge build) + npm run build --prefix landing + npm run build --prefix web-app + npm run build --prefix services/ponder + npm run build --prefix services/txnBot + rm -rf release + mkdir -p release/dist + cp -r onchain/out release/dist/abi + cp -r kraiken-lib/dist release/dist/kraiken-lib + cp -r landing/dist release/dist/landing + cp -r web-app/dist release/dist/web-app + cp -r services/txnBot/dist release/dist/txn-bot + if [ -d services/ponder/generated ]; then + cp -r services/ponder/generated release/dist/ponder-generated + fi + tar -czf release-bundle.tgz -C release dist + ' + + - name: podman-publish + environment: + REGISTRY_SERVER: + from_secret: registry_server + REGISTRY_NAMESPACE: + from_secret: registry_namespace + REGISTRY_USERNAME: + from_secret: registry_username + REGISTRY_PASSWORD: + from_secret: registry_password + commands: + - bash -lc ' + set -euo pipefail + if [ -z \"${CI_COMMIT_TAG:-}\" ]; then + echo \"CI_COMMIT_TAG not set\" >&2 + exit 1 + fi + if [ -z \"${REGISTRY_SERVER:-}\" ] || [ -z \"${REGISTRY_NAMESPACE:-}\" ]; then + echo \"Registry server or namespace missing\" >&2 + exit 1 + fi + export TAG=\"${CI_COMMIT_TAG#refs/tags/}\" + export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-harb} + podman login \"$REGISTRY_SERVER\" -u \"$REGISTRY_USERNAME\" -p \"$REGISTRY_PASSWORD\" + podman-compose build ponder webapp landing txn-bot + for service in ponder webapp landing txn-bot; do + image=$(podman image ls --filter \"label=com.docker.compose.project=${COMPOSE_PROJECT_NAME}\" --filter \"label=com.docker.compose.service=${service}\" --format \"{{.Repository}}:{{ .Tag }}\" | head -n1) + if [ -z \"$image\" ]; then + echo \"Unable to find built image for $service\" >&2 + exit 1 + fi + target=\"${REGISTRY_SERVER}/${REGISTRY_NAMESPACE}/${service}\" + podman tag \"$image\" \"${target}:${TAG}\" + podman push \"${target}:${TAG}\" + podman tag \"${target}:${TAG}\" \"${target}:latest\" + podman push \"${target}:latest\" + done + '