Skip to content
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

Release notes CI step #16283

Merged
merged 42 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e67c60d
CI Release notes testing
vzarytovskii Nov 16, 2023
3d41b12
GH actions pls
vzarytovskii Nov 16, 2023
190543a
GH actions pls
vzarytovskii Nov 16, 2023
a9dfb6c
GH actions pls
vzarytovskii Nov 16, 2023
8be6865
GH actions pls
vzarytovskii Nov 16, 2023
2e8d955
GH actions pls
vzarytovskii Nov 16, 2023
8708a0f
GH actions pls
vzarytovskii Nov 16, 2023
2ed53f2
GH actions pls
vzarytovskii Nov 16, 2023
ea6ed53
GH actions pls
vzarytovskii Nov 16, 2023
ad3893a
GH actions pls
vzarytovskii Nov 16, 2023
9ee5484
GH actions pls
vzarytovskii Nov 16, 2023
6e614ec
GH actions pls
vzarytovskii Nov 16, 2023
d5ed785
GH actions pls
vzarytovskii Nov 16, 2023
a0d1b25
GH actions pls
vzarytovskii Nov 16, 2023
30a8a19
GH actions pls
vzarytovskii Nov 16, 2023
21859e3
GH actions pls
vzarytovskii Nov 20, 2023
92755f7
GH actions pls
vzarytovskii Nov 20, 2023
e1580a5
GH actions pls
vzarytovskii Nov 20, 2023
d09a36c
GH actions pls
vzarytovskii Nov 20, 2023
2bc0fc7
GH actions pls
vzarytovskii Nov 20, 2023
bfb14a6
GH actions pls
vzarytovskii Nov 20, 2023
3ed6bf9
GH actions pls
vzarytovskii Nov 20, 2023
990a972
GH actions pls
vzarytovskii Nov 20, 2023
74373c7
GH actions pls
vzarytovskii Nov 20, 2023
0afa8d8
GH actions pls
vzarytovskii Nov 20, 2023
4981339
GH actions pls
vzarytovskii Nov 20, 2023
be7180e
GH actions pls
vzarytovskii Nov 20, 2023
afa5914
GH actions pls
vzarytovskii Nov 20, 2023
db190c1
GH actions pls
vzarytovskii Nov 20, 2023
869b71e
GH actions pls
vzarytovskii Nov 20, 2023
a07b557
GH actions pls
vzarytovskii Nov 20, 2023
641917b
GH actions pls
vzarytovskii Nov 20, 2023
0d46ef1
Merge branch 'main' into release-notes-ci-workflow
vzarytovskii Nov 20, 2023
45903e0
Merge branch 'main' into release-notes-ci-workflow
vzarytovskii Nov 20, 2023
8624e0b
GH actions pls
vzarytovskii Nov 20, 2023
21d19b7
Please work
vzarytovskii Nov 20, 2023
e11562c
Automated command ran: fantomas
github-actions[bot] Nov 20, 2023
aa58ed9
GH actions pls
vzarytovskii Nov 20, 2023
bb52192
GH actions pls
vzarytovskii Nov 20, 2023
7287b22
GH actions pls
vzarytovskii Nov 20, 2023
a9c7559
GH actions pls
vzarytovskii Nov 20, 2023
c556e35
GH actions pls
vzarytovskii Nov 20, 2023
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
25 changes: 25 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Description

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

Fixes # (issue, if applicable)

## Checklist

- [ ] Test cases added
- [ ] Performance benchmarks added in case of performance changes
- [ ] Release notes entry updated:
> Please make sure to add an entry with short succint description of the change as well as link to this pull request to the respective release notes file, if applicable.
>
> Release notes files:
> - If anything under `src/Compiler` has been changed, please make sure to make an entry in `docs/release-notes/FSharp.Compiler.Service/<version>.md`, where `<version>` is usually "highest" one, e.g. `42.8.200`
> - If language feature was added (i.e. `LanguageFeatures.fsi` was changed), please add it to `docs/releae-notes/Language/preview.md`
> - If a change to `FSharp.Core` was made, please make sure to edit `docs/release-notes/FSharp.Core/<version>.md` where version is "highest" one, e.g. `8.0.200`.

> Examples of release notes entries:
> - Respect line limit in quick info popup - https://github.com/dotnet/fsharp/pull/16208
> - More inlines for Result module - https://github.com/dotnet/fsharp/pull/16106
> - Miscellaneous fixes to parens analysis - https://github.com/dotnet/fsharp/pull/16262
>

> **If you believe that release notes are not necessary for this PR, please add `NO_RELEASE_NOTES` label to the pull request.**
257 changes: 257 additions & 0 deletions .github/workflows/check_release_notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,260 @@ jobs:
repo: context.repo.repo,
});
return { "pr_number": context.issue.number, "ref": result.data.head.ref, "repository": result.data.head.repo.full_name};
- name: Checkout repo
uses: actions/checkout@v2
with:
repository: ${{ fromJson(steps.get-pr.outputs.result).repository }}
ref: ${{ fromJson(steps.get-pr.outputs.result).ref }}
fetch-depth: 0
- name: Check for release notes changes
id: release_notes_changes
run: |
set -e
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
FSHARP_REPO_URL="https://github.com/${GITHUB_REPOSITORY}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
PR_NUMBER=${{ github.event.number }}
PR_URL="${FSHARP_REPO_URL}/pull/${PR_NUMBER}"

echo "PR Tags: ${{ toJson(github.event.pull_request.labels) }}"

OPT_OUT_RELEASE_NOTES=${{ contains(github.event.pull_request.labels.*.name, 'NO_RELEASE_NOTES') }}

echo "Opt out of release notes: $OPT_OUT_RELEASE_NOTES"

# Parse version from eng/Versions.props
# For FSarp.Core:
# <FSMajorVersion>8</FSMajorVersion>
# <FSMinorVersion>0</FSMinorVersion>
# <FSBuildVersion>100</FSBuildVersion>
# For FCS:
# FCSMajorVersion>43</FCSMajorVersion>
# <FCSMinorVersion>8</FCSMinorVersion>
# <FCSBuildVersion>$(FSBuildVersion)</FCSBuildVersion>
# For VS:
# <VSMajorVersion>17</VSMajorVersion>
# <VSMinorVersion>8</VSMinorVersion>

_fs_major_version=$(grep -oPm1 "(?<=<FSMajorVersion>)[^<]+" eng/Versions.props)
_fs_minor_version=$(grep -oPm1 "(?<=<FSMinorVersion>)[^<]+" eng/Versions.props)
_fs_build_version=$(grep -oPm1 "(?<=<FSBuildVersion>)[^<]+" eng/Versions.props)
_fcs_major_version=$(grep -oPm1 "(?<=<FCSMajorVersion>)[^<]+" eng/Versions.props)
_fcs_minor_version=$(grep -oPm1 "(?<=<FCSMinorVersion>)[^<]+" eng/Versions.props)
_fcs_build_version=$_fs_build_version
_vs_major_version=$(grep -oPm1 "(?<=<VSMajorVersion>)[^<]+" eng/Versions.props)
_vs_minor_version=$(grep -oPm1 "(?<=<VSMinorVersion>)[^<]+" eng/Versions.props)

FSHARP_CORE_VERSION="$_fs_major_version.$_fs_minor_version.$_fs_build_version"
FCS_VERSION="$_fcs_major_version.$_fcs_minor_version.$_fcs_build_version"
VISUAL_STUDIO_VERSION="$_vs_major_version.$_vs_minor_version"

echo "Found F# version: ${FSHARP_CORE_VERSION}"
echo "Found FCS version: ${FCS_VERSION}"
echo "Found Visual Studio version: ${VISUAL_STUDIO_VERSION}"

[[ "$FSHARP_CORE_VERSION" =~ ^[0-9]+\.[0-9]+.[0-9]+$ ]] || (echo " Invalid FSharp.Core Version parsed"; exit 1)
[[ "$FCS_VERSION" =~ ^[0-9]+\.[0-9]+.[0-9]+$ ]] || (echo " Invalid FCS Version parsed"; exit 1)
[[ "$VISUAL_STUDIO_VERSION" =~ ^[0-9]+\.[0-9]+$ ]] || (echo " Invalid Visual Studio Version parsed"; exit 1)

_release_notes_base_path='docs/release-notes'
_fsharp_core_release_notes_path="${_release_notes_base_path}/FSharp.Core/${FSHARP_CORE_VERSION}.md"
_fsharp_compiler_release_notes_path="${_release_notes_base_path}/FSharp.Compiler.Service/${FSHARP_CORE_VERSION}.md"
_fsharp_language_release_notes_path="${_release_notes_base_path}/Language/preview.md"
_fsharp_vs_release_notes_path="${_release_notes_base_path}/VisualStudio/${VISUAL_STUDIO_VERSION}.md"

readonly paths=(
"src/FSharp.Core|${_fsharp_core_release_notes_path}"
"src/Compiler|${_fsharp_compiler_release_notes_path}"
"LanguageFeatures.fsi|${_fsharp_language_release_notes_path}"
"vsintegration/src|${_fsharp_vs_release_notes_path}"
)

# Check all changed paths
RELEASE_NOTES_MESSAGE=""
RELEASE_NOTES_MESSAGE_DETAILS=""
RELEASE_NOTES_FOUND=""
RELEASE_NOTES_CHANGES_SUMMARY=""
RELEASE_NOTES_NOT_FOUND=""
PULL_REQUEST_FOUND=true

gh repo set-default ${GITHUB_REPOSITORY}

_modified_paths=`gh pr view ${PR_NUMBER} --json files --jq '.files.[].path'`

for fields in ${paths[@]}
do
IFS=$'|' read -r path release_notes <<< "$fields"
echo "Checking for changed files in: $path"

# Check if path is in modified files:
if [[ "${_modified_paths[@]}" =~ "${path}" ]]; then
echo " Found $path in modified files"
echo " Checking if release notes modified in: $release_notes"
if [[ "${_modified_paths[@]}" =~ "${release_notes}" ]]; then
echo " Found $release_notes in modified files"
echo " Checking for pull request URL in $release_notes"

if [[ ! -f $release_notes ]]; then
echo " $release_notes does not exist, please, create it."
#exit 1;
fi

_pr_link_occurences=`grep -c "${PR_URL}" $release_notes || true`

echo " Found $_pr_link_occurences occurences of $PR_URL in $release_notes"

if [[ ${_pr_link_occurences} -eq 1 ]]; then
echo " Found pull request URL in $release_notes once"
RELEASE_NOTES_FOUND+="> | \\\`$path\\\` | [$release_notes](${FSHARP_REPO_URL}/$release_notes) | |"
RELEASE_NOTES_FOUND+=$'\n'
elif [[ ${_pr_link_occurences} -eq 0 ]]; then
echo " Did not find pull request URL in $release_notes"
DESCRIPTION="**No current pull request URL (${PR_URL}) found, please consider adding it**"
RELEASE_NOTES_FOUND+="> | \\\`$path\\\` | [$release_notes](${FSHARP_REPO_URL}/$release_notes) | ${DESCRIPTION} |"
RELEASE_NOTES_FOUND+=$'\n'
PULL_REQUEST_FOUND=false
fi
else
echo " Did not find $release_notes in modified files"
DESCRIPTION="**No release notes found or release notes format is not correct**"
RELEASE_NOTES_NOT_FOUND+="| \\\`$path\\\` | [$release_notes](${FSHARP_REPO_URL}/$release_notes) | ${DESCRIPTION} |"
RELEASE_NOTES_NOT_FOUND+=$'\n'
fi
else
echo " Nothing found, no release notes required"
fi
done

echo "Done checking for release notes changes"

if [[ $RELEASE_NOTES_NOT_FOUND != "" ]]; then
RELEASE_NOTES_MESSAGE_DETAILS+=$"@${PR_AUTHOR},"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> [!CAUTION]"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> **No release notes found for the following paths.**"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$">"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> Please make sure to add an entry with short succint description of the change as well as link to this pull request."
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$">"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> Examples: "
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> \\\`- Respect line limit in quick info popup - https://github.com/dotnet/fsharp/pull/16208\\\`"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> \\\`- More inlines for Result module - https://github.com/dotnet/fsharp/pull/16106\\\`"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> \\\`- Miscellaneous fixes to parens analysis - https://github.com/dotnet/fsharp/pull/16262\\\`"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'**If you believe that release notes are not necessary for this PR, please add "NO_RELEASE_NOTES" label to the pull request.**'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"**You can open this PR in browser to add release notes: [open in github.dev](https://github.dev/dotnet/fsharp/pull/${PR_NUMBER})**"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+='| Change path | Release notes path | Description |'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+='| ---------------- | ------------------ | ----------- |'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+="${RELEASE_NOTES_NOT_FOUND}"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
fi

if [[ $RELEASE_NOTES_FOUND != "" ]]; then
RELEASE_NOTES_MESSAGE_DETAILS+=$"<hr/>"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> :white_check_mark: Found changes and release notes in following paths:"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'

if [[ $PULL_REQUEST_FOUND = false ]]; then
RELEASE_NOTES_MESSAGE_DETAILS+=$"> [!WARNING]"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$"> **No PR link found in some release notes, please consider adding it.**"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
fi

RELEASE_NOTES_MESSAGE_DETAILS+='> | Change path | Release notes path | Description |'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+='> | ---------------- | ------------------ | ----------- |'
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
RELEASE_NOTES_MESSAGE_DETAILS+="${RELEASE_NOTES_FOUND}"
RELEASE_NOTES_MESSAGE_DETAILS+=$'\n'
fi

RELEASE_NOTES_MESSAGE+=$'<!-- DO_NOT_REMOVE: release_notes_check -->\n'

if [[ $RELEASE_NOTES_MESSAGE_DETAILS == "" ]]; then
RELEASE_NOTES_MESSAGE+=$'## :white_check_mark: No release notes required\n\n'
else
RELEASE_NOTES_MESSAGE+=$'## :heavy_exclamation_mark: Release notes required\n\n'
RELEASE_NOTES_MESSAGE+=$RELEASE_NOTES_MESSAGE_DETAILS
fi

echo "release-notes-check-message<<$EOF" >>$GITHUB_OUTPUT

if [[ "$OPT_OUT_RELEASE_NOTES" = true ]]; then
echo "<!-- DO_NOT_REMOVE: release_notes_check -->" >>$GITHUB_OUTPUT
echo "" >>$GITHUB_OUTPUT
echo "## :warning: Release notes required, but author opted out" >>$GITHUB_OUTPUT
echo "" >>$GITHUB_OUTPUT
echo "" >>$GITHUB_OUTPUT
echo "> [!WARNING]" >>$GITHUB_OUTPUT
echo "> **Author opted out of release notes, check is disabled for this pull request.**" >>$GITHUB_OUTPUT
echo "> cc @dotnet/fsharp-team-msft" >>$GITHUB_OUTPUT
else
echo "${RELEASE_NOTES_MESSAGE}" >>$GITHUB_OUTPUT
fi

echo "$EOF" >>$GITHUB_OUTPUT

if [[ $RELEASE_NOTES_NOT_FOUND != "" && ${OPT_OUT_RELEASE_NOTES} != true ]]; then
exit 1
fi
# Did bot already commented the PR?
- name: Find Comment
if: success() || failure()
uses: peter-evans/find-comment@v2.4.0
id: fc
with:
issue-number: ${{github.event.pull_request.number}}
comment-author: 'github-actions[bot]'
body-includes: '<!-- DO_NOT_REMOVE: release_notes_check -->'
# If not, create a new comment
- name: Create comment
if: steps.fc.outputs.comment-id == '' && (success() || failure())
uses: actions/github-script@v6
with:
github-token: ${{ github.token }}
script: |
const comment = await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `${{steps.release_notes_changes.outputs.release-notes-check-message}}`
});
return comment.data.id;
# If yes, update the comment
- name: Update comment
if: steps.fc.outputs.comment-id != '' && (success() || failure())
uses: actions/github-script@v6
with:
github-token: ${{ github.token }}
script: |
const comment = await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{steps.fc.outputs.comment-id}},
body: `${{steps.release_notes_changes.outputs.release-notes-check-message}}`
});
return comment.data.id;
6 changes: 4 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,11 @@ stages:

# Check whether package with current version has been published to nuget.org
# We will try to restore both FSharp.Core and FCS and if restore is _successful_, package version needs to be bumped.
# NOTE: This CI check should only run on the release branches.
# NOTE: The check now runs on all branches (not just release),
# because we want to catch cases when version is desynched and we didn't update it.
# It is also helping the release notes automation to be up to date with packages versions.
- job: Check_Published_Package_Versions
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), or(startsWith(variables['System.PullRequest.SourceBranch'], 'release/dev'), startsWith(variables['System.PullRequest.TargetBranch'], 'release/dev')))
# condition: or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), or(startsWith(variables['System.PullRequest.SourceBranch'], 'release/dev'), startsWith(variables['System.PullRequest.TargetBranch'], 'release/dev')))
pool:
vmImage: $(UbuntuMachineQueueName)
strategy:
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/FSharp.Compiler.Service/8.0.200.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Miscellaneous fixes to parens analysis - https://github.com/dotnet/fsharp/pull/16262
1 change: 1 addition & 0 deletions docs/release-notes/FSharp.Core/8.0.200.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- More inlines for Result module - https://github.com/dotnet/fsharp/pull/16106
1 change: 1 addition & 0 deletions docs/release-notes/Language/preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Feature: Better unmanaged structs handling - https://github.com/dotnet/fsharp/pull/12154
6 changes: 4 additions & 2 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
<!-- Version number computation -->
<PropertyGroup>
<PreReleaseVersionLabel>beta</PreReleaseVersionLabel>
<!-- These have to be in sync with latest release branch -->
<!-- F# Version components -->
<FSMajorVersion>8</FSMajorVersion>
<FSMinorVersion>0</FSMinorVersion>
<FSBuildVersion>100</FSBuildVersion>
<FSBuildVersion>200</FSBuildVersion>
<FSRevisionVersion>0</FSRevisionVersion>
<!-- -->
<!-- F# Language version -->
Expand Down Expand Up @@ -55,8 +56,9 @@
<FSProductVersion>$(FSToolsMajorVersion).$(FSToolsMinorVersion).$(FSToolsBuildVersion).$(FSToolsRevisionVersion)</FSProductVersion>
</PropertyGroup>
<PropertyGroup>
<!-- These have to be in sync with latest release branch -->
<VSMajorVersion>17</VSMajorVersion>
<VSMinorVersion>8</VSMinorVersion>
<VSMinorVersion>9</VSMinorVersion>
<VSGeneralVersion>$(VSMajorVersion).0</VSGeneralVersion>
<VSAssemblyVersionPrefix>$(VSMajorVersion).$(VSMinorVersion).0</VSAssemblyVersionPrefix>
<VSAssemblyVersion>$(VSAssemblyVersionPrefix).0</VSAssemblyVersion>
Expand Down
1 change: 1 addition & 0 deletions src/FSharp.Core/prim-types-prelude.fs
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,4 @@ namespace Microsoft.FSharp.Core
type voidptr = (# "void*" #)

type ilsigptr<'T> = (# "!0*" #)

1 change: 1 addition & 0 deletions vsintegration/src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
</PropertyGroup>

</Project>