Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0e31e23
test(act): mock steps and jobs, add artifacts interface, add more tests
xnyo Dec 10, 2025
b42aea7
re-add localRepositoryArgs function
xnyo Dec 10, 2025
d7f10a3
args cleanup
xnyo Dec 10, 2025
238ec5e
cleanup args, add some comments
xnyo Dec 10, 2025
1d82d60
removed unused function NoOpStep
xnyo Dec 10, 2025
bc73fae
Update cache warmup workflow inputs
xnyo Dec 10, 2025
eddefd6
restore dist mockdata from main
xnyo Dec 10, 2025
225a1fa
add comments
xnyo Dec 10, 2025
9309d1b
small docstrings updates
xnyo Dec 10, 2025
0bfdbf0
Merge branch 'main' into giuseppe/act-follow-up-3
xnyo Dec 11, 2025
6f8c5a0
fix cache warmup
xnyo Dec 11, 2025
9c4a289
Merge remote-tracking branch 'origin/giuseppe/act-follow-up-3' into g…
xnyo Dec 11, 2025
549d015
mock GCS
xnyo Dec 11, 2025
448c469
gcs tests
xnyo Dec 11, 2025
2d38c79
update clean-act-tmp Makefile target
xnyo Dec 11, 2025
1253914
parallel gcs tests
xnyo Dec 11, 2025
12a8ae8
enable gha
xnyo Dec 11, 2025
30daf5c
output from mocked gcs upload step
xnyo Dec 11, 2025
22eeb65
add docstrings
xnyo Dec 11, 2025
acb3274
Merge branch 'main' into giuseppe/act-gcs
xnyo Dec 12, 2025
4c97d32
remove extra branch
xnyo Dec 12, 2025
f308522
add comments
xnyo Dec 12, 2025
a37a0da
refactoring
xnyo Dec 12, 2025
25f00f8
Merge branch 'main' into giuseppe/act-gcs
xnyo Dec 12, 2025
29d179f
fix mock gcs upload output
xnyo Dec 16, 2025
710ff84
fix(ci): fix universal zip url output in gcs step
xnyo Dec 16, 2025
f3e4c7c
update gcs tests assertions
xnyo Dec 16, 2025
6623680
remove pr event skip
xnyo Dec 16, 2025
ec7be72
update assertions
xnyo Dec 16, 2025
3fbb646
Merge remote-tracking branch 'origin/giuseppe/act-gcs' into giuseppe/…
xnyo Dec 16, 2025
9ed528d
remove extra output
xnyo Dec 16, 2025
2ebb8ce
keep walking in checkFilesExist
xnyo Dec 16, 2025
e87c3ff
Merge branch 'main' into giuseppe/act-gcs
xnyo Dec 16, 2025
84c6cff
allow WithNoOpStep to replace steps in any job
xnyo Dec 16, 2025
7914ca3
tweaks to validator to make it run in act + docker
xnyo Dec 16, 2025
8d5747d
fix package.sh not working with POSIX sed
xnyo Dec 16, 2025
007565c
Add mock-dist-artifacts Makefile target, refactor Makefile
xnyo Dec 16, 2025
89123ff
plugin validator ci tests
xnyo Dec 16, 2025
19964a2
add dist-artifacts-unsigned mockdata
xnyo Dec 16, 2025
2aa9877
parse gha summary
xnyo Dec 16, 2025
9007ab6
parse gha summary arguments
xnyo Dec 16, 2025
46440ae
add WithVerbose
xnyo Dec 16, 2025
4beb9a6
assert validator summary entries
xnyo Dec 16, 2025
fc87678
disable verbose
xnyo Dec 16, 2025
d981545
enable act
xnyo Dec 16, 2025
afd0adf
fix mock packaged dist artifact step output
xnyo Dec 16, 2025
00fb1b0
add test case for validator failure
xnyo Dec 16, 2025
365af52
fix dist folder for simple-frontend-yarn test case
xnyo Dec 16, 2025
33ba86c
run in parallel
xnyo Dec 16, 2025
90fd77d
remove plugin-validator container after running it
xnyo Dec 17, 2025
a1ea7d4
sanity check mockdata folders before mounting them
xnyo Dec 17, 2025
9e822ee
update comments
xnyo Dec 17, 2025
a812218
fix validator tests
xnyo Dec 17, 2025
34f08c9
refactoring
xnyo Dec 17, 2025
1614501
use FailNow
xnyo Dec 17, 2025
9fb685f
remove unnecessary comment
xnyo Dec 17, 2025
c31e7aa
remove another unnecessary comment
xnyo Dec 17, 2025
0e76613
Merge branch 'main' into giuseppe/act-gcs
xnyo Dec 17, 2025
48fd515
update makefile
xnyo Dec 17, 2025
6414d7e
Merge branch 'main' into giuseppe/act-gcs
xnyo Dec 17, 2025
c1d4f65
update act cache warmup versions
xnyo Dec 17, 2025
aa39fd1
Merge branch 'giuseppe/act-gcs' into giuseppe/ac-validator
xnyo Dec 17, 2025
010e917
Merge branch 'main' into giuseppe/ac-validator
xnyo Dec 17, 2025
4bba106
revert pr trigger
xnyo Dec 17, 2025
3a68fe6
add clean-lfs makefile target
xnyo Dec 17, 2025
60f82f1
actionlint
xnyo Dec 17, 2025
e7db501
Merge branch 'main' into giuseppe/ac-validator
xnyo Jan 7, 2026
fab4111
renamed "summary" to more correct "annotation"
xnyo Jan 7, 2026
5f26f58
disable osv-scanner to fix flaky tests
xnyo Jan 7, 2026
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
26 changes: 18 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ jobs:
shell: bash

- name: Replace plugin version
id: replace-plugin-version
if: ${{ inputs.plugin-version-suffix != '' }}
run: |
package_json_path="$PLUGIN_DIRECTORY/package.json"
Expand Down Expand Up @@ -572,8 +573,8 @@ jobs:
if [ -n "${PLUGIN_VALIDATOR_CONFIG}" ]; then
# User-provided configuration content
echo "Using provided plugin-validator configuration content."
PLUGIN_VALIDATOR_CONFIG_PATH=".plugin-validator.yaml"
echo "${PLUGIN_VALIDATOR_CONFIG}" > ${PLUGIN_VALIDATOR_CONFIG_PATH}
PLUGIN_VALIDATOR_CONFIG_PATH=$(mktemp)
echo "${PLUGIN_VALIDATOR_CONFIG}" > "${PLUGIN_VALIDATOR_CONFIG_PATH}"
elif [ -n "${PLUGIN_VALIDATOR_CONFIG_PATH}" ]; then
# User-provided configuration file path
echo "Using plugin-validator configuration file at path: ${PLUGIN_VALIDATOR_CONFIG_PATH}"
Expand All @@ -583,8 +584,8 @@ jobs:
fi
else
# Default hardcoded configuration
PLUGIN_VALIDATOR_CONFIG_PATH=".plugin-validator.yaml"
echo "${PLUGIN_VALIDATOR_CONFIG_PATH} configuration file is missing. Providing a default one as fallback."
PLUGIN_VALIDATOR_CONFIG_PATH=$(mktemp)
cat <<EOF > "${PLUGIN_VALIDATOR_CONFIG_PATH}"
global:
enabled: true
Expand All @@ -598,19 +599,28 @@ jobs:
# will recurse into the plugin's directory (including node_modules).
mkdir -p /tmp/empty

# Copy workspace to a new tempdir to avoid issues when mounting the folder when running with act
TEMP_WORKSPACE_DIR=$(mktemp -d)
cp -r "${PLUGIN_DIRECTORY}"/* "$TEMP_WORKSPACE_DIR/"

# Give a UUID to each container so multiple can be run in parallel when running with act
UUID=$(cat /proc/sys/kernel/random/uuid)

# Do not run clamav because it takes too long
docker run --name=plugin-validator --pull=always \
-v "$PWD/${PLUGIN_VALIDATOR_CONFIG_PATH}:/workspace/.plugin-validator.yaml:ro" \
-v "$PWD/${PLUGIN_DIRECTORY}/dist-artifacts:/workspace/dist-artifacts:ro" \
-v "$PWD/${PLUGIN_DIRECTORY}:/workspace" \
docker run --name="plugin-validator-${UUID}" --pull=always --platform=linux/amd64 \
-v "${PLUGIN_VALIDATOR_CONFIG_PATH}:/workspace/.plugin-validator.yaml:ro" \
-v "${TEMP_WORKSPACE_DIR}/dist-artifacts:/workspace/dist-artifacts:ro" \
-v "${TEMP_WORKSPACE_DIR}:/workspace" \
-v "/tmp/empty:/workspace/node_modules" \
-e SKIP_CLAMAV=1 \
grafana/plugin-validator-cli \
-ghaOutput \
-config=/workspace/.plugin-validator.yaml \
-sourceCodeUri=file:///workspace \
"/workspace/dist-artifacts/${UNIVERSAL_ZIP}"
exit "$(docker inspect plugin-validator --format='{{.State.ExitCode}}')"
EXIT_CODE=$(docker inspect "plugin-validator-${UUID}" --format='{{.State.ExitCode}}')
docker rm "plugin-validator-${UUID}"
exit "$EXIT_CODE"
env:
UNIVERSAL_ZIP: ${{ steps.universal-zip.outputs.zip }}
PLUGIN_VALIDATOR_CONFIG: ${{ inputs.plugin-validator-config }}
Expand Down
28 changes: 20 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
.PHONY: clean-mockdata clean-node-modules clean-act-tmp mockdata-dist
.PHONY: reset-mockdata clean-node-modules clean-act-tmp clean-lfs mockdata-dist mockdata-dist-artifacts mockdata

clean-node-modules:
find tests -name node_modules -type d -prune -exec rm -rf '{}' +

clean-dist:
find tests ! -path '*/act/*' -name dist -type d -prune -exec rm -rf '{}' +

clean-act-tmp:
rm -rf /tmp/act-artifacts
rm -rf /tmp/act-cache
Expand All @@ -11,16 +14,25 @@ clean-act-tmp:
clean-act-toolcache-volumes:
docker volume ls -q | grep "^act-toolcache-" | xargs docker volume rm

clean: clean-node-modules clean-act-tmp clean-act-toolcache-volumes
clean-lfs:
git lfs prune

clean: clean-node-modules clean-dist clean-act-tmp clean-act-toolcache-volumes clean-lfs

clean-mockdata:
reset-mockdata:
rm -rf tests/act/mockdata/dist/*
rm -rf tests/act/mockdata/dist-artifacts-unsigned/*

mockdata-dist: clean-mockdata
./scripts/mockdata-dist.sh simple-frontend
./scripts/mockdata-dist.sh simple-frontend-yarn
./scripts/mockdata-dist.sh simple-frontend-pnpm
./scripts/mockdata-dist.sh simple-backend
mockdata-dist: reset-mockdata
for tc in $$(./scripts/find-tests.sh); do \
./scripts/mockdata-dist.sh $$tc; \
done
@echo All done!

mockdata-dist-artifacts: mockdata-dist
for tc in $$(./scripts/find-tests.sh); do \
./scripts/mockdata-dist-artifacts.sh $$tc; \
done
@echo All done!

mockdata: mockdata-dist-artifacts
2 changes: 1 addition & 1 deletion actions/internal/plugins/package/package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fi
exe_basename=$(basename $exe)
for file in $(find "$backend_folder" -type f -name "${exe_basename}_*"); do
# Extract os+arch from the file name
os_arch=$(echo $(basename $file) | sed -E "s|${exe_basename}_(\w+)(.exe)?|\1|")
os_arch=$(echo $(basename $file) | sed -E "s|${exe_basename}_([a-zA-Z0-9_]+)(.exe)?|\1|")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\w is not POSIX compliant and doesn't work on Mac OS


# Temporary folder for the zip file
tmp=$(mktemp -d)
Expand Down
13 changes: 13 additions & 0 deletions scripts/find-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail

# Prints all testdata plugins found in the tests/ folder to stdout, one per line.
# The "act" folder is excluded since it contains the tests themselves.
# Can be used to loop over all test plugins, like this:
#
# for tc in $(./scripts/find-tests.sh); do
# echo $tc
# done

cd tests
find . -maxdepth 1 -type d ! -name '.' ! -name 'act' -print
21 changes: 21 additions & 0 deletions scripts/mockdata-dist-artifacts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail

if [ "$#" -ne 1 ]; then
echo "Usage: $0 <test-plugin-folder-name>"
exit 1
fi

echo "[$1] Preparing mockdata (dist-artifacts)"
cd "$(dirname "$0")/.."


mkdir -p "tests/act/mockdata/dist-artifacts-unsigned/$1"

echo "[$1] Packaging os/arch ZIPs"
# Will exit with 0 if the plugin has no backend
# (in that case, there's no need for os/arch ZIPs, just universal)
./actions/internal/plugins/package/package.sh "tests/act/mockdata/dist/$1" "tests/act/mockdata/dist-artifacts-unsigned/$1"

echo "[$1] Packaging universal ZIPs"
./actions/internal/plugins/package/package.sh -u "tests/act/mockdata/dist/$1" "tests/act/mockdata/dist-artifacts-unsigned/$1"
2 changes: 1 addition & 1 deletion scripts/mockdata-dist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if [ "$#" -ne 1 ]; then
exit 1
fi

echo "[$1] Preparing mockdata"
echo "[$1] Preparing mockdata (dist)"
cd "$(dirname "$0")/.."
cd tests/$1

Expand Down
85 changes: 80 additions & 5 deletions tests/act/internal/act/act.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,38 @@ type Runner struct {

// Verbose enables logging of JSON output from act back to stdout.
Verbose bool

// ContainerArchitecture is the architecture to use for act containers.
// By default, act uses the architecture of the host machine.
// This can be useful to force a specific platform when running on ARM Macs.
ContainerArchitecture string
}

// RunnerOption is a function that configures a Runner.
type RunnerOption func(r *Runner)

// WithVerbose enables or disables verbose logging of act output.
func WithVerbose(verbose bool) RunnerOption {
return func(r *Runner) {
r.Verbose = verbose
}
}

// WithContainerArchitecture sets the container architecture to use for act.
func WithContainerArchitecture(architecture string) RunnerOption {
return func(r *Runner) {
r.ContainerArchitecture = architecture
}
}

// WithLinuxAMD64ContainerArchitecture sets the container architecture to linux/amd64.
// This is useful when running on ARM Macs to ensure compatibility with x64 images.
func WithLinuxAMD64ContainerArchitecture() RunnerOption {
return WithContainerArchitecture("linux/amd64")
}

// NewRunner creates a new Runner instance.
func NewRunner(t *testing.T) (*Runner, error) {
func NewRunner(t *testing.T, opts ...RunnerOption) (*Runner, error) {
// Get GitHub token from environment (GHA) or gh CLI (local)
ghToken, ok := os.LookupEnv("GITHUB_TOKEN")
if !ok || ghToken == "" {
Expand All @@ -86,6 +114,12 @@ func NewRunner(t *testing.T) (*Runner, error) {
if err != nil {
return nil, fmt.Errorf("new gcs: %w", err)
}

// Apply options
for _, opt := range opts {
opt(r)
}

return r, nil
}

Expand Down Expand Up @@ -113,8 +147,11 @@ func (r *Runner) args(eventKind EventKind, workflowFile string, payloadFile stri
// Required for cloning private repos
"--secret", "GITHUB_TOKEN=" + r.gitHubToken,

// Mount mockdata (for mocked testdata, dist artifacts) and GCS (for mocked GCS)
"--container-options", `"-v $PWD/tests/act/mockdata:/mockdata -v ` + r.GCS.basePath + `:/gcs"`,
// Mounts:
// - mockdata: for mocked testdata, dist artifacts
// - GCS: for mocked GCS
// - /tmp: for temporary files, so the host's /tmp is used
"--container-options", `"-v $PWD/tests/act/mockdata:/mockdata -v ` + r.GCS.basePath + `:/gcs -v /tmp:/tmp"`,
}

// Map local all possible references of plugin-ci-workflows to the local repository
Expand All @@ -127,6 +164,9 @@ func (r *Runner) args(eventKind EventKind, workflowFile string, payloadFile stri
if r.ConcurrentJobs > 0 {
args = append(args, "--concurrent-jobs", fmt.Sprint(r.ConcurrentJobs))
}
if r.ContainerArchitecture != "" {
args = append(args, "--container-architecture", r.ContainerArchitecture)
}
// Map all self-hosted runners otherwise they don't run in act.
for _, label := range selfHostedRunnerLabels {
args = append(args, "-P", label+"="+nektosActRunnerImage)
Expand Down Expand Up @@ -296,9 +336,18 @@ func (r *Runner) parseGHACommand(data logLine, runResult *RunResult) {
// Store the output value. StepID can be an array in case of composite actions,
// group all composite action outputs under the first step ID for simplicity.
runResult.Outputs.Set(data.JobID, data.StepID[0], data.Name, data.Arg)
case "debug", "notice", "warning", "error":
// Annotations
runResult.Annotations = append(runResult.Annotations, Annotation{
Level: AnnotationLevel(data.Command),
Title: data.KvPairs["title"],
Message: data.Arg,
})
default:
// Nothing special to do, ignore silently
break
// Nothing special to do
if r.Verbose && data.Command != "" {
fmt.Printf("%s: [%s]: unhandled GHA command %q, ignoring", r.t.Name(), data.Job, data.Command)
}
}
}

Expand Down Expand Up @@ -366,6 +415,32 @@ type RunResult struct {

// Outputs contains the outputs for each job + step of the workflow run.
Outputs Outputs

// Annotations contains the GitHub Actions annotations generated during the workflow run.
Annotations []Annotation
}

// AnnotationLevel represents the level of a GitHub Actions annotation.
type AnnotationLevel string

// Annotation levels
const (
AnnotationLevelDebug AnnotationLevel = "debug"
AnnotationLevelNotice AnnotationLevel = "notice"
AnnotationLevelWarning AnnotationLevel = "warning"
AnnotationLevelError AnnotationLevel = "error"
)

// Annotation represents a single GitHub Actions annotation.
type Annotation struct {
// Level is the level of the annotation.
Level AnnotationLevel

// Title is the optional title of the annotation.
Title string

// Message is the message of the annotation itself.
Message string
}

// newRunResult creates a new empty RunResult instance.
Expand Down
7 changes: 4 additions & 3 deletions tests/act/internal/act/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ type logLine struct {

// Intercepted GHA commands

Command string `json:"command,omitempty"`
Name string `json:"name,omitempty"`
Arg string `json:"arg,omitempty"`
Command string `json:"command,omitempty"`
Name string `json:"name,omitempty"`
Arg string `json:"arg,omitempty"`
KvPairs map[string]string `json:"kvPairs,omitempty"`
}
Loading