Skip to content

Commit 1b05ebb

Browse files
committed
Merge branch 'master' into managed-jdbcio
2 parents 14b097b + 67eaded commit 1b05ebb

165 files changed

Lines changed: 1304 additions & 634 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.asf.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ github:
5151

5252
protected_branches:
5353
master: {}
54+
release-2.68: {}
5455
release-2.67.0-postrelease: {}
5556
release-2.67: {}
5657
release-2.66.0-postrelease: {}

CHANGES.md

Lines changed: 105 additions & 82 deletions
Large diffs are not rendered by default.

build.gradle.kts

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,254 @@ tasks.register("pythonFormatterPreCommit") {
510510
dependsOn("sdks:python:test-suites:tox:pycommon:formatter")
511511
}
512512

513+
tasks.register("formatChanges") {
514+
group = "formatting"
515+
description = "Formats CHANGES.md according to the template structure"
516+
517+
doLast {
518+
val changesFile = file("CHANGES.md")
519+
if (!changesFile.exists()) {
520+
throw GradleException("CHANGES.md file not found")
521+
}
522+
523+
val content = changesFile.readText()
524+
val lines = content.lines().toMutableList()
525+
526+
// Find template end (after --> that follows <!-- Template -->)
527+
var templateStartIndex = -1
528+
var templateEndIndex = -1
529+
530+
for (i in lines.indices) {
531+
if (lines[i].trim() == "<!-- Template -->") {
532+
templateStartIndex = i
533+
} else if (templateStartIndex != -1 && lines[i].trim() == "-->") {
534+
templateEndIndex = i
535+
break
536+
}
537+
}
538+
539+
if (templateEndIndex == -1) {
540+
throw GradleException("Template end marker not found in CHANGES.md")
541+
}
542+
543+
// Process each release section
544+
var i = templateEndIndex + 1
545+
val formattedLines = mutableListOf<String>()
546+
547+
// Keep header and template exactly as-is (lines 0 to templateEndIndex inclusive)
548+
formattedLines.addAll(lines.subList(0, templateEndIndex + 1))
549+
550+
// Always add blank line after template
551+
formattedLines.add("")
552+
553+
while (i < lines.size) {
554+
val line = lines[i]
555+
556+
// Check if this is a release header
557+
if (line.startsWith("# [")) {
558+
formattedLines.add(line)
559+
i++
560+
561+
// Expected sections in order (following template)
562+
val expectedSections = listOf(
563+
"## Beam 3.0.0 Development Highlights",
564+
"## Highlights",
565+
"## I/Os",
566+
"## New Features / Improvements",
567+
"## Breaking Changes",
568+
"## Deprecations",
569+
"## Bugfixes",
570+
"## Security Fixes",
571+
"## Known Issues"
572+
)
573+
574+
val sectionContent = mutableMapOf<String, MutableList<String>>()
575+
var currentSection = ""
576+
577+
// Parse existing sections
578+
while (i < lines.size && !lines[i].startsWith("# [")) {
579+
val currentLine = lines[i]
580+
581+
if (currentLine.startsWith("## ")) {
582+
currentSection = currentLine
583+
if (!sectionContent.containsKey(currentSection)) {
584+
sectionContent[currentSection] = mutableListOf()
585+
}
586+
} else if (currentSection.isNotEmpty()) {
587+
sectionContent[currentSection]!!.add(currentLine)
588+
}
589+
i++
590+
}
591+
592+
// Only add sections that actually exist with content
593+
for (section in expectedSections) {
594+
if (sectionContent.containsKey(section)) {
595+
formattedLines.add("")
596+
formattedLines.add(section)
597+
formattedLines.add("")
598+
599+
// Remove empty lines at start and end
600+
val content = sectionContent[section]!!
601+
while (content.isNotEmpty() && content.first().trim().isEmpty()) {
602+
content.removeAt(0)
603+
}
604+
while (content.isNotEmpty() && content.last().trim().isEmpty()) {
605+
content.removeAt(content.size - 1)
606+
}
607+
608+
// Format content according to template rules
609+
val formattedContent = content.map { line ->
610+
// Convert SDK language references from [Language] to (Language)
611+
line.replace(Regex("\\[([^\\]]*(?:Java|Python|Go|Kotlin|TypeScript|YAML)[^\\]]*)\\]")) { matchResult ->
612+
val languages = matchResult.groupValues[1]
613+
// Only convert if it's clearly a language reference (not a link or other content)
614+
if (languages.matches(Regex(".*(?:Java|Python|Go|Kotlin|TypeScript|YAML).*"))) {
615+
"($languages)"
616+
} else {
617+
matchResult.value
618+
}
619+
}
620+
}
621+
622+
formattedLines.addAll(formattedContent)
623+
}
624+
}
625+
626+
if (i < lines.size) {
627+
formattedLines.add("")
628+
}
629+
} else {
630+
i++
631+
}
632+
}
633+
634+
// Write formatted content back
635+
changesFile.writeText(formattedLines.joinToString("\n"))
636+
println("CHANGES.md has been formatted according to template structure")
637+
}
638+
}
639+
640+
tasks.register("validateChanges") {
641+
group = "verification"
642+
description = "Validates CHANGES.md follows required formatting rules"
643+
644+
doLast {
645+
val changesFile = file("CHANGES.md")
646+
if (!changesFile.exists()) {
647+
throw GradleException("CHANGES.md file not found")
648+
}
649+
650+
val content = changesFile.readText()
651+
val lines = content.lines()
652+
val errors = mutableListOf<String>()
653+
654+
// Find template section boundaries
655+
var templateStartIndex = -1
656+
var templateEndIndex = -1
657+
658+
for (i in lines.indices) {
659+
if (lines[i].trim() == "<!-- Template -->") {
660+
templateStartIndex = i
661+
println("Found template start at line ${i+1}")
662+
} else if (templateStartIndex != -1 && lines[i].trim() == "-->") {
663+
templateEndIndex = i
664+
println("Found template end at line ${i+1}")
665+
break
666+
}
667+
}
668+
669+
if (templateStartIndex == -1 || templateEndIndex == -1) {
670+
throw GradleException("Template section not found in CHANGES.md")
671+
}
672+
673+
println("Template section: lines ${templateStartIndex+1} to ${templateEndIndex+1}")
674+
675+
// Find unreleased section after the template section
676+
var unreleasedSectionStart = -1
677+
for (i in (templateEndIndex + 1) until lines.size) {
678+
if (lines[i].startsWith("# [") && lines[i].contains("Unreleased")) {
679+
unreleasedSectionStart = i
680+
println("Found unreleased section at line ${i+1}: ${lines[i]}")
681+
break
682+
}
683+
}
684+
685+
if (unreleasedSectionStart == -1) {
686+
throw GradleException("Unreleased section not found in CHANGES.md")
687+
}
688+
689+
// Check entries in the unreleased section
690+
var i = unreleasedSectionStart + 1
691+
println("Starting validation from line ${i+1}")
692+
693+
while (i < lines.size && !lines[i].startsWith("# [")) {
694+
val line = lines[i].trim()
695+
696+
if (line.startsWith("* ") && line.isNotEmpty()) {
697+
println("Checking line ${i+1}: $line")
698+
699+
// Skip comment lines
700+
if (line.startsWith("* [comment]:")) {
701+
println(" Skipping comment line")
702+
} else {
703+
// Rule 1: Check if language references use parentheses instead of brackets
704+
val languagePattern = "\\[(Java|Python|Go|Kotlin|TypeScript|YAML)(?:/(?:Java|Python|Go|Kotlin|TypeScript|YAML))*\\]"
705+
val languageRegex = Regex(languagePattern)
706+
707+
// Check if there's a language reference in brackets
708+
val matches = languageRegex.findAll(line).toList()
709+
if (matches.isNotEmpty()) {
710+
for (match in matches) {
711+
val matchText = match.value
712+
val matchPosition = match.range.first
713+
println(" Found language reference: $matchText at position $matchPosition")
714+
715+
// Check if this is part of an issue link or URL
716+
val beforeMatch = if (matchPosition > 0) line.substring(0, matchPosition) else ""
717+
val isPartOfLink = beforeMatch.contains("[#") ||
718+
beforeMatch.contains("http") ||
719+
line.contains("CVE-")
720+
721+
println(" Is part of link: $isPartOfLink")
722+
723+
if (!isPartOfLink) {
724+
val error = "Line ${i+1}: Language references should use parentheses () instead of brackets []: $line"
725+
println(" Adding error: $error")
726+
errors.add(error)
727+
}
728+
}
729+
} else {
730+
println(" No bracketed language reference found")
731+
}
732+
733+
// Rule 2: Check if each entry has an issue link
734+
val issueLinkPattern = "\\(\\[#[0-9a-zA-Z]+\\]\\(https://github\\.com/apache/beam/issues/[0-9a-zA-Z]+\\)\\)"
735+
val issueLinkRegex = Regex(issueLinkPattern)
736+
737+
val hasIssueLink = issueLinkRegex.containsMatchIn(line)
738+
println(" Has issue link: $hasIssueLink")
739+
740+
if (!hasIssueLink) {
741+
val error = "Line ${i+1}: Missing or malformed issue link. Each entry should end with ([#X](https://github.com/apache/beam/issues/X)): $line"
742+
println(" Adding error: $error")
743+
errors.add(error)
744+
}
745+
}
746+
}
747+
748+
i++
749+
}
750+
751+
println("Found ${errors.size} errors")
752+
753+
if (errors.isNotEmpty()) {
754+
throw GradleException("CHANGES.md validation failed with the following errors:\n${errors.joinToString("\n")}\n\nYou can run ./gradlew formatChanges to correct some issues.")
755+
}
756+
757+
println("CHANGES.md validation successful")
758+
}
759+
}
760+
513761
tasks.register("python39PostCommit") {
514762
dependsOn(":sdks:python:test-suites:dataflow:py39:postCommitIT")
515763
dependsOn(":sdks:python:test-suites:direct:py39:postCommitIT")

buildSrc/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ dependencies {
5353
runtimeOnly("gradle.plugin.com.dorongold.plugins:task-tree:1.5") // Adds a 'taskTree' task to print task dependency tree
5454
runtimeOnly("net.linguica.gradle:maven-settings-plugin:0.5")
5555
runtimeOnly("gradle.plugin.io.pry.gradle.offline_dependencies:gradle-offline-dependencies-plugin:0.5.0") // Enable creating an offline repository
56-
runtimeOnly("net.ltgt.gradle:gradle-errorprone-plugin:3.1.0") // Enable errorprone Java static analysis
56+
runtimeOnly("net.ltgt.gradle:gradle-errorprone-plugin:4.2.0") // Enable errorprone Java static analysis
5757
runtimeOnly("org.ajoberstar.grgit:grgit-gradle:5.3.2") // Enable website git publish to asf-site branch
5858
runtimeOnly("com.avast.gradle:gradle-docker-compose-plugin:0.16.12") // Enable docker compose tasks
5959
runtimeOnly("ca.cutterslade.gradle:gradle-dependency-analyze:1.8.3") // Enable dep analysis

0 commit comments

Comments
 (0)