# Features 
- Build environment variables for NodeJS builds
- Initial monorepo support (more tests needed!)

# Fixes
- Fix wrong redirects
- Logout fix for the session manager
This commit is contained in:
Andras Bacsai
2021-07-16 23:42:14 +02:00
committed by GitHub
parent 2d0f22b379
commit b4c836afbd
33 changed files with 1272 additions and 2194 deletions

View File

@@ -1,6 +1,6 @@
<script>
import { VITE_GITHUB_APP_NAME } from '$lib/consts';
import { application, isPullRequestPermissionsGranted } from '$store';
import { application, isPullRequestPermissionsGranted, originalDomain } from '$store';
import { onMount } from 'svelte';
import TooltipInfo from '$components/TooltipInfo.svelte';
import { request } from '$lib/request';
@@ -122,7 +122,7 @@
async function setPreviewDeployment() {
if ($application.general.isPreviewDeploymentEnabled) {
const result = window.confirm(
"Are you sure? It will delete all PR deployments - it's NOT reversible!"
"DANGER ZONE! It will delete all PR deployments. It's NOT reversible! Are you sure?"
);
if (result) {
loading.previewDeployment = true;
@@ -194,9 +194,12 @@
}
}
onMount(() => {
if (!$application.publish.domain) domainInput.focus();
if (!$application.publish.domain) {
domainInput.focus();
} else {
$originalDomain = $application.publish.domain;
}
});
</script>
<div>
@@ -378,7 +381,9 @@
</span>
</button>
{#if loading.previewDeployment}
<div class="absolute left-0 bottom-0 -mb-4 -ml-2 text-xs font-bold">{$application.general.isPreviewDeploymentEnabled ? 'Enabling...' : 'Disabling...' }</div>
<div class="absolute left-0 bottom-0 -mb-4 -ml-2 text-xs font-bold">
{$application.general.isPreviewDeploymentEnabled ? 'Enabling...' : 'Disabling...'}
</div>
{/if}
</div>
{:else}
@@ -438,6 +443,10 @@
<input
bind:this={domainInput}
class="border-2"
disabled={$page.path !== '/application/new'}
class:cursor-not-allowed={$page.path !== '/application/new'}
class:bg-warmGray-900={$page.path !== '/application/new'}
class:hover:bg-warmGray-900={$page.path !== '/application/new'}
class:placeholder-red-500={$application.publish.domain == null ||
$application.publish.domain == ''}
class:border-red-500={$application.publish.domain == null ||
@@ -455,7 +464,15 @@
}/api`}
/></label
>
<input id="Path" bind:value={$application.publish.path} placeholder="/" />
<input
id="Path"
bind:value={$application.publish.path}
disabled={$page.path !== '/application/new'}
class:cursor-not-allowed={$page.path !== '/application/new'}
class:bg-warmGray-900={$page.path !== '/application/new'}
class:hover:bg-warmGray-900={$page.path !== '/application/new'}
placeholder="/"
/>
</div>
</div>
<label for="Port" class:text-warmGray-800={!buildpacks[$application.build.pack].port.active}
@@ -590,5 +607,4 @@
.buildpack {
@apply px-6 py-2 mx-2 my-2 bg-warmGray-800 w-48 ease-in-out hover:scale-105 text-center rounded border-2 border-transparent border-dashed cursor-pointer transition duration-100;
}
</style>

View File

@@ -19,37 +19,29 @@
async function getPRDeployments() {
const { configuration } = await request(`/api/v1/application/config`, $session, {
body: {
name: $application.repository.name,
organization: $application.repository.organization,
branch: $application.repository.branch
nickname: $application.general.nickname
}
});
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
}
async function removePR(prConfiguration) {
const result = window.confirm("Are you sure? It's NOT reversible!");
const result = window.confirm("DANGER ZONE! It's NOT reversible! Are you sure?");
if (result) {
await request(`/api/v1/application/remove`, $session, {
body: {
organization: prConfiguration.repository.organization,
name: prConfiguration.repository.name,
branch: prConfiguration.repository.branch,
domain: prConfiguration.publish.domain
nickname: prConfiguration.general.nickname
}
});
browser && toast.push('PR deployment removed.');
const { configuration } = await request(`/api/v1/application/config`, $session, {
body: {
name: prConfiguration.repository.name,
organization: prConfiguration.repository.organization,
branch: prConfiguration.repository.branch
nickname: prConfiguration.general.nickname
}
});
$prApplication = configuration.filter((c) => c.general.pullRequest !== 0);
}
}
</script>
<div class="text-2xl font-bold border-gradient w-48">Pull Requests</div>

View File

@@ -1,80 +1,128 @@
<script>
import { application } from "$store";
import { application } from '$store';
import BuildEnv from '../BuildEnv.svelte';
let secret = {
name: null,
value: null,
};
let foundSecret = null;
async function saveSecret() {
if (secret.name && secret.value) {
const found = $application.publish.secrets.find(
s => s.name === secret.name,
);
if (!found) {
$application.publish.secrets = [
...$application.publish.secrets,
{
name: secret.name,
value: secret.value,
},
];
secret = {
name: null,
value: null,
};
} else {
foundSecret = found;
}
}
}
let secret = {
name: null,
value: null,
isBuild: false
};
let foundSecret = null;
async function saveSecret() {
if (secret.name && secret.value) {
const found = $application.publish.secrets.find((s) => s.name === secret.name);
if (!found) {
$application.publish.secrets = [
...$application.publish.secrets,
{
name: secret.name,
value: secret.value,
isBuild: secret.isBuild
}
];
secret = {
name: null,
value: null,
isBuild: false
};
} else {
foundSecret = found;
}
}
}
async function removeSecret(name) {
foundSecret = null
$application.publish.secrets = [
...$application.publish.secrets.filter(s => s.name !== name),
];
}
async function removeSecret(name) {
foundSecret = null;
$application.publish.secrets = [...$application.publish.secrets.filter((s) => s.name !== name)];
}
</script>
<div class="text-2xl font-bold border-gradient w-24">Secrets</div>
<div class="max-w-xl mx-auto text-center pt-4">
<div class="text-left text-base font-bold tracking-tight text-warmGray-400">
New Secret
</div>
<div class="flex space-x-4">
<input id="secretName" bind:value="{secret.name}" placeholder="Name" class="w-64 border-2 border-transparent" />
<input id="secretValue" bind:value="{secret.value}" placeholder="Value" class="w-64 border-2 border-transparent" />
<button class="icon hover:text-green-500" on:click="{saveSecret}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
</div>
{#if $application.publish.secrets.length > 0}
<div class="py-4">
{#each $application.publish.secrets as s}
<div class="flex space-x-4">
<input
id="{s.name}"
value="{s.name}"
disabled
class="border-2 bg-transparent border-transparent w-64"
class:border-red-600="{foundSecret && foundSecret.name === s.name}"
/>
<input
id="{s.createdAt}"
value="SAVED"
disabled
class="border-2 bg-transparent border-transparent w-64"
/>
<button class="icon hover:text-red-500" on:click="{() => removeSecret(s.name)}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
</div>
{/each}
</div>
{/if}
<div class="text-2xl font-bold border-gradient w-24">Secrets</div>
<div class="max-w-3xl mx-auto text-center pt-4">
<div class="flex space-x-4">
<div class="grid grid-flow-row">
<label for="secretName">Secret Name</label>
<input
id="secretName"
bind:value={secret.name}
placeholder="Name"
class="w-64 border-2 border-transparent"
/>
</div>
<div class="grid grid-flow-row">
<label for="secretValue">Secret Value</label>
<input
id="secretValue"
bind:value={secret.value}
placeholder="Value"
class="w-64 border-2 border-transparent"
/>
</div>
<div class="grid grid-flow-row">
<label for="buildVariable">Is build variable?</label>
<div class="mt-2 w-full">
<BuildEnv {secret} />
</div>
</div>
<div class="mt-6">
<button class="icon hover:text-green-500" on:click={saveSecret}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
</button>
</div>
</div>
{#if $application.publish.secrets.length > 0}
<div class="pt-1">
{#each $application.publish.secrets as secret}
<div class="flex space-x-4 space-y-2">
<input
id={secret.name}
value={secret.name}
disabled
class="border-2 bg-transparent border-transparent w-64 hover:bg-transparent"
class:border-red-600={foundSecret && foundSecret.name === secret.name}
/>
<input
id={secret.createdAt}
value="SAVED"
disabled
class="border-2 bg-transparent border-transparent w-64 hover:bg-transparent"
/>
<div class="flex justify-center items-center px-12">
<BuildEnv {secret} readOnly />
</div>
<button class="icon hover:text-red-500" on:click={() => removeSecret(secret.name)}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</button>
</div>
{/each}
</div>
{/if}
</div>

View File

@@ -0,0 +1,55 @@
<script>
export let secret;
export let readOnly = false;
function isBuildSet() {
if (!readOnly) secret.isBuild = !secret.isBuild;
}
</script>
<button
id="buildVariable"
type="button"
aria-pressed="false"
on:click={isBuildSet}
class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-100"
class:bg-green-600={secret.isBuild}
class:bg-warmGray-700={!secret.isBuild}
class:opacity-50={readOnly}
class:cursor-not-allowed={readOnly}
>
<span class="sr-only">Use setting</span>
<span
class="pointer-events-none relative inline-block h-5 w-5 rounded-full bg-white shadow transition ease-in-out duration-200 transform"
class:translate-x-5={secret.isBuild}
class:translate-x-0={!secret.isBuild}
>
<span
class=" ease-in duration-200 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
class:opacity-0={secret.isBuild}
class:opacity-100={!secret.isBuild}
aria-hidden="true"
>
<svg class="bg-white h-3 w-3 text-red-600" fill="none" viewBox="0 0 12 12">
<path
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
<span
class="ease-out duration-100 absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"
aria-hidden="true"
class:opacity-100={secret.isBuild}
class:opacity-0={!secret.isBuild}
>
<svg class="bg-white h-3 w-3 text-green-600" fill="currentColor" viewBox="0 0 12 12">
<path
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
/>
</svg>
</span>
</span>
</button>

View File

@@ -19,6 +19,7 @@
import Tabs from '$components/Application/Tabs.svelte';
import Repositories from '$components/Application/Repositories.svelte';
import Login from '$components/Application/Login.svelte';
import { dashify } from '$lib/common';
let loading = {
github: false,
branches: false
@@ -26,15 +27,7 @@
let branches = [];
let relogin = false;
let permissions = {};
function dashify(str: string, options?: any) {
if (typeof str !== 'string') return str;
return str
.trim()
.replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-'))
.replace(/^-+|-+$/g, '')
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
.toLowerCase();
}
async function getGithubRepos(id, page) {
return await request(
`https://api.github.com/user/installations/${id}/repositories?per_page=100&page=${page}`,
@@ -174,7 +167,6 @@
}, 100);
}
}
</script>
<div in:fade={{ duration: 100 }}>

View File

@@ -1,5 +1,5 @@
<script>
import { application, initialApplication, initConf } from '$store';
import { application, initialApplication, initConf, originalDomain } from '$store';
import { onDestroy } from 'svelte';
import { toast } from '@zerodevx/svelte-toast';
import Tooltip from '$components/Tooltip.svelte';
@@ -9,15 +9,12 @@
import { browser } from '$app/env';
async function removeApplication() {
const result = window.confirm(
"Are you sure? It will delete all deployments, including PR's - it's NOT reversible!"
"DANGER ZONE! It will delete all deployments, including PR's. It's NOT reversible! Are you sure?"
);
if (result) {
await request(`/api/v1/application/remove`, $session, {
body: {
organization: $application.repository.organization,
name: $application.repository.name,
branch: $application.repository.branch,
domain: $application.publish.domain
nickname: $application.general.nickname
}
});
@@ -46,16 +43,14 @@
$initConf = JSON.parse(JSON.stringify($application));
if (browser) {
toast.push('Application deployment queued.');
goto(
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs/${$application.general.deployId}`,
{ replaceState: true }
);
goto(`/application/${$application.general.nickname}/logs/${$application.general.deployId}`, {
replaceState: true
});
}
} catch (error) {
browser && toast.push(error.error || error || 'Ooops something went wrong.');
// browser && toast.push(error.error || error || 'Ooops something went wrong.');
}
}
</script>
<nav class="flex text-white justify-end items-center m-4 fixed right-0 top-0 space-x-4 z-50">
@@ -131,10 +126,7 @@
class:cursor-not-allowed={$page.path === '/application/new'}
class:text-blue-400={/logs\/*/.test($page.path)}
class:bg-warmGray-700={/logs\/*/.test($page.path)}
on:click={() =>
goto(
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/logs`
)}
on:click={() => goto(`/application/${$application.general.nickname}/logs`)}
>
<svg
class="w-6"
@@ -160,10 +152,7 @@
$page.path === '/application/new'}
class:bg-warmGray-700={$page.path.endsWith('configuration') ||
$page.path === '/application/new'}
on:click={() =>
goto(
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`
)}
on:click={() => goto(`/application/${$application.general.nickname}/configuration`)}
>
<svg
class="w-6"

View File

@@ -27,28 +27,6 @@
}
}
async function load() {
const found = $dashboard?.applications?.deployed.find((deployment) => {
if (
deployment.configuration.repository.organization === $application.repository.organization &&
deployment.configuration.repository.name === $application.repository.name &&
deployment.configuration.repository.branch === $application.repository.branch
) {
return deployment;
}
});
if (found) {
$application = { ...found.configuration };
if ($page.path === '/application/new') {
if (browser) {
toast.push('This repository & branch is already defined. Redirecting...');
goto(
`/application/${$application.repository.organization}/${$application.repository.name}/${$application.repository.branch}/configuration`,
{ replaceState: true }
);
}
}
return;
}
if ($page.path === '/application/new') {
try {
const dir = await request(