forked from grafana/alloy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
265 additions
and
143 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Find Go fuzz tests is a reusable workflow that will find all Go fuzz tests in | ||
# a given directory. | ||
# | ||
# This workflow assumes that the code has been checked out. | ||
# | ||
# The output is a JSON array of objects with two keys: | ||
# * package: The package path of the test, relative to the search directory. | ||
# * function: The name of the fuzz function within the package. | ||
|
||
name: Find Go fuzz tests | ||
|
||
on: | ||
workflow_call: | ||
inputs: | ||
directory: | ||
description: "Directory to search for Go fuzz tests in." | ||
default: '.' | ||
required: false | ||
type: string | ||
outputs: | ||
tests: | ||
description: A JSON array of objects of tests, containing the keys "package" and "function". | ||
value: "${{ jobs.find-tests.outputs.tests }}" | ||
|
||
jobs: | ||
find-tests: | ||
runs-on: ubuntu-latest | ||
outputs: | ||
tests: ${{ steps.find-tests.outputs.tests }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Find fuzz tests | ||
id: find-tests | ||
run: | | ||
TEST_FILES=$(find "${{ inputs.directory }}" -name '*_test.go' -not -path './vendor/*') | ||
RESULTS=() | ||
for FILE in $TEST_FILES; do | ||
FUZZ_FUNC=$(grep -E 'func Fuzz\w*' $FILE | sed 's/func //' | sed 's/(.*$//') | ||
if [ -z "$FUZZ_FUNC" ]; then | ||
continue | ||
fi | ||
PACKAGE_PATH=$(dirname ${FILE#${{ inputs.directory }}/}) | ||
RESULTS+=("{\"package\":\"$PACKAGE_PATH\",\"function\":\"$FUZZ_FUNC\"}") | ||
echo "Found $FUZZ_FUNC in $PACKAGE_PATH" | ||
done | ||
NUM_RESULTS=${#RESULTS[@]} | ||
INCLUDE_STRING="" | ||
for (( i=0; i<$NUM_RESULTS; i++ )); do | ||
INCLUDE_STRING+="${RESULTS[$i]}" | ||
if [[ $i -lt $(($NUM_RESULTS-1)) ]]; then | ||
INCLUDE_STRING+="," | ||
fi | ||
done | ||
echo 'tests=['$INCLUDE_STRING']' >> $GITHUB_OUTPUT |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Run Go fuzz tests (PR) | ||
on: | ||
pull_request: | ||
jobs: | ||
find-tests: | ||
name: Find fuzz tests | ||
uses: ./.github/workflows/fuzz-find-go.yml | ||
|
||
fuzz: | ||
name: "${{ matrix.package }}: ${{ matrix.function }}" | ||
needs: [find-tests] | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
include: ${{ fromJson(needs.find-tests.outputs.tests) }} | ||
uses: ./.github/workflows/fuzz-run-go.yml | ||
with: | ||
package-path: ${{ matrix.package }} | ||
test-name: ${{ matrix.function }} | ||
fuzz-time: 5s |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
name: Run Go fuzz tests (scheduled) | ||
on: | ||
workflow_dispatch: {} | ||
jobs: | ||
find-tests: | ||
name: Find fuzz tests | ||
uses: ./.github/workflows/fuzz-find-go.yml | ||
|
||
fuzz: | ||
name: "${{ matrix.package }}: ${{ matrix.function }}" | ||
runs-on: ubuntu-latest | ||
needs: [find-tests] | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
include: ${{ fromJson(needs.find-tests.outputs.tests) }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up Go 1.22 | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
cache: false | ||
|
||
- name: Run fuzz test | ||
id: fuzz | ||
uses: ./.github/workflows/fuzz-run-go.yml | ||
with: | ||
package-path: ${{ matrix.package }} | ||
test-name: ${{ matrix.function }} | ||
fuzz-time: 5s | ||
|
||
- name: Create new issue | ||
if: ${{ failure() && steps.fuzz.outputs.failure-name != '' }} | ||
uses: actions/github-script@v7 | ||
with: | ||
script: | | ||
const failureName = "${{ steps.fuzz.outputs.failure-name }}"; | ||
const issueTitle = `${{ matrix.package }}: ${{ matrix.function }} failed (${failureName})`; | ||
// Look for existing issue first with the same title. | ||
const issues = await github.rest.search.issuesAndPullRequests({ | ||
q: `is:issue is:open repo:${context.repo.owner}/${context.repo.repo} in:title "${failureName}"` | ||
}) | ||
console.log(issues); | ||
const issue = issues.data.items.find((issue) => issue.title === issue.title); | ||
if (issue) { | ||
return; | ||
} | ||
// Create a new issue. | ||
await github.rest.issues.create({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
title: issueTitle, | ||
body: ` | ||
A new fuzz test failure was found in <code>${{ matrix.package }}</code>. | ||
To reproduce the failure locally, run the following command using the GitHub CLI to download the corpus entry: | ||
<pre lang="bash">gh run download --repo ${{ github.repository }} ${{ github.run_id }} -n ${{ steps.fuzz.outputs.artifact-name }} --dir ${{ steps.fuzz.outputs.failure-path }}</pre> | ||
When opening a PR with the fix, please include in the corpus entry in your commit to prevent regressions. | ||
[Link to failed run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ||
`, | ||
labels: ['bug'], | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Run Go fuzz test is a reusable workflow that runs a single Go fuzz test. | ||
# | ||
# The workflow runs until fuzz-time has elapsed or a test case which causes the | ||
# test to fail is found. If a new failure is found, the workflow will fail and | ||
# upload the failing test case as an artifact. | ||
# | ||
# The workflow assumes that the code has already been checked out. | ||
|
||
name: Run Go fuzz test | ||
on: | ||
workflow_call: | ||
inputs: | ||
package-path: | ||
description: "Directory of the Go package to run a Fuzz test in." | ||
required: true | ||
type: string | ||
test-name: | ||
description: "Full name of the Fuzz test to run." | ||
required: true | ||
type: string | ||
fuzz-time: | ||
description: "Time to run the Fuzz test for. (for example, 5m)" | ||
required: true | ||
type: string | ||
|
||
outputs: | ||
arifact-name: | ||
description: "Name of the artifact that was uploaded. Only use when the workflow fails." | ||
value: ${{ jobs.fuzz.outputs.artifact-name }} | ||
failure-name: | ||
description: "Name of the test case that failed. Only use when the workflow fails." | ||
value: ${{ jobs.fuzz.outputs.failure-name }} | ||
failure-path: | ||
description: "Path to place the failure artifact. Only use when the workflow fails." | ||
value: ${{ inputs.package-path }}/testdata/fuzz/${{ inputs.test-name }} | ||
|
||
jobs: | ||
fuzz: | ||
runs-on: ubuntu-latest | ||
outputs: | ||
artifact-name: failure-${{ steps.new-failure.outputs.package }}-${{ steps.new-failure.outputs.function }} | ||
failure-name: ${{ steps.new-failure.outputs.name }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up Go 1.22 | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
cache: false | ||
|
||
- name: Find cache location | ||
run: | ||
echo "FUZZ_CACHE=$(go env GOCACHE)/fuzz" >> $GITHUB_ENV | ||
|
||
- name: Restore fuzz cache | ||
uses: actions/cache@v4 | ||
with: | ||
path: ${{ env.FUZZ_CACHE }} | ||
key: fuzz-${{ inputs.package-path }}-${{ inputs.test-name }}-${{ github.sha }} | ||
restore-keys: | | ||
fuzz-${{ inputs.package-path }}-${{ inputs.test-name }}- | ||
- name: Fuzz | ||
run: | | ||
# Change directory to the package first, since go test doesn't | ||
# support cross-module testing, and the provided directory may be in | ||
# a different module. | ||
cd "${{ inputs.package-path }}" | ||
go test -fuzz="${{ inputs.test-name }}\$" -run="${{ inputs.test-name }}\$" -fuzztime="${{ inputs.fuzz-time }}" . | ||
# Fuzzing may have failed because of an existing bug, or it may have | ||
# found a new one and written a new corpus entry in testdata/ relative to | ||
# the package. | ||
# | ||
# If that file was written, we should save it as an artifact and then | ||
# create an issue. | ||
|
||
- name: Check for new fuzz failure | ||
id: new-failure | ||
if: ${{ failure() }} | ||
run: | | ||
UNTRACKED=$(git ls-files . --exclude-standard --others) | ||
if [ -z "$UNTRACKED" ]; then | ||
exit 0 | ||
fi | ||
echo "Found new fuzz failure: $UNTRACKED" | ||
echo "file=$UNTRACKED" >> $GITHUB_OUTPUT | ||
echo "name=$(basename $UNTRACKED)" >> $GITHUB_OUTPUT | ||
echo "package=$(echo ${{ inputs.package-path }} | sed 's/\//_/g')" >> $GITHUB_OUTPUT | ||
echo "function=${{ inputs.test-name }}" >> $GITHUB_OUTPUT | ||
- name: Upload fuzz failure as artifact | ||
id: artifact | ||
if: ${{ failure() && steps.new-failure.outputs.file != '' }} | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: failure-${{ steps.new-failure.outputs.package }}-${{ steps.new-failure.outputs.function }} | ||
path: ${{ steps.new-failure.outputs.file }} | ||
|
||
- name: Generate reproduction instructions | ||
if: ${{ failure() && steps.new-failure.outputs.file != '' }} | ||
run: | | ||
cat >>$GITHUB_STEP_SUMMARY <<EOF | ||
## Fuzz test failed | ||
A new fuzz test failure was found in ${{ inputs.package-path }}. | ||
To reproduce the failure locally, run the following command using the GitHub CLI to download the failed test case: | ||
<pre>gh run download --repo ${{ github.repository }} ${{ github.run_id }} -n failure-${{ steps.new-failure.outputs.package }}-${{ steps.new-failure.outputs.function }} --dir ${{ inputs.package-path }}/testdata/fuzz/${{ inputs.test-name }}</pre> | ||
When opening a PR with the fix, please include the test case file in your PR to prevent regressions. | ||
EOF |
Oops, something went wrong.