mirror of https://github.com/Budibase/budibase.git
75 changed files with 1020 additions and 420 deletions
@ -1,85 +0,0 @@ |
|||||
# This workflow will build and push a new container image to Amazon ECR, |
|
||||
# and then will deploy a new task definition to Amazon ECS, when a release is created |
|
||||
# |
|
||||
# To use this workflow, you will need to complete the following set-up steps: |
|
||||
# |
|
||||
# 1. Create an ECR repository to store your images. |
|
||||
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. |
|
||||
# Replace the value of `ECR_REPOSITORY` in the workflow below with your repository's name. |
|
||||
# Replace the value of `aws-region` in the workflow below with your repository's region. |
|
||||
# |
|
||||
# 2. Create an ECS task definition, an ECS cluster, and an ECS service. |
|
||||
# For example, follow the Getting Started guide on the ECS console: |
|
||||
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun |
|
||||
# Replace the values for `service` and `cluster` in the workflow below with your service and cluster names. |
|
||||
# |
|
||||
# 3. Store your ECS task definition as a JSON file in your repository. |
|
||||
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. |
|
||||
# Replace the value of `task-definition` in the workflow below with your JSON file's name. |
|
||||
# Replace the value of `container-name` in the workflow below with the name of the container |
|
||||
# in the `containerDefinitions` section of the task definition. |
|
||||
# |
|
||||
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. |
|
||||
# See the documentation for each action used below for the recommended IAM policies for this IAM user, |
|
||||
# and best practices on handling the access key credentials. |
|
||||
|
|
||||
on: |
|
||||
push: |
|
||||
tags: |
|
||||
- 'v*' |
|
||||
|
|
||||
name: Deploy to Amazon ECS |
|
||||
|
|
||||
jobs: |
|
||||
deploy: |
|
||||
name: deploy |
|
||||
runs-on: ubuntu-16.04 |
|
||||
|
|
||||
steps: |
|
||||
- name: Checkout |
|
||||
uses: actions/checkout@v2 |
|
||||
|
|
||||
- name: Configure AWS credentials |
|
||||
uses: aws-actions/configure-aws-credentials@v1 |
|
||||
with: |
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} |
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} |
|
||||
aws-region: eu-west-1 |
|
||||
|
|
||||
- name: Download task definition |
|
||||
run: | |
|
||||
aws ecs describe-task-definition --task-definition ProdAppServerStackprodbudiapplbfargateserviceprodbudiappserverfargatetaskdefinition2EF7F1E7 --query taskDefinition > task-definition.json |
|
||||
|
|
||||
- name: Login to Amazon ECR |
|
||||
id: login-ecr |
|
||||
uses: aws-actions/amazon-ecr-login@v1 |
|
||||
|
|
||||
- name: Build, tag, and push image to Amazon ECR |
|
||||
id: build-image |
|
||||
env: |
|
||||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} |
|
||||
ECR_REPOSITORY: prod-budi-app-server |
|
||||
IMAGE_TAG: ${{ github.sha }} |
|
||||
run: | |
|
||||
# Build a docker container and |
|
||||
# push it to ECR so that it can |
|
||||
# be deployed to ECS |
|
||||
cd packages/server |
|
||||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . |
|
||||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG |
|
||||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" |
|
||||
- name: Fill in the new image ID in the Amazon ECS task definition |
|
||||
id: task-def |
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1 |
|
||||
with: |
|
||||
task-definition: task-definition.json |
|
||||
container-name: prod-budi-app-server |
|
||||
image: ${{ steps.build-image.outputs.image }} |
|
||||
|
|
||||
- name: Deploy Amazon ECS task definition |
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1 |
|
||||
with: |
|
||||
task-definition: ${{ steps.task-def.outputs.task-definition }} |
|
||||
service: prod-budi-app-server-service |
|
||||
cluster: prod-budi-app-server |
|
||||
wait-for-service-stability: true |
|
||||
@ -0,0 +1,35 @@ |
|||||
|
{{- if .Values.ingress.aws }} |
||||
|
apiVersion: networking.k8s.io/v1 |
||||
|
kind: Ingress |
||||
|
metadata: |
||||
|
name: ingress-budibase |
||||
|
annotations: |
||||
|
kubernetes.io/ingress.class: alb |
||||
|
alb.ingress.kubernetes.io/scheme: internet-facing |
||||
|
alb.ingress.kubernetes.io/target-type: ip |
||||
|
{{- if .Values.ingress.certificateArn }} |
||||
|
alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' |
||||
|
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' |
||||
|
alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }} |
||||
|
{{- end }} |
||||
|
spec: |
||||
|
rules: |
||||
|
- http: |
||||
|
paths: |
||||
|
{{- if .Values.ingress.certificateArn }} |
||||
|
- path: / |
||||
|
pathType: Prefix |
||||
|
backend: |
||||
|
service: |
||||
|
name: ssl-redirect |
||||
|
port: |
||||
|
name: use-annotation |
||||
|
{{- end }} |
||||
|
- path: / |
||||
|
pathType: Prefix |
||||
|
backend: |
||||
|
service: |
||||
|
name: proxy-service |
||||
|
port: |
||||
|
number: {{ .Values.services.proxy.port }} |
||||
|
{{- end }} |
||||
@ -1,12 +1,23 @@ |
|||||
#!/bin/bash |
#!/bin/bash |
||||
|
|
||||
tag=$1 |
tag=$1 |
||||
tag=${tag:-latest} |
production=$2 |
||||
|
|
||||
echo "Tagging images with SHA: $GITHUB_SHA and tag: $tag" |
if [[ ! "$tag" ]]; then |
||||
|
echo "No tag present. You must pass a tag to this script" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
echo "Tagging images with tag: $tag" |
||||
|
|
||||
docker tag app-service budibase/apps:$tag |
docker tag app-service budibase/apps:$tag |
||||
docker tag worker-service budibase/worker:$tag |
docker tag worker-service budibase/worker:$tag |
||||
|
|
||||
docker push budibase/apps:$tag |
if [[ "$production" ]]; then |
||||
docker push budibase/worker:$tag |
echo "Production Deployment. Tagging latest.." |
||||
|
docker tag app-service budibase/apps:latest |
||||
|
docker tag worker-service budibase/worker:latest |
||||
|
fi |
||||
|
|
||||
|
docker push --all-tags budibase/apps |
||||
|
docker push --all-tags budibase/worker |
||||
|
|||||
@ -0,0 +1,172 @@ |
|||||
|
<script> |
||||
|
import "@spectrum-css/textfield/dist/index-vars.css" |
||||
|
import "@spectrum-css/actionbutton/dist/index-vars.css" |
||||
|
import "@spectrum-css/stepper/dist/index-vars.css" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
|
export let value = null |
||||
|
export let placeholder = null |
||||
|
export let disabled = false |
||||
|
export let error = null |
||||
|
export let id = null |
||||
|
export let readonly = false |
||||
|
export let updateOnChange = true |
||||
|
export let quiet = false |
||||
|
export let min |
||||
|
export let max |
||||
|
export let step |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
let focus = false |
||||
|
|
||||
|
// We need to keep the field value bound to a different variable in order |
||||
|
// to properly handle erroneous values. If we don't do this then it is |
||||
|
// possible for the field to show stale text which does not represent the |
||||
|
// real value. The reactive statement is to ensure that external changes to |
||||
|
// the value prop are reflected. |
||||
|
let fieldValue = value |
||||
|
$: fieldValue = value |
||||
|
|
||||
|
// Ensure step is always a numeric value defaulting to 1 |
||||
|
$: step = step == null || isNaN(step) ? 1 : step |
||||
|
|
||||
|
const updateValue = value => { |
||||
|
if (readonly) { |
||||
|
return |
||||
|
} |
||||
|
const float = parseFloat(value) |
||||
|
value = isNaN(float) ? null : float |
||||
|
if (value != null) { |
||||
|
if (min != null && value < min) { |
||||
|
value = min |
||||
|
} else if (max != null && value > max) { |
||||
|
value = max |
||||
|
} |
||||
|
} |
||||
|
dispatch("change", value) |
||||
|
fieldValue = value |
||||
|
} |
||||
|
|
||||
|
const onFocus = () => { |
||||
|
if (readonly) { |
||||
|
return |
||||
|
} |
||||
|
focus = true |
||||
|
} |
||||
|
|
||||
|
const onBlur = event => { |
||||
|
if (readonly) { |
||||
|
return |
||||
|
} |
||||
|
focus = false |
||||
|
updateValue(event.target.value) |
||||
|
} |
||||
|
|
||||
|
const onInput = event => { |
||||
|
if (readonly || !updateOnChange) { |
||||
|
return |
||||
|
} |
||||
|
updateValue(event.target.value) |
||||
|
} |
||||
|
|
||||
|
const updateValueOnEnter = event => { |
||||
|
if (readonly) { |
||||
|
return |
||||
|
} |
||||
|
if (event.key === "Enter") { |
||||
|
updateValue(event.target.value) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const stepUp = () => { |
||||
|
if (value == null || isNaN(value)) { |
||||
|
updateValue(step) |
||||
|
} else { |
||||
|
updateValue(value + step) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const stepDown = () => { |
||||
|
if (value == null || isNaN(value)) { |
||||
|
updateValue(step) |
||||
|
} else { |
||||
|
updateValue(value - step) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div |
||||
|
class="spectrum-Stepper" |
||||
|
class:spectrum-Stepper--quiet={quiet} |
||||
|
class:is-invalid={!!error} |
||||
|
class:is-disabled={disabled} |
||||
|
class:is-focused={focus} |
||||
|
> |
||||
|
{#if error} |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon" |
||||
|
focusable="false" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-icon-18-Alert" /> |
||||
|
</svg> |
||||
|
{/if} |
||||
|
|
||||
|
<div class="spectrum-Textfield spectrum-Stepper-textfield"> |
||||
|
<input |
||||
|
{disabled} |
||||
|
{readonly} |
||||
|
{id} |
||||
|
bind:value={fieldValue} |
||||
|
placeholder={placeholder || ""} |
||||
|
type="number" |
||||
|
class="spectrum-Textfield-input spectrum-Stepper-input" |
||||
|
on:click |
||||
|
on:blur |
||||
|
on:focus |
||||
|
on:input |
||||
|
on:keyup |
||||
|
on:blur={onBlur} |
||||
|
on:focus={onFocus} |
||||
|
on:input={onInput} |
||||
|
on:keyup={updateValueOnEnter} |
||||
|
/> |
||||
|
</div> |
||||
|
<span class="spectrum-Stepper-buttons"> |
||||
|
<button |
||||
|
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepUp" |
||||
|
tabindex="-1" |
||||
|
on:click={stepUp} |
||||
|
> |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-UIIcon-ChevronUp75" |
||||
|
focusable="false" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-css-icon-Chevron75" /> |
||||
|
</svg> |
||||
|
</button> |
||||
|
<button |
||||
|
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepDown" |
||||
|
tabindex="-1" |
||||
|
on:click={stepDown} |
||||
|
> |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-UIIcon-ChevronDown75" |
||||
|
focusable="false" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-css-icon-Chevron75" /> |
||||
|
</svg> |
||||
|
</button> |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.spectrum-Stepper { |
||||
|
width: 100%; |
||||
|
} |
||||
|
.spectrum-Stepper::before { |
||||
|
display: none; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,45 @@ |
|||||
|
<script> |
||||
|
import Field from "./Field.svelte" |
||||
|
import Stepper from "./Core/Stepper.svelte" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
|
export let value = null |
||||
|
export let label = null |
||||
|
export let labelPosition = "above" |
||||
|
export let placeholder = null |
||||
|
export let disabled = false |
||||
|
export let readonly = false |
||||
|
export let error = null |
||||
|
export let updateOnChange = true |
||||
|
export let quiet = false |
||||
|
export let min = null |
||||
|
export let max = null |
||||
|
export let step = 1 |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
const onChange = e => { |
||||
|
value = e.detail |
||||
|
dispatch("change", e.detail) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<Field {label} {labelPosition} {error}> |
||||
|
<Stepper |
||||
|
{updateOnChange} |
||||
|
{error} |
||||
|
{disabled} |
||||
|
{readonly} |
||||
|
{value} |
||||
|
{placeholder} |
||||
|
{quiet} |
||||
|
{min} |
||||
|
{max} |
||||
|
{step} |
||||
|
on:change={onChange} |
||||
|
on:click |
||||
|
on:input |
||||
|
on:blur |
||||
|
on:focus |
||||
|
on:keyup |
||||
|
/> |
||||
|
</Field> |
||||
@ -0,0 +1,68 @@ |
|||||
|
<script> |
||||
|
import { Select, Label, Stepper } from "@budibase/bbui" |
||||
|
import { currentAsset, store } from "builderStore" |
||||
|
import { getActionProviderComponents } from "builderStore/dataBinding" |
||||
|
import { onMount } from "svelte" |
||||
|
|
||||
|
export let parameters |
||||
|
|
||||
|
$: actionProviders = getActionProviderComponents( |
||||
|
$currentAsset, |
||||
|
$store.selectedComponentId, |
||||
|
"ChangeFormStep" |
||||
|
) |
||||
|
|
||||
|
const typeOptions = [ |
||||
|
{ |
||||
|
label: "Next step", |
||||
|
value: "next", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Previous step", |
||||
|
value: "prev", |
||||
|
}, |
||||
|
{ |
||||
|
label: "First step", |
||||
|
value: "first", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Specific step", |
||||
|
value: "specific", |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
onMount(() => { |
||||
|
if (!parameters.type) { |
||||
|
parameters.type = "next" |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<Label small>Form</Label> |
||||
|
<Select |
||||
|
placeholder={null} |
||||
|
bind:value={parameters.componentId} |
||||
|
options={actionProviders} |
||||
|
getOptionLabel={x => x._instanceName} |
||||
|
getOptionValue={x => x._id} |
||||
|
/> |
||||
|
<Label small>Step</Label> |
||||
|
<Select bind:value={parameters.type} options={typeOptions} /> |
||||
|
{#if parameters.type === "specific"} |
||||
|
<Label small>Number</Label> |
||||
|
<Stepper bind:value={parameters.number} /> |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
display: grid; |
||||
|
column-gap: var(--spacing-l); |
||||
|
row-gap: var(--spacing-s); |
||||
|
grid-template-columns: 60px 1fr; |
||||
|
align-items: center; |
||||
|
max-width: 400px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,62 @@ |
|||||
|
/** |
||||
|
* Finds a component instance by ID |
||||
|
*/ |
||||
|
export const findComponentById = (component, componentId) => { |
||||
|
if (!component || !componentId) { |
||||
|
return null |
||||
|
} |
||||
|
if (component._id === componentId) { |
||||
|
return component |
||||
|
} |
||||
|
if (!component._children?.length) { |
||||
|
return null |
||||
|
} |
||||
|
for (let child of component._children) { |
||||
|
const result = findComponentById(child, componentId) |
||||
|
if (result) { |
||||
|
return result |
||||
|
} |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Finds the component path to a component |
||||
|
*/ |
||||
|
export const findComponentPathById = (component, componentId, path = []) => { |
||||
|
if (!component || !componentId) { |
||||
|
return null |
||||
|
} |
||||
|
path = [...path, component] |
||||
|
if (component._id === componentId) { |
||||
|
return path |
||||
|
} |
||||
|
if (!component._children?.length) { |
||||
|
return null |
||||
|
} |
||||
|
for (let child of component._children) { |
||||
|
const result = findComponentPathById(child, componentId, path) |
||||
|
if (result) { |
||||
|
return result |
||||
|
} |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Finds all children instances of a certain component type of a given component |
||||
|
*/ |
||||
|
export const findChildrenByType = (component, type, children = []) => { |
||||
|
if (!component) { |
||||
|
return |
||||
|
} |
||||
|
if (component._component.endsWith(`/${type}`)) { |
||||
|
children.push(component) |
||||
|
} |
||||
|
if (!component._children?.length) { |
||||
|
return |
||||
|
} |
||||
|
component._children.forEach(child => { |
||||
|
findChildrenByType(child, type, children) |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
<script> |
||||
|
import { getContext, setContext } from "svelte" |
||||
|
import Placeholder from "../Placeholder.svelte" |
||||
|
|
||||
|
export let step = 1 |
||||
|
|
||||
|
const { styleable, builderStore } = getContext("sdk") |
||||
|
const component = getContext("component") |
||||
|
const formContext = getContext("form") |
||||
|
|
||||
|
// Set form step context so fields know what step they are within |
||||
|
setContext("form-step", step || 1) |
||||
|
|
||||
|
$: formState = formContext?.formState |
||||
|
$: currentStep = $formState?.currentStep |
||||
|
|
||||
|
// If in the builder preview, show this step if a child is selected |
||||
|
$: { |
||||
|
if ( |
||||
|
formContext && |
||||
|
$builderStore.inBuilder && |
||||
|
$builderStore.selectedComponentPath?.includes($component.id) |
||||
|
) { |
||||
|
formContext.formApi.setStep(step) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if !formContext} |
||||
|
<Placeholder text="Form steps need to be wrapped in a form" /> |
||||
|
{:else if step === currentStep} |
||||
|
<div use:styleable={$component.styles}> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
{/if} |
||||
Loading…
Reference in new issue