Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4d15bce
Update Stack LTS to 19.33 (GHC 9.0.2)
sodic Oct 10, 2025
286bfbe
Update version bounds to reflect LTS
sodic Oct 10, 2025
058fc0e
Update Stack LTS to 20.44 (GHC 9.6.7)
sodic Oct 10, 2025
8704a6c
Update cache action version
sodic Oct 10, 2025
97aa541
Merge branch 'update-ghc-to-902' into update-ghc-to-9.6.7
sodic Oct 10, 2025
1216371
Update another occurence of actions/cache
sodic Oct 10, 2025
2cf9603
Update tested-with field
sodic Oct 10, 2025
0b4cf95
Update github action
sodic Oct 10, 2025
3411d89
Disable fail fast in GitHub action
sodic Oct 10, 2025
33161c7
Trigger CI
sodic Oct 10, 2025
6af939b
Use native setup haskell action
sodic Oct 10, 2025
b2427a3
Update cabal version
sodic Oct 10, 2025
2dec50c
Update setup haskell action
sodic Oct 10, 2025
7a67311
Merge branch 'update-ghc-to-902' into update-ghc-to-9.6.7
sodic Oct 10, 2025
c01025b
Update GHC to 9.4.5
sodic Oct 10, 2025
5809eda
Trigger CI
sodic Oct 10, 2025
1dcb30a
Change version to trigger CI
sodic Oct 10, 2025
929d766
Solve merge conflict
sodic Oct 10, 2025
1657003
Handle empty file paths in test utils
sodic Oct 29, 2025
de62054
Be explicit about impossible cases in path concat operator
sodic Oct 29, 2025
2aaf470
Set version bounds and test range from GHC 9.4.5 to GHC 9.12.2
sodic Oct 29, 2025
7a5fe5b
Fix lower bound version typo
sodic Oct 29, 2025
0268011
Remove manual stack installation
sodic Oct 29, 2025
a707a42
Remove todo
sodic Oct 29, 2025
7bce43c
Extend lower supported range to GHC 9.0.2
sodic Oct 29, 2025
77d8420
Fix tested-with field
sodic Oct 29, 2025
39ae489
Fix comment next to tested-with
sodic Oct 29, 2025
5ade54b
Fix comment
sodic Oct 29, 2025
3b1abf3
Refactor testing function
sodic Oct 31, 2025
4473715
Extract common dependencies
sodic Oct 31, 2025
6d63f23
Update note on dependency versions
sodic Oct 31, 2025
46a2c0f
Fix formatting
sodic Oct 31, 2025
b265b7c
Fix formatting
sodic Oct 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 17 additions & 42 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,15 @@ on:
pull_request: { branches: [main] }
create: { tags: [v*] }

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
shell: bash

jobs:
cancel:
name: Cancel redundant actions already in progress
runs-on: ubuntu-latest
steps:
- name: Cancel actions in progress of same workflow and same branch
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}

# Check that Haskell code is formatted.
code-formatter:
runs-on: ubuntu-latest
Expand All @@ -31,6 +26,7 @@ jobs:
runs-on: ${{ matrix.os }}
needs: code-formatter
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
Expand All @@ -44,14 +40,14 @@ jobs:
# If you change it, make sure to adjust lower bounds there to reflect the change.
# Also, make sure to adjust tested-with field in package.yaml so that it contains
# corresponding GHC version.
stack-resolver: lts-18.21
stack-resolver: lts-19.33
Copy link
Contributor Author

@sodic sodic Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lower bound for testing, corresponds to GHC 9.0.2., three years old.

This ensures that there are no GHC versions that don't have an appropriate StrongPath version.

In other words, there's no "gap" in supported GHC versions. Whichever version of GHC you are using, there's a Strong Path version for you:

  • Up to 9.0.1 - Strong Path 1.1.4.0
  • After 9.0.2 - Strong path 1.2.0.0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't quite get the second part of the message (everything beyond first sentence), could you explain in simpler way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sentence was non-sensical due to edits, so no wonder you couldn't understand it.

Edited :)


steps:
- name: Checkout the repo
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Cache (Unix)
uses: actions/cache@v2
uses: actions/cache@v4
if: runner.os == 'Linux' || runner.os == 'macOS'
with:
path: |
Expand All @@ -77,7 +73,7 @@ jobs:
restore-keys: |
haskell-${{ runner.os }}-${{ hashFiles('stack.yaml') }}-
- name: Cache (Windows)
uses: actions/cache@v2
uses: actions/cache@v4
if: runner.os == 'Windows'
with:
# C\:sr is where stack installs compiled dependencies.
Expand All @@ -95,32 +91,11 @@ jobs:
if: runner.os == 'macOS'
run: rm -rf ~/.stack/setup-exe-cache

# We are setting up haskell via ghcup instead of using haskell/actions/setup
# because the mentioned gh action can be months late with the latest versions
# of Stack.
- name: Set up Haskell (Stack) via ghcup (Unix)
if: runner.os == 'Linux' || runner.os == 'macOS'
run: |
export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
export BOOTSTRAP_HASKELL_INSTALL_STACK=1
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

- name: Set up Haskell (Stack) via ghcup (Win)
if: runner.os == 'Windows'
run: |
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Command -ScriptBlock ([ScriptBlock]::Create(".{$(Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing)} -InstallStack"))
shell: powershell

# NOTE: I commented out this in favor of manual setup above, since with this action we
# couldn't get the latest version of Stack.
# - name: Set up Haskell (Stack)
# uses: haskell/actions/setup@v1
# with:
# ghc-version: latest
# enable-stack: true
# stack-version: latest
- uses: haskell-actions/setup@v2
with:
ghc-version: latest
enable-stack: true
stack-version: latest

- name: Set Stack resolver
if: matrix.stack-resolver != 'from-stack-yaml'
Expand All @@ -134,10 +109,10 @@ jobs:
stack path --stack-root
stack ghc -- --version
ghc --version
- name: Build dependencies

- name: Build dependencies
run: stack --install-ghc test --only-dependencies

- name: Build StrongPath & Run tests
run: stack test

Expand Down
62 changes: 41 additions & 21 deletions package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: strong-path
version: 1.1.4.0
version: 1.2.0.0
Copy link
Contributor Author

@sodic sodic Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're updating the lower bounds for some libraries, so this is a breaking change. We're dropping support for GHC 8.10.7, which was a big one.

github: "wasp-lang/strong-path"
license: MIT
author: "Martin Sosic"
Expand All @@ -15,28 +15,51 @@ category: System, Filesystem, FilePath

description: Replacement for a FilePath that enables you to handle filepaths in your code in a type-safe manner. You can specify at type level if they are relative, absolute, file, directory, posix, windows, and even to which file or directory they point to or are relative to.

tested-with: GHC == 8.10.7, GHC == 9.0.1 # lts-18.21, nightly-2022-01-04
tested-with: GHC == 9.0.2, GHC == 9.12.2 # lts-19.33, nightly-2025-10-29

dependencies:
- base >= 4.7 && < 5
- hashable >=1.3 && < 1.6
- path >=0.9.2 && <0.10
- filepath >=1.4 && <1.6

library:
source-dirs: src
ghc-options:
- -Wall
dependencies:
# NOTE: Version bounds here and in tests are defined so that they cover the latest LTS snapshot
# (lts-18.21) (lower bounds) and the nightly snapshot defined in stack.yaml (upper bounds).
# Those two are also tested in the CI, and corresponding GHC versions are mentioned above
# in the tested-with field.
# In case you decide to cover a different LTS with the lower bounds,
# make sure to also update the CI to use the correct LTS for testing and also update
# tested-with field above.
- path >=0.9.2 && <0.10
# NOTE: Version bounds here and in tests are defined so that they cover the
# latest LTS snapshot (lts-19.33) (lower bounds) and the nightly snapshot
# defined in stack.yaml (upper bounds). We are ignoring the minor
# versions on the lower bound for better flexibility.
#
# Those two LTS snapshots are also tested in the CI, and corresponding
# GHC versions are mentioned above in the tested-with field.
#
# If you decide to cover a different LTS with the lower bounds, make sure
# to also update the CI to use the correct LTS for testing and also
# update tested-with field above.
#
# When doing a GHC update:
# - Read this to learn more about the process:
# https://github.com/wasp-lang/haskell-handbook/blob/master/dependencies-version-bounds.md
# - Ensure you don't leave a gap in supported GHC versions. Each GHC/base
# should have a version of StrongPath to go with it.
# - Go through the breaking changes version of all libraries that need to
# be bumped and double check they don't affect us.
#
# The current situation:
# - Lower bounds match GHC 9.0.2 (Stack's LTS 19.33, major digits only)
# - Upper bounds match GHC 9.12.2 (Stack's nightly-2025-10-29). The
# exclusive upper limit is set to the next breaking change version.
#
# For example:
# - `LTS 19.33` has the version of `filepath` set to `1.4.2.2`
# - `nighly-2025-10-29` says has a version of `filepath` set to `1.5.4.0`
# - Strong path requires `filepath >=1.4 && <1.6`
#
- exceptions >=0.10 && <0.11
- filepath >=1.4 && <1.5
- template-haskell >=2.16 && <2.18
- hashable >=1.3 && < 1.4
- template-haskell >=2.17 && <2.24

tests:
strong-path-test:
Expand All @@ -48,11 +71,8 @@ tests:
- -with-rtsopts=-N
dependencies:
- strong-path
- path
- filepath
- hashable >=1.3 && < 1.4
- tasty >=1.4 && <1.5
- tasty-hspec >=1.1 && <1.3
- tasty-quickcheck >=0.10 && <0.11
- tasty-discover >=4.2 && <4.3
- hspec >=2.7 && <2.10
- tasty >=1.4 && <1.6
- tasty-hspec >=1.2 && <1.3
- tasty-quickcheck >=0.10 && <0.12
- tasty-discover >=4.2 && <5.3
- hspec >=2.8 && <2.12
64 changes: 38 additions & 26 deletions src/StrongPath/Operations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -105,43 +105,55 @@ parent path = case path of
(</>) :: Path s b (Dir d) -> Path s (Rel d) t -> Path s b t
---- System
lsp@(RelDir _ _) </> (RelFile rp rprefix) =
let (RelDir lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelFile (lp' P.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDir lp' lprefix') -> RelFile (lp' P.</> rp) lprefix'
_ -> impossible
lsp@(RelDir _ _) </> (RelDir rp rprefix) =
let (RelDir lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelDir (lp' P.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDir lp' lprefix') -> RelDir (lp' P.</> rp) lprefix'
_ -> impossible
lsp@(AbsDir _) </> (RelFile rp rprefix) =
let (AbsDir lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsFile (lp' P.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDir lp') -> AbsFile (lp' P.</> rp)
_ -> impossible
lsp@(AbsDir _) </> (RelDir rp rprefix) =
let (AbsDir lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsDir (lp' P.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDir lp') -> AbsDir (lp' P.</> rp)
_ -> impossible
---- Windows
lsp@(RelDirW _ _) </> (RelFileW rp rprefix) =
let (RelDirW lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelFileW (lp' PW.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDirW lp' lprefix') -> RelFileW (lp' PW.</> rp) lprefix'
_ -> impossible
lsp@(RelDirW _ _) </> (RelDirW rp rprefix) =
let (RelDirW lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelDirW (lp' PW.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDirW lp' lprefix') -> RelDirW (lp' PW.</> rp) lprefix'
_ -> impossible
lsp@(AbsDirW _) </> (RelFileW rp rprefix) =
let (AbsDirW lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsFileW (lp' PW.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDirW lp') -> AbsFileW (lp' PW.</> rp)
_ -> impossible
lsp@(AbsDirW _) </> (RelDirW rp rprefix) =
let (AbsDirW lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsDirW (lp' PW.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDirW lp') -> AbsDirW (lp' PW.</> rp)
_ -> impossible
---- Posix
lsp@(RelDirP _ _) </> (RelFileP rp rprefix) =
let (RelDirP lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelFileP (lp' PP.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDirP lp' lprefix') -> RelFileP (lp' PP.</> rp) lprefix'
_ -> impossible
lsp@(RelDirP _ _) </> (RelDirP rp rprefix) =
let (RelDirP lp' lprefix') = iterate parent lsp !! prefixNumParentDirs rprefix
in RelDirP (lp' PP.</> rp) lprefix'
case iterate parent lsp !! prefixNumParentDirs rprefix of
(RelDirP lp' lprefix') -> RelDirP (lp' PP.</> rp) lprefix'
_ -> impossible
lsp@(AbsDirP _) </> (RelFileP rp rprefix) =
let (AbsDirP lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsFileP (lp' PP.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDirP lp') -> AbsFileP (lp' PP.</> rp)
_ -> impossible
lsp@(AbsDirP _) </> (RelDirP rp rprefix) =
let (AbsDirP lp') = iterate parent lsp !! prefixNumParentDirs rprefix
in AbsDirP (lp' PP.</> rp)
case iterate parent lsp !! prefixNumParentDirs rprefix of
(AbsDirP lp') -> AbsDirP (lp' PP.</> rp)
_ -> impossible
_ </> _ = impossible

-- | Returns the most right member of the path once split by separators.
Expand Down Expand Up @@ -233,15 +245,15 @@ castFile _ = impossible
-- Works well for \"normal\" relative paths like @\"a\\b\\c\"@ (Win) or @\"a\/b\/c\"@ (Posix).
-- If path is weird but still considered relative, like just @\"C:\"@ on Win,
-- results can be unexpected, most likely resulting with error thrown.
relDirToPosix :: MonadThrow m => Path s (Rel d1) (Dir d2) -> m (Path Posix (Rel d1) (Dir d2))
relDirToPosix :: (MonadThrow m) => Path s (Rel d1) (Dir d2) -> m (Path Posix (Rel d1) (Dir d2))
relDirToPosix sp@(RelDir _ _) = parseRelDirP $ FPP.joinPath $ FP.splitDirectories $ toFilePath sp
relDirToPosix sp@(RelDirW _ _) = parseRelDirP $ FPP.joinPath $ FPW.splitDirectories $ toFilePath sp
relDirToPosix (RelDirP p pr) = return $ RelDirP p pr
relDirToPosix _ = impossible

-- | Converts relative file path to posix, if it is not already posix.
-- Check 'relDirToPosix' for more details, they behave the same.
relFileToPosix :: MonadThrow m => Path s (Rel d1) (File f) -> m (Path Posix (Rel d1) (File f))
relFileToPosix :: (MonadThrow m) => Path s (Rel d1) (File f) -> m (Path Posix (Rel d1) (File f))
relFileToPosix sp@(RelFile _ _) = parseRelFileP $ FPP.joinPath $ FP.splitDirectories $ toFilePath sp
relFileToPosix sp@(RelFileW _ _) = parseRelFileP $ FPP.joinPath $ FPW.splitDirectories $ toFilePath sp
relFileToPosix (RelFileP p pr) = return $ RelFileP p pr
Expand Down
4 changes: 1 addition & 3 deletions stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

# NOTE: If you modify this field, make sure to update the tested-with
# field in package.yaml to contain the corresponding GHC versions.
resolver: nightly-2022-01-04
resolver: nightly-2025-10-29

# User packages to be built.
# Various formats can be used as shown in the example below.
Expand All @@ -43,8 +43,6 @@ packages:
# - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
extra-deps:
- path-0.9.2

# Override default flag values for local packages and extra-deps
# flags: {}
Expand Down
21 changes: 7 additions & 14 deletions stack.yaml.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
# https://docs.haskellstack.org/en/stable/topics/lock_files

packages: []
snapshots:
- original: nightly-2022-01-04
completed:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/1/4.yaml
sha256: ea584edfba307b5a88b51fc1db7c0c1b7da5f714fd30d69699fc78e3e1ce5212
size: 623654
packages:
- original:
hackage: path-0.9.2
completed:
pantry-tree:
sha256: 2acf94a62daeeb0aee9b77d044ece55b5e03445b574e6980a2e84a5a514f5517
size: 1206
hackage: path-0.9.2@sha256:2f2a7f01737cd350b30381b619e1a862601c83f10ede4d6935f76f66e63ae0c7,3273
- completed:
sha256: e527e879a59ab8d2408960c7da3e2fc5edf4fb433a0f2fe65f0c79736f2e10e8
size: 705114
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2025/10/29.yaml
original: nightly-2025-10-29
Loading