Conversation
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#14171) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…, CONTRIBUTING.md and PULL_REQUEST_TEMPLATE.md to .github folder
…ted internal transactions address placeholders (blockscout#14249)
…lockscout#14239) Co-authored-by: Alexander Kolotov <alexander.kolotov@gmail.com> Co-authored-by: Nikita Pozdniakov <nikitosing4@mail.ru> Co-authored-by: Victor Baranov <baranov.viktor.27@gmail.com>
…CTION_ASSOCIATION flag to preload smart-contract associations (blockscout#14257)
…cks BENS preload toggle (blockscout#14262)
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
| name: Build matrix | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| matrix: ${{ steps.set-matrix.outputs.matrix }} | ||
| steps: | ||
| - id: set-matrix | ||
| run: | | ||
| echo "matrix=$(node -e ' | ||
|
|
||
| const defaultChainTypes = ["default"]; | ||
|
|
||
| // Add/remove CI matrix chain types here | ||
| const chainTypes = [ | ||
| "default", | ||
| "arbitrum", | ||
| "arc", | ||
| "blackfort", | ||
| "ethereum", | ||
| "filecoin", | ||
| "optimism", | ||
| "optimism-celo", | ||
| "rsk", | ||
| "scroll", | ||
| "shibarium", | ||
| "stability", | ||
| "zetachain", | ||
| "zilliqa", | ||
| "zksync", | ||
| "neon" | ||
| ]; | ||
|
|
||
| // Add/remove CI matrix chain types for "ci:core" label here | ||
| const coreChainTypes = [ | ||
| "default", | ||
| "ethereum", | ||
| "optimism", | ||
| "optimism-celo" | ||
| ]; | ||
|
|
||
| const labels = ${{ github.event_name == 'pull_request' && toJson(github.event.pull_request.labels.*.name) || '[]' }}; | ||
| const ciLabels = labels.filter(label => label.startsWith("ci:")); | ||
| const labeledChainTypes = chainTypes.filter(chainType => | ||
| ciLabels.includes("ci:all") || | ||
| ciLabels.includes("ci:core") && coreChainTypes.includes(chainType) || | ||
| ciLabels.includes("ci:" + chainType) | ||
| ); | ||
|
|
||
| // Chain type matrix we use in PRs to master branch | ||
| const ciChainTypes = labeledChainTypes.length > 0 ? labeledChainTypes : defaultChainTypes; | ||
|
|
||
| // Check for bridged tokens label | ||
| const hasBridgedTokensLabel = ciLabels.includes("ci:bridged-tokens"); | ||
|
|
||
| // Create matrix combinations | ||
| const targetChainTypes = ${{ github.event_name == 'pull_request' && 'ciChainTypes' || 'chainTypes' }}; | ||
| const bridgedTokensConfigs = hasBridgedTokensLabel ? [false, true] : [false]; | ||
|
|
||
| const matrixIncludes = []; | ||
| for (const chainType of targetChainTypes) { | ||
| for (const bridgedTokens of bridgedTokensConfigs) { | ||
| matrixIncludes.push({ | ||
| "chain-type": chainType, | ||
| "bridged-tokens": bridgedTokens | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| const matrix = { "include": matrixIncludes }; | ||
| console.log(JSON.stringify(matrix)); | ||
| ')" >> $GITHUB_OUTPUT | ||
|
|
||
| build-and-cache: |
| name: Build and Cache deps | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: "ELIXIR_VERSION.lock" | ||
| run: echo "${ELIXIR_VERSION}" > ELIXIR_VERSION.lock | ||
|
|
||
| - name: "OTP_VERSION.lock" | ||
| run: echo "${OTP_VERSION}" > OTP_VERSION.lock | ||
|
|
||
| - name: Restore Mix Deps Cache | ||
| uses: actions/cache@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - name: Conditionally build Mix deps cache | ||
| if: steps.deps-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| mix local.hex --force | ||
| mix local.rebar --force | ||
| mix deps.clean --all | ||
| mix deps.get | ||
| mix deps.compile --skip-umbrella-children | ||
|
|
||
| - name: Restore Explorer NPM Cache | ||
| uses: actions/cache@v4 | ||
| id: explorer-npm-cache | ||
| with: | ||
| path: apps/explorer/node_modules | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm- | ||
|
|
||
| - name: Conditionally build Explorer NPM Cache | ||
| if: steps.explorer-npm-cache.outputs.cache-hit != 'true' | ||
| run: npm install | ||
| working-directory: apps/explorer | ||
|
|
||
| - name: Restore Blockscout Web NPM Cache | ||
| uses: actions/cache@v4 | ||
| id: blockscoutweb-npm-cache | ||
| with: | ||
| path: apps/block_scout_web/assets/node_modules | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- | ||
|
|
||
| - name: Conditionally build Blockscout Web NPM Cache | ||
| if: steps.blockscoutweb-npm-cache.outputs.cache-hit != 'true' | ||
| run: npm install | ||
| working-directory: apps/block_scout_web/assets | ||
|
|
||
| credo: |
| name: Credo | ||
| runs-on: ubuntu-latest | ||
| needs: build-and-cache | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: Restore Mix Deps Cache | ||
| uses: actions/cache/restore@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - run: mix credo | ||
|
|
||
| check_formatted: |
| name: Code formatting checks | ||
| runs-on: ubuntu-latest | ||
| needs: build-and-cache | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: Restore Mix Deps Cache | ||
| uses: actions/cache/restore@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - run: mix format --check-formatted | ||
|
|
||
| dialyzer: |
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJson(needs.matrix-builder.outputs.matrix) }} | ||
| name: Dialyzer static analysis | ||
| runs-on: ubuntu-latest | ||
| needs: | ||
| - build-and-cache | ||
| - matrix-builder | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: Restore Mix Deps Cache | ||
| uses: actions/cache/restore@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - name: Restore Dialyzer Cache | ||
| uses: actions/cache@v4 | ||
| id: dialyzer-cache | ||
| with: | ||
| path: priv/plts | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-${{ matrix.chain-type }}-${{ matrix.bridged-tokens }}-dialyzer-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-${{ matrix.chain-type }}-${{ matrix.bridged-tokens }}-dialyzer-mixlockhash- | ||
|
|
||
| - name: Conditionally build Dialyzer Cache | ||
| if: steps.dialyzer-cache.output.cache-hit != 'true' | ||
| run: | | ||
| mkdir -p priv/plts | ||
| mix dialyzer --plt | ||
| env: | ||
| CHAIN_TYPE: ${{ matrix.chain-type != 'default' && matrix.chain-type || '' }} | ||
| BRIDGED_TOKENS_ENABLED: ${{ matrix.bridged-tokens }} | ||
|
|
||
| - name: Run Dialyzer | ||
| run: mix dialyzer --halt-exit-status | ||
| env: | ||
| CHAIN_TYPE: ${{ matrix.chain-type != 'default' && matrix.chain-type || '' }} | ||
| BRIDGED_TOKENS_ENABLED: ${{ matrix.bridged-tokens }} | ||
|
|
||
| gettext: |
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJson(needs.matrix-builder.outputs.matrix) }} | ||
| name: Blockscout Web Tests | ||
| runs-on: ubuntu-latest | ||
| needs: | ||
| - build-and-cache | ||
| - matrix-builder | ||
| services: | ||
| redis-db: | ||
| image: "redis:alpine" | ||
| ports: | ||
| - 6379:6379 | ||
|
|
||
| postgres: | ||
| image: postgres:17 | ||
| env: | ||
| # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database | ||
| POSTGRES_DB: explorer_test | ||
| # match PGPASSWORD for elixir image above | ||
| POSTGRES_PASSWORD: postgres | ||
| # match PGUSER for elixir image above | ||
| POSTGRES_USER: postgres | ||
| # Set health checks to wait until postgres has started | ||
| options: >- | ||
| --health-cmd pg_isready | ||
| --health-interval 10s | ||
| --health-timeout 5s | ||
| --health-retries 5 | ||
| ports: | ||
| # Maps tcp port 5432 on service container to the host | ||
| - 5432:5432 | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: Mix Deps Cache | ||
| uses: actions/cache/restore@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - name: Restore Explorer NPM Cache | ||
| uses: actions/cache@v4 | ||
| id: explorer-npm-cache | ||
| with: | ||
| path: apps/explorer/node_modules | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm- | ||
|
|
||
| - name: Restore Blockscout Web NPM Cache | ||
| uses: actions/cache@v4 | ||
| id: blockscoutweb-npm-cache | ||
| with: | ||
| path: apps/block_scout_web/assets/node_modules | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- | ||
|
|
||
| - name: Build assets | ||
| run: node node_modules/webpack/bin/webpack.js --mode development | ||
| working-directory: "apps/block_scout_web/assets" | ||
|
|
||
| - run: ./bin/install_chrome_headless.sh | ||
|
|
||
| - name: mix test --exclude no_nethermind | ||
| run: | | ||
| mix ecto.create --quiet | ||
| mix ecto.migrate | ||
| cd apps/block_scout_web | ||
| mix compile | ||
| mix test --no-start --exclude no_nethermind | ||
| env: | ||
| # match POSTGRES_PASSWORD for postgres image below | ||
| PGPASSWORD: postgres | ||
| # match POSTGRES_USER for postgres image below | ||
| PGUSER: postgres | ||
| ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" | ||
| ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" | ||
| CHAIN_ID: "10200" | ||
| API_RATE_LIMIT_DISABLED: "true" | ||
| API_GRAPHQL_RATE_LIMIT_DISABLED: "true" | ||
| ADMIN_PANEL_ENABLED: "true" | ||
| ACCOUNT_ENABLED: "true" | ||
| ACCOUNT_REDIS_URL: "redis://localhost:6379" | ||
| SOURCIFY_INTEGRATION_ENABLED: "true" | ||
| CHAIN_TYPE: ${{ matrix.chain-type != 'default' && matrix.chain-type || '' }} | ||
| WETH_TOKEN_TRANSFERS_FILTERING_ENABLED: "true" | ||
| BRIDGED_TOKENS_ENABLED: ${{ matrix.bridged-tokens }} | ||
| DISABLE_WEBAPP: "false" |
| name: Build matrix | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| matrix: ${{ steps.set-matrix.outputs.matrix }} | ||
| steps: | ||
| - id: set-matrix | ||
| run: | | ||
| echo "matrix=$(node -e ' | ||
|
|
||
| // Add/remove CI matrix chain types here | ||
| const chainTypes = [ | ||
| "default", | ||
| "arbitrum", | ||
| "arc", | ||
| "blackfort", | ||
| "ethereum", | ||
| "filecoin", | ||
| "neon", | ||
| "optimism", | ||
| "optimism-celo", | ||
| "rsk", | ||
| "scroll", | ||
| "shibarium", | ||
| "stability", | ||
| "suave", | ||
| "zetachain", | ||
| "zilliqa", | ||
| "zksync" | ||
| ]; | ||
|
|
||
| const matrix = { "chain-type": ${{ 'chainTypes' }} }; | ||
| console.log(JSON.stringify(matrix)); | ||
| ')" >> $GITHUB_OUTPUT | ||
|
|
||
| build-and-cache: |
| name: Build and Cache deps | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: "ELIXIR_VERSION.lock" | ||
| run: echo "${ELIXIR_VERSION}" > ELIXIR_VERSION.lock | ||
|
|
||
| - name: "OTP_VERSION.lock" | ||
| run: echo "${OTP_VERSION}" > OTP_VERSION.lock | ||
|
|
||
| - name: Restore Mix Deps Cache | ||
| uses: actions/cache@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - name: Conditionally build Mix deps cache | ||
| if: steps.deps-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| mix local.hex --force | ||
| mix local.rebar --force | ||
| mix deps.get | ||
| mix deps.compile --skip-umbrella-children | ||
|
|
||
| generate-swagger: |
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJson(needs.matrix-builder.outputs.matrix) }} | ||
| name: Generate Open API spec | ||
| runs-on: ubuntu-latest | ||
| needs: | ||
| - build-and-cache | ||
| - matrix-builder | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: erlef/setup-beam@v1 | ||
| with: | ||
| otp-version: ${{ env.OTP_VERSION }} | ||
| elixir-version: ${{ env.ELIXIR_VERSION }} | ||
| hexpm-mirrors: | | ||
| https://builds.hex.pm | ||
| https://cdn.jsdelivr.net/hex | ||
|
|
||
| - name: Mix Deps Cache | ||
| uses: actions/cache/restore@v4 | ||
| id: deps-cache | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- | ||
|
|
||
| - name: mix openapi.spec.yaml | ||
| run: | | ||
| mix openapi.spec.yaml --spec BlockScoutWeb.Specs.Public openapi.${{ matrix.chain-type }}.yaml --start-app=false | ||
| env: | ||
| CHAIN_TYPE: ${{ matrix.chain-type != 'default' && matrix.chain-type || '' }} | ||
| MUD_INDEXER_ENABLED: false | ||
|
|
||
| - name: Generate MUD-enabled spec for Optimism | ||
| if: matrix.chain-type == 'optimism' | ||
| run: | | ||
| mix openapi.spec.yaml --spec BlockScoutWeb.Specs.Public openapi.mud.yaml --start-app=false | ||
| env: | ||
| CHAIN_TYPE: optimism | ||
| MUD_INDEXER_ENABLED: true | ||
|
|
||
| - name: Upload OpenAPI spec | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: openapi-spec-${{ matrix.chain-type }} | ||
| path: openapi.${{ matrix.chain-type }}.yaml | ||
| retention-days: 1 | ||
|
|
||
| - name: Upload MUD-enabled spec | ||
| if: matrix.chain-type == 'optimism' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: openapi-spec-mud | ||
| path: openapi.mud.yaml | ||
| retention-days: 1 | ||
|
|
||
| push-specs: |
| needs: | ||
| - generate-swagger | ||
| - matrix-builder | ||
| runs-on: ubuntu-latest | ||
| name: Push all OpenAPI specs | ||
| steps: | ||
| - name: Validate required secrets | ||
| run: | | ||
| if [ -z "${{ secrets.API_SPECS_PAT }}" ]; then | ||
| echo "Error: API_SPECS_PAT secret is not set" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Checkout specs repository | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| repository: ${{ vars.API_SPECS_REPOSITORY }} | ||
| token: ${{ secrets.API_SPECS_PAT }} | ||
| path: api-specs | ||
|
|
||
| - name: Download all swagger specs | ||
| uses: actions/download-artifact@v5 | ||
| with: | ||
| pattern: openapi-spec-* | ||
| merge-multiple: true | ||
| path: temp-specs | ||
|
|
||
| - name: Merge all OpenAPI specs into all-in-one spec | ||
| run: | | ||
| npm install js-yaml | ||
| cat > merge-specs.js << 'SCRIPT_EOF' | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const yaml = require('js-yaml'); | ||
|
|
||
| const specDir = './temp-specs'; | ||
| const outputFile = path.join(specDir, 'openapi.all.yaml'); | ||
|
|
||
| const files = fs.readdirSync(specDir) | ||
| .filter(f => f.endsWith('.yaml')) | ||
| .sort(); | ||
|
|
||
| let merged = null; | ||
|
|
||
| for (const file of files) { | ||
| const content = yaml.load(fs.readFileSync(path.join(specDir, file), 'utf8')); | ||
| if (!merged) { | ||
| merged = JSON.parse(JSON.stringify(content)); | ||
| continue; | ||
| } | ||
| // Union merge paths (first definition wins for duplicates) | ||
| if (content.paths) { | ||
| merged.paths = merged.paths || {}; | ||
| for (const [p, def] of Object.entries(content.paths)) { | ||
| if (!merged.paths[p]) { | ||
| merged.paths[p] = def; | ||
| } | ||
| } | ||
| } | ||
| // Union merge components (first definition wins for duplicates) | ||
| if (content.components) { | ||
| merged.components = merged.components || {}; | ||
| for (const [section, defs] of Object.entries(content.components)) { | ||
| if (!merged.components[section]) { | ||
| merged.components[section] = defs; | ||
| } else { | ||
| for (const [name, def] of Object.entries(defs)) { | ||
| if (!merged.components[section][name]) { | ||
| merged.components[section][name] = def; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // Union merge tags (by name) | ||
| if (content.tags) { | ||
| merged.tags = merged.tags || []; | ||
| const existingTagNames = new Set(merged.tags.map(t => t.name)); | ||
| for (const tag of content.tags) { | ||
| if (!existingTagNames.has(tag.name)) { | ||
| merged.tags.push(tag); | ||
| existingTagNames.add(tag.name); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fs.writeFileSync(outputFile, yaml.dump(merged, { lineWidth: -1 })); | ||
| console.log(`Merged ${files.length} specs into ${outputFile}`); | ||
| SCRIPT_EOF | ||
| node merge-specs.js | ||
|
|
||
| - name: Create specs directory structure | ||
| run: | | ||
| VERSION=${{ github.event_name == 'release' && env.RELEASE_VERSION || 'master' }} | ||
|
|
||
| for SPEC_FILE in temp-specs/*; do | ||
| if [ -f "$SPEC_FILE" ]; then | ||
| FILENAME=$(basename "$SPEC_FILE") | ||
|
|
||
| # Handle MUD spec specially | ||
| if [ "$FILENAME" = "openapi.mud.yaml" ]; then | ||
| mkdir -p "api-specs/blockscout/${VERSION}/mud" | ||
| cp "$SPEC_FILE" "api-specs/blockscout/${VERSION}/mud/swagger.yaml" | ||
| else | ||
| # Extract chain type from filename (openapi.CHAINTYPE.yaml) | ||
| CHAIN_TYPE=$(echo "$FILENAME" | sed 's/openapi\.\(.*\)\.yaml/\1/') | ||
| mkdir -p "api-specs/blockscout/${VERSION}/${CHAIN_TYPE}" | ||
| cp "$SPEC_FILE" "api-specs/blockscout/${VERSION}/${CHAIN_TYPE}/swagger.yaml" | ||
| fi | ||
| fi | ||
| done | ||
|
|
||
|
|
||
| - name: Commit and push changes | ||
| working-directory: api-specs | ||
| run: | | ||
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | ||
| git config --local user.name "github-actions[bot]" | ||
|
|
||
| git add . | ||
|
|
||
| # Only commit if there are changes | ||
| if git diff --staged --quiet; then | ||
| echo "No changes to commit" | ||
| else | ||
| git commit -m "[SKIP-GH-PAGES] create OpenAPI specs for Blockscout ${{ github.event_name == 'release' && env.RELEASE_VERSION || github.sha }}" | ||
| git push | ||
| fi | ||
|
|
||
| - name: Clean up | ||
| if: always() | ||
| run: | | ||
| rm -rf temp-specs | ||
| rm -rf api-specs |
There was a problem hiding this comment.
Code Review
This pull request transitions Blockscout to version 11.0.0, featuring a major refactor of internal transactions to utilize address IDs and the introduction of asynchronous CSV exports via Oban and Gokapi. The changes also include new legacy API wrappers, license updates, and the removal of Polygon zkEVM support. Feedback identifies critical issues including unresolved merge conflict markers in the internal transactions runner and a bug in the batch preloading logic for contract creation transactions. Furthermore, an improvement opportunity was noted to resolve N+1 query performance issues during data preparation.
| <<<<<<< HEAD | ||
| alias Explorer.Utility.MissingRangesManipulator | ||
| ======= | ||
| alias Explorer.Utility.{AddressIdToAddressHash, MissingBlockRange} | ||
| >>>>>>> v11.0.0 |
There was a problem hiding this comment.
| contract_creation_internal_transaction_preload_query() | ||
| |> InternalTransaction.where_address_match(:created_contract_address, address_hashes) | ||
| |> repo.all() | ||
| |> InternalTransaction.preload_addresses([], repo) |
There was a problem hiding this comment.
The batch preloading logic for contract_creation_internal_transaction is incorrect. It uses contract_creation_internal_transaction_preload_query/0, which includes a limit(1) clause. This causes the query to return at most one internal transaction for the entire list of addresses, rather than one per address. To fix this for a batch of addresses, you should use a query that fetches the top result for each address hash (e.g., using DISTINCT ON in PostgreSQL).
| AddressIdToAddressHash.id_to_hash(Map.get(trace, :created_contract_address_id)), | ||
| error: TransactionError.id_to_error(Map.get(trace, :error_id)), |
There was a problem hiding this comment.
These calls to AddressIdToAddressHash.id_to_hash/1 and TransactionError.id_to_error/1 inside an Enum.map loop introduce N+1 query problems. Since the IDs are already available in the valid_internal_transactions list, you should batch load the corresponding hashes and error messages before entering the loop to improve performance.
f1d8cf5 to
ccf92f8
Compare
ccf92f8 to
dd6a2f8
Compare
Upstream Sync - v11.0.0
Auto-merge with upstream
v11.0.0failed. Version/workflow conflicts were auto-resolved,but the following files have code conflicts that need manual resolution:
To resolve:
v11.0.0to trigger Docker buildUpstream release notes