elm-review/tests/Docs/NoMissingChangelogEntry.elm

276 lines
8.9 KiB
Elm
Raw Normal View History

2023-06-02 20:14:39 +03:00
module Docs.NoMissingChangelogEntry exposing
( rule
, Configuration, defaults, withPathToChangelog
)
{-|
@docs rule
config =
2023-06-02 20:14:39 +03:00
[ Docs.NoMissingChangelogEntry.rule Docs.NoMissingChangelogEntry.defaults
]
2023-06-02 20:14:39 +03:00
@docs Configuration, defaults, withPathToChangelog
## Example
Given the following `CHANGELOG.md` file:
# Changelog
## [Unreleased]
Stuff happened
## 1.2.0
More stuff happened
## 1.1.0
Stuff happened
[Unreleased]: https://github.com/author/package-name/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/author/package-name/releases/tag/1.2.0
[1.1.0]: https://github.com/author/package-name/releases/tag/1.1.0
If the current version is `1.2.0`, then there won't be any error.
If the current version is `1.2.1`, then an error will be reported,
and a fix will be suggested to fix to the following:
# Changelog
## [Unreleased]
## [1.2.1]
Stuff happened
## 1.2.0
More stuff happened
## 1.1.0
Stuff happened
[Unreleased]: https://github.com/author/package-name/compare/v1.2.1...HEAD
[1.2.1]: https://github.com/author/package-name/releases/tag/1.2.1
[1.2.0]: https://github.com/author/package-name/releases/tag/1.2.0
[1.1.0]: https://github.com/author/package-name/releases/tag/1.1.0
## When (not) to enable this rule
This rule is useful only when the project is an Elm package
and you would like to have an automated .
## Try it out
You can try this rule out by running the following command:
```bash
elm-review --template jfmengels/elm-review/example --rules Docs.NoMissingChangelogEntry
```
-}
2023-06-02 20:14:39 +03:00
2024-04-17 01:52:29 +03:00
import Dict exposing (Dict)
2023-06-02 20:14:39 +03:00
import Elm.Project exposing (Project)
import Elm.Version
2024-04-07 23:54:21 +03:00
import Review.FilePattern as FilePattern
2023-06-03 22:25:11 +03:00
import Review.Fix as Fix
2023-06-02 20:14:39 +03:00
import Review.Rule as Rule exposing (Rule)
{-| Reports when `CHANGELOG.md` is missing an entry for the current version of the Elm package.
2023-06-02 20:14:39 +03:00
-}
rule : Configuration -> Rule
rule (Configuration { changelogPath }) =
Rule.newProjectRuleSchema "Docs.NoMissingChangelogEntry" initialProjectContext
|> Rule.withElmJsonProjectVisitor elmJsonVisitor
2024-04-17 01:52:29 +03:00
|> Rule.withExtraFilesProjectVisitor (extraFilesVisitor changelogPath) [ FilePattern.include (getChangelogPath changelogPath) ]
2023-06-03 22:25:11 +03:00
|> Rule.providesFixesForProjectRule
|> Rule.fromProjectRuleSchema
{-| Configuration for the rule.
-}
2023-06-02 20:14:39 +03:00
type Configuration
= Configuration { changelogPath : Maybe String }
2023-06-02 20:14:39 +03:00
{-| Default configuration for the rule.
Considers the changelog to be `CHANGELOG.md` next to the project's `elm.json`.
-}
2023-06-02 20:14:39 +03:00
defaults : Configuration
defaults =
Configuration { changelogPath = Nothing }
{-| Define the path to the changelog.
2023-06-02 20:14:39 +03:00
config =
[ Docs.NoMissingChangelogEntry.defaults
|> Docs.NoMissingChangelogEntry.withPathToChangelog "path/to/changelog.txt"
|> Docs.NoMissingChangelogEntry.rule
]
2023-06-02 20:14:39 +03:00
-}
2023-06-02 20:14:39 +03:00
withPathToChangelog : String -> Configuration -> Configuration
withPathToChangelog changelogPath _ =
Configuration { changelogPath = Just changelogPath }
2023-06-02 20:14:39 +03:00
getChangelogPath : Maybe String -> String
getChangelogPath changelogPath =
Maybe.withDefault "CHANGELOG.md" changelogPath
type alias ProjectContext =
{ elmJsonVersion : Maybe String
}
initialProjectContext : ProjectContext
initialProjectContext =
{ elmJsonVersion = Nothing
}
elmJsonVisitor : Maybe { a | project : Project } -> ProjectContext -> ( List (Rule.Error scope), ProjectContext )
elmJsonVisitor maybeElmJsonData context =
case maybeElmJsonData of
Just { project } ->
case project of
Elm.Project.Package { version } ->
( [], { context | elmJsonVersion = Just (Elm.Version.toString version) } )
Elm.Project.Application _ ->
( [ Rule.globalError
{ message = "The Elm project is unexpectedly an application"
, details = [ "This rule only supports Elm packages, but doesn't support Elm applications as they don't have a version number. I recommend that you remove this rule from your review configuration." ]
}
]
, context
)
Nothing ->
( [], context )
2023-06-02 18:22:18 +03:00
2024-04-17 01:52:29 +03:00
extraFilesVisitor : Maybe String -> Dict String { fileKey : Rule.ExtraFileKey, content : String } -> ProjectContext -> ( List (Rule.Error { useErrorForModule : () }), ProjectContext )
extraFilesVisitor maybeChangelogPath files context =
let
changelogPath : String
changelogPath =
getChangelogPath maybeChangelogPath
in
case Dict.get changelogPath files of
2023-06-03 02:37:23 +03:00
Just { fileKey, content } ->
case context.elmJsonVersion of
Nothing ->
( [], context )
Just "1.0.0" ->
( [], context )
Just elmJsonVersion ->
if String.contains elmJsonVersion content then
( [], context )
else
2023-06-03 22:05:25 +03:00
( [ reportError fileKey elmJsonVersion content ]
, context
)
2023-06-02 18:22:18 +03:00
Nothing ->
case context.elmJsonVersion of
Nothing ->
( [], context )
Just "1.0.0" ->
-- TODO Report an error with a changelog skeleton
( [], context )
Just _ ->
2024-04-17 01:52:29 +03:00
case maybeChangelogPath of
Nothing ->
( [ Rule.globalError
{ message = "Could not find the CHANGELOG.md file"
, details =
[ "I was looking for the CHANGELOG.md file next to your project's elm.json file but couldn't find it. Please make sure that the spelling is correct."
, "If your changelog is named differently or is in a different location, then you can configure this rule to look for it in a different location:"
, """ config =
[ Docs.NoMissingChangelogEntry.defaults
|> Docs.NoMissingChangelogEntry.withPathToChangelog "path/to/your/changelog.md"
|> Docs.NoMissingChangelogEntry.rule
]"""
, "Note that the path is relative your project's elm.json file."
]
}
]
, context
)
Just customPath ->
( [ Rule.globalError
{ message = "Could not find the " ++ customPath ++ " changelog file"
, details =
[ "I was looking for the " ++ customPath ++ " changelog file but couldn't find it. Please make sure that the path you specified through Docs.NoMissingChangelogEntry.withPathToChangelog is correct."
, "Also note that the path you specify has to be relative to your project's elm.json file."
]
}
]
, context
)
2023-06-03 22:05:25 +03:00
reportError : Rule.ExtraFileKey -> String -> String -> Rule.Error scope
reportError fileKey elmJsonVersion content =
let
lines : List String
lines =
String.lines content
2023-06-03 22:16:15 +03:00
2023-06-03 22:19:15 +03:00
unreleased : Maybe ( Int, String )
unreleased =
findLineWithUnreleased 0 lines
in
2023-06-03 22:16:32 +03:00
Rule.errorForExtraFileWithFix
2023-06-03 22:05:25 +03:00
fileKey
{ message = "Missing entry in CHANGELOG.md for version " ++ elmJsonVersion
, details = [ "It seems you have or are ready to release a new version of your package, but forgot to include releases notes for it in your CHANGELOG.md file." ]
}
2023-06-03 22:19:15 +03:00
(case unreleased of
Just ( lineNumber, line ) ->
{ start = { row = lineNumber, column = 1 }, end = { row = lineNumber, column = String.length line + 1 } }
2023-06-03 22:12:59 +03:00
Nothing ->
{ start = { row = 1, column = 1 }, end = { row = 1, column = String.length (List.head lines |> Maybe.withDefault "") + 1 } }
2023-06-03 22:12:59 +03:00
)
2023-06-03 22:19:15 +03:00
(case unreleased of
Just ( lineNumber, _ ) ->
2023-06-03 22:25:11 +03:00
[ Fix.insertAt { row = lineNumber + 1, column = 1 } ("\n## [" ++ elmJsonVersion ++ "]\n\n") ]
2023-06-03 22:17:30 +03:00
Nothing ->
[]
)
2023-06-03 22:12:11 +03:00
findLineWithUnreleased : Int -> List String -> Maybe ( Int, String )
findLineWithUnreleased index lines =
case lines of
[] ->
Nothing
line :: rest ->
if String.contains "# [Unreleased]" line then
Just ( index + 1, line )
else
findLineWithUnreleased (index + 1) rest