├── .gitattributes ├── .github └── workflows │ └── haskell-ci.yml ├── .gitignore ├── .hlint.yaml ├── .stylish-haskell.yaml ├── CHANGELOG.md ├── CONTRIBUTORS ├── LICENSE ├── Makefile ├── README.md ├── Setup.hs ├── cabal-install-parsers ├── Changelog.md ├── LICENSE ├── LICENSE.GPLv2 ├── LICENSE.GPLv3 ├── bench │ └── Bench.hs ├── cabal-install-parsers.cabal ├── fixtures │ ├── haskell-ci.golden │ └── haskell-ci.project ├── src │ └── Cabal │ │ ├── Config.hs │ │ ├── Index.hs │ │ ├── Internal │ │ ├── Glob.hs │ │ └── Newtypes.hs │ │ ├── Optimization.hs │ │ ├── Package.hs │ │ ├── Parse.hs │ │ ├── Project.hs │ │ └── SourceRepo.hs └── test │ ├── Golden.hs │ └── Index.hs ├── cabal.haskell-ci ├── cabal.project ├── cli └── Main.hs ├── collections ├── cabal.project.debian-8 ├── cabal.project.lts-0.0 ├── cabal.project.lts-0.1 ├── cabal.project.lts-0.2 ├── cabal.project.lts-0.3 ├── cabal.project.lts-0.4 ├── cabal.project.lts-0.5 ├── cabal.project.lts-0.6 ├── cabal.project.lts-0.7 ├── cabal.project.lts-1.0 ├── cabal.project.lts-1.1 ├── cabal.project.lts-1.10 ├── cabal.project.lts-1.11 ├── cabal.project.lts-1.12 ├── cabal.project.lts-1.13 ├── cabal.project.lts-1.14 ├── cabal.project.lts-1.15 ├── cabal.project.lts-1.2 ├── cabal.project.lts-1.4 ├── cabal.project.lts-1.5 ├── cabal.project.lts-1.7 ├── cabal.project.lts-1.8 ├── cabal.project.lts-1.9 ├── cabal.project.lts-2.0 ├── cabal.project.lts-2.1 ├── cabal.project.lts-2.10 ├── cabal.project.lts-2.11 ├── cabal.project.lts-2.12 ├── cabal.project.lts-2.13 ├── cabal.project.lts-2.14 ├── cabal.project.lts-2.15 ├── cabal.project.lts-2.16 ├── cabal.project.lts-2.17 ├── cabal.project.lts-2.18 ├── cabal.project.lts-2.19 ├── cabal.project.lts-2.2 ├── cabal.project.lts-2.20 ├── cabal.project.lts-2.21 ├── cabal.project.lts-2.22 ├── cabal.project.lts-2.3 ├── cabal.project.lts-2.4 ├── cabal.project.lts-2.5 ├── cabal.project.lts-2.6 ├── cabal.project.lts-2.7 ├── cabal.project.lts-2.8 ├── cabal.project.lts-2.9 ├── cabal.project.lts-3.0 ├── cabal.project.lts-3.1 ├── cabal.project.lts-3.10 ├── cabal.project.lts-3.11 ├── cabal.project.lts-3.12 ├── cabal.project.lts-3.13 ├── cabal.project.lts-3.14 ├── cabal.project.lts-3.15 ├── cabal.project.lts-3.16 ├── cabal.project.lts-3.17 ├── cabal.project.lts-3.18 ├── cabal.project.lts-3.19 ├── cabal.project.lts-3.2 ├── cabal.project.lts-3.20 ├── cabal.project.lts-3.21 ├── cabal.project.lts-3.22 ├── cabal.project.lts-3.3 ├── cabal.project.lts-3.4 ├── cabal.project.lts-3.5 ├── cabal.project.lts-3.6 ├── cabal.project.lts-3.7 ├── cabal.project.lts-3.8 ├── cabal.project.lts-3.9 ├── cabal.project.lts-4.0 ├── cabal.project.lts-4.1 ├── cabal.project.lts-4.2 ├── cabal.project.lts-5.0 ├── cabal.project.lts-5.1 ├── cabal.project.lts-5.10 ├── cabal.project.lts-5.11 ├── cabal.project.lts-5.12 ├── cabal.project.lts-5.13 ├── cabal.project.lts-5.14 ├── cabal.project.lts-5.15 ├── cabal.project.lts-5.16 ├── cabal.project.lts-5.17 ├── cabal.project.lts-5.18 ├── cabal.project.lts-5.2 ├── cabal.project.lts-5.3 ├── cabal.project.lts-5.4 ├── cabal.project.lts-5.5 ├── cabal.project.lts-5.6 ├── cabal.project.lts-5.7 ├── cabal.project.lts-5.8 ├── cabal.project.lts-5.9 ├── cabal.project.lts-6.0 ├── cabal.project.lts-6.1 └── cabal.project.lts-6.2 ├── docker ├── .gitignore ├── Dockerfile.template ├── Makefile ├── README.md ├── cabal.config ├── generic │ ├── Dockerfile │ └── cabal.config ├── ghc-7.0.4 │ └── Dockerfile ├── ghc-7.10.3 │ └── Dockerfile ├── ghc-7.4.2 │ └── Dockerfile ├── ghc-7.6.3 │ └── Dockerfile ├── ghc-7.8.4 │ └── Dockerfile ├── ghc-8.0.2 │ └── Dockerfile ├── ghc-8.2.2 │ └── Dockerfile ├── ghc-8.4.4 │ ├── Dockerfile │ └── cabal.config ├── ghc-8.6.5 │ └── Dockerfile └── ghc-8.8.1 │ └── Dockerfile ├── docs ├── .gitignore ├── Makefile ├── conf.py └── index.rst ├── extras └── colorful.awk ├── fixtures ├── all-versions.args ├── all-versions.bash ├── all-versions.github ├── all-versions.project ├── conditionals.args ├── conditionals.bash ├── conditionals.github ├── conditionals.project ├── copy-fields-all.args ├── copy-fields-all.bash ├── copy-fields-all.github ├── copy-fields-all.project ├── copy-fields-none.args ├── copy-fields-none.bash ├── copy-fields-none.github ├── copy-fields-none.project ├── copy-fields-some.args ├── copy-fields-some.bash ├── copy-fields-some.github ├── copy-fields-some.project ├── doctest-version.args ├── doctest-version.bash ├── doctest-version.github ├── doctest-version.project ├── doctest.args ├── doctest.bash ├── doctest.github ├── doctest.project ├── empty-line.args ├── empty-line.bash ├── empty-line.github ├── empty-line.project ├── enabled-jobs.args ├── enabled-jobs.bash ├── enabled-jobs.github ├── enabled-jobs.project ├── fail-versions.args ├── fail-versions.bash ├── fail-versions.github ├── fail-versions.project ├── irc-channels.args ├── irc-channels.bash ├── irc-channels.github ├── irc-channels.project ├── messy.args ├── messy.bash ├── messy.github ├── messy.project ├── psql.args ├── psql.bash ├── psql.github ├── psql.project ├── servant-client-core │ └── servant-client-core.cabal ├── servant-client │ └── servant-client.cabal ├── servant-docs │ └── servant-docs.cabal ├── servant-foreign │ └── servant-foreign.cabal ├── servant-server │ └── servant-server.cabal ├── servant │ └── servant.cabal ├── splitmix │ └── splitmix.cabal ├── travis-patch.args ├── travis-patch.bash ├── travis-patch.github ├── travis-patch.patch └── travis-patch.project ├── haskell-ci.cabal ├── haskell-ci.sh ├── haskell-ci.sh.zinza ├── src ├── HaskellCI.hs ├── HaskellCI │ ├── Auxiliary.hs │ ├── Bash.hs │ ├── Bash │ │ └── Template.hs │ ├── Cabal.hs │ ├── Cli.hs │ ├── Compiler.hs │ ├── Config.hs │ ├── Config │ │ ├── Components.hs │ │ ├── ConstraintSet.hs │ │ ├── CopyFields.hs │ │ ├── Docspec.hs │ │ ├── Doctest.hs │ │ ├── Dump.hs │ │ ├── Empty.hs │ │ ├── Grammar.hs │ │ ├── History.hs │ │ ├── Initial.hs │ │ ├── Installed.hs │ │ ├── Jobs.hs │ │ ├── PackageScope.hs │ │ ├── Type.hs │ │ ├── Ubuntu.hs │ │ └── Validity.hs │ ├── Diagnostics.hs │ ├── Error.hs │ ├── Ghcup.hs │ ├── GitConfig.hs │ ├── GitHub.hs │ ├── GitHub │ │ └── Yaml.hs │ ├── GrammarDefault.hs │ ├── HeadHackage.hs │ ├── Jobs.hs │ ├── List.hs │ ├── MonadErr.hs │ ├── Newtypes.hs │ ├── OptionsGrammar.hs │ ├── OptparseGrammar.hs │ ├── Package.hs │ ├── ParsecUtils.hs │ ├── Prelude.hs │ ├── SetupMethod.hs │ ├── Sh.hs │ ├── ShVersionRange.hs │ ├── TestedWith.hs │ ├── Tools.hs │ ├── VersionInfo.hs │ └── YamlSyntax.hs └── System │ └── Path │ └── Extras.hs └── test └── Tests.hs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Fix incorrect language classification on GitHub.com 2 | collections/* linguist-vendored linguist-language=Cabal 3 | cabal.haskell-ci linguist-vendored linguist-language=Cabal 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .ghc.environment.* 3 | /dist-newstyle/ 4 | /dist/ 5 | /cabal-dev 6 | *.o 7 | *.hi 8 | *.chi 9 | *.chs.h 10 | *.dyn_o 11 | *.dyn_hi 12 | /.hpc 13 | /.hsenv 14 | /.cabal-sandbox 15 | cabal.sandbox.config 16 | *.prof 17 | *.aux 18 | *.hp 19 | *.eventlog 20 | /.stack-work/ 21 | cabal.project.local 22 | /.HTF 23 | -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | - ignore: {name: Use String} 2 | - ignore: {name: Use fewer imports} 3 | - ignore: {name: Use lambda-case} 4 | - ignore: {name: Use infix} # isPrefixOf 5 | - ignore: {name: Redundant $} 6 | - ignore: {name: Redundant do} 7 | - ignore: {name: Use tuple-section} 8 | - ignore: {name: Use const} 9 | - ignore: {name: Too strict maybe} 10 | - ignore: {name: "Use :"} 11 | - ignore: {name: "Move guards forward"} 12 | - ignore: {name: "Avoid lambda using `infix`"} 13 | - ignore: {name: "Fuse foldr/map"} 14 | - ignore: {name: Unused LANGUAGE pragma} 15 | - ignore: {name: "Use <$>"} 16 | - ignore: {name: Reduce duplication} 17 | - ignore: {within: HaskellCI.Bash.Template} 18 | -------------------------------------------------------------------------------- /.stylish-haskell.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - imports: 3 | align: group 4 | list_align: after_alias 5 | long_list_align: new_line 6 | empty_list_align: right_after 7 | list_padding: module_name 8 | - language_pragmas: 9 | style: vertical 10 | remove_redundant: true 11 | - trailing_whitespace: {} 12 | columns: 160 13 | language_extensions: 14 | - DerivingStrategies 15 | - ExplicitForAll 16 | - FlexibleContexts 17 | - GeneralizedNewtypeDeriving 18 | - MultiParamTypeClasses 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.20 2 | 3 | - Add support for GHCup vanilla and prerelease channels (in a more principled way) 4 | 5 | ## 0.18.1 - 2024-02-25 6 | 7 | - Add GHC-9.8.2 8 | 9 | ## 0.18 - 2024-02-13 10 | 11 | - Update Haskell dependencies (e.g. ShellCheck), support compilation wigh GHC-9.8 12 | - Add new GHC versions upto GHC-9.8.1 etc. 13 | - Update tool dependencies too 14 | 15 | ## 0.16.6 - 2023-07-10 16 | 17 | - Add `cabal-plan topo` to the constraint set steps 18 | 19 | ## 0.16.5 - 2023-06-13 20 | 21 | - Use optparse-applicative-0.18.1. 22 | When using older optparse-applicative with prettyprinter, 23 | `--help` appeared to hang. 24 | - Update other dependencies as well. 25 | 26 | ## 0.16.4 - 2023-06-09 27 | 28 | - Add GHC-9.2.8 29 | 30 | ## 0.16.3 - 2023-05-25 31 | 32 | - Add GHC-9.6.2 33 | 34 | ## 0.16.2 - 2023-05-17 35 | 36 | - Add GHC-9.4.5 37 | - Use cabal-docspec-20230517 38 | 39 | ## 0.16 - 2023-04-21 40 | 41 | - Add GHC-9.4.5 42 | 43 | ## 0.16 - 2023-04-06 44 | 45 | - Add compiler up to GHC-9.6.1 46 | - Update tools (hlint, cabal-docspec, cabal-plan, doctest, cabal-install) 47 | - Use ShellCheck-0.9.0 48 | - Use postgresql 14 49 | - head.hackage overrides hackage 50 | - Update GHA actions 51 | - Save GHA cache in failing jobs also 52 | - Add `-Werror=missing-fields` in addition to existing `-Werror=missing-methods` 53 | 54 | ## 0.14.3 - 2022-03-07 55 | 56 | - Add GHC-9.2.2 57 | - Use ghcup-0.1.17.5 58 | 59 | ## 0.14.2 - 2022-02-19 60 | 61 | - Pass `default-language` to `doctest` 62 | - Support local `.tar.gz` tarballs in `packages:`. 63 | 64 | ## 0.14 - 2022-01-05 65 | 66 | - Add GHC-9.0 releases and GHC-9.2.1 67 | - Add ghcup setup method. It is used by default for GHC versions not in hvr-ppa. 68 | - GHA: Specify GHC memory limit 69 | - GHA: timeout-minutes configuration option 70 | - Update cabal-docspec versions used 71 | 72 | ## 0.12.1 - 2021-03-20 73 | 74 | - Use HLint-3.3 75 | - GHA: Support --distribution and --submodules (thanks to Ryan Scott) 76 | 77 | ## 0.12 - 2021-03-13 78 | 79 | - Use `cabal-install-3.4` 80 | - Add `bash` (Bash + Docker) generator 81 | - Add `github` (GitHub Actions) generator 82 | 83 | ## 0.10.3 - 2020-08-10 84 | 85 | - Add GHC-8.8.4 and GHC-8.10.2 86 | 87 | ## 0.10.2 - 2020-06-05 88 | 89 | - Bump default doctest version to 0.17 90 | - Fix HCPKG variable in macOS builds [#395](https://github.com/haskell-CI/haskell-ci/issues/395) 91 | 92 | ## 0.10.1 - 2020-05-16 93 | 94 | - Add version-info command to help debugging issues with `haskell-ci` 95 | - Bump default HLint version to 3.1 96 | - Support ShellCheck with GHC-8.8 and GHC-8.10 97 | 98 | ## 0.10 - 2020-04-14 99 | 100 | - Allow turning off email notifications 101 | - Use generic-lens-lite instead of generic-lens (smaller dependency footprint) 102 | - Add GHC-8.10, use cabal-install-3.2 103 | - Fix escaping of irc-notification template 104 | - Remove output colouring, it was relying on Cabal's internal features 105 | - Add `-Werror=missing-methods` by default. 106 | 107 | ## 0.8 - 2019-11-26 108 | 109 | - Split parsing utilities into [cabal-install-parsers package](https://hackage.haskell.org/package/cabal-install-parsers) 110 | - `--hlint-download-binary` to download HLint instead of building it ourselves [#323](https://github.com/haskell-ci/haskell-ci/pull/323) 111 | - Fix `haddock` path in OSX builds [#318](https://github.com/haskell-ci/haskell-ci/pull/318) 112 | - Don't `brew upgrade` on OSX, which greatly speedups builds [#320](https://github.com/haskell-ci/haskell-ci/pull/320) 113 | - Use Travis built-in config validation [#338](https://github.com/haskell-CI/haskell-ci/pull/338) 114 | - Use `sourceline: ...` explicit entry for hvr-ppa unconditionally [#338](https://github.com/haskell-CI/haskell-ci/pull/338) 115 | 116 | ## 0.6 - 2019-10-21 117 | 118 | - ghc-options limiting heap size 119 | - GHCJS tests can be run in simple cases 120 | - Work around cabal#6214 (haddock failing with `build-type: Custom` packages) 121 | - Support requesting `google-chrome` addon 122 | - Nicer `docctest-filter-packages` 123 | - Buildable with GHC-8.8 124 | - Record `haskell-ci` version in REGENDATA; 125 | warn if older executable is used to `regenerate` 126 | 127 | ## 0.4 - 2019-08-28 128 | 129 | * Make default `--output` to be `.travis.yml`; use `--stdout` to output to standard output. 130 | * Add GHC-8.8 support 131 | * Use cabal-install-3.0 by default 132 | * Experimental support for GHCJS jobs 133 | * A lot of new configuration options 134 | 135 | ## 0.2.1 - 2019-03-06 136 | 137 | * `local-ghc-options` are always applied (independent of `copy-fields` value) 138 | * Add GHC-8.6.4 to known compilers 139 | 140 | ## 0.2 - 2019-03-03 141 | 142 | * Large configuration rework 143 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Cabal files and directory structure from Servant, copyright of the Servant 2 | contributors, licensed under BSD3. 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HC ?= ghc-9.8.4 2 | 3 | build : 4 | cabal v2-build -w $(HC) 5 | 6 | self-test : 7 | cabal v2-run -w $(HC) haskell-ci -- --config=cabal.haskell-ci haskell-ci.cabal 8 | 9 | ghcid : 10 | ghcid -c 'cabal v2-repl -w $(HC)' 11 | 12 | install: 13 | cabal v2-install -w $(HC) haskell-ci:exe:haskell-ci --overwrite-policy=always 14 | 15 | install-dev : build 16 | cp $$(cabal-plan list-bin haskell-ci) $(HOME)/.cabal/bin/haskell-ci 17 | 18 | test : build 19 | cabal v2-run -w $(HC) golden 20 | 21 | accept : build 22 | cabal v2-run -w $(HC) golden -- --accept 23 | (cd cabal-install-parsers && cabal v2-run -w $(HC) cabal-parsers-golden -- --accept) 24 | 25 | doctest : 26 | perl -i -e 'while () { print unless /package-id\s+(base-compat-batteries|bs-cmpt-bttrs)-\d+(\.\d+)*/; }' .ghc.environment.* 27 | doctest --fast -XBangPatterns -XScopedTypeVariables -XDerivingStrategies -XGeneralizedNewtypeDeriving -XDeriveAnyClass -XNoImplicitPrelude -XDeriveFunctor -XDeriveFoldable -XDeriveTraversable -XDeriveGeneric src 28 | 29 | regenerate : 30 | cabal v2-run -w $(HC) -- haskell-ci regenerate 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | haskell-ci - CI generator for multiple [GHC](http://haskell.org/ghc) versions 2 | ============================================================================= 3 | 4 | At the moment `haskell-ci` support GitHub Actions workflow generation. 5 | There is also legacy Travis-CI configuration generator, which is unmaintained. 6 | 7 | `haskell-ci` relies on [`hvr-ppa`](https://launchpad.net/~hvr/+archive/ubuntu/ghc) 8 | or [`ghcup`](https://www.haskell.org/ghcup/) to install GHC 9 | and `cabal-install`. 10 | 11 | ### Quick-start instructions 12 | 13 | * Step 1: Clone and install this project in/from any directory 14 | 15 | ```bash 16 | $ git clone https://github.com/haskell-CI/haskell-ci.git 17 | $ cd haskell-ci 18 | $ cabal install haskell-ci:exe:haskell-ci 19 | ``` 20 | 21 | or 22 | 23 | ```bash 24 | cabal install haskell-ci 25 | ``` 26 | 27 | * Step 2: Change directories to your project: 28 | 29 | ```bash 30 | $ cd path/to/your-project 31 | ``` 32 | 33 | * Step 3: Edit your project's `*.cabal` file to add a `tested-with` line, such as this one: 34 | 35 | ```bash 36 | $ cat your-project.cabal 37 | ... 38 | tested-with: GHC ==9.6.3 || ==9.4.8 || ==9.2.8 39 | ... 40 | ``` 41 | 42 | Add as many or as few GHC versions to test as you want. 43 | 44 | * Step 4: Generate a workflow file for your project: 45 | 46 | ```bash 47 | $ # You run the following command from your project's directory, even 48 | $ # though it references the script from the `haskell-ci` project 49 | $ haskell-ci github your-project.cabal 50 | ``` 51 | 52 | Note: If you have multiple local Cabal projects that you wish to build together 53 | using a `cabal.project` file, pass that file to haskell-ci instead: 54 | ```bash 55 | $ haskell-ci github cabal.project 56 | ``` 57 | 58 | The `haskell-ci` tool looks at the `Tested-With` line in your 59 | `*.cabal` files and generates a configuration that tests each compiler 60 | version you listed in parallel. 61 | 62 | * Step 5: Create a pull request with your new CI configuration. 63 | 64 | ... or push directly to your main branch if you feel lucky. 65 | 66 | Sometimes you may need to regenerate CI script, for example, when 67 | adding new compiler version to `tested-with`. 68 | You may simply run `haskell-ci regenerate`. 69 | 70 | Real-world Examples 71 | ------------------- 72 | 73 | - [aeson](https://github.com/haskell/aeson) 74 | - [lens](https://github.com/ekmett/lens) 75 | - [unordered-containers](https://github.com/haskell-unordered-containers/unordered-containers) 76 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /cabal-install-parsers/Changelog.md: -------------------------------------------------------------------------------- 1 | ## 0.6.4 2 | 3 | - Add support for reading project files with conditionals. 4 | 5 | ## 0.6.3 6 | 7 | - Drop support for GHC prior 8.8.4 8 | - Use `Cabal-syntax-3.14` 9 | 10 | ## 0.6.2 11 | 12 | - Drop support for GHC prior 8.6.5 13 | - Use `Cabal-syntax-3.12` 14 | 15 | ## 0.6.1 16 | 17 | - Use `Cabal-syntax-3.10` 18 | 19 | ## 0.6 20 | 21 | - Add sizes of tarball and cabal files to `Cabal.Index.ReleaseInfo` data structure. 22 | 23 | ## 0.5 24 | 25 | - Move to use `Cabal-syntax` package 26 | It's possible to have a build-plan with (old) `Cabal`, 27 | and new `cabal-install` syntax, which may cause 28 | `...Version` is not `...Version` like errors. 29 | 30 | ## 0.4.5 31 | 32 | - Resolve `.tar.gz` package files to `file:` URI-locations 33 | 34 | ## 0.4.4 35 | 36 | - Try to fix glob behavior on Windows 37 | - Add `Show (Project ...)` instance 38 | 39 | ## 0.4.3 40 | 41 | - Use `Cabal-3.6` 42 | - `resolveConfig` respects `CABAL_DIR` environment variable 43 | 44 | ## 0.4.2 45 | 46 | - `findConfig` respects `CABAL_DIR` environment variable 47 | 48 | ## 0.4.1 49 | 50 | - Use `Cabal-3.4` 51 | 52 | ## 0.4 53 | 54 | - Rewrite `Cabal.Index` module 55 | - Added `riTarOffset` field to `ReleaseInfo` 56 | - Make `SHA256` be backed by four `Word64`. 57 | Hackage cache file size drops from 11673149 to 4423787 bytes. 58 | 59 | - Update dependencies 60 | 61 | ## 0.3 62 | 63 | - Require `Cabal-3.2`. 64 | - Rename `prjOrigFields` to `prjOtherFields` to reflect its intended purpose: 65 | contain all fields of a `cabal.project` file that are not already covered by 66 | a different field of `Project`, such as `prjPackages`, `prjOptPackages`, 67 | `prjSourceRepos`, etc. The semantics of `parseProject` have also been changed 68 | accordingly, so `Project`s produced by `parseProject` will no longer have 69 | `prjOtherFields` entries that overlap with other `Project` fields. 70 | - `ParseError` from `Cabal.Parse` now parameterizes the list type used in 71 | `peErrors`. Most of the time, this will be instantiated to `NonEmpty`. 72 | - Add `NFData` instances 73 | 74 | ## 0.2 75 | 76 | Add `repoSecure` field to `Repo` in `Cabal.Config`. 77 | -------------------------------------------------------------------------------- /cabal-install-parsers/LICENSE: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause 2 | 3 | Copyright (c) 2019 Oleg Grenrus 4 | 5 | This library is free software: you may copy, redistribute and/or modify it 6 | under the terms of the GNU General Public License as published by the 7 | Free Software Foundation, either version 3 of the License, or (at your 8 | option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program (see `LICENSE.GPLv3`). If not, see 17 | . 18 | 19 | This library incorporates work covered by the following copyright and 20 | permission notice: 21 | 22 | Copyright (c) 2003-2017, Cabal Development Team. 23 | See the AUTHORS file for the full list of copyright holders. 24 | All rights reserved. 25 | 26 | Redistribution and use in source and binary forms, with or without 27 | modification, are permitted provided that the following conditions are 28 | met: 29 | 30 | * Redistributions of source code must retain the above copyright 31 | notice, this list of conditions and the following disclaimer. 32 | 33 | * Redistributions in binary form must reproduce the above 34 | copyright notice, this list of conditions and the following 35 | disclaimer in the documentation and/or other materials provided 36 | with the distribution. 37 | 38 | * Neither the name of Isaac Jones nor the names of other 39 | contributors may be used to endorse or promote products derived 40 | from this software without specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 44 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 45 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 46 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 47 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 48 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 49 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 50 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 52 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | -------------------------------------------------------------------------------- /cabal-install-parsers/bench/Bench.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Criterion.Main (defaultMain, bench, nf, env) 4 | 5 | import qualified Cabal.Project as C 6 | import qualified Data.ByteString as BS 7 | 8 | main :: IO () 9 | main = defaultMain 10 | [ env (BS.readFile path ) $ \contents -> 11 | bench "haskell-ci.project" $ nf (C.parseProject path) contents 12 | ] 13 | where 14 | path = "fixtures/haskell-ci.project" 15 | -------------------------------------------------------------------------------- /cabal-install-parsers/cabal-install-parsers.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.2 2 | name: cabal-install-parsers 3 | version: 0.6.4 4 | synopsis: Utilities to work with cabal-install files 5 | description: 6 | @cabal-install-parsers@ provides parsers for @cabal-install@ files: 7 | @cabal.project@ ("Cabal.Project"), 8 | @cabal.config@ ("Cabal.Config"), 9 | @01-index.tar@ ("Cabal.Index"); 10 | as well as some other utilities. 11 | . 12 | The feature set is mainly motivated by the needs of @haskell-ci@ package. 13 | The parsed data is not complete, functionality is added on demand. 14 | 15 | homepage: https://haskell-ci.rtfd.org/ 16 | bug-reports: https://github.com/haskell-CI/haskell-ci/issues 17 | license: GPL-2.0-or-later AND BSD-3-Clause 18 | license-files: 19 | LICENSE 20 | LICENSE.GPLv2 21 | LICENSE.GPLv3 22 | 23 | author: Herbert Valerio Riedel, Oleg Grenrus 24 | maintainer: hvr@gnu.org, oleg.grenrus@iki.fi 25 | category: Development 26 | build-type: Simple 27 | tested-with: 28 | GHC ==8.8.4 29 | || ==8.10.7 30 | || ==9.0.2 31 | || ==9.2.8 32 | || ==9.4.8 33 | || ==9.6.7 34 | || ==9.8.4 35 | || ==9.10.3 36 | || ==9.12.2 37 | 38 | extra-source-files: 39 | Changelog.md 40 | fixtures/*.golden 41 | fixtures/*.project 42 | 43 | source-repository head 44 | type: git 45 | location: https://github.com/haskell-CI/haskell-ci.git 46 | 47 | library 48 | default-language: Haskell2010 49 | ghc-options: 50 | -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates 51 | -Wcompat -Wnoncanonical-monad-instances 52 | 53 | hs-source-dirs: src 54 | exposed-modules: 55 | Cabal.Config 56 | Cabal.Index 57 | Cabal.Optimization 58 | Cabal.Package 59 | Cabal.Parse 60 | Cabal.Project 61 | Cabal.SourceRepo 62 | 63 | other-modules: 64 | Cabal.Internal.Glob 65 | Cabal.Internal.Newtypes 66 | 67 | -- GHC-boot libraries 68 | build-depends: 69 | , base >=4.13.0.0 && <4.22 70 | , binary ^>=0.8.7.0 71 | , bytestring ^>=0.10.10.1 || ^>=0.11.1.0 || ^>=0.12.0.2 72 | , Cabal-syntax ^>=3.14.2.0 73 | , containers ^>=0.6.2.1 || ^>=0.7 74 | , deepseq ^>=1.4.4.0 || ^>=1.5.0.0 75 | , directory ^>=1.3.6.0 76 | , filepath ^>=1.4.2.1 || ^>=1.5.2.0 77 | , parsec ^>=3.1.14.0 78 | , pretty ^>=1.1.3.6 79 | , text ^>=1.2.4.0 || ^>=2.0.1 || ^>=2.1 80 | , time ^>=1.9.3 || ^>=1.11.1.1 || ^>=1.12.2 || ^>=1.14 81 | , transformers ^>=0.5.6.2 || ^>=0.6.1.0 82 | 83 | -- extra dependencies 84 | build-depends: 85 | , aeson ^>=2.1.0.0 || ^>=2.2.0.0 86 | , base16-bytestring ^>=1.0.0.0 87 | , binary-instances ^>=1 88 | , cryptohash-sha256 ^>=0.11.101.0 89 | , lukko ^>=0.1.1 90 | , network-uri ^>=2.6.1.0 91 | , tar ^>=0.5.1.1 || ^>=0.6.0.0 92 | 93 | test-suite cabal-parsers-index 94 | default-language: Haskell2010 95 | type: exitcode-stdio-1.0 96 | main-is: Index.hs 97 | hs-source-dirs: test 98 | 99 | -- inherited constraints 100 | build-depends: 101 | , base 102 | , base16-bytestring 103 | , bytestring 104 | , cabal-install-parsers 105 | , Cabal-syntax 106 | , containers 107 | , tar 108 | 109 | -- dependencies needing explicit constraints 110 | build-depends: 111 | , tasty ^>=1.4 || ^>=1.5 112 | , tasty-hunit ^>=0.10.0.2 113 | 114 | test-suite cabal-parsers-golden 115 | default-language: Haskell2010 116 | type: exitcode-stdio-1.0 117 | main-is: Golden.hs 118 | hs-source-dirs: test 119 | 120 | -- inherited constraints 121 | build-depends: 122 | , base 123 | , bytestring 124 | , cabal-install-parsers 125 | , Cabal-syntax 126 | , filepath 127 | , pretty 128 | 129 | -- dependencies needing explicit constraints 130 | build-depends: 131 | , tasty ^>=1.4 || ^>=1.5 132 | , tasty-golden ^>=2.3.1.1 133 | , tree-diff >=0.2 && <0.4 134 | 135 | benchmark cabal-parsers-bench 136 | default-language: Haskell2010 137 | type: exitcode-stdio-1.0 138 | main-is: Bench.hs 139 | hs-source-dirs: bench 140 | 141 | -- inherited constraints 142 | build-depends: 143 | , base 144 | , bytestring 145 | , cabal-install-parsers 146 | 147 | -- dependencies needing explicit constraints 148 | build-depends: criterion ^>=1.6.0.0 149 | -------------------------------------------------------------------------------- /cabal-install-parsers/fixtures/haskell-ci.golden: -------------------------------------------------------------------------------- 1 | Project 2 | {prjAllowNewer = [], 3 | prjConstraints = [], 4 | prjMaxBackjumps = Nothing, 5 | prjOptPackages = ["cabal-parsers"], 6 | prjOptimization = OptimizationOn, 7 | prjOtherFields = [PrettyField "tests" "true", 8 | PrettySection 9 | "package" 10 | ["haskell-ci"] 11 | [PrettyField "ghc-options" "-Wall", 12 | PrettyField "ghc-options" "-Werror"]], 13 | prjPackages = ["."], 14 | prjReorderGoals = False, 15 | prjSourceRepos = [], 16 | prjUriPackages = []} 17 | -------------------------------------------------------------------------------- /cabal-install-parsers/fixtures/haskell-ci.project: -------------------------------------------------------------------------------- 1 | -- Cabal project configuration 2 | -- Consult http://cabal.readthedocs.io/en/latest/nix-local-build.html for more information 3 | 4 | packages: . 5 | optional-packages: cabal-parsers 6 | 7 | tests: true 8 | 9 | package haskell-ci 10 | ghc-options: -Wall 11 | ghc-options: -Werror 12 | -------------------------------------------------------------------------------- /cabal-install-parsers/src/Cabal/Optimization.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | -- | License: GPL-3.0-or-later AND BSD-3-Clause 3 | -- 4 | -- Optimization level. 5 | module Cabal.Optimization ( 6 | Optimization (..), 7 | ) where 8 | 9 | import Control.Applicative (Alternative (..)) 10 | import Control.DeepSeq (NFData (..)) 11 | import GHC.Generics (Generic) 12 | 13 | import qualified Distribution.Compat.CharParsing as C 14 | import qualified Distribution.Parsec as C 15 | import qualified Distribution.Pretty as C 16 | import qualified Text.PrettyPrint as PP 17 | 18 | -- | Optimization level, may be turned on with 'True' or off with 'False', 19 | -- or set an explicit optimization level. 20 | data Optimization 21 | = OptimizationOn 22 | | OptimizationOff 23 | | OptimizationLevel Int 24 | deriving (Eq, Show, Generic) 25 | 26 | instance C.Parsec Optimization where 27 | parsec = boolean <|> numeric where 28 | boolean = ite OptimizationOn OptimizationOff <$> C.parsec 29 | numeric = OptimizationLevel <$> C.integral 30 | 31 | ite t _ True = t 32 | ite _ f False = f 33 | 34 | instance C.Pretty Optimization where 35 | pretty OptimizationOn = C.pretty True 36 | pretty OptimizationOff = C.pretty False 37 | pretty (OptimizationLevel l) = PP.int l 38 | 39 | -- | @since 0.2.1 40 | instance NFData Optimization 41 | -------------------------------------------------------------------------------- /cabal-install-parsers/src/Cabal/Package.hs: -------------------------------------------------------------------------------- 1 | module Cabal.Package ( 2 | readPackage, 3 | parsePackage, 4 | ) where 5 | 6 | import Control.Exception (throwIO) 7 | import Data.ByteString (ByteString) 8 | import Data.List.NonEmpty (NonEmpty) 9 | 10 | import qualified Data.ByteString as BS 11 | import qualified Distribution.Fields as C 12 | import qualified Distribution.PackageDescription as C 13 | import qualified Distribution.PackageDescription.Parsec as C 14 | 15 | import Cabal.Parse 16 | 17 | -- | High level convinience function to read package definitons, @.cabal@ files. 18 | -- 19 | -- May throw 'IOException' when file doesn't exist, and 'ParseError' 20 | -- on parse error. 21 | readPackage :: FilePath -> IO C.GenericPackageDescription 22 | readPackage fp = do 23 | contents <- BS.readFile fp 24 | either throwIO return (parsePackage fp contents) 25 | 26 | -- | Parse @.cabal@ file. 27 | parsePackage :: FilePath -> ByteString -> Either (ParseError NonEmpty) C.GenericPackageDescription 28 | parsePackage fp contents = case C.runParseResult $ C.parseGenericPackageDescription contents of 29 | (ws, Left (_mv, errs)) -> Left $ ParseError fp contents errs ws 30 | (_, Right gpd) -> Right gpd 31 | -------------------------------------------------------------------------------- /cabal-install-parsers/src/Cabal/Parse.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | {-# LANGUAGE StandaloneDeriving #-} 3 | {-# LANGUAGE UndecidableInstances #-} 4 | -- | License: GPL-3.0-or-later AND BSD-3-Clause 5 | -- 6 | -- @.cabal@ and a like file parsing helpers. 7 | module Cabal.Parse ( 8 | parseWith, 9 | ParseError (..), 10 | renderParseError, 11 | ) where 12 | 13 | import Control.DeepSeq (NFData (..)) 14 | import Control.Exception (Exception (..)) 15 | import Data.ByteString (ByteString) 16 | import Data.Foldable (for_) 17 | import Data.List.NonEmpty (NonEmpty) 18 | import Data.Typeable (Typeable) 19 | import Distribution.Utils.Generic (fromUTF8BS) 20 | import GHC.Generics (Generic) 21 | import System.FilePath (normalise) 22 | 23 | import qualified Data.ByteString.Char8 as BS8 24 | import qualified Distribution.Fields as C 25 | import qualified Distribution.Fields.LexerMonad as C 26 | import qualified Distribution.Parsec as C 27 | import qualified Distribution.Utils.Generic as C 28 | import qualified Text.Parsec as P 29 | 30 | -- | Parse the contents using provided parser from 'C.Field' list. 31 | -- 32 | -- This variant doesn't return any warnings in the successful case. 33 | -- 34 | parseWith 35 | :: ([C.Field C.Position] -> C.ParseResult a) -- ^ parse 36 | -> FilePath -- ^ filename 37 | -> ByteString -- ^ contents 38 | -> Either (ParseError NonEmpty) a 39 | parseWith parser fp bs = case C.runParseResult result of 40 | (_, Right x) -> return x 41 | (ws, Left (_, es)) -> Left $ ParseError fp bs es ws 42 | where 43 | result = case C.readFields' bs of 44 | Left perr -> C.parseFatalFailure pos (show perr) where 45 | ppos = P.errorPos perr 46 | pos = C.Position (P.sourceLine ppos) (P.sourceColumn ppos) 47 | Right (fields, lexWarnings) -> do 48 | C.parseWarnings (C.toPWarnings lexWarnings) 49 | for_ (C.validateUTF8 bs) $ \pos -> 50 | C.parseWarning C.zeroPos C.PWTUTF $ "UTF8 encoding problem at byte offset " ++ show pos 51 | parser fields 52 | 53 | -- | Parse error. 54 | data ParseError f = ParseError 55 | { peFilename :: FilePath 56 | , peContents :: ByteString 57 | , peErrors :: f C.PError 58 | , peWarnings :: [C.PWarning] 59 | } 60 | deriving (Generic) 61 | 62 | deriving instance (Show (f C.PError)) => Show (ParseError f) 63 | 64 | instance (Foldable f, Show (f C.PError), Typeable f) => Exception (ParseError f) where 65 | displayException = renderParseError 66 | 67 | -- | @since 0.2.1 68 | instance (NFData (f C.PError)) => NFData (ParseError f) 69 | 70 | -- | Render parse error highlighting the part of the input file. 71 | renderParseError :: Foldable f => ParseError f -> String 72 | renderParseError (ParseError filepath contents errors warnings) 73 | | null errors && null warnings = "" 74 | | null errors = unlines $ 75 | ("Warnings encountered when parsing file " ++ filepath ++ ":") 76 | : renderedWarnings 77 | | otherwise = unlines $ 78 | [ "Errors encountered when parsing file " ++ filepath ++ ":" 79 | ] 80 | ++ renderedErrors 81 | ++ renderedWarnings 82 | where 83 | filepath' = normalise filepath 84 | 85 | -- lines of the input file. 'lines' is taken, so they are called rows 86 | -- contents, line number, whether it's empty line 87 | rows :: [(String, Int, Bool)] 88 | rows = zipWith f (BS8.lines contents) [1..] where 89 | f bs i = let s = fromUTF8BS bs in (s, i, isEmptyOrComment s) 90 | 91 | rowsZipper = listToZipper rows 92 | 93 | isEmptyOrComment :: String -> Bool 94 | isEmptyOrComment s = case dropWhile (== ' ') s of 95 | "" -> True -- empty 96 | ('-':'-':_) -> True -- comment 97 | _ -> False 98 | 99 | renderedErrors = concatMap renderError errors 100 | renderedWarnings = concatMap renderWarning warnings 101 | 102 | renderError :: C.PError -> [String] 103 | renderError (C.PError pos@(C.Position row col) msg) 104 | -- if position is 0:0, then it doesn't make sense to show input 105 | -- looks like, Parsec errors have line-feed in them 106 | | pos == C.zeroPos = msgs 107 | | otherwise = msgs ++ formatInput row col 108 | where 109 | msgs = [ "", filepath' ++ ":" ++ C.showPos pos ++ ": error:", trimLF msg, "" ] 110 | 111 | renderWarning :: C.PWarning -> [String] 112 | renderWarning (C.PWarning _ pos@(C.Position row col) msg) 113 | | pos == C.zeroPos = msgs 114 | | otherwise = msgs ++ formatInput row col 115 | where 116 | msgs = [ "", filepath' ++ ":" ++ C.showPos pos ++ ": warning:", trimLF msg, "" ] 117 | 118 | -- sometimes there are (especially trailing) newlines. 119 | trimLF :: String -> String 120 | trimLF = dropWhile (== '\n') . reverse . dropWhile (== '\n') . reverse 121 | 122 | -- format line: prepend the given line number 123 | formatInput :: Int -> Int -> [String] 124 | formatInput row col = case advance (row - 1) rowsZipper of 125 | Zipper xs ys -> before ++ after where 126 | before = case span (\(_, _, b) -> b) xs of 127 | (_, []) -> [] 128 | (zs, z : _) -> map formatInputLine $ z : reverse zs 129 | 130 | after = case ys of 131 | [] -> [] 132 | (z : _zs) -> 133 | [ formatInputLine z -- error line 134 | , " | " ++ replicate (col - 1) ' ' ++ "^" -- pointer: ^ 135 | ] 136 | -- do we need rows after? 137 | -- ++ map formatInputLine (take 1 zs) -- one row after 138 | 139 | formatInputLine :: (String, Int, Bool) -> String 140 | formatInputLine (str, row, _) = leftPadShow row ++ " | " ++ str 141 | 142 | -- hopefully we don't need to work with over 99999 lines .cabal files 143 | -- at that point small glitches in error messages are hopefully fine. 144 | leftPadShow :: Int -> String 145 | leftPadShow n = let s = show n in replicate (5 - length s) ' ' ++ s 146 | 147 | data Zipper a = Zipper [a] [a] 148 | 149 | listToZipper :: [a] -> Zipper a 150 | listToZipper = Zipper [] 151 | 152 | advance :: Int -> Zipper a -> Zipper a 153 | advance n z@(Zipper xs ys) 154 | | n <= 0 = z 155 | | otherwise = case ys of 156 | [] -> z 157 | (y:ys') -> advance (n - 1) $ Zipper (y:xs) ys' 158 | -------------------------------------------------------------------------------- /cabal-install-parsers/src/Cabal/SourceRepo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE OverloadedStrings #-} 5 | {-# LANGUAGE RankNTypes #-} 6 | {-# LANGUAGE StandaloneDeriving #-} 7 | {-# LANGUAGE UndecidableInstances #-} 8 | -- | Originally Distribution.Client.SourceRepo 9 | module Cabal.SourceRepo ( 10 | -- * SourceRepo 11 | SourceRepositoryPackage (..), 12 | -- * Aliases 13 | SourceRepoList, 14 | SourceRepoMaybe, 15 | SourceRepoProxy, 16 | -- * Functions 17 | srpHoist, 18 | srpToProxy, 19 | srpFanOut, 20 | -- * Grammar 21 | sourceRepositoryPackageGrammar, 22 | ) where 23 | 24 | import Control.DeepSeq (NFData (..)) 25 | import Data.Functor.Identity (Identity) 26 | import Data.List.NonEmpty (NonEmpty (..)) 27 | import Data.Proxy (Proxy (..)) 28 | import GHC.Generics (Generic) 29 | 30 | import Distribution.Compat.Lens (Lens, Lens') 31 | import Distribution.FieldGrammar 32 | (FieldGrammar, ParsecFieldGrammar', PrettyFieldGrammar', monoidalFieldAla, optionalFieldAla, uniqueField, uniqueFieldAla) 33 | import Distribution.FieldGrammar.Newtypes (FilePathNT (..), List, NoCommaFSep (..), Token (..), alaList') 34 | import Distribution.Types.SourceRepo (RepoType (..)) 35 | 36 | -- | @source-repository-package@ definition 37 | -- 38 | data SourceRepositoryPackage f = SourceRepositoryPackage 39 | { srpType :: !RepoType 40 | , srpLocation :: !String 41 | , srpTag :: !(Maybe String) 42 | , srpBranch :: !(Maybe String) 43 | , srpSubdir :: !(f FilePath) 44 | } 45 | deriving (Generic) 46 | 47 | deriving instance (Eq (f FilePath)) => Eq (SourceRepositoryPackage f) 48 | deriving instance (Ord (f FilePath)) => Ord (SourceRepositoryPackage f) 49 | deriving instance (Show (f FilePath)) => Show (SourceRepositoryPackage f) 50 | 51 | -- | @since 0.2.1 52 | instance NFData (f FilePath) => NFData (SourceRepositoryPackage f) 53 | 54 | -- | Read from @cabal.project@ 55 | type SourceRepoList = SourceRepositoryPackage [] 56 | 57 | -- | Distilled from 'Distribution.Types.SourceRepo.SourceRepo' 58 | type SourceRepoMaybe = SourceRepositoryPackage Maybe 59 | 60 | -- | 'SourceRepositoryPackage' without subdir. Used in clone errors. Cloning doesn't care about subdirectory. 61 | type SourceRepoProxy = SourceRepositoryPackage Proxy 62 | 63 | srpHoist :: (forall x. f x -> g x) -> SourceRepositoryPackage f -> SourceRepositoryPackage g 64 | srpHoist nt s = s { srpSubdir = nt (srpSubdir s) } 65 | 66 | srpToProxy :: SourceRepositoryPackage f -> SourceRepositoryPackage Proxy 67 | srpToProxy s = s { srpSubdir = Proxy } 68 | 69 | -- | Split single @source-repository-package@ declaration with multiple subdirs, 70 | -- into multiple ones with at most single subdir. 71 | srpFanOut :: SourceRepositoryPackage [] -> NonEmpty (SourceRepositoryPackage Maybe) 72 | srpFanOut s@SourceRepositoryPackage { srpSubdir = [] } = 73 | s { srpSubdir = Nothing } :| [] 74 | srpFanOut s@SourceRepositoryPackage { srpSubdir = d:ds } = f d :| map f ds where 75 | f subdir = s { srpSubdir = Just subdir } 76 | 77 | ------------------------------------------------------------------------------- 78 | -- Lens 79 | ------------------------------------------------------------------------------- 80 | 81 | srpTypeLens :: Lens' (SourceRepositoryPackage f) RepoType 82 | srpTypeLens f s = fmap (\x -> s { srpType = x }) (f (srpType s)) 83 | {-# INLINE srpTypeLens #-} 84 | 85 | srpLocationLens :: Lens' (SourceRepositoryPackage f) String 86 | srpLocationLens f s = fmap (\x -> s { srpLocation = x }) (f (srpLocation s)) 87 | {-# INLINE srpLocationLens #-} 88 | 89 | srpTagLens :: Lens' (SourceRepositoryPackage f) (Maybe String) 90 | srpTagLens f s = fmap (\x -> s { srpTag = x }) (f (srpTag s)) 91 | {-# INLINE srpTagLens #-} 92 | 93 | srpBranchLens :: Lens' (SourceRepositoryPackage f) (Maybe String) 94 | srpBranchLens f s = fmap (\x -> s { srpBranch = x }) (f (srpBranch s)) 95 | {-# INLINE srpBranchLens #-} 96 | 97 | srpSubdirLens :: Lens (SourceRepositoryPackage f) (SourceRepositoryPackage g) (f FilePath) (g FilePath) 98 | srpSubdirLens f s = fmap (\x -> s { srpSubdir = x }) (f (srpSubdir s)) 99 | {-# INLINE srpSubdirLens #-} 100 | 101 | ------------------------------------------------------------------------------- 102 | -- Parser & PPrinter 103 | ------------------------------------------------------------------------------- 104 | 105 | sourceRepositoryPackageGrammar 106 | :: ( FieldGrammar c g, Applicative (g SourceRepoList) 107 | , c (List NoCommaFSep FilePathNT String) 108 | , c (Identity RepoType) 109 | ) 110 | => g SourceRepoList SourceRepoList 111 | sourceRepositoryPackageGrammar = SourceRepositoryPackage 112 | <$> uniqueField "type" srpTypeLens 113 | <*> uniqueFieldAla "location" Token srpLocationLens 114 | <*> optionalFieldAla "tag" Token srpTagLens 115 | <*> optionalFieldAla "branch" Token srpBranchLens 116 | <*> monoidalFieldAla "subdir" (alaList' NoCommaFSep FilePathNT) srpSubdirLens -- note: NoCommaFSep is somewhat important for roundtrip, as "." is there... 117 | {-# SPECIALIZE sourceRepositoryPackageGrammar :: ParsecFieldGrammar' SourceRepoList #-} 118 | {-# SPECIALIZE sourceRepositoryPackageGrammar :: PrettyFieldGrammar' SourceRepoList #-} 119 | -------------------------------------------------------------------------------- /cabal-install-parsers/test/Golden.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE UndecidableInstances #-} 2 | module Main (main) where 3 | 4 | import Data.TreeDiff 5 | import Data.TreeDiff.Golden (ediffGolden) 6 | import System.FilePath ((-<.>), ()) 7 | import Test.Tasty (TestName, TestTree, defaultMain, testGroup) 8 | import Test.Tasty.Golden.Advanced (goldenTest) 9 | import Text.PrettyPrint (Doc, render) 10 | 11 | import qualified Data.ByteString as BS 12 | import qualified Data.TreeDiff.OMap as OMap 13 | 14 | import Distribution.Fields (PrettyField (..)) 15 | import Distribution.Types.SourceRepo (KnownRepoType, RepoKind, RepoType, SourceRepo) 16 | 17 | import Cabal.Optimization 18 | import Cabal.Parse 19 | import Cabal.Project 20 | import Cabal.SourceRepo 21 | 22 | main :: IO () 23 | main = defaultMain $ testGroup "golden" 24 | [ golden "haskell-ci" 25 | ] 26 | where 27 | golden name = ediffGolden goldenTest name goldenPath $ do 28 | contents <- BS.readFile projectPath 29 | either (fail . renderParseError) return $ parseProject projectPath contents 30 | where 31 | goldenPath = "fixtures" name -<.> "golden" 32 | projectPath = "fixtures" name -<.> "project" 33 | 34 | ------------------------------------------------------------------------------- 35 | -- orphans 36 | ------------------------------------------------------------------------------- 37 | 38 | instance (ToExpr uri, ToExpr opt, ToExpr pkg) => ToExpr (Project uri opt pkg) where 39 | toExpr prj = Rec "Project" $ OMap.fromList 40 | [ field "prjPackages" prjPackages 41 | , field "prjOptPackages" prjOptPackages 42 | , field "prjUriPackages" prjUriPackages 43 | , field "prjConstraints" prjConstraints 44 | , field "prjAllowNewer" prjAllowNewer 45 | , field "prjReorderGoals" prjReorderGoals 46 | , field "prjMaxBackjumps" prjMaxBackjumps 47 | , field "prjOptimization" prjOptimization 48 | , field "prjSourceRepos" prjSourceRepos 49 | , field "prjOtherFields" prjOtherFields 50 | ] 51 | where 52 | field name f = (name, toExpr (f prj)) 53 | 54 | instance ToExpr Optimization 55 | 56 | instance ToExpr SourceRepo 57 | instance ToExpr RepoKind 58 | instance ToExpr RepoType 59 | instance ToExpr KnownRepoType 60 | instance ToExpr (f FilePath) => ToExpr (SourceRepositoryPackage f) 61 | 62 | instance ToExpr Doc where 63 | toExpr = toExpr . render 64 | 65 | instance ToExpr (PrettyField ann) where 66 | toExpr (PrettyField _ fn d) = App "PrettyField" [toExpr fn, toExpr d] 67 | toExpr (PrettySection _ fn ds ps) = App "PrettySection" [toExpr fn, toExpr ds, toExpr ps] 68 | toExpr PrettyEmpty = App "PrettyEmpty" [] 69 | -------------------------------------------------------------------------------- /cabal-install-parsers/test/Index.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Main (main) where 3 | 4 | import Data.String (IsString (..)) 5 | import Prelude hiding (pi) 6 | import System.IO (IOMode (ReadMode), withFile) 7 | import Test.Tasty (defaultMain, testGroup) 8 | import Test.Tasty.HUnit (assertBool, assertEqual, assertFailure, testCase, testCaseSteps) 9 | 10 | import qualified Codec.Archive.Tar.Entry as Tar 11 | import qualified Codec.Archive.Tar.Index as Tar 12 | import qualified Data.ByteString as BS 13 | import qualified Data.ByteString.Base16 as Base16 14 | import qualified Data.ByteString.Lazy as LBS 15 | import qualified Data.Map.Strict as Map 16 | import qualified Distribution.Package as C 17 | import qualified Distribution.Version as C 18 | 19 | import Cabal.Config 20 | import Cabal.Index 21 | 22 | main :: IO () 23 | main = defaultMain $ testGroup "Cabal.Index" 24 | [ testGroup "SHA256" 25 | [ testCase "Base16.encode . getSHA256 . unsafeMkSHA256 rountrip" $ do 26 | let s :: IsString s => s 27 | s = "a6f5eddcff9526c786a1b77bdfade54b42f67c066b379bbc4b55ffb291e6c7d6" 28 | 29 | let expected :: BS.ByteString 30 | expected = s 31 | 32 | assertEqual "hash" 33 | expected 34 | (Base16.encode . getSHA256 . unsafeMkSHA256 $ s) 35 | ] 36 | 37 | , testCaseSteps "low-level approach" $ \step -> do 38 | step "Read ~/.cabal/config" 39 | cfg <- readConfig 40 | 41 | step "Find hackage 01-index.tar" 42 | indexPath <- maybe 43 | (assertFailure "Cannot find hackage 01.indexTar") 44 | return 45 | (cfgRepoIndex cfg hackageHaskellOrg) 46 | 47 | step "Read metadata" 48 | meta <- indexMetadata indexPath Nothing 49 | 50 | step "Check aeson-1.4.4.0 metadata" 51 | case Map.lookup (C.mkPackageName "aeson") meta of 52 | Nothing -> assertFailure "Cannot find aeson on Hackage" 53 | Just pi -> case Map.lookup (C.mkVersion [1,4,4,0]) (piVersions pi) of 54 | Nothing -> assertFailure "Cannot find aeson-1.4.4.0 on Hackage" 55 | Just ri -> do 56 | -- Note: if someone makes revision to aeson-1.4.4.0 57 | -- revision and cabal hash check will start failing 58 | -- tarball hash shouldn't ever change. 59 | assertEqual "revision" 1 (riRevision ri) 60 | assertEqual "cabal size" 61 | 7251 62 | (riCabalSize ri) 63 | assertEqual "cabal hash" 64 | (unsafeMkSHA256 "a6f5eddcff9526c786a1b77bdfade54b42f67c066b379bbc4b55ffb291e6c7d6") 65 | (riCabalHash ri) 66 | assertEqual "tarball size" 67 | 279980 68 | (riTarballSize ri) 69 | assertEqual "tarball hash" 70 | (unsafeMkSHA256 "17c67cdaca651e18f310b21b2b12bac6bcec5188c3ac0e4b64cc60c94d7e4d2e") 71 | (riTarballHash ri) 72 | 73 | -- check contents 74 | withFile indexPath ReadMode $ \hdl -> do 75 | entry <- Tar.hReadEntry hdl (riTarOffset ri) 76 | case Tar.entryContent entry of 77 | Tar.NormalFile bs fs -> do 78 | assertEqual "entry content size" 79 | 7251 80 | fs 81 | assertEqual "entry content (prefix)" 82 | "name: aeson\r\nversion: 1.4.4.0\r\nx-revision: 1\r\nlicense: " 83 | (LBS.take 80 bs) 84 | 85 | _ -> assertFailure "invalid entry content" 86 | 87 | step "binary (deprecated versions)" 88 | case Map.lookup (C.mkPackageName "binary") meta of 89 | Nothing -> assertFailure "Cannot find binary on Hackage" 90 | Just pi -> do 91 | assertBool "binary-0.9.0.0 should be in piVersions" $ 92 | Map.member (C.mkVersion [0,9,0,0]) (piVersions pi) 93 | 94 | assertBool "binary-0.9.0.0 should NOT be in piPreferredVersions" $ 95 | Map.notMember (C.mkVersion [0,9,0,0]) (piPreferredVersions pi) 96 | 97 | , testCaseSteps "cachedHackageMetadata" $ \step -> do 98 | step "First read" 99 | meta1 <- cachedHackageMetadata 100 | 101 | step "Second read" 102 | meta2 <- cachedHackageMetadata 103 | 104 | assertEqual "cached value should be the same" meta1 meta2 105 | 106 | ] 107 | -------------------------------------------------------------------------------- /cabal.haskell-ci: -------------------------------------------------------------------------------- 1 | -- Most settings can be given as command line arguments 2 | -- Config file is convenient if you maintain large amount of packages 3 | 4 | -- Supported: xenial, bionic, focal. 5 | -- Bionic is a default. 6 | distribution: jammy 7 | 8 | -- list of extra apt packages to be installed 9 | -- apt: foo bar 10 | 11 | jobs: 2:2 12 | 13 | -- This is useful if you want limit jobs beyond tested-with specification, 14 | -- e.g. while iterating the CI setup itself. 15 | -- linux-jobs: >=8.10 16 | 17 | -- Add GHC head job 18 | ghc-head: False 19 | 20 | -- travis Caching 21 | -- cache: True 22 | 23 | -- remove cabal noise from test output 24 | -- cabal-noise: False 25 | 26 | -- Build tests. In addition to True and False you may specify 27 | -- a version range, e.g. >= 8.0 to build tests only in some jobs. 28 | tests: True 29 | 30 | -- Run tests. Note that only built tests are run. Accepts booleans or version range. 31 | run-tests: True 32 | 33 | -- Build benchmarks. There are no way to run benchmarks. Accepts booleans or version range. 34 | benchmarks: True 35 | 36 | -- Build haddocks. Accepts booleans or version range. 37 | haddock: True 38 | 39 | -- Run cabal check 40 | -- cabal-check: True 41 | 42 | -- Install dependencies in a separate step 43 | -- If your project has inplace packages, you want to disable this. 44 | -- install-dependencies: True 45 | 46 | -- --no-tests --no-benchmarks build is useful to verify that package 47 | -- builds when less constrained 48 | -- no-tests-no-bench: True 49 | 50 | -- By default `installed` constraints are used for packages 51 | -- in global db. We can modify which constraints are used. 52 | installed: +all -Cabal -Cabal-syntax -parsec 53 | 54 | -- Options for local packages 55 | local-ghc-options: -Werror 56 | 57 | -- Add -Werror-missing-options to all, local (default), or none of the packages. 58 | error-missing-methods: all 59 | 60 | -- Build only these branches 61 | branches: master 62 | 63 | -- Enable IRC notifications to the given channel 64 | -- project-name: haskell-ci 65 | -- irc-channels: irc.libera.chat#haskell-lens 66 | -- irc-nickname: github-actions 67 | -- irc-password: ${{ secrets.MyIrcBotPassword }} 68 | -- irc-if-in-origin-repo: True 69 | 70 | -- Run doctest (on GHC-8.0+ which support .ghc.environment) 71 | doctest: <9 72 | doctest-options: --fast 73 | -- doctest-version: ==0.13.* 74 | doctest-filter-packages: base-compat-batteries 75 | 76 | -- Run cabal-docspec 77 | docspec: True 78 | docspec-options: --verbose --timeout 2 79 | 80 | -- macOS job 81 | macos-jobs: ==8.10.* 82 | 83 | -- on Linux we can use ghcup to setup (some) of jobs 84 | -- ghcup-jobs: >=9 85 | 86 | -- Define per-job environment variables 87 | -- env: 8.4.4:FOO=bar, 88 | -- 8.6.4:FOO=baz 89 | 90 | -- Constraint sets 91 | -- Package will be build with different constraints. 92 | -- This is useful to check compatibility with older versions of dependencies. 93 | 94 | constraint-set prefer-oldest 95 | -- we can limit GHC's and add extra constraints for each constraint-set 96 | -- for haskell-ci itself, we don't. 97 | 98 | -- ghc: (>= 7.8 && <7.10) || == 8.2.2 99 | -- constraints: deepseq ==1.4.* 100 | 101 | -- Constraint sets accept booleans for few steps, as the main script 102 | -- Defaults are False. 103 | -- These fields don't accept version ranges: you should rather create 104 | -- another constraint set. 105 | 106 | tests: False 107 | run-tests: False 108 | prefer-oldest: True 109 | -- docspec: False 110 | -- benchmarks: False 111 | -- haddock: True 112 | 113 | -- Copy over additional properties specified in a cabal.project file. 114 | -- Possible values are: 115 | -- none: Do not copy any properties from the cabal.project file. 116 | -- some: Copy a subset of the properties from the cabal.project file. (Default) 117 | -- The fields and sections that are copied over are: 118 | -- 119 | -- * constraints 120 | -- * allow-newer 121 | -- * reorder-goals 122 | -- * max-backjumps 123 | -- * optimization 124 | -- * source-repository-package 125 | -- all: Copy every property from the cabal.project file. 126 | copy-fields: some 127 | 128 | -- Configure haskell-ci's behavior when presented multiple packages (e.g., a 129 | -- cabal.project file that specifies multiple .cabal files), each of which 130 | -- having its own range of GHC versions in its tested-with stanza. 131 | -- Possible values are: 132 | -- uniform: Every package must have the same range of versions in its 133 | -- tested-with stanza. (Default) 134 | -- any: Take the union of all packages' version ranges. This implies 135 | -- that different packages' version ranges can be disjoint. 136 | jobs-selection: uniform 137 | 138 | -- Controls whether the haskell-ci version should be inserted into the 139 | -- generated Travis YAML file. (Default: True) 140 | -- 141 | -- We set this option to False here only to prevent version number churn in 142 | -- the commit history, as version numbers for haskell-ci HEAD change extremely 143 | -- frequently. 144 | insert-version: False 145 | 146 | -- Include these fields "as is" in generated cabal.project 147 | raw-project 148 | keep-going: False 149 | package bytestring 150 | tests: False 151 | 152 | -- Mapping of GHC versions (used for prereleases) 153 | ghc-version-mapping: 9.12.1:9.12.0.20241114 154 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | packages: cabal-install-parsers/*.cabal 3 | 4 | tests: True 5 | 6 | package haskell-ci 7 | ghc-options: -Wall 8 | ghc-options: -Werror 9 | 10 | allow-newer: ShellCheck:filepath 11 | -------------------------------------------------------------------------------- /cli/Main.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | -- https://gitlab.haskell.org/ghc/ghc/-/issues/19397 3 | import qualified HaskellCI 4 | main :: IO () 5 | main = HaskellCI.main 6 | -------------------------------------------------------------------------------- /docker/.gitignore: -------------------------------------------------------------------------------- 1 | *-image 2 | -------------------------------------------------------------------------------- /docker/Dockerfile.template: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | __ # Image specific GHC GHC 65 | __ RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | __ __HC__ \ 67 | __ && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | TAG=1 2 | 3 | COMPILERS=ghc-8.8.1 ghc-8.6.5 ghc-8.4.4 ghc-8.2.2 ghc-8.0.2 ghc-7.10.3 ghc-7.8.4 ghc-7.6.3 ghc-7.4.2 ghc-7.0.4 4 | 5 | IMAGES=generic-image $(COMPILERS:%=%-image) 6 | 7 | .PHONY : all clean images push-images 8 | .PRECIOUS : generic/Dockerfile $(COMPILERS:%=%/Dockerfile) 9 | 10 | all : images 11 | 12 | clean : 13 | rm -f $(IMAGES) 14 | 15 | generic/Dockerfile : Dockerfile.template 16 | mkdir -p generic 17 | cat $< | sed 's/^__.*//' > $@ 18 | 19 | %/cabal.config : cabal.config 20 | cp cabal.config $@ 21 | 22 | %/Dockerfile : Dockerfile.template 23 | mkdir -p $* 24 | cat $< | sed 's/__HC__/$*/g' | sed 's/^__ //' > $@ 25 | 26 | images : $(IMAGES) 27 | 28 | # Strips __ section 29 | generic-image : generic/Dockerfile generic/cabal.config 30 | docker build -t registry.gitlab.com/haskell-ci/haskell-ci/generic generic 31 | docker build -t registry.gitlab.com/haskell-ci/haskell-ci/generic:1 generic 32 | touch $@ 33 | 34 | # Uses __ section and rewrites __HC__ to the actual compiler 35 | %-image : %/Dockerfile %/cabal.config 36 | docker build -t registry.gitlab.com/haskell-ci/haskell-ci/$* $* 37 | docker build -t registry.gitlab.com/haskell-ci/haskell-ci/$*:$(TAG) $* 38 | touch $@ 39 | 40 | push-images : images 41 | for HC in generic $(COMPILERS); do \ 42 | docker push registry.gitlab.com/haskell-ci/haskell-ci/$$HC; \ 43 | docker push registry.gitlab.com/haskell-ci/haskell-ci/$$HC:$(TAG); \ 44 | done 45 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Build docker images 2 | 3 | Current registry is: https://gitlab.com/haskell-ci/haskell-ci/container_registry 4 | 5 | We don't build images for each compiler, only for latest in series (for now) 6 | 7 | All images are built from the same template, `Dockerfile.template` 8 | We install few C `-dev` dependencies. Make a PR if you need some more. 9 | 10 | There's also a `generic` image without `ghc` and `cabal`, which is used 11 | in cases where there are no image for that ghc&cabal pair. 12 | 13 | - `make all` will build images 14 | - `make push-images` will also push them to the registry 15 | - `make ghc-8.4.4-image` or `make generic-image` to build single image. 16 | This is useful when making changes (and checking the size of resulting images) 17 | 18 | ## Misc 19 | 20 | Docker cleanup: 21 | 22 | ``` 23 | docker system prune --volumes 24 | ``` 25 | -------------------------------------------------------------------------------- /docker/cabal.config: -------------------------------------------------------------------------------- 1 | -- https://github.com/haskell/cabal/issues/5956 2 | verbose: normal +nowrap +markoutput 3 | remote-build-reporting: anonymous 4 | write-ghc-environment-files: always 5 | 6 | remote-repo-cache: /home/haskellci/.cabal/packages 7 | logs-dir: /home/haskellci/.cabal/logs 8 | world-file: /home/haskellci/.cabal/world 9 | extra-prog-path: /home/haskellci/.cabal/bin 10 | symlink-bindir: /home/haskellci/.cabal/bin 11 | installdir: /home/haskellci/.cabal/bin 12 | build-summary: /home/haskellci/.cabal/logs/build.log 13 | store-dir: /home/haskellci/.cabal/store 14 | 15 | install-dirs user 16 | prefix: /home/haskellci/.cabal 17 | 18 | repository hackage.haskell.org 19 | url: http://hackage.haskell.org/ 20 | secure: True 21 | key-threshold: 3 22 | root-keys: 23 | fe331502606802feac15e514d9b9ea83fee8b6ffef71335479a2e68d84adc6b0 24 | 1ea9ba32c526d1cc91ab5e5bd364ec5e9e8cb67179a471872f6e26f0ae773d42 25 | 2c6c3627bd6c982990239487f1abd02e08a02e6cf16edb105a8012d444d870c3 26 | 0a5c7ea47cd1b15f01f5f51a33adda7e655bc0f0b0615baa8e271f4c3351e21d 27 | 51f0161b906011b52c6613376b1ae937670da69322113a246a09f807c62f6921 28 | -------------------------------------------------------------------------------- /docker/generic/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docker/generic/cabal.config: -------------------------------------------------------------------------------- 1 | -- https://github.com/haskell/cabal/issues/5956 2 | verbose: normal +nowrap +markoutput 3 | remote-build-reporting: anonymous 4 | write-ghc-environment-files: always 5 | 6 | remote-repo-cache: /home/haskellci/.cabal/packages 7 | logs-dir: /home/haskellci/.cabal/logs 8 | world-file: /home/haskellci/.cabal/world 9 | extra-prog-path: /home/haskellci/.cabal/bin 10 | symlink-bindir: /home/haskellci/.cabal/bin 11 | installdir: /home/haskellci/.cabal/bin 12 | build-summary: /home/haskellci/.cabal/logs/build.log 13 | store-dir: /home/haskellci/.cabal/store 14 | 15 | install-dirs user 16 | prefix: /home/haskellci/.cabal 17 | 18 | repository hackage.haskell.org 19 | url: http://hackage.haskell.org/ 20 | secure: True 21 | key-threshold: 3 22 | root-keys: 23 | fe331502606802feac15e514d9b9ea83fee8b6ffef71335479a2e68d84adc6b0 24 | 1ea9ba32c526d1cc91ab5e5bd364ec5e9e8cb67179a471872f6e26f0ae773d42 25 | 2c6c3627bd6c982990239487f1abd02e08a02e6cf16edb105a8012d444d870c3 26 | 0a5c7ea47cd1b15f01f5f51a33adda7e655bc0f0b0615baa8e271f4c3351e21d 27 | 51f0161b906011b52c6613376b1ae937670da69322113a246a09f807c62f6921 28 | -------------------------------------------------------------------------------- /docker/ghc-7.0.4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-7.0.4 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-7.10.3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-7.10.3 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-7.4.2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-7.4.2 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-7.6.3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-7.6.3 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-7.8.4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-7.8.4 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-8.0.2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-8.0.2 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-8.2.2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-8.2.2 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-8.4.4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-8.4.4 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-8.4.4/cabal.config: -------------------------------------------------------------------------------- 1 | -- https://github.com/haskell/cabal/issues/5956 2 | verbose: normal +nowrap +markoutput 3 | remote-build-reporting: anonymous 4 | write-ghc-environment-files: always 5 | 6 | remote-repo-cache: /home/haskellci/.cabal/packages 7 | logs-dir: /home/haskellci/.cabal/logs 8 | world-file: /home/haskellci/.cabal/world 9 | extra-prog-path: /home/haskellci/.cabal/bin 10 | symlink-bindir: /home/haskellci/.cabal/bin 11 | installdir: /home/haskellci/.cabal/bin 12 | build-summary: /home/haskellci/.cabal/logs/build.log 13 | store-dir: /home/haskellci/.cabal/store 14 | 15 | install-dirs user 16 | prefix: /home/haskellci/.cabal 17 | 18 | repository hackage.haskell.org 19 | url: http://hackage.haskell.org/ 20 | secure: True 21 | key-threshold: 3 22 | root-keys: 23 | fe331502606802feac15e514d9b9ea83fee8b6ffef71335479a2e68d84adc6b0 24 | 1ea9ba32c526d1cc91ab5e5bd364ec5e9e8cb67179a471872f6e26f0ae773d42 25 | 2c6c3627bd6c982990239487f1abd02e08a02e6cf16edb105a8012d444d870c3 26 | 0a5c7ea47cd1b15f01f5f51a33adda7e655bc0f0b0615baa8e271f4c3351e21d 27 | 51f0161b906011b52c6613376b1ae937670da69322113a246a09f807c62f6921 28 | -------------------------------------------------------------------------------- /docker/ghc-8.6.5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-8.6.5 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docker/ghc-8.8.1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | MAINTAINER Oleg Grenrus 3 | 4 | # UTF FTW 5 | ENV LANG C.UTF-8 6 | 7 | # hvr-ppa and some core packages 8 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 9 | automake \ 10 | build-essential \ 11 | ca-certificates \ 12 | git \ 13 | python-software-properties \ 14 | pkg-config \ 15 | software-properties-common \ 16 | sudo \ 17 | && apt-add-repository -y "ppa:hvr/ghc" \ 18 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Dependencies 21 | RUN apt-get -yq update && apt-get -yq --no-install-suggests --no-install-recommends install \ 22 | libbz2-dev \ 23 | libfftw3-dev \ 24 | libgmp-dev \ 25 | liblapack-dev \ 26 | libncurses-dev \ 27 | libncursesw5-dev \ 28 | libpq-dev \ 29 | libsqlite3-dev \ 30 | libssl-dev \ 31 | lzma-dev \ 32 | zlib1g-dev \ 33 | && apt-get autoremove -y --purge && apt-get clean && rm -rf /var/lib/apt/lists/* 34 | 35 | # User 36 | RUN adduser haskellci --gecos "Haskell CI builds" --disabled-password 37 | RUN echo "haskellci ALL = NOPASSWD : ALL" > /etc/sudoers.d/ghc 38 | USER haskellci 39 | WORKDIR /home/haskellci 40 | ENV PATH /home/haskellci/.cabal/bin:$PATH 41 | 42 | # Cmd 43 | CMD ["/bin/bash"] 44 | 45 | # We install cabal-install always, also for generic image 46 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 47 | cabal-install-3.0 \ 48 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* && rm -rf /home/haskellci/.cabal/packages 49 | 50 | # Cabal config 51 | RUN mkdir -p /home/haskellci/.cabal 52 | ADD cabal.config /home/haskellci/.cabal/config 53 | 54 | # TODO: when cabal-install-3.0 is out with `copy`; install cabal-plan :) 55 | 56 | # We also perform initial v2-update 57 | # 01-index.tar.gz is large, but rather it's downloaded during the image download 58 | RUN /opt/ghc/bin/cabal v2-update \ 59 | && mv /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz /home/haskellci/01-index.tar.gz \ 60 | && rm -rf /home/haskellci/.cabal \ 61 | && mkdir -p /home/haskellci/.cabal/packages/hackage.haskell.org \ 62 | && mv /home/haskellci/01-index.tar.gz /home/haskellci/.cabal/packages/hackage.haskell.org/01-index.tar.gz 63 | 64 | # Image specific GHC GHC 65 | RUN sudo apt-get -yq update && sudo apt-get -yq --no-install-suggests --no-install-recommends install \ 66 | ghc-8.8.1 \ 67 | && sudo apt-get autoremove -y --purge && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* 68 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /.build/ 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Continous Integration with Haskell 2 | ================================== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | 10 | Indices and tables 11 | ================== 12 | 13 | * :ref:`genindex` 14 | * :ref:`search` 15 | 16 | -------------------------------------------------------------------------------- /extras/colorful.awk: -------------------------------------------------------------------------------- 1 | function blue(s) { printf "\033[0;34m" s "\033[0m " } 2 | BEGIN { state = "output"; } 3 | /^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" } 4 | /^-----END CABAL OUTPUT-----$/ { state = "output" } 5 | !/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ { 6 | if (state == "cabal") { 7 | print blue($0) 8 | } else { 9 | print $0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /fixtures/all-versions.args: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell-CI/haskell-ci/399a45cabc849d73e60227a703afaaa065d5762a/fixtures/all-versions.args -------------------------------------------------------------------------------- /fixtures/all-versions.project: -------------------------------------------------------------------------------- 1 | packages: splitmix 2 | -------------------------------------------------------------------------------- /fixtures/conditionals.args: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell-CI/haskell-ci/399a45cabc849d73e60227a703afaaa065d5762a/fixtures/conditionals.args -------------------------------------------------------------------------------- /fixtures/conditionals.project: -------------------------------------------------------------------------------- 1 | packages: servant 2 | if impl(ghc >=8.6) 3 | packages: servant-client 4 | -------------------------------------------------------------------------------- /fixtures/copy-fields-all.args: -------------------------------------------------------------------------------- 1 | --copy-fields=all 2 | -------------------------------------------------------------------------------- /fixtures/copy-fields-all.project: -------------------------------------------------------------------------------- 1 | packages: 2 | servant/ 3 | servant-client/ 4 | servant-docs/ 5 | servant-server/ 6 | 7 | package servant 8 | tests: False 9 | 10 | constraints: foundation >= 0.14 11 | 12 | allow-newer: 13 | servant-js:servant 14 | allow-newer: 15 | servant-js:servant-foreign 16 | 17 | source-repository-package 18 | type: git 19 | location: https://github.com/haskell-servant/servant-auth 20 | tag: 4a134c3db79293d28f8b74a02863047e41fbaf56 21 | -------------------------------------------------------------------------------- /fixtures/copy-fields-none.args: -------------------------------------------------------------------------------- 1 | --copy-fields=none 2 | -------------------------------------------------------------------------------- /fixtures/copy-fields-none.project: -------------------------------------------------------------------------------- 1 | packages: 2 | servant/ 3 | servant-client/ 4 | servant-docs/ 5 | servant-server/ 6 | 7 | package servant 8 | tests: False 9 | 10 | constraints: foundation >= 0.14 11 | 12 | allow-newer: 13 | servant-js:servant 14 | allow-newer: 15 | servant-js:servant-foreign 16 | -------------------------------------------------------------------------------- /fixtures/copy-fields-some.args: -------------------------------------------------------------------------------- 1 | --copy-fields=some 2 | -------------------------------------------------------------------------------- /fixtures/copy-fields-some.project: -------------------------------------------------------------------------------- 1 | packages: 2 | servant/ 3 | servant-client/ 4 | servant-docs/ 5 | servant-server/ 6 | 7 | package servant 8 | tests: False 9 | 10 | constraints: foundation >= 0.14 11 | 12 | allow-newer: 13 | servant-js:servant 14 | allow-newer: 15 | servant-js:servant-foreign 16 | -------------------------------------------------------------------------------- /fixtures/doctest-version.args: -------------------------------------------------------------------------------- 1 | --doctest 2 | --doctest-version=^>=0.20 3 | -------------------------------------------------------------------------------- /fixtures/doctest-version.project: -------------------------------------------------------------------------------- 1 | packages: splitmix 2 | -------------------------------------------------------------------------------- /fixtures/doctest.args: -------------------------------------------------------------------------------- 1 | --doctest 2 | -------------------------------------------------------------------------------- /fixtures/doctest.project: -------------------------------------------------------------------------------- 1 | packages: splitmix 2 | -------------------------------------------------------------------------------- /fixtures/empty-line.args: -------------------------------------------------------------------------------- 1 | --ghc-head 2 | -------------------------------------------------------------------------------- /fixtures/empty-line.project: -------------------------------------------------------------------------------- 1 | packages: 2 | servant/ 3 | servant-client/ 4 | servant-docs/ 5 | servant-server/ 6 | 7 | constraints: foundatiion >= 0.14 8 | allow-newer: 9 | servant-js:servant 10 | allow-newer: 11 | servant-js:servant-foreign 12 | allow-newer: 13 | -------------------------------------------------------------------------------- /fixtures/enabled-jobs.args: -------------------------------------------------------------------------------- 1 | --enabled-jobs=>=8 2 | -------------------------------------------------------------------------------- /fixtures/enabled-jobs.project: -------------------------------------------------------------------------------- 1 | packages: splitmix 2 | -------------------------------------------------------------------------------- /fixtures/fail-versions.args: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haskell-CI/haskell-ci/399a45cabc849d73e60227a703afaaa065d5762a/fixtures/fail-versions.args -------------------------------------------------------------------------------- /fixtures/fail-versions.project: -------------------------------------------------------------------------------- 1 | packages: servant/ 2 | servant-client/ 3 | servant-client-core/ 4 | servant-docs/ 5 | servant-foreign/ 6 | servant-server/ 7 | -------------------------------------------------------------------------------- /fixtures/irc-channels.args: -------------------------------------------------------------------------------- 1 | --irc-channels=irc.libera.chat#mychannel 2 | -------------------------------------------------------------------------------- /fixtures/irc-channels.project: -------------------------------------------------------------------------------- 1 | packages: servant 2 | -------------------------------------------------------------------------------- /fixtures/messy.args: -------------------------------------------------------------------------------- 1 | --ghc-head 2 | --apt=fftw3-dev 3 | --installed=-all +deepseq 4 | -------------------------------------------------------------------------------- /fixtures/messy.project: -------------------------------------------------------------------------------- 1 | packages: servant/, 2 | 3 | servant-client/ 4 | 5 | , 6 | servant-docs/,servant-server/ 7 | -------------------------------------------------------------------------------- /fixtures/psql.args: -------------------------------------------------------------------------------- 1 | --postgresql 2 | -------------------------------------------------------------------------------- /fixtures/psql.project: -------------------------------------------------------------------------------- 1 | packages: servant 2 | -------------------------------------------------------------------------------- /fixtures/servant-client-core/servant-client-core.cabal: -------------------------------------------------------------------------------- 1 | name: servant-client-core 2 | version: 0.11 3 | synopsis: Core functionality and class for client function generation for servant APIs 4 | description: 5 | This library provides backend-agnostic generation of client functions. For 6 | more information, see the README. 7 | license: BSD3 8 | license-file: LICENSE 9 | author: Servant Contributors 10 | maintainer: haskell-servant-maintainers@googlegroups.com 11 | homepage: http://haskell-servant.readthedocs.org/ 12 | bug-reports: http://github.com/haskell-servant/servant/issues 13 | cabal-version: >=1.10 14 | copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2017 Servant Contributors 15 | category: Web 16 | build-type: Simple 17 | tested-with: GHC >= 7.10 && <9 18 | extra-source-files: 19 | include/*.h 20 | CHANGELOG.md 21 | README.md 22 | source-repository head 23 | type: git 24 | location: http://github.com/haskell-servant/servant.git 25 | 26 | library 27 | exposed-modules: 28 | Servant.Client.Core 29 | Servant.Client.Core.Reexport 30 | Servant.Client.Core.Internal.Auth 31 | Servant.Client.Core.Internal.BaseUrl 32 | Servant.Client.Core.Internal.BasicAuth 33 | Servant.Client.Core.Internal.Generic 34 | Servant.Client.Core.Internal.HasClient 35 | Servant.Client.Core.Internal.Request 36 | Servant.Client.Core.Internal.RunClient 37 | build-depends: 38 | base >= 4.7 && < 4.11 39 | , base-compat >= 0.9.1 && < 0.10 40 | , base64-bytestring >= 1.0.0.1 && < 1.1 41 | , bytestring >= 0.10 && < 0.11 42 | , containers >= 0.5 && < 0.6 43 | , exceptions >= 0.8 && < 0.9 44 | , generics-sop >= 0.1.0.0 && < 0.4 45 | , http-api-data >= 0.3.6 && < 0.4 46 | , http-media >= 0.6.2 && < 0.8 47 | , http-types >= 0.8.6 && < 0.10 48 | , mtl >= 2.1 && < 2.3 49 | , network-uri >= 2.6 && < 2.7 50 | , safe >= 0.3.9 && < 0.4 51 | , servant == 0.11.* 52 | , text >= 1.2 && < 1.3 53 | if !impl(ghc >= 8.0) 54 | build-depends: 55 | semigroups >=0.16.2.2 && <0.19 56 | hs-source-dirs: src 57 | default-language: Haskell2010 58 | ghc-options: -Wall 59 | include-dirs: include 60 | 61 | test-suite spec 62 | type: exitcode-stdio-1.0 63 | ghc-options: -Wall 64 | default-language: Haskell2010 65 | hs-source-dirs: test 66 | main-is: Spec.hs 67 | build-depends: 68 | base 69 | , base-compat 70 | , deepseq 71 | , servant-client-core 72 | , hspec == 2.* 73 | , QuickCheck >= 2.7 && < 2.11 74 | other-modules: 75 | Servant.Client.Core.Internal.BaseUrlSpec 76 | -------------------------------------------------------------------------------- /fixtures/servant-client/servant-client.cabal: -------------------------------------------------------------------------------- 1 | name: servant-client 2 | version: 0.11 3 | synopsis: automatical derivation of querying functions for servant webservices 4 | description: 5 | This library lets you derive automatically Haskell functions that 6 | let you query each endpoint of a webservice. 7 | . 8 | See . 9 | . 10 | 11 | license: BSD3 12 | license-file: LICENSE 13 | author: Servant Contributors 14 | maintainer: haskell-servant-maintainers@googlegroups.com 15 | copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2017 Servant Contributors 16 | category: Servant, Web 17 | build-type: Simple 18 | cabal-version: >=1.10 19 | tested-with: GHC >= 7.8 && <9 20 | homepage: http://haskell-servant.readthedocs.org/ 21 | Bug-reports: http://github.com/haskell-servant/servant/issues 22 | extra-source-files: 23 | include/*.h 24 | CHANGELOG.md 25 | README.md 26 | source-repository head 27 | type: git 28 | location: http://github.com/haskell-servant/servant.git 29 | 30 | library 31 | exposed-modules: 32 | Servant.Client 33 | Servant.Client.Internal.HttpClient 34 | build-depends: 35 | base >= 4.7 && < 4.11 36 | , base-compat >= 0.9.1 && < 0.10 37 | , bytestring >= 0.10 && < 0.11 38 | , aeson >= 0.7 && < 1.3 39 | , attoparsec >= 0.12 && < 0.14 40 | , containers >= 0.5 && < 0.6 41 | , http-client >= 0.4.30 && < 0.6 42 | , http-client-tls >= 0.2.2 && < 0.4 43 | , http-media >= 0.6.2 && < 0.8 44 | , http-types >= 0.8.6 && < 0.10 45 | , exceptions >= 0.8 && < 0.9 46 | , monad-control >= 1.0.0.4 && < 1.1 47 | , mtl >= 2.1 && < 2.3 48 | , semigroupoids >= 4.3 && < 5.3 49 | , servant-client-core == 0.11.* 50 | , text >= 1.2 && < 1.3 51 | , transformers >= 0.3 && < 0.6 52 | , transformers-base >= 0.4.4 && < 0.5 53 | , transformers-compat >= 0.4 && < 0.6 54 | hs-source-dirs: src 55 | default-language: Haskell2010 56 | ghc-options: -Wall 57 | if impl(ghc >= 8.0) 58 | ghc-options: -Wno-redundant-constraints 59 | include-dirs: include 60 | 61 | test-suite spec 62 | type: exitcode-stdio-1.0 63 | ghc-options: -Wall 64 | default-language: Haskell2010 65 | hs-source-dirs: test 66 | main-is: Spec.hs 67 | other-modules: 68 | Servant.ClientSpec 69 | build-depends: 70 | base == 4.* 71 | , aeson 72 | , base-compat 73 | , bytestring 74 | , containers 75 | , deepseq 76 | , hspec == 2.* 77 | , http-api-data 78 | , http-client 79 | , http-media 80 | , http-types 81 | , HUnit 82 | , mtl 83 | , network >= 2.6 84 | , QuickCheck >= 2.7 85 | , servant 86 | , servant-client 87 | , servant-client-core 88 | , servant-server == 0.11.* 89 | , text 90 | , transformers 91 | , transformers-compat 92 | , wai 93 | , warp 94 | , generics-sop 95 | -------------------------------------------------------------------------------- /fixtures/servant-docs/servant-docs.cabal: -------------------------------------------------------------------------------- 1 | name: servant-docs 2 | version: 0.11 3 | synopsis: generate API docs for your servant webservice 4 | description: 5 | Library for generating API docs from a servant API definition. 6 | . 7 | Runnable example . 8 | . 9 | 10 | license: BSD3 11 | license-file: LICENSE 12 | author: Servant Contributors 13 | maintainer: haskell-servant-maintainers@googlegroups.com 14 | copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors 15 | category: Servant, Web 16 | build-type: Simple 17 | cabal-version: >=1.10 18 | tested-with: GHC >= 7.8 && <9 19 | homepage: http://haskell-servant.readthedocs.org/ 20 | Bug-reports: http://github.com/haskell-servant/servant/issues 21 | extra-source-files: 22 | include/*.h 23 | CHANGELOG.md 24 | README.md 25 | source-repository head 26 | type: git 27 | location: http://github.com/haskell-servant/servant.git 28 | 29 | library 30 | exposed-modules: 31 | Servant.Docs 32 | , Servant.Docs.Internal 33 | , Servant.Docs.Internal.Pretty 34 | build-depends: 35 | base >=4.7 && <5 36 | , base-compat >= 0.9.1 && <0.10 37 | , aeson 38 | , aeson-pretty 39 | , bytestring 40 | , case-insensitive 41 | , hashable 42 | , http-media >= 0.6 43 | , http-types >= 0.7 44 | , lens 45 | , servant == 0.11.* 46 | , string-conversions 47 | , text 48 | , unordered-containers 49 | , control-monad-omega == 0.3.* 50 | if !impl(ghc >= 8.0) 51 | build-depends: 52 | semigroups >=0.17 && <0.19 53 | hs-source-dirs: src 54 | default-language: Haskell2010 55 | ghc-options: -Wall 56 | if impl(ghc >= 8.0) 57 | ghc-options: -Wno-redundant-constraints 58 | include-dirs: include 59 | 60 | executable greet-docs 61 | main-is: greet.hs 62 | hs-source-dirs: example 63 | ghc-options: -Wall 64 | build-depends: 65 | base 66 | , aeson 67 | , lens 68 | , servant 69 | , servant-docs 70 | , string-conversions 71 | , text 72 | default-language: Haskell2010 73 | 74 | test-suite spec 75 | type: exitcode-stdio-1.0 76 | main-is: Spec.hs 77 | other-modules: Servant.DocsSpec 78 | hs-source-dirs: test 79 | ghc-options: -Wall 80 | build-depends: 81 | base 82 | , aeson 83 | , hspec 84 | , lens 85 | , servant 86 | , servant-docs 87 | , string-conversions 88 | default-language: Haskell2010 89 | -------------------------------------------------------------------------------- /fixtures/servant-foreign/servant-foreign.cabal: -------------------------------------------------------------------------------- 1 | name: servant-foreign 2 | version: 0.10.1 3 | synopsis: Helpers for generating clients for servant APIs in any programming language 4 | description: 5 | Helper types and functions for generating client functions for servant APIs in any programming language 6 | . 7 | This package provides types and functions that collect all the data needed to generate client functions in the programming language of your choice. This effectively means you only have to write the code that "pretty-prints" this data as some code in your target language. 8 | . 9 | See the servant-js package for an example 10 | . 11 | 12 | license: BSD3 13 | license-file: LICENSE 14 | author: Servant Contributors 15 | maintainer: haskell-servant-maintainers@googlegroups.com 16 | copyright: 2015-2016 Servant Contributors 17 | category: Servant, Web 18 | build-type: Simple 19 | cabal-version: >=1.10 20 | tested-with: GHC >= 7.10 && <9 21 | extra-source-files: 22 | include/*.h 23 | CHANGELOG.md 24 | README.md 25 | bug-reports: http://github.com/haskell-servant/servant/issues 26 | source-repository head 27 | type: git 28 | location: http://github.com/haskell-servant/servant.git 29 | 30 | library 31 | exposed-modules: Servant.Foreign 32 | , Servant.Foreign.Internal 33 | , Servant.Foreign.Inflections 34 | build-depends: base == 4.* 35 | , lens == 4.* 36 | , servant == 0.11.* 37 | , text >= 1.2 && < 1.3 38 | , http-types 39 | hs-source-dirs: src 40 | default-language: Haskell2010 41 | ghc-options: -Wall 42 | if impl(ghc >= 8.0) 43 | ghc-options: -Wno-redundant-constraints 44 | include-dirs: include 45 | default-extensions: CPP 46 | , ConstraintKinds 47 | , DataKinds 48 | , FlexibleContexts 49 | , FlexibleInstances 50 | , GeneralizedNewtypeDeriving 51 | , MultiParamTypeClasses 52 | , ScopedTypeVariables 53 | , StandaloneDeriving 54 | , TemplateHaskell 55 | , TypeFamilies 56 | , TypeOperators 57 | , UndecidableInstances 58 | , OverloadedStrings 59 | , PolyKinds 60 | 61 | 62 | test-suite spec 63 | type: exitcode-stdio-1.0 64 | hs-source-dirs: test 65 | ghc-options: -Wall 66 | include-dirs: include 67 | main-is: Spec.hs 68 | other-modules: Servant.ForeignSpec 69 | build-depends: base 70 | , hspec >= 2.1.8 71 | , servant 72 | , servant-foreign 73 | default-language: Haskell2010 74 | default-extensions: ConstraintKinds 75 | , DataKinds 76 | , FlexibleContexts 77 | , FlexibleInstances 78 | , GeneralizedNewtypeDeriving 79 | , MultiParamTypeClasses 80 | , ScopedTypeVariables 81 | , TypeFamilies 82 | , TypeOperators 83 | , UndecidableInstances 84 | , OverloadedStrings 85 | , PolyKinds 86 | -------------------------------------------------------------------------------- /fixtures/servant-server/servant-server.cabal: -------------------------------------------------------------------------------- 1 | name: servant-server 2 | version: 0.11 3 | synopsis: A family of combinators for defining webservices APIs and serving them 4 | description: 5 | A family of combinators for defining webservices APIs and serving them 6 | . 7 | You can learn about the basics in the . 8 | . 9 | 10 | is a runnable example, with comments, that defines a dummy API and implements 11 | a webserver that serves this API, using this package. 12 | . 13 | 14 | homepage: http://haskell-servant.readthedocs.org/ 15 | Bug-reports: http://github.com/haskell-servant/servant/issues 16 | license: BSD3 17 | license-file: LICENSE 18 | author: Servant Contributors 19 | maintainer: haskell-servant-maintainers@googlegroups.com 20 | copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors 21 | category: Servant, Web 22 | build-type: Custom 23 | cabal-version: >=1.10 24 | tested-with: GHC >= 7.8 && <9 25 | extra-source-files: 26 | include/*.h 27 | CHANGELOG.md 28 | README.md 29 | source-repository head 30 | type: git 31 | location: http://github.com/haskell-servant/servant.git 32 | 33 | custom-setup 34 | setup-depends: 35 | base >= 4 && <5, 36 | Cabal, 37 | cabal-doctest >= 1.0.1 && <1.1 38 | 39 | library 40 | exposed-modules: 41 | Servant 42 | Servant.Server 43 | Servant.Server.Experimental.Auth 44 | Servant.Server.Internal 45 | Servant.Server.Internal.BasicAuth 46 | Servant.Server.Internal.Context 47 | Servant.Server.Internal.Handler 48 | Servant.Server.Internal.Router 49 | Servant.Server.Internal.RoutingApplication 50 | Servant.Server.Internal.ServantErr 51 | Servant.Utils.StaticFiles 52 | build-depends: 53 | base >= 4.7 && < 4.11 54 | , base-compat >= 0.9 && < 0.10 55 | , aeson >= 0.7 && < 1.3 56 | , attoparsec >= 0.12 && < 0.14 57 | , base64-bytestring >= 1.0 && < 1.1 58 | , bytestring >= 0.10 && < 0.11 59 | , containers >= 0.5 && < 0.6 60 | , exceptions >= 0.8 && < 0.9 61 | , http-api-data >= 0.3 && < 0.4 62 | , http-types >= 0.8 && < 0.10 63 | , network-uri >= 2.6 && < 2.7 64 | , monad-control >= 1.0.0.4 && < 1.1 65 | , mtl >= 2 && < 2.3 66 | , network >= 2.6 && < 2.7 67 | , safe >= 0.3 && < 0.4 68 | , servant == 0.11.* 69 | , split >= 0.2 && < 0.3 70 | , string-conversions >= 0.3 && < 0.5 71 | , system-filepath >= 0.4 && < 0.5 72 | , filepath >= 1 && < 1.5 73 | , resourcet >= 1.1.6 && <1.2 74 | , tagged >= 0.7.3 && <0.9 75 | , text >= 1.2 && < 1.3 76 | , transformers >= 0.3 && < 0.6 77 | , transformers-base >= 0.4.4 && < 0.5 78 | , transformers-compat>= 0.4 && < 0.6 79 | , wai >= 3.0 && < 3.3 80 | , wai-app-static >= 3.1 && < 3.2 81 | , warp >= 3.0 && < 3.3 82 | , word8 >= 0.1 && < 0.2 83 | 84 | hs-source-dirs: src 85 | default-language: Haskell2010 86 | ghc-options: -Wall 87 | if impl(ghc >= 8.0) 88 | ghc-options: -Wno-redundant-constraints 89 | include-dirs: include 90 | 91 | executable greet 92 | main-is: greet.hs 93 | hs-source-dirs: example 94 | ghc-options: -Wall 95 | default-language: Haskell2010 96 | build-depends: 97 | base 98 | , servant 99 | , servant-server 100 | , aeson 101 | , warp 102 | , wai 103 | , text 104 | 105 | test-suite spec 106 | type: exitcode-stdio-1.0 107 | ghc-options: -Wall 108 | default-language: Haskell2010 109 | hs-source-dirs: test 110 | main-is: Spec.hs 111 | other-modules: 112 | Servant.ArbitraryMonadServerSpec 113 | Servant.Server.ErrorSpec 114 | Servant.Server.Internal.ContextSpec 115 | Servant.Server.Internal.RoutingApplicationSpec 116 | Servant.Server.RouterSpec 117 | Servant.Server.StreamingSpec 118 | Servant.Server.UsingContextSpec 119 | Servant.Server.UsingContextSpec.TestCombinators 120 | Servant.ServerSpec 121 | Servant.Utils.StaticFilesSpec 122 | build-depends: 123 | base == 4.* 124 | , base-compat 125 | , aeson 126 | , base64-bytestring 127 | , bytestring 128 | , directory 129 | , exceptions 130 | , hspec == 2.* 131 | , hspec-wai >= 0.8 && <0.9 132 | , http-types 133 | , mtl 134 | , network >= 2.6 135 | , parsec 136 | , QuickCheck 137 | , resourcet 138 | , safe 139 | , servant 140 | , servant-server 141 | , should-not-typecheck == 2.1.* 142 | , string-conversions 143 | , temporary 144 | , text 145 | , transformers 146 | , transformers-compat 147 | , wai 148 | , wai-extra 149 | , warp 150 | 151 | test-suite doctests 152 | build-depends: base 153 | , servant 154 | , doctest 155 | , filemanip 156 | , directory 157 | , filepath 158 | type: exitcode-stdio-1.0 159 | main-is: test/doctests.hs 160 | buildable: True 161 | default-language: Haskell2010 162 | ghc-options: -Wall -threaded 163 | if impl(ghc >= 8.2) 164 | x-doctest-options: -fdiagnostics-color=never 165 | include-dirs: include 166 | -------------------------------------------------------------------------------- /fixtures/servant/servant.cabal: -------------------------------------------------------------------------------- 1 | name: servant 2 | version: 0.11 3 | synopsis: A family of combinators for defining webservices APIs 4 | description: 5 | A family of combinators for defining webservices APIs and serving them 6 | . 7 | You can learn about the basics in the . 8 | . 9 | 10 | homepage: http://haskell-servant.readthedocs.org/ 11 | Bug-reports: http://github.com/haskell-servant/servant/issues 12 | license: BSD3 13 | license-file: LICENSE 14 | author: Servant Contributors 15 | maintainer: haskell-servant-maintainers@googlegroups.com 16 | copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors 17 | category: Servant, Web 18 | build-type: Custom 19 | cabal-version: >=1.10 20 | tested-with: GHC >= 7.8 && <9 21 | extra-source-files: 22 | include/*.h 23 | CHANGELOG.md 24 | source-repository head 25 | type: git 26 | location: http://github.com/haskell-servant/servant.git 27 | 28 | custom-setup 29 | setup-depends: 30 | base >= 4 && <5, 31 | Cabal, 32 | cabal-doctest >= 1.0.2 && <1.1 33 | 34 | library 35 | exposed-modules: 36 | Servant.API 37 | Servant.API.Alternative 38 | Servant.API.BasicAuth 39 | Servant.API.Capture 40 | Servant.API.ContentTypes 41 | Servant.API.Description 42 | Servant.API.Empty 43 | Servant.API.Experimental.Auth 44 | Servant.API.Header 45 | Servant.API.HttpVersion 46 | Servant.API.Internal.Test.ComprehensiveAPI 47 | Servant.API.IsSecure 48 | Servant.API.QueryParam 49 | Servant.API.Raw 50 | Servant.API.RemoteHost 51 | Servant.API.ReqBody 52 | Servant.API.ResponseHeaders 53 | Servant.API.Sub 54 | Servant.API.TypeLevel 55 | Servant.API.Vault 56 | Servant.API.Verbs 57 | Servant.API.WithNamedContext 58 | Servant.Utils.Links 59 | Servant.Utils.Enter 60 | build-depends: 61 | base >= 4.7 && < 4.11 62 | , base-compat >= 0.9 && < 0.10 63 | , aeson >= 0.7 && < 1.3 64 | , attoparsec >= 0.12 && < 0.14 65 | , bytestring >= 0.10 && < 0.11 66 | , case-insensitive >= 1.2 && < 1.3 67 | , http-api-data >= 0.3 && < 0.4 68 | , http-media >= 0.4 && < 0.8 69 | , http-types >= 0.8 && < 0.10 70 | , natural-transformation >= 0.4 && < 0.5 71 | , mtl >= 2.0 && < 2.3 72 | , mmorph >= 1 && < 1.2 73 | , tagged >= 0.7.3 && < 0.9 74 | , text >= 1 && < 1.3 75 | , string-conversions >= 0.3 && < 0.5 76 | , network-uri >= 2.6 && < 2.7 77 | , vault >= 0.3 && < 0.4 78 | 79 | if !impl(ghc >= 8.0) 80 | build-depends: 81 | semigroups >= 0.16 && < 0.19 82 | 83 | hs-source-dirs: src 84 | default-language: Haskell2010 85 | other-extensions: CPP 86 | , ConstraintKinds 87 | , DataKinds 88 | , DeriveDataTypeable 89 | , FlexibleInstances 90 | , FunctionalDependencies 91 | , GADTs 92 | , KindSignatures 93 | , MultiParamTypeClasses 94 | , OverlappingInstances 95 | , OverloadedStrings 96 | , PolyKinds 97 | , QuasiQuotes 98 | , RecordWildCards 99 | , ScopedTypeVariables 100 | , TemplateHaskell 101 | , TypeFamilies 102 | , TypeOperators 103 | , TypeSynonymInstances 104 | , UndecidableInstances 105 | ghc-options: -Wall 106 | if impl(ghc >= 8.0) 107 | ghc-options: -Wno-redundant-constraints 108 | include-dirs: include 109 | 110 | test-suite spec 111 | type: exitcode-stdio-1.0 112 | ghc-options: -Wall 113 | default-language: Haskell2010 114 | hs-source-dirs: test 115 | main-is: Spec.hs 116 | other-modules: 117 | Servant.API.ContentTypesSpec 118 | Servant.API.ResponseHeadersSpec 119 | Servant.Utils.LinksSpec 120 | Servant.Utils.EnterSpec 121 | build-depends: 122 | base == 4.* 123 | , base-compat 124 | , aeson 125 | , aeson-compat >=0.3.3 && <0.4 126 | , attoparsec 127 | , bytestring 128 | , hspec == 2.* 129 | , QuickCheck 130 | , quickcheck-instances 131 | , servant 132 | , string-conversions 133 | , text 134 | , url 135 | 136 | if !impl(ghc >= 8.0) 137 | build-depends: 138 | semigroups >= 0.16 && < 0.19 139 | 140 | test-suite doctests 141 | build-depends: base 142 | , servant 143 | , doctest 144 | , filemanip 145 | , directory 146 | , filepath 147 | , hspec 148 | type: exitcode-stdio-1.0 149 | main-is: test/doctests.hs 150 | buildable: True 151 | default-language: Haskell2010 152 | ghc-options: -Wall -threaded 153 | if impl(ghc >= 8.2) 154 | x-doctest-options: -fdiagnostics-color=never 155 | include-dirs: include 156 | x-doctest-source-dirs: test 157 | x-doctest-modules: Servant.Utils.LinksSpec 158 | -------------------------------------------------------------------------------- /fixtures/travis-patch.args: -------------------------------------------------------------------------------- 1 | --travis-patches=travis-patch.patch 2 | -------------------------------------------------------------------------------- /fixtures/travis-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git a/fixtures/cabal.project.travis-patch.travis.yml b/fixtures/cabal.project.travis-patch.travis.yml 2 | index 9a725c1..0554fed 100644 3 | --- a/fixtures/cabal.project.travis-patch.travis.yml 4 | +++ b/fixtures/cabal.project.travis-patch.travis.yml 5 | @@ -8,7 +8,7 @@ 6 | # 7 | language: c 8 | dist: jammy 9 | -git: 10 | + git: 11 | # whether to recursively clone submodules 12 | submodules: false 13 | cache: 14 | -------------------------------------------------------------------------------- /fixtures/travis-patch.project: -------------------------------------------------------------------------------- 1 | packages: servant 2 | -------------------------------------------------------------------------------- /src/HaskellCI/Auxiliary.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE MultiWayIf #-} 2 | {-# LANGUAGE NamedFieldPuns #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE RecordWildCards #-} 5 | module HaskellCI.Auxiliary ( 6 | Auxiliary (..), 7 | auxiliary, 8 | pkgNameDirVariable', 9 | pkgNameDirVariable, 10 | ) where 11 | 12 | import HaskellCI.Prelude 13 | 14 | import qualified Data.Set as S 15 | import qualified Distribution.CabalSpecVersion as C 16 | import qualified Distribution.FieldGrammar.Pretty as C 17 | import qualified Distribution.Fields.Pretty as C 18 | import qualified Distribution.Pretty as C 19 | import qualified Distribution.Types.GenericPackageDescription as C 20 | import qualified Distribution.Types.Version as C 21 | import qualified Distribution.Types.VersionRange as C 22 | import qualified Network.URI as URI 23 | import qualified Text.PrettyPrint as PP 24 | 25 | import Cabal.Optimization 26 | import Cabal.Project 27 | import Cabal.SourceRepo 28 | import HaskellCI.Compiler 29 | import HaskellCI.Config 30 | import HaskellCI.Config.Components 31 | import HaskellCI.Config.CopyFields 32 | import HaskellCI.Config.Docspec 33 | import HaskellCI.Config.Doctest 34 | import HaskellCI.Jobs 35 | import HaskellCI.List 36 | import HaskellCI.Package 37 | 38 | -- | Auxiliary definitions, probably useful for all backends 39 | data Auxiliary = Auxiliary 40 | { pkgs :: [Package] 41 | , uris :: [URI] 42 | , projectName :: String 43 | , doctestEnabled :: Bool 44 | , docspecEnabled :: Bool 45 | , hasTests :: CompilerRange 46 | , hasLibrary :: Bool 47 | , extraCabalProjectFields :: FilePath -> [C.PrettyField ()] 48 | , testShowDetails :: String 49 | , anyJobUsesHeadHackage :: Bool 50 | , runHaddock :: Bool 51 | , haddockFlags :: String 52 | , cabalPrerelease :: Bool 53 | } 54 | 55 | auxiliary :: Config -> Project URI Void Package -> JobVersions -> Auxiliary 56 | auxiliary Config {..} prj JobVersions {..} = Auxiliary {..} 57 | where 58 | pkgs = prjPackages prj 59 | uris = prjUriPackages prj 60 | projectName = fromMaybe "ci" (cfgProjectName <|> pkgName <$> listToMaybe pkgs) 61 | 62 | doctestEnabled = any (maybeGHC False (`C.withinRange` cfgDoctestEnabled cfgDoctest)) linuxVersions 63 | docspecEnabled = any (maybeGHC False (`C.withinRange` cfgDocspecEnabled cfgDocspec)) linuxVersions 64 | 65 | testShowDetails 66 | | cfgTestOutputDirect = " --test-show-details=direct" 67 | | otherwise = "" 68 | 69 | -- version range which has tests 70 | hasTests :: CompilerRange 71 | hasTests = RangePoints $ S.unions 72 | [ pkgJobs 73 | | Pkg{pkgGpd,pkgJobs} <- pkgs 74 | , not $ null $ C.condTestSuites pkgGpd 75 | ] 76 | 77 | hasLibrary = any (\Pkg{pkgGpd} -> isJust $ C.condLibrary pkgGpd) pkgs 78 | 79 | runHaddock = not (equivVersionRanges C.noVersion cfgHaddock) 80 | && case cfgHaddockComponents of 81 | ComponentsAll -> True 82 | ComponentsLibs -> hasLibrary 83 | 84 | haddockFlags = case cfgHaddockComponents of 85 | ComponentsAll -> " --haddock-all" 86 | ComponentsLibs -> "" 87 | 88 | extraCabalProjectFields :: FilePath -> [C.PrettyField ()] 89 | extraCabalProjectFields rootdir = buildList $ do 90 | -- generate package fields for URI packages. 91 | for_ uris $ \uri -> 92 | item $ C.PrettyField () "packages" $ PP.text $ case URI.uriScheme uri of 93 | "file:" -> rootdir ++ URI.uriPath uri 94 | _ -> uriToString id uri "" 95 | 96 | -- copy fields from original cabal.project 97 | case cfgCopyFields of 98 | CopyFieldsNone -> pure () 99 | CopyFieldsSome -> copyFieldsSome 100 | CopyFieldsAll -> copyFieldsSome *> traverse_ item (prjOtherFields prj) 101 | 102 | -- local ghc-options 103 | unless (null cfgLocalGhcOptions) $ for_ pkgs $ \Pkg{pkgName} -> do 104 | let s = unwords $ map (show . C.showToken) cfgLocalGhcOptions 105 | item $ C.PrettySection () "package" [PP.text pkgName] $ buildList $ 106 | item $ C.PrettyField () "ghc-options" $ PP.text s 107 | 108 | -- raw-project is after local-ghc-options so we can override per package. 109 | traverse_ item cfgRawProject 110 | where 111 | copyFieldsSome :: ListBuilder (C.PrettyField ()) () 112 | copyFieldsSome = do 113 | for_ (prjConstraints prj) $ \xs -> do 114 | let s = concat (lines xs) 115 | item $ C.PrettyField () "constraints" $ PP.text s 116 | 117 | for_ (prjAllowNewer prj) $ \xs -> do 118 | let s = concat (lines xs) 119 | item $ C.PrettyField () "allow-newer" $ PP.text s 120 | 121 | when (prjReorderGoals prj) $ 122 | item $ C.PrettyField () "reorder-goals" $ PP.text "True" 123 | 124 | for_ (prjMaxBackjumps prj) $ \bj -> 125 | item $ C.PrettyField () "max-backjumps" $ PP.text $ show bj 126 | 127 | case prjOptimization prj of 128 | OptimizationOn -> return () 129 | OptimizationOff -> item $ C.PrettyField () "optimization" $ PP.text "False" 130 | OptimizationLevel l -> item $ C.PrettyField () "optimization" $ PP.text $ show l 131 | 132 | for_ (prjSourceRepos prj) $ \repo -> 133 | item $ C.PrettySection () "source-repository-package" [] $ 134 | C.prettyFieldGrammar C.cabalSpecLatest sourceRepositoryPackageGrammar (srpHoist toList repo) 135 | 136 | -- GHC versions which need head.hackage 137 | headGhcVers :: Set CompilerVersion 138 | headGhcVers = S.filter (usesHeadHackage cfgHeadHackage) allVersions 139 | 140 | anyJobUsesHeadHackage :: Bool 141 | anyJobUsesHeadHackage = not $ null headGhcVers 142 | 143 | cabalPrerelease :: Bool 144 | cabalPrerelease = case C.versionNumbers $ fromMaybe C.nullVersion cfgCabalInstallVersion of 145 | _ : m : _ | odd m -> True 146 | _ -> False 147 | 148 | pkgNameDirVariable' :: String -> String 149 | pkgNameDirVariable' n = "PKGDIR_" ++ map f n where 150 | f '-' = '_' 151 | f c = c 152 | 153 | pkgNameDirVariable :: String -> String 154 | pkgNameDirVariable n = "${" ++ pkgNameDirVariable' n ++ "}" 155 | -------------------------------------------------------------------------------- /src/HaskellCI/Cabal.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Cabal where 2 | -------------------------------------------------------------------------------- /src/HaskellCI/Cli.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | 3 | -- | Most of client interface. 4 | module HaskellCI.Cli where 5 | 6 | import HaskellCI.Prelude 7 | 8 | import System.Exit (exitFailure) 9 | import System.FilePath.Posix (takeFileName) 10 | import System.IO (hPutStrLn, stderr) 11 | 12 | import qualified Options.Applicative as O 13 | 14 | import HaskellCI.Config 15 | import HaskellCI.OptparseGrammar 16 | import HaskellCI.VersionInfo 17 | 18 | ------------------------------------------------------------------------------- 19 | -- Command 20 | ------------------------------------------------------------------------------- 21 | 22 | data Command 23 | = CommandBash FilePath 24 | | CommandGitHub FilePath 25 | | CommandRegenerate 26 | | CommandListGHC 27 | | CommandDumpConfig 28 | | CommandVersionInfo 29 | deriving Show 30 | 31 | ------------------------------------------------------------------------------- 32 | -- Options 33 | ------------------------------------------------------------------------------- 34 | 35 | data Options = Options 36 | { optOutput :: Maybe Output 37 | , optConfig :: ConfigOpt 38 | , optCwd :: Maybe FilePath 39 | , optInputType :: Maybe InputType 40 | , optConfigMorphism :: Config -> Config 41 | } 42 | 43 | instance Semigroup Options where 44 | Options b d c e f <> Options b' d' c' e' f' = 45 | Options (b <|> b') (d <> d') (c <|> c') (e <|> e') (f' . f) 46 | 47 | defaultOptions :: Options 48 | defaultOptions = Options 49 | { optOutput = Nothing 50 | , optConfig = ConfigOptAuto 51 | , optCwd = Nothing 52 | , optInputType = Nothing 53 | , optConfigMorphism = id 54 | } 55 | 56 | optionsWithOutputFile :: FilePath -> Options 57 | optionsWithOutputFile fp = defaultOptions 58 | { optOutput = Just (OutputFile fp) 59 | } 60 | 61 | data Output = OutputStdout | OutputFile FilePath 62 | 63 | data ConfigOpt 64 | = ConfigOptAuto 65 | | ConfigOpt FilePath 66 | | ConfigOptNo 67 | deriving (Eq, Show) 68 | 69 | instance Semigroup ConfigOpt where 70 | a <> ConfigOptAuto = a 71 | _ <> b = b 72 | 73 | ------------------------------------------------------------------------------- 74 | -- InputType 75 | ------------------------------------------------------------------------------- 76 | 77 | data InputType 78 | = InputTypePackage -- ^ @.cabal@ 79 | | InputTypeProject -- ^ @cabal.project 80 | deriving Show 81 | 82 | optInputType' :: Options -> FilePath -> InputType 83 | optInputType' opts path = 84 | fromMaybe def (optInputType opts) 85 | where 86 | def | "cabal.project" `isPrefixOf` takeFileName path = InputTypeProject 87 | | otherwise = InputTypePackage 88 | 89 | ------------------------------------------------------------------------------- 90 | -- Parsers 91 | ------------------------------------------------------------------------------- 92 | 93 | optionsP :: O.Parser Options 94 | optionsP = Options 95 | <$> O.optional outputP 96 | <*> configOptP 97 | <*> O.optional (O.strOption (O.long "cwd" <> O.metavar "Dir" <> O.action "directory" <> O.help "Directory to change to")) 98 | <*> O.optional inputTypeP 99 | <*> runOptparseGrammar configGrammar 100 | 101 | configOptP :: O.Parser ConfigOpt 102 | configOptP = file <|> noconfig <|> pure ConfigOptAuto 103 | where 104 | file = ConfigOpt <$> O.strOption (O.long "config" <> O.metavar "CONFIGFILE" <> O.action "file" <> O.help "Configuration file") 105 | noconfig = O.flag' ConfigOptNo (O.long "no-config" <> O.help "Don't read configuration file") 106 | 107 | outputP :: O.Parser Output 108 | outputP = 109 | OutputFile <$> O.strOption (O.long "output" <> O.short 'o' <> O.metavar "FILE" <> O.action "file" <> O.help "Output file") <|> 110 | O.flag' OutputStdout (O.long "stdout" <> O.help "Use stdout output") 111 | 112 | versionP :: O.Parser (a -> a) 113 | versionP = O.infoOption haskellCIVerStr $ mconcat 114 | [ O.long "version" 115 | , O.short 'V' 116 | , O.help "Print version information" 117 | ] 118 | 119 | inputTypeP :: O.Parser InputType 120 | inputTypeP = pkg <|> prj where 121 | pkg = O.flag' InputTypePackage $ O.long "package" 122 | prj = O.flag' InputTypeProject $ O.long "project" 123 | 124 | cliParserInfo :: O.ParserInfo (Command, Options) 125 | cliParserInfo = O.info ((,) <$> cmdP <*> optionsP O.<**> versionP O.<**> O.helper) $ mconcat 126 | [ O.fullDesc 127 | , O.header "haskell-ci - generate CI scripts for Haskell projects" 128 | ] 129 | where 130 | cmdP = O.subparser (mconcat 131 | [ O.command "regenerate" $ O.info (pure CommandRegenerate) $ O.progDesc "Regenerate outputs" 132 | , O.command "bash" $ O.info bashP $ O.progDesc "Generate local-bash-docker script" 133 | , O.command "github" $ O.info githubP $ O.progDesc "Generate GitHub Actions config" 134 | , O.command "list-ghc" $ O.info (pure CommandListGHC) $ O.progDesc "List known GHC versions" 135 | , O.command "dump-config" $ O.info (pure CommandDumpConfig) $ O.progDesc "Dump cabal.haskell-ci config with default values" 136 | , O.command "version-info" $ O.info (pure CommandVersionInfo) $ O.progDesc "Print versions info haskell-ci was compiled with" 137 | ]) 138 | 139 | bashP = CommandBash 140 | <$> O.strArgument (O.metavar "CABAL.FILE" <> O.action "file" <> O.help "Either or cabal.project") 141 | 142 | githubP = CommandGitHub 143 | <$> O.strArgument (O.metavar "CABAL.FILE" <> O.action "file" <> O.help "Either or cabal.project") 144 | 145 | ------------------------------------------------------------------------------- 146 | -- Parsing helpers 147 | ------------------------------------------------------------------------------- 148 | 149 | parseOptions :: [String] -> IO (FilePath, Options) 150 | parseOptions argv = case res of 151 | O.Success (cmd, opts) -> do 152 | path <- fromCmd cmd 153 | return (path, opts) 154 | O.Failure f -> case O.renderFailure f "haskell-ci" of 155 | (help, _) -> hPutStrLn stderr help >> exitFailure 156 | O.CompletionInvoked _ -> exitFailure -- unexpected 157 | where 158 | res = O.execParserPure (O.prefs O.subparserInline) cliParserInfo argv 159 | 160 | fromCmd :: Command -> IO FilePath 161 | fromCmd (CommandBash fp) = return fp 162 | fromCmd (CommandGitHub fp) = return fp 163 | fromCmd cmd = fail $ "Command without filepath: " ++ show cmd 164 | -------------------------------------------------------------------------------- /src/HaskellCI/Config.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | {-# LANGUAGE MultiParamTypeClasses #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | {-# LANGUAGE TypeApplications #-} 7 | module HaskellCI.Config ( 8 | Config (..), 9 | configGrammar, 10 | emptyConfig, 11 | readConfigFile, 12 | ) where 13 | 14 | import HaskellCI.Prelude 15 | 16 | import qualified Data.ByteString as BS 17 | import qualified Distribution.CabalSpecVersion as C 18 | import qualified Distribution.FieldGrammar as C 19 | import qualified Distribution.Fields as C 20 | import qualified Distribution.Parsec as C 21 | 22 | import HaskellCI.Config.ConstraintSet 23 | import HaskellCI.Config.Empty 24 | import HaskellCI.Config.Grammar 25 | import HaskellCI.Config.Type 26 | import HaskellCI.Config.Ubuntu 27 | import HaskellCI.ParsecUtils 28 | 29 | emptyConfig :: Config 30 | emptyConfig = case runEG configGrammar of 31 | Left xs -> error $ "Required fields: " ++ show xs 32 | Right x -> postprocessConfig x 33 | 34 | ------------------------------------------------------------------------------- 35 | -- Reading 36 | ------------------------------------------------------------------------------- 37 | 38 | readConfigFile :: MonadIO m => FilePath -> m Config 39 | readConfigFile = liftIO . readAndParseFile parseConfigFile 40 | 41 | parseConfigFile :: [C.Field C.Position] -> C.ParseResult Config 42 | parseConfigFile fields0 = do 43 | config <- C.parseFieldGrammar C.cabalSpecLatest fields configGrammar 44 | config' <- traverse parseSection $ concat sections 45 | return $ postprocessConfig $ foldl' (&) config config' 46 | where 47 | (fields, sections) = C.partitionFields fields0 48 | 49 | parseSection :: C.Section C.Position -> C.ParseResult (Config -> Config) 50 | parseSection (C.MkSection (C.Name pos name) args cfields) 51 | | name == "constraint-set" = do 52 | name' <- parseName pos args 53 | let (fs, _sections) = C.partitionFields cfields 54 | cs <- C.parseFieldGrammar C.cabalSpecLatest fs (constraintSetGrammar name') 55 | return $ over (field @"cfgConstraintSets") (cs :) 56 | | name == "raw-project" = do 57 | let fs = C.fromParsecFields cfields 58 | return $ over (field @"cfgRawProject") (++ map void fs) 59 | | otherwise = do 60 | C.parseWarning pos C.PWTUnknownSection $ "Unknown section " ++ fromUTF8BS name 61 | return id 62 | 63 | postprocessConfig :: Config -> Config 64 | postprocessConfig cfg 65 | | cfgUbuntu cfg >= Jammy = cfg 66 | | otherwise = cfg 67 | 68 | ------------------------------------------------------------------------------- 69 | -- From Cabal 70 | ------------------------------------------------------------------------------- 71 | 72 | parseName :: C.Position -> [C.SectionArg C.Position] -> C.ParseResult String 73 | parseName pos args = fromUTF8BS <$> parseNameBS pos args 74 | 75 | parseNameBS :: C.Position -> [C.SectionArg C.Position] -> C.ParseResult BS.ByteString 76 | parseNameBS pos args = case args of 77 | [C.SecArgName _pos secName] -> 78 | pure secName 79 | [C.SecArgStr _pos secName] -> 80 | pure secName 81 | [] -> do 82 | C.parseFailure pos "name required" 83 | pure "" 84 | _ -> do 85 | -- TODO: pretty print args 86 | C.parseFailure pos $ "Invalid name " ++ show args 87 | pure "" 88 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Components.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Config.Components where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Compat.CharParsing as C 6 | import qualified Distribution.Parsec as C 7 | import qualified Distribution.Pretty as C 8 | import qualified Text.PrettyPrint as PP 9 | 10 | ------------------------------------------------------------------------------- 11 | -- Single action 12 | ------------------------------------------------------------------------------- 13 | 14 | data Components 15 | = ComponentsAll 16 | | ComponentsLibs 17 | deriving (Eq, Show) 18 | 19 | instance C.Pretty Components where 20 | pretty ComponentsAll = PP.text "all" 21 | pretty ComponentsLibs = PP.text "libs" 22 | 23 | instance C.Parsec Components where 24 | parsec = do 25 | t <- C.parsecToken 26 | case t of 27 | "all" -> return ComponentsAll 28 | "libs" -> return ComponentsLibs 29 | _ -> C.unexpected $ "Component " ++ t 30 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/ConstraintSet.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | module HaskellCI.Config.ConstraintSet where 5 | 6 | import HaskellCI.Prelude 7 | 8 | import qualified Distribution.FieldGrammar as C 9 | 10 | import HaskellCI.Newtypes 11 | import HaskellCI.OptionsGrammar 12 | 13 | data ConstraintSet = ConstraintSet 14 | { csName :: String 15 | , csGhcVersions :: VersionRange 16 | , csGhcjs :: Bool 17 | , csConstraints :: [String] -- we parse these simply as strings 18 | , csTests :: Bool 19 | , csRunTests :: Bool 20 | , csDocspec :: Bool 21 | , csBenchmarks :: Bool 22 | , csHaddock :: Bool 23 | , csPreferOldest :: Bool 24 | } 25 | deriving (Show, Generic) 26 | 27 | ------------------------------------------------------------------------------- 28 | -- Grammar 29 | ------------------------------------------------------------------------------- 30 | 31 | constraintSetGrammar 32 | :: ( OptionsGrammar c g, Applicative (g ConstraintSet) 33 | ) 34 | => String -> g ConstraintSet ConstraintSet 35 | constraintSetGrammar name = ConstraintSet name 36 | <$> C.optionalFieldDef "ghc" (field @"csGhcVersions") anyVersion 37 | <*> C.booleanFieldDef "ghcjs" (field @"csGhcjs") True 38 | <*> C.monoidalFieldAla "constraints" (C.alaList' C.CommaVCat NoCommas) (field @"csConstraints") 39 | <*> C.booleanFieldDef "tests" (field @"csTests") False 40 | <*> C.booleanFieldDef "run-tests" (field @"csRunTests") False 41 | <*> C.booleanFieldDef "docspec" (field @"csDocspec") False 42 | <*> C.booleanFieldDef "benchmarks" (field @"csBenchmarks") False 43 | <*> C.booleanFieldDef "haddock" (field @"csHaddock") False 44 | <*> C.booleanFieldDef "prefer-oldest" (field @"csPreferOldest") False 45 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/CopyFields.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE MultiParamTypeClasses #-} 3 | module HaskellCI.Config.CopyFields where 4 | 5 | import HaskellCI.Prelude 6 | 7 | import qualified Distribution.Compat.CharParsing as C 8 | import qualified Distribution.Parsec as C 9 | import qualified Distribution.Pretty as C 10 | import qualified Text.PrettyPrint as PP 11 | 12 | data CopyFields 13 | = CopyFieldsNone 14 | | CopyFieldsSome 15 | | CopyFieldsAll 16 | deriving (Eq, Ord, Show, Enum, Bounded) 17 | 18 | ------------------------------------------------------------------------------- 19 | -- Functions 20 | ------------------------------------------------------------------------------- 21 | 22 | showCopyFields :: CopyFields -> String 23 | showCopyFields CopyFieldsNone = "none" 24 | showCopyFields CopyFieldsSome = "some" 25 | showCopyFields CopyFieldsAll = "all" 26 | 27 | instance C.Pretty CopyFields where 28 | pretty = PP.text . showCopyFields 29 | 30 | instance C.Parsec CopyFields where 31 | parsec = C.choice 32 | [ f <$ C.string (showCopyFields f) 33 | | f <- [ minBound .. maxBound ] 34 | ] 35 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Docspec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | module HaskellCI.Config.Docspec ( 5 | DocspecConfig (..), 6 | docspecConfigGrammar, 7 | initialDocspecConfig, 8 | ) where 9 | 10 | import HaskellCI.Prelude 11 | 12 | import qualified Distribution.FieldGrammar as C 13 | 14 | import HaskellCI.GrammarDefault 15 | import HaskellCI.OptionsGrammar 16 | 17 | data DocspecConfig = DocspecConfig 18 | { cfgDocspecEnabled :: !VersionRange 19 | , cfgDocspecOptions :: [String] 20 | , cfgDocspecUrl :: String 21 | , cfgDocspecHash :: String 22 | } 23 | deriving (Show, Generic, Binary) 24 | 25 | ------------------------------------------------------------------------------- 26 | -- Default 27 | ------------------------------------------------------------------------------- 28 | 29 | initialDocspecConfig :: DocspecConfig 30 | initialDocspecConfig = DocspecConfig 31 | { cfgDocspecEnabled = noVersion 32 | , cfgDocspecOptions = [] 33 | , cfgDocspecUrl = "https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20240414/cabal-docspec-0.0.0.20240414-x86_64-linux.xz" 34 | , cfgDocspecHash = "2d18a3f79619e8ec5f11870f926f6dc2616e02a6c889315b7f82044b95a1adb9" 35 | } 36 | 37 | ------------------------------------------------------------------------------- 38 | -- Grammar 39 | ------------------------------------------------------------------------------- 40 | 41 | docspecConfigGrammar :: OptionsGrammar c g => DocspecConfig -> g DocspecConfig DocspecConfig 42 | docspecConfigGrammar def = DocspecConfig 43 | <$> rangeField "docspec" (field @"cfgDocspecEnabled") def 44 | ^^^ help "Enable Docspec job" 45 | <*> monoidalFieldAla "docspec-options" (C.alaList' C.NoCommaFSep C.Token') (field @"cfgDocspecOptions") 46 | ^^^ metahelp "OPTS" "Additional Docspec options" 47 | <*> optionalFieldDefAla "docspec-url" C.Token' (field @"cfgDocspecUrl") def 48 | ^^^ metahelp "URL" "URL to download cabal-docspec" 49 | <*> optionalFieldDefAla "docspec-hash" C.Token' (field @"cfgDocspecHash") def 50 | ^^^ metahelp "HASH" "SHA256 of cabal-docspec" 51 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Doctest.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | module HaskellCI.Config.Doctest ( 5 | DoctestConfig (..), 6 | initialDoctestConfig, 7 | doctestConfigGrammar, 8 | ) where 9 | 10 | import HaskellCI.Prelude 11 | 12 | import Distribution.Version (majorBoundVersion) 13 | 14 | import qualified Distribution.FieldGrammar as C 15 | import qualified Distribution.Types.PackageName as C 16 | 17 | import HaskellCI.GrammarDefault 18 | import HaskellCI.OptionsGrammar 19 | 20 | data DoctestConfig = DoctestConfig 21 | { cfgDoctestEnabled :: !VersionRange 22 | , cfgDoctestOptions :: [String] 23 | , cfgDoctestVersion :: !VersionRange 24 | , cfgDoctestFilterEnvPkgs :: ![C.PackageName] 25 | , cfgDoctestFilterSrcPkgs :: ![C.PackageName] 26 | } 27 | deriving (Show, Generic, Binary) 28 | 29 | ------------------------------------------------------------------------------- 30 | -- Default 31 | ------------------------------------------------------------------------------- 32 | 33 | initialDoctestConfig :: DoctestConfig 34 | initialDoctestConfig = DoctestConfig 35 | { cfgDoctestEnabled = noVersion 36 | , cfgDoctestOptions = [] 37 | , cfgDoctestVersion = majorBoundVersion (mkVersion [0,22,0]) 38 | , cfgDoctestFilterEnvPkgs = [] 39 | , cfgDoctestFilterSrcPkgs = [] 40 | } 41 | 42 | ------------------------------------------------------------------------------- 43 | -- Grammar 44 | ------------------------------------------------------------------------------- 45 | 46 | doctestConfigGrammar :: OptionsGrammar c g => DoctestConfig -> g DoctestConfig DoctestConfig 47 | doctestConfigGrammar def = DoctestConfig 48 | <$> rangeField "doctest" (field @"cfgDoctestEnabled") def 49 | ^^^ help "Enable Doctest job" 50 | <*> monoidalFieldAla "doctest-options" (C.alaList' C.NoCommaFSep C.Token') (field @"cfgDoctestOptions") 51 | ^^^ metahelp "OPTS" "Additional Doctest options" 52 | <*> optionalFieldDef "doctest-version" (field @"cfgDoctestVersion") def 53 | ^^^ metahelp "RANGE" "Doctest version" 54 | <*> monoidalFieldAla "doctest-filter-packages" (C.alaList C.NoCommaFSep) (field @"cfgDoctestFilterEnvPkgs") 55 | ^^^ metahelp "PKGS" "Filter packages from .ghc.environment file" 56 | <*> monoidalFieldAla "doctest-skip" (C.alaList C.NoCommaFSep) (field @"cfgDoctestFilterSrcPkgs") 57 | ^^^ metahelp "PKGS" "Skip doctests for these packages" 58 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Dump.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FunctionalDependencies #-} 2 | module HaskellCI.Config.Dump where 3 | 4 | import HaskellCI.Prelude 5 | 6 | import qualified Distribution.Compat.Newtype as C 7 | import qualified Distribution.FieldGrammar as C 8 | import qualified Distribution.Pretty as C 9 | 10 | import HaskellCI.OptionsGrammar 11 | 12 | -- TODO: with Cabal-2.6 this can be prettier, using Pretty.Field 13 | newtype DumpGrammar s a = DG { runDG :: [String] } 14 | deriving Functor 15 | 16 | instance Applicative (DumpGrammar s) where 17 | pure _ = DG [] 18 | DG f <*> DG x = DG (f ++ x) 19 | 20 | instance C.FieldGrammar C.Pretty DumpGrammar where 21 | blurFieldGrammar _ = coerce 22 | 23 | uniqueFieldAla _ _ _ = DG [] 24 | 25 | booleanFieldDef fn _ def = DG 26 | [ fromUTF8BS fn ++ ": " ++ C.prettyShow def 27 | , "" 28 | ] 29 | 30 | optionalFieldAla fn _ _ = DG 31 | [ fromUTF8BS fn ++ ":" 32 | , "" 33 | ] 34 | 35 | optionalFieldDefAla fn c _ def = DG 36 | [ fromUTF8BS fn ++ ": " ++ C.prettyShow (C.pack' c def) 37 | , "" 38 | ] 39 | 40 | monoidalFieldAla fn _ _ = DG 41 | [ fromUTF8BS fn ++ ":" 42 | , "" 43 | ] 44 | 45 | freeTextField fn _ = DG 46 | [ fromUTF8BS fn ++ ":" 47 | , "" 48 | ] 49 | 50 | freeTextFieldDef fn _ = DG 51 | [ fromUTF8BS fn ++ ":" 52 | , "" 53 | ] 54 | 55 | freeTextFieldDefST fn _ = DG 56 | [ fromUTF8BS fn ++ ":" 57 | , "" 58 | ] 59 | 60 | prefixedFields _ _ = pure [] 61 | knownField _ = pure () 62 | deprecatedSince _ _ = id 63 | availableSince _ _ = id 64 | removedIn _ _ = id 65 | hiddenField = id 66 | 67 | instance OptionsGrammar C.Pretty DumpGrammar where 68 | metahelp _ = help 69 | 70 | help h (DG xs) = DG $ 71 | ("-- " ++ h) : xs 72 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Empty.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FunctionalDependencies #-} 2 | module HaskellCI.Config.Empty where 3 | 4 | import HaskellCI.Prelude 5 | 6 | import qualified Distribution.FieldGrammar as C 7 | import qualified Distribution.Fields as C 8 | 9 | import HaskellCI.OptionsGrammar 10 | 11 | newtype EmptyGrammar s a = EG { runEG :: Either (NonEmpty C.FieldName) a } 12 | deriving Functor 13 | 14 | instance Applicative (EmptyGrammar s) where 15 | pure x = EG (Right x) 16 | EG f <*> EG x = EG (apVal f x) where 17 | apVal (Right g) (Right y) = Right (g y) 18 | apVal (Right _) (Left y) = Left y 19 | apVal (Left g) (Right _) = Left g 20 | apVal (Left g) (Left y) = Left (g <> y) 21 | 22 | instance C.FieldGrammar Typeable EmptyGrammar where 23 | blurFieldGrammar _ = coerce 24 | 25 | uniqueFieldAla fn _ _ = EG (Left (pure fn)) 26 | booleanFieldDef _ _ def = EG (Right def) 27 | optionalFieldAla _ _ _ = EG (Right Nothing) 28 | optionalFieldDefAla _ _ _ def = EG (Right def) 29 | monoidalFieldAla _ _ _ = EG (Right mempty) 30 | 31 | freeTextField _ _ = EG (Right Nothing) 32 | freeTextFieldDef _ _ = EG (Right "") 33 | freeTextFieldDefST _ _ = EG (Right (fromString "")) 34 | 35 | prefixedFields _ _ = pure [] 36 | knownField _ = pure () 37 | deprecatedSince _ _ = id 38 | availableSince _ _ = id 39 | removedIn _ _ = id 40 | hiddenField = id 41 | 42 | instance OptionsGrammar Typeable EmptyGrammar 43 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/History.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | module HaskellCI.Config.History ( 4 | configHistory, 5 | defaultConfig, 6 | ) where 7 | 8 | import HaskellCI.Prelude 9 | 10 | import qualified Distribution.Version as C 11 | import qualified Data.Map.Strict as Map 12 | 13 | import HaskellCI.Config.Initial 14 | import HaskellCI.Config.Type 15 | import HaskellCI.Config.Ubuntu 16 | import HaskellCI.SetupMethod 17 | 18 | import HaskellCI.Compiler (invertVersionRange) 19 | 20 | configHistory :: [([Int], Config -> Config)] 21 | configHistory = 22 | [ ver 0 19 20240414 := \cfg -> cfg 23 | & field @"cfgDocspec" . field @"cfgDocspecUrl" .~ "https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20240414/cabal-docspec-0.0.0.20240414-x86_64-linux.xz" 24 | & field @"cfgDocspec" . field @"cfgDocspecHash" .~ "2d18a3f79619e8ec5f11870f926f6dc2616e02a6c889315b7f82044b95a1adb9" 25 | , ver 0 19 20240420 := \cfg -> cfg 26 | & field @"cfgUbuntu" .~ Jammy 27 | , ver 0 19 20240513 := \cfg -> cfg 28 | -- & defaultHeadHackage .~ C.orLaterVersion (C.mkVersion [9,11]) 29 | , ver 0 19 20240702 := \cfg -> cfg 30 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,12,1,0] 31 | & field @"cfgDocspec" . field @"cfgDocspecUrl" .~ "https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20240703/cabal-docspec-0.0.0.20240703-x86_64-linux.xz" 32 | & field @"cfgDocspec" . field @"cfgDocspecHash" .~ "48bf3b7fd2f7f0caa6162afee57a755be8523e7f467b694900eb420f5f9a7b76" 33 | , ver 0 19 20240708 := \cfg -> cfg 34 | & field @"cfgGhcupVersion" .~ C.mkVersion [0,1,30,0] 35 | , ver 0 19 20241111 := \cfg -> cfg 36 | & field @"cfgSetupMethods" .~ PerSetupMethod 37 | { hvrPpa = C.earlierVersion (C.mkVersion [8,4]) 38 | , ghcup = C.simplifyVersionRange (C.laterVersion (C.mkVersion [8,4,4]) \/ C.earlierVersion (C.mkVersion [9,12,0]) \/ invertVersionRange (C.withinVersion (C.mkVersion [9,8,3]))) 39 | , ghcupVanilla = C.withinVersion (C.mkVersion [9,8,3]) 40 | , ghcupPrerelease = C.orLaterVersion (C.mkVersion [9,12,0]) 41 | } 42 | , ver 0 19 20241114 := \cfg -> cfg 43 | & field @"cfgSetupMethods" .~ PerSetupMethod 44 | { hvrPpa = C.noVersion 45 | , ghcup = invertVersionRange (C.withinVersion (C.mkVersion [9,8,3])) /\ C.earlierVersion (C.mkVersion [9,12]) 46 | , ghcupVanilla = C.withinVersion (C.mkVersion [9,8,3]) 47 | , ghcupPrerelease = C.orLaterVersion (C.mkVersion [9,12]) 48 | } 49 | , ver 0 19 20241117 := \cfg -> cfg 50 | & field @"cfgVersionMapping" .~ Map.singleton (mkVersion [9,12,1]) (mkVersion [9,12,0,20241031]) 51 | , ver 0 19 20241121 := \cfg -> cfg 52 | & field @"cfgVersionMapping" .~ Map.singleton (mkVersion [9,12,1]) (mkVersion [9,12,0,20241114]) 53 | , ver 0 19 20241202 := \cfg -> cfg 54 | & field @"cfgVersionMapping" .~ Map.singleton (mkVersion [9,12,1]) (mkVersion [9,12,0,20241128]) 55 | , ver 0 19 20241218 := \cfg -> cfg 56 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,15,0,0,2024,10,3] 57 | , ver 0 19 20241219 := \cfg -> cfg 58 | & field @"cfgVersionMapping" .~ mempty 59 | & field @"cfgSetupMethods" .~ PerSetupMethod 60 | { hvrPpa = C.noVersion 61 | , ghcup = invertVersionRange (C.withinVersion (C.mkVersion [9,8,3])) 62 | , ghcupVanilla = C.withinVersion (C.mkVersion [9,8,3]) 63 | , ghcupPrerelease = C.noVersion 64 | } 65 | , ver 0 19 20241222 := \cfg -> cfg 66 | & field @"cfgHeadHackage" .~ C.orLaterVersion (C.mkVersion [9,13]) 67 | , ver 0 19 20241223 := \cfg -> cfg 68 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,14,1,0] 69 | , ver 0 19 20250104 := \cfg -> cfg 70 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,14,1,1] 71 | 72 | -- https://github.com/haskell-CI/haskell-ci/issues/766 73 | , ver 0 19 20250115 := \cfg -> cfg 74 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,12,1,0] 75 | , ver 0 19 20250315 := \cfg -> cfg 76 | & field @"cfgGhcupVersion" .~ C.mkVersion [0,1,40,0] 77 | , ver 0 19 20250327 := \cfg -> cfg 78 | & field @"cfgGhcupVersion" .~ C.mkVersion [0,1,50,1] 79 | , ver 0 19 20250330 := \cfg -> cfg 80 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,14,1,1] 81 | , ver 0 19 20250330 := \cfg -> cfg 82 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,14,2,0] 83 | , ver 0 19 20250606 := \cfg -> cfg 84 | & field @"cfgDocspec" . field @"cfgDocspecUrl" .~ "https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20250606/cabal-docspec-0.0.0.20250606-x86_64-linux.xz" 85 | & field @"cfgDocspec" . field @"cfgDocspecHash" .~ "cc20bb5c19501b42bde77556bc419c7c0a5c8d1eb65663024d8a4e4c868bef25" 86 | , ver 0 19 20250708 := \cfg -> cfg 87 | & field @"cfgErrorUnusedPkgs" .~ C.orLaterVersion (C.mkVersion [9,4]) 88 | , ver 0 19 20250108 := \cfg -> cfg 89 | & field @"cfgErrorIncompletePatterns" .~ C.orLaterVersion (C.mkVersion [9,0]) 90 | , ver 0 19 20250801 := \cfg -> cfg 91 | & field @"cfgCabalInstallVersion" ?~ C.mkVersion [3,16,0,0] 92 | , ver 0 19 20250801 := \cfg -> cfg 93 | & field @"cfgVersionMapping" .~ Map.singleton (mkVersion [9,14,1]) (mkVersion [9,14,0,20250819]) 94 | & field @"cfgSetupMethods" .~ PerSetupMethod 95 | { hvrPpa = C.noVersion 96 | , ghcup = invertVersionRange (C.withinVersion (C.mkVersion [9,8,3])) /\ C.earlierVersion (C.mkVersion [9,14]) 97 | , ghcupVanilla = C.withinVersion (C.mkVersion [9,8,3]) 98 | , ghcupPrerelease = C.orLaterVersion (C.mkVersion [9,14]) 99 | } 100 | , ver 0 19 20250916 := \cfg -> cfg 101 | & field @"cfgVersionMapping" .~ Map.singleton (mkVersion [9,14,1]) (mkVersion [9,14,0,20250908]) 102 | ] 103 | where 104 | ver x y z = [x, y, z] 105 | 106 | -- staticHistory [ ver 0 19 20240702 := add GHC-9.6.6 107 | 108 | defaultConfig :: Config 109 | defaultConfig = foldl' f initialConfig configHistory 110 | where 111 | f !cfg (_, g) = g cfg 112 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Initial.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | module HaskellCI.Config.Initial where 4 | 5 | import HaskellCI.Prelude 6 | 7 | import qualified Distribution.Version as C 8 | 9 | import HaskellCI.Config.Components 10 | import HaskellCI.Config.CopyFields 11 | import HaskellCI.Config.Docspec 12 | import HaskellCI.Config.Doctest 13 | import HaskellCI.Config.PackageScope 14 | import HaskellCI.Config.Type 15 | import HaskellCI.Config.Ubuntu 16 | import HaskellCI.Ghcup 17 | import HaskellCI.HeadHackage 18 | import HaskellCI.SetupMethod 19 | import HaskellCI.TestedWith 20 | 21 | -- | This is an "initial" configuration. It's meant to stay immutable. 22 | -- All changes to defaults should be done in History. 23 | initialConfig :: Config 24 | initialConfig = Config 25 | { cfgCabalInstallVersion = Just (C.mkVersion [3,10,2,0]) 26 | , cfgJobs = Nothing 27 | , cfgUbuntu = Bionic 28 | , cfgTestedWith = TestedWithUniform 29 | , cfgEnabledJobs = anyVersion 30 | , cfgCopyFields = CopyFieldsSome 31 | , cfgLocalGhcOptions = [] 32 | , cfgSubmodules = False 33 | , cfgCache = True 34 | , cfgInstallDeps = True 35 | , cfgInstalled = [] 36 | , cfgTests = anyVersion 37 | , cfgRunTests = anyVersion 38 | , cfgBenchmarks = anyVersion 39 | , cfgHaddock = anyVersion 40 | , cfgHaddockComponents = ComponentsAll 41 | , cfgNoTestsNoBench = anyVersion 42 | , cfgUnconstrainted = anyVersion 43 | , cfgHeadHackage = defaultHeadHackage 44 | , cfgHeadHackageOverride = True 45 | , cfgGhcjsTests = False 46 | , cfgGhcjsTools = [] 47 | , cfgTestOutputDirect = True 48 | , cfgCheck = True 49 | , cfgOnlyBranches = [] 50 | , cfgIrcChannels = [] 51 | , cfgIrcNickname = Nothing 52 | , cfgIrcPassword = Nothing 53 | , cfgIrcIfInOriginRepo = False 54 | , cfgEmailNotifications = True 55 | , cfgProjectName = Nothing 56 | , cfgGhcHead = False 57 | , cfgPostgres = False 58 | , cfgGoogleChrome = False 59 | , cfgEnv = mempty 60 | , cfgAllowFailures = noVersion 61 | , cfgLastInSeries = False 62 | , cfgLinuxJobs = anyVersion 63 | , cfgMacosJobs = noVersion 64 | , cfgGhcupCabal = True 65 | , cfgSetupMethods = PerSetupMethod 66 | { hvrPpa = noVersion 67 | , ghcup = C.unionVersionRanges (C.intersectVersionRanges (C.laterVersion (mkVersion [8,10,4])) (C.earlierVersion (mkVersion [9]))) (C.laterVersion (mkVersion [9,0,1])) 68 | , ghcupVanilla = noVersion -- TODO -- include GHC-9.8.3 69 | , ghcupPrerelease = C.orLaterVersion (mkVersion ([9,11,0])) 70 | } 71 | , cfgGhcupVersion = initialGhcupVersion 72 | , cfgApt = mempty 73 | , cfgTravisPatches = [] 74 | , cfgGitHubPatches = [] 75 | , cfgInsertVersion = True 76 | , cfgErrorMissingMethods = PackageScopeLocal 77 | , cfgErrorUnusedPkgs = noVersion 78 | , cfgErrorIncompletePatterns = noVersion 79 | , cfgDoctest = initialDoctestConfig 80 | , cfgDocspec = initialDocspecConfig 81 | , cfgConstraintSets = [] 82 | , cfgRawProject = [] 83 | , cfgRawTravis = "" 84 | , cfgGitHubActionName = Nothing 85 | , cfgTimeoutMinutes = 60 86 | , cfgVersionMapping = mempty 87 | } 88 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Installed.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Config.Installed where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Data.Set as S 6 | import qualified Distribution.Compat.CharParsing as C 7 | import qualified Distribution.Parsec as C 8 | import qualified Distribution.Pretty as C 9 | import qualified Distribution.Types.PackageName as C 10 | import qualified Text.PrettyPrint as PP 11 | 12 | -- $setup 13 | -- >>> import qualified Distribution.Parsec as C 14 | 15 | ------------------------------------------------------------------------------- 16 | -- Single action 17 | ------------------------------------------------------------------------------- 18 | 19 | data Installed 20 | = InstalledAll 21 | | InstalledNone 22 | | Add C.PackageName 23 | | Remove C.PackageName 24 | deriving (Eq, Show) 25 | 26 | instance C.Pretty Installed where 27 | pretty InstalledAll = PP.text "+all" 28 | pretty InstalledNone = PP.text "-all" 29 | pretty (Add pn) = PP.char '+' PP.<> C.pretty pn 30 | pretty (Remove pn) = PP.char '-' PP.<> C.pretty pn 31 | 32 | instance C.Parsec Installed where 33 | parsec = do 34 | s <- True <$ C.char '+' <|> False <$ C.char '-' 35 | pn <- C.parsec 36 | return $ case (s, pn == C.mkPackageName "all") of 37 | (True, True) -> InstalledAll 38 | (True, False) -> Add pn 39 | (False, True) -> InstalledNone 40 | (False, False) -> Remove pn 41 | 42 | ------------------------------------------------------------------------------- 43 | -- Normalised 44 | ------------------------------------------------------------------------------- 45 | 46 | data InstalledNormalised 47 | = InstalledDiff (S.Set C.PackageName) 48 | | InstalledOnly (S.Set C.PackageName) 49 | deriving Show 50 | 51 | -- | Normalised 52 | -- 53 | -- >>> parseI = maybe (error "foo") id . traverse C.simpleParsec 54 | -- >>> normaliseInstalled $ parseI ["-Cabal"] 55 | -- InstalledDiff (fromList [PackageName "Cabal"]) 56 | -- 57 | -- >>> normaliseInstalled $ parseI ["-all"] 58 | -- InstalledOnly (fromList []) 59 | -- 60 | -- >>> normaliseInstalled $ parseI ["-all", "+transformers"] 61 | -- InstalledOnly (fromList [PackageName "transformers"]) 62 | -- 63 | normaliseInstalled :: [Installed] -> InstalledNormalised 64 | normaliseInstalled = foldl' f (InstalledDiff S.empty) where 65 | f :: InstalledNormalised -> Installed -> InstalledNormalised 66 | f _ InstalledNone = InstalledOnly S.empty 67 | f _ InstalledAll = InstalledDiff S.empty 68 | 69 | f (InstalledDiff s) (Remove p) = InstalledDiff (S.insert p s) 70 | f (InstalledDiff s) (Add p) = InstalledDiff (S.delete p s) 71 | 72 | f (InstalledOnly s) (Remove p) = InstalledOnly (S.delete p s) 73 | f (InstalledOnly s) (Add p) = InstalledOnly (S.insert p s) 74 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Jobs.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Config.Jobs where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Compat.CharParsing as C 6 | import qualified Distribution.Parsec as C 7 | import qualified Distribution.Pretty as C 8 | import qualified Text.PrettyPrint as PP 9 | 10 | -- $setup 11 | -- >>> import qualified Distribution.Parsec as C 12 | 13 | -- | Jobs 14 | -- 15 | -- * @N:M@ - @N@ ghcs (cabal -j), @M@ threads (ghc -j) 16 | -- 17 | -- >>> let parseJobs = C.simpleParsec :: String -> Maybe Jobs 18 | -- >>> parseJobs "2:2" 19 | -- Just (BothJobs 2 2) 20 | -- 21 | -- >>> parseJobs ":2" 22 | -- Just (GhcJobs 2) 23 | -- 24 | -- >>> parseJobs "2" 25 | -- Just (CabalJobs 2) 26 | -- 27 | -- >>> parseJobs "garbage" 28 | -- Nothing 29 | -- 30 | data Jobs 31 | = CabalJobs Int 32 | | GhcJobs Int 33 | | BothJobs Int Int 34 | deriving (Show) 35 | 36 | cabalJobs :: Jobs -> Maybe Int 37 | cabalJobs (CabalJobs n) = Just n 38 | cabalJobs (GhcJobs _) = Nothing 39 | cabalJobs (BothJobs n _) = Just n 40 | 41 | ghcJobs :: Jobs -> Maybe Int 42 | ghcJobs (CabalJobs _) = Nothing 43 | ghcJobs (GhcJobs m) = Just m 44 | ghcJobs (BothJobs _ m) = Just m 45 | 46 | instance C.Parsec Jobs where 47 | parsec = ghc <|> rest where 48 | ghc = C.char ':' *> (GhcJobs <$> C.integral) 49 | rest = do 50 | n <- C.integral 51 | m' <- C.optional (C.char ':' *> C.integral) 52 | return $ case m' of 53 | Nothing -> CabalJobs n 54 | Just m -> BothJobs n m 55 | 56 | instance C.Pretty Jobs where 57 | pretty (BothJobs n m) = PP.int n PP.<> PP.colon PP.<> PP.int m 58 | pretty (CabalJobs n) = PP.int n 59 | pretty (GhcJobs m) = PP.colon PP.<> PP.int m 60 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/PackageScope.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Config.PackageScope where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Compat.CharParsing as C 6 | import qualified Distribution.Parsec as C 7 | import qualified Distribution.Pretty as C 8 | import qualified Text.PrettyPrint as PP 9 | 10 | data PackageScope 11 | = PackageScopeNone 12 | | PackageScopeLocal 13 | | PackageScopeAll 14 | deriving (Eq, Show) 15 | 16 | instance C.Parsec PackageScope where 17 | parsec = 18 | PackageScopeNone <$ C.string "none" 19 | <|> PackageScopeLocal <$ C.string "local" 20 | <|> PackageScopeAll <$ C.string "all" 21 | 22 | instance C.Pretty PackageScope where 23 | pretty PackageScopeNone = PP.text "none" 24 | pretty PackageScopeLocal = PP.text "local" 25 | pretty PackageScopeAll = PP.text "all" 26 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Type.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE StandaloneDeriving #-} 2 | {-# OPTIONS_GHC -Wno-orphans #-} 3 | module HaskellCI.Config.Type where 4 | 5 | import HaskellCI.Prelude 6 | 7 | import qualified Data.Map as M 8 | import qualified Data.Set as S 9 | import qualified Distribution.Fields as C 10 | import qualified Distribution.Types.PackageName as C 11 | 12 | import HaskellCI.Config.Components 13 | import HaskellCI.Config.ConstraintSet 14 | import HaskellCI.Config.CopyFields 15 | import HaskellCI.Config.Docspec 16 | import HaskellCI.Config.Doctest 17 | import HaskellCI.Config.Installed 18 | import HaskellCI.Config.Jobs 19 | import HaskellCI.Config.PackageScope 20 | import HaskellCI.Config.Ubuntu 21 | import HaskellCI.SetupMethod 22 | import HaskellCI.TestedWith 23 | 24 | ------------------------------------------------------------------------------- 25 | -- Config 26 | ------------------------------------------------------------------------------- 27 | 28 | -- TODO: split other blocks like DoctestConfig 29 | data Config = Config 30 | { cfgCabalInstallVersion :: Maybe Version 31 | , cfgJobs :: Maybe Jobs 32 | , cfgUbuntu :: !Ubuntu 33 | , cfgTestedWith :: !TestedWithJobs 34 | , cfgEnabledJobs :: !VersionRange 35 | , cfgCopyFields :: !CopyFields 36 | , cfgLocalGhcOptions :: [String] 37 | , cfgSubmodules :: !Bool 38 | , cfgCache :: !Bool 39 | , cfgInstallDeps :: !Bool 40 | , cfgInstalled :: [Installed] 41 | , cfgTests :: !VersionRange 42 | , cfgRunTests :: !VersionRange 43 | , cfgBenchmarks :: !VersionRange 44 | , cfgHaddock :: !VersionRange 45 | , cfgHaddockComponents :: !Components 46 | , cfgNoTestsNoBench :: !VersionRange 47 | , cfgUnconstrainted :: !VersionRange 48 | , cfgHeadHackage :: !VersionRange 49 | , cfgHeadHackageOverride :: !Bool 50 | , cfgGhcjsTests :: !Bool 51 | , cfgGhcjsTools :: ![C.PackageName] 52 | , cfgTestOutputDirect :: !Bool 53 | , cfgCheck :: !Bool 54 | , cfgOnlyBranches :: [String] 55 | , cfgIrcChannels :: [String] 56 | , cfgIrcNickname :: Maybe String 57 | , cfgIrcPassword :: Maybe String 58 | , cfgIrcIfInOriginRepo :: Bool 59 | , cfgEmailNotifications :: Bool 60 | , cfgProjectName :: Maybe String 61 | , cfgGhcHead :: !Bool 62 | , cfgPostgres :: !Bool 63 | , cfgGoogleChrome :: !Bool 64 | , cfgEnv :: M.Map Version String 65 | , cfgAllowFailures :: !VersionRange 66 | , cfgLastInSeries :: !Bool 67 | , cfgLinuxJobs :: !VersionRange 68 | , cfgMacosJobs :: !VersionRange 69 | , cfgGhcupCabal :: !Bool 70 | , cfgSetupMethods :: !(PerSetupMethod VersionRange) 71 | , cfgGhcupVersion :: !Version 72 | , cfgApt :: S.Set String 73 | , cfgTravisPatches :: [FilePath] 74 | , cfgGitHubPatches :: [FilePath] 75 | , cfgInsertVersion :: !Bool 76 | , cfgErrorMissingMethods :: !PackageScope 77 | , cfgErrorUnusedPkgs :: !VersionRange 78 | , cfgErrorIncompletePatterns :: !VersionRange 79 | , cfgDoctest :: !DoctestConfig 80 | , cfgDocspec :: !DocspecConfig 81 | , cfgConstraintSets :: [ConstraintSet] 82 | , cfgRawProject :: [C.PrettyField ()] 83 | , cfgRawTravis :: !String 84 | , cfgGitHubActionName :: !(Maybe String) 85 | , cfgTimeoutMinutes :: !Natural 86 | , cfgVersionMapping :: !(Map Version Version) 87 | } 88 | deriving (Show, Generic) 89 | 90 | deriving instance Show (C.PrettyField ()) 91 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Ubuntu.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Config.Ubuntu where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Parsec as C 6 | import qualified Distribution.Pretty as C 7 | import qualified Text.PrettyPrint as PP 8 | 9 | data Ubuntu 10 | = Xenial 11 | | Bionic 12 | | Focal 13 | | Jammy 14 | | Noble 15 | deriving (Eq, Ord, Show, Enum, Bounded) 16 | 17 | instance C.Parsec Ubuntu where 18 | parsec = do 19 | t <- C.parsecToken 20 | case t of 21 | "xenial" -> return Xenial 22 | "bionic" -> return Bionic 23 | "focal" -> return Focal 24 | "jammy" -> return Jammy 25 | "noble" -> return Noble 26 | _ -> fail $ "Unknown ubuntu release " ++ t 27 | 28 | instance C.Pretty Ubuntu where 29 | pretty = PP.text . showUbuntu 30 | 31 | showUbuntu :: Ubuntu -> String 32 | showUbuntu Xenial = "xenial" 33 | showUbuntu Bionic = "bionic" 34 | showUbuntu Focal = "focal" 35 | showUbuntu Jammy = "jammy" 36 | showUbuntu Noble = "noble" 37 | -------------------------------------------------------------------------------- /src/HaskellCI/Config/Validity.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE RecordWildCards #-} 3 | module HaskellCI.Config.Validity where 4 | 5 | import HaskellCI.Prelude 6 | 7 | import HaskellCI.Config 8 | import HaskellCI.Config.Ubuntu 9 | import HaskellCI.Error 10 | import HaskellCI.Jobs 11 | import HaskellCI.MonadErr 12 | 13 | -- Validity checks shared in common among all backends. 14 | checkConfigValidity :: MonadErr HsCiError m => Config -> JobVersions -> m () 15 | checkConfigValidity Config {..} _ = do 16 | unless (cfgUbuntu >= Focal) $ 17 | throwErr $ ValidationError $ prettyShow cfgUbuntu ++ "distribution is not supported" 18 | 19 | unless cfgGhcupCabal $ 20 | throwErr $ ValidationError $ "The GHCUP is the only supported installation method for cabal-install" 21 | -------------------------------------------------------------------------------- /src/HaskellCI/Diagnostics.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Diagnostics where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import Control.Monad.Trans.Maybe (MaybeT (..)) 6 | import Control.Monad.Writer (WriterT, runWriterT, tell) 7 | import System.Exit (exitFailure) 8 | import System.IO (hPutStrLn, stderr) 9 | 10 | class Monad m => MonadDiagnostics m where 11 | putStrLnErr :: String -> m a 12 | putStrLnErrs :: NonEmpty String -> m a 13 | putStrLnWarn :: String -> m () 14 | putStrLnInfo :: String -> m () 15 | 16 | instance MonadDiagnostics IO where 17 | putStrLnErr err = do 18 | hPutStrLn stderr $ "*ERROR* " ++ err 19 | exitFailure 20 | 21 | putStrLnErrs errs = do 22 | for_ errs $ \err -> hPutStrLn stderr $ "*ERROR* " ++ err 23 | exitFailure 24 | 25 | putStrLnWarn = hPutStrLn stderr . ("*WARNING* " ++) 26 | putStrLnInfo = hPutStrLn stderr . ("*INFO* " ++) 27 | 28 | newtype DiagnosticsT m a = Diagnostics { unDiagnostics :: MaybeT (WriterT [String] m) a } 29 | deriving stock (Functor) 30 | deriving newtype (Applicative, Monad, MonadIO, MonadCatch, MonadMask, MonadThrow) 31 | 32 | runDiagnosticsT :: DiagnosticsT m a -> m (Maybe a, [String]) 33 | runDiagnosticsT (Diagnostics m) = runWriterT (runMaybeT m) 34 | 35 | instance Monad m => MonadDiagnostics (DiagnosticsT m) where 36 | putStrLnWarn err = Diagnostics $ tell ["*WARNING* " ++ err] 37 | putStrLnInfo err = Diagnostics $ tell ["*INFO* " ++ err] 38 | 39 | putStrLnErr err = Diagnostics $ do 40 | tell ["*ERROR* " ++ err] 41 | MaybeT $ return Nothing 42 | 43 | putStrLnErrs errs = Diagnostics $ do 44 | tell $ map ("*ERROR* " ++) (toList errs) 45 | MaybeT $ return Nothing 46 | -------------------------------------------------------------------------------- /src/HaskellCI/Error.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Error ( 2 | HsCiError (..), 3 | FromHsCiError (..), 4 | ) where 5 | 6 | import HaskellCI.Prelude 7 | 8 | data HsCiError 9 | = ShellCheckError String -- ^ @ShellCheck@ disagrees. 10 | | ValidationError String -- ^ used by validations 11 | | FailError String -- ^ made by 'fail'. 12 | deriving (Show) 13 | 14 | instance Exception HsCiError where 15 | displayException (ShellCheckError s) = s 16 | displayException (ValidationError s) = s 17 | displayException (FailError s) = "PANIC " ++ s 18 | 19 | class FromHsCiError e where 20 | fromHsCiError :: HsCiError -> e 21 | 22 | instance FromHsCiError HsCiError where 23 | fromHsCiError = id 24 | -------------------------------------------------------------------------------- /src/HaskellCI/Ghcup.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Ghcup where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Version as C 6 | 7 | initialGhcupVersion :: Version 8 | initialGhcupVersion = C.mkVersion [0,1,20,0] 9 | 10 | -- https://github.com/haskell/cabal/issues/10836#issuecomment-2757855218 11 | -- if you interface with GHCup, you SHOULD be able to deal with non-PVP versions. 12 | -- 13 | -- Perfectly, we'd have these mappings configurable; 14 | -- but for now they are hardcoded. 15 | -- 16 | translateGhcVersion :: Version -> String 17 | translateGhcVersion = prettyShow 18 | 19 | translateCabalVersion :: Version -> String 20 | translateCabalVersion x 21 | | [3,14,1,1] <- C.versionNumbers x 22 | = "3.14.1.1-p1" 23 | 24 | | otherwise 25 | = prettyShow x 26 | -------------------------------------------------------------------------------- /src/HaskellCI/GitConfig.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module HaskellCI.GitConfig ( 3 | GitConfig (..), 4 | emptyGitConfig, 5 | readGitConfig, 6 | ) where 7 | 8 | import HaskellCI.Prelude 9 | 10 | import Data.Ini 11 | 12 | import qualified Data.Attoparsec.Text as Atto 13 | import qualified Data.Map.Strict as Map 14 | 15 | newtype GitConfig = GitConfig 16 | { gitCfgRemotes :: Map.Map Text Text 17 | } 18 | deriving Show 19 | 20 | emptyGitConfig :: GitConfig 21 | emptyGitConfig = GitConfig 22 | { gitCfgRemotes = mempty 23 | } 24 | 25 | -- | Read 'GitConfig'. On error, return 'emptyGitConfg'. 26 | readGitConfig :: IO GitConfig 27 | readGitConfig = handle fallback $ do 28 | e <- readIniFile ".git/config" 29 | return $ case e of 30 | Left _ -> emptyGitConfig 31 | Right ini -> elaborateGitConfig ini 32 | where 33 | fallback :: IOException -> IO GitConfig 34 | fallback _ = return emptyGitConfig 35 | 36 | elaborateGitConfig :: Ini -> GitConfig 37 | elaborateGitConfig ini = ifoldr go emptyGitConfig (iniSections ini) where 38 | go :: Text -> [(Text, Text)] -> GitConfig -> GitConfig 39 | go secname secfields cfg 40 | | Right name <- Atto.parseOnly (sectionP <* Atto.endOfInput) secname 41 | , Just url <- lookup "url" secfields 42 | = cfg 43 | { gitCfgRemotes = Map.insert name url (gitCfgRemotes cfg) 44 | } 45 | 46 | go _ _ cfg = cfg 47 | 48 | -- We use attoparsec here, because it backtracks 49 | sectionP :: Atto.Parser Text 50 | sectionP = do 51 | _ <- Atto.string "remote" 52 | Atto.skipSpace 53 | _ <- Atto.char '"' 54 | remote <- Atto.takeWhile (/= '"') 55 | _ <- Atto.char '"' 56 | return remote 57 | 58 | -------------------------------------------------------------------------------- /src/HaskellCI/GitHub/Yaml.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE RecordWildCards #-} 3 | {-# LANGUAGE StrictData #-} 4 | module HaskellCI.GitHub.Yaml where 5 | 6 | import HaskellCI.Prelude 7 | 8 | import qualified Data.Map.Strict as M 9 | 10 | import HaskellCI.Compiler 11 | import HaskellCI.Ghcup 12 | import HaskellCI.List 13 | import HaskellCI.SetupMethod 14 | import HaskellCI.Sh 15 | import HaskellCI.YamlSyntax 16 | 17 | ------------------------------------------------------------------------------- 18 | -- Data 19 | ------------------------------------------------------------------------------- 20 | 21 | data GitHub = GitHub 22 | { ghName :: String 23 | , ghOn :: GitHubOn 24 | , ghJobs :: M.Map String GitHubJob 25 | } 26 | deriving (Show) 27 | 28 | newtype GitHubOn = GitHubOn 29 | { ghBranches :: [String] 30 | } 31 | deriving (Show) 32 | 33 | data GitHubJob = GitHubJob 34 | { ghjName :: String 35 | , ghjRunsOn :: String 36 | , ghjNeeds :: [String] 37 | , ghjIf :: Maybe String 38 | , ghjContainer :: Maybe String 39 | , ghjServices :: M.Map String GitHubService 40 | , ghjContinueOnError :: Maybe String 41 | , ghjMatrix :: [GitHubMatrixEntry] 42 | , ghjSteps :: [GitHubStep] 43 | , ghjTimeout :: Natural 44 | } 45 | deriving (Show) 46 | 47 | data GitHubMatrixEntry = GitHubMatrixEntry 48 | { ghmeCompiler :: CompilerVersion 49 | , ghmeAllowFailure :: Bool 50 | , ghmeSetupMethod :: SetupMethod 51 | } 52 | deriving (Show) 53 | 54 | data GitHubStep = GitHubStep 55 | { ghsName :: String 56 | , ghsIf :: Maybe String 57 | , ghsStep :: Either GitHubRun GitHubUses 58 | } 59 | deriving (Show) 60 | 61 | -- | Steps with @run@ 62 | data GitHubRun = GitHubRun 63 | { ghsRun :: [Sh] 64 | , ghsEnv :: M.Map String String 65 | } 66 | deriving (Show) 67 | 68 | -- | Steps with @uses@ 69 | data GitHubUses = GitHubUses 70 | { ghsAction :: String 71 | , ghsWith :: M.Map String String 72 | } 73 | deriving (Show) 74 | 75 | data GitHubService = GitHubService 76 | { ghServImage :: String 77 | , ghServEnv :: M.Map String String 78 | , ghServOptions :: Maybe String 79 | } 80 | deriving (Show) 81 | 82 | ------------------------------------------------------------------------------- 83 | -- ToYaml 84 | ------------------------------------------------------------------------------- 85 | 86 | instance ToYaml GitHub where 87 | toYaml GitHub {..} = ykeyValuesFilt [] 88 | [ "name" ~> fromString ghName 89 | , "on" ~> toYaml ghOn 90 | , "jobs" ~> ykeyValuesFilt [] 91 | [ ([], j, toYaml job) 92 | | (j, job) <- M.toList ghJobs 93 | ] 94 | ] 95 | 96 | instance ToYaml GitHubOn where 97 | toYaml GitHubOn {..} 98 | | null ghBranches 99 | = ylistFilt [] ["push", "pull_request", "merge_group"] 100 | | otherwise 101 | = ykeyValuesFilt [] 102 | [ "push" ~> branches 103 | , "pull_request" ~> branches 104 | , "merge_group" ~> branches 105 | ] 106 | where 107 | branches = ykeyValuesFilt [] 108 | [ "branches" ~> ylistFilt [] (map fromString ghBranches) 109 | ] 110 | 111 | instance ToYaml GitHubJob where 112 | toYaml GitHubJob {..} = ykeyValuesFilt [] $ buildList $ do 113 | item $ "name" ~> fromString ghjName 114 | item $ "runs-on" ~> fromString ghjRunsOn 115 | item $ "needs" ~> ylistFilt [] (map fromString ghjNeeds) 116 | for_ ghjIf $ \if_ -> 117 | item $ "if" ~> fromString if_ 118 | item $ "timeout-minutes" ~> YNumber [] (toInteger ghjTimeout) 119 | item $ "container" ~> ykeyValuesFilt [] (buildList $ 120 | for_ ghjContainer $ \image -> item $ "image" ~> fromString image) 121 | item $ "services" ~> toYaml ghjServices 122 | for_ ghjContinueOnError $ \continueOnError -> 123 | item $ "continue-on-error" ~> fromString continueOnError 124 | item $ "strategy" ~> ykeyValuesFilt [] 125 | [ "matrix" ~> ykeyValuesFilt [] 126 | [ "include" ~> ylistFilt [] (map toYaml ghjMatrix) 127 | ] 128 | , "fail-fast" ~> YBool [] False 129 | ] 130 | item $ "steps" ~> ylistFilt [] (map toYaml $ filter notEmptyStep ghjSteps) 131 | 132 | instance ToYaml GitHubMatrixEntry where 133 | toYaml GitHubMatrixEntry {..} = ykeyValuesFilt [] 134 | [ "compiler" ~> fromString compiler 135 | , "compilerKind" ~> fromString (compilerKind ghmeCompiler) 136 | , "compilerVersion" ~> fromString (compilerVersion ghmeCompiler) 137 | , "setup-method" ~> toYaml ghmeSetupMethod 138 | , "allow-failure" ~> toYaml ghmeAllowFailure 139 | ] 140 | where 141 | compiler 142 | | GHCUP <- ghmeSetupMethod 143 | , GHC v <- ghmeCompiler 144 | = "ghc-" ++ translateGhcVersion v 145 | 146 | | otherwise 147 | = dispGhcVersion ghmeCompiler 148 | 149 | instance ToYaml GitHubStep where 150 | toYaml GitHubStep {..} = ykeyValuesFilt [] $ buildList $ do 151 | item $ "name" ~> fromString ghsName 152 | for_ ghsIf $ \if_ -> item $ "if" ~> fromString if_ 153 | 154 | case ghsStep of 155 | Left GitHubRun {..} -> do 156 | item $ "run" ~> fromString (shlistToString ghsRun) 157 | item $ "env" ~> mapToYaml ghsEnv 158 | 159 | Right GitHubUses {..} -> do 160 | item $ "uses" ~> fromString ghsAction 161 | item $ "with" ~> mapToYaml ghsWith 162 | 163 | notEmptyStep :: GitHubStep -> Bool 164 | notEmptyStep (GitHubStep _ _ (Left (GitHubRun [] _))) = False 165 | notEmptyStep _ = True 166 | 167 | instance ToYaml GitHubService where 168 | toYaml GitHubService {..} = ykeyValuesFilt [] $ buildList $ do 169 | item $ "image" ~> fromString ghServImage 170 | item $ "env" ~> toYaml (YString [] <$> ghServEnv) 171 | for_ ghServOptions $ \opt -> item $ "options" ~> fromString opt 172 | 173 | ------------------------------------------------------------------------------- 174 | -- Helpers 175 | ------------------------------------------------------------------------------- 176 | 177 | mapToYaml :: M.Map String String -> Yaml [String] 178 | mapToYaml m = ykeyValuesFilt [] 179 | [ k ~> fromString v 180 | | (k, v) <- M.toList m 181 | ] 182 | -------------------------------------------------------------------------------- /src/HaskellCI/GrammarDefault.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.GrammarDefault where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Compat.Lens as C 6 | import qualified Distribution.Compat.Newtype as C 7 | import qualified Distribution.FieldGrammar as C 8 | import qualified Distribution.Fields as C 9 | 10 | import HaskellCI.OptionsGrammar 11 | 12 | blurFieldGrammar :: OptionsGrammar c g => C.ALens' s d -> (d -> g d d) -> s -> g s d 13 | blurFieldGrammar l sub s = C.blurFieldGrammar l (sub (C.aview l s)) 14 | 15 | monoidalFieldAla :: (OptionsGrammar c g, C.Newtype a b, c b, Monoid a) => C.FieldName -> (a -> b) -> C.ALens' s a -> g s a 16 | monoidalFieldAla = C.monoidalFieldAla 17 | 18 | freeTextField :: OptionsGrammar c g => C.FieldName -> C.ALens' s (Maybe String) -> g s (Maybe String) 19 | freeTextField = C.freeTextField 20 | 21 | freeTextFieldDef :: OptionsGrammar c g => C.FieldName -> C.ALens' s String -> g s String 22 | freeTextFieldDef = C.freeTextFieldDef 23 | 24 | booleanFieldDef :: (OptionsGrammar c g) => C.FieldName -> C.ALens' s Bool -> s -> g s Bool 25 | booleanFieldDef fn l s = C.booleanFieldDef fn l (C.aview l s) 26 | 27 | optionalField :: (OptionsGrammar c g, c a) => C.FieldName -> C.ALens' s (Maybe a) -> g s (Maybe a) 28 | optionalField fn l = C.optionalField fn l 29 | 30 | optionalFieldAla :: (OptionsGrammar c g, C.Newtype a b, c b) => C.FieldName -> (a -> b) -> C.ALens' s (Maybe a) -> g s (Maybe a) 31 | optionalFieldAla fn pack l = C.optionalFieldAla fn pack l 32 | 33 | optionalFieldDef :: (OptionsGrammar c g, c a, Eq a) => C.FieldName -> C.ALens' s a -> s -> g s a 34 | optionalFieldDef fn l s = C.optionalFieldDef fn l (C.aview l s) 35 | 36 | optionalFieldDefAla :: (OptionsGrammar c g, C.Newtype a b, c b, Eq a) => C.FieldName -> (a -> b) -> C.ALens' s a -> s -> g s a 37 | optionalFieldDefAla fn pack l s = C.optionalFieldDefAla fn pack l (C.aview l s) 38 | -------------------------------------------------------------------------------- /src/HaskellCI/HeadHackage.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.HeadHackage where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Version as C 6 | 7 | defaultHeadHackage :: VersionRange 8 | defaultHeadHackage = C.orLaterVersion (C.mkVersion [9,11]) 9 | 10 | headHackageRepoStanza :: Bool -> [String] 11 | headHackageRepoStanza override = 12 | [ "repository head.hackage.ghc.haskell.org" 13 | , " url: https://ghc.gitlab.haskell.org/head.hackage/" 14 | , " secure: True" 15 | , " root-keys: 7541f32a4ccca4f97aea3b22f5e593ba2c0267546016b992dfadcd2fe944e55d" 16 | , " 26021a13b401500c8eb2761ca95c61f2d625bfef951b939a8124ed12ecf07329" 17 | , " f76d08be13e9a61a377a85e2fb63f4c5435d40f8feb3e12eb05905edb8cdea89" 18 | , " key-threshold: 3" 19 | ] ++ 20 | activeRepositories 21 | where 22 | activeRepositories 23 | | override 24 | = [ "active-repositories: hackage.haskell.org, head.hackage.ghc.haskell.org:override" 25 | ] 26 | 27 | | otherwise 28 | = [] 29 | -------------------------------------------------------------------------------- /src/HaskellCI/Jobs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE RecordWildCards #-} 2 | -- | Which jobs to generate. Also helper for diagnostics output. 3 | module HaskellCI.Jobs ( 4 | JobVersions (..), 5 | describeJobs, 6 | makeJobVersions, 7 | ) where 8 | 9 | import HaskellCI.Prelude 10 | 11 | import qualified Data.Set as S 12 | 13 | import HaskellCI.Compiler 14 | import HaskellCI.Config 15 | import HaskellCI.Diagnostics 16 | import HaskellCI.Package 17 | import HaskellCI.TestedWith 18 | 19 | data JobVersions = JobVersions 20 | { allVersions :: Set CompilerVersion -- ^ all versions (useful for Travis) 21 | , linuxVersions :: Set CompilerVersion -- ^ linux jobs 22 | , macosVersions :: Set CompilerVersion -- ^ macos jobs 23 | } 24 | 25 | describeJobs 26 | :: MonadDiagnostics m 27 | => String -- ^ config 28 | -> TestedWithJobs 29 | -> JobVersions 30 | -> [Package] -> m () 31 | describeJobs typ twj JobVersions {..} pkgs = do 32 | putStrLnInfo $ "Generating " ++ typ ++ " for testing for GHC versions: " ++ ghcVersions 33 | case twj of 34 | TestedWithUniform -> pure () 35 | TestedWithAny -> traverse_ putStrLnInfo $ table' 36 | [ pkgName pkg : "": map (showPkgVersion (pkgJobs pkg)) (S.toList allVersions) 37 | | pkg <- pkgs 38 | ] 39 | 40 | unless (null macosVersions) $ do 41 | putStrLnInfo $ "Also macos jobs for: " ++ ghcmacosVersions 42 | where 43 | showPkgVersion :: Set CompilerVersion -> CompilerVersion -> String 44 | showPkgVersion vs v 45 | | S.member v vs = dispGhcVersionShort v 46 | | otherwise = "" 47 | 48 | showVersions :: Set CompilerVersion -> String 49 | showVersions = unwords . map dispGhcVersionShort . S.toList 50 | 51 | ghcVersions :: String 52 | ghcVersions = showVersions linuxVersions 53 | 54 | ghcmacosVersions :: String 55 | ghcmacosVersions = showVersions macosVersions 56 | 57 | makeJobVersions :: Config -> Set CompilerVersion -> JobVersions 58 | makeJobVersions Config {..} versions' = JobVersions {..} where 59 | -- All jobs 60 | versions :: Set CompilerVersion 61 | versions 62 | | cfgGhcHead = S.insert GHCHead versions' 63 | | otherwise = versions' 64 | 65 | allVersions :: Set CompilerVersion 66 | allVersions = S.filter (`compilerWithinRange` range) versions 67 | 68 | linuxVersions :: Set CompilerVersion 69 | linuxVersions = S.filter (`compilerWithinRange` linuxRange) allVersions 70 | 71 | macosVersions :: Set CompilerVersion 72 | macosVersions = S.filter (`compilerWithinRange` macosRange) allVersions 73 | 74 | range, linuxRange, macosRange :: CompilerRange 75 | range = Range cfgEnabledJobs 76 | linuxRange = Range cfgLinuxJobs 77 | macosRange = RangeGHC /\ Range cfgMacosJobs 78 | 79 | -- https://oleg.fi/gists/posts/2019-04-28-tabular.html 80 | table' :: [[String]] -> [String] 81 | table' cells = rows 82 | where 83 | cols :: Int 84 | rowWidths :: [Int] 85 | rows :: [String] 86 | 87 | (cols, rowWidths, rows) = foldr go (0, repeat 0, []) cells 88 | 89 | go :: [String] -> (Int, [Int], [String]) -> (Int, [Int], [String]) 90 | go xs (c, w, yss) = 91 | ( max c (length xs) 92 | , zipWith max w (map length xs ++ repeat 0) 93 | , unwords (take cols (zipWith fill xs rowWidths)) 94 | : yss 95 | ) 96 | 97 | fill :: String -> Int -> String 98 | fill s n = s ++ replicate (n - length s) ' ' 99 | -------------------------------------------------------------------------------- /src/HaskellCI/List.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE RankNTypes #-} 2 | module HaskellCI.List ( 3 | ListBuilder, 4 | buildList, 5 | item, 6 | ) where 7 | 8 | import HaskellCI.Prelude 9 | 10 | newtype ListBuilder x a = LB { unLB :: forall r. (([x] -> [x]) -> a -> r) -> r } 11 | 12 | instance Functor (ListBuilder x) where 13 | fmap f (LB k) = LB $ k $ \endo a k' -> k' endo (f a) 14 | 15 | instance Applicative (ListBuilder x) where 16 | pure x = LB $ \f -> f id x 17 | (<*>) = ap 18 | 19 | instance Monad (ListBuilder x) where 20 | return = pure 21 | 22 | m >>= k = 23 | LB $ \r -> 24 | unLB m $ \endo1 a -> 25 | unLB (k a) $ \endo2 b -> 26 | r (endo1 . endo2) b 27 | 28 | buildList :: ListBuilder x () -> [x] 29 | buildList (LB f) = f $ \endo _ -> endo [] 30 | 31 | item :: x -> ListBuilder x () 32 | item x = LB $ \f -> f (x :) () 33 | -------------------------------------------------------------------------------- /src/HaskellCI/MonadErr.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE FunctionalDependencies #-} 3 | module HaskellCI.MonadErr where 4 | 5 | import HaskellCI.Prelude 6 | 7 | -- | 'MonadErr' is 'MonadError' without @catch@ 8 | class Monad m => MonadErr e m | m -> e where 9 | throwErr :: e -> m a 10 | 11 | instance MonadErr e (Either e) where 12 | throwErr = Left 13 | -------------------------------------------------------------------------------- /src/HaskellCI/OptionsGrammar.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.OptionsGrammar ( 2 | OptionsGrammar (..), 3 | (C.^^^), 4 | metaActionHelp, 5 | ParsecPretty, 6 | Help, MetaVar, BashCompletionAction 7 | ) where 8 | 9 | import HaskellCI.Prelude 10 | 11 | import qualified Distribution.Compat.Lens as C 12 | import qualified Distribution.FieldGrammar as C 13 | import qualified Distribution.Fields as C 14 | import qualified Distribution.Parsec as C 15 | import qualified Distribution.Pretty as C 16 | import qualified Distribution.Types.PackageName as C 17 | import qualified Distribution.Types.VersionRange as C 18 | import qualified Options.Applicative as O 19 | 20 | import HaskellCI.Newtypes 21 | 22 | -- | Help text for option. 23 | type Help = String 24 | 25 | -- | Meta variable for option argument. 26 | type MetaVar = String 27 | 28 | -- | Bash completion action for option argument. 29 | -- Example: @"file"@ or @"directory"@. 30 | -- 31 | -- See 32 | -- and . 33 | type BashCompletionAction = String 34 | 35 | class 36 | ( C.FieldGrammar c p 37 | , forall s. Applicative (p s) 38 | , forall a. c a => c (Identity a) 39 | , c Range, c C.VersionRange 40 | , c (C.List C.NoCommaFSep C.Token' String) 41 | , c (C.List C.FSep C.Token' String) 42 | , c (AlaSet C.NoCommaFSep C.Token' String) 43 | , c (AlaSet C.NoCommaFSep (Identity Version) Version) 44 | , c (C.List C.CommaVCat NoCommas String) 45 | , c (C.List C.NoCommaFSep (Identity C.PackageName) C.PackageName) 46 | , c (C.List C.FSep (Identity C.PackageName) C.PackageName) 47 | , c (AlaMap C.NoCommaFSep VersionPair Version Version) 48 | ) 49 | => OptionsGrammar c p | p -> c 50 | where 51 | metaCompleterHelp :: MetaVar -> O.Completer -> Help -> p s a -> p s a 52 | metaCompleterHelp _ _ _ = id 53 | 54 | metahelp :: MetaVar -> Help -> p s a -> p s a 55 | metahelp _ _ = id 56 | 57 | help :: Help -> p s a -> p s a 58 | help _ = id 59 | 60 | -- we treat range fields specially in options 61 | rangeField :: C.FieldName -> C.ALens' s C.VersionRange -> s -> p s C.VersionRange 62 | rangeField fn l s = C.optionalFieldDefAla fn Range l (C.aview l s) 63 | 64 | metaActionHelp :: OptionsGrammar c p => MetaVar -> BashCompletionAction -> Help -> p s a -> p s a 65 | metaActionHelp m a = metaCompleterHelp m (O.bashCompleter a) 66 | 67 | instance OptionsGrammar C.Parsec C.ParsecFieldGrammar 68 | 69 | class (C.Parsec a, C.Pretty a) => ParsecPretty a 70 | instance (C.Parsec a, C.Pretty a) => ParsecPretty a 71 | -------------------------------------------------------------------------------- /src/HaskellCI/OptparseGrammar.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | {-# LANGUAGE OverloadedStrings #-} 5 | module HaskellCI.OptparseGrammar ( 6 | OptparseGrammar, 7 | runOptparseGrammar, 8 | ) where 9 | 10 | import HaskellCI.Prelude 11 | 12 | import Control.Applicative (many) 13 | import Data.Foldable (asum) 14 | 15 | import qualified Distribution.Compat.Lens as C 16 | import qualified Distribution.Compat.Newtype as C 17 | import qualified Distribution.FieldGrammar as C 18 | import qualified Distribution.Fields as C 19 | import qualified Distribution.Parsec as C 20 | import qualified Distribution.Pretty as C 21 | import qualified Distribution.Version as C 22 | import qualified Options.Applicative as O 23 | 24 | import HaskellCI.OptionsGrammar 25 | 26 | data SomeParser s where 27 | SP :: (Maybe MetaVar -> Maybe O.Completer -> Maybe Help -> O.Parser (s -> s)) -> SomeParser s 28 | 29 | newtype OptparseGrammar s a = OG [SomeParser s] 30 | deriving Functor 31 | 32 | runOptparseGrammar :: OptparseGrammar s a -> O.Parser (s -> s) 33 | runOptparseGrammar (OG ps) = fmap (foldr (flip (.)) id) $ many $ asum 34 | [ p Nothing Nothing Nothing 35 | | SP p <- ps 36 | ] 37 | 38 | instance Applicative (OptparseGrammar s) where 39 | pure _ = OG [] 40 | OG f <*> OG x = OG (f ++ x) 41 | 42 | instance C.FieldGrammar ParsecPretty OptparseGrammar where 43 | blurFieldGrammar l (OG ps) = OG 44 | [ SP $ \v c h -> fmap (l C.#%~) (p v c h) 45 | | SP p <- ps 46 | ] 47 | 48 | -- we don't support unique fields atm 49 | uniqueFieldAla _ _ _ = OG [] 50 | 51 | -- the non default flag has help entry 52 | booleanFieldDef fn l def = OG 53 | [ SP $ \_m _c h -> setOG l $ O.flag' True $ flagMods fn (th h) 54 | , SP $ \_m _c h -> setOG l $ O.flag' False $ flagMods ("no-" <> fn) (fh h) 55 | ] 56 | where 57 | th h = if def then Nothing else h 58 | fh h = if def then h else Nothing 59 | 60 | optionalFieldAla fn c l = OG 61 | [ SP $ \m cpl h -> setOptionalOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m cpl h ] 62 | 63 | optionalFieldDefAla fn c l def = OG 64 | [ SP $ \m cpl h -> setOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m cpl (fmap hdef h) ] 65 | where 66 | hdef h = h ++ " (Default: " ++ C.prettyShow (C.pack' c def) ++ ")" 67 | 68 | monoidalFieldAla fn c l = OG 69 | [ SP $ \m cpl h -> monoidOG l $ O.option (C.unpack' c <$> readMParsec) $ optionMods fn m cpl h ] 70 | 71 | prefixedFields _ _ = pure [] 72 | knownField _ = pure () 73 | deprecatedSince _ _ = id 74 | availableSince _ _ = id 75 | removedIn _ _ = id 76 | hiddenField = id 77 | 78 | freeTextField fn l = OG 79 | [ SP $ \m c h -> setOptionalOG l $ O.strOption $ optionMods fn m c h ] 80 | 81 | freeTextFieldDef fn l = OG 82 | [ SP $ \m c h -> setOG l $ O.strOption $ optionMods fn m c h ] 83 | 84 | freeTextFieldDefST fn l = OG 85 | [ SP $ \m c h -> setOG l $ O.strOption $ optionMods fn m c h ] 86 | 87 | instance OptionsGrammar ParsecPretty OptparseGrammar where 88 | help h (OG ps) = OG 89 | [ SP $ \m c _h -> p m c (Just h) 90 | | SP p <- ps 91 | ] 92 | 93 | metahelp m h (OG ps) = OG 94 | [ SP $ \_m c _h -> p (Just m) c (Just h) 95 | | SP p <- ps 96 | ] 97 | 98 | metaCompleterHelp m c h (OG ps) = OG 99 | [ SP $ \_m _c _h -> p (Just m) (Just c) (Just h) 100 | | SP p <- ps 101 | ] 102 | 103 | -- example: @rangeField tests #cfgTests anyVersion@, generates options: 104 | -- 105 | -- --tests 106 | -- --no-tests 107 | -- --tests-jobs RANGE 108 | -- 109 | -- where the --no-tests has help, because it's not default. 110 | -- 111 | rangeField fn l s = OG 112 | [ SP $ \_m _c h -> setOG l $ O.flag' C.anyVersion $ flagMods fn (th h) 113 | , SP $ \_m _c h -> setOG l $ O.flag' C.noVersion $ flagMods ("no-" <> fn) (fh h) 114 | , SP $ \_m _c _h -> setOG l $ O.option readMParsec $ O.long (fromUTF8BS $ fn <> "-jobs") <> O.metavar "RANGE" 115 | ] 116 | where 117 | def = C.aview l s 118 | th h = if equivVersionRanges def C.anyVersion then Nothing else h 119 | fh h = if equivVersionRanges def C.anyVersion then h else Nothing 120 | 121 | optionMods :: (O.HasName mods, O.HasCompleter mods, O.HasMetavar mods) 122 | => C.FieldName -> Maybe MetaVar -> Maybe O.Completer -> Maybe Help -> O.Mod mods a 123 | optionMods fn mmetavar mcompl mhelp = flagMods fn mhelp 124 | <> maybe mempty O.metavar mmetavar 125 | <> maybe mempty O.completer mcompl 126 | 127 | flagMods :: O.HasName mods => C.FieldName -> Maybe Help -> O.Mod mods a 128 | flagMods fn mhelp = O.long (fromUTF8BS fn) 129 | <> maybe mempty O.help mhelp 130 | 131 | readMParsec :: C.Parsec a => O.ReadM a 132 | readMParsec = O.eitherReader C.eitherParsec 133 | 134 | setOG :: C.ALens' s a -> O.Parser a -> O.Parser (s -> s) 135 | setOG l = fmap (l C.#~) 136 | 137 | setOptionalOG :: C.ALens' s (Maybe a) -> O.Parser a -> O.Parser (s -> s) 138 | setOptionalOG l = fmap $ \x -> l C.#~ Just x 139 | 140 | monoidOG :: Monoid a => C.ALens' s a -> O.Parser a -> O.Parser (s -> s) 141 | monoidOG l = fmap $ \x -> l C.#%~ \y -> mappend y x 142 | -------------------------------------------------------------------------------- /src/HaskellCI/Package.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.Package where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import qualified Distribution.Types.GenericPackageDescription as C 6 | 7 | import HaskellCI.Compiler 8 | 9 | data Package = Pkg 10 | { pkgName :: String 11 | , pkgJobs :: Set CompilerVersion 12 | , pkgDir :: FilePath 13 | , pkgGpd :: C.GenericPackageDescription 14 | } 15 | deriving (Eq, Show, Generic) 16 | -------------------------------------------------------------------------------- /src/HaskellCI/ParsecUtils.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.ParsecUtils where 2 | 3 | import HaskellCI.Prelude 4 | 5 | import System.Directory (doesFileExist) 6 | import System.Exit (exitFailure) 7 | import System.IO (hPutStr, stderr) 8 | 9 | import qualified Data.ByteString as BS 10 | import qualified Distribution.Fields as C 11 | import qualified Distribution.Fields.LexerMonad as C (toPWarnings) 12 | import qualified Distribution.Parsec as C 13 | import qualified Text.Parsec as P 14 | 15 | import Cabal.Parse 16 | 17 | readAndParseFile 18 | :: ([C.Field C.Position] -> C.ParseResult a) -- ^ File fields to final value parser 19 | -> FilePath -- ^ File to read 20 | -> IO a 21 | readAndParseFile parser fpath = do 22 | exists <- doesFileExist fpath 23 | unless exists $ do 24 | putStrLn $ "Error Parsing: file \"" ++ fpath ++ "\" doesn't exist. Cannot continue." 25 | exitFailure 26 | bs <- BS.readFile fpath 27 | run bs $ case C.readFields' bs of 28 | Right (fs, lexWarnings) -> do 29 | C.parseWarnings (C.toPWarnings lexWarnings) 30 | parser fs 31 | Left perr -> C.parseFatalFailure pos (show perr) where 32 | ppos = P.errorPos perr 33 | pos = C.Position (P.sourceLine ppos) (P.sourceColumn ppos) 34 | where 35 | run :: BS.ByteString -> C.ParseResult a -> IO a 36 | run bs r = case C.runParseResult r of 37 | (ws, Right x) -> do 38 | hPutStr stderr $ renderParseError (ParseError fpath bs [] ws) 39 | return x 40 | (ws, Left (_, es)) -> do 41 | hPutStr stderr $ renderParseError (ParseError fpath bs (toList es) ws) 42 | exitFailure 43 | -------------------------------------------------------------------------------- /src/HaskellCI/Prelude.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE PatternSynonyms #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# OPTIONS_GHC -Wno-orphans #-} 4 | module HaskellCI.Prelude ( 5 | module Prelude.Compat, 6 | module X, 7 | module HaskellCI.Prelude, 8 | ) where 9 | 10 | import Prelude.Compat hiding (head, tail) 11 | 12 | import Algebra.Lattice as X (BoundedJoinSemiLattice (..), BoundedLattice, BoundedMeetSemiLattice (..), Lattice (..)) 13 | import Control.Applicative as X (liftA2, (<|>)) 14 | import Control.Exception as X (Exception (..), IOException, handle) 15 | import Control.Monad as X (ap, unless, void, when) 16 | import Control.Monad.Catch as X (MonadCatch, MonadMask, MonadThrow) 17 | import Control.Monad.IO.Class as X (MonadIO (..)) 18 | import Data.Bifoldable as X (Bifoldable (..)) 19 | import Data.Bifunctor as X (Bifunctor (..)) 20 | import Data.Binary as X (Binary) 21 | import Data.Bitraversable as X (Bitraversable (..), bifoldMapDefault, bimapDefault) 22 | import Data.ByteString as X (ByteString) 23 | import Data.Char as X (isSpace, isUpper, toLower) 24 | import Data.Coerce as X (coerce) 25 | import Data.Either as X (partitionEithers) 26 | import Data.Foldable as X (for_, toList, traverse_) 27 | import Data.Foldable.WithIndex as X (ifoldr) 28 | import Data.Function as X (on) 29 | import Data.Functor.Compat as X ((<&>)) 30 | import Data.Functor.Identity as X (Identity (..)) 31 | import Data.List as X (intercalate, isPrefixOf, nub, stripPrefix, tails) 32 | import Data.List.NonEmpty as X (NonEmpty (..), groupBy) 33 | import Data.Maybe as X (fromMaybe, isJust, isNothing, listToMaybe, mapMaybe) 34 | import Data.Map.Strict as X (Map) 35 | import Data.Proxy as X (Proxy (..)) 36 | import Data.Set as X (Set) 37 | import Data.String as X (IsString (fromString)) 38 | import Data.Text as X (Text) 39 | import Data.Typeable as X (Typeable) 40 | import Data.Void as X (Void) 41 | import GHC.Generics as X (Generic) 42 | import Network.URI as X (URI, parseURI, uriToString) 43 | import Numeric.Natural as X (Natural) 44 | import Text.Read as X (readMaybe) 45 | 46 | import Data.Generics.Lens.Lite as X (field) 47 | import Distribution.Compat.Lens as X (over, toListOf, (&), (.~), (?~), (^.)) 48 | 49 | import Distribution.Parsec as X (simpleParsec) 50 | import Distribution.Pretty as X (prettyShow) 51 | import Distribution.Utils.Generic as X (fromUTF8BS, toUTF8BS) 52 | import Distribution.Version as X (Version, VersionRange, anyVersion, mkVersion, noVersion) 53 | 54 | import qualified Distribution.Compat.CharParsing as C 55 | import qualified Distribution.Parsec as C 56 | import qualified Distribution.Pretty as C 57 | import qualified Distribution.Version as C 58 | import qualified Text.PrettyPrint as PP 59 | 60 | import Data.Functor.WithIndex.Instances () 61 | 62 | ------------------------------------------------------------------------------- 63 | -- Extras 64 | ------------------------------------------------------------------------------- 65 | 66 | mapped :: forall f a b. Functor f => (a -> Identity b) -> f a -> Identity (f b) 67 | mapped = coerce (fmap :: (a -> b) -> f a -> f b) 68 | 69 | head :: NonEmpty a -> a 70 | head (x :| _) = x 71 | 72 | -- $setup 73 | -- >>> import Text.Read (readMaybe) 74 | 75 | -- | Return the part after the first argument 76 | -- 77 | -- >>> afterInfix "BAR" "FOOBAR XYZZY" 78 | -- Just " XYZZY" 79 | -- 80 | afterInfix :: Eq a => [a] -> [a] -> Maybe [a] 81 | afterInfix needle haystack = findMaybe (afterPrefix needle) (tails haystack) 82 | 83 | -- | 84 | -- 85 | -- >>> afterPrefix "FOO" "FOOBAR" 86 | -- Just "BAR" 87 | -- 88 | afterPrefix :: Eq a => [a] -> [a] -> Maybe [a] 89 | afterPrefix needle haystack 90 | | needle `isPrefixOf` haystack = Just (drop (length needle) haystack) 91 | | otherwise = Nothing 92 | 93 | -- | 94 | -- 95 | -- >>> findMaybe readMaybe ["foo", "1", "bar"] :: Maybe Int 96 | -- Just 1 97 | -- 98 | findMaybe :: (a -> Maybe b) -> [a] -> Maybe b 99 | findMaybe f = foldr (\a b -> f a <|> b) Nothing 100 | 101 | -- | Whether two ranges are equivalent. 102 | equivVersionRanges :: C.VersionRange -> C.VersionRange -> Bool 103 | equivVersionRanges = on (==) C.asVersionIntervals 104 | 105 | -- | Functional dependency variant of @Representable@ class. 106 | class Representable i f | f -> i where 107 | tabulate :: (i -> a) -> f a 108 | index :: f a -> i -> a 109 | 110 | instance Representable a ((->) a) where 111 | tabulate = id 112 | index = ($) 113 | 114 | ------------------------------------------------------------------------------- 115 | -- Orphans 116 | ------------------------------------------------------------------------------- 117 | 118 | instance Lattice VersionRange where 119 | (/\) = C.intersectVersionRanges 120 | (\/) = C.unionVersionRanges 121 | 122 | instance C.Parsec Natural where 123 | parsec = C.integral 124 | 125 | instance C.Pretty Natural where 126 | pretty = PP.text . show 127 | 128 | type a := b = (a, b) 129 | 130 | pattern (:=) :: a -> b -> a := b 131 | pattern a := b = (a, b) 132 | 133 | infixr 0 := 134 | -------------------------------------------------------------------------------- /src/HaskellCI/SetupMethod.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module HaskellCI.SetupMethod ( 3 | SetupMethod (..), 4 | PerSetupMethod (..), 5 | ) where 6 | 7 | import HaskellCI.Prelude 8 | import HaskellCI.YamlSyntax 9 | 10 | data SetupMethod = HVRPPA | GHCUP | GHCUPvanilla | GHCUPprerelease 11 | deriving (Eq, Ord, Enum, Bounded, Show) 12 | 13 | instance ToYaml SetupMethod where 14 | toYaml HVRPPA = "hvr-ppa" 15 | toYaml GHCUP = "ghcup" 16 | toYaml GHCUPvanilla = "ghcup-vanilla" 17 | toYaml GHCUPprerelease = "ghcup-prerelease" 18 | 19 | data PerSetupMethod a = PerSetupMethod 20 | { hvrPpa :: a 21 | , ghcup :: a 22 | , ghcupVanilla :: a 23 | , ghcupPrerelease :: a 24 | } 25 | deriving (Show, Functor, Foldable, Traversable, Generic, Binary) 26 | 27 | instance Representable SetupMethod PerSetupMethod where 28 | index f HVRPPA = hvrPpa f 29 | index f GHCUP = ghcup f 30 | index f GHCUPvanilla = ghcupVanilla f 31 | index f GHCUPprerelease = ghcupPrerelease f 32 | 33 | tabulate f = PerSetupMethod 34 | { hvrPpa = f HVRPPA 35 | , ghcup = f GHCUP 36 | , ghcupVanilla = f GHCUPvanilla 37 | , ghcupPrerelease = f GHCUPprerelease 38 | } 39 | -------------------------------------------------------------------------------- /src/HaskellCI/Sh.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE DeriveFunctor #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | module HaskellCI.Sh ( 5 | Sh (..), 6 | isComment, 7 | shToString, 8 | shlistToString, 9 | MonadSh (..), 10 | sh, 11 | ShM (..), 12 | runSh, 13 | liftSh, 14 | HsCiError (..), 15 | FromHsCiError (..), 16 | ) where 17 | 18 | import HaskellCI.Prelude 19 | 20 | #ifdef MIN_VERSION_ShellCheck 21 | import ShellCheck.Checker (checkScript) 22 | import qualified ShellCheck.Interface as SC 23 | #endif 24 | 25 | import HaskellCI.Error 26 | import HaskellCI.MonadErr 27 | 28 | ------------------------------------------------------------------------------- 29 | -- shell command 30 | ------------------------------------------------------------------------------- 31 | 32 | data Sh 33 | = Sh String -- ^ command 34 | | Comment String -- ^ comment 35 | deriving Show 36 | 37 | isComment :: Sh -> Bool 38 | isComment (Comment _) = True 39 | isComment (Sh _) = False 40 | 41 | shToString :: Sh -> String 42 | shToString (Comment c) = "# " ++ c 43 | shToString (Sh x) = x 44 | 45 | shlistToString :: [Sh] -> String 46 | shlistToString shs = unlines (map shToString shs) 47 | 48 | ------------------------------------------------------------------------------- 49 | -- class 50 | ------------------------------------------------------------------------------- 51 | 52 | class Monad m => MonadSh m where 53 | -- | Write shell command 54 | sh' :: [Integer] -> String -> m () 55 | 56 | -- | Write comment 57 | comment :: String -> m () 58 | 59 | -- | Commented block. 60 | -- 61 | -- If the block is empty (or comments only), nothing might be written. 62 | commentedBlock :: String -> m () -> m () 63 | 64 | sh :: MonadSh m => String -> m () 65 | sh = sh' 66 | [ 2034 -- VAR appears unused. Verify it or export it. 67 | , 2086 -- SC2086: Double quote to prevent globbing and word splitting. 68 | , 2002 -- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead. 69 | -- TODO: because HEREDOC doesn't work 70 | , 2129 -- SC2129: Consider using { cmd1; cmd2; } >> file instead of individual redirects 71 | , 2154 -- SC2154: PKGDIR_splitmix is referenced but not assigned. 72 | 73 | , 1102 74 | , 2046 75 | , 2210 76 | {- 77 | SC1102: Shells disambiguate $(( differently or not at all. For $(command substitution), add space after $( . For $((arithmetics)), fix parsing errors. 78 | SC2046: Quote this to prevent word splitting. 79 | SC2210: This is a file redirection. Was it supposed to be a comparison or fd operation? 80 | -} 81 | 82 | ] 83 | 84 | ------------------------------------------------------------------------------- 85 | -- implementation 86 | ------------------------------------------------------------------------------- 87 | 88 | newtype ShM a = ShM { unShM :: ([Sh] -> [Sh]) -> Either HsCiError ([Sh] -> [Sh], a) } 89 | deriving (Functor) 90 | 91 | runSh :: (MonadErr e m, FromHsCiError e) => ShM () -> m [Sh] 92 | runSh (ShM f) = case f id of 93 | Left err -> throwErr (fromHsCiError err) 94 | Right (g, ()) -> return (g []) 95 | 96 | liftSh :: Sh -> ShM () 97 | liftSh s = ShM $ \shs -> Right (shs . (s :), ()) 98 | 99 | instance Applicative ShM where 100 | pure x = ShM $ \shs -> Right (shs, x) 101 | (<*>) = ap 102 | 103 | instance Monad ShM where 104 | return = pure 105 | 106 | m >>= k = ShM $ \shs0 -> do 107 | (shs1, x) <- unShM m shs0 108 | (shs2, y) <- unShM (k x) shs1 109 | return (shs2, y) 110 | 111 | instance MonadErr HsCiError ShM where 112 | throwErr err = ShM $ \_ -> Left err 113 | 114 | unsafeSh :: String -> ShM () 115 | unsafeSh x = ShM $ \shs -> Right (shs . (Sh x :), ()) 116 | 117 | instance MonadSh ShM where 118 | #ifndef MIN_VERSION_ShellCheck 119 | sh' _ = unsafeSh 120 | #else 121 | sh' excl cmd 122 | | null (SC.crComments res) = unsafeSh cmd 123 | | otherwise = throwErr $ ShellCheckError $ unlines $ 124 | ("ShellCheck! " ++ cmd) : 125 | [ "SC" ++ show (SC.cCode c) ++ ": " ++ SC.cMessage c 126 | | pc <- SC.crComments res 127 | , let c = SC.pcComment pc 128 | ] 129 | where 130 | res = runIdentity $ checkScript iface spec 131 | iface = SC.mockedSystemInterface [] 132 | spec = SC.emptyCheckSpec 133 | { SC.csFilename = "stdin" 134 | , SC.csScript = cmd 135 | , SC.csExcludedWarnings = excl 136 | , SC.csShellTypeOverride = Just SC.Sh 137 | } 138 | #endif 139 | comment x = ShM $ \shs -> Right (shs . (Comment x :), ()) 140 | 141 | commentedBlock c m = case runSh m of 142 | Left err -> throwErr err 143 | Right shs 144 | | all isComment shs -> pure () 145 | | otherwise -> ShM $ \shs1 -> Right 146 | (shs1 . (\shs2 -> Comment c : shs ++ shs2), ()) 147 | -------------------------------------------------------------------------------- /src/HaskellCI/TestedWith.hs: -------------------------------------------------------------------------------- 1 | module HaskellCI.TestedWith ( 2 | TestedWithJobs (..), 3 | checkVersions, 4 | ) where 5 | 6 | import Prelude () 7 | import Prelude.Compat 8 | 9 | import Control.Applicative ((<|>)) 10 | import Data.List (intercalate) 11 | 12 | import qualified Data.Foldable as F 13 | import qualified Data.Set as S 14 | import qualified Distribution.Compat.CharParsing as C 15 | import qualified Distribution.Parsec as C 16 | import qualified Distribution.Pretty as C 17 | import qualified Text.PrettyPrint as PP 18 | 19 | import Cabal.Project 20 | import HaskellCI.Compiler 21 | import HaskellCI.Package 22 | 23 | data TestedWithJobs 24 | = TestedWithUniform 25 | | TestedWithAny 26 | deriving (Eq, Show) 27 | 28 | instance C.Parsec TestedWithJobs where 29 | parsec = TestedWithUniform <$ C.string "uniform" 30 | <|> TestedWithAny <$ C.string "any" 31 | 32 | instance C.Pretty TestedWithJobs where 33 | pretty TestedWithUniform = PP.text "uniform" 34 | pretty TestedWithAny = PP.text "any" 35 | 36 | ------------------------------------------------------------------------------- 37 | -- Selection 38 | ------------------------------------------------------------------------------- 39 | 40 | checkVersions 41 | :: TestedWithJobs 42 | -> Project a b Package 43 | -> Either [String] (S.Set CompilerVersion, Project a b Package) 44 | checkVersions TestedWithUniform = checkVersionsUniform 45 | checkVersions TestedWithAny = checkVersionsAny 46 | 47 | checkVersionsUniform 48 | :: Project a b Package 49 | -> Either [String] (S.Set CompilerVersion, Project a b Package) 50 | checkVersionsUniform prj | null (prjPackages prj) = Left ["Error reading cabal file(s)!"] 51 | checkVersionsUniform prj = do 52 | let (errors, names) = F.foldl' collectConfig mempty prj 53 | if not (null errors) 54 | then Left errors 55 | else Right (allVersions, prj { prjPackages = names, prjOptPackages = [] }) 56 | where 57 | allVersions :: S.Set CompilerVersion 58 | allVersions = F.foldMap pkgJobs prj 59 | 60 | collectConfig 61 | :: ([String], [Package]) 62 | -> Package 63 | -> ([String], [Package]) 64 | collectConfig aggregate pkg = 65 | aggregate <> (errors, [pkg]) 66 | where 67 | testWith = pkgJobs pkg 68 | symDiff a b = S.union a b `S.difference` S.intersection a b 69 | diff = symDiff testWith allVersions 70 | missingVersions = map dispGhcVersion $ S.toList diff 71 | errors | S.null diff = [] 72 | | otherwise = pure $ mconcat 73 | [ pkgName pkg 74 | , " is missing tested-with annotations for: " 75 | ] ++ intercalate "," missingVersions 76 | 77 | checkVersionsAny 78 | :: Project a b Package 79 | -> Either [String] (S.Set CompilerVersion, Project a b Package) 80 | checkVersionsAny prj | null (prjPackages prj) = Left ["Error reading cabal file(s)!"] 81 | checkVersionsAny prj = 82 | Right (allVersions, prj) 83 | where 84 | allVersions :: S.Set CompilerVersion 85 | allVersions = F.foldMap pkgJobs prj 86 | -------------------------------------------------------------------------------- /src/HaskellCI/Tools.hs: -------------------------------------------------------------------------------- 1 | -- Helpers to generate steps using tools: hlint and doctest 2 | module HaskellCI.Tools ( 3 | -- * Doctest 4 | doctestJobVersionRange, 5 | doctestArgs, 6 | ) where 7 | 8 | import HaskellCI.Prelude 9 | 10 | import qualified Distribution.PackageDescription as C 11 | import qualified Distribution.PackageDescription.Configuration as C 12 | import qualified Distribution.Pretty as C 13 | import qualified Distribution.Types.VersionRange as C 14 | import qualified Distribution.Utils.Path as C 15 | import qualified Distribution.Version as C 16 | 17 | import qualified Distribution.Types.BuildInfo.Lens as L 18 | import qualified Distribution.Types.PackageDescription.Lens as L 19 | 20 | import HaskellCI.Compiler 21 | 22 | ------------------------------------------------------------------------------- 23 | -- Doctest 24 | ------------------------------------------------------------------------------- 25 | 26 | doctestJobVersionRange :: CompilerRange 27 | doctestJobVersionRange = RangeGHC /\ Range (C.orLaterVersion $ C.mkVersion [8,0]) 28 | 29 | -- | Modules arguments to the library 30 | -- 31 | -- * We check the library component 32 | -- 33 | -- * If there are hs-source-dirs, use them 34 | -- 35 | -- * otherwise use exposed + other modules 36 | -- 37 | -- * Also add default-extensions 38 | -- 39 | doctestArgs :: C.GenericPackageDescription -> [[String]] 40 | doctestArgs gpd = nub $ 41 | [ libraryModuleArgs c 42 | | c <- toListOf (L.library . traverse) (C.flattenPackageDescription gpd) 43 | ] ++ 44 | [ libraryModuleArgs c 45 | | c <- toListOf (L.subLibraries . traverse) (C.flattenPackageDescription gpd) 46 | ] 47 | 48 | libraryModuleArgs :: C.Library -> [String] 49 | libraryModuleArgs l 50 | | null dirsOrMods = [] 51 | | otherwise = lang ++ exts ++ dirsOrMods 52 | where 53 | bi = l ^. L.buildInfo 54 | 55 | dirsOrMods 56 | | null (C.hsSourceDirs bi) = map C.prettyShow (C.exposedModules l) 57 | | otherwise = map C.getSymbolicPath $ C.hsSourceDirs bi 58 | 59 | lang = maybe [] (pure . ("-X" ++) . C.prettyShow) (C.defaultLanguage bi) 60 | 61 | exts = map (("-X" ++) . C.prettyShow) (C.defaultExtensions bi) 62 | -------------------------------------------------------------------------------- /src/HaskellCI/VersionInfo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | module HaskellCI.VersionInfo ( 3 | haskellCIVerStr, 4 | dependencies, 5 | ) where 6 | 7 | import HaskellCI.Prelude 8 | 9 | import qualified Data.Map as Map 10 | 11 | haskellCIVerStr :: String 12 | haskellCIVerStr = VERSION_haskell_ci 13 | 14 | dependencies :: Map String String 15 | dependencies = Map.fromList 16 | [ ("aeson", VERSION_aeson) 17 | , ("base", VERSION_base) 18 | , ("base-compat", VERSION_base_compat) 19 | , ("bytestring", VERSION_bytestring) 20 | , ("Cabal-syntax", VERSION_Cabal_syntax) 21 | , ("cabal-install-parsers", VERSION_cabal_install_parsers) 22 | , ("containers", VERSION_containers) 23 | , ("directory", VERSION_directory) 24 | , ("exceptions", VERSION_exceptions) 25 | , ("filepath", VERSION_filepath) 26 | , ("generic-lens-lite", VERSION_generic_lens_lite) 27 | , ("HsYAML", VERSION_HsYAML) 28 | , ("lattices", VERSION_lattices) 29 | , ("mtl", VERSION_mtl) 30 | , ("network-uri", VERSION_network_uri) 31 | , ("optparse-applicative", VERSION_optparse_applicative) 32 | , ("parsec", VERSION_parsec) 33 | , ("pretty", VERSION_pretty) 34 | , ("process", VERSION_process) 35 | #ifdef VERSION_ShellCheck 36 | , ("ShellCheck", VERSION_ShellCheck) 37 | #endif 38 | , ("temporary", VERSION_temporary) 39 | , ("text", VERSION_text) 40 | , ("transformers", VERSION_transformers) 41 | ] 42 | -------------------------------------------------------------------------------- /src/System/Path/Extras.hs: -------------------------------------------------------------------------------- 1 | module System.Path.Extras where 2 | 3 | {- 4 | import System.Path.Unsafe (Path (..)) 5 | import System.Path (Absolute, Unrooted) 6 | 7 | import qualified System.FilePath as Native 8 | import qualified System.Directory as Native 9 | 10 | makeRelative :: Path Absolute -> Path Absolute -> Path Unrooted 11 | makeRelative (Path a) (Path b) = Path (Native.makeRelative a b) 12 | 13 | getCurrentDirectory :: IO (Path Absolute) 14 | getCurrentDirectory = Path <$> Native.getCurrentDirectory 15 | -} 16 | --------------------------------------------------------------------------------