From c94ddfcf7daf13946063c5f28a6162e527c01ecf Mon Sep 17 00:00:00 2001 From: Sergey Gulin Date: Thu, 15 Sep 2022 01:23:51 +1000 Subject: [PATCH] [#135] CI: add stylish-haskell and shellcheck Problem: We should add stylish-haskell and shellcheck to our pipline. Solution: Add stylish-haskell and shellcheck. Use stylish-haskell on repo. --- .buildkite/pipeline.yml | 6 ++++ .stylish-haskell.yaml | 1 - Makefile | 5 +++- ci.nix | 3 ++ ftp-tests/Test/Xrefcheck/FtpLinks.hs | 3 +- nix/sources.json | 12 ++++++++ scripts/validate-stylish.sh | 30 +++++++++++++++++++ src/Xrefcheck/CLI.hs | 8 ++--- src/Xrefcheck/Command.hs | 3 +- src/Xrefcheck/Config.hs | 10 +++---- src/Xrefcheck/Core.hs | 4 +-- src/Xrefcheck/Orphans.hs | 4 +-- src/Xrefcheck/Scan.hs | 12 ++++---- src/Xrefcheck/System.hs | 2 +- tests/Test/Xrefcheck/AnchorsInHeadersSpec.hs | 2 +- tests/Test/Xrefcheck/AnchorsSpec.hs | 4 +-- tests/Test/Xrefcheck/ConfigSpec.hs | 4 +-- tests/Test/Xrefcheck/IgnoreAnnotationsSpec.hs | 2 +- tests/Test/Xrefcheck/IgnoreRegexSpec.hs | 4 +-- tests/Test/Xrefcheck/TooManyRequestsSpec.hs | 12 ++++---- tests/Test/Xrefcheck/TrailingSlashSpec.hs | 2 +- 21 files changed, 94 insertions(+), 39 deletions(-) create mode 100755 scripts/validate-stylish.sh diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 219fc3a..cca24ac 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -6,6 +6,12 @@ steps: - command: nix-build ci.nix -A trailing-whitespace-check label: Check trailing whitespaces + - label: shellcheck + command: nix run -f ci.nix pkgs.shellcheck -c find . -name '*.sh' -exec shellcheck {} + + + - label: stylish + command: nix run -f ci.nix pkgs.gnumake pkgs-stylish.stylish-haskell -c ./scripts/validate-stylish.sh + - command: nix-build ci.nix -A xrefcheck-lib-and-tests label: Library and tests diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml index f32324b..680a979 100644 --- a/.stylish-haskell.yaml +++ b/.stylish-haskell.yaml @@ -44,7 +44,6 @@ language_extensions: - MultiParamTypeClasses - MultiWayIf - NamedFieldPuns - - NoImplicitPrelude - NumericUnderscores - OverloadedLabels - OverloadedStrings diff --git a/Makefile b/Makefile index ab3e5b4..6628bb0 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MPL-2.0 -.PHONY: xrefcheck test lint clean bats all +.PHONY: xrefcheck test lint stylish clean bats all # Build target from the common utility Makefile MAKEU = $(MAKE) -C make/ @@ -33,6 +33,9 @@ clean: lint: hlint . +stylish: + find . -name '.stack-work' -prune -o -name '.dist-newstyle' -prune -o -name '*.hs' -exec stylish-haskell -i '{}' \; + #################################### # Individual test suites diff --git a/ci.nix b/ci.nix index b6245f2..2687cdf 100644 --- a/ci.nix +++ b/ci.nix @@ -24,6 +24,9 @@ rec { }; }; + # TODO: drop this when `serokell/nixpkgs` acquires stylish-haskell >= 0.13.0.0. + pkgs-stylish = import sources.nixpkgs-stylish {}; + xrefcheck-lib-and-tests = (import ./xrefcheck.nix { linux = true; }); xrefcheck-static = (import ./xrefcheck.nix { linux-static = true; }).components.exes.xrefcheck; xrefcheck-windows = (import ./xrefcheck.nix { windows = true; }).components.exes.xrefcheck; diff --git a/ftp-tests/Test/Xrefcheck/FtpLinks.hs b/ftp-tests/Test/Xrefcheck/FtpLinks.hs index 56fea15..6246886 100644 --- a/ftp-tests/Test/Xrefcheck/FtpLinks.hs +++ b/ftp-tests/Test/Xrefcheck/FtpLinks.hs @@ -15,7 +15,8 @@ import Test.Tasty (TestTree, askOption, testGroup) import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=)) import Test.Tasty.Options as Tasty (IsOption (..), OptionDescription (Option), safeRead) -import Xrefcheck.Config (Config' (cVerification), VerifyConfig, VerifyConfig' (vcIgnoreRefs), defConfig) +import Xrefcheck.Config + (Config' (cVerification), VerifyConfig, VerifyConfig' (vcIgnoreRefs), defConfig) import Xrefcheck.Core (Flavor (GitHub)) import Xrefcheck.Verify (VerifyError (..), VerifyResult (VerifyResult), checkExternalResource, verifyErrors) diff --git a/nix/sources.json b/nix/sources.json index 690a530..f0d1368 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -35,6 +35,18 @@ "url": "https://github.com/serokell/nixpkgs/archive/1714a2ead1a18678afa3cbf75dff3f024c579061.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "nixpkgs-stylish": { + "branch": "master", + "description": "Nix Packages collection", + "homepage": "", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "19574af0af3ffaf7c9e359744ed32556f34536bd", + "sha256": "0v3c4r8v40jimicdxqvxnzmdypnafm2baam7z131zk6ljhb8jpg9", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/19574af0af3ffaf7c9e359744ed32556f34536bd.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "serokell.nix": { "branch": "master", "description": "Serokell Nix infrastructure library", diff --git a/scripts/validate-stylish.sh b/scripts/validate-stylish.sh new file mode 100755 index 0000000..b2e85f5 --- /dev/null +++ b/scripts/validate-stylish.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2022 Serokell +# +# SPDX-License-Identifier: MPL-2.0 + +# This script verifies that the repo adheres to the stylish-haskell rules. +# +# It does this by running `make stylish` on the repo and checking +# that no files were affected. + +set -euo pipefail + +make stylish + +# Note: we temporarily disable `-e`; +# otherwise the script would exit when `git diff` returns 1. +set +e +diff=$(git diff --exit-code --name-only) +exitCode=$? +set -e + +if [ "$exitCode" != 0 ]; then + echo "Found files that do not adhere to stylish-haskell." + echo "Run 'make stylish' on the repository to fix this." + echo "" + echo "Offending files:" + echo "$diff" + exit 1 +fi diff --git a/src/Xrefcheck/CLI.hs b/src/Xrefcheck/CLI.hs index 5be9602..2f0f3b2 100644 --- a/src/Xrefcheck/CLI.hs +++ b/src/Xrefcheck/CLI.hs @@ -26,9 +26,9 @@ import Data.List qualified as L import Data.Text qualified as T import Data.Version (showVersion) import Options.Applicative - (Mod, OptionFields, Parser, ReadM, auto, command, eitherReader, execParser, flag', - footerDoc, fullDesc, help, helpDoc, helper, hsubparser, info, infoOption, long, metavar, option, - progDesc, short, strOption, switch, value) + (Mod, OptionFields, Parser, ReadM, auto, command, eitherReader, execParser, flag', footerDoc, + fullDesc, help, helpDoc, helper, hsubparser, info, infoOption, long, metavar, option, progDesc, + short, strOption, switch, value) import Options.Applicative.Help.Pretty (Doc, displayS, fill, fillSep, indent, renderPretty, text) import Options.Applicative.Help.Pretty qualified as Pretty @@ -36,8 +36,8 @@ import Paths_xrefcheck (version) import Xrefcheck.Config (VerifyConfig, VerifyConfig' (..)) import Xrefcheck.Core import Xrefcheck.Scan -import Xrefcheck.Util (normaliseWithNoTrailing) import Xrefcheck.System (RelGlobPattern (..)) +import Xrefcheck.Util (normaliseWithNoTrailing) modeReadM :: ReadM VerifyMode modeReadM = eitherReader $ \s -> diff --git a/src/Xrefcheck/Command.hs b/src/Xrefcheck/Command.hs index f97afb2..f92c9d1 100644 --- a/src/Xrefcheck/Command.hs +++ b/src/Xrefcheck/Command.hs @@ -19,7 +19,8 @@ import Xrefcheck.Config overrideConfig) import Xrefcheck.Core (Flavor (..)) import Xrefcheck.Progress (allowRewrite) -import Xrefcheck.Scan (FormatsSupport, scanRepo, specificFormatsSupport, ScanResult (..), ScanError (..)) +import Xrefcheck.Scan + (FormatsSupport, ScanError (..), ScanResult (..), scanRepo, specificFormatsSupport) import Xrefcheck.Scanners.Markdown (markdownSupport) import Xrefcheck.System (askWithinCI) import Xrefcheck.Verify (verifyErrors, verifyRepo) diff --git a/src/Xrefcheck/Config.hs b/src/Xrefcheck/Config.hs index 38b92e4..cad267c 100644 --- a/src/Xrefcheck/Config.hs +++ b/src/Xrefcheck/Config.hs @@ -7,7 +7,7 @@ module Xrefcheck.Config where -import qualified Universum.Unsafe as Unsafe +import Universum.Unsafe qualified as Unsafe import Universum @@ -20,17 +20,17 @@ import Data.Yaml (FromJSON (..), decodeEither', prettyPrintParseException, withT import Instances.TH.Lift () import Text.Regex.TDFA qualified as R import Text.Regex.TDFA.ByteString () +import Text.Regex.TDFA.Common import Text.Regex.TDFA.Text qualified as R -import Time (KnownRatName, Second, Time(..), unitsP) +import Time (KnownRatName, Second, Time (..), unitsP) +import Xrefcheck.Config.Default import Xrefcheck.Core import Xrefcheck.Scan import Xrefcheck.Scanners.Markdown import Xrefcheck.System (RelGlobPattern, normaliseGlobPattern) -import Xrefcheck.Util (aesonConfigOption, postfixFields, (-:), Field) -import Xrefcheck.Config.Default -import Text.Regex.TDFA.Common +import Xrefcheck.Util (Field, aesonConfigOption, postfixFields, (-:)) -- | Type alias for Config' with all required fields. type Config = Config' Identity diff --git a/src/Xrefcheck/Core.hs b/src/Xrefcheck/Core.hs index 76493dd..0526c94 100644 --- a/src/Xrefcheck/Core.hs +++ b/src/Xrefcheck/Core.hs @@ -25,10 +25,10 @@ import System.FilePath (isPathSeparator, pathSeparator) import Text.Numeral.Roman (toRoman) import Time (Second, Time) -import Xrefcheck.Progress -import Xrefcheck.Util import Data.DList (DList) import Data.DList qualified as DList +import Xrefcheck.Progress +import Xrefcheck.Util ----------------------------------------------------------- -- Types diff --git a/src/Xrefcheck/Orphans.hs b/src/Xrefcheck/Orphans.hs index f7f4286..b9ccfcf 100644 --- a/src/Xrefcheck/Orphans.hs +++ b/src/Xrefcheck/Orphans.hs @@ -11,13 +11,13 @@ module Xrefcheck.Orphans () where import Universum -import qualified Data.ByteString.Char8 as C +import Data.ByteString.Char8 qualified as C import Fmt (Buildable (..), unlinesF, (+|), (|+)) import Network.FTP.Client (FTPException (..), FTPMessage (..), FTPResponse (..), ResponseStatus (..)) import Text.URI (RText, unRText) -import URI.ByteString (URIParseError (..), SchemaError (..)) +import URI.ByteString (SchemaError (..), URIParseError (..)) instance ToString (RText t) where toString = toString . unRText diff --git a/src/Xrefcheck/Scan.hs b/src/Xrefcheck/Scan.hs index f8d7c83..a43cbee 100644 --- a/src/Xrefcheck/Scan.hs +++ b/src/Xrefcheck/Scan.hs @@ -22,19 +22,19 @@ module Xrefcheck.Scan import Universum -import Data.Aeson(FromJSON (..), genericParseJSON) +import Data.Aeson (FromJSON (..), genericParseJSON) import Data.Foldable qualified as F import Data.Map qualified as M -import Fmt (Buildable (..), (+|), (|+), nameF) -import System.Console.Pretty (Pretty(..), Style (..)) +import Fmt (Buildable (..), nameF, (+|), (|+)) +import System.Console.Pretty (Pretty (..), Style (..)) import System.Directory (doesDirectoryExist) import System.Directory.Tree qualified as Tree -import System.FilePath (dropTrailingPathSeparator, takeDirectory, takeExtension, equalFilePath) +import System.FilePath (dropTrailingPathSeparator, equalFilePath, takeDirectory, takeExtension) import Xrefcheck.Core import Xrefcheck.Progress -import Xrefcheck.System (readingSystem, RelGlobPattern, normaliseGlobPattern, matchesGlobPatterns) -import Xrefcheck.Util (aesonConfigOption, normaliseWithNoTrailing, Field) +import Xrefcheck.System (RelGlobPattern, matchesGlobPatterns, normaliseGlobPattern, readingSystem) +import Xrefcheck.Util (Field, aesonConfigOption, normaliseWithNoTrailing) -- | Type alias for TraversalConfig' with all required fields. type TraversalConfig = TraversalConfig' Identity diff --git a/src/Xrefcheck/System.hs b/src/Xrefcheck/System.hs index 2376883..f09742b 100644 --- a/src/Xrefcheck/System.hs +++ b/src/Xrefcheck/System.hs @@ -16,12 +16,12 @@ import Universum import Data.Aeson (FromJSON (..), withText) import Data.Char qualified as C +import Data.Coerce (coerce) import GHC.IO.Unsafe (unsafePerformIO) import System.Directory (canonicalizePath) import System.Environment (lookupEnv) import System.FilePath (()) import System.FilePath.Glob qualified as Glob -import Data.Coerce (coerce) import Xrefcheck.Util (normaliseWithNoTrailing) -- | We can quite safely treat surrounding filesystem as frozen, diff --git a/tests/Test/Xrefcheck/AnchorsInHeadersSpec.hs b/tests/Test/Xrefcheck/AnchorsInHeadersSpec.hs index 5fd2c7f..3b54ace 100644 --- a/tests/Test/Xrefcheck/AnchorsInHeadersSpec.hs +++ b/tests/Test/Xrefcheck/AnchorsInHeadersSpec.hs @@ -10,8 +10,8 @@ import Universum import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase, (@?=)) -import Xrefcheck.Core import Test.Xrefcheck.Util +import Xrefcheck.Core test_anchorsInHeaders :: TestTree test_anchorsInHeaders = testGroup "Anchors in headers" diff --git a/tests/Test/Xrefcheck/AnchorsSpec.hs b/tests/Test/Xrefcheck/AnchorsSpec.hs index 5bacad4..3c4f339 100644 --- a/tests/Test/Xrefcheck/AnchorsSpec.hs +++ b/tests/Test/Xrefcheck/AnchorsSpec.hs @@ -7,8 +7,8 @@ module Test.Xrefcheck.AnchorsSpec (test_anchors) where import Universum -import Test.Tasty (testGroup, TestTree) -import Test.Tasty.HUnit ((@?=), testCase) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase, (@?=)) import Test.Xrefcheck.Util import Xrefcheck.Core diff --git a/tests/Test/Xrefcheck/ConfigSpec.hs b/tests/Test/Xrefcheck/ConfigSpec.hs index 9dc8101..5cf76ae 100644 --- a/tests/Test/Xrefcheck/ConfigSpec.hs +++ b/tests/Test/Xrefcheck/ConfigSpec.hs @@ -12,11 +12,11 @@ import Control.Exception qualified as E import Data.ByteString qualified as BS import Data.List (isInfixOf) -import Data.Yaml (decodeEither', ParseException (..)) +import Data.Yaml (ParseException (..), decodeEither') import Network.HTTP.Types (Status (..)) import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertFailure, testCase, (@?=)) import Test.Tasty.QuickCheck (ioProperty, testProperty) -import Test.Tasty.HUnit (testCase, assertFailure, (@?=)) import Xrefcheck.Config (Config, Config' (..), VerifyConfig' (..), defConfig, defConfigText) diff --git a/tests/Test/Xrefcheck/IgnoreAnnotationsSpec.hs b/tests/Test/Xrefcheck/IgnoreAnnotationsSpec.hs index 2074c65..79f4184 100644 --- a/tests/Test/Xrefcheck/IgnoreAnnotationsSpec.hs +++ b/tests/Test/Xrefcheck/IgnoreAnnotationsSpec.hs @@ -7,7 +7,7 @@ module Test.Xrefcheck.IgnoreAnnotationsSpec where import Universum -import CMarkGFM (PosInfo(..)) +import CMarkGFM (PosInfo (..)) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase, (@?=)) diff --git a/tests/Test/Xrefcheck/IgnoreRegexSpec.hs b/tests/Test/Xrefcheck/IgnoreRegexSpec.hs index 06ae7ed..762e0b9 100644 --- a/tests/Test/Xrefcheck/IgnoreRegexSpec.hs +++ b/tests/Test/Xrefcheck/IgnoreRegexSpec.hs @@ -9,13 +9,13 @@ import Universum import Data.Yaml (decodeEither') import Test.Tasty (TestTree, testGroup) -import Test.Tasty.HUnit (testCase, assertFailure) +import Test.Tasty.HUnit (assertFailure, testCase) import Text.Regex.TDFA (Regex) import Xrefcheck.Config import Xrefcheck.Core import Xrefcheck.Progress (allowRewrite) -import Xrefcheck.Scan (scanRepo, specificFormatsSupport, ScanResult (..)) +import Xrefcheck.Scan (ScanResult (..), scanRepo, specificFormatsSupport) import Xrefcheck.Scanners.Markdown import Xrefcheck.Verify (VerifyError, VerifyResult, WithReferenceLoc (..), verifyErrors, verifyRepo) diff --git a/tests/Test/Xrefcheck/TooManyRequestsSpec.hs b/tests/Test/Xrefcheck/TooManyRequestsSpec.hs index 739cd04..ea68dac 100644 --- a/tests/Test/Xrefcheck/TooManyRequestsSpec.hs +++ b/tests/Test/Xrefcheck/TooManyRequestsSpec.hs @@ -11,15 +11,15 @@ import Control.Concurrent (forkIO, killThread) import Control.Exception qualified as E import Data.CaseInsensitive qualified as CI import Data.Map qualified as M -import Data.Time (addUTCTime, formatTime, getCurrentTime, defaultTimeLocale, rfc822DateFormat) +import Data.Time (addUTCTime, defaultTimeLocale, formatTime, getCurrentTime, rfc822DateFormat) import Data.Time.Clock.POSIX (getPOSIXTime) import Fmt (indentF, pretty, unlinesF) import Network.HTTP.Types (Status (..), ok200, serviceUnavailable503, tooManyRequests429) import Network.HTTP.Types.Header (hRetryAfter) -import Test.Tasty (testGroup, TestTree) -import Test.Tasty.HUnit (testCase, (@?=), assertBool) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertBool, testCase, (@?=)) import Time (sec, (-:-)) -import Web.Firefly (ToResponse (toResponse), route, run, getMethod) +import Web.Firefly (ToResponse (toResponse), getMethod, route, run) import Xrefcheck.Config import Xrefcheck.Core @@ -133,9 +133,9 @@ test_tooManyRequests = testGroup "429 response tests" callCount <- atomicModifyIORef' callCountRef $ \cc -> (cc + 1, cc) atomicModifyIORef' infoReverseAccumulatorRef $ \lst -> ( ( m - , if | m == "GET" -> ok200 + , if | m == "GET" -> ok200 | callCount == 0 -> tooManyRequests429 - | otherwise -> serviceUnavailable503 + | otherwise -> serviceUnavailable503 ) : lst , () ) diff --git a/tests/Test/Xrefcheck/TrailingSlashSpec.hs b/tests/Test/Xrefcheck/TrailingSlashSpec.hs index 3852a95..267c9f8 100644 --- a/tests/Test/Xrefcheck/TrailingSlashSpec.hs +++ b/tests/Test/Xrefcheck/TrailingSlashSpec.hs @@ -10,7 +10,7 @@ import Universum import Fmt (blockListF, pretty, unlinesF) import System.Directory (doesFileExist) import Test.Tasty (TestTree, testGroup) -import Test.Tasty.HUnit (testCase, assertFailure) +import Test.Tasty.HUnit (assertFailure, testCase) import Xrefcheck.Config import Xrefcheck.Core