Skip to content

feat: add joinEnv and joinUrl string functions and 2 new system vars #2408

Merged
andreynering merged 7 commits into
go-task:mainfrom
solvingj:add_joinenv
May 9, 2026
Merged

feat: add joinEnv and joinUrl string functions and 2 new system vars #2408
andreynering merged 7 commits into
go-task:mainfrom
solvingj:add_joinenv

Conversation

@solvingj
Copy link
Copy Markdown
Contributor

@solvingj solvingj commented Sep 3, 2025

Add two new system variables which map directly to platform specific go constants:

  • PATH_LIST_SEPARATOR -> os.PathListSeparator
  • FILE_PATH_SEPARATOR -> os.PathSeparator

Add joinEnv string function which is sibling to joinPath:

  • strings.Join(elem, string(os.PathListSeparator))

Add joinUrl string function which is sibling to joinPath:

  • path.Join(elem...)

Add all to docs

closes #2406

Copy link
Copy Markdown
Contributor

@trulede trulede left a comment

Choose a reason for hiding this comment

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

@solvingj there are quite a few functions already in https://go-task.github.io/slim-sprig/ and perhaps its better to use those existing list functions (join, append, prepend) since that gives a better control over how such lists/strings are generated.

The PathSeparator and PathListSeparator are useful however I would suggest following the existing most-predominate naming convention:

  • PathSeparator -> osPathSeparator (note also the absence of "file")
  • PathListSeparator -> osPathListSeparator

since the allcaps just looks ODD.

Observation: this could also be done with a .env file, introducing these constants (and others). Perhaps this PR is more effort than value.

@solvingj
Copy link
Copy Markdown
Contributor Author

Up to @pd93 and @andreynering I suppose.

Copy link
Copy Markdown
Member

@vmaerten vmaerten left a comment

Choose a reason for hiding this comment

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

Hello!
Sorry for the delay, we have been busy lately.

PATH_LIST_SEPARATOR / FILE_PATH_SEPARATOR does not work in the templating system.

If you take this Taskfile :

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - echo "{{.PATH_LIST_SEPARATOR}}"
      - echo "{{.FILE_PATH_SEPARATOR}}"

You will get this result:

Image

For these variables, you need to define them in getSpecialVars inside compiler.go

@solvingj
Copy link
Copy Markdown
Contributor Author

solvingj commented Nov 5, 2025

For these variables, you need to define them in getSpecialVars inside compiler.go

Ah, of course, moved...

@vmaerten
Copy link
Copy Markdown
Member

@solvingj the lint is failing. Do you still want to work on it?

@solvingj
Copy link
Copy Markdown
Contributor Author

Yah I can fix it tomorrow. Just to confirm, are you sure you want to merge it?

@solvingj
Copy link
Copy Markdown
Contributor Author

@vmaerten i just added this proposed example to the ENV_PRECEDENCE documentation:
d5431e0

Just pointing out that the use case would be a touch more elegant with this feature.

return true
}

func joinEnv(elem ...string) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this called joinEnv? It has nothing to do with environment.

There is already a join function in slim-sprig which takes a second argument for the separator. Would this not be more suitable, along with the added seperator (in this PR).

Copy link
Copy Markdown
Contributor Author

@solvingj solvingj Mar 19, 2026

Choose a reason for hiding this comment

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

Why is this called joinEnv? It has nothing to do with environment.

os.PathListSeparator is abstract rune which resolves to the character ":" when compiled on posix operating systems, and ";" on windows. It represents the special value used by these operating systems when parsing 1 or more filesystem paths from an environment variable, such as in the special case of PATH.

This function returns a string, which is the combination of input strings, but with the appropriate platform-specific character placed in-between each. In this way, it returns a string which is valid to be assigned to an environment variable which is designated to hold 1 or more filesystem paths.

Thus, this function is related to environment variables, and that is why it was called joinEnv.

I apologize for not explaining this more clearly in the original description.

}

func joinUrl(elem ...string) string {
return path.Join(elem...)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is already a joinPath function. Any reason not to use that? It calls filepath.Join.

Also, there is an existing urlJoin which is actually joining a URL. And a corresponding urlParse. Those are both working with URLs, the function presented here is not...

Copy link
Copy Markdown
Contributor Author

@solvingj solvingj Mar 19, 2026

Choose a reason for hiding this comment

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

There is already a joinPath function. Any reason not to use that? It calls filepath.Join.

filepath.Join is abstract method which takes 1 or more strings understood to be filesystem paths, and combines them by putting the platform-specific separator in-between each element. When compiled on posix operating systems, the separator used is "/", when compiled on windows, the separator is "\".

When joining path elements for a URL, the inputs must be combined together, with the character "/" placed in-between each element. The path.Join function does this. The use of joinPath on posix operating-systems returns a valid URL with the "/" character, however when called on windows with the same inputs, the function returns a string with the separator "\" placed in-between each element. This result is not a valid URL.

This is one of two key reason why this function is implemented, and why it used the path.Join function instead of filepath.Join.

Also, there is an existing urlJoin which is actually joining a URL. And a corresponding urlParse. Those are both working with URLs, the function presented here is not...

urlJoin is a function which takes an input of type map and combines them into a URL based on a number of optional inputs, according to RFC 3986. It looks for the following keys (with example values provided):

scheme:   'http'
host:     'server.com:8080'
path:     '/api'
query:    'list=false'
opaque:   nil
fragment: 'anchor'
userinfo: 'admin:secret'

The path key supports a single string value. If the path of the url has multiple path elements, such as api,foo, and bar, they must be combined into a single string prior to calling this function. To be valid, they must be combined with the "/" character in-between each element, such as /api/foo/bar.

This is the second of the two key reason why this function is implemented. It provides a first-class way to prepare a path string for use with URLs, including for use with the existing urlJoin function.

I apologize for not explaining these things more clearly in the original description.

@trulede
Copy link
Copy Markdown
Contributor

trulede commented Mar 14, 2026

@solvingj why not just add pathJoinList alongside the existing pathJoin? And don't bother will all the additional stuff.

For your problem, its much neater than dealing with the separators. It seems like it would just solve your problem (sorry I don't exactly know the templating syntax to use here, but you will get the point):

export PATH={{pathJoinList env PWD/tools/bin env PATH}}

https://github.com/SergioChan/task/blob/54bdcba369357b47e19066b57badfb216a4c8d95/internal/templater/funcs.go#L35

@solvingj
Copy link
Copy Markdown
Contributor Author

@solvingj why not just add pathJoinList alongside the existing pathJoin? And don't bother will all the additional stuff.

For your problem, its much neater than dealing with the separators. It seems like it would just solve your problem (sorry I don't exactly know the templating syntax to use here, but you will get the point):

export PATH={{pathJoinList env PWD/tools/bin env PATH}}

https://github.com/SergioChan/task/blob/54bdcba369357b47e19066b57badfb216a4c8d95/internal/templater/funcs.go#L35

This suggested implementation does not result in the desired behavior (it does not pass the included unit tests). To try to help understand why, I have provided some responses to your other comments and questions in this PR, with some key details about how the functions you mentioned work. Your confusion is understandable, as the details of these functions are somewhat esoteric, and cross-platform nuances are often not well-understood. I apologize I did not give more detailed explanations in my original PR description.

@solvingj solvingj requested a review from vmaerten March 19, 2026 02:34
@andreynering
Copy link
Copy Markdown
Member

Hi @solvingj,

Looks like this build is broken. This needs a rebase.

…constants:

  - PATH_LIST_SEPARATOR -> os.PathListSeparator
  - FILE_PATH_SEPARATOR -> os.PathSeparator

Add joinEnv string function which is sibling to joinPath:
- strings.Join(elem, string(os.PathListSeparator))

Add joinUrl string function which is sibling to joinPath:
- path.Join(elem...)

Add all to docs

closes go-task#2406
- Rebased on latest main branch (origin/main)
- Resolved conflicts in compiler.go: merged filepath.ToSlash() changes with new special variables
- Fixed type conversion for os.PathListSeparator and os.PathSeparator (rune to string)
- Added PATH_LIST_SEPARATOR and FILE_PATH_SEPARATOR to summary filter to prevent them from appearing in task summaries (consistent with other special variables)
- All tests passing including linting
@solvingj
Copy link
Copy Markdown
Contributor Author

@andreynering done. All tests pass locally now.

@solvingj
Copy link
Copy Markdown
Contributor Author

solvingj commented May 1, 2026

@andreynering any additional blockers?

Copy link
Copy Markdown
Member

@andreynering andreynering left a comment

Choose a reason for hiding this comment

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

Thank you for your patience!

@andreynering andreynering changed the title Add joinEnv and joinUrl string functions and 2 new system vars feat: add joinEnv and joinUrl string functions and 2 new system vars May 9, 2026
@andreynering andreynering enabled auto-merge (squash) May 9, 2026 14:18
@andreynering andreynering merged commit d9e0e04 into go-task:main May 9, 2026
14 of 15 checks passed
andreynering added a commit that referenced this pull request May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

joinEnv - Just like joinPath but with ; for windows else :

4 participants