Skip to content

Commit

Permalink
Fix output retrieval during Direnv import
Browse files Browse the repository at this point in the history
- Add settings option for configuring timeout
- Handle direnv errors correctly
- Read stdout of direnv completely right away as the process only then terminates
  • Loading branch information
Chrisimx committed Jan 10, 2025
1 parent 9dc34bb commit 4293d7e
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,32 @@
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DirenvSettingsComponent {
private final JPanel mainPanel;
private final TextFieldWithBrowseButton direnvPath = new TextFieldWithBrowseButton();
private final JSlider direnvTimeout = new JSlider(0, 240);
private final JLabel direnvTiemoutLabel = new JLabel();
private final JBCheckBox direnvImportOnStartup = new JBCheckBox("Automatically import any .envrc in the project root when the project is opened.");
private final JBCheckBox direnvImportEveryExecution = new JBCheckBox("Automatically import any .envrc in the project root before every run/debug");


public DirenvSettingsComponent() {
direnvPath.addBrowseFolderListener( "Direnv Path", "Path to the direnv file", null,
FileChooserDescriptorFactory.createSingleFileDescriptor());
direnvTimeout.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
direnvTiemoutLabel.setText("Direnv execution timeout: " + direnvTimeout.getValue());
}
});
mainPanel = FormBuilder
.createFormBuilder()
.addLabeledComponent(new JLabel("DirenvPath: "), direnvPath, 1, false)
.addComponent(direnvImportOnStartup, 1)
.addComponent(direnvImportEveryExecution, 1)
.addLabeledComponent(direnvTiemoutLabel, direnvTimeout, 1, false)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}
Expand Down Expand Up @@ -59,4 +69,12 @@ public boolean getDirenvImportEveryExecution() {
public void setDirenvImportEveryExecution(boolean newStatus) {
direnvImportEveryExecution.setSelected(newStatus);
}

public int getDirenvTimeout() {
return direnvTimeout.getValue();
}

public void setDirenvTimeout(int timeoutInSeconds) {
direnvTimeout.setValue(timeoutInSeconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public boolean isModified() {
DirenvSettingsState settings = DirenvSettingsState.getInstance();
return !direnvSettingsComponent.getDirenvPath().equals(settings.direnvSettingsPath) ||
direnvSettingsComponent.getDirenvImportOnStartup() != settings.direnvSettingsImportOnStartup ||
direnvSettingsComponent.getDirenvImportEveryExecution() != settings.direnvSettingsImportEveryExecution;
direnvSettingsComponent.getDirenvImportEveryExecution() != settings.direnvSettingsImportEveryExecution ||
direnvSettingsComponent.getDirenvTimeout() != settings.direnvTimeout;
}

@Override
Expand All @@ -40,6 +41,7 @@ public void apply() {
settings.direnvSettingsPath = direnvSettingsComponent.getDirenvPath();
settings.direnvSettingsImportOnStartup = direnvSettingsComponent.getDirenvImportOnStartup();
settings.direnvSettingsImportEveryExecution = direnvSettingsComponent.getDirenvImportEveryExecution();
settings.direnvTimeout = direnvSettingsComponent.getDirenvTimeout();
}

@Override
Expand All @@ -48,6 +50,7 @@ public void reset() {
direnvSettingsComponent.setDirenvPath(settings.direnvSettingsPath);
direnvSettingsComponent.setDirenvImportOnStartup(settings.direnvSettingsImportOnStartup);
direnvSettingsComponent.setDirenvImportEveryExecution(settings.direnvSettingsImportEveryExecution);
direnvSettingsComponent.setDirenvTimeout(settings.direnvTimeout);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class DirenvSettingsState implements PersistentStateComponent<DirenvSetti
public String direnvSettingsPath = "";
public Boolean direnvSettingsImportOnStartup = false;
public Boolean direnvSettingsImportEveryExecution = false;
public Integer direnvTimeout = 15;

public static DirenvSettingsState getInstance() {
return ApplicationManager.getApplication().getService(DirenvSettingsState.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import systems.fehn.intellijdirenv.MyBundle
import systems.fehn.intellijdirenv.notificationGroup
import systems.fehn.intellijdirenv.settings.DirenvSettingsState
import systems.fehn.intellijdirenv.switchNull
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit

@Service(Service.Level.PROJECT)
class DirenvProjectService(private val project: Project) {
Expand All @@ -42,12 +44,20 @@ class DirenvProjectService(private val project: Project) {
fun importDirenv(envrcFile: VirtualFile, notifyNoChange: Boolean = true) {
val process = executeDirenv(envrcFile, "export", "json")

if (process.waitFor() != 0) {
handleDirenvError(process, envrcFile)
val output = process.inputStream.readAllBytes().toString(StandardCharsets.UTF_8)

val timoutInSeconds = DirenvSettingsState.getInstance().direnvTimeout

if (!process.waitFor(timoutInSeconds!!.toLong(), TimeUnit.SECONDS)) {
handleTimeout(envrcFile)
return
}

jsonFactory.createParser(process.inputStream).use { parser ->
if (process.exitValue() != 0) {
handleDirenvError(process, envrcFile)
}

jsonFactory.createParser(output).use { parser ->

try {
val didWork = handleDirenvOutput(parser)
Expand Down Expand Up @@ -91,15 +101,15 @@ class DirenvProjectService(private val project: Project) {
}

didWork = true
logger.trace { "Set variable ${parser.currentName} to ${parser.valueAsString}" }
logger.trace("Set variable ${parser.currentName} to ${parser.valueAsString}")
}
}

return didWork
}

private fun handleDirenvError(process: Process, envrcFile: VirtualFile) {
val error = process.errorStream.bufferedReader().readText()
val error = process.errorStream.readAllBytes().toString(StandardCharsets.UTF_8)

val notification = if (error.contains(" is blocked")) {
notificationGroup
Expand Down Expand Up @@ -138,6 +148,23 @@ class DirenvProjectService(private val project: Project) {
.notify(project)
}

private fun handleTimeout(envrcFile: VirtualFile) {
notificationGroup
.createNotification(
MyBundle.message("envrcTimeout"),
"",
NotificationType.WARNING,
)
.addAction(
NotificationAction.create(MyBundle.message("openEnvrc")) { _, it ->
it.hideBalloon()

FileEditorManager.getInstance(project).openFile(envrcFile, true, true)
},
)
.notify(project)
}

private fun executeDirenv(envrcFile: VirtualFile, vararg args: String): Process {
val workingDir = envrcFile.parent.path

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/messages/MyBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ envrcFileFound=.envrc file found
noTopLevelEnvrcFileFound=No .envrc in project dir found
executedSuccessfully=Direnv import finished
alreadyUpToDate=Direnv was already up-to-date
envrcTimeout=Timeout reached during direnv execution

0 comments on commit 4293d7e

Please sign in to comment.