4ch1m / intellij-ansible-lint

A plugin for the IntelliJ platform utilizing Ansible Lint.
MIT License
10 stars 5 forks source link

WSL "Unable to call Ansible Lint" #21

Closed Bravehartk2 closed 2 weeks ago

Bravehartk2 commented 2 weeks ago

I'm using WSL (Debian 12) under Windows 11. Unfortunately I get an error, when testing the executable in the PHPStorm Settings (Tools -> Ansible Lint)

I'm not sure, if the line "Ansible-lint does not currently support installation on Windows systems." from the docs includes the WSL. ;-) In general PHPStorm interacts well with the WSL. That's why I hoped, this would work.

Inside WSL Ansible Lint is available, executable and recognises violations very well. The path is /usr/local/bin/ansible-lint.

Versions:

PhpStorm 2024.1.6

ansible [core 2.17.4]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/bravehartk2/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/bravehartk2/.local/lib/python3.11/site-packages/ansible
  ansible collection location = /home/bravehartk2/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.11.2 (main, May  2 2024, 11:59:08) [GCC 12.2.0] (/usr/bin/python3)
  jinja version = 3.1.4
  libyaml = True
╰─⠠⠵ ansible-lint --version
ansible-lint 24.9.2 using ansible-core:2.17.4 ansible-compat:24.9.1 ruamel-yaml:0.18.6 ruamel-yaml-clib:0.2.8
4ch1m commented 2 weeks ago

Hi there,

I'm sorry... I can't provide support for WSL. Closing, since this is not an "issue" of the plugin.

kolinger commented 3 days ago

This issue is very simple. If someone runs intellij-ansible-lint under Windows, then all paths are in Windows format obviously, thus there is no way this can work without some translation layer. If someone said it works then he doesn't use WSL and IDE in Windows because intellij-ansible-lint simply can't work under Windows with WSL as is... This should be mentioned in the readme that this plugin is by design incompatible with combination of Windows and WSL.

If you want to use intellij-ansible-lint where IDE runs under Windows then you need to have ansible-lint installed under WSL (I used WSL 1), you can follow the example with venv/pip in the readme.

This should yield ~/ansible-lint/ but we need to add some bits to the ~/ansible-lint/run.sh to translate the Windows paths into WSL/Linux format, the adjusted ~/ansible-lint/run.sh looks like this:

#!/usr/bin/env bash
# Translate what looks like with Windows path via `wslpath`.
args=()
for arg in "$@"
do
    if [[ "$arg" =~ ^[A-Z]+:\\ ]]; then
        arg=$(wslpath "$arg")
    fi
    args+=("$arg")
done

# Call the ansible-lint with translated arguments.
source ~/ansible-lint/venv/bin/activate
ansible-lint "${args[@]}"

Then you need to create batch script on Windows side, something like ansible-lint.cmd somewhere where you find it:

@echo off
wsl.exe --exec /home/YOUR_WSL_USERNAME/ansible-lint/run.sh %*

Of course replace the /home/YOUR_WSL_USERNAME with your WSL user home directory. It's important to use the --exec of wsl.exe otherwise the arguments will arrive malformed in one way or another.

Now you can select the ansible-lint.cmd in the settings of intellij-ansible-lint in the IDE according to the readme and it should work. If it doesn't then check if all paths are correct and also if the .ansible-lint configuration file is correct.


It doesn't help that the intellij-ansible-lint eats the errors and doesn't provide useful debug information. The maintainer should have motivation to make the code easily debuggable via the logs. For example if external command fails, why not log into the exception what command failed, with what arguments and what output - such simple change would make debugging issues like these extremely straightforward. Verbose error logging saves everyone's time for basically nothing.

What can easily happen is, that the configuration file is incorrect, or whatever other reason, where the ansible-lint fails, currently the plugin just log generic message "something failed exit code X" and that's really not helpful, the exit code helps a bit if someone knows what exit codes the ansible-lint uses but if it did show the output of the failed command, that would simplify things by a lot.

4ch1m commented 3 days ago

If someone said it works then he doesn't use WSL and IDE in Windows because intellij-ansible-lint simply can't work under Windows with WSL as is...

Well... older comments - from Windows users - suggest the opposite: e.g. #8

This should be mentioned in the readme that this plugin is by design incompatible with combination of Windows and WSL.

Could you please be more elaborate and refer to the exact code parts in the plugin that your statement is based on.

This function handles the actual call to ansible-lint:

https://github.com/4ch1m/intellij-ansible-lint/blob/494da8f2a86fd9b1c54eaf82da1732e239037dce/src/main/kotlin/de/achimonline/ansible_lint/command/AnsibleLintCommandLine.kt#L36-L76 Which parts do you consider "by design incompatible with combination of Windows"?

If you want to use intellij-ansible-lint where IDE runs under Windows then you need ... ... Now you can select the ansible-lint.cmd in the settings of intellij-ansible-lint in the IDE according to the readme and it should work.

Oh, wait. Did you just say: "... and it should work".

So now you're saying that you actually did manage to get the plugin running with your Windows-setup. :confused: It basically just took some fiddling on your side.

It doesn't help that the intellij-ansible-lint eats the errors and doesn't provide useful debug information.

That is not correct.

Both the "test feature" in the settings-dialog, and the actual call in the annotator catch the full exception and pass it through to the logger.

https://github.com/4ch1m/intellij-ansible-lint/blob/494da8f2a86fd9b1c54eaf82da1732e239037dce/src/main/kotlin/de/achimonline/ansible_lint/settings/AnsibleLintConfigurable.kt#L143-L146

https://github.com/4ch1m/intellij-ansible-lint/blob/494da8f2a86fd9b1c54eaf82da1732e239037dce/src/main/kotlin/de/achimonline/ansible_lint/settings/AnsibleLintConfigurable.kt#L198-L200

https://github.com/4ch1m/intellij-ansible-lint/blob/494da8f2a86fd9b1c54eaf82da1732e239037dce/src/main/kotlin/de/achimonline/ansible_lint/annotator/AnsibleLintAnnotator.kt#L114-L134

The IDE log can always be checked for these exceptions/messages by the end-user.

The maintainer should have motivation to make the code easily debuggable via the logs.

That's exactly what's being done.

As far as my "motivation" is concerned...

This project/plugin is free and open source. Developed in my spare-time. Instead of poking around and badmouthing my work you could also try to contribute and provide improvements via pull-request. Productive collaboration is always welcome.

But I guess that baseless accusations and false assumptions is everything to be expected from you.

I personally don't use Windows (or any other OS) for development; so I don't test compatibility myself. However, as other user already reported, they got everything working with Windows. It just requires a (OS-specific) setup.

To end this... If you like the plugin - fine. If you don't - also fine; just move along.

kolinger commented 3 days ago

Well... older comments - from Windows users - suggest the opposite: e.g. https://github.com/4ch1m/intellij-ansible-lint/issues/8

It's not that simple...

For example - you can run the ansible-lint in Windows and the IDE in Windows - that would work, expect the ansible doesn't run on Windows - since it depends on unix-only Python modules as far as I know.

You could also run ansible-lint and IDE in WSL 2, that basically means you running in Linux VM and this doesn't have anything to do with Windows. That's not something I would call "Windows support" because the implementation is basically "just install Linux VM".

If someone asks for WSL support they likely mean ansible-lint in WSL and IDE in Windows and that doesn't work and there is no way you make it work without implementing the missing path translation. Is that fiddling with your setup? Judge yourself but either way I won't call this a "supported" platform if you need to fiddle so hard. One could argue that you could fiddle so hard that you could write whole another implementation... Really depends how hard you are willing to fiddle, right?

The #8 post doesn't say anything about WSL or Windows and that's important as you see since the combination matters. What the post really says? You can install ansible-lint into path via pipx and that's surely a thing but that doesn't change anything at all, you can also install the ansible-lint into venv and that's the same thing - if you try this on Windows it will install both ways but ansible won't work. If you can do this in WSL, both methods of installation will work, and ansible will work too but the plugin feeds the ansible Windows paths and that obviously can't work since C:\something won't exist inside WSL. You can trust me on on this one - Linux doesn't support Windows paths - for real...

Which parts do you consider "by design incompatible with combination of Windows"?

Remember I said combination with WSL, if it was just Windows this would be just fine but just Windows CAN'T BE USED. Thus you need to use WSL and if you use WSL then the plugin is passing Windows paths into Linux subsystem and that's the by design incompatible part. If you don't bake in the WSL support specifically, it can't work.

You need to use WSL, because otherwise the Ansible won't run. You can't use WSL, because that's not supported. How would you define such level of support? It totally supports Windows but in way that the Ansible won't run and the only way to run the Ansible on Windows is not implemented.

It's absolutely correct to call that this plugin doesn't support Windows by design, since Ansible doesn't support Windows by design. It's absolutely correct to call that WSL isn't supported and it can work but you need very specific workaround. If this workaround isn't easy to come by and it's not specifically mentioned then WSL isn't officially supported since officially you missing a piece of the puzzle. The workaround isn't specific to any system it's required on all Windows+WSL systems regardless of the setup.

So now you're saying that you actually did manage to get the plugin running with your Windows-setup. 😕

The hack "works" at first glance but not fully since it translates the paths only one way, thus the ansible-lint outputs WSL paths in stdout that the plugin doesn't understand completely. The linting works but doesn't report everything that ansible-lint does in the CLI thus it's not complete for sure. For example the playbook seems to see all linting but the role sees only partial linting, not quite sure how this works but the ansible-lint totally outputs the WSL paths as expected so that's a issue.

Thus the reverse path translation is unsurprisingly required, oh well no neat short bash then. More hacking to do...

The IDE log can always be checked for these exceptions/messages by the end-user.

This is not great UI/UX design anyway since those unexpected exists are expected behavior from the command - the command is telling you a message this way, it's not a unexpected error at all, it's a valid response. Thus if the exception dialog fails to mention the message, that's not ideal. Perhaps this shouldn't even throw a exception but even the more verbose exception message would be a improvement since then user doesn't need to dig outside the IDE for log. Currently this means it's easer to run ansible-lint in cli when something goes wrong since that is faster way to see the error than looking for log - is that ideal state? You tell me.

Instead of poking around and badmouthing my work you could also try to contribute and provide improvements via pull-request.

Why would you want a code if you aren't willing to even test it? I totally get you just don't care and that's totally fine but why then you want the implementation? I didn't write single line of kotlin in my life but I can start if you are willing to work this way but I have my doubts about your cooperation. I know from experience that the contributed implementation is just the first part - the maintainer still has to work it otherwise there is no way it would work long term. That's perhaps why external wrapper is more realistic solution.

Maybe it's easier just to clearly state that the Windows isn't supported and WSL with combination with Windows doesn't work without specific workaround. I have yet to do the workaround...

However, as other user already reported, they got everything working with Windows. It just requires a (OS-specific) setup.

I would like to see examples because the one you given doesn't mean anything at all. I hope you understand now and there is a issue, not necessarily something this project need to solve, but there is a issue that needs solving before anyone has a chance to make it work and if you expect that every user writes it's own implementation of the path translation - then you are having very high bar since you expect that all users of ansible are also programmers.

Such "supported" state doesn't make sense in general - even if some exotic setup was required that most people can't figure out - even that alone is enough to state that such platform isn't supported without providing the instructions for the given setup. But this isn't the case, this goes far beyond setup. From my own experience it's not usual that you are required to write code before you can finish installation of something like plugin.


BTW: Your error handling isn't as good as you think, for example when Jackson fails because the command returns non-JSON output then I don't see the command/arguments/output in the log. Only massive spam without any useful information - whatever is your error handling - it may log the important details only sometimes when the code expect failure but if something else fails then the log is useless. This could be improved since it's quite likely that if the output isn't what is expected then the command is trying to say something.

The offending exception example ``` 2024-10-18 10:52:32,635 [ 6593] SEVERE - #c.i.c.d.i.ExternalToolPass - IdeaLoggingEvent[message=ExternalToolPass: , throwable=com.intellij.diagnostic.PluginException: annotator: de.achimonline.ansible_lint.annotator.AnsibleLintAnnotator@1800dd (class de.achimonline.ansible_lint.annotator.AnsibleLintAnnotator) [Plugin: de.achimonline.ansible_lint] at com.intellij.diagnostic.PluginProblemReporterImpl.createPluginExceptionByClass(PluginProblemReporterImpl.java:23) at com.intellij.diagnostic.PluginException.createByClass(PluginException.java:90) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.processError(ExternalToolPass.java:268) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.doAnnotate(ExternalToolPass.java:225) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.doAnnotate(ExternalToolPass.java:212) at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.lambda$run$0(ExternalToolPass.java:170) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.runChangeAware(ExternalToolPass.java:283) at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.lambda$run$2(ExternalToolPass.java:170) at com.intellij.openapi.progress.util.BackgroundTaskUtil.lambda$runUnderDisposeAwareIndicator$15(BackgroundTaskUtil.java:371) at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:217) at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$13(CoreProgressManager.java:660) at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:735) at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:691) at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:659) at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:79) at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:202) at com.intellij.openapi.progress.util.BackgroundTaskUtil.runUnderDisposeAwareIndicator(BackgroundTaskUtil.java:366) at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.run(ExternalToolPass.java:168) at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:366) at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:356) at com.intellij.util.ui.update.MergingUpdateQueue.doFlush(MergingUpdateQueue.java:313) at com.intellij.util.ui.update.MergingUpdateQueue.flush(MergingUpdateQueue.java:295) at com.intellij.util.ui.update.MergingUpdateQueue.run(MergingUpdateQueue.java:262) at com.intellij.util.Alarm$Request.lambda$runSafely$0(Alarm.java:371) at com.intellij.util.concurrency.ChildContext$runAsCoroutine$1.invoke(propagation.kt:92) at com.intellij.util.concurrency.ChildContext$runAsCoroutine$1.invoke(propagation.kt:92) at com.intellij.util.concurrency.ChildContext.runAsCoroutine(propagation.kt:97) at com.intellij.util.concurrency.ChildContext.runAsCoroutine(propagation.kt:92) at com.intellij.util.Alarm$Request.lambda$runSafely$1(Alarm.java:369) at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:255) at com.intellij.util.Alarm$Request.runSafely(Alarm.java:368) at com.intellij.util.Alarm$Request.run(Alarm.java:356) at com.intellij.util.concurrency.Propagation.contextAwareCallable$lambda$2(propagation.kt:383) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:272) at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:249) at com.intellij.util.concurrency.BoundedTaskExecutor.access$200(BoundedTaskExecutor.java:30) at com.intellij.util.concurrency.BoundedTaskExecutor$1.executeFirstTaskAndHelpQueue(BoundedTaskExecutor.java:227) at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:218) at com.intellij.util.concurrency.BoundedTaskExecutor$1.run(BoundedTaskExecutor.java:212) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:735) at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:732) at java.base/java.security.AccessController.doPrivileged(AccessController.java:400) at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:732) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'usage': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false') at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 6] at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2567) at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2593) at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2601) at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:765) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:3018) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:2052) at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:780) at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4992) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4898) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3848) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3831) at de.achimonline.ansible_lint.parser.AnsibleLintParser$Companion.parse(AnsibleLintParser.kt:73) at de.achimonline.ansible_lint.annotator.AnsibleLintAnnotator.doAnnotate(AnsibleLintAnnotator.kt:106) at de.achimonline.ansible_lint.annotator.AnsibleLintAnnotator.doAnnotate(AnsibleLintAnnotator.kt:40) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.lambda$doAnnotate$1(ExternalToolPass.java:220) at com.intellij.codeInsight.daemon.impl.AnnotationSessionImpl.computeWithSession(AnnotationSessionImpl.java:87) at com.intellij.codeInsight.daemon.impl.AnnotationSessionImpl.computeWithSession(AnnotationSessionImpl.java:79) at com.intellij.codeInsight.daemon.impl.ExternalToolPass.doAnnotate(ExternalToolPass.java:218) ... 43 more ] ```

Let's focus on the more productive things - the plugin is broken under Windows for some reason.

kolinger commented 2 days ago

After digging more the issue with the difference in the lint output doesn't seems to be related to the paths. Although the ansible-lint returns WSL path in the root directory - the value doesn't seems to be used (?) and instead the plugins uses relative paths from the rules so that's why it works with one-way argument translation? The issue persists even if I correct paths in the stdout of ansible-lint.

Everything works just fine for the playbook.yml but doesn't work for any main.yml inside nested directories like roles, I can see that the temporary directory copies the nested main.yml from roles into root of the temp directory, that's wrong. That's why only basic yaml syntax rules are detected.

Is this bug related only to the WSL environment I have but how? Why the plugin doesn't copy the contents of editor into the main.yml where it should be?

I see the code https://github.com/4ch1m/intellij-ansible-lint/blob/master/src/main/kotlin/de/achimonline/ansible_lint/common/AnsibleLintTempEnv.kt#L33-L46 is trying to handle this case correctly. Yet the file is placed incorrectly in the root.

The paths look like this:

--config-file C:\PROJECT_DIR_PREFIX\.config\ansible-lint.yml
--project-dir C:\USER_DIR_PREFIX\AppData\Local\Temp\intellij_ansible_lint_2790127958537270337
C:\USER_DIR_PREFIX\AppData\Local\Temp\intellij_ansible_lint_2790127958537270337\main.yml

The target file should be C:\USER_DIR_PREFIX\AppData\Local\Temp\intellij_ansible_lint_2790127958537270337\roles\ROLE_NAME\tasks\main.yml but it isn't. The directory structure is there but the file for lint is put into wrong place.

I don't see how this could be related to the WSL-thing since these paths are generated exclusively inside the plugin before the WSL is even involved? Thus my Windows environment itself is somehow breaking the relative directory part?

Update: the setup is fine but there is a 🐛

See this issue for specifics https://github.com/4ch1m/intellij-ansible-lint/issues/22 and final form of the simple workaround.