Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
40 changes: 40 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Patterns are path-anchored under packages/ rather than `**/<name>/` because
# BuildKit treats `<name>/` as matching files too, so `**/build/` would also
# hide the per-package `scripts/build` entry scripts.

.git
.github
**/node_modules
.pnpm-store

# Build outputs (regenerated by `pnpm build`).
packages/*/artifacts
packages/*/cache
packages/*/cache_forge
packages/*/forge-artifacts
packages/*/out
packages/*/dist
packages/*/build
packages/*/typechain-types
packages/*/coverage
packages/*/types

# Forge crash dumps (also gitignored). Match only the per-package `core` file at the
# package root — NOT `**/core`, which would also exclude `packages/toolshed/src/core/`
# and other directories named `core` deeper in the tree.
packages/*/core

# Editor / OS noise
.vscode
.idea
.DS_Store

# Local env / addresses (must not leak into a published image)
.env
**/.env
**/addresses-local*.json
**/localNetwork.json
**/.keystore

# Docs and audit PDFs aren't needed at runtime
docs
8 changes: 3 additions & 5 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Setup
description: Install system deps, Foundry, Node.js, pnpm, and the workspace's dependencies.

runs:
using: composite
Expand All @@ -15,13 +16,10 @@ runs:
shell: bash
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Set up pnpm via Corepack
shell: bash
run: corepack prepare pnpm@10.17.0 --activate
- name: Install dependencies
shell: bash
run: pnpm install --frozen-lockfile
9 changes: 4 additions & 5 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ env:

on:
pull_request:
branches: '*'
workflow_dispatch:

jobs:
Expand All @@ -15,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive

Expand All @@ -34,14 +33,14 @@ jobs:
- name: Find coverage files
id: coverage_files
run: |
# Find all coverage-final.json files
COVERAGE_FILES=$(find ./packages -name "coverage-final.json" -path "*/coverage/*" | tr '\n' ',' | sed 's/,$//')
# Find coverage files: Istanbul JSON (Hardhat) and lcov (Forge)
COVERAGE_FILES=$(find ./packages \( -name "coverage-final.json" -o -name "lcov.info" \) -path "*/coverage/*" | tr '\n' ',' | sed 's/,$//')
echo "files=$COVERAGE_FILES" >> $GITHUB_OUTPUT
echo "Found coverage files: $COVERAGE_FILES"

- name: Upload coverage reports
if: steps.coverage_files.outputs.files != ''
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v6
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ${{ steps.coverage_files.outputs.files }}
Expand Down
370 changes: 135 additions & 235 deletions .github/workflows/lint.yml

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Builds the workspace image multi-arch (linux/amd64 + linux/arm64) and pushes
# to ghcr.io/graphprotocol/contracts. Consumed by local-network's
# graph-contracts wrapper via CONTRACTS_VERSION (pin a `:sha-<short>` for
# reproducibility).
#
# Native runner per platform (no QEMU), per-platform digest push, manifest
# merge in a separate job. Runs independently of build-test.yml — they share
# `pnpm install + pnpm build` but stay decoupled so test feedback isn't tied
# to release packaging.

name: Publish container image

permissions:
contents: read

on:
workflow_dispatch:
push:
branches:
- main
- 'deployment/**'
tags:
- 'v*'

env:
IMAGE: ghcr.io/graphprotocol/contracts

jobs:
build:
name: Build (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-24.04
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Prepare platform pair
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive

- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker labels
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: ${{ env.IMAGE }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
pull: true
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}

- name: Export digest
if: github.event_name != 'pull_request'
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: github.event_name != 'pull_request'
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

merge:
name: Merge into multi-arch manifest
needs: build
# Avoids orphan per-platform digest blobs if a `need` is skipped or fails.
if: |
!cancelled()
&& needs.build.result == 'success'
&& github.event_name != 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true

- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker tags
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: ${{ env.IMAGE }}
tags: |
type=ref,event=tag
# Moving per-branch tag for casual use; pin :sha-<short> for reproducibility.
type=ref,event=branch
# Ensures workflow_dispatch from any branch yields a usable tag.
type=sha,enable=true

# Glob `*` expands to the digest-named files from the build job.
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.IMAGE }}@sha256:%s ' *)

- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.meta.outputs.version }}
51 changes: 46 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# Publishes a single workspace package to npm via OIDC trusted publishing.
# No NPM_TOKEN is used; the job exchanges its GitHub OIDC token for a short-lived
# npm credential and attaches SLSA provenance via `--provenance`.
#
# Prerequisite: each package in the `package` choice list must have a Trusted
# Publisher entry configured on npmjs.com under Settings → Publishing access.
# Use owner=`graphprotocol`, repo=`contracts`, workflow=`publish.yml`,
# environment=blank. Adding a package to the choice list without that npm-side
# entry will 403 at the publish step.
#
# Conventions:
# - `tag=latest` is reserved for the changeset-driven release flow (see README).
# Use a custom dist-tag (`dips`, `sepolia`, `next`, …) for ad-hoc or
# pre-release publishes so the stable channel is never overwritten.
# - `dry_run=true` validates the workflow end-to-end without consuming a
# version or pushing a git tag — useful when adding a new package or
# verifying a fresh Trusted Publisher entry.
#
# Fallback: maintainers with publish rights on `@graphprotocol/*` can still run
# `pnpm publish` locally if OIDC is unavailable.

name: Publish package to NPM

on:
Expand All @@ -8,29 +29,49 @@ on:
required: true
type: choice
options:
- address-book
- contracts
- sdk
- interfaces
- toolshed
tag:
description: 'Tag to publish'
required: true
type: string
default: latest
dry_run:
description: 'Dry-run (validate only, no publish or git tag)'
required: false
type: boolean
default: false

jobs:
publish:
name: Publish package
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up environment
uses: ./.github/actions/setup
- name: Set npm token for publishing
run: pnpm config set //registry.npmjs.org/:_authToken ${{ secrets.GRAPHPROTOCOL_NPM_TOKEN }}
- name: Read package info
id: pkg
shell: bash
run: |
PKG_NAME=$(node -p "require('./packages/${{ inputs.package }}/package.json').name")
PKG_VERSION=$(node -p "require('./packages/${{ inputs.package }}/package.json').version")
echo "tag=${PKG_NAME}@${PKG_VERSION}" >> $GITHUB_OUTPUT
- name: Publish 🚀
shell: bash
run: |
pushd packages/${{ inputs.package }}
pnpm publish --tag ${{ inputs.tag }} --access public --no-git-checks
pnpm publish --provenance --tag ${{ inputs.tag }} --access public --no-git-checks ${{ inputs.dry_run && '--dry-run' || '' }}
- name: Tag release
if: ${{ !inputs.dry_run }}
run: |
git tag ${{ steps.pkg.outputs.tag }}
git push origin ${{ steps.pkg.outputs.tag }}
56 changes: 56 additions & 0 deletions .github/workflows/require-audit-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Require Audit Label

on:
pull_request:
branches: [main]
types: [opened, labeled, unlabeled, synchronize]

jobs:
check-label:
runs-on: ubuntu-latest
steps:
- name: Get changed files
id: changed
uses: actions/github-script@v9
with:
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 100
});

// Filter for .sol files, excluding tests
const solFiles = files
.map(f => f.filename)
.filter(f => f.endsWith('.sol'))
.filter(f => !f.includes('/test/'))
.filter(f => !f.includes('/tests/'))
.filter(f => !f.endsWith('.t.sol'));

console.log('Non-test Solidity files changed:', solFiles);
core.setOutput('has_sol_files', solFiles.length > 0);
core.setOutput('sol_files', solFiles.join('\n'));

- name: Check for required label
if: steps.changed.outputs.has_sol_files == 'true'
run: |
echo "Solidity files changed (excluding tests):"
echo "${{ steps.changed.outputs.sol_files }}"
echo ""

LABELS='${{ toJson(github.event.pull_request.labels.*.name) }}'
if echo "$LABELS" | grep -q '"audited"'; then
echo "✓ PR has 'audited' label"
else
echo "::error::This PR modifies Solidity contract files and must have the 'audited' label before merging to main."
echo ""
echo "If this code has been audited, add the 'audited' label to proceed."
exit 1
fi

- name: Skip check (no contract changes)
if: steps.changed.outputs.has_sol_files == 'false'
run: |
echo "✓ No non-test Solidity files changed, skipping audit label check"
Loading