Skip to content

Commit 52bdd87

Browse files
committed
feat(settings): add support for per-project environment variable configuration for SOPS
1 parent ae78276 commit 52bdd87

8 files changed

Lines changed: 81 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- Support for per-project environment variable configuration for SOPS.
8+
59
### Changed
610

711
- Remove SOPS_AGE_KEY_FILE from default environment variables.

src/main/kotlin/com/github/blarc/sops/intellij/plugin/SopsWrapper.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.github.blarc.sops.intellij.plugin
22

33
import com.github.blarc.sops.intellij.plugin.settings.AppSettings
4+
import com.github.blarc.sops.intellij.plugin.settings.ProjectSettings
45
import com.intellij.execution.configurations.GeneralCommandLine
56
import com.intellij.execution.process.*
67
import com.intellij.execution.util.ExecUtil
8+
import com.intellij.openapi.components.service
9+
import com.intellij.openapi.project.Project
710
import com.intellij.openapi.util.Key
811
import com.intellij.openapi.vfs.LocalFileSystem
912
import com.intellij.openapi.vfs.VirtualFile
@@ -18,36 +21,42 @@ object SopsWrapper {
1821

1922
suspend fun version(
2023
sopsPath: String,
24+
project: Project,
2125
onSuccess: suspend (String) -> Unit,
2226
onError: suspend (String) -> Unit = {}
2327
) {
2428
run(
25-
sopsPath, "--version",
29+
sopsPath,
30+
"--version",
31+
project,
2632
onSuccess = { result -> onSuccess(result.lines().first()) },
2733
onError = onError
2834
)
2935
}
3036

3137
suspend fun encrypt(
3238
file: VirtualFile,
39+
project: Project,
3340
inPlace: Boolean = false,
3441
onSuccess: suspend (String) -> Unit,
3542
onError: suspend (String) -> Unit = {}
3643
) {
37-
run("encrypt", file, inPlace, onSuccess, onError)
44+
run("encrypt", project, file, inPlace, onSuccess, onError)
3845
}
3946

4047
suspend fun decrypt(
4148
file: VirtualFile,
49+
project: Project,
4250
inPlace: Boolean = false,
4351
onSuccess: suspend (String) -> Unit,
4452
onError: suspend (String) -> Unit = {}
4553
) {
46-
run("decrypt", file, inPlace, onSuccess, onError)
54+
run("decrypt", project, file, inPlace, onSuccess, onError)
4755
}
4856

4957
suspend fun decrypt(
5058
text: String,
59+
project: Project,
5160
onSuccess: suspend (String) -> Unit,
5261
onError: suspend (String) -> Unit = {}
5362
) {
@@ -58,11 +67,12 @@ object SopsWrapper {
5867
tmpFile.deleteOnExit()
5968

6069
val file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tmpFile)
61-
run("decrypt", file!!, false, onSuccess, onError)
70+
run("decrypt", project, file!!, false, onSuccess, onError)
6271
}
6372

6473
suspend fun edit(
6574
file: VirtualFile,
75+
project: Project,
6676
newText: String?,
6777
onSuccess: suspend () -> Unit = {},
6878
onError: suspend (String, Int) -> Unit = { _, _ -> }
@@ -73,7 +83,7 @@ object SopsWrapper {
7383
onError("Sops path not configured", 1)
7484
return
7585
}
76-
val command = buildCommand(sopsPath, file.parent.path)
86+
val command = buildCommand(sopsPath, project, file.parent.path)
7787

7888
val scriptFiles = ScriptUtil.createScriptFiles()
7989
val editorPath: String = scriptFiles.script.toAbsolutePath().toString()
@@ -130,6 +140,7 @@ object SopsWrapper {
130140

131141
suspend fun run(
132142
sopsCommand: String,
143+
project: Project,
133144
file: VirtualFile? = null,
134145
inPlace: Boolean = false,
135146
onSuccess: suspend (String) -> Unit,
@@ -140,18 +151,19 @@ object SopsWrapper {
140151
onError("Sops path not configured")
141152
return
142153
}
143-
run(sopsPath, sopsCommand, file, inPlace, onSuccess, onError)
154+
run(sopsPath, sopsCommand, project, file, inPlace, onSuccess, onError)
144155
}
145156

146157
suspend fun run(
147158
sopsPath: String,
148159
sopsCommand: String,
160+
project: Project,
149161
file: VirtualFile? = null,
150162
inPlace: Boolean = false,
151163
onSuccess: suspend (String) -> Unit,
152164
onError: suspend (String) -> Unit
153165
) {
154-
val command = buildCommand(sopsPath, file?.parent?.path)
166+
val command = buildCommand(sopsPath, project, file?.parent?.path)
155167
command.addParameter(sopsCommand)
156168
if (inPlace) {
157169
command.addParameter("--in-place")
@@ -176,10 +188,11 @@ object SopsWrapper {
176188
}
177189
}
178190

179-
private fun buildCommand(sopsPath: String, cwd: String? = null): GeneralCommandLine {
191+
private fun buildCommand(sopsPath: String, project: Project, cwd: String? = null): GeneralCommandLine {
192+
val projectSettings = project.service<ProjectSettings>()
180193
val command: GeneralCommandLine = GeneralCommandLine(sopsPath)
181194
.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
182-
.withEnvironment(AppSettings.instance.sopsEnvironment)
195+
.withEnvironment(projectSettings.sopsProjectEnvironment + AppSettings.instance.sopsEnvironment)
183196
.withCharset(StandardCharsets.UTF_8)
184197
.withWorkDirectory(cwd)
185198

src/main/kotlin/com/github/blarc/sops/intellij/plugin/services/SopsService.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class SopsService(
3737
cs.launch {
3838
withBackgroundProgress(project, message("background.decrypting")) {
3939
SopsWrapper.decrypt(
40-
file, inPlace,
40+
file, project, inPlace,
4141
{ decryptedText ->
4242
errors[file.path] = ""
4343
EditorNotifications.getInstance(project).updateAllNotifications()
@@ -62,7 +62,7 @@ class SopsService(
6262
cs.launch {
6363
withBackgroundProgress(project, message("background.decrypting")) {
6464
AppSettings.instance.recordHit()
65-
SopsWrapper.decrypt(text, {
65+
SopsWrapper.decrypt(text, project, {
6666
AppSettings.instance.recordHit()
6767
onSuccess(it)
6868
}, onError)
@@ -84,7 +84,7 @@ class SopsService(
8484

8585
val originalEncryptedText = file.getLastCommitContent(project)
8686
var originalDecryptedText = ""
87-
SopsWrapper.decrypt(originalEncryptedText.orEmpty(), onSuccess = {
87+
SopsWrapper.decrypt(originalEncryptedText.orEmpty(), project, onSuccess = {
8888
originalDecryptedText = it
8989
})
9090

@@ -102,7 +102,7 @@ class SopsService(
102102
}
103103

104104
SopsWrapper.encrypt(
105-
file, inPlace,
105+
file, project, inPlace,
106106
{
107107
errors[file.path] = ""
108108
EditorNotifications.getInstance(project).updateAllNotifications()
@@ -146,7 +146,7 @@ class SopsService(
146146

147147
withContext(Dispatchers.IO) {
148148
SopsWrapper.edit(
149-
file, newDecryptedText,
149+
file, project,newDecryptedText,
150150
{
151151
errors[file.path] = ""
152152
EditorNotifications.getInstance(project).updateAllNotifications()

src/main/kotlin/com/github/blarc/sops/intellij/plugin/settings/AppSettingsConfigurable.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ package com.github.blarc.sops.intellij.plugin.settings
33
import com.github.blarc.sops.intellij.plugin.SopsBundle
44
import com.github.blarc.sops.intellij.plugin.SopsBundle.message
55
import com.github.blarc.sops.intellij.plugin.notBlank
6-
import com.intellij.execution.configuration.EnvironmentVariablesComponent
76
import com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton
7+
import com.intellij.openapi.components.service
88
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
99
import com.intellij.openapi.options.BoundConfigurable
10+
import com.intellij.openapi.project.Project
1011
import com.intellij.openapi.ui.TextFieldWithBrowseButton
1112
import com.intellij.ui.JBColor
1213
import com.intellij.ui.components.JBLabel
1314
import com.intellij.ui.dsl.builder.*
1415

15-
class AppSettingsConfigurable : BoundConfigurable(message("name")) {
16+
// Most of the settings are global, but we use project configurable to set environment variables per project
17+
class AppSettingsConfigurable(val project: Project) : BoundConfigurable(message("name")) {
1618

1719
val sopsPathTextField = TextFieldWithBrowseButton()
1820
val sopsPathVerifyLabel = JBLabel()
@@ -36,7 +38,7 @@ class AppSettingsConfigurable : BoundConfigurable(message("name")) {
3638
}
3739

3840
button(message("settings.verify")) {
39-
AppService.instance.version(
41+
project.service<ProjectService>().version(
4042
sopsPathTextField.text,
4143
{ result ->
4244
sopsPathVerifyLabel.text = "$result"
@@ -68,6 +70,18 @@ class AppSettingsConfigurable : BoundConfigurable(message("name")) {
6870
)
6971
}
7072

73+
row {
74+
label(message("settings.project.environment"))
75+
.widthGroup("label")
76+
cell(EnvironmentVariablesTextFieldWithBrowseButton())
77+
.align(AlignX.FILL)
78+
.bind(
79+
componentSet = { c, s -> c.envs = s },
80+
componentGet = { c -> c.envs },
81+
prop = project.service<ProjectSettings>()::sopsProjectEnvironment.toMutableProperty()
82+
)
83+
}
84+
7185
row {
7286
panel {
7387
row {

src/main/kotlin/com/github/blarc/sops/intellij/plugin/settings/AppService.kt renamed to src/main/kotlin/com/github/blarc/sops/intellij/plugin/settings/ProjectService.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,20 @@ package com.github.blarc.sops.intellij.plugin.settings
33
import com.github.blarc.sops.intellij.plugin.SopsWrapper
44
import com.intellij.openapi.application.ApplicationManager
55
import com.intellij.openapi.components.Service
6+
import com.intellij.openapi.project.Project
67
import kotlinx.coroutines.CoroutineScope
78
import kotlinx.coroutines.launch
89

9-
@Service(Service.Level.APP)
10-
class AppService(private val cs: CoroutineScope) {
11-
12-
companion object {
13-
val instance: AppService
14-
get() = ApplicationManager.getApplication().getService(AppService::class.java)
15-
}
10+
@Service(Service.Level.PROJECT)
11+
class ProjectService(private val project: Project, private val cs: CoroutineScope) {
1612

1713
fun version(
1814
sopsPath: String,
1915
onSuccess: suspend (result: String) -> Unit,
2016
onError: suspend (message: String) -> Unit = {}
2117
) {
2218
cs.launch {
23-
SopsWrapper.version(sopsPath, onSuccess, onError)
19+
SopsWrapper.version(sopsPath, project, onSuccess, onError)
2420
}
2521
}
2622
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.github.blarc.sops.intellij.plugin.settings
2+
3+
import com.intellij.openapi.components.PersistentStateComponent
4+
import com.intellij.openapi.components.Service
5+
import com.intellij.openapi.components.State
6+
import com.intellij.openapi.components.Storage
7+
import com.intellij.util.xmlb.XmlSerializerUtil
8+
9+
@State(
10+
name = ProjectSettings.SERVICE_NAME,
11+
storages = [Storage("Sops.xml")]
12+
)
13+
@Service(Service.Level.PROJECT)
14+
class ProjectSettings : PersistentStateComponent<ProjectSettings?> {
15+
companion object {
16+
const val SERVICE_NAME = "com.github.blarc.sops.intellij.plugin.settings.ProjectSettings"
17+
}
18+
19+
var sopsProjectEnvironment: Map<String, String> = emptyMap()
20+
21+
override fun getState() = this
22+
23+
override fun loadState(state: ProjectSettings) {
24+
XmlSerializerUtil.copyBean(state, this)
25+
}
26+
27+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<resource-bundle>messages.SopsBundle</resource-bundle>
3535

3636
<extensions defaultExtensionNs="com.intellij">
37-
<applicationConfigurable
37+
<projectConfigurable
3838
parentId="tools"
3939
instance="com.github.blarc.sops.intellij.plugin.settings.AppSettingsConfigurable"
4040
key="name"

src/main/resources/messages/SopsBundle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ settings.title=Settings
44
settings.report-bug=Report bug
55
settings.path=Path to SOPS executable
66
settings.environment=SOPS environment
7+
settings.project.environment=SOPS project environment
78
settings.github-star=Star on GitHub
89
settings.github-sponsors=Sponsor on GitHub
910
settings.kofi=Buy me a coffee

0 commit comments

Comments
 (0)