[Git][ghc/ghc][wip/27183] lint-changelog: also reject MRs which manually modify changelog files that changelog-d owns
Zubin pushed to branch wip/27183 at Glasgow Haskell Compiler / GHC Commits: 1d33272d by Zubin Duggal at 2026-04-28T17:28:33+05:30 lint-changelog: also reject MRs which manually modify changelog files that changelog-d owns - - - - - 3 changed files: - .gitlab-ci.yml - hadrian/src/Rules/Changelog.hs - utils/changelog-d/ChangelogD.hs Changes: ===================================== .gitlab-ci.yml ===================================== @@ -274,6 +274,24 @@ lint-changelog: # Build changelog-d and validate all entries (checks required fields, section names, MR number) - .gitlab/ci.sh configure - .gitlab/ci.sh run_hadrian test --test-root-dirs="testsuite/tests/linters" --only=changelog-d + # Reject MRs that directly modify any path listed in + # changelog.d/config's markdown-targets: those files are regenerated + # from fragments at release time. + - export TOOL_OUTPUT=_build/changelog-d-markdown-targets.txt + - .gitlab/ci.sh run_hadrian list-markdown-targets + - | + paths=$(cat "$TOOL_OUTPUT") + touched=$(git diff --name-only "$base..$CI_COMMIT_SHA" -- $paths) + if [ -n "$touched" ]; then + echo "ERROR: This MR modifies a per-library changelog file directly:" + echo "$touched" | sed 's/^/ /' + echo + echo "These files are generated at release time from changelog.d/" + echo "fragments. Please add a fragment in changelog.d/ with the" + echo "appropriate 'section:' instead of editing the file directly." + echo "If this is a deliberate exception, apply the 'no-changelog' label." + exit 1 + fi dependencies: [] rules: - if: '$CI_MERGE_REQUEST_LABELS =~ /.*marge_bot_batch_merge_job.*/' ===================================== hadrian/src/Rules/Changelog.hs ===================================== @@ -7,6 +7,7 @@ import Packages import Settings.Program (programContext) import qualified System.Directory as IO +import qualified System.Environment as IO -- | Rules for generating and managing changelog entries. -- @@ -14,6 +15,8 @@ import qualified System.Directory as IO -- hadrian/build changelog -- generate RST release notes -- hadrian/build changelog --changelog-version=10.2.1 -- with explicit version -- hadrian/build libraries-changelog-markdown -- emit per-library Markdown bullets to stdout +-- hadrian/build list-markdown-targets -- print one repo-relative path per +-- markdown-targets: row, used by CI -- hadrian/build changelog-clear -- remove old entries changelogRules :: Rules () changelogRules = do @@ -47,6 +50,17 @@ changelogRules = do , "--libraries-changelog-markdown" ] + phony "list-markdown-targets" $ do + ctx <- programContext stage0Boot changelogD + progPath <- programPath ctx + need [progPath] + top <- topDirectory + let args = [top -/- "changelog.d/", "--list-markdown-targets"] + mOut <- liftIO $ IO.lookupEnv "TOOL_OUTPUT" + case mOut of + Nothing -> cmd_ [progPath] args + Just fp -> quietly $ (cmd (FileStdout fp) [progPath] args :: Action ()) + phony "changelog-clear" $ do top <- topDirectory let dir = top -/- "changelog.d" ===================================== utils/changelog-d/ChangelogD.hs ===================================== @@ -20,7 +20,7 @@ import Data.Set (Set) import Data.Traversable (for) import System.Directory (doesDirectoryExist, doesFileExist, listDirectory) import System.Environment (getArgs) -import System.Exit (exitFailure) +import System.Exit (ExitCode(..), exitFailure, exitWith) import System.FilePath ((</>), dropTrailingPathSeparator, takeDirectory) import System.IO (hPutStrLn, stderr) @@ -72,6 +72,10 @@ usage = unlines , " to a single section. Without this, all" , " configured markdown-targets are emitted," , " separated by HTML-comment markers." + , " --list-markdown-targets Print one repo-relative path per line for" + , " every entry in `markdown-targets:`. Used" + , " by CI to source the list of files MRs are" + , " not allowed to edit directly." , " --help Show this help" ] @@ -79,13 +83,14 @@ parseArgs :: [String] -> Either String Opts parseArgs = go defaultOpts where defaultOpts = Opts - { optDirectory = "changelog.d" - , optVersion = Nothing - , optValidate = False - , optExpectMR = Nothing - , optExpectCLC = False - , optMarkdown = False - , optMdSection = Nothing + { optDirectory = "changelog.d" + , optVersion = Nothing + , optValidate = False + , optExpectMR = Nothing + , optExpectCLC = False + , optMarkdown = False + , optMdSection = Nothing + , optListTargets = False } go opts [] = Right opts @@ -102,6 +107,8 @@ parseArgs = go defaultOpts go opts { optMarkdown = True } rest go opts ("--section" : s : rest) = go opts { optMdSection = Just s } rest go _ ("--section" : []) = Left "--section requires an argument" + go opts ("--list-markdown-targets" : rest) = + go opts { optListTargets = True } rest go _ (('-':'-':opt) : _) = Left $ "Unknown option: --" ++ opt go _ (('-':opt) : _) = Left $ "Unknown option: -" ++ opt go opts (dir : rest) = go opts { optDirectory = dir } rest @@ -148,6 +155,10 @@ makeChangelog Opts {..} = do either (exitWithExc . PlainError) return $ parseWith parseConfig filename contents + when optListTargets $ do + for_ (cfgMarkdownTargets cfg) (putStrLn . mtPath) + exitWith ExitSuccess + -- Read only regular files, skipping config, dotfiles, and any -- subdirectories (e.g. golden-output dirs alongside test fragments). dirContents <- filter (not . isTmpFile) <$> listDirectory optDirectory @@ -619,6 +630,7 @@ data Opts = Opts , optExpectCLC :: Bool -- ^ Require entry matched by --expect-mr to have clc: , optMarkdown :: Bool -- ^ Emit per-library Markdown to stdout , optMdSection :: Maybe String -- ^ Restrict markdown emission to one section + , optListTargets :: Bool -- ^ List markdown-targets paths to stdout } deriving (Show) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1d33272d67fcc4c8cdeec7bd976d3157... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1d33272d67fcc4c8cdeec7bd976d3157... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Zubin (@wz1000)