Skip to content

Commit

Permalink
Merge pull request #2288 from posit-dev/dotnomad/secrets-view
Browse files Browse the repository at this point in the history
Add Secrets view and deploy with Secrets to extension
  • Loading branch information
dotNomad authored Sep 20, 2024
2 parents c1303e7 + 144b185 commit 5e563c7
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export type DeployMsg = AnyWebviewToHostMessage<
credentialName: string;
configurationName: string;
projectDir: string;
secrets?: Record<string, string>;
}
>;

Expand Down
12 changes: 3 additions & 9 deletions extensions/vscode/src/views/homeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
credentialName: string,
configurationName: string,
projectDir: string,
secrets?: Record<string, string>,
) {
try {
const api = await useApi();
Expand All @@ -256,6 +257,7 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
credentialName,
configurationName,
projectDir,
secrets,
);
deployProject(response.data.localId, this.stream);
} catch (error: unknown) {
Expand All @@ -270,6 +272,7 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
msg.content.credentialName,
msg.content.configurationName,
msg.content.projectDir,
msg.content.secrets,
);
}

Expand Down Expand Up @@ -1398,15 +1401,6 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
return await this.sendRefreshedFilesLists();
}, DebounceDelaysMS.file);

public initiatePublish(target: PublishProcessParams) {
this.initiateDeployment(
target.deploymentName,
target.credentialName,
target.configurationName,
target.projectDir,
);
}

public async handleFileInitiatedDeploymentSelection(uri: Uri) {
// Guide the user to create a new Deployment with that file as the entrypoint
// if one doesn’t exist
Expand Down
2 changes: 2 additions & 0 deletions extensions/vscode/webviews/homeView/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<EvenEasierDeploy class="easy-deploy-container" />
<template v-if="home.selectedConfiguration">
<ProjectFiles v-model:expanded="projectFilesExpanded" />
<Secrets />
<PythonPackages />
<RPackages />
<Credentials />
Expand All @@ -21,6 +22,7 @@ import { ref } from "vue";
import OverlayableView from "src/components/OverlayableView.vue";
import EvenEasierDeploy from "src/components/EvenEasierDeploy.vue";
import ProjectFiles from "src/components/views/projectFiles/ProjectFiles.vue";
import Secrets from "src/components/views/secrets/Secrets.vue";
import PythonPackages from "src/components/views/PythonPackages.vue";
import RPackages from "src/components/views/RPackages.vue";
import Credentials from "src/components/views/Credentials.vue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const onPublishStartMsg = () => {
};
const onPublishFinishSuccessMsg = () => {
const home = useHomeStore();
home.clearSecretValues();
home.publishInProgress = false;
home.lastContentRecordResult = `Last Deployment was Successful`;
home.lastContentRecordMsg = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,22 @@ const deploy = () => {
return;
}
// Only send up secrets that have values
const secrets: Record<string, string> = {};
home.secrets.forEach((value, name) => {
if (value) {
secrets[name] = value;
}
});
hostConduit.sendMsg({
kind: WebviewToHostMessageType.DEPLOY,
content: {
deploymentName: home.selectedContentRecord.saveName,
configurationName: home.selectedConfiguration.configurationName,
credentialName: home.serverCredential.name,
projectDir: home.selectedContentRecord.projectDir,
secrets: secrets,
},
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<input
v-model="model"
ref="input"
class="sidebar-input"
:aria-label="ariaLabel"
@keydown.prevent.enter="$emit('submit')"
@keydown.escape="$emit('cancel')"
/>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
const model = defineModel();
const props = defineProps<{
label: string;
}>();
defineEmits(["submit", "cancel"]);
const input = ref<HTMLInputElement | null>(null);
const ariaLabel = computed(
() => `${props.label}. Press Enter to confirm or Escape to cancel.`,
);
const select = () => {
input.value?.select();
};
defineExpose({ select });
</script>

<style lang="scss" scoped>
.sidebar-input {
background-color: var(--vscode-input-background);
border: 1px solid var(--vscode-input-border, transparent);
color: var(--vscode-input-foreground);
line-height: 20px;
padding: 0;
outline-color: var(--vscode-focusBorder);
outline-offset: -1px;
outline-style: solid;
outline-width: 1px;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
/>
<div class="tree-item-label-container">
<span class="tree-item-title" data-automation="req">{{ title }}</span>
<span v-if="description" class="tree-item-description">
<span v-if="$slots.description" class="tree-item-description">
<slot name="description" />
</span>
<span v-else-if="description" class="tree-item-description">
{{ description }}
</span>
</div>
Expand Down Expand Up @@ -79,6 +82,7 @@ withDefaults(defineProps<Props>(), {
defineSlots<{
default(props: { indentLevel: number }): any;
description(): any;
postDecor(): any;
}>();
Expand Down Expand Up @@ -151,6 +155,7 @@ const toggleExpanded = () => {
.tree-item-label-container {
flex: 1;
display: flex;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
Expand All @@ -159,13 +164,24 @@ const toggleExpanded = () => {
line-height: 22px;
color: inherit;
white-space: pre;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.tree-item-description {
line-height: 22px;
font-size: 0.9em;
margin-left: 0.5em;
opacity: 0.7;
flex-shrink: 100000;
white-space: pre;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
&:not(:has(input)) {
opacity: 0.7;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<template>
<TreeItem
:title="name"
:actions="actions"
codicon="codicon-lock-small"
:list-style="secretValue || isEditing ? 'default' : 'deemphasized'"
:tooltip="tooltip"
align-icon-with-twisty
>
<template #description>
<SidebarInput
v-if="isEditing"
ref="input"
v-model="inputValue"
class="w-full"
label="Type secret value"
@submit="updateSecret"
@cancel="isEditing = false"
/>
<template v-else-if="secretValue">••••••</template>
</template>
</TreeItem>
</template>

<script setup lang="ts">
import { computed, ref, nextTick } from "vue";
import TreeItem from "src/components/tree/TreeItem.vue";
import SidebarInput from "src/components/SidebarInput.vue";
import { useHomeStore } from "src/stores/home";
import { ActionButton } from "src/components/ActionToolbar.vue";
interface Props {
name: string;
}
const props = defineProps<Props>();
const input = ref<InstanceType<typeof SidebarInput> | null>(null);
const isEditing = ref(false);
const inputValue = ref<string>();
const home = useHomeStore();
const secretValue = computed(() => home.secrets.get(props.name));
const inputSecret = () => {
// Update inputValue in case the secret value has changed or been cleared
inputValue.value = secretValue.value;
isEditing.value = true;
// Wait for next tick to ensure the input is rendered
nextTick(() => input.value?.select());
};
const updateSecret = () => {
home.secrets.set(props.name, inputValue.value);
isEditing.value = false;
};
const tooltip = computed(() => {
if (secretValue.value) {
return "On the next deploy the new value will be set for the deployment.";
}
return "No value has been set. The value will not change on the next deploy.";
});
const actions = computed<ActionButton[]>(() => {
// Show no actions while the value is being edited
if (isEditing.value) {
return [];
}
const result = [
{
label: "Edit Value",
codicon: "codicon-pencil",
fn: inputSecret,
},
];
if (secretValue.value) {
result.push({
label: "Clear Value",
codicon: "codicon-remove",
fn: () => {
home.secrets.set(props.name, undefined);
},
});
}
return result;
});
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<TreeSection
title="Secrets"
:actions="sectionActions"
:count="home.secretsWithValueCount"
>
<template v-if="home.secrets.size">
<Secret v-for="[name] in home.secrets" :name="name" :key="name" />
</template>
<WelcomeView v-else>
<p>No secrets have been added to the configuration.</p>
</WelcomeView>
</TreeSection>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { ActionButton } from "src/components/ActionToolbar.vue";
import TreeSection from "src/components/tree/TreeSection.vue";
import WelcomeView from "src/components/WelcomeView.vue";
import { useHomeStore } from "src/stores/home";
import Secret from "src/components/views/secrets/Secret.vue";
const home = useHomeStore();
const sectionActions = computed<ActionButton[]>(() => [
{
label: "Clear Values for all Secrets",
codicon: "codicon-clear-all",
fn: () => {
home.secrets.forEach((_, key) => {
home.secrets.set(key, undefined);
});
},
},
]);
</script>
Loading

0 comments on commit 5e563c7

Please sign in to comment.