feat: Environments release using source control (#6653)

* initial telemetry setup and adjusted pull return

* quicksave before merge

* feat: add conflicting workflow list to pull modal

* feat: update source control pull modal

* fix: fix linting issue

* feat: add Enter keydown event for submitting source control push modal (no-changelog)

feat: add Enter keydown event for submitting source control push modal

* quicksave

* user workflow table for export

* improve telemetry data

* pull api telemetry

* fix lint

* Copy tweaks.

* remove authorName and authorEmail and pick from user

* rename owners.json to workflow_owners.json

* ignore credential conflicts on pull

* feat: several push/pull flow changes and design update

* pull and push return same data format

* fix: add One last step toast for successful pull

* feat: add up to date pull toast

* fix: add proper Learn more link for push and pull modals

* do not await tracking being sent

* fix import

* fix await

* add more sourcecontrolfile status

* Minor copy tweak for "More info".

* Minor copy tweak for "More info".

* ignore variable_stub conflicts on pull

* ignore whitespace differences

* do not show remote workflows that are not yet created

* fix telemetry

* fix toast when pulling deleted wf

* lint fix

* refactor and make some imports dynamic

* fix variable edit validation

* fix telemetry response

* improve telemetry

* fix unintenional delete commit

* fix status unknown issue

* fix up to date toast

* do not export active state and reapply versionid

* use update instead of upsert

* fix: show all workflows when clicking push to git

* feat: update Up to date pull translation

* fix: update read only env checks

* do not update versionid of only active flag changes

* feat: prevent access to new workflow and templates import when read only env

* feat: send only active state and version if workflow state is not dirty

* fix: Detect when only active state has changed and prevent generation a new version ID

* feat: improve readonly env messages

* make getPreferences public

* fix telemetry issue

* fix: add partial workflow update based on dirty state when changing active state

* update unit tests

* fix: remove unsaved changes check in readOnlyEnv

* fix: disable push to git button when read onyl env

* fix: update readonly toast duration

* fix: fix pinning and title input in protected mode

* initial commit (NOT working)

* working push

* cleanup and implement pull

* fix getstatus

* update import to new method

* var and tag diffs are no conflicts

* only show pull conflict for workflows

* refactor and ignore faulty credentials

* add sanitycheck for missing git folder

* prefer fetch over pull and limit depth to 1

* back to pull...

* fix setting branch on initial connect

* fix test

* remove clean workfolder

* refactor: Remove some unnecessary code

* Fixed links to docs.

* fix getstatus query params

* lint fix

* dialog to show local and remote name on conflict

* only show remote name on conflict

* fix credential expression export

* fix: Broken test

* dont show toast on pull with empty var/tags and refactor

* apply frontend changes from old branch

* fix tag with same name import

* fix buttons shown for non instance owners

* prepare local storage key for removal

* refactor: Change wording on pushing and pulling

* refactor: Change menu item

* test: Fix broken test

* Update packages/cli/src/environments/sourceControl/types/sourceControlPushWorkFolder.ts

Co-authored-by: Iván Ovejero <ivov.src@gmail.com>

---------

Co-authored-by: Alex Grozav <alex@grozav.com>
Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
Michael Auerswald
2023-07-26 09:25:01 +02:00
committed by GitHub
parent bcfc5e717b
commit fc7aa8bd66
51 changed files with 2210 additions and 1064 deletions

View File

@@ -3,28 +3,45 @@ import Modal from './Modal.vue';
import { SOURCE_CONTROL_PULL_MODAL_KEY } from '@/constants';
import type { PropType } from 'vue';
import type { EventBus } from 'n8n-design-system/utils';
import type { SourceControlStatus } from '@/Interface';
import type { SourceControlAggregatedFile } from '@/Interface';
import { useI18n, useLoadingService, useToast } from '@/composables';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useUIStore } from '@/stores';
import { useRoute } from 'vue-router/composables';
import { useRoute, useRouter } from 'vue-router/composables';
import { computed, nextTick, ref } from 'vue';
import { sourceControlEventBus } from '@/event-bus/source-control';
const props = defineProps({
data: {
type: Object as PropType<{ eventBus: EventBus; status: SourceControlStatus }>,
type: Object as PropType<{ eventBus: EventBus; status: SourceControlAggregatedFile[] }>,
default: () => ({}),
},
});
const defaultStagedFileTypes = ['tags', 'variables', 'credential'];
const incompleteFileTypes = ['variables', 'credential'];
const loadingService = useLoadingService();
const uiStore = useUIStore();
const toast = useToast();
const { i18n } = useI18n();
const sourceControlStore = useSourceControlStore();
const router = useRouter();
const route = useRoute();
const files = ref<SourceControlAggregatedFile[]>(props.data.status || []);
const workflowFiles = computed(() => {
return files.value.filter((file) => file.type === 'workflow');
});
const modifiedWorkflowFiles = computed(() => {
return workflowFiles.value.filter((file) => file.status === 'modified');
});
const deletedWorkflowFiles = computed(() => {
return workflowFiles.value.filter((file) => file.status === 'deleted');
});
function close() {
uiStore.closeModal(SOURCE_CONTROL_PULL_MODAL_KEY);
}
@@ -35,11 +52,29 @@ async function pullWorkfolder() {
try {
await sourceControlStore.pullWorkfolder(true);
const hasVariablesOrCredentials = files.value.some((file) => {
return incompleteFileTypes.includes(file.type);
});
toast.showMessage({
message: i18n.baseText('settings.sourceControl.pull.success.description'),
title: i18n.baseText('settings.sourceControl.pull.success.title'),
type: 'success',
});
if (hasVariablesOrCredentials) {
nextTick(() => {
toast.showMessage({
message: i18n.baseText('settings.sourceControl.pull.oneLastStep.description'),
title: i18n.baseText('settings.sourceControl.pull.oneLastStep.title'),
type: 'info',
duration: 0,
showClose: true,
offset: 0,
});
});
}
sourceControlEventBus.emit('pull');
} catch (error) {
toast.showError(error, 'Error');
} finally {
@@ -59,7 +94,21 @@ async function pullWorkfolder() {
<div :class="$style.container">
<n8n-text>
{{ i18n.baseText('settings.sourceControl.modals.pull.description') }}
<n8n-link :to="i18n.baseText('settings.sourceControl.docs.using.pushPull.url')">
{{ i18n.baseText('settings.sourceControl.modals.pull.description.learnMore') }}
</n8n-link>
</n8n-text>
<div v-if="modifiedWorkflowFiles.length > 0" class="mt-l">
<ul :class="$style.filesList">
<li v-for="file in modifiedWorkflowFiles" :key="file.id">
<n8n-link :class="$style.fileLink" new-window :to="`/workflow/${file.id}`">
{{ file.name }}
<n8n-icon icon="external-link-alt" />
</n8n-link>
</li>
</ul>
</div>
</div>
</template>
@@ -81,6 +130,27 @@ async function pullWorkfolder() {
overflow-wrap: break-word;
}
.filesList {
list-style: inside;
margin-top: var(--spacing-3xs);
padding-left: var(--spacing-2xs);
li {
margin-top: var(--spacing-3xs);
}
}
.fileLink {
svg {
display: none;
margin-left: var(--spacing-4xs);
}
&:hover svg {
display: inline-flex;
}
}
.footer {
display: flex;
flex-direction: row;