diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml index bb78275..cb717a7 100644 --- a/.woodpecker/ci.yml +++ b/.woodpecker/ci.yml @@ -9,21 +9,17 @@ trigger: steps: - name: bootstrap-deps - image: node:20-bullseye + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' set -euo pipefail git submodule update --init --recursive - corepack enable yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile ' - name: foundry-suite - image: ghcr.io/foundry-rs/foundry:stable - environment: - FOUNDRY_DIR: /root/.foundry - PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' @@ -36,7 +32,7 @@ steps: ' - name: node-quality - image: node:20-bullseye + image: registry.sovraigns.network/harb/node-ci:latest environment: CI: "true" commands: diff --git a/.woodpecker/contracts.yml b/.woodpecker/contracts.yml index 9301c6e..8d2b61a 100644 --- a/.woodpecker/contracts.yml +++ b/.woodpecker/contracts.yml @@ -9,21 +9,18 @@ trigger: steps: - name: bootstrap-deps - image: node:20-bullseye + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' set -euo pipefail git submodule update --init --recursive - corepack enable yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile ' - name: forge-suite - image: ghcr.io/foundry-rs/foundry:stable + image: registry.sovraigns.network/harb/node-ci:latest 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: - | @@ -48,21 +45,18 @@ trigger: steps: - name: bootstrap-deps - image: node:20-bullseye + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' set -euo pipefail git submodule update --init --recursive - corepack enable yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile ' - name: forge-suite - image: ghcr.io/foundry-rs/foundry:stable + image: registry.sovraigns.network/harb/node-ci:latest 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 diff --git a/.woodpecker/e2e.yml b/.woodpecker/e2e.yml index c9fb10d..95d1161 100644 --- a/.woodpecker/e2e.yml +++ b/.woodpecker/e2e.yml @@ -12,8 +12,7 @@ trigger: steps: - name: run-e2e - image: mcr.microsoft.com/playwright:v1.56.0-jammy - pull: true + image: registry.sovraigns.network/harb/playwright-ci:latest privileged: true environment: PNPM_HOME: /root/.local/share/pnpm @@ -24,13 +23,8 @@ steps: commands: - | set -euo pipefail - export DEBIAN_FRONTEND=noninteractive mkdir -p "$XDG_RUNTIME_DIR" - apt-get update - apt-get install -y podman python3-pip curl jq ca-certificates - python3 -m pip install --no-cache-dir podman-compose git submodule update --init --recursive - corepack enable yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile npm config set fund false npm config set audit false diff --git a/.woodpecker/fuzz-nightly.yml b/.woodpecker/fuzz-nightly.yml index 1fe0bcc..6a0fc1c 100644 --- a/.woodpecker/fuzz-nightly.yml +++ b/.woodpecker/fuzz-nightly.yml @@ -8,21 +8,17 @@ trigger: steps: - name: bootstrap-deps - image: node:20-bullseye + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' set -euo pipefail git submodule update --init --recursive - corepack enable yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile ' - name: fuzz - image: ghcr.io/foundry-rs/foundry:stable - environment: - FOUNDRY_DIR: /root/.foundry - PATH: /root/.foundry/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + image: registry.sovraigns.network/harb/node-ci:latest commands: - | bash -lc ' diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml index 1379f32..8577322 100644 --- a/.woodpecker/release.yml +++ b/.woodpecker/release.yml @@ -6,123 +6,114 @@ labels: podman: "true" when: - - event: tag + event: tag steps: - name: version-check - image: node:20-bullseye - pull: true + image: registry.sovraigns.network/harb/node-ci:latest when: - - event: tag + event: tag commands: - - bash -lc ' - set -euo pipefail - git submodule update --init --recursive - corepack enable - yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile - 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\"; + - | + bash -lc ' + set -euo pipefail + git submodule update --init --recursive + corepack enable + yarn install --cwd onchain/lib/uni-v3-lib --frozen-lockfile + 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 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 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 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); + 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); - } + 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 - ' + console.log(\"Version check passed for VERSION \" + contractVersion); + NODE + ' - name: build-artifacts - image: node:20-bullseye - pull: true + image: registry.sovraigns.network/harb/node-ci:latest depends_on: - version-check when: - - event: tag + event: tag commands: - - bash -lc ' - set -euo pipefail - apt-get update - apt-get install -y curl build-essential pkg-config libssl-dev - 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 - ' + - | + 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 + 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 - image: mcr.microsoft.com/playwright:v1.56.0-jammy + image: registry.sovraigns.network/harb/playwright-ci:latest pull: true privileged: true depends_on: - build-artifacts when: - - event: tag + event: tag environment: REGISTRY_SERVER: from_secret: registry_server @@ -133,37 +124,54 @@ steps: REGISTRY_PASSWORD: from_secret: registry_password commands: - - bash -lc ' - set -eo pipefail - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y podman python3-pip curl jq ca-certificates - python3 -m pip install --no-cache-dir podman-compose - 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 - TAG=$(printf '%s' \"$CI_COMMIT_TAG\" | sed 's#^refs/tags/##') - export TAG - if [ -z \"$COMPOSE_PROJECT_NAME\" ]; then - COMPOSE_PROJECT_NAME=\"harb\" - fi - 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 + - | + bash -lc ' + set -eo pipefail + if [ -z "${CI_COMMIT_TAG:-}" ]; then + echo "CI_COMMIT_TAG not set" >&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 - ' + if [ -z "${REGISTRY_SERVER:-}" ] || [ -z "${REGISTRY_NAMESPACE:-}" ]; then + echo "Registry server or namespace missing" >&2 + exit 1 + fi + TAG=$(printf '%s' "$CI_COMMIT_TAG" | sed "s#^refs/tags/##") + export TAG + if [ -z "${COMPOSE_PROJECT_NAME:-}" ]; then + COMPOSE_PROJECT_NAME=harb + fi + REGISTRY_ROOT="${REGISTRY_SERVER:-registry.sovraigns.network}" + REGISTRY_NS="${REGISTRY_NAMESPACE:-harb}" + REGISTRY_BASE="$REGISTRY_ROOT/$REGISTRY_NS" + + podman login "$REGISTRY_ROOT" -u "$REGISTRY_USERNAME" -p "$REGISTRY_PASSWORD" + # Build and publish CI base images + node_ci_tmp=harb-node-ci-build + playwright_ci_tmp=harb-playwright-ci-build + + podman build -f docker/Dockerfile.node-ci -t "$node_ci_tmp" . + podman tag "$node_ci_tmp" "$REGISTRY_BASE/node-ci:$TAG" + podman push "$REGISTRY_BASE/node-ci:$TAG" + podman tag "$REGISTRY_BASE/node-ci:$TAG" "$REGISTRY_BASE/node-ci:latest" + podman push "$REGISTRY_BASE/node-ci:latest" + + podman build -f docker/Dockerfile.playwright-ci -t "$playwright_ci_tmp" . + podman tag "$playwright_ci_tmp" "$REGISTRY_BASE/playwright-ci:$TAG" + podman push "$REGISTRY_BASE/playwright-ci:$TAG" + podman tag "$REGISTRY_BASE/playwright-ci:$TAG" "$REGISTRY_BASE/playwright-ci:latest" + podman push "$REGISTRY_BASE/playwright-ci:latest" + + 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_BASE/$service" + podman tag "$image" "$target:$TAG" + podman push "$target:$TAG" + podman tag "$target:$TAG" "$target:latest" + podman push "$target:latest" + done + ' diff --git a/docker/Dockerfile.node-ci b/docker/Dockerfile.node-ci new file mode 100644 index 0000000..4774f71 --- /dev/null +++ b/docker/Dockerfile.node-ci @@ -0,0 +1,40 @@ +# syntax=docker/dockerfile:1.6 + +FROM node:20-bookworm + +LABEL org.opencontainers.image.source="https://codeberg.org/johba/harb-ci" +LABEL org.opencontainers.image.description="Node.js toolchain for Harb Stack CI jobs" + +ENV DEBIAN_FRONTEND=noninteractive \ + PNPM_HOME=/root/.local/share/pnpm \ + PATH=/root/.local/share/pnpm:/root/.local/bin:/root/.foundry/bin:$PATH + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + build-essential \ + pkg-config \ + libssl-dev \ + python3 \ + python3-pip \ + bc \ + jq \ + curl && \ + rm -rf /var/lib/apt/lists/* + +# Enable corepack-managed package managers and pin the versions we expect in CI. +RUN corepack enable && \ + corepack prepare pnpm@8.15.4 --activate && \ + corepack prepare yarn@4.3.1 --activate + +# Install Foundry once so downstream jobs skip the bootstrap step. +RUN curl -L https://foundry.paradigm.xyz | bash && \ + ~/.foundry/bin/foundryup --version && \ + ~/.foundry/bin/foundryup + +WORKDIR /workspace + +CMD ["bash"] diff --git a/docker/Dockerfile.playwright-ci b/docker/Dockerfile.playwright-ci new file mode 100644 index 0000000..32bce1e --- /dev/null +++ b/docker/Dockerfile.playwright-ci @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1.6 + +FROM mcr.microsoft.com/playwright:v1.56.0-jammy + +LABEL org.opencontainers.image.source="https://codeberg.org/johba/harb-ci" +LABEL org.opencontainers.image.description="Playwright + Podman image for Harb Stack end-to-end CI" + +ENV DEBIAN_FRONTEND=noninteractive \ + PNPM_HOME=/root/.local/share/pnpm \ + PATH=/root/.local/share/pnpm:/root/.local/bin:$PATH + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + podman \ + slirp4netns \ + uidmap \ + iptables \ + git \ + ca-certificates \ + python3-pip \ + jq \ + curl && \ + rm -rf /var/lib/apt/lists/* + +RUN python3 -m pip install --no-cache-dir podman-compose && \ + corepack enable && \ + corepack prepare pnpm@8.15.4 --activate && \ + corepack prepare yarn@4.3.1 --activate + +WORKDIR /workspace + +CMD ["bash"] diff --git a/onchain/test/helpers/TestBase.sol b/onchain/test/helpers/TestBase.sol index 7fd51b3..0a2d50d 100644 --- a/onchain/test/helpers/TestBase.sol +++ b/onchain/test/helpers/TestBase.sol @@ -72,17 +72,17 @@ contract TestEnvironment is TestConstants { using UniswapHelpers for IUniswapV3Pool; // Core contracts - IUniswapV3Factory public factory; - IUniswapV3Pool public pool; - IWETH9 public weth; - Kraiken public harberg; - Stake public stake; - LiquidityManager public lm; - Optimizer public optimizer; + IUniswapV3Factory internal factory; + IUniswapV3Pool internal pool; + IWETH9 internal weth; + Kraiken internal harberg; + Stake internal stake; + LiquidityManager internal lm; + Optimizer internal optimizer; // State variables - bool public token0isWeth; - address public feeDestination; + bool internal token0isWeth; + address internal feeDestination; constructor(address _feeDestination) { feeDestination = _feeDestination; @@ -314,17 +314,4 @@ contract TestEnvironment is TestConstants { return (factory, pool, weth, harberg, stake, lm, optimizer, token0isWeth); } - /** - * @notice Perform recenter with proper time warp and oracle updates - * @param liquidityManager The LiquidityManager instance to recenter - * @param caller The address that will call recenter - */ - function performRecenter(LiquidityManager liquidityManager, address caller) external { - // Update oracle time - vm.warp(block.timestamp + ORACLE_UPDATE_INTERVAL); - - // Perform recenter - vm.prank(caller); - liquidityManager.recenter(); - } }