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
16 changes: 13 additions & 3 deletions .github/workflows/changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
name: Changelog

on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]

pull_request_target:
types: [opened, synchronize, reopened, labeled, unlabeled]

Expand Down Expand Up @@ -47,8 +50,9 @@ jobs:
with:
ref: master
sparse-checkout: |
scripts/check_changelog_fragments.sh
changelog.d/
scripts/environment/release-flags.sh
scripts/environment/binstall.sh
sparse-checkout-cone-mode: false

# Checkout PR's changelog.d/ into tmp/
Expand All @@ -58,7 +62,13 @@ jobs:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
path: tmp
sparse-checkout: changelog.d/
sparse-checkout: |
changelog.d/
scripts/environment/prepare.sh # FIXME DO NOT MERGE
Copy link
Contributor

Choose a reason for hiding this comment

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

Reminder: this change needs to be reverted before merging


- name: Prepare vdev
if: env.SHOULD_RUN == 'true'
run: bash tmp/scripts/environment/prepare.sh --modules=vdev

- name: Run changelog fragment checker
if: env.SHOULD_RUN == 'true'
Expand All @@ -68,4 +78,4 @@ jobs:

# Add files and then compare with HEAD instead of origin/master
git add changelog.d/
MERGE_BASE=HEAD ./scripts/check_changelog_fragments.sh
vdev check changelog-fragments --merge-base HEAD
3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ make check-licenses
make check-fmt
make check-clippy
make check-component-docs
make check-changelog-fragments

# Some other checks that in our experience rarely fail on PRs.
make check-deny
Expand All @@ -145,8 +146,6 @@ make check-version
make check-examples
make check-scripts

./scripts/check_changelog_fragments.sh

# The following check is very slow.
# make check-component-features
```
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,11 @@ check-all: check-scripts check-deny check-component-docs check-licenses
check-component-features: ## Check that all component features are setup properly
${MAYBE_ENVIRONMENT_EXEC} cargo vdev check component-features

.PHONY: check-changelog-fragments
check-changelog-fragments: ## Check that all component features are setup properly
${MAYBE_ENVIRONMENT_EXEC} cargo vdev check changelog-fragments


.PHONY: check-clippy
check-clippy: ## Check code with Clippy
${MAYBE_ENVIRONMENT_EXEC} cargo vdev check rust
Expand Down
2 changes: 1 addition & 1 deletion changelog.d/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This is enforced during CI.
To mark a PR as not requiring user-facing changelog notes, add the label 'no-changelog'.

To run the same check that is run in CI to validate that your changelog fragments have
the correct syntax, commit the fragment additions and then run ./scripts/check_changelog_fragments.sh
the correct syntax, commit the fragment additions and then run `make check-changelog-fragments`

The format for fragments is: `<unique_name>.<fragment_type>.md`

Expand Down
76 changes: 0 additions & 76 deletions scripts/check_changelog_fragments.sh

This file was deleted.

9 changes: 8 additions & 1 deletion scripts/environment/prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Modules:
wasm-pack
markdownlint
datadog-ci
vdev

If a module requires rust then rustup will be automatically installed.
By default, all modules are installed. To install only a subset:
Expand Down Expand Up @@ -77,7 +78,7 @@ contains_module() {
# Always ensure git safe.directory is set
git config --global --add safe.directory "$(pwd)"

REQUIRES_RUSTUP=(dd-rust-license-tool cargo-deb cross cargo-nextest cargo-deny cargo-msrv wasm-pack)
REQUIRES_RUSTUP=(dd-rust-license-tool cargo-deb cross cargo-nextest cargo-deny cargo-msrv wasm-pack vdev)

REQUIRES_BINSTALL=("${REQUIRES_RUSTUP[@]}")
unset -v 'REQUIRES_BINSTALL[0]' # remove dd-rust-license-tool
Expand Down Expand Up @@ -169,3 +170,9 @@ if contains_module datadog-ci; then
sudo npm install -g @datadog/[email protected]
fi
fi

if contains_module vdev; then
if [[ "$(vdev --version 2>/dev/null)" != "vdev 0.1.0" ]]; then
rustup run stable cargo "${install[@]}" vdev --version 0.1.0 --force --locked
fi
fi
2 changes: 1 addition & 1 deletion scripts/generate-release-cue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def generate_changelog!(new_version)
# these are handled in the upgrade guide separately.

# NOTE: If the fragment types are altered, update both the 'changelog.d/README.md' and
# 'scripts/check_changelog_fragments.sh' accordingly.
# 'vdev/src/commands/check/changelog_fragments.rs' accordingly.
type = ""
if fragment_type == "breaking"
type = "chore"
Expand Down
148 changes: 148 additions & 0 deletions vdev/src/commands/check/changelog_fragments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use crate::git::added_files_against_merge_base;
use crate::path_utils::get_changelog_dir;
use anyhow::{anyhow, Context, Result};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

const FRAGMENT_TYPES: &[&str] = &[
"breaking",
"security",
"deprecation",
"feature",
"enhancement",
"fix",
];

const DEFAULT_MAX_FRAGMENTS: usize = 1000;

/// Validate changelog fragments added in this branch/PR.
#[derive(clap::Args, Debug)]
#[command()]
pub struct Cli {
/// Changelog directory (defaults to `changelog.d`)
#[arg(long)]
changelog_dir: Option<PathBuf>,

/// Merge base to diff against (defaults to `origin/master`)
#[arg(long)]
merge_base: Option<String>,

/// Max fragments threshold (defaults to `1000`)
#[arg(long)]
max_fragments: Option<usize>,
}

impl Cli {
pub fn exec(&self) -> Result<()> {
let changelog_dir = self
.changelog_dir.clone().unwrap_or_else(get_changelog_dir);

if !changelog_dir.is_dir() {
error!(
"No ./{} found. This tool must be invoked from the root of the repo.",
changelog_dir.display()
);
std::process::exit(1);
}

let merge_base = self
.merge_base.clone()
.unwrap_or_else(|| "origin/master".to_string());

let max_fragments: usize = self
.max_fragments
.unwrap_or(DEFAULT_MAX_FRAGMENTS);

let fragments = added_files_against_merge_base(&merge_base, &changelog_dir)
.context("failed to collect added changelog fragments")?;

if fragments.is_empty() {
info!("No changelog fragments detected");
info!("If no changes necessitate user-facing explanations, add the GH label 'no-changelog'");
info!("Otherwise, add changelog fragments to {}/", changelog_dir.display());
info!("For details, see '{}/README.md'", changelog_dir.display());
std::process::exit(1);
}

if fragments.len() > max_fragments {
error!("Too many changelog fragments ({} > {max_fragments}).", fragments.len());
std::process::exit(1);
}

for path in fragments {
if let Some(name) = path.file_name().and_then(OsStr::to_str) {
if name == "README.md" {
continue;
}
info!("Validating `{name}`");
validate_fragment_filename(name)?;
validate_fragment_contents(&changelog_dir.join(name), name)?;
} else {
return Err(anyhow!("unexpected path (no filename): {}", path.display()));
}
}

info!("changelog additions are valid.");
Ok(())
}
}

fn validate_fragment_filename(filename: &str) -> Result<()> {
// Expected: <unique_name>.<fragment_type>.md
let parts: Vec<&str> = filename.split('.').collect();
if parts.len() != 3 {
return Err(anyhow!(
"invalid fragment filename: {filename} - wrong number of period delimiters. \
expected '<unique_name>.<fragment_type>.md'",
));
}

let fragment_type = parts[1];
if !FRAGMENT_TYPES.contains(&fragment_type) {
return Err(anyhow!(
"invalid fragment filename: {filename} - fragment type must be one of: {})",
FRAGMENT_TYPES.join("|"),
));
}

if parts[2] != "md" {
return Err(anyhow!(
"invalid fragment filename: {filename} - extension must be markdown (.md)",
));
}

Ok(())
}

fn validate_fragment_contents(path: &Path, filename: &str) -> Result<()> {
let content = std::fs::read_to_string(path).with_context(|| {
format!("failed to read fragment file: {}", path.to_string_lossy())
})?;

// Use last non-empty line to avoid false negatives due to trailing newline(s).
let last_line = content.lines().rev().find(|l| !l.trim().is_empty()).unwrap_or("");

if !last_line.starts_with("authors:") {
return Err(anyhow!("last line must start with 'authors: ' and include at least one name. ({filename})"));
}

// Split on the first colon, take the remainder as names
let names = last_line.split_once(':').map_or("", |(_, rest)| rest.trim());

if names.is_empty() {
return Err(anyhow!(
"author line is empty. ({})",
filename
));
}

if names.contains('@') {
return Err(anyhow!("author should not be prefixed with @"));
}

if names.contains(',') {
return Err(anyhow!("authors should be space delimited, not comma delimited."));
}

Ok(())
}
5 changes: 3 additions & 2 deletions vdev/src/commands/check/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
crate::cli_subcommands! {
"Check parts of the Vector code base..."
docs,
events,
mod changelog_fragments,
mod component_docs,
mod component_features,
mod deny,
docs,
events,
mod examples,
mod fmt,
mod licenses,
Expand Down
5 changes: 1 addition & 4 deletions vdev/src/commands/release/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![allow(clippy::print_stderr)]

use crate::git;
use crate::path_utils::get_repo_root;
use crate::util::run_command;
use anyhow::{anyhow, Result};
use reqwest::blocking::Client;
Expand Down Expand Up @@ -389,10 +390,6 @@ impl Prepare {

// FREE FUNCTIONS AFTER THIS LINE

fn get_repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().to_path_buf()
}

fn get_latest_version_from_vector_tags() -> Result<Version> {
let tags = run_command("git tag --list --sort=-v:refname");
let latest_tag = tags
Expand Down
Loading
Loading