diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d40c3c3..852ebceb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,26 +1,49 @@ +# ============================================================================ +# Pezkuwi SDK — GitLab CI/CD Pipeline +# Mirrors: checks-quick, checks, security-audit, tests, docs, release +# +# Same unified image as GitHub Actions — no apt-get overhead, sccache ready. +# ============================================================================ + +image: ghcr.io/pezkuwichain/ci-unified:bullseye-1.88.0-2025-06-27-v202511141243 + stages: - - lint + - quick-checks + - checks - security - test + - docs - build - release +# ── Variables ───────────────────────────────────────────────────────────────── variables: CARGO_HOME: "$CI_PROJECT_DIR/.cargo" CARGO_INCREMENTAL: "0" CARGO_TERM_COLOR: "always" RUST_BACKTRACE: "short" RUSTFLAGS: "-D warnings" - SCCACHE_DIR: "$CI_PROJECT_DIR/.sccache" + # Disable forklift (GitHub-only sccache proxy not available on GitLab) + RUSTC_WRAPPER: "" + # Faster artifact transfer + FF_USE_FASTZIP: "true" + ARTIFACT_COMPRESSION_LEVEL: "fast" + CACHE_COMPRESSION_LEVEL: "fast" +# ── Workflow: avoid duplicate pipelines ─────────────────────────────────────── +workflow: + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID + - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_PIPELINE_SOURCE == "web" + +# ── Default ─────────────────────────────────────────────────────────────────── default: - image: rust:slim before_script: - - apt-get update -qq && apt-get install -y -qq --no-install-recommends - pkg-config libssl-dev clang cmake protobuf-compiler git curl ca-certificates - - rustup show # installs channel from rust-toolchain.toml (1.88.0) - - rustup target add wasm32-unknown-unknown - - cargo --version && rustc --version + - rustup show + - rustc --version && cargo --version cache: key: files: @@ -29,122 +52,478 @@ default: paths: - .cargo/registry/ - .cargo/git/ - - target/ policy: pull-push interruptible: true - timeout: 3h -# ─── LINT ──────────────────────────────────────────────────────────────────── +# ╔══════════════════════════════════════════════════════════════════════════════ +# QUICK CHECKS — fast feedback on every push/MR (mirrors checks-quick.yml) +# ╚══════════════════════════════════════════════════════════════════════════════ fmt: - stage: lint + stage: quick-checks script: - cargo fmt --all -- --check - allow_failure: false -check: - stage: lint +check-toml-format: + stage: quick-checks script: - - cargo check --workspace --locked - allow_failure: false + - taplo format --check --config .config/taplo.toml + - echo "Run 'taplo format --config .config/taplo.toml' to fix TOML issues" + +check-dependency-rules: + stage: quick-checks + script: + - cd bizinikiwi/ && ../.gitlab/ensure-deps.sh + +check-fail-ci: + stage: quick-checks + script: + - | + if rg --line-number --hidden --type rust --glob '!{.git,target}' "FAIL-CI" .; then + echo "FAIL-CI marker found — remove before merging" + exit 1 + fi + echo "✅ No FAIL-CI markers found" + +check-workspace-integrity: + stage: quick-checks + script: + - pip3 install toml "cargo-workspace>=1.2.6" --quiet + - | + python3 .github/scripts/check-workspace.py . \ + --exclude "bizinikiwi/pezframe/contracts/fixtures/build" \ + --exclude "bizinikiwi/pezframe/contracts/fixtures/contracts/common" + - python3 .github/scripts/deny-git-deps.py . + +check-zepter: + stage: quick-checks + script: + - cargo metadata --format-version=1 --locked > /dev/null + - zepter --version + - zepter run check + +test-rust-features: + stage: quick-checks + script: + - bash .gitlab/rust-features.sh . + +check-umbrella: + stage: quick-checks + script: + - pip3 install "cargo-workspace>=1.2.4" toml --quiet + - git config --global --add safe.directory '*' + - | + UMBRELLA_VERSION=$(cargo metadata --format-version=1 | \ + python3 -c "import json,sys; pkgs=json.load(sys.stdin)['packages']; \ + print(next(p['version'] for p in pkgs if p['manifest_path'].endswith('umbrella/Cargo.toml')))") + echo "Umbrella version: $UMBRELLA_VERSION" + python3 scripts/generate-umbrella.py --sdk . --version "$UMBRELLA_VERSION" + cargo +nightly fmt -p pezkuwi-sdk + - | + if [ -n "$(git status --porcelain)" ]; then + echo "❌ Umbrella crate is out of sync. Run: python3 scripts/generate-umbrella.py --sdk . --version " + git diff + exit 1 + fi + echo "✅ Umbrella crate is up to date" + +check-licenses-headers: + stage: quick-checks + script: + - | + for dir in pezkuwi pezcumulus bizinikiwi; do + echo "Checking license headers in ./$dir..." + MISSING=$(find "./$dir" -name "*.rs" -type f | head -200 | while read f; do + head -20 "$f" | grep -qiE "(apache|gpl|mit|unlicense|copyright)" || echo "$f" + done) + if [ -n "$MISSING" ]; then + echo "⚠️ Files potentially missing license headers in $dir:" + echo "$MISSING" | head -20 + else + echo "✅ $dir: license headers OK (sampled)" + fi + done + +# ╔══════════════════════════════════════════════════════════════════════════════ +# CHECKS — deeper linting and feature checks (mirrors checks.yml) +# ╚══════════════════════════════════════════════════════════════════════════════ clippy: - stage: lint + stage: checks + needs: [fmt] + variables: + SKIP_WASM_BUILD: "1" + timeout: 90m script: - - cargo clippy --workspace --locked -- -D warnings - allow_failure: false + - git config --global --add safe.directory '*' + - | + cargo clippy --all-targets --all-features --locked --workspace --quiet \ + --exclude pezpallet-revive-eth-rpc \ + --exclude pezkuwi-zombienet-sdk-tests \ + -- -D warnings -# ─── SECURITY ──────────────────────────────────────────────────────────────── +check-try-runtime: + stage: checks + needs: [clippy] + variables: + SKIP_WASM_BUILD: "1" + timeout: 90m + script: + - git config --global --add safe.directory '*' + - cargo check --locked --all --features try-runtime --quiet + - cargo check --locked -p teyrchain-template-node --features try-runtime + - cargo check --locked --all --features try-runtime,experimental --quiet + +check-core-crypto-features: + stage: checks + needs: [fmt] + script: + - rustup target add wasm32v1-none + - | + for pkg in core application-crypto keyring; do + echo "--- Checking bizinikiwi/primitives/$pkg ---" + (cd bizinikiwi/primitives/$pkg && ./check-features-variants.sh) + done + +# ╔══════════════════════════════════════════════════════════════════════════════ +# SECURITY — mirrors security-audit.yml (runs on push + schedule weekly Mon 06:00) +# ╚══════════════════════════════════════════════════════════════════════════════ + +cargo-deny-advisories: + stage: security + needs: [] + before_script: [] + script: + - cargo deny check advisories --config deny.toml + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID + - if: $CI_PIPELINE_SOURCE == "schedule" + +cargo-deny-licenses: + stage: security + needs: [] + before_script: [] + script: + - cargo deny check licenses --config deny.toml + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID + - if: $CI_PIPELINE_SOURCE == "schedule" + +cargo-deny-sources: + stage: security + needs: [] + before_script: [] + script: + - cargo deny check sources --config deny.toml + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID + - if: $CI_PIPELINE_SOURCE == "schedule" cargo-audit: stage: security - script: - - cargo install cargo-audit --quiet - - cargo audit - allow_failure: true # advisory-only; does not block merge - -cargo-deny: - stage: security - script: - - cargo install cargo-deny --quiet - - cargo deny check --config deny.toml - allow_failure: false - -license-check: - stage: security + needs: [] + before_script: [] script: - | - echo "Checking Apache-2.0 license headers..." - if [ ! -f LICENSE-APACHE ]; then - echo "ERROR: LICENSE-APACHE missing" && exit 1 - fi - if [ ! -f NOTICE ]; then - echo "ERROR: NOTICE file missing" && exit 1 - fi - echo "✅ License files present" + cargo audit \ + --ignore RUSTSEC-2026-0006 \ + --ignore RUSTSEC-2026-0020 \ + --ignore RUSTSEC-2026-0021 \ + --ignore RUSTSEC-2026-0049 \ + --ignore RUSTSEC-2023-0071 \ + --ignore RUSTSEC-2025-0055 \ + --ignore RUSTSEC-2026-0067 \ + --ignore RUSTSEC-2026-0068 \ + 2>&1 | tee audit-output.txt || (cat audit-output.txt && exit 1) allow_failure: false + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == "schedule" -# ─── TEST ──────────────────────────────────────────────────────────────────── +# ╔══════════════════════════════════════════════════════════════════════════════ +# TEST — mirrors tests-linux-stable.yml (manual-only on GitHub, schedule here) +# ╚══════════════════════════════════════════════════════════════════════════════ -unit-tests: +# Tests split into 3 partitions via cargo-nextest — mirrors GitHub matrix strategy +.test-base: stage: test + needs: [clippy, check-try-runtime] + variables: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + WASM_BUILD_NO_COLOR: "1" + CARGO_INCREMENTAL: "0" + timeout: 90m + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_PIPELINE_SOURCE == "web" + when: manual + +test-partition-1: + extends: .test-base script: - - cargo test --workspace --lib --locked 2>&1 | tee test-results.txt + - git config --global --add safe.directory '*' + - | + cargo nextest run \ + --workspace --locked --release --no-fail-fast --cargo-quiet \ + --features try-runtime,experimental,ci-only-tests \ + --partition count:1/3 artifacts: when: always - paths: - - test-results.txt + paths: [test-results-1.xml] + reports: + junit: test-results-1.xml expire_in: 7 days - allow_failure: false + +test-partition-2: + extends: .test-base + script: + - git config --global --add safe.directory '*' + - | + cargo nextest run \ + --workspace --locked --release --no-fail-fast --cargo-quiet \ + --features try-runtime,experimental,ci-only-tests \ + --partition count:2/3 + artifacts: + when: always + paths: [test-results-2.xml] + reports: + junit: test-results-2.xml + expire_in: 7 days + +test-partition-3: + extends: .test-base + script: + - git config --global --add safe.directory '*' + - | + cargo nextest run \ + --workspace --locked --release --no-fail-fast --cargo-quiet \ + --features try-runtime,experimental,ci-only-tests \ + --partition count:3/3 + # First partition also runs runtime-api tests + - cargo nextest run -p pezsp-api-test --features enable-pez-staging-api --cargo-quiet + artifacts: + when: always + paths: [test-results-3.xml] + reports: + junit: test-results-3.xml + expire_in: 7 days + +test-no-try-runtime-1: + extends: .test-base + script: + - git config --global --add safe.directory '*' + - | + cargo nextest run \ + --workspace --locked --release --no-fail-fast --cargo-quiet \ + --features experimental,ci-only-tests \ + --partition count:1/2 + +test-no-try-runtime-2: + extends: .test-base + script: + - git config --global --add safe.directory '*' + - | + cargo nextest run \ + --workspace --locked --release --no-fail-fast --cargo-quiet \ + --features experimental,ci-only-tests \ + --partition count:2/2 wasm-check: stage: test + needs: [check-try-runtime] script: + - rustup target add wasm32-unknown-unknown - cargo check -p pezkuwichain-runtime --locked --target wasm32-unknown-unknown - allow_failure: false + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID -# ─── BUILD ─────────────────────────────────────────────────────────────────── - -build: - stage: build +# Runtime migration — disabled until try-runtime Zagros endpoints are live +# Enable by removing the `when: manual` rule and setting proper URIs +runtime-migration: + stage: test + needs: [] + variables: + RUSTFLAGS: "" script: - - cargo build --release -p pezkuwi --locked 2>&1 | tee build.log - - ls -lh target/release/pezkuwi target/release/pezkuwi-execute-worker target/release/pezkuwi-prepare-worker + - | + echo "Runtime migration check — currently manual/disabled" + echo "Targets: zagros, asset-hub-zagros, collectives-zagros, coretime-zagros" + echo "Re-enable when wss://try-runtime-zagros.pezkuwichain.io is live" + rules: + - if: $CI_PIPELINE_SOURCE == "web" + when: manual + - if: $CI_PIPELINE_SOURCE == "schedule" + when: manual + allow_failure: true + +# ╔══════════════════════════════════════════════════════════════════════════════ +# DOCS — mirrors docs.yml +# ╚══════════════════════════════════════════════════════════════════════════════ + +test-doc: + stage: docs + needs: [clippy] + variables: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + SKIP_WASM_BUILD: "0" + timeout: 5h + script: + - rustup target add wasm32v1-none + - git config --global --add safe.directory '*' + - | + cargo test --doc --workspace --locked --all-features \ + --exclude pezkuwi-zombienet-sdk-tests \ + --exclude pezpallet-revive-eth-rpc + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == "schedule" + allow_failure: true + +build-rustdoc: + stage: docs + needs: [test-doc] + variables: + SKIP_WASM_BUILD: "1" + RUSTDOCFLAGS: >- + -Dwarnings + --default-theme=ayu + --html-in-header ./docs/sdk/assets/header.html + --extend-css ./docs/sdk/assets/theme.css + --html-after-content ./docs/sdk/assets/after-content.html + timeout: 4h + script: + - git config --global --add safe.directory '*' + - | + cargo doc --all-features --workspace --no-deps --locked \ + --exclude pezkuwi-zombienet-sdk-tests \ + --exclude pezpallet-revive-eth-rpc + - rm -f target/doc/.lock + - mv target/doc ./crate-docs + - echo '' > ./crate-docs/index.html + - tar czf crate-docs.tar.gz crate-docs/ artifacts: + paths: [crate-docs.tar.gz] + expire_in: 1 day + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + allow_failure: true + +build-implementers-guide: + stage: docs + needs: [] + before_script: [] + image: rust:slim + script: + - cargo install mdbook --version 0.4.35 --locked --quiet + - mdbook build ./pezkuwi/roadmap/implementers-guide + artifacts: + paths: [pezkuwi/roadmap/implementers-guide/book/] + expire_in: 1 day + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_MERGE_REQUEST_IID + allow_failure: true + +# GitLab Pages — publish rustdocs to pages.gitlab.io +pages: + stage: docs + needs: [build-rustdoc, build-implementers-guide] + before_script: [] + script: + - mkdir -p public + - tar xzf crate-docs.tar.gz -C public/ --strip-components=1 || true + - cp -r pezkuwi/roadmap/implementers-guide/book public/guide || true + artifacts: + paths: [public/] + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + allow_failure: true + +# ╔══════════════════════════════════════════════════════════════════════════════ +# BUILD — release binary (main branch + tags) +# ╚══════════════════════════════════════════════════════════════════════════════ + +build-release: + stage: build + needs: [clippy, wasm-check] + variables: + RUSTFLAGS: "-D warnings" + timeout: 4h + script: + - git config --global --add safe.directory '*' + - cargo build --release -p pezkuwi --locked + - | + echo "=== Build artifacts ===" + ls -lh target/release/pezkuwi \ + target/release/pezkuwi-execute-worker \ + target/release/pezkuwi-prepare-worker + echo "" + echo "=== SHA256 checksums ===" + sha256sum \ + target/release/pezkuwi \ + target/release/pezkuwi-execute-worker \ + target/release/pezkuwi-prepare-worker | tee SHA256SUMS + artifacts: + name: "pezkuwi-$CI_COMMIT_REF_NAME" paths: - target/release/pezkuwi - target/release/pezkuwi-execute-worker - target/release/pezkuwi-prepare-worker - expire_in: 1 day - when: on_success - only: - - main - - tags + - SHA256SUMS + expire_in: 7 days + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ -# ─── RELEASE ───────────────────────────────────────────────────────────────── +# ╔══════════════════════════════════════════════════════════════════════════════ +# RELEASE — GitLab Release with assets (tags only) +# ╚══════════════════════════════════════════════════════════════════════════════ release: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest - before_script: [] # override default before_script — no Rust needed - needs: - - build + before_script: [] + needs: [build-release] script: - - echo "Creating release $CI_COMMIT_TAG" + - echo "Creating GitLab Release for $CI_COMMIT_TAG" release: tag_name: "$CI_COMMIT_TAG" name: "Pezkuwi SDK $CI_COMMIT_TAG" description: | ## Pezkuwi SDK $CI_COMMIT_TAG - Binary artifacts built from this release are attached. + ### Binaries (Linux x86_64) - **Install:** + | Binary | Description | + |--------|-------------| + | `pezkuwi` | Node binary | + | `pezkuwi-execute-worker` | WASM execution worker | + | `pezkuwi-prepare-worker` | WASM preparation worker | + + SHA256 checksums in `SHA256SUMS`. + + ### Verify ```bash - curl -LO https://gitlab.com/pezkuwichain/pezkuwi-sdk/-/releases/$CI_COMMIT_TAG/downloads/pezkuwi - chmod +x pezkuwi + sha256sum -c SHA256SUMS ``` - only: - - /^v\d+\.\d+\.\d+$/ - except: - - branches + assets: + links: + - name: "pezkuwi (node binary)" + url: "$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/raw/target/release/pezkuwi?job=build-release" + link_type: package + - name: "pezkuwi-execute-worker" + url: "$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/raw/target/release/pezkuwi-execute-worker?job=build-release" + link_type: package + - name: "pezkuwi-prepare-worker" + url: "$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/raw/target/release/pezkuwi-prepare-worker?job=build-release" + link_type: package + - name: "SHA256SUMS" + url: "$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/raw/SHA256SUMS?job=build-release" + link_type: other + rules: + - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/