-
Notifications
You must be signed in to change notification settings - Fork 74
tools: Add version computation and native SDK pinning to releaser. #1130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
a4c6f65
tools(releaser): Add version computation and native SDK pinning to re…
fuzzybinary 3c6a613
Add spm support to version pinning / release plan
fuzzybinary 13f1f31
Address review feedback
fuzzybinary 5581e4f
Merge remote-tracking branch 'origin/v4' into jward/version-computati…
fuzzybinary 0cf4ae0
tools(releaser): Fix two more Codex-flagged native SDK pinning bugs.
fuzzybinary 3cef8c2
tools(releaser): Fix native SDK pin comparison baseline and CMake blo…
fuzzybinary f805e51
tools(releaser): Fix mainline prerelease promotion, missing-pin, and …
fuzzybinary ea37d0a
tools(releaser): Replace native SDK pin comparison with target resolu…
fuzzybinary 2869929
tools(releaser): Scope the pre-release tag and support single-line Fe…
fuzzybinary b383158
Throw if BUMP_TYPE will be ignored.
fuzzybinary 96a8c45
More review fixes
fuzzybinary File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| // Copyright 2019-Present Datadog, Inc. | ||
|
|
||
| import 'dart:io'; | ||
|
|
||
| import 'package:logging/logging.dart'; | ||
|
|
||
| import 'helpers.dart'; | ||
|
|
||
| /// Matches the `GIT_TAG <ref>` argument anywhere on a line -- not anchored to | ||
| /// the line start, since `FetchContent_Declare(dd-sdk-cpp ... GIT_TAG develop)` | ||
| /// is valid CMake written on a single line. | ||
| final _gitTagPattern = RegExp(r'(?<prefix>GIT_TAG\s+)(?<ref>[\w./-]+)'); | ||
|
|
||
| /// A trailing `# ...` comment, stripped before [_gitTagPattern] is applied so | ||
| /// a previous run's `# <tag>` annotation is replaced rather than duplicated -- | ||
| /// and so a `#`-commented mention of `GIT_TAG` can't be mistaken for the pin. | ||
| final _trailingCommentPattern = RegExp(r'\s*#.*$'); | ||
|
|
||
| final _ddSdkCppDeclareStartPattern = RegExp( | ||
| r'FetchContent_Declare\(\s*dd-sdk-cpp\b', | ||
| ); | ||
|
|
||
| /// Tracks whether a line belongs to the `FetchContent_Declare(dd-sdk-cpp ...)` | ||
| /// call, which can span several lines -- a `CMakeLists.txt` is free to declare | ||
| /// other `FetchContent` dependencies, and only dd-sdk-cpp's `GIT_TAG` is this | ||
| /// tooling's to read or rewrite. | ||
| /// | ||
| /// Shared by [hasDdSdkCppGitTag] (which decides whether a file counts as a | ||
| /// dd-sdk-cpp dependency file at all) and [pinCppVersion] (which rewrites it), | ||
| /// so the two can't disagree about where the block starts and ends. | ||
| class _DdSdkCppBlockScanner { | ||
| var _inBlock = false; | ||
| var _parenDepth = 0; | ||
|
|
||
| static int _parenBalance(String line) => | ||
| '('.allMatches(line).length - ')'.allMatches(line).length; | ||
|
|
||
| /// Advances the scanner over [line] and returns whether that line is inside | ||
| /// the block -- the opening `FetchContent_Declare(dd-sdk-cpp` line included, | ||
| /// since a single-line declaration carries its `GIT_TAG` there. | ||
| bool accept(String line) { | ||
| if (_ddSdkCppDeclareStartPattern.hasMatch(line)) { | ||
| _inBlock = true; | ||
| _parenDepth = _parenBalance(line); | ||
| } else if (_inBlock) { | ||
| _parenDepth += _parenBalance(line); | ||
| } | ||
|
|
||
| final result = _inBlock; | ||
| if (_inBlock && _parenDepth <= 0) _inBlock = false; | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
| /// Whether [cmakeListsContent] pins dd-sdk-cpp -- a `GIT_TAG` line inside a | ||
| /// `FetchContent_Declare(dd-sdk-cpp ...)` call. This is what makes a | ||
| /// `windows/`/`linux/` `CMakeLists.txt` a native-dependency file as far as | ||
| /// `native_sdk.dart`'s discovery is concerned; a file whose only `GIT_TAG` | ||
| /// belongs to some other vendored dependency isn't one. | ||
| bool hasDdSdkCppGitTag(String cmakeListsContent) { | ||
| final scanner = _DdSdkCppBlockScanner(); | ||
| for (final line in cmakeListsContent.split('\n')) { | ||
| if (scanner.accept(line) && | ||
| _gitTagPattern.hasMatch( | ||
| line.replaceFirst(_trailingCommentPattern, ''), | ||
| )) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /// Rewrites [line]'s `GIT_TAG` ref to [targetSha], re-annotated with | ||
| /// `# [targetTag]`, leaving everything else on the line as-is -- a trailing | ||
| /// `)` closing the `FetchContent_Declare(...` call, a `GIT_REPOSITORY` sharing | ||
| /// the line in a single-line declaration, the original indentation. The | ||
| /// comment goes at end of line, since a `#` placed before the closing paren | ||
| /// would comment it out and break the call. | ||
| String _pinGitTagLine(String line, String targetTag, String targetSha) { | ||
| final bare = line.replaceFirst(_trailingCommentPattern, ''); | ||
| final match = _gitTagPattern.firstMatch(bare); | ||
| if (match == null) return line; | ||
|
|
||
| final pinned = bare.replaceRange( | ||
| match.start, | ||
| match.end, | ||
| '${match.namedGroup('prefix')}$targetSha', | ||
| ); | ||
| return '${pinned.trimRight()} # $targetTag'; | ||
| } | ||
|
|
||
| /// Rewrites a CMakeLists.txt's dd-sdk-cpp `GIT_TAG` line to pin at | ||
| /// [targetSha] -- the resolved commit SHA for [targetTag] (e.g. `v1.4.0`), | ||
| /// kept as a trailing `# <tag>` comment since CMake's `FetchContent_Declare` | ||
| /// has no field of its own for pairing a tag with a verified commit. A | ||
| /// full commit SHA is used as the actual pin (not the tag) because it's | ||
| /// immutable, unlike a tag, which can be moved to point elsewhere later. | ||
| /// | ||
| /// Preserves the line's existing whitespace and everything around the ref -- | ||
| /// see [_pinGitTagLine]. | ||
| /// | ||
| /// Only ever called against a release-prep/patch/pre-release branch's copy | ||
| /// of the file -- `develop`'s own floating `GIT_TAG develop` is never | ||
| /// touched by release tooling. | ||
| Future<void> pinCppVersion( | ||
| File cmakeListsFile, | ||
| String targetTag, | ||
| String targetSha, | ||
| Logger logger, | ||
| bool dryRun, | ||
| ) async { | ||
| logger.info( | ||
| 'ℹ️ Pinning dd-sdk-cpp GIT_TAG to $targetSha ($targetTag) in ' | ||
| '${cmakeListsFile.path}', | ||
| ); | ||
|
|
||
| final scanner = _DdSdkCppBlockScanner(); | ||
|
|
||
| await transformFile( | ||
| cmakeListsFile, | ||
| logger, | ||
| dryRun, | ||
| (line) => scanner.accept(line) | ||
| ? _pinGitTagLine(line, targetTag, targetSha) | ||
| : line, | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| // Copyright 2019-Present Datadog, Inc. | ||
|
|
||
| import 'version_updater.dart'; | ||
|
|
||
| /// A parsed conventional commit: header (`type(scope)!: description`) plus | ||
| /// the footers other tooling in this package cares about (a `BREAKING | ||
| /// CHANGE:` footer, and `refs:` lines referencing GitHub issues). | ||
| class ConventionalCommit { | ||
| final String type; | ||
| final String? scope; | ||
|
|
||
| /// Whether the header itself carries a `!` breaking marker. | ||
| final bool hasBreakingMarker; | ||
| final String description; | ||
|
|
||
| /// Whether a `BREAKING CHANGE:`/`BREAKING-CHANGE:` footer is present, | ||
| /// independent of [hasBreakingMarker] -- either one marks the commit | ||
| /// breaking, see [isBreaking]. | ||
| final bool hasBreakingFooter; | ||
|
|
||
| /// Raw reference tokens from `refs:` footer lines (e.g. `refs: #123, | ||
| /// RUM-456` -> `['#123', 'RUM-456']`) -- a GitHub issue number, a JIRA | ||
| /// ticket, or anything else a `refs:` footer might carry. Not filtered | ||
| /// or interpreted here; a consumer that only wants GitHub issue links | ||
| /// (like `generate_changelog.dart`) picks those out itself. | ||
| final List<String> refs; | ||
|
|
||
| ConventionalCommit({ | ||
| required this.type, | ||
| required this.scope, | ||
| required this.hasBreakingMarker, | ||
| required this.description, | ||
| required this.hasBreakingFooter, | ||
| required this.refs, | ||
| }); | ||
|
|
||
| bool get isBreaking => hasBreakingMarker || hasBreakingFooter; | ||
|
|
||
| /// The semver bump this commit implies on its own: major if breaking | ||
| /// (marker or footer), minor for `feat`, patch for `fix`/`perf`, or null | ||
| /// for a type that doesn't carry semver weight by itself (`chore:`, | ||
| /// `docs:`, `test:`, etc.) and isn't marked breaking. | ||
| VersionBumpType? get bumpType { | ||
| if (isBreaking) return VersionBumpType.major; | ||
|
|
||
| switch (type) { | ||
| case 'feat': | ||
| return VersionBumpType.minor; | ||
| case 'fix': | ||
| case 'perf': | ||
| return VersionBumpType.patch; | ||
| default: | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| static final _headerPattern = RegExp( | ||
| r'^(?<type>\w+)(\((?<scope>[^)]*)\))?(?<breaking>!)?:\s*(?<rest>.*)', | ||
| ); | ||
| static final _breakingFooterPattern = RegExp( | ||
| r'^BREAKING[ -]CHANGE:', | ||
| multiLine: true, | ||
| ); | ||
|
|
||
| /// Parses a full commit message (subject line plus body/footers) into | ||
| /// its conventional-commit parts, or returns null if the subject line | ||
| /// doesn't match the convention at all. | ||
| static ConventionalCommit? parse(String commitMessage) { | ||
| final lines = commitMessage.split('\n'); | ||
| final match = _headerPattern.firstMatch(lines.first); | ||
| if (match == null) return null; | ||
|
|
||
| final refs = [ | ||
| for (final refLine in lines.where((l) => l.startsWith('refs:'))) | ||
| for (final token | ||
| in refLine.substring('refs:'.length).split(RegExp(r'[,\s]+'))) | ||
| if (token.isNotEmpty) token, | ||
| ]; | ||
|
|
||
| return ConventionalCommit( | ||
| type: match.namedGroup('type')!, | ||
| scope: match.namedGroup('scope'), | ||
| hasBreakingMarker: match.namedGroup('breaking') == '!', | ||
| description: match.namedGroup('rest')!, | ||
| hasBreakingFooter: _breakingFooterPattern.hasMatch(commitMessage), | ||
| refs: refs, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// The highest-severity bump implied by [commits] (major > minor > patch), | ||
| /// or null if none of them carry semver weight. | ||
| VersionBumpType? aggregateBumpLevel(Iterable<ConventionalCommit> commits) { | ||
| VersionBumpType? highest; | ||
| for (final commit in commits) { | ||
| final bump = commit.bumpType; | ||
| if (bump == null) continue; | ||
| if (highest == null || bump.severity > highest.severity) { | ||
| highest = bump; | ||
| } | ||
| } | ||
| return highest; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.