Skip to content

Commit fec8a23

Browse files
authored
Merge branch 'main' into host-masquerading-rule
2 parents 0022f2d + 816e31c commit fec8a23

23 files changed

+2555
-31
lines changed

.github/workflows/backport.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
strategy:
5656
max-parallel: 1
5757
matrix:
58-
target_branch: [7.13]
58+
target_branch: [7.13, 7.14]
5959

6060
steps:
6161
- name: Checkout repo

.github/workflows/release-fleet.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: release-fleet
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
target_repo:
6+
description: 'Target repository to build a PR against'
7+
required: true
8+
default: 'elastic/integrations'
9+
target_branch:
10+
description: 'Target branch for PR base'
11+
required: true
12+
default: 'master'
13+
draft:
14+
description: 'Create a PR as draft (y/n)'
15+
required: false
16+
17+
jobs:
18+
fleet-pr:
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
- name: Validate the source branch
23+
uses: actions/github-script@v3
24+
with:
25+
script: |
26+
if ('refs/heads/main' === '${{github.event.ref}}') {
27+
core.setFailed('Forbidden branch')
28+
}
29+
30+
- name: Checkout elastic/integrations
31+
uses: actions/checkout@v2
32+
with:
33+
token: ${{ secrets.PROTECTIONS_MACHINE_TOKEN }}
34+
ref: ${{github.event.inputs.target_branch}}
35+
repository: ${{github.event.inputs.target_repo}}
36+
path: integrations
37+
38+
- name: Set up Python 3.8
39+
uses: actions/setup-python@v2
40+
with:
41+
python-version: 3.8
42+
43+
- name: Install Python dependencies
44+
run: |
45+
cd detection-rules
46+
python -m pip install --upgrade pip
47+
pip install -r requirements.txt -r requirements-dev.txt
48+
49+
- name: Build release package
50+
run: |
51+
cd detection-rules
52+
python -m detection_rules dev build-release
53+
54+
- name: Set github config
55+
run: |
56+
git config --global user.email "[email protected]"
57+
git config --global user.name "protectionsmachine"
58+
59+
- name: Setup go
60+
uses: actions/setup-go@v2
61+
with:
62+
go-version: '^1.16.0'
63+
64+
- name: Build elastic-package
65+
run: |
66+
go get github.com/elastic/elastic-package
67+
68+
- name: Create the PR to Integrations
69+
env:
70+
DRAFT_ARGS: "${{startsWith(github.event.inputs.draft,'y') && '--draft' || ' '}}"
71+
TARGET_REPO: "${{github.event.inputs.target_repo}}"
72+
TARGET_BRANCH: "${{github.event.inputs.target_branch}}"
73+
LOCAL_REPO: "../integrations"
74+
GITHUB_TOKEN: "${{ secrets.PROTECTIONS_MACHINE_TOKEN }}"
75+
run: |
76+
cd detection-rules
77+
python -m detection_rules dev integrations-pr \
78+
$LOCAL_REPO \
79+
--github-repo $TARGET_REPO \
80+
--base-branch $TARGET_BRANCH \
81+
--assign ${{github.actor}} \
82+
$DRAFT_ARGS
83+
84+
- name: Archive production artifacts
85+
uses: actions/upload-artifact@v2
86+
with:
87+
name: release-files
88+
path: |
89+
detection-rules/releases

.github/workflows/release-kibana.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ jobs:
3232
repository: elastic/kibana
3333
path: kibana
3434

35+
- name: Set up Python 3.8
36+
uses: actions/setup-python@v2
37+
with:
38+
python-version: 3.8
39+
3540
- name: Install dependencies
3641
run: |
3742
cd detection-rules
@@ -58,9 +63,9 @@ jobs:
5863
cd detection-rules
5964
python -m detection_rules dev kibana-pr --assign ${{github.actor}} $LABEL_ARGS $DRAFT_ARGS $BRANCH_ARGS
6065
61-
- name: Archive production artifacts for branch builds
62-
uses: actions/upload-artifact@v2
63-
with:
64-
name: release-files
65-
path: |
66-
detection-rules/releases
66+
- name: Archive production artifacts for branch builds
67+
uses: actions/upload-artifact@v2
68+
with:
69+
name: release-files
70+
path: |
71+
detection-rules/releases

detection_rules/devtools.py

Lines changed: 170 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@
1313
import subprocess
1414
import textwrap
1515
import time
16+
import typing
1617
from pathlib import Path
1718
from typing import Optional, Tuple, List
1819

1920
import click
20-
import typing
21+
import yaml
2122
from elasticsearch import Elasticsearch
2223

2324
from kibana.connector import Kibana
24-
from . import rule_loader
25+
from . import rule_loader, utils
2526
from .cli_utils import single_collection
2627
from .eswrap import CollectEvents, add_range_to_dsl
2728
from .ghwrap import GithubClient
@@ -95,6 +96,7 @@ def path(self) -> Path:
9596

9697
def revert(self, dry_run=False):
9798
"""Run a git command to revert this change."""
99+
98100
def git(*args):
99101
command_line = ["git"] + [str(arg) for arg in args]
100102
click.echo(subprocess.list2cmdline(command_line))
@@ -252,8 +254,6 @@ def decorated(*args, **kwargs):
252254
def kibana_commit(ctx, local_repo: str, github_repo: str, ssh: bool, kibana_directory: str, base_branch: str,
253255
branch_name: Optional[str], message: Optional[str], push: bool) -> (str, str):
254256
"""Prep a commit and push to Kibana."""
255-
git_exe = shutil.which("git")
256-
257257
package_name = Package.load_configs()["name"]
258258
release_dir = os.path.join(RELEASE_DIR, package_name)
259259
message = message or f"[Detection Rules] Add {package_name} rules"
@@ -263,23 +263,17 @@ def kibana_commit(ctx, local_repo: str, github_repo: str, ssh: bool, kibana_dire
263263
click.echo(f"Run {click.style('python -m detection_rules dev build-release', bold=True)} to populate", err=True)
264264
ctx.exit(1)
265265

266-
if not git_exe:
267-
click.secho("Unable to find git", err=True, fg="red")
268-
ctx.exit(1)
266+
git = utils.make_git("-C", local_repo)
269267

270268
# Get the current hash of the repo
271-
long_commit_hash = subprocess.check_output([git_exe, "rev-parse", "HEAD"], encoding="utf-8").strip()
272-
short_commit_hash = subprocess.check_output([git_exe, "rev-parse", "--short", "HEAD"], encoding="utf-8").strip()
269+
long_commit_hash = git("rev-parse", "HEAD")
270+
short_commit_hash = git("rev-parse", "--short", "HEAD")
273271

274272
try:
275-
def git(*args, show_output=False):
276-
method = subprocess.call if show_output else subprocess.check_output
277-
return method([git_exe, "-C", local_repo] + list(args), encoding="utf-8")
278-
279273
if not os.path.exists(local_repo):
280274
click.echo(f"Kibana repository doesn't exist at {local_repo}. Cloning...")
281275
url = f"[email protected]:{github_repo}.git" if ssh else f"https://github.com/{github_repo}.git"
282-
subprocess.check_call([git_exe, "clone", url, local_repo, "--depth", "1"])
276+
utils.make_git()("clone", url, local_repo, "--depth", "1")
283277
else:
284278
git("checkout", base_branch)
285279

@@ -324,7 +318,6 @@ def git(*args, show_output=False):
324318
# Pending an official GitHub API
325319
# @click.option("--automerge", is_flag=True, help="Enable auto-merge on the PR")
326320
@add_git_args
327-
@click.pass_context
328321
def kibana_pr(ctx: click.Context, label: Tuple[str, ...], assign: Tuple[str, ...], draft: bool, token: str, **kwargs):
329322
"""Create a pull request to Kibana."""
330323
branch_name, commit_hash = ctx.invoke(kibana_commit, push=True, **kwargs)
@@ -344,7 +337,7 @@ def kibana_pr(ctx: click.Context, label: Tuple[str, ...], assign: Tuple[str, ...
344337
- [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing),
345338
uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)
346339
""").strip() # noqa: E501
347-
pr = repo.create_pull(title, body, kwargs["base_branch"], branch_name, draft=draft)
340+
pr = repo.create_pull(title, body, kwargs["base_branch"], branch_name, maintainer_can_modify=True, draft=draft)
348341

349342
# labels could also be comma separated
350343
label = {lbl for cs_labels in label for lbl in cs_labels.split(",") if lbl}
@@ -359,6 +352,167 @@ def kibana_pr(ctx: click.Context, label: Tuple[str, ...], assign: Tuple[str, ...
359352
click.echo(pr.html_url)
360353

361354

355+
@dev_group.command("integrations-pr")
356+
@click.argument("local-repo", type=click.Path(exists=True, file_okay=False, dir_okay=True),
357+
default=get_path("..", "integrations"))
358+
@click.option("--token", required=True, prompt=get_github_token() is None, default=get_github_token(),
359+
help="GitHub token to use for the PR", hide_input=True)
360+
@click.option("--pkg-directory", "-d", help="Directory to save the package in cloned repository",
361+
default=os.path.join("packages", "security_detection_engine"))
362+
@click.option("--base-branch", "-b", help="Base branch in target repository", default="master")
363+
@click.option("--branch-name", "-n", help="New branch for the rules commit")
364+
@click.option("--github-repo", "-r", help="Repository to use for the branch", default="elastic/integrations")
365+
@click.option("--assign", multiple=True, help="GitHub users to assign the PR")
366+
@click.option("--label", multiple=True, help="GitHub labels to add to the PR")
367+
@click.option("--draft", is_flag=True, help="Open the PR as a draft")
368+
@click.option("--remote", help="Override the remote from 'origin'", default="origin")
369+
@click.pass_context
370+
def integrations_pr(ctx: click.Context, local_repo: str, token: str, draft: bool,
371+
pkg_directory: str, base_branch: str, remote: str,
372+
branch_name: Optional[str], github_repo: str, assign: Tuple[str, ...], label: Tuple[str, ...]):
373+
"""Create a pull request to publish the Fleet package to elastic/integrations."""
374+
local_repo = os.path.abspath(local_repo)
375+
stack_version = Package.load_configs()["name"]
376+
package_version = Package.load_configs()["registry_data"]["version"]
377+
378+
release_dir = Path(RELEASE_DIR) / stack_version / "fleet" / package_version
379+
message = f"[Security Rules] Update security rules package to v{package_version}"
380+
381+
if not release_dir.exists():
382+
click.secho("Release directory doesn't exist.", fg="red", err=True)
383+
click.echo(f"Run {click.style('python -m detection_rules dev build-release', bold=True)} to populate", err=True)
384+
ctx.exit(1)
385+
386+
if not Path(local_repo).exists():
387+
click.secho(f"{github_repo} is not present at {local_repo}.", fg="red", err=True)
388+
ctx.exit(1)
389+
390+
# Get the most recent commit hash of detection-rules
391+
detection_rules_git = utils.make_git()
392+
long_commit_hash = detection_rules_git("rev-parse", "HEAD")
393+
short_commit_hash = detection_rules_git("rev-parse", "--short", "HEAD")
394+
395+
# refresh the local clone of the repository
396+
git = utils.make_git("-C", local_repo)
397+
git("checkout", base_branch)
398+
git("pull", remote, base_branch)
399+
400+
# Switch to a new branch in elastic/integrations
401+
branch_name = branch_name or f"detection-rules/{package_version}-{short_commit_hash}"
402+
git("checkout", "-b", branch_name)
403+
404+
# Load the changelog in memory, before it's removed. Come back for it after the PR is created
405+
target_directory = Path(local_repo) / pkg_directory
406+
changelog_path = target_directory / "changelog.yml"
407+
changelog_entries: list = yaml.safe_load(changelog_path.read_text(encoding="utf-8"))
408+
409+
changelog_entries.insert(0, {
410+
"version": package_version,
411+
"changes": [
412+
# This will be changed later
413+
{"description": "Release security rules update", "type": "enhancement",
414+
"link": "https://github.com/elastic/integrations/pulls/0000"}
415+
]
416+
})
417+
418+
# Remove existing assets and replace everything
419+
shutil.rmtree(target_directory)
420+
actual_target_directory = shutil.copytree(release_dir, target_directory)
421+
assert Path(actual_target_directory).absolute() == Path(target_directory).absolute(), \
422+
f"Expected a copy to {pkg_directory}"
423+
424+
# Add the changelog back
425+
def save_changelog():
426+
with changelog_path.open("wt") as f:
427+
# add a note for other maintainers of elastic/integrations to be careful with versions
428+
f.write("# newer versions go on top\n")
429+
f.write("# NOTE: please use pre-release versions (e.g. -dev.0) until a package is ready for production\n")
430+
431+
yaml.dump(changelog_entries, f, allow_unicode=True, default_flow_style=False, indent=2)
432+
433+
save_changelog()
434+
435+
# Use elastic-package to format and lint
436+
gopath = utils.gopath()
437+
assert gopath is not None, "$GOPATH isn't set"
438+
439+
def elastic_pkg(*args):
440+
"""Run a command with $GOPATH/bin/elastic-package in the package directory."""
441+
prev = os.path.abspath(os.getcwd())
442+
os.chdir(target_directory)
443+
444+
try:
445+
return subprocess.check_call([os.path.join(gopath, "bin", "elastic-package")] + list(args))
446+
finally:
447+
os.chdir(prev)
448+
449+
elastic_pkg("format")
450+
elastic_pkg("lint")
451+
452+
# Upload the files to a branch
453+
git("add", pkg_directory)
454+
git("commit", "-m", message)
455+
git("push", "--set-upstream", remote, branch_name)
456+
457+
# Create a pull request (not done yet, but we need the PR number)
458+
client = GithubClient(token).authenticated_client
459+
repo = client.get_repo(github_repo)
460+
body = textwrap.dedent(f"""
461+
## What does this PR do?
462+
Update the Security Rules package to version {package_version}.
463+
Autogenerated from commit https://github.com/elastic/detection-rules/tree/{long_commit_hash}
464+
465+
## Checklist
466+
467+
- [x] I have reviewed [tips for building integrations](https://github.com/elastic/integrations/blob/master/docs/tips_for_building_integrations.md) and this pull request is aligned with them.
468+
- [ ] ~I have verified that all data streams collect metrics or logs.~
469+
- [x] I have added an entry to my package's `changelog.yml` file.
470+
- [x] If I'm introducing a new feature, I have modified the Kibana version constraint in my package's `manifest.yml` file to point to the latest Elastic stack release (e.g. `^7.13.0`).
471+
472+
## Author's Checklist
473+
- Install the most recently release security rules in the Detection Engine
474+
- Install the package
475+
- Confirm the update is available in Kibana. Click "Update X rules" or "Install X rules"
476+
- Look at the changes made after the install and confirm they are consistent
477+
478+
## How to test this PR locally
479+
- Perform the above checklist, and use `package-storage` to build EPR from source
480+
481+
## Related issues
482+
None
483+
484+
## Screenshots
485+
None
486+
""") # noqa: E501
487+
488+
pr = repo.create_pull(message, body, base_branch, branch_name, maintainer_can_modify=True, draft=draft)
489+
490+
# labels could also be comma separated
491+
label = {lbl for cs_labels in label for lbl in cs_labels.split(",") if lbl}
492+
493+
if label:
494+
pr.add_to_labels(*sorted(label))
495+
496+
if assign:
497+
pr.add_to_assignees(*assign)
498+
499+
click.echo("PR created:")
500+
click.echo(pr.html_url)
501+
502+
# replace the changelog entry with the actual PR link
503+
changelog_entries[0]["changes"][0]["link"] = pr.html_url
504+
save_changelog()
505+
506+
# format the yml file with elastic-package
507+
elastic_pkg("format")
508+
elastic_pkg("lint")
509+
510+
# Push the updated changelog to the PR branch
511+
git("add", pkg_directory)
512+
git("commit", "-m", f"Add changelog entry for {package_version}")
513+
git("push")
514+
515+
362516
@dev_group.command('license-check')
363517
@click.option('--ignore-directory', '-i', multiple=True, help='Directories to skip (relative to base)')
364518
@click.pass_context

0 commit comments

Comments
 (0)