Skip to content
Open
49 changes: 49 additions & 0 deletions .github/actions/setup-sbpf-linker/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Set up sbpf-linker against checkout sbpf crates
description: >
Installs the toolchain, points the sbpf-linker checkout in ./sbpf-linker
at the sbpf crates from the workspace root, then verifies they resolve locally.

runs:
using: composite
steps:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: rust-src

- uses: Swatinem/rust-cache@v2
with:
workspaces: sbpf-linker
cache-on-failure: true

- name: Install FileCheck
shell: bash
run: sudo apt-get update && sudo apt-get install -y llvm-18-tools

- name: Point sbpf deps at this checkout
shell: bash
working-directory: sbpf-linker
run: |
for crate in assembler common; do
cargo add --path "../crates/$crate"
done

- name: Verify sbpf deps resolve locally
shell: bash
working-directory: sbpf-linker
run: |
cargo metadata --format-version 1 > "$RUNNER_TEMP/linker-metadata.json"
checkout_root="$(realpath ..)"
for crate in assembler common; do
expected_manifest="$checkout_root/crates/$crate/Cargo.toml"
if ! jq -e --arg name "sbpf-$crate" --arg manifest "$expected_manifest" '
[.resolve.nodes[].id] as $resolved
| [.packages[]
| select(.id as $id | $resolved | index($id))
| select(.name == $name)]
| length > 0 and all(.[]; .source == null and .manifest_path == $manifest)
' "$RUNNER_TEMP/linker-metadata.json"; then
echo "::error::sbpf-$crate must resolve only to $expected_manifest — check the cargo add --path step"
exit 1
fi
done
123 changes: 123 additions & 0 deletions .github/workflows/downstream-sbpf-linker-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Test sbpf-linker fork

# Worflow for testing the specified sbpf-linker fork against the PR's sbpf crates.
# Usage: `/test-sbpf-linker <owner>/<repo> <ref>`

on:
issue_comment:
types: [created]

env:
CARGO_TERM_COLOR: always

jobs:
test-contributor-fork:
name: Test contributor fork of sbpf-linker
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read # checkouts
statuses: write # pending/success/failure on the PR head commit
pull-requests: write # result comment on the PR
if: >
github.event.issue.pull_request &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, '/test-sbpf-linker')

steps:
- name: Parse and validate command
id: parse
env:
COMMENT: ${{ github.event.comment.body }}
run: |
read -r command repository ref extra <<< "$COMMENT"

if [[ "$command" != "/test-sbpf-linker" ]]; then
echo "Invalid command"
exit 1
fi

if [[ -z "$repository" || -z "$ref" || -n "$extra" ]]; then
echo "Usage: /test-sbpf-linker <owner>/<repo> <ref>"
exit 1
fi

if [[ ! "$repository" =~ ^[A-Za-z0-9_-.]+/[A-Za-z0-9_-.]+$ ]]; then
echo "Invalid repository: $repository"
exit 1
fi

echo "repository=$repository" >> "$GITHUB_OUTPUT"
echo "ref=$ref" >> "$GITHUB_OUTPUT"

# we need this to get into the PR branch or else github.event.issue runs on default branch
- name: Get PR head
uses: actions/github-script@v9
id: comment-branch
with:
script: |
const pr = await github.rest.pulls.get({ ...context.repo, pull_number: context.issue.number });
core.setOutput('head_sha', pr.data.head.sha);

# same logic as above but for the commit, and put the status as pending as a remembering
- name: Set latest commit status as pending
uses: actions/github-script@v9
with:
script: |
await github.rest.repos.createCommitStatus({
...context.repo,
sha: '${{ steps.comment-branch.outputs.head_sha }}',
state: 'pending',
context: context.workflow,
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});

- name: Checkout PR branch
uses: actions/checkout@v5
with:
ref: ${{ steps.comment-branch.outputs.head_sha }}
persist-credentials: false

- name: Checkout sbpf-linker
uses: actions/checkout@v5
with:
repository: ${{ steps.parse.outputs.repository }}
ref: ${{ steps.parse.outputs.ref }}
path: sbpf-linker
persist-credentials: false

- uses: ./.github/actions/setup-sbpf-linker

- name: Run sbpf-linker test suite
working-directory: sbpf-linker
run: cargo test -- --nocapture

- name: Set latest commit status as ${{ job.status }}
uses: actions/github-script@v9
if: always()
with:
script: |
await github.rest.repos.createCommitStatus({
...context.repo,
sha: '${{ steps.comment-branch.outputs.head_sha }}',
state: '${{ job.status == 'success' && 'success' || 'failure' }}',
context: context.workflow,
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});

- name: Add a comment to PR
uses: actions/github-script@v9
if: always()
with:
script: |
const name = '${{ github.workflow }}';
const url = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}';
const success = '${{ job.status }}' === 'success';
const body = `${name}: ${success ? 'succeeded ✅' : 'failed ❌'}\n${url}`;

await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})
113 changes: 113 additions & 0 deletions .github/workflows/downstream-sbpf-linker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: downstream

# Build and test sbpf-linker at sbpf-linker-next HEAD against the sbpf
# crates in this checkout, so API breakage is caught here before a release is published.

on:
push:
branches: [master]
pull_request:

env:
CARGO_TERM_COLOR: always

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
sbpf-linker:
name: Sbpf-linker against local sbpf crates
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
steps:
- uses: actions/checkout@v5 # put sbpf repo into the workspace

- name: Checkout sbpf-linker
id: linker-checkout
uses: actions/checkout@v5
with:
repository: blueshift-gg/sbpf-linker
ref: sbpf-linker-next
path: sbpf-linker

- name: Checkout failed
if: ${{ failure() && steps.linker-checkout.outcome == 'failure' }}
run: |
echo "::error title=sbpf-linker checkout failed::See the job summary for likely causes."

cat >> "$GITHUB_STEP_SUMMARY" <<'EOF'
## Sbpf-linker Checkout failed

This job checks out sbpf-linker and puts it in the workspace.

Review the action logs to identify the cause. Possible reasons are:
1. sbpf-linker-next ref is missing
2. GitHub internal issue
EOF

- uses: ./.github/actions/setup-sbpf-linker
id: linker-setup

- name: Setup failed
if: ${{ failure() && steps.linker-setup.outcome == 'failure' }}
run: |
echo "::error title=sbpf-linker setup failed::See the job summary for likely causes."

cat >> "$GITHUB_STEP_SUMMARY" <<'EOF'
## Configuring sbpf-linker failed

This job installs required workspace dependencies and points sbpf-linker's sbpf deps at this checkout, then verifies they resolve locally.

Review the action logs to identify the cause. Possible reasons are:
1. Toolchain or FileCheck install failed.
2. Sbpf deps didn't resolve to this checkout.
EOF

- name: Run sbpf-linker compilation
id: linker-compilation
working-directory: sbpf-linker
run: cargo test --no-run

- name: Sbpf-linker compilation failed
if: ${{ failure() && steps.linker-compilation.outcome == 'failure' }}
run: |
echo "::error title=sbpf-linker compilation failed::See the job summary for likely causes."

cat >> "$GITHUB_STEP_SUMMARY" <<'EOF'
## Compiling sbpf-linker failed

This job tries to compile sbpf-linker with the sbpf crates from this checkout.

If your changes touched sbpf API level, they could have broken compatibility with sbpf-linker. Review the logs to identify the cause.
If so, you must submit a PR to adapt sbpf-linker to these changes.

See [Compatibility with sbpf-linker](https://github.com/blueshift-gg/sbpf/blob/master/CONTRIBUTING.md#compatibility-with-sbpf-linker) for the contribution process.
EOF

- name: Run sbpf-linker test suite
id: linker-tests
working-directory: sbpf-linker
run: cargo test -- --nocapture

- name: Sbpf-linker test suite failed
if: ${{ failure() && steps.linker-tests.outcome == 'failure' }}
Comment thread
BretasArthur1 marked this conversation as resolved.
run: |
echo "::error title=sbpf-linker tests failed::See the job summary for likely causes."

cat >> "$GITHUB_STEP_SUMMARY" <<'EOF'
## Sbpf-linker test suite failed

This job tests sbpf-linker against the sbpf crates from this checkout.

The possible reasons for this failure are:

1. API breakage. If your changes touched sbpf API level, they could have broken compatibility with sbpf-linker. See [Compatibility with sbpf-linker](https://github.com/blueshift-gg/sbpf/blob/master/CONTRIBUTING.md#compatibility-with-sbpf-linker) for the contribution process.
2. Bug in your PR that your own tests missed. Fix the PR, and add a test to sbpf.
3. Your change exposed an old sbpf bug. Fix sbpf, possibly as a separate PR.
4. Your change exposed an old sbpf-linker bug. Fix sbpf-linker.

Triage hint: Does the failing test pass against sbpf master? If yes, your change caused or exposed it.
EOF
44 changes: 44 additions & 0 deletions .github/workflows/set-commit-status.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Mark failed downstream linker test

on:
workflow_run:
workflows: [downstream]
types: [completed]

permissions:
actions: read
statuses: write # write commit status

jobs:
set-status:
if: >-
${{ github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v9
with:
script: |
const run = context.payload.workflow_run;
const { data } = await github.rest.actions.listJobsForWorkflowRun({
...context.repo,
run_id: run.id,
});

const linkerJob = data.jobs.find(
job => job.name === 'Sbpf-linker against local sbpf crates'
);
const linkerTestFailed = linkerJob?.steps?.some(
step => step.name === 'Run sbpf-linker test suite' &&
step.conclusion === 'failure'
);

if (!linkerTestFailed) return;

await github.rest.repos.createCommitStatus({
...context.repo,
sha: run.head_sha,
state: 'pending',
context: 'Test sbpf-linker fork',
description: 'Dispatches a test workflow to test against users fork',
});
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,23 @@ For minor bugs that are solvable in a single-issue PR, feel free to immediately

## Nits/Typos
Feel free to directly [open a PR](https://github.com/blueshift-gg/sbpf/compare).

## Compatibility with sbpf-linker

[sbpf-linker](https://github.com/blueshift-gg/sbpf-linker) is a downstream consumer of the sbpf API. It relinks BPF binaries into its sbpf-compatible form, so changes to this repository must account for compatibility with the linker.

The [downstream CI job](https://github.com/blueshift-gg/sbpf/blob/master/.github/workflows/downstream-sbpf-linker.yml) runs on every PR against sbpf-linker. If your changes break compatibility, submit a PR to sbpf-linker that ports the changes, following these steps:

1. Clone sbpf-linker and create a feature branch based on the [downstream gate branch](https://github.com/blueshift-gg/sbpf-linker/tree/sbpf-linker-next).
2. Update both [sbpf dependencies](https://github.com/blueshift-gg/sbpf-linker/blob/8edec876120bcc3e0679d5636b724ddd994df1cc/Cargo.toml#L18-L19) to point to your sbpf crates version (you can also specify a commit with rev tag).
3. Adapt the linker to your changes and verify that its tests pass.
4. Open a companion PR and link it from your sbpf PR so maintainers can coordinate merging both.

### Other Possible Failures:

1. If the checkout or setup step fails, your change isn't the cause. Re-run the job, and ping a maintainer if it keeps failing.
2. Bug in your PR that your own tests missed. Fix the PR, and add a test to sbpf.
3. Your change exposed an old sbpf bug. Fix sbpf, possibly as a separate PR.
4. Your change exposed an old sbpf-linker bug. Fix sbpf-linker.

*For test failures: Does the failing test pass against sbpf master? If yes, your change caused or exposed it.*
Loading