Skip to content

Commit 6257d82

Browse files
committed
add new comparasion for binary size in prs
1 parent 16d92f8 commit 6257d82

6 files changed

Lines changed: 332 additions & 1 deletion

File tree

.github/workflows/component_packages.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ on:
2222
pfx_passphrase:
2323
description: 'Passphrase of the PFX certificate'
2424
required: false
25+
nr_account_id:
26+
description: 'New Relic account ID to report binary sizes to (optional, skips reporting if unset)'
27+
required: false
28+
nr_license_key:
29+
description: 'New Relic license key used as the Events API ingest key (optional, skips reporting if unset)'
30+
required: false
31+
nr_user_api_key:
32+
description: 'New Relic User API key (NerdGraph), used to look up the previous build size and detect growth (optional, skips the growth check if unset)'
33+
required: false
34+
slack_webhook_url:
35+
description: 'Slack webhook to warn on when a binary grows more than 10% versus its previous build (optional, skips the Slack warning if unset)'
36+
required: false
2537
inputs:
2638
pre-release:
2739
description: 'set to true if running a real pre-release'
@@ -41,6 +53,8 @@ jobs:
4153
build:
4254
runs-on: ubuntu-latest
4355
name: Build and upload
56+
outputs:
57+
binary-size-warnings: ${{ steps.binary-size.outputs.warnings }}
4458
steps:
4559
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
4660
with:
@@ -117,6 +131,32 @@ jobs:
117131
GORELEASER_CURRENT_TAG: ${{ inputs.tag_name }}
118132
SKIP_WINDOWS_SIGN: ${{ env.SKIP_WINDOWS_SIGN }}
119133

134+
- name: Report binary sizes to New Relic
135+
id: binary-size
136+
env:
137+
BINARY_VERSION: ${{ inputs.tag_name }}
138+
NR_ACCOUNT_ID: ${{ secrets.nr_account_id }}
139+
NR_LICENSE_KEY: ${{ secrets.nr_license_key }}
140+
NR_USER_API_KEY: ${{ secrets.nr_user_api_key }}
141+
WARNINGS_FILE: binary-size-warnings.txt
142+
run: |
143+
: > "$WARNINGS_FILE"
144+
for path in dist/*/newrelic-agent-control dist/*/newrelic-agent-control-cli dist/*/newrelic-agent-control.exe dist/*/newrelic-agent-control-cli.exe; do
145+
[ -f "$path" ] || continue
146+
dir=$(basename "$(dirname "$path")")
147+
export BINARY_NAME=$(basename "$path" .exe)
148+
export BINARY_TARGET="${dir#*_}"
149+
export BINARY_PATH="$path"
150+
bash .github/workflows/scripts/report_binary_size.sh
151+
done
152+
if [ -s "$WARNINGS_FILE" ]; then
153+
{
154+
echo "warnings<<EOF"
155+
cat "$WARNINGS_FILE"
156+
echo "EOF"
157+
} >> "$GITHUB_OUTPUT"
158+
fi
159+
120160
- name: Upload assets to pipeline
121161
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
122162
with:
@@ -126,6 +166,16 @@ jobs:
126166
./bin/*
127167
./dist/*
128168
169+
notify-binary-size-growth:
170+
name: Warn on binary size growth
171+
needs: build
172+
if: needs.build.outputs.binary-size-warnings != ''
173+
uses: ./.github/workflows/component_send_warning_via_slack.yml
174+
with:
175+
message: ${{ needs.build.outputs.binary-size-warnings }}
176+
secrets:
177+
slack_webhook_url: ${{ secrets.slack_webhook_url }}
178+
129179
verify-windows-signatures:
130180
runs-on: windows-latest
131181
name: Verify Windows signatures

.github/workflows/component_send_warning_via_slack.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
description: 'The message to include in the Slack notification'
88
required: true
99
type: string
10+
secrets:
11+
slack_webhook_url:
12+
description: 'Slack webhook URL. Optional: falls back to AC_SLACK_WEBHOOK, which callers using `secrets: inherit` (e.g. nightly.yml) already get for free.'
13+
required: false
1014

1115
permissions:
1216
contents: read
@@ -24,5 +28,5 @@ jobs:
2428
"text": ":warning: [${{ inputs.message }}] @hero check <${{ env.GITHUB_JOB_URL }}>"
2529
}
2630
env:
27-
SLACK_WEBHOOK_URL: ${{ secrets.AC_SLACK_WEBHOOK }}
31+
SLACK_WEBHOOK_URL: ${{ secrets.slack_webhook_url || secrets.AC_SLACK_WEBHOOK }}
2832
GITHUB_JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

.github/workflows/nightly.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ jobs:
2424
gpg_passphrase: ${{ secrets.OHAI_GPG_PASSPHRASE }}
2525
pfx_certificate_base64: ${{ secrets.OHAI_PFX_CERTIFICATE_BASE64 }}
2626
pfx_passphrase: ${{ secrets.OHAI_PFX_PASSPHRASE }}
27+
nr_account_id: ${{ secrets.AC_PROD_E2E_ACCOUNT_ID }}
28+
nr_license_key: ${{ secrets.AC_PROD_E2E_LICENSE_KEY }}
29+
nr_user_api_key: ${{ secrets.AC_NR_USER_API_KEY }}
30+
slack_webhook_url: ${{ secrets.AC_SLACK_WEBHOOK }}
2731

2832
build-image:
2933
name: Build and Push nightly image
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: 📞 Test binary size report (on demand)
2+
3+
# Exercises report_binary_size.sh end-to-end (NerdGraph lookup, Events API report, growth
4+
# detection, Slack warning) without running a real nightly/release build. Useful to test
5+
# changes to the reporting/alerting logic without triggering canary deploys, S3 uploads or
6+
# real Rust builds.
7+
8+
on:
9+
workflow_dispatch:
10+
inputs:
11+
previous_size_bytes:
12+
description: 'Fake "previous build" size in bytes, reported first'
13+
required: true
14+
default: '1000000'
15+
current_size_bytes:
16+
description: 'Fake "current build" size in bytes, reported second and compared against the previous one'
17+
required: true
18+
default: '1200000'
19+
# TEMP for testing: workflow_dispatch can't be run from a branch until this file exists on
20+
# the default branch. Push to this branch to trigger it meanwhile. Remove this `push:`
21+
# trigger once merged, workflow_dispatch will work normally from then on.
22+
push:
23+
branches:
24+
- check-binary-size
25+
26+
permissions:
27+
contents: read
28+
29+
jobs:
30+
simulate:
31+
name: Report fake previous + current builds
32+
runs-on: ubuntu-latest
33+
outputs:
34+
binary-size-warnings: ${{ steps.report-current.outputs.warnings }}
35+
steps:
36+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
37+
38+
- name: Report fake "previous" build
39+
env:
40+
BINARY_NAME: newrelic-agent-control-test
41+
BINARY_TARGET: x86_64-unknown-linux-musl
42+
BINARY_VERSION: test-previous-${{ github.run_id }}
43+
NR_ACCOUNT_ID: ${{ secrets.AC_PROD_E2E_ACCOUNT_ID }}
44+
NR_LICENSE_KEY: ${{ secrets.AC_PROD_E2E_LICENSE_KEY }}
45+
run: |
46+
truncate -s "${{ inputs.previous_size_bytes || '1000000' }}" /tmp/fake-binary
47+
export BINARY_PATH=/tmp/fake-binary
48+
bash .github/workflows/scripts/report_binary_size.sh
49+
50+
- name: Wait for the previous event to become queryable
51+
run: sleep 60
52+
53+
- name: Report fake "current" build and detect growth
54+
id: report-current
55+
env:
56+
BINARY_NAME: newrelic-agent-control-test
57+
BINARY_TARGET: x86_64-unknown-linux-musl
58+
BINARY_VERSION: test-current-${{ github.run_id }}
59+
NR_ACCOUNT_ID: ${{ secrets.AC_PROD_E2E_ACCOUNT_ID }}
60+
NR_LICENSE_KEY: ${{ secrets.AC_PROD_E2E_LICENSE_KEY }}
61+
NR_USER_API_KEY: ${{ secrets.AC_NR_USER_API_KEY }}
62+
WARNINGS_FILE: binary-size-warnings.txt
63+
run: |
64+
truncate -s "${{ inputs.current_size_bytes || '1200000' }}" /tmp/fake-binary
65+
export BINARY_PATH=/tmp/fake-binary
66+
: > "$WARNINGS_FILE"
67+
bash .github/workflows/scripts/report_binary_size.sh
68+
if [ -s "$WARNINGS_FILE" ]; then
69+
{
70+
echo "warnings<<EOF"
71+
cat "$WARNINGS_FILE"
72+
echo "EOF"
73+
} >> "$GITHUB_OUTPUT"
74+
fi
75+
76+
notify-binary-size-growth:
77+
name: Warn on binary size growth (test)
78+
needs: simulate
79+
if: needs.simulate.outputs.binary-size-warnings != ''
80+
uses: ./.github/workflows/component_send_warning_via_slack.yml
81+
with:
82+
message: ${{ needs.simulate.outputs.binary-size-warnings }}
83+
secrets:
84+
slack_webhook_url: ${{ secrets.AC_SLACK_WEBHOOK }}

.github/workflows/push_pr_checks_tests.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ jobs:
149149
generate-release-build:
150150
name: "Build release binaries"
151151
runs-on: ubuntu-latest
152+
permissions:
153+
contents: read
154+
actions: read
155+
outputs:
156+
binary-size-report: ${{ steps.sizes.outputs.report }}
152157
steps:
153158
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
154159

@@ -182,6 +187,93 @@ jobs:
182187
run: cargo zigbuild --release --package newrelic_agent_control --bin newrelic-agent-control-k8s --target x86_64-unknown-linux-musl
183188
- name: Build onhost in release mode
184189
run: cargo zigbuild --release --package newrelic_agent_control --bin newrelic-agent-control --target x86_64-unknown-linux-musl
190+
- name: Build onhost CLI in release mode
191+
run: cargo zigbuild --release --package newrelic_agent_control --bin newrelic-agent-control-cli --target x86_64-unknown-linux-musl
192+
193+
- name: Find latest baseline run on main
194+
id: baseline-run
195+
if: github.event_name == 'pull_request'
196+
env:
197+
GH_TOKEN: ${{ github.token }}
198+
run: |
199+
run_id=$(gh run list --workflow=push_pr_checks_tests.yml --branch=main \
200+
--status=success --limit=1 --json databaseId --jq '.[0].databaseId // empty')
201+
echo "run_id=$run_id" >> "$GITHUB_OUTPUT"
202+
203+
- name: Download baseline artifact
204+
if: steps.baseline-run.outputs.run_id != ''
205+
continue-on-error: true
206+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
207+
with:
208+
name: binary-size-baseline
209+
path: binary-size/baseline
210+
run-id: ${{ steps.baseline-run.outputs.run_id }}
211+
github-token: ${{ github.token }}
212+
213+
- name: Measure binary sizes
214+
id: sizes
215+
run: |
216+
dir="target/x86_64-unknown-linux-musl/release"
217+
mkdir -p binary-size
218+
: > binary-size/current.txt
219+
for name in newrelic-agent-control newrelic-agent-control-cli; do
220+
stat -c%s "$dir/$name" | xargs -I{} echo "$name {}" >> binary-size/current.txt
221+
done
222+
223+
baseline_file="binary-size/baseline/current.txt"
224+
{
225+
echo "report<<EOF"
226+
echo "## 📦 Binary size"
227+
if [ -f "$baseline_file" ]; then
228+
echo "| Binary | main | PR | Δ |"
229+
echo "|---|---|---|---|"
230+
else
231+
echo "| Binary | Size |"
232+
echo "|---|---|"
233+
fi
234+
while read -r name bytes; do
235+
human=$(numfmt --to=iec-i --suffix=B "$bytes")
236+
if [ -f "$baseline_file" ]; then
237+
base_bytes=$(awk -v n="$name" '$1==n {print $2}' "$baseline_file")
238+
if [ -n "$base_bytes" ]; then
239+
base_human=$(numfmt --to=iec-i --suffix=B "$base_bytes")
240+
delta=$((bytes - base_bytes))
241+
pct=$(awk -v d="$delta" -v b="$base_bytes" 'BEGIN { printf "%.2f", (b==0)?0:(d*100.0/b) }')
242+
icon="➡️"; [ "$delta" -gt 0 ] && icon="⬆️"; [ "$delta" -lt 0 ] && icon="⬇️"
243+
echo "| $name | $base_human | $human | $icon ${delta}B (${pct}%) |"
244+
else
245+
echo "| $name | _no baseline_ | $human | — |"
246+
fi
247+
else
248+
echo "| $name | $human ($bytes bytes) |"
249+
fi
250+
done < binary-size/current.txt
251+
if [ ! -f "$baseline_file" ] && [ "${{ github.event_name }}" = "pull_request" ]; then
252+
echo ""
253+
echo "_No baseline found for \`main\` (first run after enabling this check, or the artifact expired)._"
254+
fi
255+
echo "EOF"
256+
} >> "$GITHUB_OUTPUT"
257+
258+
- name: Upload size baseline
259+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
260+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
261+
with:
262+
name: binary-size-baseline
263+
path: binary-size/current.txt
264+
265+
binary-size-comment:
266+
name: Comment binary size on PR
267+
needs: generate-release-build
268+
if: github.event_name == 'pull_request'
269+
runs-on: ubuntu-latest
270+
permissions:
271+
pull-requests: write
272+
steps:
273+
- uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
274+
with:
275+
header: binary-size
276+
message: ${{ needs.generate-release-build.outputs.binary-size-report }}
185277

186278
unit-docs-onhost-integration-tests:
187279
name: Unit, docs and onhost integration tests
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/bin/bash
2+
# Reports a single AgentControlBinarySize custom event to the New Relic Events API,
3+
# and appends a warning line to WARNINGS_FILE if the binary grew more than 10% versus
4+
# its previous build. Sending that warning to Slack is not this script's job: the
5+
# workflow step collects WARNINGS_FILE across all binaries and a separate job hands
6+
# it to the existing component_send_warning_via_slack.yml, instead of duplicating that
7+
# webhook call here.
8+
#
9+
# Required environment variables (set via the workflow step's `env:` block):
10+
# BINARY_NAME - e.g. newrelic-agent-control
11+
# BINARY_TARGET - Rust target triple, e.g. x86_64-unknown-linux-musl
12+
# BINARY_PATH - path to the built binary file
13+
# BINARY_VERSION - version/tag being built
14+
# NR_ACCOUNT_ID - New Relic account ID
15+
# NR_LICENSE_KEY - New Relic license key (used as the Events API ingest key)
16+
#
17+
# Optional environment variables:
18+
# NR_USER_API_KEY - New Relic User API key (NerdGraph). Needed to look up the previous
19+
# build's size and compute growth. If unset, the event is still
20+
# reported but the growth check is skipped.
21+
# WARNINGS_FILE - Path to append growth warnings to. Defaults to binary-size-warnings.txt
22+
# in the current directory.
23+
#
24+
# Standard GitHub Actions variables (automatically injected by the runner):
25+
# GITHUB_EVENT_NAME, GITHUB_HEAD_REF, GITHUB_REF_NAME, GITHUB_RUN_ID
26+
#
27+
# If NR_ACCOUNT_ID/NR_LICENSE_KEY are not set, the report is skipped without failing the build
28+
# (some callers of component_packages.yml, e.g. e2e-only workflows, don't wire these secrets).
29+
30+
set -euo pipefail
31+
32+
GROWTH_THRESHOLD_PCT=10
33+
34+
if [[ -z "${NR_ACCOUNT_ID:-}" || -z "${NR_LICENSE_KEY:-}" ]]; then
35+
echo "NR_ACCOUNT_ID/NR_LICENSE_KEY not set, skipping binary size report for ${BINARY_PATH:-unknown}"
36+
exit 0
37+
fi
38+
39+
SIZE_BYTES=$(stat -c%s "$BINARY_PATH")
40+
41+
# GITHUB_HEAD_REF is set for pull requests; GITHUB_REF_NAME covers push/schedule/dispatch.
42+
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
43+
44+
# Look up the previous build's size for this exact binary+target via NerdGraph, before
45+
# reporting the current one (so we compare against the true previous value, not a race
46+
# against data we're about to insert ourselves).
47+
PREVIOUS_SIZE_BYTES=""
48+
if [[ -n "${NR_USER_API_KEY:-}" ]]; then
49+
nrql_query="SELECT sizeBytes FROM AgentControlBinarySize WHERE binaryName = '${BINARY_NAME}' AND target = '${BINARY_TARGET}' SINCE 90 days ago ORDER BY timestamp DESC LIMIT 1"
50+
graphql_query="{ actor { account(id: ${NR_ACCOUNT_ID}) { nrql(query: \"${nrql_query}\") { results } } } }"
51+
request_body=$(jq -n --arg query "$graphql_query" '{query: $query}')
52+
53+
nerdgraph_response=$(curl -s -X POST "https://api.newrelic.com/graphql" \
54+
-H "Content-Type: application/json" \
55+
-H "API-Key: ${NR_USER_API_KEY}" \
56+
-d "$request_body")
57+
58+
PREVIOUS_SIZE_BYTES=$(echo "$nerdgraph_response" | jq -r '.data.actor.account.nrql.results[0].sizeBytes // empty')
59+
fi
60+
61+
event=$(jq -n \
62+
--arg binaryName "$BINARY_NAME" \
63+
--arg target "$BINARY_TARGET" \
64+
--arg version "$BINARY_VERSION" \
65+
--arg branch "$BRANCH" \
66+
--arg runId "$GITHUB_RUN_ID" \
67+
--arg triggerEvent "$GITHUB_EVENT_NAME" \
68+
--argjson sizeBytes "$SIZE_BYTES" \
69+
'[{
70+
eventType: "AgentControlBinarySize",
71+
binaryName: $binaryName,
72+
target: $target,
73+
version: $version,
74+
branch: $branch,
75+
runId: $runId,
76+
triggerEvent: $triggerEvent,
77+
sizeBytes: $sizeBytes
78+
}]')
79+
80+
curl -s -X POST \
81+
"https://insights-collector.newrelic.com/v1/accounts/${NR_ACCOUNT_ID}/events" \
82+
-H "Content-Type: application/json" \
83+
-H "Api-Key: ${NR_LICENSE_KEY}" \
84+
-d "$event"
85+
86+
if [[ -n "$PREVIOUS_SIZE_BYTES" ]]; then
87+
growth_pct=$(awk -v cur="$SIZE_BYTES" -v prev="$PREVIOUS_SIZE_BYTES" \
88+
'BEGIN { printf "%.2f", (prev == 0) ? 0 : ((cur - prev) * 100.0 / prev) }')
89+
90+
if awk -v p="$growth_pct" -v t="$GROWTH_THRESHOLD_PCT" 'BEGIN { exit !(p > t) }'; then
91+
warning="Agent Control binary \`${BINARY_NAME}\` (${BINARY_TARGET}) grew ${growth_pct}% versus its previous build: ${PREVIOUS_SIZE_BYTES} -> ${SIZE_BYTES} bytes (version ${BINARY_VERSION})"
92+
echo "Binary size regression: $warning"
93+
echo "$warning" >> "${WARNINGS_FILE:-binary-size-warnings.txt}"
94+
fi
95+
else
96+
echo "No previous build found for ${BINARY_NAME} (${BINARY_TARGET}), skipping growth check"
97+
fi

0 commit comments

Comments
 (0)