Implement CI improvements outlined in #3925 #6
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: hardhat CI | |
on: | |
push: | |
branches: | |
- main | |
pull_request: | |
workflow_dispatch: | |
concurrency: | |
group: ${{github.workflow}}-${{github.ref}} | |
cancel-in-progress: true | |
jobs: | |
packages: | |
name: Find packages to test | |
runs-on: ubuntu-latest | |
outputs: | |
packages: ${{ steps.packages.outputs.result }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Fetch files modified in push event | |
if: github.event_name == 'push' | |
env: | |
BEFORE: ${{ github.event.before }} | |
AFTER: ${{ github.event.after }} | |
run: git diff --name-only $BEFORE $AFTER | tee files.txt | |
- name: Parse pnpm lock file | |
run: yq -p yaml -o json pnpm-lock.yaml | tee pnpm-lock.json | |
- uses: actions/github-script@v7 | |
name: Find packages to test | |
id: packages | |
env: | |
EXCLUDED_PACKAGES: | | |
. | |
packages/hardhat-core | |
packages/common | |
packages/eslint-plugin-hardhat-internal-rules | |
packages/eslint-plugin-slow-imports | |
COMMON_DEPENDENCIES: | | |
packages/common | |
config | |
WORKFLOW_FILE: .github/workflows/hardhat-ci.yml | |
with: | |
script: | | |
const fs = require('fs'); | |
const excludedPackages = process.env.EXCLUDED_PACKAGES.split('\n').filter(p => p); | |
const commonDependencies = process.env.COMMON_DEPENDENCIES.split('\n').filter(p => p); | |
const workflowFile = process.env.WORKFLOW_FILE; | |
const event = context.eventName; | |
const modifiedFiles = []; | |
if (event === 'pull_request') { | |
core.info('This is a pull request event') | |
const pullNumber = context.issue.number; | |
core.info(`Fetching files for pull request #${pullNumber}`); | |
const files = await github.paginate(github.rest.pulls.listFiles, { | |
...context.issue, | |
pull_number: pullNumber | |
}); | |
modifiedFiles.push(...files.map(file => file.filename)); | |
core.info(`Modified files: ${JSON.stringify(modifiedFiles)}`); | |
} else if (event === 'push') { | |
core.info('This is a push event') | |
core.info(`Fetching files modified between ${context.payload.before} and ${context.payload.after}`) | |
const files = fs.readFileSync('files.txt', 'utf8').split('\n').filter(f => f); | |
modifiedFiles.push(...files); | |
core.info(`Modified files: ${JSON.stringify(modifiedFiles)}`); | |
} | |
core.info('Checking if workflow file was modified') | |
const isWorkflowFileModified = modifiedFiles.includes(workflowFile); | |
core.info(`Workflow file was modified: ${isWorkflowFileModified}`) | |
core.info('Reading pnpm lock file') | |
const lock = JSON.parse(fs.readFileSync('pnpm-lock.json', 'utf8')); | |
const packagesToTest = []; | |
core.info('Finding packages to test') | |
for (const [package, dependencies] of Object.entries(lock.importers)) { | |
core.info(`Checking package ${package}`) | |
if (excludedPackages.includes(package)) { | |
core.info(`Package ${package} is excluded, skipping`) | |
continue; | |
} | |
if (event === 'workflow_dispatch' || isWorkflowFileModified) { | |
core.info(`Workflow was dispatched or workflow file was modified, testing package ${package}`) | |
packagesToTest.push(package); | |
continue; | |
} | |
core.info(`Finding internal dependencies for package ${package} (only direct; not transitive)`) | |
const internalDependencies = Object.values(dependencies) | |
.flatMap(d => Object.values(d)) | |
.map(d => d.version) | |
.filter(dependency => dependency.startsWith('link:../')) | |
.map(dependency => dependency.replace('link:../', 'packages/')); | |
core.info(`Internal dependencies for package ${package}: ${JSON.stringify(internalDependencies)}`) | |
core.info(`Checking if package ${package} or its internal dependencies were modified`) | |
const prefixes = [ | |
package, | |
...internalDependencies, | |
...commonDependencies, | |
].map(p => `${p}/`); | |
const isPackageModified = prefixes.some(p => modifiedFiles.some(f => f.startsWith(p))); | |
core.info(`Package ${package} or its internal dependencies were modified: ${isPackageModified}`) | |
if (isPackageModified) { | |
core.info(`Package ${package} or its internal dependencies were modified, testing package ${package}`) | |
packagesToTest.push(package); | |
} | |
} | |
core.info(`Packages to test: ${JSON.stringify(packagesToTest)}`) | |
return packagesToTest.map(p => p.replace('packages/', '')); | |
test: | |
needs: [packages] | |
name: Test ${{ matrix.package }} on ${{ matrix.runner }} with Node ${{ matrix.node_version }} | |
runs-on: ${{ matrix.runner }} | |
if: needs.packages.outputs.packages != '[]' | |
strategy: | |
fail-fast: false | |
matrix: | |
package: ${{ fromJSON(needs.packages.outputs.packages) }} | |
runner: [windows-latest, macos-latest, ubuntu-latest] | |
node_version: [18, 20, 22] | |
exclude: | |
- package: hardhat-vyper | |
runner: windows-latest | |
- package: hardhat-vyper | |
runner: macos-latest | |
- runner: windows-latest | |
node_version: 20 | |
- runner: macos-latest | |
node_version: 20 | |
- runner: windows-latest | |
node_version: 22 | |
- runner: macos-latest | |
node_version: 22 | |
defaults: | |
run: | |
working-directory: packages/${{ matrix.package }} | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: ./.github/actions/setup-env | |
with: | |
node-version: ${{ matrix.node_version }} | |
- name: Install | |
run: pnpm install --frozen-lockfile --prefer-offline | |
- name: Build | |
run: pnpm build | |
- name: Run tests | |
env: | |
FORCE_COLOR: 3 | |
run: pnpm ${{ matrix.package == 'hardhat-chai-matchers' && 'test:ci' || 'test' }} |