Some things to keep in mind when making changes:
- Make the minimal amount of changes
- Avoid refactoring where possible, don't reformat untouched code
- Since we continuously merge in changes from Ormolu, reducing the number of potential conflicts goes a long way towards maintainability of this project.
- This includes behavior changes that drastically change how
fourmolu
formats Fourmolu's source code itself
- Add a file to
changelog.d/
when applicable (seechangelog.d/README.md
)
The Fourmolu test suite contains the following types of tests:
- Tests inherited from Ormolu
- Unit tests specific for Fourmolu
- Integration tests
All of these tests can be run with cabal test
or stack test
.
To regenerate the printer test outputs (data/examples/**/*-four-out.hs
), set ORMOLU_REGENERATE_EXAMPLES=1
in the environment before running test
. Generally, this should not change any of the *-out.hs
files, but it might change the *-four-out.hs
files, if the setting in defaultPrinterOpts
is different from the one in fourmolu.yaml
After building from source (see README.md
), you can run Fourmolu with
scripts/run-fourmolu.sh --mode=inplace ...
This script automatically detects whether you built fourmolu
with Stack or Cabal. If the auto-detection isn't working out, you can override it by setting export BUILD_TYPE={stack,cabal}
in your environment.
This is automatically run on Fourmolu's source code in the pre-commit hooks (see the "Pre-commit hooks" section) and is checked in CI. If you're not using the pre-commit hooks, use the above command to manually style the files you changed (see .pre-commit-config.yaml
for the files to exclude).
We highly recommend turning on pre-commit hooks to run checks every time you commit. To do so, install pre-commit
and run pre-commit install
in this directory.
This is optional, but is run in CI regardless.
Considering configurability is the raison d'être of Fourmolu, you're probably making a change that involves adding a new configuration option. Ideally, you've already opened an issue asking for thoughts on the new configuration. Assuming you've already done all that, here's a checklist to follow to ensure you've touched all the right places:
-
Add the configuration option to
config/FourmoluConfig/ConfigData.hs
- Set
sinceVersion
toNothing
- If the option requires a custom data type, add one to
allFieldTypes
- Set
-
Regenerate files with
config/generate.sh
-
Make the required changes to change styling based on the configuration option
-
Add a test case to
Ormolu.Config.PrinterOptsSpec
- Add a corresponding
data/fourmolu/<label>/input.hs
file
- Add a corresponding
-
Regenerate test outputs (see the "Running tests" section above)
-
Add documentation in
web/site/pages/config/<name>.md
-
Add a file to
changelog.d/
(seechangelog.d/README.md
)
We often want to immediately see how changes to Fourmolu's source code affect outputs. Try adding something like this to Ormolu.hs
:
import qualified Data.Text.IO as T
import System.Directory (getHomeDirectory)
import System.FilePath ((</>))
main :: IO ()
main = do
dir <- (</> "Desktop") <$> getHomeDirectory
ormoluFile conf (dir </> "In.hs") >>= T.writeFile (dir </> "Out.hs")
where
conf =
defaultConfig
{ cfgUnsafe = True,
cfgPrinterOpts =
defaultPrinterOpts
{ poCommaStyle = pure Trailing
}
}
Put some interesting code in In.hs
. The contents of Out.hs
can be kept up to date to reflect the result of running Fourmolu on it, by running:
ghcid -c 'cabal repl' -W -r --reload=$HOME/Desktop/In.hs
To release a new version, do the following workflow:
-
Create a new branch
-
Bump version in
fourmolu.cabal
- All version bumps should follow PvP
-
Curate
CHANGELOG.md
(seechangelog.d/README.md
) -
Curate option order
- Re-order the options in
config/ConfigData.hs
- Sort by popularity/importance (using your best judgement, without too much churn every release)
- Regenerate with
config/generate.sh
- Ensure the
PrinterOptsSpec.hs
tests are also in the same order as the options
- Re-order the options in
-
Update any
sinceVersion
set toNothing
inConfigData.hs
-
Audit
web/site/
docs
-
-
Create PR as usual and merge into
main
- In the
check_sdist
CI job, check the output of thestack sdist
step for any warnings.
- In the
-
Ensure your Hackage token is set in Settings > Secrets > Actions as
HACKAGE_TOKEN_<github_username>
(replace any non alphanumeric characters in username with_
).- Generate a token from
https://hackage.haskell.org/user/<hackage_username>/manage
- Generate a token from
-
Go to the GitHub Actions page, click on the "Release" workflow, and click "Run workflow" on the
main
branch -
Publish the candidate: https://hackage.haskell.org/package/fourmolu/candidates
-
Publish the GitHub release: https://github.com/fourmolu/fourmolu/releases
-
If this is a new major version, update HLS to use it (example). It's rare that we'll be changing our API in a way that requires actual code changes.
-
Publicize on Reddit (https://reddit.com/r/haskell) and Discourse (https://discourse.haskell.org)
Fourmolu aims to continue merging upstream changes in Ormolu. Whenever Ormolu makes a new release (ideally within a week), the following steps should be run to merge the changes into Fourmolu.
cd
into your local copy of the Fourmolu repository- Add Ormolu as an upstream remote:
git remote add ormolu git@github.com:tweag/ormolu
- Check out a new branch:
git switch -c merge-ormolu main
- Pull Ormolu's git history:
git fetch ormolu --no-tags
- Find the commit corresponding to the new Ormolu version and merge it:
git merge <commit> -m 'Merge ormolu-X.Y.Z'
- (Recommended) Switch to diff3 conflicts:
git checkout --conflict=diff3
. This provides more context that might be helpful for resolving conflicts. See docs. - Resolve conflicts + finish merge:
git merge --continue
- Run tests to ensure everything works well:
stack test
- Make a PR and merge as usual
- MAKE SURE TO CREATE A MERGE COMMIT. Don't use the "Squash and merge" or "Rebase and merge" options.
-
Conflicts at the following paths should be resolved by keeping the files DELETED (i.e. if there's a "deleted by us" conflict, use
git rm
to avoid adding the file to our repo):**/.envrc
**/.ormolu
.github/workflows/binaries.yml
CONTRIBUTING.md
DESIGN.md
flake.lock
flake.nix
nix/
ormolu-live/
weeder.dhall
-
Conflicts at the following paths should be resolved by throwing out Ormolu's changes and keeping our changes (i.e. if there's a conflict, use
git checkout --ours
):stack.yaml
.github/workflows/ci.yml
-
The state of the following paths should be the same as they are in Ormolu (i.e. if there's a conflict, use
git checkout --theirs
)expected-failures/
-
If any of the
default.nix
files are changed, manually verify that all end-to-end tests are accounted for. After doing so,git rm
each of them.- For example,
./region-tests/
is one directory of tests, which is captured in theOrmolu.Integration.RegionSpec
test suite, where every test inregion-tests/default.nix
has been ported into the Haskell test suite.
- For example,
-
Any Ormolu additions to
CHANGELOG.md
should NOT be kept, but instead be added to a new file inchangelog.d/
(e.g. namedormolu-X.Y.Z
). Seechangelog.d/README.md
for more details. -
Be careful when editing
fourmolu.cabal
to only change shared things (e.g.tested-with
) and not Fourmolu things (e.g.name
orversion
).
- Regenerate test files (see the "Running tests" section above)
- Remove any redundant Fourmolu output files
./scripts/clean_redundant_examples.py
Ormolu isn't HLint-clean, and making Fourmolu HLint-clean would increase the diff with Ormolu and make merge conflicts more likely, so we shouldn't make unnecessary changes to solve HLint warnings.
If you're using HLS you may wish to disable HLint on this codebase entirely. In VSCode, for example, add "haskell.plugin.hlint.diagnosticsOn": false
to fourmolu/.vscode/settings.json
.