Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.7.1
- name: Cache tool binaries
id: cache-tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: tools-cache
key: perfspect-tools-binaries-${{ hashFiles('tools/Makefile', 'tools/**/*.Dockerfile', 'tools/**/*.patch') }}
- name: Set cache hit flag
if: steps.cache-tools.outputs.cache-hit == 'true'
run: |
echo "TOOLS_CACHE_HIT=true" >> $GITHUB_ENV
- name: build perfspect
run: |
builder/build.sh
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/debug_out
/tools/bin
/tools/bin-aarch64
/tools-cache
/dist
/internal/script/resources/x86_64
/internal/script/resources/aarch64
Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ check_static:
.PHONY: check_license
check_license:
@echo "Confirming source files have license headers..."
@for f in `find . -type f ! -path './perfspect_202*' ! -path './tools/bin/*' ! -path './tools/bin-aarch64/*' ! -path './internal/script/resources/*' ! -path './scripts/.venv/*' ! -path './test/output/*' ! -path './debug_out/*' ! -path './tools/perf-archive/*' ! -path './tools/avx-turbo/*' \( -name "*.go" -o -name "*.s" -o -name "*.html" -o -name "Makefile" -o -name "*.sh" -o -name "*.Dockerfile" -o -name "*.py" \)`; do \
@for f in `find . -type f ! -path './perfspect_202*' ! -path './tools/bin/*' ! -path './tools/bin-aarch64/*' ! -path './tools-cache/*' ! -path './internal/script/resources/*' ! -path './scripts/.venv/*' ! -path './test/output/*' ! -path './debug_out/*' ! -path './tools/perf-archive/*' ! -path './tools/avx-turbo/*' \( -name "*.go" -o -name "*.s" -o -name "*.html" -o -name "Makefile" -o -name "*.sh" -o -name "*.Dockerfile" -o -name "*.py" \)`; do \
if ! grep -E 'SPDX-License-Identifier: BSD-3-Clause' "$$f" >/dev/null; then echo "Error: license not found: $$f"; fail=1; fi; \
done; if [ -n "$$fail" ]; then exit 1; fi

Expand Down Expand Up @@ -162,9 +162,14 @@ sweep:
rm -f perfspect.log

.PHONY: clean
clean: sweep
clean: sweep clean-tools-cache
@echo "Cleaning up..."
rm -f perfspect
rm -f perfspect-aarch64
sudo rm -rf dist
rm -rf internal/script/resources

.PHONY: clean-tools-cache
clean-tools-cache:
@echo "Removing cached tool binaries..."
rm -rf tools-cache
29 changes: 20 additions & 9 deletions builder/build.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,33 @@
# build perfspect:
# $ docker run --rm -v "$PWD":/localrepo -w /localrepo perfspect-builder:v1 make dist

ARG REGISTRY=
ARG PREFIX=
ARG TAG=
ARG REGISTRY
ARG PREFIX
ARG TAG=v1
ARG TOOLS_IMAGE=${REGISTRY}${PREFIX}perfspect-tools:${TAG}
# STAGE 1 - image contains pre-built tools components, rebuild the image to rebuild the tools components
FROM ${REGISTRY}${PREFIX}perfspect-tools:${TAG} AS tools
FROM ${TOOLS_IMAGE} AS tools

# STAGE 2 - image contains perfspect's Go components build environment
FROM golang:1.25.5@sha256:20b91eda7a9627c127c0225b0d4e8ec927b476fa4130c6760928b849d769c149
# install system dependencies
RUN apt-get update && apt-get install -y jq
# allow git to operate in the mounted repository regardless of the user
RUN git config --global --add safe.directory /localrepo
# pre-install Go tools used by make check
RUN go install honnef.co/go/tools/cmd/staticcheck@latest && \
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest && \
go install golang.org/x/vuln/cmd/govulncheck@latest && \
go install github.com/securego/gosec/v2/cmd/gosec@latest
# copy the tools binaries and source from the previous stage
RUN mkdir /prebuilt
RUN mkdir /prebuilt/tools
COPY --from=tools /bin/ /prebuilt/tools
COPY --from=tools /oss_source.tgz /prebuilt/
COPY --from=tools /oss_source.tgz.md5 /prebuilt/
# allow git to operate in the mounted repository regardless of the user
RUN git config --global --add safe.directory /localrepo
# install jq as it is used in the Makefile to create the manifest
RUN apt-get update
RUN apt-get install -y jq
# pre-download Go module dependencies (changes when go.mod/go.sum change)
WORKDIR /tmp/deps
COPY go.mod go.sum ./
RUN go mod download

WORKDIR /
120 changes: 115 additions & 5 deletions builder/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,130 @@

# run this script from repo's root directory

set -ex
set -e

TAG=v1

# build tools image
docker build -f tools/build.Dockerfile --tag perfspect-tools:$TAG ./tools
CACHE_DIR="tools-cache"
# A few files that must be present in the cache for it to be valid
REQUIRED_CACHE_FILES=(
"$CACHE_DIR/bin/x86_64/perf"
"$CACHE_DIR/bin/aarch64/perf"
"$CACHE_DIR/bin/x86_64/fio"
"$CACHE_DIR/bin/aarch64/fio"
"$CACHE_DIR/oss_source.tgz"
"$CACHE_DIR/oss_source.tgz.md5"
)

cache_ready() {
local missing=0
for f in "${REQUIRED_CACHE_FILES[@]}"; do
if [ ! -s "$f" ]; then
echo "Cached tools missing required file: $f"
missing=1
fi
done
if [ "$missing" -eq 1 ]; then
return 1
fi
return 0
}

invalidate_cache() {
if [ -d "$CACHE_DIR" ]; then
echo "Removing incomplete cached tools at $CACHE_DIR"
rm -rf "$CACHE_DIR"
fi
}

# Determine if we're in GitHub Actions
if [ -n "$GITHUB_ACTIONS" ]; then
# Use buildx with GitHub Actions cache
CACHE_FROM="--cache-from type=gha,scope=perfspect-tools"
CACHE_TO="--cache-to type=gha,mode=max,scope=perfspect-tools"
BUILD_CMD="docker buildx build --load"
else
# Local build without cache export
CACHE_FROM=""
CACHE_TO=""
BUILD_CMD="docker build"
fi

# Check if we can use cached binaries
USE_CACHE=""
if [ "$TOOLS_CACHE_HIT" = "true" ]; then
# GitHub Actions cache hit
USE_CACHE="true"
elif [ -z "$SKIP_TOOLS_CACHE" ] && [ -d "$CACHE_DIR/bin" ]; then
# Local cache exists and not disabled
echo "Found local tools cache in $CACHE_DIR/"
echo "To force rebuild, run: SKIP_TOOLS_CACHE=1 builder/build.sh"
if cache_ready; then
USE_CACHE="true"
else
echo "Local tools cache is incomplete; it will be discarded and rebuilt."
invalidate_cache
fi
fi

# build tools image (or use cached binaries)
if [ "$USE_CACHE" = "true" ]; then
echo "Using cached tool binaries, creating minimal tools image"
# Create a minimal Dockerfile that packages the cached binaries
cat > /tmp/cached-tools.Dockerfile << EOF
FROM scratch AS output
COPY $CACHE_DIR/bin /bin
COPY $CACHE_DIR/oss_source.tgz /oss_source.tgz
COPY $CACHE_DIR/oss_source.tgz.md5 /oss_source.tgz.md5
EOF
docker build -f /tmp/cached-tools.Dockerfile -t perfspect-tools:$TAG .
rm /tmp/cached-tools.Dockerfile
else
echo "Building tools from source"
$BUILD_CMD -f tools/build.Dockerfile \
$CACHE_FROM $CACHE_TO \
--tag perfspect-tools:$TAG ./tools

# Extract binaries for caching (both GitHub Actions and local)
if [ -z "$SKIP_TOOLS_CACHE" ]; then
echo "Extracting tool binaries to tools-cache/ for future builds"
rm -rf "$CACHE_DIR"
mkdir -p "$CACHE_DIR"
# Use a helper container to extract files from the scratch-based image
# Create a temporary Dockerfile that copies from the tools image
cat > /tmp/extract-tools.Dockerfile << EOF
FROM perfspect-tools:$TAG AS source
FROM busybox:latest
COPY --from=source /bin /output/bin
COPY --from=source /oss_source.tgz /output/
COPY --from=source /oss_source.tgz.md5 /output/
CMD ["true"]
EOF
# Build the extractor image
docker build -q -f /tmp/extract-tools.Dockerfile -t perfspect-tools-extractor:$TAG . > /dev/null
# Create container and extract files
CONTAINER_ID=$(docker create perfspect-tools-extractor:$TAG)
docker cp $CONTAINER_ID:/output/. "$CACHE_DIR"/
docker rm $CONTAINER_ID > /dev/null
# Cleanup
docker rmi perfspect-tools-extractor:$TAG > /dev/null
rm /tmp/extract-tools.Dockerfile
echo "Tools cached locally. Next build will be faster!"
fi
fi

# build the perfspect builder image
docker build -f builder/build.Dockerfile --build-arg TAG=$TAG --tag perfspect-builder:$TAG .
# Note: Use regular docker build (not buildx) because it needs access to the
# locally built perfspect-tools:$TAG image. The Go module and tool caching
# is handled via Docker's built-in layer caching.
docker build -f builder/build.Dockerfile \
--build-arg TAG=$TAG \
--tag perfspect-builder:$TAG .

# build perfspect using the builder image
docker container run \
--volume "$(pwd)":/localrepo \
-w /localrepo \
--rm \
perfspect-builder:$TAG \
make dist
make dist