diff --git a/make_release/Readme.md b/make_release/Readme.md index c278203a..aa2c8dd7 100644 --- a/make_release/Readme.md +++ b/make_release/Readme.md @@ -60,8 +60,8 @@ There are two threads you can do in parallel: publishing crates and generating r :point_right: check that there is the same number of targets compared to [last release](https://github.com/nushell/nushell/releases/latest) ## 3. Publish `nu` to *crates.io* -- [ ] check the order of dependencies with `nushell/nu_scripts/make_release/nu_deps.nu` from the `nushell` repo -- [ ] release the Nushell crates `nushell/nu_scripts/make_release/nu_release.nu` from the `nushell` repo +- [ ] check that any new crates that should not be published have `package.publish = false` set in their `Cargo.toml` +- [ ] release the Nushell crates with `cargo publish --workspace` - [ ] **Important!** add any new crates to the `github:nushell:publishing` group on crates.io: `cargo owner --add github:nushell:publishing -p ` > **Note** diff --git a/make_release/notes/create-pr.nu b/make_release/notes/create-pr.nu index c3b25337..430192fd 100644 --- a/make_release/notes/create-pr.nu +++ b/make_release/notes/create-pr.nu @@ -37,7 +37,7 @@ export def main [ version: string@"nu-complete version" # the version of the release date: datetime@"nu-complete date next" # the date of the upcoming release ] { - let repo = ($nu.temp-path | path join (random uuid)) + let repo = ($nu.temp-dir | path join (random uuid)) let branch = $"release-notes-($version)" let blog_path = ( @@ -71,7 +71,7 @@ by opening PRs against the `release-notes-($version)` branch. } } - let temp_file = $nu.temp-path | path join $"(random uuid).md" + let temp_file = $nu.temp-dir | path join $"(random uuid).md" [ "" "" diff --git a/make_release/notes/generate.nu b/make_release/notes/generate.nu index 7726b198..3d0ccc14 100644 --- a/make_release/notes/generate.nu +++ b/make_release/notes/generate.nu @@ -21,7 +21,16 @@ use util.nu * export def get-release-notes []: record -> record { mut pr = $in + # Do not throw any warnings for hidden PRs + let has_hide_label = "notes:hide" in $pr.labels.name + let hidden = $SECTIONS | where label == "notes:hide" | only + if $has_hide_label { + $pr = ($pr | add-notice info "appearance only in full changelog") + return ($pr | insert section $hidden) + } + let has_ready_label = "notes:ready" in $pr.labels.name + let has_hall_of_fame_label = "notes:mention" in $pr.labels.name let sections = $SECTIONS | where label in $pr.labels.name let hall_of_fame = $SECTIONS | where label == "notes:mention" | only @@ -30,7 +39,10 @@ export def get-release-notes []: record -> record { $pr.body | extract-notes } else if $has_ready_label { # If no release notes summary exists but ready label is set, treat as empty - $pr = $pr | add-notice warning "no release notes section but notes:ready label" + if not $has_hall_of_fame_label { + # Hall of fame does not need release notes section necessarily + $pr = $pr | add-notice warning "no release notes section but notes:ready label" + } "" } else { return ($pr | add-notice error "no release notes section") @@ -144,26 +156,38 @@ export def generate-section []: record -> string { let bullet = $prs | where ($it.notes | lines | length) == 1 # Add header - $body ++= [$"## ($section.h2)\n"] + $body ++= [$"## ($section.h2) \n"] # Add multi-line summaries - for note in $multiline.notes { - if ($note | str ends-with "\n") { - $body ++= [$note] - } else { - $body ++= [($note ++ (char nl))] - } - } + $body ++= $multiline | generate-multiline-notes # Add single-line summaries if ($multiline | is-not-empty) and ($bullet | is-not-empty) { - $body ++= [$"### ($section.h3)\n"] + $body ++= [$"### ($section.h3) \n"] } $body ++= $bullet | each {|pr| "* " ++ $pr.notes ++ $" \(($pr | pr-link)\)" } ($body | str join (char nl)) ++ (char nl) } +def generate-multiline-notes []: table -> list { + $in | each {|pr| + let number = $pr.number + let author = $pr.author.login + + let pr_by_tag = $'' + let replacer = $"### $1 ($pr_by_tag)\n" + let matcher = "^### ([^\n]*)\n" + let updated = ($pr.notes | str replace --all --regex $matcher $replacer) + + if ($updated | str ends-with "\n") { + $updated + } else { + $updated ++ (char nl) + } + } +} + # Generate the "Hall of Fame" section of the release notes. export def generate-hall-of-fame []: table -> string { where section.label == "notes:mention" diff --git a/make_release/notes/notice.nu b/make_release/notes/notice.nu index ba7bacb1..fe8922f3 100644 --- a/make_release/notes/notice.nu +++ b/make_release/notes/notice.nu @@ -21,15 +21,21 @@ export def group-notices []: table -> table { | sort-by {|i| $TYPES | where type == $i.type | only rank } message } -# Print all of the notices associated with a PR -export def display-notices []: table -> nothing { - group-notices - | each {|e| +# Format all of the notices associated with a PR +export def format-notices []: table -> string { + mut output = "" + + mut first = true + for e in ($in | group-notices) { + if $first { $first = false } else { $output += "\n\n" } let color = $TYPES | where type == $e.type | only color let number = $e.items | length - print $"($color)($number) PR\(s\) with ($e.message):" - $e.items | each { format-pr | print $"- ($in)" } - print "" + $output += $"($color)($number) PR\(s\) with ($e.message):" + for item in $e.items { + $item | format-pr | $output += $"\n- ($in)" + } } - print -n (ansi reset) + + $output += (ansi reset) + $output } diff --git a/make_release/notes/template.md b/make_release/notes/template.md index e266066e..b9284539 100644 --- a/make_release/notes/template.md +++ b/make_release/notes/template.md @@ -24,9 +24,9 @@ As part of this release, we also publish a set of optional [plugins](https://www # Table of contents - + -# Highlights and themes of this release +# Highlights and themes of this release -# Changes +# Changes {changes} -# Notes for plugin developers +# Notes for plugin developers -# Hall of fame +# Hall of fame Thanks to all the contributors below for helping us solve issues, improve documentation, refactor code, and more! :pray: {hall_of_fame} -# Full changelog +# Full changelog {changelog} diff --git a/make_release/notes/tools.nu b/make_release/notes/tools.nu index cf8e613e..8b772f40 100644 --- a/make_release/notes/tools.nu +++ b/make_release/notes/tools.nu @@ -65,7 +65,7 @@ export def release-notes [ | where not author.is_bot | sort-by mergedAt | each { get-release-notes } - | tee { display-notices } + | tee { format-notices } | where {|pr| "error" not-in ($pr.notices?.type? | default []) } | generate-notes $version } @@ -75,14 +75,14 @@ export def check-prs [ version: string@"nu-complete version" # the version to generate release notes for --as-table (-t) # output PR checks as a table ]: [ - nothing -> nothing, + nothing -> string, nothing -> table ] { query-prs --milestone=$version | where not author.is_bot | sort-by mergedAt | each { get-release-notes } - | if $as_table { group-notices } else { display-notices } + | if $as_table { group-notices } else { format-notices } } # Format the output of `list-prs` as a markdown table @@ -94,108 +94,3 @@ export def pr-table [] { | to md | escape-tag } - -const toc = '[[toc](#table-of-contents)]' - -# Generate and write the table of contents to a release notes file -export def write-toc [file: path] { - let known_h1s = [ - "# Highlights and themes of this release", - "# Changes", - "# Notes for plugin developers", - "# Hall of fame", - "# Full changelog", - ] - - let lines = open $file | lines | each { str trim -r } - - let content_start = 2 + ( - $lines - | enumerate - | where item == '# Table of contents' - | first - | get index - ) - - let data = ( - $lines - | slice $content_start.. - | wrap line - | insert level { - get line | split chars | take while { $in == '#' } | length - } - | insert nocomment { - # We assume that comments only have one `#` - if ($in.level != 1) { - return true - } - let line = $in.line - - # Try to use the whitelist first - if ($known_h1s | any {|| $line =~ $in}) { - return true - } - - # We don't know so let's ask - let user = ([Ignore Accept] | - input list $"Is this a code comment or a markdown h1 heading:(char nl)(ansi blue)($line)(ansi reset)(char nl)Choose if we include it in the TOC!") - match $user { - "Accept" => {true} - "Ignore" => {false} - } - - } - ) - - let table_of_contents = ( - $data - | where level in 1..=3 and nocomment == true - | each {|header| - let indent = '- ' | fill -w ($header.level * 2) -a right - - mut text = $header.line | str trim -l -c '#' | str trim -l - if $text ends-with $toc { - $text = $text | str substring ..<(-1 * ($toc | str length)) | str trim -r - } - - let link = ( - $text - | str downcase - | str kebab-case - ) - - # remove PR link from header, if applicable - let regex = r#'(?x) # verbose mode - (?.+?) # the actual header text - \s+ - \( # start PR link - \[\#\d+\] # PR number component - (?: # optional non-capturing group - \(.+?\) # link to PR - )? # end group - \) - '# - let prlink = $text | parse -r $regex - if ($prlink | is-not-empty) { - $text = $prlink.0.text - } - - $"($indent)[_($text)_]\(#($link)-toc\)" - } - ) - - let content = $data | each { - if $in.level in 1..=3 and not ($in.line ends-with $toc) and $in.nocomment { - $'($in.line) ($toc)' - } else { - $in.line - } - } - - [ - ...($lines | slice ..<$content_start) - ...$table_of_contents - ...$content - ] - | save -r -f $file -}