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
PrinterOpts
inOrmolu.Config.Types
- Follow all the compiler errors
-
Make the required changes to change styling based on the configuration option
-
Update the in-repo
fourmolu.yaml
with your configuration option set to most closely imitate Ormolu's default style -
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 your new option to the "Configuration" section in
README.md
- Both in the table and in the example
fourmolu.yaml
files
- Both in the table and in the example
-
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 "Available options" table in the
README
with the options sorted by popularity/importance (using your best judgement, without too much churn every release) - Ensure the options are in the same order in the
fourmolu.yaml
file andREADME
examples - Ensure the options are in the same order in
Ormolu.Config.Types
,overFieldsM
, andprinterOptsMeta
- Ensure the
PrinterOptsSpec.hs
tests are also in the same order as the options
- Re-order the "Available options" table in the
-
-
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
-
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.
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 [email protected]:tweag/ormolu
- Check out a new branch:
git switch -c merge-ormolu
- 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
-
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):.github/workflows/binaries.yml
.buildkite/
CONTRIBUTING.md
DESIGN.md
format.sh
nix/
shell.nix
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
-
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
default.nix
is changed, manually verify that all end-to-end tests are accounted for. For example,./region-tests/
is one directory of tests, which is captured in thefourmolu:region-tests
test suite, where every test indefault.nix
has been ported into the Haskell test suite. -
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, so Fourmolu can't be fully.
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
.