| import 'dart:io'; |
| |
| import 'package:devtools_repo/repo_tool.dart'; |
| import 'package:devtools_shared/devtools_shared.dart'; |
| import 'package:json_annotation/json_annotation.dart'; |
| |
| part 'release_note_classes.g.dart'; |
| |
| @JsonSerializable() |
| class ReleaseNotes { |
| ReleaseNotes({ |
| required this.releases, |
| }); |
| |
| List<Release> releases; |
| |
| String toMarkdown() { |
| String markdown = ''; |
| for (var release in releases) { |
| markdown += release.toMarkdown(); |
| markdown += '\n'; |
| } |
| return markdown; |
| } |
| |
| factory ReleaseNotes.fromJson(Map<String, dynamic> json) => |
| _$ReleaseNotesFromJson(json); |
| |
| Map<String, dynamic> toJson() => _$ReleaseNotesToJson(this); |
| } |
| |
| @JsonSerializable() |
| class Release { |
| Release({ |
| required this.version, |
| required this.sections, |
| }) { |
| for (var element in sections) { |
| _sectionMap[element.name] = element; |
| } |
| } |
| |
| final SemanticVersion version; |
| List<ReleaseSection> sections = []; |
| final Map<String, ReleaseSection> _sectionMap = {}; |
| |
| void addNote(String sectionName, ReleaseNote note) { |
| _sectionMap[sectionName]!.notes.add(note); |
| } |
| |
| String toMarkdown() { |
| String markdown = ''; |
| markdown += '# DevTools $version release notes\n\n'; |
| sections.forEach((section) { |
| markdown += '# ${section.name}\n\n'; |
| for (var note in section.notes) { |
| markdown += note.toMarkdown(); |
| } |
| markdown += '\n'; |
| }); |
| return markdown; |
| } |
| |
| factory Release.fromJson(Map<String, dynamic> json) => |
| _$ReleaseFromJson(json); |
| |
| Map<String, dynamic> toJson() => _$ReleaseToJson(this); |
| } |
| |
| @JsonSerializable() |
| class ReleaseSection { |
| ReleaseSection({ |
| required this.name, |
| List<ReleaseNote>? notes, |
| }) : notes = notes ?? []; |
| |
| ReleaseSection.emptyNotes({ |
| required this.name, |
| }) : notes = []; |
| |
| final String name; |
| final List<ReleaseNote> notes; |
| |
| factory ReleaseSection.fromJson(Map<String, dynamic> json) => |
| _$ReleaseSectionFromJson(json); |
| |
| Map<String, dynamic> toJson() => _$ReleaseSectionToJson(this); |
| } |
| |
| @JsonSerializable() |
| class ReleaseNote { |
| ReleaseNote({ |
| required this.message, |
| this.imageNames, |
| this.githubPullRequestUrls, |
| }) { |
| if (imageNames != null) { |
| for (var name in imageNames!) { |
| final path = "release_notes/files/$name"; |
| //TODO: get the proper path? |
| if (!File(path).existsSync()) { |
| throw Exception( |
| "Could not find image file $path for note: \n${toMarkdown()}"); |
| } |
| } |
| } |
| } |
| |
| List<String>? githubPullRequestUrls; |
| final String message; |
| final List<String>? imageNames; |
| |
| String toMarkdown() { |
| String markdown = ''; |
| markdown += '- $message'; |
| if (githubPullRequestUrls != null) { |
| List<String> prUrls = []; |
| for (var url in githubPullRequestUrls!) { |
| final match = RegExp( |
| r'^https://github.com/flutter/devtools/pull/(?<pr_number>\d+)$') |
| .firstMatch(url); |
| if (match == null) { |
| throw Exception( |
| "Invalid github PR Url($url) found in message: $message}"); |
| } |
| final prNumber = match.namedGroup('pr_number'); |
| prUrls.add('[#$prNumber]($url)'); |
| } |
| markdown += ' - ${prUrls.join(", ")}'; |
| } |
| |
| markdown += '\n'; |
| |
| if (imageNames != null) { |
| for (var imageName in imageNames!) { |
| markdown += '\n'; |
| } |
| } |
| |
| return markdown; |
| } |
| |
| factory ReleaseNote.fromJson(Map<String, dynamic> json) => |
| _$ReleaseNoteFromJson(json); |
| |
| Map<String, dynamic> toJson() => _$ReleaseNoteToJson(this); |
| } |
| // # Sementic line breaks of 80 chars or fewer |
| // # each line requires an PR command |