mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a2b0e0f30 | ||
|
|
1013f2a160 | ||
|
|
e73e1375cc | ||
|
|
9278d50252 | ||
|
|
e084c0a46a | ||
|
|
f7a941c220 | ||
|
|
8920a9d09a | ||
|
|
a677646b17 | ||
|
|
c84ea20b88 | ||
|
|
409a02a125 | ||
|
|
78af9208cf | ||
|
|
fb0983480c | ||
|
|
1aa74db81d | ||
|
|
8f7cf26cc9 | ||
|
|
a6d3e3e423 | ||
|
|
6ea8cbcb09 | ||
|
|
bf1dfa0318 | ||
|
|
45ee61aa4d | ||
|
|
e3458ad297 | ||
|
|
cb32216df8 | ||
|
|
2b82454ef8 | ||
|
|
e05962bcf3 | ||
|
|
0f747f55dc | ||
|
|
1c234e432c | ||
|
|
d325804359 | ||
|
|
2d664b8736 | ||
|
|
8762bd1515 | ||
|
|
a2a424b201 | ||
|
|
3f069125a2 | ||
|
|
37292e6ba1 |
@@ -0,0 +1,192 @@
|
||||
name: Affected Go Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test-affected:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
outputs:
|
||||
unit_has_changes: ${{ steps.affected.outputs.has_changes }}
|
||||
e2e_has_changes: ${{ steps.affected_e2e.outputs.has_changes }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Go Mod Download
|
||||
run: go mod download
|
||||
|
||||
|
||||
- name: Compute base ref
|
||||
id: pr-info
|
||||
run: |
|
||||
echo "BASE_REF=origin/${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
|
||||
echo "Base ref: origin/${{ github.base_ref }}"
|
||||
|
||||
# 2) Detect relevant unit packages and e2e tests
|
||||
- name: Compute affected packages
|
||||
id: affected
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Base: ${{ steps.pr-info.outputs.BASE_REF }}"
|
||||
# Generate affected package list to a file for reuse in subsequent steps
|
||||
go run ./scripts/affected-packages.go -base "${{ steps.pr-info.outputs.BASE_REF }}" > /tmp/affected.txt
|
||||
echo "Affected packages:" || true
|
||||
if [ -s /tmp/affected.txt ]; then
|
||||
cat /tmp/affected.txt
|
||||
else
|
||||
echo "(none)"
|
||||
fi
|
||||
# Expose whether we have any packages to test
|
||||
if [ -s /tmp/affected.txt ]; then
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Compute affected e2e tests
|
||||
id: affected_e2e
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go run ./scripts/affected-packages.go -mode=suites -base "${{ steps.pr-info.outputs.BASE_REF }}" > /tmp/affected-e2e.txt
|
||||
awk -F: '$1=="preflight"{print $2}' /tmp/affected-e2e.txt > /tmp/preflight-tests.txt
|
||||
awk -F: '$1=="support-bundle"{print $2}' /tmp/affected-e2e.txt > /tmp/support-tests.txt
|
||||
if [ -s /tmp/preflight-tests.txt ] || [ -s /tmp/support-tests.txt ]; then
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Publish affected summary
|
||||
if: always()
|
||||
run: |
|
||||
{
|
||||
echo "### Affected unit packages";
|
||||
if [ -s /tmp/affected.txt ]; then
|
||||
sed 's/^/- /' /tmp/affected.txt;
|
||||
else
|
||||
echo "- (none)";
|
||||
fi;
|
||||
echo;
|
||||
echo "### Affected e2e tests";
|
||||
if [ -s /tmp/affected-e2e.txt ]; then
|
||||
sed 's/^/- /' /tmp/affected-e2e.txt;
|
||||
else
|
||||
echo "- (none)";
|
||||
fi;
|
||||
} | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload affected unit packages
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: affected-unit
|
||||
path: /tmp/affected.txt
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload affected e2e artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: affected-e2e
|
||||
path: |
|
||||
/tmp/affected-e2e.txt
|
||||
/tmp/preflight-tests.txt
|
||||
/tmp/support-tests.txt
|
||||
if-no-files-found: warn
|
||||
|
||||
|
||||
- name: No affected packages — skip tests
|
||||
if: steps.affected.outputs.has_changes != 'true'
|
||||
run: echo "No Go packages affected by this PR; skipping tests."
|
||||
|
||||
e2e-affected:
|
||||
needs: test-affected
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: [unit, preflight, support-bundle]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
|
||||
- name: Go Mod Download
|
||||
run: go mod download
|
||||
|
||||
- name: Download affected unit packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: affected-unit
|
||||
path: /tmp
|
||||
|
||||
- name: Download affected e2e artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: affected-e2e
|
||||
path: /tmp
|
||||
|
||||
- name: Run unit tests (filtered)
|
||||
if: matrix.suite == 'unit' && needs.test-affected.outputs.unit_has_changes == 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if grep -qx "./..." /tmp/affected.txt; then
|
||||
echo "Module files changed; running all unit tests"
|
||||
make test
|
||||
else
|
||||
echo "Running unit tests for affected packages"
|
||||
pkgs=$(tr '\n' ' ' < /tmp/affected.txt)
|
||||
PACKAGES="$pkgs" make test-packages
|
||||
fi
|
||||
|
||||
|
||||
- name: Run e2e (filtered) - ${{ matrix.suite }}
|
||||
if: matrix.suite != 'unit' && needs.test-affected.outputs.e2e_has_changes == 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker rm -f kind-cluster-control-plane 2>/dev/null || true
|
||||
if [ "${{ matrix.suite }}" = "preflight" ]; then
|
||||
file=/tmp/preflight-tests.txt
|
||||
path=./test/e2e/preflight
|
||||
else
|
||||
file=/tmp/support-tests.txt
|
||||
path=./test/e2e/support-bundle
|
||||
fi
|
||||
if [ -s "$file" ]; then
|
||||
regex="$(grep -v '^$' "$file" | tr '\n' '|' | sed 's/|$//')"
|
||||
if [ -n "$regex" ]; then
|
||||
if [ "${{ matrix.suite }}" = "preflight" ]; then
|
||||
E2EPATHS="$path" RUN="^(${regex})$" make preflight-e2e-go-test
|
||||
else
|
||||
E2EPATHS="$path" RUN="^(${regex})$" make support-bundle-e2e-go-test
|
||||
fi
|
||||
else
|
||||
echo "No valid ${{ matrix.suite }} tests matched after filtering"
|
||||
fi
|
||||
else
|
||||
echo "No ${{ matrix.suite }} e2e changes"
|
||||
fi
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ jobs:
|
||||
- run: make tidy-diff
|
||||
|
||||
test-integration:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
@@ -65,6 +66,7 @@ jobs:
|
||||
path: bin/preflight
|
||||
|
||||
validate-preflight-e2e:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-preflight
|
||||
steps:
|
||||
@@ -95,6 +97,7 @@ jobs:
|
||||
path: bin/support-bundle
|
||||
|
||||
validate-supportbundle-e2e:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
@@ -113,9 +116,10 @@ jobs:
|
||||
|
||||
# Additional e2e tests for support bundle that run in Go, these create a Kind cluster
|
||||
validate-supportbundle-e2e-go:
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
needs: compile-supportbundle
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download support bundle binary
|
||||
uses: actions/download-artifact@v5
|
||||
@@ -133,6 +137,7 @@ jobs:
|
||||
|
||||
# summary jobs, these jobs will only run if all the other jobs have succeeded
|
||||
validate-pr-tests:
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- tidy-check
|
||||
@@ -147,10 +152,10 @@ jobs:
|
||||
# this job will validate that the validation did not fail and that all pr-tests succeed
|
||||
# it is used for the github branch protection rule
|
||||
validate-success:
|
||||
if: ${{ always() && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- validate-pr-tests
|
||||
if: always()
|
||||
steps:
|
||||
# https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
|
||||
# if the validate-pr-tests job was not successful, this job will fail
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: build-test
|
||||
name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -47,6 +47,19 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/setup-go
|
||||
|
||||
- name: Cache Go build and modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Go mod download
|
||||
run: go mod download
|
||||
|
||||
- name: Check go mod tidy
|
||||
run: |
|
||||
go mod tidy
|
||||
@@ -65,22 +78,7 @@ jobs:
|
||||
make vet
|
||||
|
||||
# Unit and integration tests
|
||||
test:
|
||||
if: needs.changes.outputs.go-files == 'true'
|
||||
needs: [changes, lint]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/setup-go
|
||||
|
||||
- name: Setup K3s
|
||||
uses: replicatedhq/action-k3s@main
|
||||
with:
|
||||
version: v1.31.2-k3s1
|
||||
|
||||
- name: Run tests
|
||||
run: make test-integration
|
||||
# (moved to push-full-tests.yml)
|
||||
|
||||
# Build binaries
|
||||
build:
|
||||
@@ -91,6 +89,19 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/setup-go
|
||||
|
||||
- name: Cache Go build and modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Go mod download
|
||||
run: go mod download
|
||||
- run: make build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -98,64 +109,26 @@ jobs:
|
||||
path: bin/
|
||||
retention-days: 1
|
||||
|
||||
# E2E tests
|
||||
e2e:
|
||||
if: needs.changes.outputs.go-files == 'true' || github.event_name == 'push'
|
||||
needs: [changes, build]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: preflight
|
||||
target: preflight-e2e-test
|
||||
needs-k3s: true
|
||||
- name: support-bundle-shell
|
||||
target: support-bundle-e2e-test
|
||||
needs-k3s: true
|
||||
- name: support-bundle-go
|
||||
target: support-bundle-e2e-go-test
|
||||
needs-k3s: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup K3s
|
||||
if: matrix.needs-k3s
|
||||
uses: replicatedhq/action-k3s@main
|
||||
with:
|
||||
version: v1.31.2-k3s1
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binaries
|
||||
path: bin/
|
||||
|
||||
- run: chmod +x bin/*
|
||||
- run: make ${{ matrix.target }}
|
||||
# (moved to push-full-tests.yml)
|
||||
|
||||
# Success summary
|
||||
success:
|
||||
if: always()
|
||||
needs: [lint, test, build, e2e]
|
||||
needs: [lint, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check results
|
||||
run: |
|
||||
# Check if any required jobs failed
|
||||
if [[ "${{ needs.lint.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.test.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.build.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.e2e.result }}" == "failure" ]]; then
|
||||
[[ "${{ needs.build.result }}" == "failure" ]]; then
|
||||
echo "::error::Some jobs failed or were cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if any required jobs were cancelled
|
||||
if [[ "${{ needs.lint.result }}" == "cancelled" ]] || \
|
||||
[[ "${{ needs.test.result }}" == "cancelled" ]] || \
|
||||
[[ "${{ needs.build.result }}" == "cancelled" ]] || \
|
||||
[[ "${{ needs.e2e.result }}" == "cancelled" ]]; then
|
||||
[[ "${{ needs.build.result }}" == "cancelled" ]]; then
|
||||
echo "::error::Some jobs failed or were cancelled"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,77 @@
|
||||
name: push-full-tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
unit-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/setup-go
|
||||
- name: Setup K3s
|
||||
uses: replicatedhq/action-k3s@main
|
||||
with:
|
||||
version: v1.31.2-k3s1
|
||||
- name: Run tests
|
||||
run: make test-integration
|
||||
|
||||
build-binaries:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/setup-go
|
||||
- name: Cache Go build and modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Go mod download
|
||||
run: go mod download
|
||||
- name: Build binaries
|
||||
run: make build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries
|
||||
path: bin/
|
||||
retention-days: 1
|
||||
|
||||
e2e:
|
||||
needs: [unit-integration, build-binaries]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: preflight
|
||||
target: preflight-e2e-test
|
||||
needs-k3s: true
|
||||
- name: support-bundle-shell
|
||||
target: support-bundle-e2e-test
|
||||
needs-k3s: true
|
||||
- name: support-bundle-go
|
||||
target: support-bundle-e2e-go-test
|
||||
needs-k3s: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binaries
|
||||
path: bin/
|
||||
- run: chmod +x bin/*
|
||||
- name: Setup K3s
|
||||
if: matrix.needs-k3s
|
||||
uses: replicatedhq/action-k3s@main
|
||||
with:
|
||||
version: v1.31.2-k3s1
|
||||
- run: make ${{ matrix.target }}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Regression Test Suite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, v1beta3]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
@@ -289,4 +289,4 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
api-token: ${{ secrets.REPLICATED_API_TOKEN }}
|
||||
cluster-id: ${{ steps.create-cluster.outputs.cluster-id }}
|
||||
cluster-id: ${{ steps.create-cluster.outputs.cluster-id }}
|
||||
@@ -37,7 +37,7 @@ endef
|
||||
BUILDTAGS = "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp"
|
||||
BUILDFLAGS = -tags ${BUILDTAGS} -installsuffix netgo
|
||||
BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
|
||||
E2EPATHS = ./test/e2e/...
|
||||
E2EPATHS ?= ./test/e2e/...
|
||||
TESTFLAGS ?= -v -coverprofile cover.out
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
@@ -49,12 +49,23 @@ ffi: fmt vet
|
||||
|
||||
.PHONY: test
|
||||
test: generate fmt vet
|
||||
if [ -n $(RUN) ]; then \
|
||||
go test ${BUILDFLAGS} ${BUILDPATHS} ${TESTFLAGS} -run $(RUN); \
|
||||
if [ -n "$(RUN)" ]; then \
|
||||
go test ${BUILDFLAGS} ${BUILDPATHS} ${TESTFLAGS} -run "$(RUN)"; \
|
||||
else \
|
||||
go test ${BUILDFLAGS} ${BUILDPATHS} ${TESTFLAGS}; \
|
||||
fi
|
||||
|
||||
# Run unit tests only for a provided list of packages.
|
||||
# Usage: make test-packages PACKAGES="pkg/a pkg/b cmd/foo"
|
||||
.PHONY: test-packages
|
||||
test-packages:
|
||||
@if [ -z "$(PACKAGES)" ]; then \
|
||||
echo "No PACKAGES provided; nothing to test."; \
|
||||
exit 0; \
|
||||
fi
|
||||
@echo "Running unit tests for packages: $(PACKAGES)"
|
||||
go test ${BUILDFLAGS} $(PACKAGES) ${TESTFLAGS}
|
||||
|
||||
# Go tests that require a K8s instance
|
||||
# TODOLATER: merge with test, so we get unified coverage reports? it'll add 21~sec to the test job though...
|
||||
.PHONY: test-integration
|
||||
@@ -73,10 +84,18 @@ run-examples:
|
||||
support-bundle-e2e-test:
|
||||
./test/validate-support-bundle-e2e.sh
|
||||
|
||||
.PHONY: preflight-e2e-go-test
|
||||
preflight-e2e-go-test: bin/preflight
|
||||
if [ -n "$(RUN)" ]; then \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v -run "$(RUN)"; \
|
||||
else \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v; \
|
||||
fi
|
||||
|
||||
.PHONY: support-bundle-e2e-go-test
|
||||
support-bundle-e2e-go-test:
|
||||
if [ -n $(RUN) ]; then \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v -run $(RUN); \
|
||||
support-bundle-e2e-go-test: bin/support-bundle
|
||||
if [ -n "$(RUN)" ]; then \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v -run "$(RUN)"; \
|
||||
else \
|
||||
go test ${BUILDFLAGS} ${E2EPATHS} -v; \
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// goListPackageJSON models the subset of fields we need from `go list -json` output.
|
||||
// The JSON can be quite large; we intentionally only decode what we use to keep memory reasonable.
|
||||
type goListPackageJSON struct {
|
||||
ImportPath string `json:"ImportPath"`
|
||||
Deps []string `json:"Deps"`
|
||||
}
|
||||
|
||||
// runCommand executes a command and returns stdout as bytes with trimmed trailing newline.
|
||||
func runCommand(name string, args ...string) ([]byte, error) {
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Stderr = os.Stderr
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.TrimRight(out, "\n"), nil
|
||||
}
|
||||
|
||||
// listPackageWithDeps returns the full transitive dependency set for a package import path,
|
||||
// including the package itself.
|
||||
func listPackageWithDeps(importPath string) (map[string]struct{}, error) {
|
||||
cmd := exec.Command("go", "list", "-json", "-deps", importPath)
|
||||
cmd.Stderr = os.Stderr
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go list -json -deps %s failed: %w", importPath, err)
|
||||
}
|
||||
deps := make(map[string]struct{})
|
||||
dec := json.NewDecoder(bytes.NewReader(out))
|
||||
for {
|
||||
var pkg goListPackageJSON
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("decode go list json: %w", err)
|
||||
}
|
||||
if pkg.ImportPath != "" {
|
||||
deps[pkg.ImportPath] = struct{}{}
|
||||
}
|
||||
for _, d := range pkg.Deps {
|
||||
deps[d] = struct{}{}
|
||||
}
|
||||
}
|
||||
return deps, nil
|
||||
}
|
||||
|
||||
// changedFiles returns a slice of file paths changed between baseRef and HEAD.
|
||||
func changedFiles(baseRef string) ([]string, error) {
|
||||
// Use triple-dot to include merge base with baseRef, typical for PR diffs.
|
||||
out, err := runCommand("git", "diff", "--name-only", baseRef+"...HEAD")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git diff failed: %w", err)
|
||||
}
|
||||
var files []string
|
||||
scanner := bufio.NewScanner(bytes.NewReader(out))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
files = append(files, line)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// mapFilesToPackages resolves a set of Go package import paths that directly contain the changed Go files.
|
||||
func mapFilesToPackages(files []string) (map[string]struct{}, error) {
|
||||
packages := make(map[string]struct{})
|
||||
// Collect unique directories that contain changed Go files.
|
||||
dirSet := make(map[string]struct{})
|
||||
for _, f := range files {
|
||||
if strings.HasPrefix(f, "vendor/") {
|
||||
continue
|
||||
}
|
||||
if filepath.Ext(f) != ".go" {
|
||||
continue
|
||||
}
|
||||
d := filepath.Dir(f)
|
||||
if d == "." {
|
||||
d = "."
|
||||
}
|
||||
dirSet[d] = struct{}{}
|
||||
}
|
||||
if len(dirSet) == 0 {
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
// Convert to a stable-ordered slice of directories to avoid nondeterminism.
|
||||
var dirs []string
|
||||
for d := range dirSet {
|
||||
// Ensure relative paths are treated as packages; prepend ./ for clarity.
|
||||
if strings.HasPrefix(d, "./") || d == "." {
|
||||
dirs = append(dirs, d)
|
||||
} else {
|
||||
dirs = append(dirs, "./"+d)
|
||||
}
|
||||
}
|
||||
sort.Strings(dirs)
|
||||
|
||||
// `go list` accepts directories and returns their package import paths.
|
||||
args := append([]string{"list", "-f", "{{.ImportPath}}"}, dirs...)
|
||||
out, err := runCommand("go", args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go list for files failed: %w", err)
|
||||
}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(out))
|
||||
for scanner.Scan() {
|
||||
pkg := strings.TrimSpace(scanner.Text())
|
||||
if pkg != "" {
|
||||
packages[pkg] = struct{}{}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
// computeAffectedPackages expands directly changed packages to include reverse dependencies across the module.
|
||||
// We query all packages with test dependencies (-test) to ensure test-only imports are considered.
|
||||
func computeAffectedPackages(directPkgs map[string]struct{}) (map[string]struct{}, error) {
|
||||
affected := make(map[string]struct{})
|
||||
for p := range directPkgs {
|
||||
affected[p] = struct{}{}
|
||||
}
|
||||
if len(directPkgs) == 0 {
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
// Enumerate all packages in the module with their deps.
|
||||
// We stream decode concatenated JSON objects produced by `go list -json`.
|
||||
cmd := exec.Command("go", "list", "-json", "-deps", "-test", "./...")
|
||||
cmd.Stderr = os.Stderr
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go list -json failed: %w", err)
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(out))
|
||||
for {
|
||||
var pkg goListPackageJSON
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("decode go list json: %w", err)
|
||||
}
|
||||
// If this package is directly changed, it's already included.
|
||||
// If it depends (directly or transitively) on any changed package, include it.
|
||||
for changed := range directPkgs {
|
||||
if pkg.ImportPath == changed {
|
||||
affected[pkg.ImportPath] = struct{}{}
|
||||
break
|
||||
}
|
||||
// Linear scan over deps is acceptable given typical package counts.
|
||||
for _, dep := range pkg.Deps {
|
||||
if dep == changed {
|
||||
affected[pkg.ImportPath] = struct{}{}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
// listTestFunctions scans a directory for Go test files and returns names of functions
|
||||
// that match the pattern `func TestXxx(t *testing.T)`.
|
||||
func listTestFunctions(dir string) ([]string, error) {
|
||||
var tests []string
|
||||
// Regex to capture test function names. This is a simple heuristic suitable for our codebase.
|
||||
testFuncRe := regexp.MustCompile(`^func\s+(Test[\w\d_]+)\s*\(`)
|
||||
|
||||
walkFn := func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(d.Name(), "_test.go") {
|
||||
return nil
|
||||
}
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if m := testFuncRe.FindStringSubmatch(line); m != nil {
|
||||
tests = append(tests, m[1])
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
if err := filepath.WalkDir(dir, walkFn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(tests)
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
baseRef := flag.String("base", "origin/main", "Git base ref to diff against (e.g., origin/main)")
|
||||
printAllOnChanges := flag.Bool("all-on-mod-change", true, "Run all tests if go.mod or go.sum changed")
|
||||
verbose := flag.Bool("v", false, "Enable verbose diagnostics to stderr")
|
||||
mode := flag.String("mode", "packages", "Output mode: 'packages' to print import paths; 'suites' to print e2e suite names")
|
||||
changedFilesCSV := flag.String("changed-files", "", "Comma-separated paths to treat as changed (bypass git)")
|
||||
changedFilesFile := flag.String("changed-files-file", "", "File with newline-separated paths to treat as changed")
|
||||
flag.Parse()
|
||||
|
||||
// Determine the set of changed files: explicit list if provided, otherwise via git diff.
|
||||
var files []string
|
||||
if *changedFilesCSV != "" || *changedFilesFile != "" {
|
||||
if *changedFilesCSV != "" {
|
||||
parts := strings.Split(*changedFilesCSV, ",")
|
||||
for _, p := range parts {
|
||||
if s := strings.TrimSpace(p); s != "" {
|
||||
files = append(files, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if *changedFilesFile != "" {
|
||||
b, err := os.ReadFile(*changedFilesFile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
for scanner.Scan() {
|
||||
if s := strings.TrimSpace(scanner.Text()); s != "" {
|
||||
files = append(files, s)
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
files, err = changedFiles(*baseRef)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
if *verbose {
|
||||
fmt.Fprintln(os.Stderr, "Changed files vs base:")
|
||||
if len(files) == 0 {
|
||||
fmt.Fprintln(os.Stderr, " (none)")
|
||||
} else {
|
||||
for _, f := range files {
|
||||
fmt.Fprintln(os.Stderr, " ", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track module change and CI configuration changes to drive conservative behavior.
|
||||
moduleChanged := false
|
||||
ciChanged := false
|
||||
if *printAllOnChanges {
|
||||
for _, f := range files {
|
||||
if f == "go.mod" || f == "go.sum" {
|
||||
moduleChanged = true
|
||||
}
|
||||
if strings.HasPrefix(f, "scripts/") || strings.HasPrefix(f, ".github/workflows/") {
|
||||
ciChanged = true
|
||||
}
|
||||
}
|
||||
if (moduleChanged || ciChanged) && *mode == "packages" {
|
||||
if *verbose {
|
||||
if moduleChanged {
|
||||
fmt.Fprintln(os.Stderr, "Detected module file change (go.mod/go.sum); selecting all packages ./...")
|
||||
}
|
||||
if ciChanged {
|
||||
fmt.Fprintln(os.Stderr, "Detected CI/detector change (scripts/ or .github/workflows/); selecting all packages ./...")
|
||||
}
|
||||
}
|
||||
fmt.Println("./...")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
directPkgs, err := mapFilesToPackages(files)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if *verbose {
|
||||
// Stable dump of direct packages
|
||||
var dirs []string
|
||||
for p := range directPkgs {
|
||||
dirs = append(dirs, p)
|
||||
}
|
||||
sort.Strings(dirs)
|
||||
fmt.Fprintln(os.Stderr, "Directly changed packages:")
|
||||
if len(dirs) == 0 {
|
||||
fmt.Fprintln(os.Stderr, " (none)")
|
||||
} else {
|
||||
for _, p := range dirs {
|
||||
fmt.Fprintln(os.Stderr, " ", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch *mode {
|
||||
case "packages":
|
||||
affected, err := computeAffectedPackages(directPkgs)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if *verbose {
|
||||
var dbg []string
|
||||
for p := range affected {
|
||||
dbg = append(dbg, p)
|
||||
}
|
||||
sort.Strings(dbg)
|
||||
fmt.Fprintln(os.Stderr, "Final affected packages:")
|
||||
if len(dbg) == 0 {
|
||||
fmt.Fprintln(os.Stderr, " (none)")
|
||||
} else {
|
||||
for _, p := range dbg {
|
||||
fmt.Fprintln(os.Stderr, " ", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Normalize and filter import paths:
|
||||
// - Strip test variant suffixes like "pkg [pkg.test]"
|
||||
// - Exclude e2e test packages (./test/e2e/...)
|
||||
normalized := make(map[string]struct{})
|
||||
for p := range affected {
|
||||
// Trim Go test variant decorations that appear in `go list -test`
|
||||
if idx := strings.Index(p, " ["); idx != -1 {
|
||||
p = p[:idx]
|
||||
}
|
||||
// Exclude synthetic test packages like github.com/org/repo/pkg.name.test
|
||||
if strings.HasSuffix(p, ".test") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(p, "/test/e2e/") {
|
||||
continue
|
||||
}
|
||||
if p != "" {
|
||||
normalized[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
var list []string
|
||||
for p := range normalized {
|
||||
list = append(list, p)
|
||||
}
|
||||
sort.Strings(list)
|
||||
for _, p := range list {
|
||||
fmt.Println(p)
|
||||
}
|
||||
case "suites":
|
||||
// Determine impacted suites by dependency mapping and direct e2e test changes,
|
||||
// then print exact test names for those suites.
|
||||
preflightRoot := "github.com/replicatedhq/troubleshoot/cmd/preflight"
|
||||
supportRoot := "github.com/replicatedhq/troubleshoot/cmd/troubleshoot"
|
||||
|
||||
preflightDeps, err := listPackageWithDeps(preflightRoot)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
supportDeps, err := listPackageWithDeps(supportRoot)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
preflightHit := false
|
||||
supportHit := false
|
||||
|
||||
// Track whether e2e test files were directly changed per suite and collect specific test names
|
||||
changedPreflightTests := make(map[string]struct{})
|
||||
changedSupportTests := make(map[string]struct{})
|
||||
preflightE2EChangedNonGo := false
|
||||
supportE2EChangedNonGo := false
|
||||
for _, f := range files {
|
||||
if strings.HasPrefix(f, "test/e2e/preflight/") {
|
||||
if strings.HasSuffix(f, "_test.go") {
|
||||
// Extract test names from just this file
|
||||
b, err := os.ReadFile(f)
|
||||
if err == nil { // ignore read errors; they will be caught later if needed
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
re := regexp.MustCompile(`^func\s+(Test[\w\d_]+)\s*\(`)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if m := re.FindStringSubmatch(line); m != nil {
|
||||
changedPreflightTests[m[1]] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
preflightHit = true
|
||||
} else {
|
||||
// Non-go change under preflight e2e; run whole suite
|
||||
preflightE2EChangedNonGo = true
|
||||
preflightHit = true
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(f, "test/e2e/support-bundle/") {
|
||||
if strings.HasSuffix(f, "_test.go") {
|
||||
b, err := os.ReadFile(f)
|
||||
if err == nil {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
re := regexp.MustCompile(`^func\s+(Test[\w\d_]+)\s*\(`)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if m := re.FindStringSubmatch(line); m != nil {
|
||||
changedSupportTests[m[1]] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
supportHit = true
|
||||
} else {
|
||||
supportE2EChangedNonGo = true
|
||||
supportHit = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for changed := range directPkgs {
|
||||
if !preflightHit {
|
||||
if _, ok := preflightDeps[changed]; ok {
|
||||
preflightHit = true
|
||||
}
|
||||
}
|
||||
if !supportHit {
|
||||
if _, ok := supportDeps[changed]; ok {
|
||||
supportHit = true
|
||||
}
|
||||
}
|
||||
if preflightHit && supportHit {
|
||||
break
|
||||
}
|
||||
}
|
||||
if *verbose {
|
||||
fmt.Fprintln(os.Stderr, "E2E suite impact:")
|
||||
fmt.Fprintf(os.Stderr, " preflight: %v\n", preflightHit)
|
||||
fmt.Fprintf(os.Stderr, " support-bundle: %v\n", supportHit)
|
||||
}
|
||||
|
||||
// If module files or CI/detector changed, conservatively select all tests for both suites.
|
||||
if moduleChanged || ciChanged {
|
||||
preTests, err := listTestFunctions("test/e2e/preflight")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, tname := range preTests {
|
||||
fmt.Printf("preflight:%s\n", tname)
|
||||
}
|
||||
sbTests, err := listTestFunctions("test/e2e/support-bundle")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, tname := range sbTests {
|
||||
fmt.Printf("support-bundle:%s\n", tname)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Collect tests for impacted suites and print as `<suite>:<TestName>`
|
||||
if preflightHit || supportHit {
|
||||
if preflightHit {
|
||||
toPrint := make(map[string]struct{})
|
||||
if preflightE2EChangedNonGo || len(changedPreflightTests) == 0 {
|
||||
// Run full suite if e2e non-go assets changed or no specific test names collected
|
||||
preTests, err := listTestFunctions("test/e2e/preflight")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, t := range preTests {
|
||||
toPrint[t] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
for t := range changedPreflightTests {
|
||||
toPrint[t] = struct{}{}
|
||||
}
|
||||
}
|
||||
var list []string
|
||||
for t := range toPrint {
|
||||
list = append(list, t)
|
||||
}
|
||||
sort.Strings(list)
|
||||
for _, tname := range list {
|
||||
fmt.Printf("preflight:%s\n", tname)
|
||||
}
|
||||
}
|
||||
if supportHit {
|
||||
toPrint := make(map[string]struct{})
|
||||
if supportE2EChangedNonGo || len(changedSupportTests) == 0 {
|
||||
sbTests, err := listTestFunctions("test/e2e/support-bundle")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, t := range sbTests {
|
||||
toPrint[t] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
for t := range changedSupportTests {
|
||||
toPrint[t] = struct{}{}
|
||||
}
|
||||
}
|
||||
var list []string
|
||||
for t := range toPrint {
|
||||
list = append(list, t)
|
||||
}
|
||||
sort.Strings(list)
|
||||
for _, tname := range list {
|
||||
fmt.Printf("support-bundle:%s\n", tname)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "unknown mode; use 'packages' or 'suites'")
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 0) Preconditions (one-time)
|
||||
export PATH="$(go env GOPATH)/bin:$PATH"
|
||||
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.19.0 >/dev/null
|
||||
go install k8s.io/code-generator/cmd/client-gen@v0.34.0 >/dev/null
|
||||
git fetch origin main --depth=1 || true
|
||||
|
||||
# 1) Determine changed files source: explicit args or git base diff
|
||||
if [ "$#" -gt 0 ]; then
|
||||
# Treat provided paths as changed files
|
||||
CHANGED_CSV=$(printf "%s," "$@" | sed 's/,$//')
|
||||
echo "Simulating changes in: $CHANGED_CSV"
|
||||
PKGS="$(go run ./scripts/affected-packages.go -changed-files "${CHANGED_CSV}")"
|
||||
E2E_OUT="$(go run ./scripts/affected-packages.go -mode=suites -changed-files "${CHANGED_CSV}")"
|
||||
else
|
||||
# Compute base (robust to unrelated histories)
|
||||
BASE="$(git merge-base HEAD origin/main 2>/dev/null || true)"
|
||||
if [ -z "${BASE}" ]; then
|
||||
echo "No merge-base with origin/main → running full set"
|
||||
PKGS="./..."
|
||||
E2E_OUT="$(go run ./scripts/affected-packages.go -mode=suites -changed-files go.mod || true)"
|
||||
else
|
||||
PKGS="$(go run ./scripts/affected-packages.go -base "${BASE}")"
|
||||
E2E_OUT="$(go run ./scripts/affected-packages.go -mode=suites -base "${BASE}")"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2) Print what will run
|
||||
echo "=== Affected unit packages ==="
|
||||
if [ -n "${PKGS}" ]; then echo "${PKGS}"; else echo "(none)"; fi
|
||||
echo
|
||||
echo "=== Affected e2e tests ==="
|
||||
if [ -n "${E2E_OUT}" ]; then echo "${E2E_OUT}"; else echo "(none)"; fi
|
||||
echo
|
||||
|
||||
# 3) Unit tests via Makefile (inherits required build tags)
|
||||
if [ "${PKGS}" = "./..." ]; then
|
||||
echo "Running: make test (all)"
|
||||
make test
|
||||
elif [ -n "${PKGS}" ]; then
|
||||
echo "Running: make test-packages for affected pkgs"
|
||||
PACKAGES="$(echo "${PKGS}" | xargs)" make test-packages
|
||||
else
|
||||
echo "No affected unit packages"
|
||||
fi
|
||||
|
||||
# 4) E2E tests via Makefile (filtered by regex)
|
||||
PRE="$(echo "${E2E_OUT}" | awk -F: '$1=="preflight"{print $2}' | paste -sd'|' -)"
|
||||
SB="$( echo "${E2E_OUT}" | awk -F: '$1=="support-bundle"{print $2}' | paste -sd'|' -)"
|
||||
|
||||
# Use direct go test with the same build tags as the Makefile to avoid RUN quoting issues locally
|
||||
BUILD_TAGS='netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp'
|
||||
|
||||
overall=0
|
||||
|
||||
if [ -n "${PRE}" ]; then
|
||||
echo "Running preflight e2e: ${PRE}"
|
||||
go test -tags "${BUILD_TAGS}" -installsuffix netgo -v -count=1 ./test/e2e/preflight -run "^(${PRE})$" || overall=1
|
||||
fi
|
||||
if [ -n "${SB}" ]; then
|
||||
echo "Running support-bundle e2e: ${SB}"
|
||||
go test -tags "${BUILD_TAGS}" -installsuffix netgo -v -count=1 ./test/e2e/support-bundle -run "^(${SB})$" || overall=1
|
||||
fi
|
||||
|
||||
exit $overall
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive test for affected test detection
|
||||
# Tests various code change scenarios to ensure correct suite detection
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
echo "========================================"
|
||||
echo "Affected Test Detection Validation"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Helper function to run test
|
||||
run_test() {
|
||||
local test_name="$1"
|
||||
local test_file="$2"
|
||||
local expected_suites="$3"
|
||||
|
||||
echo -e "${BLUE}Test: $test_name${NC}"
|
||||
echo "File: $test_file"
|
||||
echo "Expected: $expected_suites"
|
||||
|
||||
# Get affected tests from explicit changed files (no git required); detector prints <suite>:<TestName>
|
||||
local detector_output=$(go run ./scripts/affected-packages.go -mode=suites -changed-files "$test_file" 2>/dev/null)
|
||||
# Derive suites from prefixes for comparison
|
||||
local actual_suites=$(echo "$detector_output" | cut -d':' -f1 | grep -v '^$' | sort | uniq | tr '\n' ' ' | xargs)
|
||||
|
||||
# Compare results
|
||||
if [ "$actual_suites" = "$expected_suites" ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Got: $actual_suites"
|
||||
if [ -n "$detector_output" ]; then
|
||||
echo "Tests:" && echo "$detector_output" | sed 's/^/ - /'
|
||||
fi
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Got: '$actual_suites', Expected: '$expected_suites'"
|
||||
if [ -n "$detector_output" ]; then
|
||||
echo "Tests:" && echo "$detector_output" | sed 's/^/ - /'
|
||||
fi
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 1: Preflight-only package (should only trigger preflight)
|
||||
run_test "Preflight-only package change" \
|
||||
"pkg/preflight/run.go" \
|
||||
"preflight"
|
||||
|
||||
# Test 2: Support-bundle-only package
|
||||
run_test "Support-bundle-only package change" \
|
||||
"pkg/supportbundle/supportbundle.go" \
|
||||
"support-bundle"
|
||||
|
||||
# Test 3: Shared package - collect
|
||||
run_test "Shared package (collect) change" \
|
||||
"pkg/collect/run.go" \
|
||||
"preflight support-bundle"
|
||||
|
||||
# Test 4: Shared package - analyze
|
||||
run_test "Shared package (analyze) change" \
|
||||
"pkg/analyze/analyzer.go" \
|
||||
"preflight support-bundle"
|
||||
|
||||
# Test 5: Shared package - k8sutil
|
||||
run_test "Shared package (k8sutil) change" \
|
||||
"pkg/k8sutil/config.go" \
|
||||
"preflight support-bundle"
|
||||
|
||||
# Test 6: Shared package - convert
|
||||
run_test "Shared package (convert) change" \
|
||||
"pkg/convert/output.go" \
|
||||
"preflight support-bundle"
|
||||
|
||||
# Test 7: Shared package - redact (another shared one)
|
||||
run_test "Shared package (redact) change" \
|
||||
"pkg/redact/redact.go" \
|
||||
"preflight support-bundle"
|
||||
|
||||
# Test 8: Preflight command (should only trigger preflight)
|
||||
run_test "Preflight command change" \
|
||||
"cmd/preflight/main.go" \
|
||||
"preflight"
|
||||
|
||||
# Test 9: Support-bundle types (support-bundle only package)
|
||||
run_test "Support-bundle types change" \
|
||||
"pkg/supportbundle/types/types.go" \
|
||||
"support-bundle"
|
||||
|
||||
# Test 10: Workflow file (should not trigger e2e)
|
||||
echo -e "${BLUE}Test: Workflow file change (should trigger nothing)${NC}"
|
||||
echo "File: .github/workflows/affected-tests.yml"
|
||||
echo "Expected: (no suites)"
|
||||
|
||||
detector_output=$(go run ./scripts/affected-packages.go -mode=suites -changed-files ".github/workflows/affected-tests.yml" 2>/dev/null)
|
||||
actual_suites=$(echo "$detector_output" | cut -d':' -f1 | grep -v '^$' | sort | uniq | tr '\n' ' ' | xargs)
|
||||
|
||||
if [ -z "$actual_suites" ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - No suites affected (as expected)"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Got: '$actual_suites', Expected: (empty)"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 11: go.mod change (should trigger all)
|
||||
echo -e "${BLUE}Test: go.mod change (should trigger all suites)${NC}"
|
||||
echo "File: go.mod"
|
||||
echo "Expected: preflight support-bundle"
|
||||
|
||||
detector_output=$(go run ./scripts/affected-packages.go -mode=suites -changed-files "go.mod" 2>/dev/null)
|
||||
actual_suites=$(echo "$detector_output" | cut -d':' -f1 | grep -v '^$' | sort | uniq | tr '\n' ' ' | xargs)
|
||||
|
||||
if [ "$actual_suites" = "preflight support-bundle" ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Got: $actual_suites"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Got: '$actual_suites', Expected: 'preflight support-bundle'"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 12: Multiple files across different areas
|
||||
echo -e "${BLUE}Test: Multiple file changes (support-bundle + shared)${NC}"
|
||||
echo "Files: pkg/supportbundle/supportbundle.go + pkg/collect/run.go"
|
||||
echo "Expected: preflight support-bundle"
|
||||
|
||||
detector_output=$(go run ./scripts/affected-packages.go -mode=suites -changed-files "pkg/supportbundle/supportbundle.go,pkg/collect/run.go" 2>/dev/null)
|
||||
actual_suites=$(echo "$detector_output" | cut -d':' -f1 | grep -v '^$' | sort | uniq | tr '\n' ' ' | xargs)
|
||||
|
||||
if [ "$actual_suites" = "preflight support-bundle" ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Got: $actual_suites"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Got: '$actual_suites', Expected: 'preflight support-bundle'"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 13: README change (should not trigger e2e)
|
||||
echo -e "${BLUE}Test: Documentation change (should trigger nothing)${NC}"
|
||||
echo "File: README.md"
|
||||
echo "Expected: (no suites)"
|
||||
|
||||
detector_output=$(go run ./scripts/affected-packages.go -mode=suites -changed-files "README.md" 2>/dev/null)
|
||||
actual_suites=$(echo "$detector_output" | cut -d':' -f1 | grep -v '^$' | sort | uniq | tr '\n' ' ' | xargs)
|
||||
|
||||
if [ -z "$actual_suites" ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - No suites affected (as expected)"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Got: '$actual_suites', Expected: (empty)"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "========================================"
|
||||
echo -e "${GREEN}Tests Passed: $TESTS_PASSED${NC}"
|
||||
echo -e "${RED}Tests Failed: $TESTS_FAILED${NC}"
|
||||
echo "========================================"
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Some tests failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user