Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/_docset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,11 @@ toc:
- file: changelog-add.md
- file: changelog-bundle.md
- file: changelog-bundle-amend.md
- file: changelog-evaluate-artifact.md
- file: changelog-evaluate-pr.md
- file: changelog-gh-release.md
- file: changelog-init.md
- file: changelog-prepare-artifact.md
- file: changelog-remove.md
- file: changelog-render.md
- folder: mcp
Expand Down
3 changes: 3 additions & 0 deletions docs/cli/release/changelog-add.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ docs-builder changelog add [options...] [-h|--help]
`--areas <string[]?>`
: Optional: Areas affected (comma-separated or specify multiple times).

`--concise`
: Optional: Omit schema reference comments from the generated YAML files. Useful in CI workflows to produce compact output.

`--config <string?>`
: Optional: Path to the changelog.yml configuration file. Defaults to `docs/changelog.yml`.

Expand Down
75 changes: 75 additions & 0 deletions docs/cli/release/changelog-evaluate-artifact.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
navigation_title: "changelog evaluate-artifact"
---

# changelog evaluate-artifact

:::{note}
This command is intended for CI automation. It is used internally by the changelog GitHub Actions and is not typically invoked directly by users.
:::

Evaluate a downloaded changelog artifact in the submit workflow.
Reads `metadata.json`, fetches the current PR state from the GitHub API, validates the head SHA, re-checks PR labels against the creation rules embedded in the metadata, and sets GitHub Actions outputs for downstream steps (commit, comment).

## Usage

```sh
docs-builder changelog evaluate-artifact [options...] [-h|--help]
```

## Options

`--metadata <string>`
: Path to the downloaded `metadata.json` file.

`--owner <string>`
: GitHub repository owner.

`--repo <string>`
: GitHub repository name.

`--comment-only`
: Post changelog as a PR comment instead of committing.
: Default: `false`

## GitHub Actions outputs

| Output | Description |
|--------|-------------|
| `pr-number` | Pull request number (from metadata) |
| `head-ref` | PR head branch ref |
| `head-sha` | PR head commit SHA |
| `status` | Artifact status |
| `config-file` | Path to `changelog.yml` (from metadata) |
| `changelog-dir` | Changelog directory path |
| `label-table` | Markdown label-to-type table (for failure comments) |
| `should-commit` | `true` if the changelog should be committed to the PR branch |
| `should-comment-success` | `true` if a success comment should be posted (fork or comment-only mode) |
| `should-comment-failure` | `true` if a failure comment should be posted (e.g., missing type label) |

## Environment variables

| Variable | Purpose |
|----------|---------|
| `GITHUB_TOKEN` | GitHub API authentication for fetching current PR state |

## Examples

Evaluate a downloaded artifact:

```sh
docs-builder changelog evaluate-artifact \
--metadata /tmp/changelog-result/metadata.json \
--owner elastic \
--repo elasticsearch
```

Evaluate in comment-only mode (for fork PRs or dry-run):

```sh
docs-builder changelog evaluate-artifact \
--metadata /tmp/changelog-result/metadata.json \
--owner elastic \
--repo elasticsearch \
--comment-only
```
98 changes: 98 additions & 0 deletions docs/cli/release/changelog-evaluate-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
navigation_title: "changelog evaluate-pr"
---

# changelog evaluate-pr

:::{note}
This command is intended for CI automation. It is used internally by the changelog GitHub Actions and is not typically invoked directly by users.
:::

Evaluate a pull request for changelog generation eligibility.
Performs pre-flight checks (body-only edit, bot loop detection, manual edit detection), loads the changelog configuration, checks label-based creation rules, resolves the PR title and type, and sets GitHub Actions outputs for downstream steps.

## Usage

```sh
docs-builder changelog evaluate-pr [options...] [-h|--help]
```

## Options

`--config <string>`
: Path to the `changelog.yml` configuration file.

`--owner <string>`
: GitHub repository owner.

`--repo <string>`
: GitHub repository name.

`--pr-number <int>`
: Pull request number.

`--pr-title <string>`
: Pull request title.

`--pr-labels <string>`
: Comma-separated PR labels.

`--head-ref <string>`
: PR head branch ref.

`--head-sha <string>`
: PR head commit SHA.

`--event-action <string>`
: GitHub event action (e.g., `opened`, `synchronize`, `edited`).

`--title-changed`
: Whether the PR title changed (for `edited` events).
: Default: `false`

`--strip-title-prefix`
: Remove square-bracket prefixes from the PR title (e.g., `[Inference API] Title` becomes `Title`).
: Default: `false`

`--bot-name <string>`
: Bot login name for loop detection.
: Default: `github-actions[bot]`

`--output <string>`
: Output directory for metadata.
: Default: `.`

## GitHub Actions outputs

| Output | Description |
|--------|-------------|
| `status` | Evaluation result: `skipped`, `manually-edited`, `no-title`, `no-label`, or `proceed` |
| `should-generate` | `true` if `changelog add` should run |
| `should-upload` | `true` if the artifact should be uploaded |
| `title` | Resolved PR title |
| `type` | Resolved changelog type |
| `label-table` | Markdown table of configured label-to-type mappings |

## Environment variables

| Variable | Purpose |
|----------|---------|
| `GITHUB_TOKEN` | GitHub API authentication for bot-commit and manual-edit detection |

## Examples

Evaluate PR #42 in the `elastic/elasticsearch` repository:

```sh
docs-builder changelog evaluate-pr \
--config docs/changelog.yml \
--owner elastic \
--repo elasticsearch \
--pr-number 42 \
--pr-title "Add new feature" \
--pr-labels "enhancement,Team:Core" \
--head-ref feature-branch \
--head-sha abc123 \
--event-action opened \
--strip-title-prefix
```
78 changes: 78 additions & 0 deletions docs/cli/release/changelog-prepare-artifact.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
navigation_title: "changelog prepare-artifact"
---

# changelog prepare-artifact

:::{note}
This command is intended for CI automation. It is used internally by the changelog GitHub Actions and is not typically invoked directly by users.
:::

Package the changelog artifact for cross-workflow transfer.
Resolves the final status from the `evaluate-pr` and `changelog add` outcomes, copies the generated YAML file (if any), writes `metadata.json`, and sets GitHub Actions outputs.

This command always exits with code 0 so the subsequent artifact upload step runs regardless of the internal status.

## Usage

```sh
docs-builder changelog prepare-artifact [options...] [-h|--help]
```

## Options

`--staging-dir <string>`
: Directory where `changelog add` wrote the generated YAML.

`--output-dir <string>`
: Directory to write the artifact (`metadata.json` + YAML).

`--evaluate-status <string>`
: Status output from the `evaluate-pr` step.

`--generate-outcome <string>`
: Outcome of the `changelog add` step (`success`, `failure`, or `skipped`).

`--pr-number <int>`
: Pull request number.

`--head-ref <string>`
: PR head branch ref.

`--head-sha <string>`
: PR head commit SHA.

`--label-table <string?>`
: Optional: markdown label table from `evaluate-pr`.

`--config <string?>`
: Optional: path to `changelog.yml` (used to extract creation rules for metadata).

## GitHub Actions outputs

| Output | Description |
|--------|-------------|
| `status` | Final artifact status: `success`, `no-label`, `no-title`, `error`, `skipped`, or `manually-edited` |

## File outputs

| File | Condition |
|------|-----------|
| `{output-dir}/metadata.json` | Always written |
| `{output-dir}/{pr_number}.yaml` | Only when the changelog was generated successfully |

## Examples

Package a successful changelog generation:

```sh
docs-builder changelog prepare-artifact \
--staging-dir /tmp/changelog-staging \
--output-dir /tmp/changelog-result \
--evaluate-status proceed \
--generate-outcome success \
--pr-number 42 \
--head-ref feature-branch \
--head-sha abc123 \
--config docs/changelog.yml
```
3 changes: 3 additions & 0 deletions docs/cli/release/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ These commands are associated with product release documentation.
- [changelog init](changelog-init.md) - Initialize changelog configuration and folder structure
- [changelog bundle-amend](changelog-bundle-amend.md) - Add entries to an existing bundle
- [changelog render](changelog-render.md) - Generate markdown output from changelog bundle files
- [changelog evaluate-pr](changelog-evaluate-pr.md) - (CI) Evaluate a PR for changelog generation eligibility
- [changelog prepare-artifact](changelog-prepare-artifact.md) - (CI) Package changelog artifact for cross-workflow transfer
- [changelog evaluate-artifact](changelog-evaluate-artifact.md) - (CI) Evaluate downloaded artifact in commit workflow
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public record CreateChangelogArguments
/// Whether to extract linked issues/PRs from PR/issue body. null = use config default.
/// </summary>
public bool? ExtractIssues { get; init; }

/// <summary>
/// When true, omit schema reference comments from generated YAML files.
/// </summary>
public bool Concise { get; init; }
}

/// <summary>
Expand Down
15 changes: 14 additions & 1 deletion src/services/Elastic.Changelog/Creation/ChangelogFileWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public async Task<bool> WriteChangelogAsync(
var changelogData = BuildChangelogData(input);

// Generate YAML file
var yamlContent = GenerateYaml(changelogData, config, titleMissing, typeMissing);
var yamlContent = input.Concise
? GenerateConciseYaml(changelogData)
: GenerateYaml(changelogData, config, titleMissing, typeMissing);

// Determine output path
var outputDir = input.Output ?? fileSystem.Directory.GetCurrentDirectory();
Expand Down Expand Up @@ -267,4 +269,15 @@ private static string GenerateYaml(ChangelogEntry data, ChangelogConfiguration c

return result;
}

private static string GenerateConciseYaml(ChangelogEntry data)
{
var serializeData = data with
{
Areas = data.Areas is { Count: 0 } ? null : data.Areas,
Issues = data.Issues is { Count: 0 } ? null : data.Issues
};

return ReleaseNotesSerialization.SerializeEntry(serializeData);
}
}
49 changes: 48 additions & 1 deletion src/services/Elastic.Changelog/Creation/PrInfoProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public async Task<PrProcessingResult> ProcessPrAsync(
return derived;
}

private bool ShouldSkipPrDueToLabelBlockers(
internal static bool ShouldSkipPrDueToLabelBlockers(
string[] prLabels,
IReadOnlyList<ProductArgument> products,
ChangelogConfiguration config,
Expand Down Expand Up @@ -307,6 +307,53 @@ AccessViolationException or
}
}

/// <summary>
/// Returns true only when every product defined in the create rules would be blocked by the given labels.
/// When no per-product overrides exist, checks global rules only.
/// </summary>
internal static bool AreAllProductsBlocked(string[] prLabels, CreateRules? createRules)
{
if (createRules == null)
return false;

// Global rules must block (products without overrides fall back to global)
if (!IsBlockedByRules(prLabels, createRules))
return false;

// Each per-product override must also block (overrides replace global entirely)
if (createRules.ByProduct is { Count: > 0 })
{
foreach (var (_, productRules) in createRules.ByProduct)
{
if (!IsBlockedByRules(prLabels, productRules))
return false;
}
}

return true;
}

/// <summary>Checks if a single set of create rules blocks the given labels (no diagnostics).</summary>
internal static bool IsBlockedByRules(string[] prLabels, CreateRules rules)
{
if (rules.Labels is not { Count: > 0 })
return false;

return rules.Mode switch
{
FieldMode.Exclude => rules.Match switch
{
MatchMode.All => prLabels.All(label => rules.Labels.Contains(label, StringComparer.OrdinalIgnoreCase)),
_ => rules.Labels.Any(label => prLabels.Contains(label, StringComparer.OrdinalIgnoreCase))
},
_ => rules.Match switch
{
MatchMode.All => !prLabels.All(label => rules.Labels.Contains(label, StringComparer.OrdinalIgnoreCase)),
_ => !prLabels.Any(label => rules.Labels.Contains(label, StringComparer.OrdinalIgnoreCase))
}
};
}

internal static string? MapLabelsToType(string[] labels, IReadOnlyDictionary<string, string> labelToTypeMapping) => labels
.Select(label => labelToTypeMapping.TryGetValue(label, out var mappedType) ? mappedType : null)
.FirstOrDefault(mappedType => mappedType != null);
Expand Down
4 changes: 4 additions & 0 deletions src/services/Elastic.Changelog/Elastic.Changelog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Elastic.Documentation.Services.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Elastic.Changelog.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
<PackageReference Include="GitHub.Actions.Core" />
<PackageReference Include="Vecc.YamlDotNet.Analyzers.StaticGenerator" />
<PackageReference Include="YamlDotNet" />
<PackageReference Include="System.IO.Abstractions" />
Expand Down
Loading
Loading