prototype of the deploys page using the inspect component
This commit is contained in:
@@ -0,0 +1 @@
|
||||
CONFIG_PATH=config.yaml
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "consistent",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"arrowParens": "avoid",
|
||||
"proseWrap": "always"
|
||||
}
|
||||
@@ -23,11 +23,19 @@ deploys:
|
||||
branch: main
|
||||
type: dockerfile
|
||||
options:
|
||||
path: ./Dockerfile
|
||||
ports:
|
||||
- 80:8080
|
||||
volumes:
|
||||
- /var/www/html:/var/www/html
|
||||
- name: project2
|
||||
url: ssh://example.org/username/project2.git
|
||||
commit: 04c540647a
|
||||
type: docker-compose
|
||||
options:
|
||||
path: ./docker-compose.yml
|
||||
path: ./docker-compose.yml # already the default
|
||||
- name: project3
|
||||
url: https://github.com/username/project3
|
||||
type: shell
|
||||
options:
|
||||
path: ./deploy.sh # already the default
|
||||
```
|
||||
+10
-1
@@ -2,9 +2,18 @@ deploys:
|
||||
- name: project1
|
||||
url: https://github.com/username/project1
|
||||
branch: main
|
||||
type: docker
|
||||
options:
|
||||
ports:
|
||||
- 80:8080
|
||||
volumes:
|
||||
- /var/www/html:/var/www/html
|
||||
- name: project2
|
||||
url: https://github.com/username/project2
|
||||
type: dockerfile
|
||||
options:
|
||||
port: 80:8080
|
||||
ports:
|
||||
- 9000:8080
|
||||
volumes:
|
||||
- /var/www/html:/var/www/html
|
||||
- name: project2
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"async-mutex": "^0.4.1",
|
||||
"dockerode": "^4.0.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nodegit": "^0.27.0",
|
||||
"preact": "^10.19.4",
|
||||
"typescript": "^5.3.3"
|
||||
@@ -27,6 +28,7 @@
|
||||
"devDependencies": {
|
||||
"@types/dockerode": "^3.3.23",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"sass": "^1.70.0"
|
||||
}
|
||||
}
|
||||
Generated
+10
@@ -35,6 +35,9 @@ dependencies:
|
||||
js-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
nodegit:
|
||||
specifier: ^0.27.0
|
||||
version: 0.27.0
|
||||
@@ -52,6 +55,9 @@ devDependencies:
|
||||
'@types/js-yaml':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.202
|
||||
version: 4.14.202
|
||||
sass:
|
||||
specifier: ^1.70.0
|
||||
version: 1.70.0
|
||||
@@ -1000,6 +1006,10 @@ packages:
|
||||
'@types/node': 20.11.17
|
||||
dev: false
|
||||
|
||||
/@types/lodash@4.14.202:
|
||||
resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==}
|
||||
dev: true
|
||||
|
||||
/@types/mdast@4.0.3:
|
||||
resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
|
||||
dependencies:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Value } from './Inspect.jsx'
|
||||
|
||||
export const Deploy = ({ deploy }) => {
|
||||
return (
|
||||
<div class="deploy">
|
||||
<Value value={deploy} borderless />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import _ from 'lodash'
|
||||
import { clsx } from '../utils.js'
|
||||
|
||||
export const Value = ({ value, borderless }) => {
|
||||
return Array.isArray(value) ? (
|
||||
<ValueArray value={value} borderless={borderless} />
|
||||
) : typeof value === 'object' ? (
|
||||
<ValueObject value={value} borderless={borderless} />
|
||||
) : (
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
export const ValueArray = ({ value, borderless }) => {
|
||||
return (
|
||||
<div class={clsx('compound-value array', borderless && 'borderless')}>
|
||||
{value.map(item => (
|
||||
<div class="item">
|
||||
<Value value={item} borderless={borderless} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const ValueObject = ({ value, borderless }) => {
|
||||
return (
|
||||
<div class={clsx('compound-value object', borderless && 'borderless')}>
|
||||
{Object.entries(value).map(([k, v]) => (
|
||||
<>
|
||||
<div title={k} class="key">
|
||||
{_.startCase(k)}
|
||||
</div>
|
||||
<div class="value">
|
||||
<Value value={v} borderless={borderless} />
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+113
-18
@@ -10,27 +10,50 @@ export const NewDeployForm = () => {
|
||||
<input id="deploy-name" name="deploy-name" type="text" placeholder="Project name..." />
|
||||
|
||||
<label for="deploy-url">Url</label>
|
||||
<input id="deploy-url" name="deploy-url" type="text" placeholder="Valid git clone url..." />
|
||||
<input
|
||||
id="deploy-url"
|
||||
name="deploy-url"
|
||||
type="text"
|
||||
placeholder="Valid git clone url..."
|
||||
/>
|
||||
|
||||
<label for="deploy-ref-type">Ref</label>
|
||||
<div class="compound">
|
||||
<select id="deploy-ref-type" name="deploy-ref-type" value={refType} onChange={e => setRefType(e.target.value)}>
|
||||
<select
|
||||
id="deploy-ref-type"
|
||||
name="deploy-ref-type"
|
||||
value={refType}
|
||||
onChange={e => setRefType(e.target.value)}
|
||||
>
|
||||
<option value="default">Default</option>
|
||||
<option value="branch">Branch</option>
|
||||
<option value="tag">Tag</option>
|
||||
<option value="commit">Commit</option>
|
||||
</select>
|
||||
<input class="fill" id="deploy-ref-value" name="deploy-ref-value" disabled={refType === 'default'} type="text" placeholder="Ref value..." />
|
||||
<input
|
||||
class="fill"
|
||||
id="deploy-ref-value"
|
||||
name="deploy-ref-value"
|
||||
disabled={refType === 'default'}
|
||||
type="text"
|
||||
placeholder="Ref value..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label for="deploy-type">Type</label>
|
||||
<select id="deploy-type" name="deploy-type" value={deployType} onChange={e => setDeployType(e.target.value)}>
|
||||
<select
|
||||
id="deploy-type"
|
||||
name="deploy-type"
|
||||
value={deployType}
|
||||
onChange={e => setDeployType(e.target.value)}
|
||||
>
|
||||
<option value="initial" disabled>
|
||||
Select a deploy type...
|
||||
</option>
|
||||
<option value="shell">Shell</option>
|
||||
<option value="docker">Docker</option>
|
||||
<option value="dockerfile">Dockerfile</option>
|
||||
<option value="docker-compose">Docker Compose</option>
|
||||
<option value="shell">Shell</option>
|
||||
</select>
|
||||
|
||||
<DeployOptions type={deployType} />
|
||||
@@ -44,12 +67,14 @@ export const NewDeployForm = () => {
|
||||
|
||||
export const DeployOptions = ({ type }) => {
|
||||
switch (type) {
|
||||
case 'shell':
|
||||
return <ShellDeploy />
|
||||
case 'dockerfile':
|
||||
case 'docker':
|
||||
return <DockerDeploy />
|
||||
case 'dockerfile':
|
||||
return <DockerfileDeploy />
|
||||
case 'docker-compose':
|
||||
return <DockerComposeDeploy />
|
||||
case 'shell':
|
||||
return <ShellDeploy />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@@ -58,20 +83,75 @@ export const DeployOptions = ({ type }) => {
|
||||
const DockerDeploy = () => {
|
||||
return (
|
||||
<>
|
||||
<label for="deploy-options-path">Path</label>
|
||||
<input id="deploy-options-path" name="deploy-options-path" type="text" placeholder="Default is ./Dockerfile..." />
|
||||
|
||||
<label for="deploy-options-image">Image</label>
|
||||
<input id="deploy-options-image" name="deploy-options-image" type="text" placeholder="organization/image:latest" />
|
||||
<input
|
||||
id="deploy-options-image"
|
||||
name="deploy-options-image"
|
||||
type="text"
|
||||
placeholder="organization/image:latest"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-ports">Ports</label>
|
||||
<textarea id="deploy-options-ports" name="deploy-options-ports" rows={2} placeholder="80:8080" />
|
||||
<textarea
|
||||
id="deploy-options-ports"
|
||||
name="deploy-options-ports"
|
||||
rows={2}
|
||||
placeholder="80:8080"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-env">Environment</label>
|
||||
<textarea id="deploy-options-env" name="deploy-options-env" rows={2} placeholder="FOO=bar" />
|
||||
<textarea
|
||||
id="deploy-options-env"
|
||||
name="deploy-options-env"
|
||||
rows={2}
|
||||
placeholder="FOO=bar"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-volumes">Volumes</label>
|
||||
<textarea id="deploy-options-volumes" name="deploy-options-volumes" rows={2} placeholder="/var/www/example:/data" />
|
||||
<textarea
|
||||
id="deploy-options-volumes"
|
||||
name="deploy-options-volumes"
|
||||
rows={2}
|
||||
placeholder="/var/www/example:/data"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const DockerfileDeploy = () => {
|
||||
return (
|
||||
<>
|
||||
<label for="deploy-options-path">Path</label>
|
||||
<input
|
||||
id="deploy-options-path"
|
||||
name="deploy-options-path"
|
||||
type="text"
|
||||
placeholder="./Dockerfile"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-ports">Ports</label>
|
||||
<textarea
|
||||
id="deploy-options-ports"
|
||||
name="deploy-options-ports"
|
||||
rows={2}
|
||||
placeholder="80:8080..."
|
||||
/>
|
||||
|
||||
<label for="deploy-options-env">Environment</label>
|
||||
<textarea
|
||||
id="deploy-options-env"
|
||||
name="deploy-options-env"
|
||||
rows={2}
|
||||
placeholder="FOO=bar"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-volumes">Volumes</label>
|
||||
<textarea
|
||||
id="deploy-options-volumes"
|
||||
name="deploy-options-volumes"
|
||||
rows={2}
|
||||
placeholder="/var/www/example:/data"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -80,10 +160,20 @@ const ShellDeploy = () => {
|
||||
return (
|
||||
<>
|
||||
<label for="deploy-options-path">Path</label>
|
||||
<input id="deploy-options-path" name="deploy-options-path" type="text" placeholder="./deploy.sh" />
|
||||
<input
|
||||
id="deploy-options-path"
|
||||
name="deploy-options-path"
|
||||
type="text"
|
||||
placeholder="./deploy.sh"
|
||||
/>
|
||||
|
||||
<label for="deploy-options-env">Environment</label>
|
||||
<textarea id="deploy-options-env" name="deploy-options-env" rows={2} placeholder="FOO=bar" />
|
||||
<textarea
|
||||
id="deploy-options-env"
|
||||
name="deploy-options-env"
|
||||
rows={2}
|
||||
placeholder="FOO=bar"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -92,7 +182,12 @@ const DockerComposeDeploy = () => {
|
||||
return (
|
||||
<>
|
||||
<label for="deploy-options-path">Path</label>
|
||||
<input id="deploy-options-path" name="deploy-options-path" type="text" />
|
||||
<input
|
||||
id="deploy-options-path"
|
||||
name="deploy-options-path"
|
||||
type="text"
|
||||
placeholder="./docker-compose.yml"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+26
-11
@@ -3,46 +3,61 @@ import yaml from 'js-yaml'
|
||||
import { readFile, writeFile } from 'fs/promises'
|
||||
import { Mutex } from 'async-mutex'
|
||||
|
||||
type GitRef = { commit: string } | { branch: string } | { tag: string }
|
||||
export type GitRef = { type: 'default' } | { type: 'branch' | 'tag' | 'commit'; value: string }
|
||||
|
||||
type BaseDeploy = {
|
||||
name: string
|
||||
export type BaseDeploy = { name: string }
|
||||
|
||||
export type BaseGitDeploy = BaseDeploy & {
|
||||
url: string
|
||||
} & GitRef
|
||||
ref: GitRef
|
||||
}
|
||||
|
||||
type DockerDeploy = BaseDeploy & {
|
||||
export type DockerDeploy = BaseDeploy & {
|
||||
type: 'docker'
|
||||
options: {
|
||||
path?: string
|
||||
image: string
|
||||
name?: string
|
||||
volumes?: string[]
|
||||
ports?: string[]
|
||||
env?: Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
type DockerComposeDeploy = BaseDeploy & {
|
||||
export type DockerfileDeploy = BaseGitDeploy & {
|
||||
type: 'dockerfile'
|
||||
options: {
|
||||
path?: string
|
||||
|
||||
name?: string
|
||||
volumes?: string[]
|
||||
ports?: string[]
|
||||
env?: Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
export type DockerComposeDeploy = BaseGitDeploy & {
|
||||
type: 'docker-compose'
|
||||
options: {
|
||||
path?: string
|
||||
}
|
||||
}
|
||||
|
||||
type ShellDeploy = BaseDeploy & {
|
||||
export type ShellDeploy = BaseGitDeploy & {
|
||||
type: 'shell'
|
||||
options: {
|
||||
path?: string
|
||||
env?: Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
type Deploy = DockerDeploy | DockerComposeDeploy | ShellDeploy
|
||||
export type Deploy = DockerDeploy | DockerfileDeploy | DockerComposeDeploy | ShellDeploy
|
||||
|
||||
type Config = {
|
||||
export type Config = {
|
||||
deploys: Deploy[]
|
||||
}
|
||||
|
||||
export const refToString = (ref: GitRef) => {
|
||||
return 'commit' in ref ? ref.commit : 'branch' in ref ? ref.branch : 'tag' in ref ? ref.tag : '<default>'
|
||||
return ref.type === 'default' ? '<default>' : ref.value
|
||||
}
|
||||
|
||||
const mutex = new Mutex()
|
||||
|
||||
@@ -1,36 +1,15 @@
|
||||
---
|
||||
import { Deploy } from '../../client/Deploy'
|
||||
import { loadConfig, refToString } from '../../config'
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
|
||||
const { deploys } = await loadConfig()
|
||||
---
|
||||
|
||||
<Layout title="Deplots | phCD">
|
||||
<Layout title="Deploys | phCD">
|
||||
<h1>Deploys</h1>
|
||||
<a class="button" href="/deploys/new">New Deploy</a>
|
||||
<ul>
|
||||
{
|
||||
deploys.map(deploy => (
|
||||
<li>
|
||||
<strong>Name: </strong>
|
||||
{deploy.name} <br />
|
||||
<strong>URL: </strong>
|
||||
{deploy.url.startsWith('http') ? (
|
||||
<a href={deploy.url} target="_blank">
|
||||
{deploy.url}
|
||||
</a>
|
||||
) : (
|
||||
deploy.url
|
||||
)}
|
||||
<br />
|
||||
<strong>Ref: </strong>
|
||||
{refToString(deploy)} <br />
|
||||
<strong>Type: </strong>
|
||||
{deploy.type} <br />
|
||||
<strong>Path: </strong>
|
||||
{deploy.options.path} <br />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div class="deploys">
|
||||
{deploys.map(deploy => <Deploy deploy={deploy} />)}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
+99
-4
@@ -104,7 +104,7 @@ textarea {
|
||||
width: auto;
|
||||
|
||||
&:not(textarea) {
|
||||
height: 32px;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
font-size: 18px;
|
||||
@@ -116,7 +116,7 @@ textarea {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
|
||||
padding: 5px 0.25rem;
|
||||
padding: 0 0.25rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -132,6 +132,11 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 2rem;
|
||||
padding: 7px 0.25rem;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
|
||||
@@ -144,7 +149,7 @@ form {
|
||||
border: 1px solid #ddd;
|
||||
|
||||
grid-template-columns: minmax(7rem, auto) 1fr;
|
||||
gap: 0.75rem 1rem;
|
||||
gap: 0.5rem;
|
||||
|
||||
align-items: start;
|
||||
|
||||
@@ -180,11 +185,101 @@ form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 32px;
|
||||
height: 2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.object {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
border: 1px solid #ddd;
|
||||
|
||||
width: 100%;
|
||||
|
||||
& > .key {
|
||||
font-weight: 700;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
min-height: 2rem;
|
||||
|
||||
padding: 0 1rem 0 0.25rem;
|
||||
}
|
||||
|
||||
& > .value {
|
||||
min-height: 2rem;
|
||||
padding: 0.25rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
& > .key,
|
||||
& > .value {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
& > .key:nth-child(1) {
|
||||
border-top: none;
|
||||
}
|
||||
& > .value:nth-child(2) {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.array {
|
||||
display: grid;
|
||||
|
||||
border: 1px solid #ddd;
|
||||
|
||||
width: 100%;
|
||||
|
||||
& > .item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
min-height: 2rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
& > .item {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
& > .item:nth-child(1) {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.compound-value {
|
||||
border-radius: 4px;
|
||||
|
||||
&.borderless {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.deploys {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.deploy {
|
||||
width: 100%;
|
||||
|
||||
background: #fff;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
//
|
||||
// Structure
|
||||
//
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export const clsx = (...args) =>
|
||||
args
|
||||
.filter(Boolean)
|
||||
.flatMap(s => (typeof s === 'string' ? s.split(' ') : [s]))
|
||||
.join(' ')
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact"
|
||||
"jsxImportSource": "preact",
|
||||
"exactOptionalPropertyTypes": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user