Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
895cef0
Always run CI when plugin READMEs change
delanni Oct 6, 2025
4f8d4fe
bootstrap .buildkite upon yarn kbn bootstrap
delanni Oct 7, 2025
f418e46
collect stashed changes and commit&push them after quick-checks
delanni Oct 7, 2025
70074d1
check_for_changed_files with STASH_QUICKCHECK_CHANGES=true will stash…
delanni Oct 7, 2025
7a28edd
set STASH_QUICKCHECK_CHANGES=true in the quickcheck runner
delanni Oct 7, 2025
6cd0ae4
break multiple quickchecks
delanni Oct 7, 2025
83552e7
try only stashing tracked files
delanni Oct 7, 2025
9b759a8
Merge branch 'main' into improve-quick-checks
delanni Oct 10, 2025
02763bd
make file-changing quick checks run on one thread, push once all is done
delanni Oct 21, 2025
36703e2
fix quick check message setup
delanni Oct 22, 2025
383809b
fix typecheck issue
delanni Oct 22, 2025
e6b98ad
turn on change collection, fail auto-corrected quick-check steps
delanni Oct 22, 2025
e5ccf77
Merge branch 'main' into improve-quick-checks
delanni Oct 22, 2025
68b10ae
Merge branch 'main' into improve-quick-checks
delanni Oct 27, 2025
d2deb86
commit changes after the quick-checks ran
delanni Oct 27, 2025
c5d3d2b
correctly break checks with changed files
delanni Oct 27, 2025
8d78f91
remove function to collect and commit together
delanni Oct 27, 2025
5309854
Changes from node scripts/lint_ts_projects --fix
kibanamachine Oct 27, 2025
1d84aac
re-add sleep before exiting
delanni Oct 27, 2025
4227b2e
Changes from node scripts/build_plugin_list_docs
kibanamachine Oct 27, 2025
cf2c820
Changes from node scripts/generate codeowners
kibanamachine Oct 27, 2025
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
2 changes: 1 addition & 1 deletion .buildkite/pull_requests.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"always_require_ci_on_changed": [
"^docs/developer/plugin-list.asciidoc$",
"^\\.github/CODEOWNERS$",
"/plugins/[^/]+/readme\\.(md|asciidoc)$"
"/plugins/([^/]+/)+readme\\.(md|asciidoc)$"
],
"enable_trigger_checkbox": true,
"kibana_versions_check": true,
Expand Down
22 changes: 5 additions & 17 deletions .buildkite/scripts/common/util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,39 +32,27 @@ check_for_changed_files() {
C_RESET='\033[0m' # Reset color

SHOULD_AUTO_COMMIT_CHANGES="${2:-}"
CUSTOM_FIX_MESSAGE="${3:-}"
CUSTOM_FIX_MESSAGE="${3:-Changes from $1}"
GIT_CHANGES="$(git status --porcelain -- . ':!:config/node.options' ':!config/kibana.yml')"

if [ "$GIT_CHANGES" ]; then
if ! is_auto_commit_disabled && [[ "$SHOULD_AUTO_COMMIT_CHANGES" == "true" && "${BUILDKITE_PULL_REQUEST:-false}" != "false" ]]; then
NEW_COMMIT_MESSAGE="[CI] Auto-commit changed files from '$1'"
PREVIOUS_COMMIT_MESSAGE="$(git log -1 --pretty=%B)"

if [[ "$NEW_COMMIT_MESSAGE" == "$PREVIOUS_COMMIT_MESSAGE" ]]; then
echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n"
echo -e "$GIT_CHANGES\n"
echo -e "CI already attempted to commit these changes, but the file(s) seem to have changed again."
echo -e "Please review and fix manually."
exit 1
fi

echo "'$1' caused changes to the following files:"
echo "$GIT_CHANGES"
echo ""
echo "Auto-committing these changes now. A new build should start soon if successful."
echo "Auto-committing & pushing these changes now."

git config --global user.name kibanamachine
git config --global user.email '42973632+kibanamachine@users.noreply.github.com'
gh pr checkout "${BUILDKITE_PULL_REQUEST}"
git add -A -- . ':!config/node.options' ':!config/kibana.yml'

git commit -m "$NEW_COMMIT_MESSAGE"
git commit -m "$CUSTOM_FIX_MESSAGE"
git push

# After the git push, the new commit will trigger a new build within a few seconds and this build should get cancelled
# So, let's just sleep to give the build time to cancel itself without an error
# If it doesn't get cancelled for some reason, then exit with an error, because we don't want this build to be green (we just don't want it to generate an error either)
# Wait to ensure all commits arrive before we terminate the build
sleep 300
# Still exit with error to fail the current build, a new build should be started after the push
exit 1
else
echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n"
Expand Down
88 changes: 88 additions & 0 deletions .buildkite/scripts/steps/checks/quick_checks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
[
{
"script": ".buildkite/scripts/steps/checks/ts_projects.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/packages.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/verify_notice.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/plugin_list_docs.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/event_log.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/telemetry.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/jest_configs.sh"
},
{
"script": ".buildkite/scripts/steps/checks/bundle_limits.sh"
},
{
"script": ".buildkite/scripts/steps/checks/i18n.sh"
},
{
"script": ".buildkite/scripts/steps/checks/file_casing.sh"
},
{
"script": ".buildkite/scripts/steps/checks/licenses.sh"
},
{
"script": ".buildkite/scripts/steps/checks/test_projects.sh"
},
{
"script": ".buildkite/scripts/steps/checks/test_hardening.sh"
},
{
"script": ".buildkite/scripts/steps/checks/ftr_configs.sh"
},
{
"script": ".buildkite/scripts/steps/checks/yarn_deduplicate.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/prettier_topology.sh"
},
{
"script": ".buildkite/scripts/steps/checks/renovate.sh"
},
{
"script": ".buildkite/scripts/steps/checks/native_modules.sh"
},
{
"script": ".buildkite/scripts/steps/checks/test_files_missing_owner.sh"
},
{
"script": ".buildkite/scripts/steps/checks/styled_components_mapping.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/dependencies_missing_owner.sh"
},
{
"script": ".buildkite/scripts/steps/checks/validate_pipelines.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/check_scout_config.sh"
},
{
"script": ".buildkite/scripts/steps/checks/dependencies_diff.sh",
"mayChangeFiles": true
},
{
"script": ".buildkite/scripts/steps/checks/verify_codeowners.sh",
"mayChangeFiles": true
}
]
2 changes: 1 addition & 1 deletion .buildkite/scripts/steps/checks/yarn_deduplicate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ set -euo pipefail
source .buildkite/scripts/common/util.sh

echo "--- Check yarn.lock for duplicated modules"
node scripts/yarn_deduplicate && yarn kbn bootstrap
node scripts/yarn_deduplicate && yarn kbn bootstrap --force-install

check_for_changed_files 'node scripts/yarn_deduplicate' true 'TO FIX: Run node '"'"'scripts/yarn_deduplicate && yarn kbn bootstrap'"'"' locally, or add an exception to src/dev/yarn_deduplicate/index.ts and then commit the changes and push to your branch'
4 changes: 3 additions & 1 deletion .buildkite/scripts/steps/quick_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

set -euo pipefail

source .buildkite/scripts/common/util.sh

if [[ "${CI:-}" =~ ^(1|true)$ ]]; then
export DISABLE_BOOTSTRAP_VALIDATION=false
.buildkite/scripts/bootstrap.sh
fi

node scripts/quick_checks --file .buildkite/scripts/steps/checks/quick_checks.txt
node scripts/quick_checks --file .buildkite/scripts/steps/checks/quick_checks.json
4 changes: 4 additions & 0 deletions kbn_pm/src/commands/bootstrap/bootstrap_command.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { regenerateTsconfigPaths } from './regenerate_tsconfig_paths.mjs';
import { regenerateBaseTsconfig } from './regenerate_base_tsconfig.mjs';
import { discovery } from './discovery.mjs';
import { updatePackageJson } from './update_package_json.mjs';
import { bootstrapBuildkite } from './buildkite.mjs';

const IS_CI = process.env.CI?.match(/(1|true)/i);

Expand Down Expand Up @@ -86,6 +87,9 @@ export const command = {
time('regenerate tsconfig.base.json', async () => {
await regenerateBaseTsconfig(packages, log);
}),
time('bootstrap .buildkite folder', async () => {
await bootstrapBuildkite();
}),
]);

/**
Expand Down
20 changes: 20 additions & 0 deletions kbn_pm/src/commands/bootstrap/buildkite.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { run } from '../../lib/spawn.mjs';
import { REPO_ROOT } from '../../lib/paths.mjs';
import { resolve } from 'path';

/**
* Bootstraps the the .buildkite directory
* @return {Promise<*>}
*/
export function bootstrapBuildkite() {
return run('npm', ['i'], { cwd: resolve(REPO_ROOT, '.buildkite') });
}
5 changes: 4 additions & 1 deletion packages/kbn-eslint-plugin-telemetry/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
},
"include": ["**/*.ts"],
"exclude": ["target/**/*"],
"kbn_references": ["@kbn/repo-packages", "@kbn/repo-info"]
"kbn_references": [
"@kbn/repo-packages",
"@kbn/repo-info",
]
}
68 changes: 53 additions & 15 deletions src/dev/run_quick_checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ import type { ToolingLog } from '@kbn/tooling-log';

const MAX_PARALLELISM = availableParallelism();
const buildkiteQuickchecksFolder = join('.buildkite', 'scripts', 'steps', 'checks');
const quickChecksList = join(buildkiteQuickchecksFolder, 'quick_checks.txt');
const quickChecksList = join(buildkiteQuickchecksFolder, 'quick_checks.json');
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

interface QuickCheck {
script: string;
mayChangeFiles?: boolean;
// Additional properties can be added here in the future
}

interface CheckResult {
success: boolean;
script: string;
Expand Down Expand Up @@ -52,18 +58,26 @@ let logger: ToolingLog;
void run(async ({ log, flagsReader }) => {
logger = log;

const scriptsToRun = collectScriptsToRun({
const checksToRun = collectScriptsToRun({
targetFile: flagsReader.string('file'),
targetDir: flagsReader.string('dir'),
checks: flagsReader.string('checks'),
}).map((script) => (isAbsolute(script) ? script : join(REPO_ROOT, script)));
});

// Partition checks based on mayChangeFiles flag
const fileChangingChecks = checksToRun
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lodash partition is good for this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it will end up with similar readability, I'll stick to this for now

.filter((check) => check.mayChangeFiles)
.map((check) => (isAbsolute(check.script) ? check.script : join(REPO_ROOT, check.script)));

const regularChecks = checksToRun
.filter((check) => !check.mayChangeFiles)
.map((check) => (isAbsolute(check.script) ? check.script : join(REPO_ROOT, check.script)));

logger.write(
`--- Running ${scriptsToRun.length} checks, with parallelism ${MAX_PARALLELISM}...`,
scriptsToRun
`--- Running ${checksToRun.length} checks (${fileChangingChecks.length} file-changing with parallelism=1, ${regularChecks.length} regular with parallelism=${MAX_PARALLELISM})...`
);
const startTime = Date.now();
const results = await runAllChecks(scriptsToRun);
const results = await runPartitionedChecks(fileChangingChecks, regularChecks);

logger.write('--- All checks finished.');
printResults(startTime, results);
Expand All @@ -83,39 +97,63 @@ function collectScriptsToRun(inputOptions: {
targetFile: string | undefined;
targetDir: string | undefined;
checks: string | undefined;
}) {
}): QuickCheck[] {
const { targetFile, targetDir, checks } = inputOptions;
if ([targetFile, targetDir, checks].filter(Boolean).length > 1) {
throw new Error('Only one of --file, --dir, or --checks can be used at a time.');
}

if (targetDir) {
const targetDirAbsolute = isAbsolute(targetDir) ? targetDir : join(REPO_ROOT, targetDir);
return readdirSync(targetDirAbsolute).map((file) => join(targetDir, file));
return readdirSync(targetDirAbsolute).map((file) => ({ script: join(targetDir, file) }));
} else if (checks) {
return checks
.trim()
.split(/[,\n]/)
.map((script) => script.trim());
.map((script) => ({ script: script.trim() }));
} else {
const targetFileWithDefault = targetFile || quickChecksList;
const targetFileAbsolute = isAbsolute(targetFileWithDefault)
? targetFileWithDefault
: join(REPO_ROOT, targetFileWithDefault);

return readFileSync(targetFileAbsolute, 'utf-8')
.trim()
.split('\n')
.map((line) => line.trim());
const fileContent = readFileSync(targetFileAbsolute, 'utf-8');

// Support both JSON and legacy plain text formats for backward compatibility
if (targetFileAbsolute.endsWith('.json')) {
return JSON.parse(fileContent) as QuickCheck[];
} else {
// Legacy plain text format
return fileContent
.trim()
.split('\n')
.map((line) => ({ script: line.trim() }));
}
}
}

async function runAllChecks(scriptsToRun: string[]): Promise<CheckResult[]> {
async function runPartitionedChecks(
fileChangingChecks: string[],
regularChecks: string[]
): Promise<CheckResult[]> {
// Run both partitions concurrently, but with different parallelism
const [fileChangingResults, regularResults] = await Promise.all([
runAllChecks(fileChangingChecks, 1), // File-changing checks run one at a time
runAllChecks(regularChecks, MAX_PARALLELISM), // Regular checks run with full parallelism
]);

return [...fileChangingResults, ...regularResults];
}

async function runAllChecks(
scriptsToRun: string[],
parallelism = MAX_PARALLELISM
): Promise<CheckResult[]> {
const checksRunning: Array<Promise<any>> = [];
const checksFinished: CheckResult[] = [];

while (scriptsToRun.length > 0 || checksRunning.length > 0) {
while (scriptsToRun.length > 0 && checksRunning.length < MAX_PARALLELISM) {
while (scriptsToRun.length > 0 && checksRunning.length < parallelism) {
const script = scriptsToRun.shift();
if (!script) {
continue;
Expand Down