Releases and distributable binaries

Starting with 0.2.1.0 haskell-language-server provides pre-built binaries on each GitHub release. These binaries are used by the vscode-hie-server extension to provide automatic installation for users on VS Code, but they can also be installed manually when added to the path.

Starting with 0.8.0.0 haskell-language-server and all its related packages (core libraries like ghcide, plugins and hls itself) is being released in hackage as well. This allow cabal users to install it with cabal install haskell-language-server and it is being used in nix environments.

Minimal checklist

prerelease sanity checks

  • [ ] set the supported GHC versions and their corresponding cabal project-files in bindist/ghcs according to the GHC version deprecation policy

  • [ ] trigger manually the hackage workflow without uploading the packages

  • [ ] trigger manually the build workflow

  • [ ] create a prerelease tag ${version}-check-gitlab and push it to the project repo in gitlab to check the build is fine

github release

  • [ ] generate the list of pull requests finished since the last release using the haskell script in the project root. Nix users should run command gen-hls-changelogs (a wrapper of the script) in nix-shell instead.

  • [ ] add that list to the actual Changelog with a description of the release.

  • [ ] bump up versions of changed packages. All are optional but haskell-language-server itself.

  • [ ] create the tag and make an initial prerelease to trigger the ci workflow (see details below)

  • [ ] contact ghcup team (#haskell-ghcup irc channel or via its repo) to try to sync our release and its inclusion in ghcup

  • [ ] in the github release edit page, check the attached binaries and the release description (usually the changelog entry) and uncheck the prerelease box

  • [ ] make public the release in the usual social channels (not required but useful to spread the word :slightly_smiling_face:):

    • [ ] irc

    • [ ] matrix

    • [ ] twitter

    • [ ] discord

    • [ ] discourse

    • [ ] reddit

hackage release

  • [ ] bump up package versions following the pvp specification if they are not already updated. You could use policeman to help with this step.

  • [ ] create ${version}-hackage branch to trigger the hackage github workflow which will upload all changed packages to hackage as candidates

  • [ ] for new plugins or packages, update hackage uploaders to add the author of the plugin/package and some hls maintainer(s) other than the owner of the hackage api key used to upload them (it has to be done by the owner of the api key, actually @pepeiborra)

  • [ ] check manually candidates in hackage

  • [ ] publish them definitely triggering a manual run of the hackage workflow setting the upload and publish inputs to true

ghcup release

Making a new release of haskell-language-server in github

Go to the GitHub releases page for haskell-language-server and start to create a new release. Choose or create a tag, fill out the release notes etc., but before you create it make sure to check the pre-release checkbox. This will prevent VS Code *extension users from attempting to install this version before the binaries are created.

Once the release is created the GitHub Actions workflow will be kicked off and will start creating binaries. They will be gzipped and uploaded to the release.

It creates a haskell-language-server-${os}-${ghcVersion} binary for each platform (Linux, macOS, Windows) and each GHC version that we currently support, as well as a haskell-language-server-wrapper-${os} binary for each platform. Note that only one wrapper binary is created per platform, and it should be built with the most recent GHC version.

ghcup

It creates a haskell-language-server-${os}-${hlsVersion}.tar.gz tarball with the binaries for all supported ghc versions and a custom source tarball to help downstream publishers in the distribution of the release.

The most prominent publisher using them is ghcup.

checksums

The sha256 checksum of all artifacts are listed in the SHA256SUMS release file.

Distributable binaries

In order to compile a hls binary on one machine and have it run on another, you need to make sure there are no hardcoded paths or data-files.

ghc libdir

One noteable thing which cannot be hardcoded is the GHC libdir – this is a path to /usr/local/lib/ghc or something like that, which was previously baked in at compile-time with ghc-paths. Note that with static binaries we can no longer use this because the GHC libdir of the GitHub Actions machine will most almost certainly not exist on the end user’s machine. Therefore, hie-bios provides getGhcRuntimeLibDir to obtain this path on the fly by consulting the cradle.

Static binaries

We use the word “distributable” here because technically only the Linux builds are static. They are built by passing --enable-executable-static to cabal. Static binaries don’t really exist on macOS, and there are issues with proprietary code being linked in on Windows. However, the .dylibs linked on macOS are all already provided by the system:

$ objdump -macho --dylibs-used  haskell-language-server
haskell-language-server:
  /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
  /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
  /usr/lib/libcharset.1.dylib (compatibility version 2.0.0, current version 2.0.0)

The GitHub Actions workflow

It just kicks off a matrix of jobs varying across GHC versions and OSs, building the binaries with Cabal and extracting them from the dist-newstyle directory. The binaries are built with -O2.

Failing workflow

If the workflow fail and some binaries has been already uploaded, those artifacts must be removed and the build should be re-ran (the build tries to upload themm all and it fails if there is an existing artifact with the same name)

Updating release artifacts

IMPORTANT: release artifacts must not be modified, cause it would break its secure distribution using their hashes. We should only add new ones.

To manually upload a new binary we should:

  • Add the new tar/zip following the name conventions of existing ones

    • haskell-language-server-${os}-${ghcVersion}.gz for Linux and macOS and haskell-language-server-Windows-${ghcVersion}.exe.zip for Windows

    • the binary inside the gz file is named haskell-language-server-${ghcVersion} (with the .exe extension for Windows). Note that the binary name does not contain the ${os} part.

  • Add the executable to the existing tar haskell-language-server-${os}-${ghcVersion}.tar.gz locally and upload it under a new name haskell-language-server-${os}-${ghcVersion}-rev${n}.tar.gz following the same schema for the binary as the previous one.

    • -rev${n} is the next revision number of the tarball, starting at 1.

    • we should contact users of the tarball (particularly ghcup) to notify the change

Hackage release workflow

We aim to do hackage releases following the github ones described above. To help in that job we have added a github workflow

That script checks, generates the tar.gz files, unpacks and builds them in isolation against hackage head if the package version in the branch is different from hackage. If the package in the branch has the same version as the released one, it will check the relevant files have not changed and will throw an error otherwise.

You can trigger the build manually.

The script will upload the tarballs as candidates, maintainers will have to check and publish them definitely.

haskell gitlab release pipeline

The project is present in the haskell gitlab server: https://gitlab.haskell.org/haskell/haskell-language-server The main motivation is to leverage the ci infrastructure which includes architectures not included in the github ci. The specific architectures only available through gitlab are: aarch64-darwin, aarch64-linux, armv7-linux, x86_64-freebsd12, x86_64-freebsd13, x86_64-linux-alpine The gitlab pipeline uses the configuration file .gitlab-ci.yml and the sh scripts in .gitlab It is triggered by pushing a tag to the gitlab repo.