/ .github / workflows / interop.yml
interop.yml
  1  # Interoperability Tests
  2  #
  3  # This workflow ensures Kubo remains compatible with the broader IPFS ecosystem.
  4  # It builds Kubo from source, then runs:
  5  #
  6  # 1. helia-interop: Tests compatibility with Helia (JavaScript IPFS implementation)
  7  #    using Playwright-based tests from @helia/interop package.
  8  #
  9  # 2. ipfs-webui: Runs E2E tests from ipfs/ipfs-webui repository to verify
 10  #    the web interface works correctly with the locally built Kubo binary.
 11  #
 12  # Both jobs use caching to speed up repeated runs (npm dependencies, Playwright
 13  # browsers, and webui build artifacts).
 14  
 15  name: Interop
 16  
 17  on:
 18    workflow_dispatch:
 19    pull_request:
 20      paths-ignore:
 21        - '**/*.md'
 22    push:
 23      branches:
 24        - 'master'
 25  
 26  concurrency:
 27    group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
 28    cancel-in-progress: true
 29  
 30  defaults:
 31    run:
 32      shell: bash
 33  
 34  jobs:
 35    interop-prep:
 36      if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch'
 37      runs-on: ubuntu-latest
 38      timeout-minutes: 5
 39      env:
 40        TEST_DOCKER: 0
 41        TEST_FUSE: 0
 42        TEST_VERBOSE: 1
 43        GIT_PAGER: cat
 44        IPFS_CHECK_RCMGR_DEFAULTS: 1
 45      defaults:
 46        run:
 47          shell: bash
 48      steps:
 49        - uses: actions/checkout@v6
 50        - uses: actions/setup-go@v6
 51          with:
 52            go-version-file: 'go.mod'
 53        - run: make build
 54        - uses: actions/upload-artifact@v7
 55          with:
 56            name: kubo
 57            path: cmd/ipfs/ipfs
 58    helia-interop:
 59      needs: [interop-prep]
 60      runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }}
 61      timeout-minutes: 20
 62      defaults:
 63        run:
 64          shell: bash
 65      steps:
 66        - uses: actions/setup-node@v6
 67          with:
 68            node-version: lts/*
 69        - uses: actions/download-artifact@v8
 70          with:
 71            name: kubo
 72            path: cmd/ipfs
 73        - run: chmod +x cmd/ipfs/ipfs
 74        - run: sudo apt update
 75        - run: sudo apt install -y libxkbcommon0 libxdamage1 libgbm1 libpango-1.0-0 libcairo2 # dependencies for playwright
 76        # Cache node_modules based on latest @helia/interop version from npm registry.
 77        # This ensures we always test against the latest release while still benefiting
 78        # from caching when the version hasn't changed.
 79        - name: Get latest @helia/interop version
 80          id: helia-version
 81          run: echo "version=$(npm view @helia/interop version)" >> $GITHUB_OUTPUT
 82        - name: Cache helia-interop node_modules
 83          uses: actions/cache@v5
 84          id: helia-cache
 85          with:
 86            path: node_modules
 87            key: ${{ runner.os }}-helia-interop-${{ steps.helia-version.outputs.version }}
 88        - name: Install @helia/interop
 89          if: steps.helia-cache.outputs.cache-hit != 'true'
 90          run: npm install @helia/interop
 91        # TODO(IPIP-499): Remove --grep --invert workaround once helia implements IPIP-499
 92        # Tracking issue: https://github.com/ipfs/helia/issues/941
 93        #
 94        # PROVISIONAL HACK: Skip '@helia/mfs - should have the same CID after
 95        # creating a file' test due to IPIP-499 changes in kubo.
 96        #
 97        # WHY IT FAILS: The test creates a 5-byte file in MFS on both kubo and helia,
 98        # then compares the root directory CID. With kubo PR #11148, `ipfs files write`
 99        # now produces raw CIDs for single-block files (matching `ipfs add --raw-leaves`),
100        # while helia uses `reduceSingleLeafToSelf: false` which keeps the dag-pb wrapper.
101        # Different file CIDs lead to different directory CIDs.
102        #
103        # We run aegir directly (instead of helia-interop binary) because only aegir
104        # supports the --grep/--invert flags needed to exclude specific tests.
105        - name: Run helia-interop tests (excluding IPIP-499 incompatible test)
106          run: npx aegir test -t node --bail -- --grep 'should have the same CID after creating a file' --invert
107          env:
108            KUBO_BINARY: ${{ github.workspace }}/cmd/ipfs/ipfs
109          working-directory: node_modules/@helia/interop
110    ipfs-webui:
111      needs: [interop-prep]
112      runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }}
113      timeout-minutes: 20
114      env:
115        NO_SANDBOX: true
116        LIBP2P_TCP_REUSEPORT: false
117        LIBP2P_ALLOW_WEAK_RSA_KEYS: 1
118        E2E_IPFSD_TYPE: go
119        GIT_PAGER: cat
120        IPFS_CHECK_RCMGR_DEFAULTS: 1
121      defaults:
122        run:
123          shell: bash
124      steps:
125        - uses: actions/download-artifact@v8
126          with:
127            name: kubo
128            path: cmd/ipfs
129        - run: chmod +x cmd/ipfs/ipfs
130        - uses: actions/checkout@v6
131          with:
132            repository: ipfs/ipfs-webui
133            path: ipfs-webui
134        - uses: actions/setup-node@v6
135          with:
136            node-version-file: 'ipfs-webui/.tool-versions'
137        - id: webui-ref
138          run: echo "ref=$(git rev-parse --short HEAD)" | tee -a $GITHUB_OUTPUT
139          working-directory: ipfs-webui
140        - id: webui-state
141          env:
142            GITHUB_TOKEN: ${{ github.token }}
143            ENDPOINT: repos/ipfs/ipfs-webui/commits/${{ steps.webui-ref.outputs.ref }}/status
144            SELECTOR: .state
145            KEY: state
146          run: gh api "$ENDPOINT" --jq "$SELECTOR" | xargs -I{} echo "$KEY={}" | tee -a $GITHUB_OUTPUT
147        # Cache node_modules based on package-lock.json
148        - name: Cache node_modules
149          uses: actions/cache@v5
150          id: node-modules-cache
151          with:
152            path: ipfs-webui/node_modules
153            key: ${{ runner.os }}-webui-node-modules-${{ hashFiles('ipfs-webui/package-lock.json') }}
154            restore-keys: |
155              ${{ runner.os }}-webui-node-modules-
156        - name: Install dependencies
157          if: steps.node-modules-cache.outputs.cache-hit != 'true'
158          run: npm ci --prefer-offline --no-audit --progress=false
159          working-directory: ipfs-webui
160        # Cache Playwright browsers
161        - name: Cache Playwright browsers
162          uses: actions/cache@v5
163          id: playwright-cache
164          with:
165            path: ~/.cache/ms-playwright
166            key: ${{ runner.os }}-playwright-${{ hashFiles('ipfs-webui/package-lock.json') }}
167            restore-keys: |
168              ${{ runner.os }}-playwright-
169        # On cache miss: download browsers and install OS dependencies
170        - name: Install Playwright with dependencies
171          if: steps.playwright-cache.outputs.cache-hit != 'true'
172          run: npx playwright install --with-deps
173          working-directory: ipfs-webui
174        # On cache hit: only ensure OS dependencies are present (fast, idempotent)
175        - name: Install Playwright OS dependencies
176          if: steps.playwright-cache.outputs.cache-hit == 'true'
177          run: npx playwright install-deps
178          working-directory: ipfs-webui
179        # Cache test build output
180        - name: Cache test build
181          uses: actions/cache@v5
182          id: test-build-cache
183          with:
184            path: ipfs-webui/build
185            key: ${{ runner.os }}-webui-build-${{ hashFiles('ipfs-webui/package-lock.json', 'ipfs-webui/src/**', 'ipfs-webui/public/**') }}
186            restore-keys: |
187              ${{ runner.os }}-webui-build-
188        - name: Build ipfs-webui@${{ steps.webui-ref.outputs.ref }} (state=${{ steps.webui-state.outputs.state }})
189          if: steps.test-build-cache.outputs.cache-hit != 'true'
190          run: npm run test:build
191          working-directory: ipfs-webui
192        - name: Test ipfs-webui@${{ steps.webui-ref.outputs.ref }} (state=${{ steps.webui-state.outputs.state }}) E2E against the locally built Kubo binary
193          run: npm run test:e2e
194          env:
195            IPFS_GO_EXEC: ${{ github.workspace }}/cmd/ipfs/ipfs
196          working-directory: ipfs-webui
197        - name: Upload test artifacts on failure
198          if: failure()
199          uses: actions/upload-artifact@v7
200          with:
201            name: webui-test-results
202            path: ipfs-webui/test-results/
203            retention-days: 7